2017-11-13 01:23:39 -05:00
'use strict' ;
// ============================================================================
// Emote Handling and Default Provider
// ============================================================================
import Module from 'utilities/module' ;
import { ManagedStyle } from 'utilities/dom' ;
2019-01-15 16:14:21 -05:00
import { get , has , timeout , SourcedSet } from 'utilities/object' ;
2019-10-28 14:56:55 -04:00
import { NEW _API , API _SERVER , IS _OSX , EmoteTypes , TWITCH _GLOBAL _SETS , TWITCH _POINTS _SETS , TWITCH _PRIME _SETS } from 'utilities/constants' ;
2017-11-13 01:23:39 -05:00
2019-01-15 16:14:21 -05:00
import GET _EMOTE from './emote_info.gql' ;
2019-10-28 14:56:55 -04:00
import GET _EMOTE _SET from './emote_set_info.gql' ;
2019-01-15 16:14:21 -05:00
2021-03-20 18:47:12 -04:00
const HoverRAF = Symbol ( 'FFZ:Hover:RAF' ) ;
const HoverState = Symbol ( 'FFZ:Hover:State' ) ;
2018-04-09 19:57:05 -04:00
const MOD _KEY = IS _OSX ? 'metaKey' : 'ctrlKey' ;
2017-11-13 01:23:39 -05:00
const MODIFIERS = {
59847 : {
modifier _offset : '0 15px 15px 0' ,
modifier : true
} ,
70852 : {
modifier : true ,
modifier _offset : '0 5px 20px 0' ,
extra _width : 5 ,
shrink _to _fit : true
} ,
70854 : {
modifier : true ,
modifier _offset : '30px 0 0'
} ,
147049 : {
modifier : true ,
modifier _offset : '4px 1px 0 3px'
} ,
147011 : {
modifier : true ,
modifier _offset : '0'
} ,
70864 : {
modifier : true ,
modifier _offset : '0'
} ,
147038 : {
modifier : true ,
modifier _offset : '0'
}
} ;
export default class Emotes extends Module {
constructor ( ... args ) {
super ( ... args ) ;
2019-10-28 14:56:55 -04:00
this . EmoteTypes = EmoteTypes ;
2017-12-13 20:22:11 -05:00
this . inject ( 'settings' ) ;
2018-04-12 02:29:43 -04:00
this . inject ( 'experiments' ) ;
2017-11-13 01:23:39 -05:00
2019-10-28 01:06:02 -04:00
this . twitch _inventory _sets = new Set ; //(EXTRA_INVENTORY);
2019-12-12 18:44:19 -05:00
this . _ _twitch _emote _to _set = { } ;
this . _ _twitch _set _to _channel = { } ;
2017-11-13 01:23:39 -05:00
2017-11-15 21:59:13 -05:00
this . default _sets = new SourcedSet ;
this . global _sets = new SourcedSet ;
2017-11-13 01:23:39 -05:00
2018-04-06 21:12:12 -04:00
this . providers = new Map ;
this . providers . set ( 'featured' , {
name : 'Featured' ,
i18n _key : 'emote-menu.featured' ,
sort _key : 75
} )
2017-11-13 01:23:39 -05:00
this . emote _sets = { } ;
2017-11-22 20:21:01 -05:00
this . _set _refs = { } ;
this . _set _timers = { } ;
2017-12-13 20:22:11 -05:00
2021-04-27 16:23:19 -04:00
this . settings . add ( 'chat.emotes.enabled' , {
default : 2 ,
ui : {
path : 'Chat > Appearance >> Emotes' ,
title : 'Display Emotes' ,
sort : - 100 ,
force _seen : true ,
description : 'If you do not wish to see emotes, you can disable them here.' ,
component : 'setting-select-box' ,
data : [
{ value : 0 , title : 'Disabled' } ,
{ value : 1 , title : 'Twitch Only' } ,
{ value : 2 , title : 'Enabled' }
]
}
} ) ;
2021-02-15 17:48:30 -05:00
this . settings . add ( 'chat.emotes.2x' , {
2022-02-11 15:17:32 -05:00
default : 0 ,
process ( ctx , val ) {
if ( val === true ) return 1 ;
else if ( val === false ) return 0 ;
return val ;
} ,
2021-02-15 17:48:30 -05:00
ui : {
path : 'Chat > Appearance >> Emotes' ,
title : 'Larger Emotes' ,
description : 'This setting will make emotes appear twice as large in chat. It\'s good for use with larger fonts or just if you really like emotes.' ,
2022-02-11 15:17:32 -05:00
component : 'setting-select-box' ,
data : [
{ value : 0 , title : 'Disabled' } ,
{ value : 1 , title : 'Emotes' } ,
{ value : 2 , title : 'Emotes and Emoji' }
]
2021-02-15 17:48:30 -05:00
}
} ) ;
2022-03-05 15:15:27 -05:00
this . settings . add ( 'chat.emotes.limit-size' , {
default : true ,
ui : {
path : 'Chat > Appearance >> Emotes' ,
title : 'Limit Native Emote Size' ,
description : 'Sometimes, really obnoxiously large emotes slip through the cracks and wind up on Twitch. This limits the size of Twitch emotes to mitigate the issue.' ,
component : 'setting-check-box'
}
} ) ;
2017-12-13 20:22:11 -05:00
this . settings . add ( 'chat.fix-bad-emotes' , {
default : true ,
ui : {
path : 'Chat > Appearance >> Emotes' ,
title : 'Fix Bad Twitch Global Emotes' ,
description : 'Clean up the images for bad Twitch global emotes, removing white borders and solid backgrounds.' ,
component : 'setting-check-box'
}
} ) ;
2018-04-09 19:57:05 -04:00
2019-01-18 19:07:57 -05:00
this . settings . add ( 'chat.click-emotes' , {
default : true ,
ui : {
path : 'Chat > Behavior >> General' ,
title : 'Open emote information pages by Shift-Clicking them.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.sub-emotes' , {
default : true ,
ui : {
path : 'Chat > Behavior >> General' ,
title : 'Open Twitch subscription pages by Shift-Clicking emotes when relevant.' ,
component : 'setting-check-box'
}
} ) ;
this . settings . add ( 'chat.emote-dialogs' , {
default : true ,
ui : {
path : 'Chat > Behavior >> General' ,
title : 'Open emote information cards for Twitch emotes by clicking them.' ,
component : 'setting-check-box'
}
} ) ;
2018-04-09 19:57:05 -04:00
// Because this may be used elsewhere.
this . handleClick = this . handleClick . bind ( this ) ;
2021-03-20 18:47:12 -04:00
this . animHover = this . animHover . bind ( this ) ;
this . animLeave = this . animLeave . bind ( this ) ;
2017-11-13 01:23:39 -05:00
}
onEnable ( ) {
this . style = new ManagedStyle ( 'emotes' ) ;
2019-12-12 18:44:19 -05:00
// Fix numeric Twitch favorite IDs.
const favs = this . getFavorites ( 'twitch' ) ;
let changed = false ;
for ( let i = 0 ; i < favs . length ; i ++ ) {
if ( typeof favs [ i ] === 'number' ) {
changed = true ;
favs [ i ] = ` ${ favs [ i ] } ` ;
}
}
if ( changed )
this . setFavorites ( 'twitch' , favs ) ;
2017-11-13 01:23:39 -05:00
if ( Object . keys ( this . emote _sets ) . length ) {
this . log . info ( 'Generating CSS for existing emote sets.' ) ;
for ( const set _id in this . emote _sets )
if ( has ( this . emote _sets , set _id ) ) {
const emote _set = this . emote _sets [ set _id ] ;
2023-01-19 17:00:09 -05:00
if ( emote _set && ( emote _set . pending _css || emote _set . css ) ) {
this . style . set ( ` es-- ${ set _id } ` , ( emote _set . pending _css || '' ) + ( emote _set . css || '' ) ) ;
2017-11-13 01:23:39 -05:00
emote _set . pending _css = null ;
}
}
}
2021-03-02 16:55:25 -05:00
this . on ( 'socket:command:follow_sets' , this . updateFollowSets , this ) ;
2018-04-06 21:12:12 -04:00
2017-11-16 15:54:58 -05:00
this . loadGlobalSets ( ) ;
2017-11-13 01:23:39 -05:00
}
2018-04-06 21:12:12 -04:00
// ========================================================================
// Featured Sets
// ========================================================================
updateFollowSets ( data ) {
for ( const room _login in data )
if ( has ( data , room _login ) ) {
2018-04-13 13:36:05 -04:00
const room = this . parent . getRoom ( null , room _login , true ) ;
if ( ! room || room . destroyed )
continue ;
const new _sets = data [ room _login ] || [ ] ,
2018-04-06 21:12:12 -04:00
emote _sets = room . emote _sets ,
2018-04-13 13:36:05 -04:00
providers = emote _sets && emote _sets . _sources ;
2018-04-06 21:12:12 -04:00
if ( providers && providers . has ( 'featured' ) )
2018-04-12 02:29:43 -04:00
for ( const item of providers . get ( 'featured' ) ) {
const idx = new _sets . indexOf ( item ) ;
if ( idx === - 1 )
2018-04-06 21:12:12 -04:00
room . removeSet ( 'featured' , item ) ;
2018-04-12 02:29:43 -04:00
else
new _sets . splice ( idx , 1 ) ;
}
2018-04-06 21:12:12 -04:00
for ( const set _id of new _sets ) {
room . addSet ( 'featured' , set _id ) ;
if ( ! this . emote _sets [ set _id ] )
this . loadSet ( set _id ) ;
}
}
}
2020-06-23 17:17:00 -04:00
// ========================================================================
// Hidden Checking
// ========================================================================
toggleHidden ( source , id , value = null ) {
const key = ` hidden-emotes. ${ source } ` ,
p = this . settings . provider ,
hidden = p . get ( key , [ ] ) ,
idx = hidden . indexOf ( id ) ;
if ( value === null )
value = idx === - 1 ;
if ( value && idx === - 1 )
hidden . push ( id ) ;
else if ( ! value && idx !== - 1 )
hidden . splice ( idx , 1 ) ;
else
return ;
if ( hidden . length )
p . set ( key , hidden ) ;
else
p . delete ( key ) ;
this . emit ( ':change-hidden' , source , id , value ) ;
}
isHidden ( source , id ) {
return this . getHidden ( source ) . includes ( id ) ;
}
getHidden ( source ) {
2020-11-15 17:33:55 -05:00
return this . settings . provider . get ( ` hidden-emotes. ${ source } ` ) || [ ] ;
2020-06-23 17:17:00 -04:00
}
setHidden ( source , list ) {
const key = ` hidden-emotes. ${ source } ` ;
if ( ! Array . isArray ( list ) || ! list . length )
this . settings . provider . delete ( key ) ;
else
this . settings . provider . set ( key , list ) ;
}
2021-03-20 18:47:12 -04:00
// ========================================================================
// Animation Hover
// ========================================================================
animHover ( event ) { // eslint-disable-line class-methods-use-this
const target = event . currentTarget ;
if ( target [ HoverState ] )
return ;
if ( target [ HoverRAF ] )
cancelAnimationFrame ( target [ HoverRAF ] ) ;
target [ HoverRAF ] = requestAnimationFrame ( ( ) => {
target [ HoverRAF ] = null ;
if ( target [ HoverState ] )
return ;
if ( ! target . matches ( ':hover' ) )
return ;
target [ HoverState ] = true ;
const emotes = target . querySelectorAll ( '.ffz-hover-emote' ) ;
for ( const em of emotes ) {
const ds = em . dataset ;
if ( ds . normalSrc && ds . hoverSrc ) {
em . src = ds . hoverSrc ;
em . srcset = ds . hoverSrcSet ;
}
}
} ) ;
}
animLeave ( event ) { // eslint-disable-line class-methods-use-this
const target = event . currentTarget ;
if ( ! target [ HoverState ] )
return ;
if ( target [ HoverRAF ] )
cancelAnimationFrame ( target [ HoverRAF ] ) ;
target [ HoverRAF ] = requestAnimationFrame ( ( ) => {
target [ HoverRAF ] = null ;
if ( ! target [ HoverState ] )
return ;
if ( target . matches ( ':hover' ) )
return ;
target [ HoverState ] = false ;
const emotes = target . querySelectorAll ( '.ffz-hover-emote' ) ;
for ( const em of emotes ) {
const ds = em . dataset ;
if ( ds . normalSrc ) {
em . src = ds . normalSrc ;
em . srcset = ds . normalSrcSet ;
}
}
} ) ;
}
2018-04-09 19:57:05 -04:00
// ========================================================================
// Favorite Checking
// ========================================================================
toggleFavorite ( source , id , value = null ) {
const key = ` favorite-emotes. ${ source } ` ,
p = this . settings . provider ,
favorites = p . get ( key ) || [ ] ,
idx = favorites . indexOf ( id ) ;
if ( value === null )
value = idx === - 1 ;
if ( value && idx === - 1 )
favorites . push ( id ) ;
else if ( ! value && idx !== - 1 )
favorites . splice ( idx , 1 ) ;
else
return ;
if ( favorites . length )
p . set ( key , favorites ) ;
else
p . delete ( key ) ;
this . emit ( ':change-favorite' , source , id , value ) ;
}
isFavorite ( source , id ) {
const favorites = this . settings . provider . get ( ` favorite-emotes. ${ source } ` ) ;
return favorites && favorites . includes ( id ) ;
}
getFavorites ( source ) {
return this . settings . provider . get ( ` favorite-emotes. ${ source } ` ) || [ ] ;
}
2019-12-12 18:44:19 -05:00
setFavorites ( source , favs ) {
const key = ` favorite-emotes. ${ source } ` ;
if ( ! Array . isArray ( favs ) || ! favs . length )
this . settings . provider . delete ( key ) ;
else
this . settings . provider . set ( key , favs ) ;
}
2018-04-09 19:57:05 -04:00
handleClick ( event ) {
const target = event . target ,
ds = target && target . dataset ;
if ( ! ds )
return ;
2019-01-15 16:14:21 -05:00
const provider = ds . provider ,
click _emote = this . parent . context . get ( 'chat.click-emotes' ) ,
click _sub = this . parent . context . get ( 'chat.sub-emotes' ) ;
2018-04-09 19:57:05 -04:00
2019-01-15 16:14:21 -05:00
if ( event . shiftKey && ( click _emote || click _sub ) ) {
2018-04-11 17:05:31 -04:00
let url ;
2019-01-15 16:14:21 -05:00
if ( provider === 'twitch' ) {
2018-04-11 17:05:31 -04:00
url = ` https://twitchemotes.com/emotes/ ${ ds . id } ` ;
2019-01-15 16:14:21 -05:00
if ( click _sub ) {
const apollo = this . resolve ( 'site.apollo' ) ;
if ( apollo ) {
apollo . client . query ( {
query : GET _EMOTE ,
variables : {
id : ds . id
}
} ) . then ( result => {
const prod = get ( 'data.emote.subscriptionProduct' , result ) ;
if ( prod && prod . state === 'ACTIVE' && prod . owner && prod . owner . login )
url = ` https://www.twitch.tv/subs/ ${ prod . owner . login } ` ;
else if ( ! click _emote )
return false ;
if ( url ) {
const win = window . open ( ) ;
if ( win ) {
win . opener = null ;
win . location = url ;
}
}
} ) ;
return true ;
}
}
} else if ( provider === 'ffz' ) {
2018-04-11 17:05:31 -04:00
const emote _set = this . emote _sets [ ds . set ] ,
emote = emote _set && emote _set . emotes [ ds . id ] ;
if ( ! emote )
return ;
if ( emote . click _url )
url = emote . click _url ;
else if ( ! emote _set . source )
url = ` https://www.frankerfacez.com/emoticons/ ${ emote . id } ` ;
}
2019-01-15 16:14:21 -05:00
if ( ! click _emote )
return false ;
2018-04-11 17:05:31 -04:00
if ( url ) {
const win = window . open ( ) ;
2018-07-26 19:40:53 -04:00
if ( win ) {
win . opener = null ;
win . location = url ;
}
2018-04-11 17:05:31 -04:00
}
return true ;
}
2018-04-09 19:57:05 -04:00
if ( event [ MOD _KEY ] ) {
// Favoriting Emotes
let source , id ;
if ( provider === 'twitch' ) {
source = 'twitch' ;
2019-12-12 18:44:19 -05:00
id = ds . id ;
2018-04-09 19:57:05 -04:00
} else if ( provider === 'ffz' ) {
const emote _set = this . emote _sets [ ds . set ] ,
emote = emote _set && emote _set . emotes [ ds . id ] ;
if ( ! emote )
return ;
source = emote _set . source || 'ffz' ;
id = emote . id ;
2018-04-12 20:30:00 -04:00
} else if ( provider === 'emoji' ) {
source = 'emoji' ;
id = ds . code ;
2018-04-09 19:57:05 -04:00
} else
return ;
this . toggleFavorite ( source , id ) ;
2020-07-18 15:44:02 -04:00
const tt = target . _ffz _tooltip ;
2018-04-09 19:57:05 -04:00
if ( tt && tt . visible ) {
tt . hide ( ) ;
setTimeout ( ( ) => document . contains ( target ) && tt . show ( ) , 0 ) ;
}
return true ;
}
2019-01-18 19:07:57 -05:00
if ( provider === 'twitch' && this . parent . context . get ( 'chat.emote-dialogs' ) ) {
const fine = this . resolve ( 'site.fine' ) ;
if ( ! fine )
return ;
2020-08-07 01:32:18 -04:00
const line = fine . searchParent ( target , n => n . props && n . props . message ) ,
2022-12-11 15:48:32 -05:00
opener = fine . searchParent ( target , n => n . onShowEmoteCard , 250 ) ;
2020-08-07 01:32:18 -04:00
if ( ! line || ! opener )
2019-01-18 19:07:57 -05:00
return ;
2021-12-16 15:00:14 -05:00
const rect = target . getBoundingClientRect ( ) ;
2020-08-07 01:32:18 -04:00
opener . onShowEmoteCard ( {
channelID : line . props . channelID || '' ,
channelLogin : line . props . channelLogin || '' ,
2019-01-18 19:07:57 -05:00
emoteID : ds . id ,
emoteCode : target . alt ,
sourceID : 'chat' ,
referrerID : '' ,
2021-12-16 15:00:14 -05:00
initialTopOffset : rect . bottom ,
initialBottomOffset : rect . top
2019-01-18 19:07:57 -05:00
} ) ;
return true ;
}
2018-04-09 19:57:05 -04:00
}
2017-11-13 01:23:39 -05:00
// ========================================================================
// Access
// ========================================================================
getSetIDs ( user _id , user _login , room _id , room _login ) {
const room = this . parent . getRoom ( room _id , room _login , true ) ,
2017-12-13 17:35:20 -05:00
room _user = room && room . getUser ( user _id , user _login , true ) ,
2017-11-13 01:23:39 -05:00
user = this . parent . getUser ( user _id , user _login , true ) ;
2021-03-22 18:19:09 -04:00
return ( user ? . emote _sets ? user . emote _sets . _cache : [ ] ) . concat (
room _user ? . emote _sets ? room _user . emote _sets . _cache : [ ] ,
room ? . emote _sets ? room . emote _sets . _cache : [ ] ,
2017-11-15 21:59:13 -05:00
this . default _sets . _cache
2017-11-13 01:23:39 -05:00
) ;
}
getSets ( user _id , user _login , room _id , room _login ) {
return this . getSetIDs ( user _id , user _login , room _id , room _login )
. map ( set _id => this . emote _sets [ set _id ] ) ;
}
2018-04-06 21:12:12 -04:00
_withSources ( out , seen , emote _sets ) { // eslint-disable-line class-methods-use-this
2021-03-22 18:19:09 -04:00
if ( ! emote _sets ? . _sources )
2018-04-06 21:12:12 -04:00
return ;
for ( const [ provider , data ] of emote _sets . _sources )
for ( const item of data )
if ( ! seen . has ( item ) ) {
out . push ( [ item , provider ] ) ;
seen . add ( item ) ;
}
return out ;
}
getRoomSetIDsWithSources ( user _id , user _login , room _id , room _login ) {
const room = this . parent . getRoom ( room _id , room _login , true ) ,
room _user = room && room . getUser ( user _id , user _login , true ) ;
if ( ! room )
return [ ] ;
const out = [ ] , seen = new Set ;
this . _withSources ( out , seen , room . emote _sets ) ;
if ( room _user )
this . _withSources ( out , seen , room _user ) ;
return out ;
}
getRoomSetsWithSources ( user _id , user _login , room _id , room _login ) {
return this . getRoomSetIDsWithSources ( user _id , user _login , room _id , room _login )
. map ( ( [ set _id , source ] ) => [ this . emote _sets [ set _id ] , source ] ) ;
}
2017-12-13 17:35:20 -05:00
getRoomSetIDs ( user _id , user _login , room _id , room _login ) {
const room = this . parent . getRoom ( room _id , room _login , true ) ,
room _user = room && room . getUser ( user _id , user _login , true ) ;
if ( ! room )
return [ ] ;
2021-03-22 18:19:09 -04:00
if ( ! room _user ? . emote _sets )
return room . emote _sets ? room . emote _sets . _cache : [ ] ;
else if ( ! room . emote _sets )
return room _user . emote _sets . _cache ;
2017-12-13 17:35:20 -05:00
return room _user . emote _sets . _cache . concat ( room . emote _sets . _cache ) ;
}
getRoomSets ( user _id , user _login , room _id , room _login ) {
return this . getRoomSetIDs ( user _id , user _login , room _id , room _login )
. map ( set _id => this . emote _sets [ set _id ] ) ;
}
2018-04-06 21:12:12 -04:00
getGlobalSetIDsWithSources ( user _id , user _login ) {
const user = this . parent . getUser ( user _id , user _login , true ) ,
out = [ ] , seen = new Set ;
this . _withSources ( out , seen , this . default _sets ) ;
if ( user )
this . _withSources ( out , seen , user . emote _sets ) ;
return out ;
}
getGlobalSetsWithSources ( user _id , user _login ) {
return this . getGlobalSetIDsWithSources ( user _id , user _login )
. map ( ( [ set _id , source ] ) => [ this . emote _sets [ set _id ] , source ] ) ;
}
2017-12-13 17:35:20 -05:00
getGlobalSetIDs ( user _id , user _login ) {
const user = this . parent . getUser ( user _id , user _login , true ) ;
2021-03-22 18:19:09 -04:00
if ( ! user ? . emote _sets )
2017-12-13 17:35:20 -05:00
return this . default _sets . _cache ;
return user . emote _sets . _cache . concat ( this . default _sets . _cache ) ;
}
getGlobalSets ( user _id , user _login ) {
return this . getGlobalSetIDs ( user _id , user _login )
. map ( set _id => this . emote _sets [ set _id ] ) ;
}
2017-11-22 15:39:38 -05:00
getEmotes ( user _id , user _login , room _id , room _login ) {
const emotes = { } ;
for ( const emote _set of this . getSets ( user _id , user _login , room _id , room _login ) )
if ( emote _set && emote _set . emotes )
for ( const emote of Object . values ( emote _set . emotes ) )
if ( emote && ! has ( emotes , emote . name ) )
emotes [ emote . name ] = emote ;
return emotes ;
}
2017-11-13 01:23:39 -05:00
// ========================================================================
2017-11-22 20:21:01 -05:00
// Emote Set Ref Counting
// ========================================================================
addDefaultSet ( provider , set _id , data ) {
2021-06-10 18:53:16 -04:00
if ( typeof set _id === 'number' )
set _id = ` ${ set _id } ` ;
let changed = false , added = false ;
2017-11-22 20:21:01 -05:00
if ( ! this . default _sets . sourceIncludes ( provider , set _id ) ) {
2021-06-10 18:53:16 -04:00
changed = ! this . default _sets . includes ( set _id ) ;
2017-11-22 20:21:01 -05:00
this . default _sets . push ( provider , set _id ) ;
2021-06-10 18:53:16 -04:00
added = true ;
2017-11-22 20:21:01 -05:00
}
if ( data )
this . loadSetData ( set _id , data ) ;
2018-04-01 18:24:08 -04:00
2021-06-10 18:53:16 -04:00
if ( changed ) {
this . refSet ( set _id ) ;
2018-04-06 21:12:12 -04:00
this . emit ( ':update-default-sets' , provider , set _id , true ) ;
2021-06-10 18:53:16 -04:00
}
return added ;
2017-11-22 20:21:01 -05:00
}
removeDefaultSet ( provider , set _id ) {
2021-06-10 18:53:16 -04:00
if ( typeof set _id === 'number' )
set _id = ` ${ set _id } ` ;
2017-11-22 20:21:01 -05:00
if ( this . default _sets . sourceIncludes ( provider , set _id ) ) {
this . default _sets . remove ( provider , set _id ) ;
2021-06-10 18:53:16 -04:00
if ( ! this . default _sets . includes ( set _id ) ) {
this . unrefSet ( set _id ) ;
this . emit ( ':update-default-sets' , provider , set _id , false ) ;
}
return true ;
2017-11-22 20:21:01 -05:00
}
2018-04-01 18:24:08 -04:00
2021-06-10 18:53:16 -04:00
return false ;
2017-11-22 20:21:01 -05:00
}
refSet ( set _id ) {
this . _set _refs [ set _id ] = ( this . _set _refs [ set _id ] || 0 ) + 1 ;
if ( this . _set _timers [ set _id ] ) {
clearTimeout ( this . _set _timers [ set _id ] ) ;
this . _set _timers [ set _id ] = null ;
}
}
unrefSet ( set _id ) {
const c = this . _set _refs [ set _id ] = ( this . _set _refs [ set _id ] || 1 ) - 1 ;
if ( c <= 0 && ! this . _set _timers [ set _id ] )
this . _set _timers [ set _id ] = setTimeout ( ( ) => this . unloadSet ( set _id ) , 5000 ) ;
}
// ========================================================================
// Emote Set Loading
2017-11-13 01:23:39 -05:00
// ========================================================================
2017-11-16 15:54:58 -05:00
async loadGlobalSets ( tries = 0 ) {
2017-11-13 01:23:39 -05:00
let response , data ;
2018-04-12 02:29:43 -04:00
2020-03-05 18:21:11 -05:00
if ( this . experiments . getAssignment ( 'api_load' ) && tries < 1 )
2018-04-12 02:29:43 -04:00
try {
fetch ( ` ${ NEW _API } /v1/set/global ` ) . catch ( ( ) => { } ) ;
} catch ( err ) { /* do nothing */ }
2017-11-13 01:23:39 -05:00
try {
response = await fetch ( ` ${ API _SERVER } /v1/set/global ` )
} catch ( err ) {
tries ++ ;
if ( tries < 10 )
2017-11-16 15:54:58 -05:00
return setTimeout ( ( ) => this . loadGlobalSets ( tries ) , 500 * tries ) ;
2017-11-13 01:23:39 -05:00
this . log . error ( 'Error loading global emote sets.' , err ) ;
return false ;
}
if ( ! response . ok )
return false ;
try {
data = await response . json ( ) ;
} catch ( err ) {
this . log . error ( 'Error parsing global emote data.' , err ) ;
return false ;
}
const sets = data . sets || { } ;
2017-11-22 20:21:01 -05:00
for ( const set _id of data . default _sets )
this . addDefaultSet ( 'ffz-global' , set _id ) ;
2017-11-13 01:23:39 -05:00
for ( const set _id in sets )
2017-11-22 20:21:01 -05:00
if ( has ( sets , set _id ) )
2017-11-16 15:54:58 -05:00
this . loadSetData ( set _id , sets [ set _id ] ) ;
2017-11-13 01:23:39 -05:00
if ( data . users )
2017-11-16 15:54:58 -05:00
this . loadSetUsers ( data . users ) ;
2018-04-12 20:30:00 -04:00
return true ;
2017-11-13 01:23:39 -05:00
}
2018-04-06 21:12:12 -04:00
async loadSet ( set _id , suppress _log = false , tries = 0 ) {
let response , data ;
2018-04-12 02:29:43 -04:00
if ( this . experiments . getAssignment ( 'api_load' ) )
try {
fetch ( ` ${ NEW _API } /v1/set/ ${ set _id } ` ) . catch ( ( ) => { } ) ;
} catch ( err ) { /* do nothing */ }
2018-04-06 21:12:12 -04:00
try {
response = await fetch ( ` ${ API _SERVER } /v1/set/ ${ set _id } ` )
} catch ( err ) {
tries ++ ;
if ( tries < 10 )
return setTimeout ( ( ) => this . loadGlobalSets ( tries ) , 500 * tries ) ;
this . log . error ( ` Error loading data for set " ${ set _id } ". ` , err ) ;
return false ;
}
if ( ! response . ok )
return false ;
try {
data = await response . json ( ) ;
} catch ( err ) {
this . log . error ( ` Error parsing data for set " ${ set _id } ". ` , err ) ;
return false ;
}
const set = data . set ;
if ( set )
this . loadSetData ( set . id , set , suppress _log ) ;
if ( data . users )
this . loadSetUsers ( data . users ) ;
return true ;
}
loadSetUsers ( data , suppress _log = false ) {
2017-11-13 01:23:39 -05:00
for ( const set _id in data )
if ( has ( data , set _id ) ) {
const emote _set = this . emote _sets [ set _id ] ,
users = data [ set _id ] ;
2017-11-22 20:21:01 -05:00
for ( const login of users )
this . parent . getUser ( undefined , login )
. addSet ( 'ffz-global' , set _id ) ;
2017-11-13 01:23:39 -05:00
2018-04-06 21:12:12 -04:00
if ( ! suppress _log )
this . log . info ( ` Added " ${ emote _set ? emote _set . title : set _id } " emote set to ${ users . length } users. ` ) ;
2017-11-13 01:23:39 -05:00
}
}
2023-01-19 17:00:09 -05:00
processEmote ( emote , set _id ) {
if ( ! emote . id || ! emote . name || ! emote . urls )
return null ;
emote . set _id = set _id ;
emote . src = emote . urls [ 1 ] ;
emote . srcSet = ` ${ emote . urls [ 1 ] } 1x ` ;
if ( emote . urls [ 2 ] )
emote . srcSet += ` , ${ emote . urls [ 2 ] } 2x ` ;
if ( emote . urls [ 4 ] )
emote . srcSet += ` , ${ emote . urls [ 4 ] } 4x ` ;
if ( emote . urls [ 2 ] ) {
emote . can _big = true ;
emote . src2 = emote . urls [ 2 ] ;
emote . srcSet2 = ` ${ emote . urls [ 2 ] } 1x ` ;
if ( emote . urls [ 4 ] )
emote . srcSet2 += ` , ${ emote . urls [ 4 ] } 2x ` ;
}
if ( emote . animated ? . [ 1 ] ) {
emote . animSrc = emote . animated [ 1 ] ;
emote . animSrcSet = ` ${ emote . animated [ 1 ] } 1x ` ;
if ( emote . animated [ 2 ] ) {
emote . animSrcSet += ` , ${ emote . animated [ 2 ] } 2x ` ;
emote . animSrc2 = emote . animated [ 2 ] ;
emote . animSrcSet2 = ` ${ emote . animated [ 2 ] } 1x ` ;
if ( emote . animated [ 4 ] ) {
emote . animSrcSet += ` , ${ emote . animated [ 4 ] } 4x ` ;
emote . animSrcSet2 += ` , ${ emote . animated [ 4 ] } 2x ` ;
}
}
}
emote . token = {
type : 'emote' ,
id : emote . id ,
set : set _id ,
provider : 'ffz' ,
src : emote . src ,
srcSet : emote . srcSet ,
can _big : ! ! emote . urls [ 2 ] ,
src2 : emote . src2 ,
srcSet2 : emote . srcSet2 ,
animSrc : emote . animSrc ,
animSrcSet : emote . animSrcSet ,
animSrc2 : emote . animSrc2 ,
animSrcSet2 : emote . animSrcSet2 ,
text : emote . hidden ? '???' : emote . name ,
length : emote . name . length ,
height : emote . height
} ;
if ( has ( MODIFIERS , emote . id ) )
Object . assign ( emote , MODIFIERS [ emote . id ] ) ;
return emote ;
}
addEmoteToSet ( set _id , emote ) {
const set = this . emote _sets [ set _id ] ;
if ( ! set )
throw new Error ( ` Invalid emote set " ${ set _id } " ` ) ;
let processed = this . processEmote ( emote , set _id ) ;
if ( ! processed )
throw new Error ( "Invalid emote data object." ) ;
// Are we removing an existing emote?
const old _emote = set . emotes [ processed . id ] ,
old _css = old _emote && this . generateEmoteCSS ( old _emote ) ;
// Store the emote.
set . emotes [ processed . id ] = processed ;
if ( ! old _emote )
set . count ++ ;
// Now we need to update the CSS. If we had old emote CSS, then we
// will need to totally rebuild the CSS.
const style _key = ` es-- ${ set _id } ` ;
if ( old _css && old _css . length ) {
const css = [ ] ;
for ( const em of Object . values ( set . emotes ) ) {
const emote _css = this . generateEmoteCSS ( em ) ;
if ( emote _css && emote _css . length )
css . push ( emote _css ) ;
}
if ( this . style && ( css . length || set . css ) )
this . style . set ( style _key , css . join ( '' ) + ( set . css || '' ) ) ;
else if ( css . length )
set . pending _css = css . join ( '' ) ;
} else {
const emote _css = this . generateEmoteCSS ( processed ) ;
if ( emote _css && emote _css . length ) {
if ( this . style )
this . style . set ( style _key , ( this . style . get ( style _key ) || '' ) + emote _css ) ;
else
set . pending _css = ( set . pending _css || '' ) + emote _css ;
}
}
// Send a loaded event because this emote set changed.
this . emit ( ':loaded' , set _id , set ) ;
}
removeEmoteFromSet ( set _id , emote _id ) {
const set = this . emote _sets [ set _id ] ;
if ( ! set )
throw new Error ( ` Invalid emote set " ${ set _id } " ` ) ;
if ( emote _id && emote _id . id )
emote _id = emote _id . id ;
const emote = set . emotes [ emote _id ] ;
if ( ! emote )
return ;
const emote _css = this . generateEmoteCSS ( emote ) ;
const css = ( emote _css && emote _css . length ) ? [ ] : null ;
// Rebuild the emotes object to avoid gaps.
const new _emotes = { } ;
let count = 0 ;
for ( const em of Object . values ( set . emotes ) ) {
if ( em . id == emote _id )
continue ;
new _emotes [ em . id ] = em ;
count ++ ;
if ( css != null ) {
const em _css = this . generateEmoteCSS ( em ) ;
if ( em _css && em _css . length )
css . push ( em _css ) ;
}
}
set . emotes = new _emotes ;
set . count = count ;
if ( css != null ) {
const style _key = ` es-- ${ set _id } ` ;
if ( this . style && ( css . length || set . css ) )
this . style . set ( style _key , css . join ( '' ) + ( set . css || '' ) ) ;
else if ( css . length )
set . pending _css = css . join ( '' ) ;
}
// Send a loaded event because this emote set changed.
this . emit ( ':loaded' , set _id , set ) ;
}
2018-02-02 18:11:37 -05:00
loadSetData ( set _id , data , suppress _log = false ) {
2017-11-13 01:23:39 -05:00
const old _set = this . emote _sets [ set _id ] ;
if ( ! data ) {
if ( old _set )
this . emote _sets [ set _id ] = null ;
return ;
}
this . emote _sets [ set _id ] = data ;
let count = 0 ;
const ems = data . emotes || data . emoticons ,
new _ems = data . emotes = { } ,
css = [ ] ;
2018-04-06 21:12:12 -04:00
data . id = set _id ;
2017-11-13 01:23:39 -05:00
data . emoticons = undefined ;
2018-04-09 19:57:05 -04:00
const bad _emotes = [ ] ;
2017-11-13 01:23:39 -05:00
for ( const emote of ems ) {
2023-01-19 17:00:09 -05:00
let processed = this . processEmote ( emote , set _id ) ;
if ( ! processed ) {
2018-04-09 19:57:05 -04:00
bad _emotes . push ( emote ) ;
continue ;
}
2023-01-19 17:00:09 -05:00
const emote _css = this . generateEmoteCSS ( processed ) ;
2017-11-13 01:23:39 -05:00
if ( emote _css )
css . push ( emote _css ) ;
count ++ ;
2023-01-19 17:00:09 -05:00
new _ems [ processed . id ] = processed ;
2017-11-13 01:23:39 -05:00
}
2018-04-09 19:57:05 -04:00
if ( bad _emotes . length )
this . log . warn ( ` Bad Emote Data for Set # ${ set _id } ` , bad _emotes ) ;
2017-11-13 01:23:39 -05:00
data . count = count ;
if ( this . style && ( css . length || data . css ) )
this . style . set ( ` es-- ${ set _id } ` , css . join ( '' ) + ( data . css || '' ) ) ;
else if ( css . length )
data . pending _css = css . join ( '' ) ;
2018-02-02 18:11:37 -05:00
if ( ! suppress _log )
this . log . info ( ` Loaded emote set # ${ set _id } : ${ data . title } ( ${ count } emotes) ` ) ;
2017-11-13 01:23:39 -05:00
this . emit ( ':loaded' , set _id , data ) ;
2021-06-10 18:53:16 -04:00
// Don't let people endlessly load unused sets.
const refs = this . _set _refs [ set _id ] || 0 ;
if ( refs <= 0 && ! this . _set _timers [ set _id ] )
this . _set _timers [ set _id ] = setTimeout ( ( ) => this . unloadSet ( set _id ) , 5000 ) ;
2017-11-13 01:23:39 -05:00
}
2018-02-02 18:11:37 -05:00
unloadSet ( set _id , force = false , suppress _log = false ) {
2017-11-22 20:21:01 -05:00
const old _set = this . emote _sets [ set _id ] ,
count = this . _set _refs [ set _id ] || 0 ;
if ( ! old _set )
return ;
if ( count > 0 ) {
if ( ! force )
return this . log . warn ( ` Attempted to unload emote set # ${ set _id } with ${ count } users. ` ) ;
this . log . warn ( ` Unloading emote set ${ set _id } with ${ count } users. ` ) ;
}
2018-02-02 18:11:37 -05:00
if ( ! suppress _log )
this . log . info ( ` Unloaded emote set # ${ set _id } : ${ old _set . title } ` ) ;
2021-06-10 18:53:16 -04:00
if ( this . _set _timers [ set _id ] ) {
clearTimeout ( this . _set _timers [ set _id ] ) ;
this . _set _timers [ set _id ] = null ;
}
2017-11-22 20:21:01 -05:00
this . emit ( ':unloaded' , set _id , old _set ) ;
this . emote _sets [ set _id ] = null ;
}
2017-11-13 01:23:39 -05:00
// ========================================================================
// Emote CSS
// ========================================================================
generateEmoteCSS ( emote ) { // eslint-disable-line class-methods-use-this
if ( ! emote . margins && ( ! emote . modifier || ( ! emote . modifier _offset && ! emote . extra _width && ! emote . shrink _to _fit ) ) && ! emote . css )
return '' ;
let output = '' ;
if ( emote . modifier && ( emote . modifier _offset || emote . margins || emote . extra _width || emote . shrink _to _fit ) ) {
let margins = emote . modifier _offset || emote . margins || '0' ;
margins = margins . split ( /\s+/ ) . map ( x => parseInt ( x , 10 ) ) ;
if ( margins . length === 3 )
margins . push ( margins [ 1 ] ) ;
const l = margins . length ,
m _top = margins [ 0 % l ] ,
m _right = margins [ 1 % l ] ,
m _bottom = margins [ 2 % l ] ,
m _left = margins [ 3 % l ] ;
output = ` .modified-emote span .ffz-emote[data-id=" ${ emote . id } "] {
padding : $ { m _top } px $ { m _right } px $ { m _bottom } px $ { m _left } px ;
$ { emote . shrink _to _fit ? ` max-width: calc(100% - ${ 40 - m _left - m _right - ( emote . extra _width || 0 ) } px); ` : '' }
margin : 0 ! important ;
} ` ;
}
return ` ${ output } .ffz-emote[data-id=" ${ emote . id } "] {
$ { ( emote . margins && ! emote . modifier ) ? ` margin: ${ emote . margins } !important; ` : '' }
$ { emote . css || '' }
} ` ;
}
// ========================================================================
// Twitch Data Lookup
// ========================================================================
2019-10-28 01:06:02 -04:00
setTwitchEmoteSet ( emote _id , set _id ) {
2019-12-12 18:44:19 -05:00
if ( typeof emote _id === 'number' ) {
if ( isNaN ( emote _id ) || ! isFinite ( emote _id ) )
return ;
emote _id = ` ${ emote _id } ` ;
}
if ( typeof set _id === 'number' ) {
if ( isNaN ( set _id ) || ! isFinite ( set _id ) )
return ;
set _id = ` ${ set _id } ` ;
}
2019-10-28 01:06:02 -04:00
2019-12-12 18:44:19 -05:00
this . _ _twitch _emote _to _set [ emote _id ] = set _id ;
2019-10-28 01:06:02 -04:00
}
setTwitchSetChannel ( set _id , channel ) {
2019-12-12 18:44:19 -05:00
if ( typeof set _id === 'number' ) {
if ( isNaN ( set _id ) || ! isFinite ( set _id ) )
return ;
2019-10-28 01:06:02 -04:00
2019-12-12 18:44:19 -05:00
set _id = ` ${ set _id } ` ;
}
this . _ _twitch _set _to _channel [ set _id ] = channel ;
2019-10-28 01:06:02 -04:00
}
2019-10-28 14:56:55 -04:00
_getTwitchEmoteSet ( emote _id ) {
const tes = this . _ _twitch _emote _to _set ,
tsc = this . _ _twitch _set _to _channel ;
2017-11-13 01:23:39 -05:00
2019-12-12 18:44:19 -05:00
if ( typeof emote _id === 'number' ) {
if ( isNaN ( emote _id ) || ! isFinite ( emote _id ) )
return Promise . resolve ( null ) ;
emote _id = ` ${ emote _id } ` ;
}
2019-10-28 14:56:55 -04:00
2019-12-12 18:44:19 -05:00
if ( has ( tes , emote _id ) ) {
const val = tes [ emote _id ] ;
2019-10-28 14:56:55 -04:00
if ( Array . isArray ( val ) )
return new Promise ( s => val . push ( s ) ) ;
else
return Promise . resolve ( val ) ;
}
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
const apollo = this . resolve ( 'site.apollo' ) ;
if ( ! apollo ? . client )
return Promise . resolve ( null ) ;
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
return new Promise ( s => {
const promises = [ s ] ;
2019-12-12 18:44:19 -05:00
tes [ emote _id ] = promises ;
2019-08-12 22:52:57 -04:00
timeout ( apollo . client . query ( {
query : GET _EMOTE ,
variables : {
id : ` ${ emote _id } `
}
2019-10-28 14:56:55 -04:00
} ) , 2000 ) . then ( data => {
const emote = data ? . data ? . emote ;
let set _id = null ;
2019-08-12 22:52:57 -04:00
2019-10-28 14:56:55 -04:00
if ( emote ) {
2019-12-12 18:44:19 -05:00
set _id = emote . setID ;
2019-08-12 22:52:57 -04:00
2019-12-12 18:44:19 -05:00
if ( set _id && ! has ( tsc , set _id ) ) {
2019-10-28 14:56:55 -04:00
const type = determineEmoteType ( emote ) ;
2019-08-12 22:52:57 -04:00
2019-12-12 18:44:19 -05:00
tsc [ set _id ] = {
2019-10-28 14:56:55 -04:00
id : set _id ,
type ,
2019-12-12 18:44:19 -05:00
owner : emote ? . subscriptionProduct ? . owner || emote ? . owner
} ;
2019-10-28 14:56:55 -04:00
}
}
2019-08-12 22:52:57 -04:00
2019-12-12 18:44:19 -05:00
tes [ emote _id ] = set _id ;
2019-10-28 14:56:55 -04:00
for ( const fn of promises )
fn ( set _id ) ;
2019-08-12 22:52:57 -04:00
2019-10-28 14:56:55 -04:00
} ) . catch ( ( ) => {
2019-12-12 18:44:19 -05:00
tes [ emote _id ] = null ;
2019-10-28 14:56:55 -04:00
for ( const fn of promises )
fn ( null ) ;
} ) ;
} ) ;
}
2019-08-12 22:52:57 -04:00
2019-10-28 14:56:55 -04:00
getTwitchEmoteSet ( emote _id , callback ) {
const promise = this . _getTwitchEmoteSet ( emote _id ) ;
if ( callback )
promise . then ( callback ) ;
else
return promise ;
}
2019-08-12 22:52:57 -04:00
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
_getTwitchSetChannel ( set _id ) {
const tsc = this . _ _twitch _set _to _channel ;
2017-11-13 01:23:39 -05:00
2019-12-12 18:44:19 -05:00
if ( typeof set _id === 'number' ) {
if ( isNaN ( set _id ) || ! isFinite ( set _id ) )
return Promise . resolve ( null ) ;
2019-10-28 14:56:55 -04:00
2019-12-12 18:44:19 -05:00
set _id = ` ${ set _id } ` ;
}
if ( has ( tsc , set _id ) ) {
const val = tsc [ set _id ] ;
2019-10-28 14:56:55 -04:00
if ( Array . isArray ( val ) )
return new Promise ( s => val . push ( s ) ) ;
else
return Promise . resolve ( val ) ;
}
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
const apollo = this . resolve ( 'site.apollo' ) ;
if ( ! apollo ? . client )
return Promise . resolve ( null ) ;
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
return new Promise ( s => {
const promises = [ s ] ;
2019-12-12 18:44:19 -05:00
tsc [ set _id ] = promises ;
2018-07-26 19:40:53 -04:00
2019-10-28 14:56:55 -04:00
timeout ( apollo . client . query ( {
query : GET _EMOTE _SET ,
variables : {
id : ` ${ set _id } `
}
} ) , 2000 ) . then ( data => {
const set = data ? . data ? . emoteSet ;
let result = null ;
if ( set ) {
result = {
id : set _id ,
type : determineSetType ( set ) ,
owner : set . owner ? {
id : set . owner . id ,
login : set . owner . login ,
displayName : set . owner . displayName
} : null
} ;
}
2018-07-26 19:40:53 -04:00
2019-12-12 18:44:19 -05:00
tsc [ set _id ] = result ;
2019-10-28 14:56:55 -04:00
for ( const fn of promises )
fn ( result ) ;
2018-07-26 19:40:53 -04:00
2019-10-28 14:56:55 -04:00
} ) . catch ( ( ) => {
2019-12-12 18:44:19 -05:00
tsc [ set _id ] = null ;
2019-10-28 14:56:55 -04:00
for ( const fn of promises )
fn ( null ) ;
} ) ;
} ) ;
}
2018-07-26 19:40:53 -04:00
2019-10-28 14:56:55 -04:00
getTwitchSetChannel ( set _id , callback ) {
const promise = this . _getTwitchSetChannel ( set _id ) ;
if ( callback )
promise . then ( callback ) ;
else
return promise ;
}
}
2019-08-12 22:52:57 -04:00
2018-07-26 19:40:53 -04:00
2019-10-28 14:56:55 -04:00
function determineEmoteType ( emote ) {
const product = emote . subscriptionProduct ;
if ( product ) {
if ( product . id == 12658 )
return EmoteTypes . Prime ;
else if ( product . id == 324 )
return EmoteTypes . Turbo ;
2019-10-28 01:06:02 -04:00
2019-10-28 14:56:55 -04:00
// TODO: Care about Overwatch League
const owner = product . owner ;
if ( owner ) {
if ( owner . id == 139075904 || product . state === 'INACTIVE' )
return EmoteTypes . LimitedTime ;
return EmoteTypes . Subscription ;
2018-07-26 19:40:53 -04:00
}
}
2019-10-28 14:56:55 -04:00
if ( emote . setID == 300238151 )
return EmoteTypes . ChannelPoints ;
2018-07-26 19:40:53 -04:00
2020-04-03 19:30:28 -04:00
if ( emote . setID == 300374282 )
return EmoteTypes . TwoFactor ;
2019-12-12 18:44:19 -05:00
const id = parseInt ( emote . setID , 10 ) ;
if ( ! isNaN ( id ) && isFinite ( id ) && id >= 5e8 )
return EmoteTypes . BitsTier ;
2019-10-28 14:56:55 -04:00
return EmoteTypes . Global ;
}
2018-07-26 19:40:53 -04:00
2017-11-13 01:23:39 -05:00
2019-10-28 14:56:55 -04:00
function determineSetType ( set ) {
2021-06-30 15:51:37 -04:00
const id = /^\d+$/ . test ( set . id ) ? parseInt ( set . id , 10 ) : null ;
2017-11-13 01:23:39 -05:00
2021-06-30 15:51:37 -04:00
if ( id && TWITCH _GLOBAL _SETS . includes ( id ) )
2019-10-28 14:56:55 -04:00
return EmoteTypes . Global ;
2018-07-26 19:40:53 -04:00
2021-06-30 15:51:37 -04:00
if ( id && TWITCH _POINTS _SETS . includes ( id ) )
2019-10-28 14:56:55 -04:00
return EmoteTypes . ChannelPoints ;
2018-07-26 19:40:53 -04:00
2021-06-30 15:51:37 -04:00
if ( id && TWITCH _PRIME _SETS . includes ( id ) )
2019-10-28 14:56:55 -04:00
return EmoteTypes . Prime ;
2017-11-13 01:23:39 -05:00
2020-04-03 19:30:28 -04:00
if ( id == 300374282 )
return EmoteTypes . TwoFactor ;
2019-10-28 14:56:55 -04:00
const owner = set . owner ;
if ( owner ) {
if ( owner . id == 139075904 )
return EmoteTypes . LimitedTime ;
2019-10-28 01:06:02 -04:00
2019-10-28 14:56:55 -04:00
let product ;
if ( Array . isArray ( owner . subscriptionProducts ) )
for ( const prod of owner . subscriptionProducts )
if ( set . id == prod . emoteSetID ) {
product = prod ;
break ;
}
2019-10-28 01:06:02 -04:00
2019-10-28 14:56:55 -04:00
if ( product ) {
if ( product . id == 12658 )
return EmoteTypes . Prime ;
else if ( product . id == 324 )
return EmoteTypes . Turbo ;
else if ( product . state === 'INACTIVE' )
return EmoteTypes . LimitedTime ;
}
return EmoteTypes . Subscription ;
2017-11-13 01:23:39 -05:00
}
2019-10-28 14:56:55 -04:00
2019-12-12 18:44:19 -05:00
if ( id >= 5e8 )
return EmoteTypes . BitsTier ;
2019-10-28 14:56:55 -04:00
return EmoteTypes . Global ;
2017-11-13 01:23:39 -05:00
}