@@ -24,7 +24,7 @@ import roleHelpers from "../roles/role-helpers";
2424import teamRolesManager from "../roles/team-roles-manager" ;
2525import frontendCommunicator from "../common/frontend-communicator" ;
2626import { LoggerCache } from "../logger-cache" ;
27- import { commafy , wait } from "../utils" ;
27+ import { commafy , escapeRegExp , wait } from "../utils" ;
2828
2929interface ViewerDbChangePacket {
3030 userId : string ;
@@ -56,6 +56,20 @@ interface ViewerPurgeOptions {
5656 } ;
5757}
5858
59+ interface ViewersPageRequest {
60+ page : number ;
61+ pageSize : number ;
62+ sortField ?: string ;
63+ sortReversed ?: boolean ;
64+ search ?: string ;
65+ }
66+
67+ interface ViewersPage {
68+ viewers : FirebotViewer [ ] ;
69+ total : number ;
70+ totalUnfiltered : number ;
71+ }
72+
5973interface UserDetails {
6074 firebotData : FirebotViewer ;
6175 twitchData : Record < string , unknown > ;
@@ -70,22 +84,14 @@ class ViewerDatabase extends TypedEmitter<{
7084 private logger = LoggerCache . getLogger ( "Viewers" ) ;
7185
7286 private _db : Datastore < FirebotViewer > ;
73- private _dbCompactionInterval = 30000 ;
87+ private _dbCompactionInterval = 60 * 60 * 1000 ; // 1 hour
7488
7589 private cancelRankRecalculation = false ;
7690 private _activeViewers : string [ ] = [ ] ;
7791
7892 constructor ( ) {
7993 super ( ) ;
8094
81- frontendCommunicator . onAsync ( "connect-viewer-db" , async ( ) => {
82- if ( this . isViewerDBOn ( ) !== true ) {
83- return ;
84- }
85- await this . connectViewerDatabase ( ) ;
86- this . logger . debug ( "Connecting to viewer database." ) ;
87- } ) ;
88-
8995 frontendCommunicator . onAsync ( "viewer-db-change" , async ( data : ViewerDbChangePacket ) => {
9096 if ( this . isViewerDBOn ( ) !== true ) {
9197 return ;
@@ -108,11 +114,8 @@ class ViewerDatabase extends TypedEmitter<{
108114 return await this . purgeViewers ( options ) ;
109115 } ) ;
110116
111- frontendCommunicator . onAsync ( "viewer-database:get-all-viewers" , async ( ) => {
112- if ( this . isViewerDBOn ( ) !== true ) {
113- return [ ] ;
114- }
115- return await this . getAllViewers ( ) ;
117+ frontendCommunicator . onAsync ( "viewer-database:get-viewers-page" , async ( request : ViewersPageRequest ) => {
118+ return await this . getViewersPage ( request ) ;
116119 } ) ;
117120
118121 frontendCommunicator . onAsync ( "create-firebot-viewer-data" , async ( viewer : BasicViewer ) => {
@@ -192,11 +195,15 @@ class ViewerDatabase extends TypedEmitter<{
192195 } , this . _dbCompactionInterval ) ;
193196
194197 this . logger . info ( "Viewer Database Loaded: " , path ) ;
195- this . emit ( "viewer-database-loaded" ) ;
196- }
197198
198- disconnectViewerDatabase ( ) : void {
199- this . _db = null ;
199+ try {
200+ await this . _db . ensureIndexAsync ( { fieldName : "username" , unique : false } ) ;
201+ await this . _db . ensureIndexAsync ( { fieldName : "displayName" , unique : false } ) ;
202+ } catch ( error ) {
203+ this . logger . error ( "Error setting up viewer database indexes: " , error ) ;
204+ }
205+
206+ this . emit ( "viewer-database-loaded" ) ;
200207 }
201208
202209 getViewerDb ( ) : Datastore < FirebotViewer > {
@@ -301,6 +308,38 @@ class ViewerDatabase extends TypedEmitter<{
301308 return Object . values ( await this . _db . findAsync ( { } ) ) ;
302309 }
303310
311+ async getViewersPage ( { page, pageSize, sortField, sortReversed, search } : ViewersPageRequest ) : Promise < ViewersPage > {
312+ if ( this . isViewerDBOn ( ) !== true ) {
313+ return { viewers : [ ] , total : 0 , totalUnfiltered : 0 } ;
314+ }
315+
316+ const query : Record < string , unknown > = { } ;
317+ if ( search != null && search . length > 0 ) {
318+ const searchRegex = new RegExp ( escapeRegExp ( search ) , "i" ) ;
319+ query . $or = [
320+ { username : { $regex : searchRegex } } ,
321+ { displayName : { $regex : searchRegex } }
322+ ] ;
323+ }
324+
325+ const sortObj = sortField ? { [ sortField ] : sortReversed ? - 1 : 1 } : { } ;
326+
327+ try {
328+ const totalUnfiltered = await this . _db . countAsync ( { } ) ;
329+ const total = query . $or ? await this . _db . countAsync ( query ) : totalUnfiltered ;
330+
331+ const viewers = await this . _db . findAsync ( query )
332+ . sort ( sortObj )
333+ . skip ( Math . max ( 0 , ( page - 1 ) * pageSize ) )
334+ . limit ( pageSize ) ;
335+
336+ return { viewers, total, totalUnfiltered } ;
337+ } catch ( error ) {
338+ this . logger . error ( "Error getting viewers page: " , error ) ;
339+ return { viewers : [ ] , total : 0 , totalUnfiltered : 0 } ;
340+ }
341+ }
342+
304343 async getAllUsernames ( ) : Promise < string [ ] > {
305344 if ( this . isViewerDBOn ( ) !== true ) {
306345 return [ ] ;
@@ -490,7 +529,10 @@ class ViewerDatabase extends TypedEmitter<{
490529
491530 async getPurgeViewers ( options : ViewerPurgeOptions ) : Promise < FirebotViewer [ ] > {
492531 try {
493- const bannedUsers = ( await TwitchApi . moderation . getBannedUsers ( ) ) . filter ( u => u . expiryDate === null ) ;
532+ let bannedUsers : HelixBan [ ] = [ ] ;
533+ if ( options . banned . enabled ) {
534+ bannedUsers = ( await TwitchApi . moderation . getBannedUsers ( ) ) . filter ( u => u . expiryDate === null ) ;
535+ }
494536 return await this . _db . findAsync ( { $where : this . getPurgeWherePredicate ( options , bannedUsers ) } ) ;
495537 } catch {
496538 return [ ] ;
0 commit comments