diff --git a/package.json b/package.json index 3e7e32c4..38ee60e2 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.20.6", + "version": "4.20.7", "description": "FrankerFaceZ is a Twitch enhancement suite.", "license": "Apache-2.0", "scripts": { diff --git a/src/modules/chat/badges.jsx b/src/modules/chat/badges.jsx index 4e3c2ec7..c898a4d2 100644 --- a/src/modules/chat/badges.jsx +++ b/src/modules/chat/badges.jsx @@ -169,6 +169,13 @@ export default class Badges extends Module { this.inject('experiments'); this.style = new ManagedStyle('badges'); + + // Special data structure for supporters to greatly reduce + // memory usage and speed things up for people who only have + // a supporter badge. + this.supporter_id = null; + this.supporters = new Set; + this.badges = {}; this.twitch_badges = {}; @@ -646,8 +653,13 @@ export default class Badges extends Module { global_user = this.parent.getUser(user_id, user_login, true), room_user = room && room.getUser(user_id, user_login, true); - return (global_user ? global_user.badges._cache : []).concat( + const out = (global_user ? global_user.badges._cache : []).concat( room_user ? room_user.badges._cache : []); + + if ( this.supporter_id && this.supporters.has(`${user_id}`) ) + out.push({id: this.supporter_id}); + + return out; } @@ -656,11 +668,11 @@ export default class Badges extends Module { if ( this.experiments.getAssignment('api_load') && tries < 1 ) try { - fetch(`${NEW_API}/v1/badges`).catch(() => {}); + fetch(`${NEW_API}/v2/badges`).catch(() => {}); } catch(err) { /* do nothing */ } try { - response = await fetch(`${API_SERVER}/v1/badges`); + response = await fetch(`${API_SERVER}/v1/badges/ids`); } catch(err) { tries++; if ( tries < 10 ) @@ -694,13 +706,21 @@ export default class Badges extends Module { if ( has(data.users, badge_id) ) { const badge = this.badges[badge_id]; let c = 0; - for(const user_login of data.users[badge_id]) { - const user = this.parent.getUser(undefined, user_login); - if ( user.addBadge('ffz-global', badge_id) ) { - c++; - users++; + + if ( badge?.name === 'supporter' ) { + this.supporter_id = badge_id; + for(const user_id of data.users[badge_id]) + this.supporters.add(`${user_id}`); + + c = this.supporters.size; + } else + for(const user_id of data.users[badge_id]) { + const user = this.parent.getUser(user_id, undefined); + if ( user.addBadge('ffz-global', badge_id) ) { + c++; + users++; + } } - } if ( c > 0 ) this.log.info(`Added "${badge ? badge.name : `#${badge_id}`}" to ${c} users.`); diff --git a/src/sites/twitch-twilight/modules/css_tweaks/styles/hide-native-viewers.scss b/src/sites/twitch-twilight/modules/css_tweaks/styles/hide-native-viewers.scss new file mode 100644 index 00000000..7c100d07 --- /dev/null +++ b/src/sites/twitch-twilight/modules/css_tweaks/styles/hide-native-viewers.scss @@ -0,0 +1,6 @@ +.ffz--meta-tray { + & > :first-child > :first-child { + display: none !important; + margin-right: 0 !important; + } +} \ No newline at end of file diff --git a/src/socket.js b/src/socket.js index 4a271438..ca80882a 100644 --- a/src/socket.js +++ b/src/socket.js @@ -7,6 +7,7 @@ import Module from 'utilities/module'; import {DEBUG, WS_CLUSTERS} from 'utilities/constants'; +import { on } from 'utilities/dom'; export const State = { @@ -140,7 +141,80 @@ export default class SocketClient extends Module { return new Promise((s, f) => { this._token_waiters.push([s, f]); - this.call('get_api_token').then(token => { + let done = false, timer = null; + + const fail = err => { + if ( done ) + return; + + clearTimeout(timer); + done = true; + this.log.error('Unable to get API token.', err); + const waiters = this._token_waiters; + this._token_waiters = null; + + for(const pair of waiters) + pair[1](err); + } + + const user = this.resolve('site')?.getUser?.(); + if ( ! user || ! user.id ) + return fail(new Error('Unable to get current user or not logged in.')); + + const es = new EventSource(`https://api-test.frankerfacez.com/auth/ext_verify/${user.id}`); + + on(es, 'challenge', event => { + const conn = this.resolve('site.chat')?.ChatService?.first?.client?.connection; + if ( conn && conn.send ) + conn.send(`PRIVMSG #frankerfacezauthorizer :AUTH ${event.data}`); + }); + + on(es, 'token', event => { + if ( done ) + return; + + clearTimeout(timer); + + let token = null; + try { + token = JSON.parse(event.data); + } catch(err) { + fail(err); + return; + } + + if ( ! token || ! token.token ) { + fail(new Error('Received empty token from server.')); + return; + } + + token.expires = (new Date(token.expires)).getTime(); + this._cached_token = token; + + done = true; + + const waiters = this._token_waiters; + this._token_waiters = null; + + for(const pair of waiters) + pair[0](token); + }); + + on(es, 'error', err => { + fail(err); + }); + + on(es, 'close', () => { + es.close(); + if ( ! done ) + fail(new Error('Connection closed unexpectedly.')); + }); + + timer = setTimeout(() => { + fail(new Error('timeout')); + }, 5000); + + /*this.call('get_api_token').then(token => { token.expires = (new Date(token.expires)).getTime(); this._cached_token = token; @@ -157,7 +231,7 @@ export default class SocketClient extends Module { for(const pair of waiters) pair[1](err); - }); + });*/ }); }