diff --git a/package.json b/package.json index 427fa6e0..693e358f 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "frankerfacez", "author": "Dan Salvato LLC", - "version": "4.20.83", + "version": "4.20.84", "description": "FrankerFaceZ is a Twitch enhancement suite.", "private": true, "license": "Apache-2.0", diff --git a/src/modules/chat/badges.jsx b/src/modules/chat/badges.jsx index 19c290b9..56eb4f59 100644 --- a/src/modules/chat/badges.jsx +++ b/src/modules/chat/badges.jsx @@ -7,7 +7,7 @@ import {NEW_API, SERVER, API_SERVER, IS_WEBKIT, IS_FIREFOX, WEBKIT_CSS as WEBKIT} from 'utilities/constants'; import {createElement, ManagedStyle} from 'utilities/dom'; -import {has, maybe_call} from 'utilities/object'; +import {has, maybe_call, SourcedSet} from 'utilities/object'; import Module from 'utilities/module'; import { ColorAdjuster } from 'src/utilities/color'; @@ -181,11 +181,16 @@ export default class Badges extends Module { this.style = new ManagedStyle('badges'); + // Bulk data structure for badges applied to a lot of users. + // This lets us avoid allocating lots of individual user + // objects when we don't need to do so. + this.bulk = new Map; + // 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.supporter_id = null; + //this.supporters = new Set; this.badges = {}; this.twitch_badges = {}; @@ -340,17 +345,23 @@ export default class Badges extends Module { if ( include_addons ) for(const key in this.badges) if ( has(this.badges, key) ) { - const badge = this.badges[key], - image = badge.urls ? (badge.urls[2] || badge.urls[1]) : badge.image; - + const badge = this.badges[key]; if ( badge.no_visibility ) continue; + let image = badge.urls ? (badge.urls[2] || badge.urls[1]) : badge.image, + color = badge.color || 'transparent'; + + if ( ! badge.addon ) { + image = `//cdn.frankerfacez.com/badge/${badge.id}/2/rounded`; + color = 'transparent'; + } + (badge.addon ? addon : ffz).push({ id: key, provider: 'ffz', name: badge.title, - color: badge.color || 'transparent', + color, image, styleImage: `url("${image}")` }); @@ -539,6 +550,11 @@ export default class Badges extends Module { render(msg, createElement, skip_hide = false) { // eslint-disable-line class-methods-use-this + if ( ! msg.badges && ! msg.ffz_badges ) + return null; + + // TODO: A lot of this can be cached + const hidden_badges = skip_hide ? {} : (this.parent.context.get('chat.badges.hidden') || {}), badge_style = this.parent.context.get('chat.badges.style'), custom_mod = this.parent.context.get('chat.badges.custom-mod'), @@ -556,14 +572,14 @@ export default class Badges extends Module { twitch_badges = msg.badges || {}, dynamic_data = msg.badgeDynamicData || {}, - user = msg.user || {}, - user_id = user.id, - user_login = user.login, + //user = msg.user || {}, + //user_id = user.id, + //user_login = user.login, room_id = msg.roomID, room_login = msg.roomLogin, room = this.parent.getRoom(room_id, room_login, true), - badges = this.getBadges(user_id, user_login, room_id, room_login); + badges = msg.ffz_badges; // this.getBadges(user_id, user_login, room_id, room_login); let last_slot = 50, slot; @@ -616,105 +632,107 @@ export default class Badges extends Module { }; } - const handled_ids = new Set; + if ( Array.isArray(badges) ) { + const handled_ids = new Set; - for(const badge of badges) - if ( badge && badge.id != null ) { - if ( handled_ids.has(badge.id) ) - continue; - - handled_ids.add(badge.id); - - const full_badge = this.badges[badge.id] || {}, - is_hidden = hidden_badges[badge.id]; - - if ( is_hidden || (is_hidden == null && (full_badge.addon ? addon_hidden : ffz_hidden)) ) - continue; - - const slot = has(badge, 'slot') ? badge.slot : full_badge.slot, - old_badge = slotted[slot], - urls = badge.urls || (badge.image ? {1: badge.image} : null), - color = badge.color || full_badge.color || 'transparent', - no_invert = badge.no_invert, - masked = color !== 'transparent' && is_mask, - - bu = (urls || full_badge.urls || {1: full_badge.image}), - bd = { - provider: 'ffz', - image: bu[4] || bu[2] || bu[1], - color: badge.color || full_badge.color, - title: badge.title || full_badge.title, - }; - - // Hacky nonsense. - if ( ! full_badge.addon ) { - bd.image = `//cdn.frankerfacez.com/badge/${badge.id}/4/rounded`; - bd.color = null; - } - - let style; - - if ( old_badge ) { - old_badge.badges.push(bd); - - const replaces = has(badge, 'replaces') ? badge.replaces : full_badge.replaces, - replaces_type = badge.replaces_type || full_badge.replaces_type; - if ( replaces && (!replaces_type || replaces_type === old_badge.id) ) { - old_badge.replaced = badge.id; - old_badge.content = badge.content || full_badge.content || old_badge.content; - } else + for(const badge of badges) + if ( badge && badge.id != null ) { + if ( handled_ids.has(badge.id) ) continue; - style = old_badge.props.style; + handled_ids.add(badge.id); - } else if ( slot == null ) - continue; + const full_badge = this.badges[badge.id] || {}, + is_hidden = hidden_badges[badge.id]; - else { - style = {}; - const props = { - className: 'ffz-tooltip ffz-badge', - 'data-tooltip-type': 'badge', - 'data-provider': 'ffz', - 'data-badge': badge.id, - style - }; + if ( is_hidden || (is_hidden == null && (full_badge.addon ? addon_hidden : ffz_hidden)) ) + continue; - slotted[slot] = { - id: badge.id, - props, - badges: [bd], - content: badge.content || full_badge.content + const slot = has(badge, 'slot') ? badge.slot : full_badge.slot, + old_badge = slotted[slot], + urls = badge.urls || (badge.image ? {1: badge.image} : null), + color = badge.color || full_badge.color || 'transparent', + no_invert = badge.no_invert, + masked = color !== 'transparent' && is_mask, + + bu = (urls || full_badge.urls || {1: full_badge.image}), + bd = { + provider: 'ffz', + image: bu[4] || bu[2] || bu[1], + color: badge.color || full_badge.color, + title: badge.title || full_badge.title, + }; + + // Hacky nonsense. + if ( ! full_badge.addon ) { + bd.image = `//cdn.frankerfacez.com/badge/${badge.id}/4/rounded`; + bd.color = null; + } + + let style; + + if ( old_badge ) { + old_badge.badges.push(bd); + + const replaces = has(badge, 'replaces') ? badge.replaces : full_badge.replaces, + replaces_type = badge.replaces_type || full_badge.replaces_type; + if ( replaces && (!replaces_type || replaces_type === old_badge.id) ) { + old_badge.replaced = badge.id; + old_badge.content = badge.content || full_badge.content || old_badge.content; + } else + continue; + + style = old_badge.props.style; + + } else if ( slot == null ) + continue; + + else { + style = {}; + const props = { + className: 'ffz-tooltip ffz-badge', + 'data-tooltip-type': 'badge', + 'data-provider': 'ffz', + 'data-badge': badge.id, + style + }; + + slotted[slot] = { + id: badge.id, + props, + badges: [bd], + content: badge.content || full_badge.content + } + } + + if (no_invert) { + slotted[slot].full_size = true; + slotted[slot].no_invert = true; + + style.background = 'unset'; + style.backgroundSize = 'unset'; + style[CSS_MASK_IMAGE] = 'unset'; + } + + if ( (has_image || color === 'transparent') && urls ) { + const image = `url("${urls[1]}")`; + let image_set; + if ( urls[2] || urls[4] ) + image_set = `${WEBKIT}image-set(${image} 1x${urls[2] ? `, url("${urls[2]}") 2x` : ''}${urls[4] ? `, url("${urls[4]}") 4x` : ''})`; + + style[masked && !no_invert ? CSS_MASK_IMAGE : 'backgroundImage'] = image; + if ( image_set ) + style[masked && !no_invert ? CSS_MASK_IMAGE : 'backgroundImage'] = image_set; + } + + if ( is_colored && badge.color ) { + if ( masked && !no_invert ) + style.backgroundImage = `linear-gradient(${badge.color},${badge.color})`; + else + style.backgroundColor = badge.color; } } - - if (no_invert) { - slotted[slot].full_size = true; - slotted[slot].no_invert = true; - - style.background = 'unset'; - style.backgroundSize = 'unset'; - style[CSS_MASK_IMAGE] = 'unset'; - } - - if ( (has_image || color === 'transparent') && urls ) { - const image = `url("${urls[1]}")`; - let image_set; - if ( urls[2] || urls[4] ) - image_set = `${WEBKIT}image-set(${image} 1x${urls[2] ? `, url("${urls[2]}") 2x` : ''}${urls[4] ? `, url("${urls[4]}") 4x` : ''})`; - - style[masked && !no_invert ? CSS_MASK_IMAGE : 'backgroundImage'] = image; - if ( image_set ) - style[masked && !no_invert ? CSS_MASK_IMAGE : 'backgroundImage'] = image_set; - } - - if ( is_colored && badge.color ) { - if ( masked && !no_invert ) - style.backgroundImage = `linear-gradient(${badge.color},${badge.color})`; - else - style.backgroundColor = badge.color; - } - } + } for(const slot in slotted) if ( has(slotted, slot) ) { @@ -785,19 +803,56 @@ export default class Badges extends Module { getBadges(user_id, user_login, room_id, room_login) { const room = this.parent.getRoom(room_id, room_login, true), - global_user = this.parent.getUser(user_id, user_login, true), - room_user = room && room.getUser(user_id, user_login, true); + global_user = this.parent.getUser(user_id, user_login, true); - const out = (global_user ? global_user.badges._cache : []).concat( - room_user ? room_user.badges._cache : []); + if ( global_user ) { + user_id = user_id ?? global_user.id; + user_login = user_login ?? global_user.login; + } - if ( this.supporter_id && this.supporters.has(`${user_id}`) ) - out.push({id: this.supporter_id}); + const room_user = room && room.getUser(user_id, user_login, true); + + const out = (global_user?.badges ? global_user.badges._cache : []).concat( + room_user?.badges ? room_user.badges._cache : []); + + if ( this.bulk.size ) { + const str_user = String(user_id); + for(const [badge_id, users] of this.bulk) { + if ( users?._cache.has(str_user) ) + out.push({id: badge_id}); + } + } return out; } + setBulk(source, badge_id, entries) { + let set = this.bulk.get(badge_id); + if ( ! set ) + this.bulk.set(badge_id, set = new SourcedSet(true)); + + set.set(source, entries); + } + + deleteBulk(source, badge_id) { + const set = this.bulk.get(badge_id); + if ( set ) + set.delete(source); + } + + extendBulk(source, badge_id, entries) { + let set = this.bulk.get(badge_id); + if ( ! set ) + this.bulk.set(badge_id, set = new SourcedSet(true)); + + if ( ! Array.isArray(entries) ) + entries = [entries]; + + set.extend(source, ...entries); + } + + async loadGlobalBadges(tries = 0) { let response, data; @@ -839,15 +894,17 @@ export default class Badges extends Module { if ( data.users ) for(const badge_id in data.users) if ( has(data.users, badge_id) ) { - const badge = this.badges[badge_id]; + const badge = this.badges[badge_id], + name = badge?.name; let c = 0; - if ( badge?.name === 'supporter' ) { - this.supporter_id = badge_id; + if ( name === 'supporter' || name === 'bot' ) { + this.setBulk('ffz-global', badge_id, data.users[badge_id].map(x => String(x))); + /*this.supporter_id = badge_id; for(const user_id of data.users[badge_id]) - this.supporters.add(`${user_id}`); + this.supporters.add(`${user_id}`);*/ - c = this.supporters.size; + c = data.users[badge_id].length; // this.supporters.size; } else for(const user_id of data.users[badge_id]) { const user = this.parent.getUser(user_id, undefined); diff --git a/src/modules/chat/emotes.js b/src/modules/chat/emotes.js index 9ad40159..3ff245f4 100644 --- a/src/modules/chat/emotes.js +++ b/src/modules/chat/emotes.js @@ -506,9 +506,9 @@ export default class Emotes extends Module { room_user = room && room.getUser(user_id, user_login, true), user = this.parent.getUser(user_id, user_login, true); - return (user ? user.emote_sets._cache : []).concat( - room_user ? room_user.emote_sets._cache : [], - room ? room.emote_sets._cache : [], + 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 : [], this.default_sets._cache ); } @@ -519,7 +519,7 @@ export default class Emotes extends Module { } _withSources(out, seen, emote_sets) { // eslint-disable-line class-methods-use-this - if ( ! emote_sets._sources ) + if ( ! emote_sets?._sources ) return; for(const [provider, data] of emote_sets._sources) @@ -560,8 +560,11 @@ export default class Emotes extends Module { if ( ! room ) return []; - if ( ! room_user ) - return room.emote_sets._cache; + if ( ! room_user?.emote_sets ) + return room.emote_sets ? room.emote_sets._cache : []; + + else if ( ! room.emote_sets ) + return room_user.emote_sets._cache; return room_user.emote_sets._cache.concat(room.emote_sets._cache); } @@ -589,7 +592,7 @@ export default class Emotes extends Module { getGlobalSetIDs(user_id, user_login) { const user = this.parent.getUser(user_id, user_login, true); - if ( ! user ) + if ( ! user?.emote_sets ) return this.default_sets._cache; return user.emote_sets._cache.concat(this.default_sets._cache); diff --git a/src/modules/chat/index.js b/src/modules/chat/index.js index 758d560e..4b88f9bb 100644 --- a/src/modules/chat/index.js +++ b/src/modules/chat/index.js @@ -504,7 +504,7 @@ export default class Chat extends Module { path: 'Chat > Filtering > Highlight >> Badges', component: 'badge-highlighting', colored: true, - data: () => this.badges.getSettingsBadges() + data: () => this.badges.getSettingsBadges(true) } }); @@ -540,7 +540,7 @@ export default class Chat extends Module { path: 'Chat > Filtering > Block >> Badges @{"description": "**Note:** This section is for filtering messages out of chat from users with specific badges. If you wish to hide a badge, go to [Chat > Badges >> Visibility](~chat.badges.tabs.visibility)."}', component: 'badge-highlighting', removable: true, - data: () => this.badges.getSettingsBadges() + data: () => this.badges.getSettingsBadges(true) } }); @@ -687,13 +687,31 @@ export default class Chat extends Module { if ( ! val || ! val.length ) return null; - const out = [ - [[], []], - [[], []] + const data = [ + [ // no-remove + [ // sensitive + [], [] // word + ], + [ // intensitive + [], [] + ] + ], + [ // remove + [ // sensitive + [], [] // word + ], + [ // intensiitve + [], [] + ] + ] ]; + let had_remove = false, + had_non = false; + for(const item of val) { const t = item.t, + sensitive = item.s, word = has(item, 'w') ? item.w : t !== 'raw'; let v = item.v; @@ -706,15 +724,21 @@ export default class Chat extends Module { if ( ! v || ! v.length ) continue; - out[item.remove ? 1 : 0][word ? 0 : 1].push(v); + if ( item.remove ) + had_remove = true; + else + had_non = true; + + data[item.remove ? 1 : 0][sensitive ? 0 : 1][word ? 0 : 1].push(v); } - return out.map(data => { - if ( data[0].length ) - data[1].push(`(^|.*?${SEPARATORS})(?:${data[0].join('|')})(?=$|${SEPARATORS})`); + if ( ! had_remove && ! had_non ) + return null; - return data[1].length ? new RegExp(data[1].join('|'), 'gi') : null; - }); + return { + remove: had_remove ? formatTerms(data[1]) : null, + non: had_non ? formatTerms(data[0]) : null + }; } }); @@ -936,6 +960,13 @@ export default class Chat extends Module { path: 'Chat > Appearance >> Emotes', title: 'Animated Emotes', + default(ctx) { + const temp = ctx.get('ffzap.betterttv.gif_emoticons_mode'); + if ( temp == null ) + return ctx.get('context.bttv.gifs') ? 1 : 0; + return temp === 2 ? 1 : 0; + }, + getExtraTerms: () => GIF_TERMS, description: 'This controls whether or not animated emotes are allowed to play in chat. When this is `Disabled`, emotes will appear as static images. Setting this to `Enable on Hover` may cause performance issues.', @@ -966,13 +997,7 @@ export default class Chat extends Module { }); this.settings.add('chat.bits.animated', { - requires: ['chat.emotes.animated'], - default: null, - process(ctx, val) { - if ( val == null ) - val = ctx.get('chat.emotes.animated') ? true : false - }, - + default: true, ui: { path: 'Chat > Bits and Cheering >> Appearance', title: 'Display animated cheers.', @@ -1395,6 +1420,9 @@ export default class Chat extends Module { if ( msg.deletedAt !== undefined ) msg.deleted = !!msg.deletedAt; + // Addon Badges + msg.ffz_badges = this.badges.getBadges(user.id, user.login, msg.roomID, msg.roomLogin); + return msg; } @@ -1634,7 +1662,7 @@ export default class Chat extends Module { } - tokenizeMessage(msg, user) { + tokenizeMessage(msg, user, haltable = false) { if ( msg.content && ! msg.message ) msg.message = msg.content.text; @@ -1646,8 +1674,13 @@ export default class Chat extends Module { let tokens = [{type: 'text', text: msg.message}]; - for(const tokenizer of this.__tokenizers) - tokens = tokenizer.process.call(this, tokens, msg, user); + for(const tokenizer of this.__tokenizers) { + tokens = tokenizer.process.call(this, tokens, msg, user, haltable); + if ( haltable && msg.ffz_halt_tokens ) { + msg.ffz_halt_tokens = undefined; + break; + } + } return tokens; } diff --git a/src/modules/chat/room.js b/src/modules/chat/room.js index d77f53e2..86886574 100644 --- a/src/modules/chat/room.js +++ b/src/modules/chat/room.js @@ -20,7 +20,7 @@ export default class Room { this.refs = new Set; this.style = new ManagedStyle(`room--${login}`); - this.emote_sets = new SourcedSet; + this.emote_sets = null; // new SourcedSet; this.badges = null; this.users = {}; this.user_ids = {}; @@ -305,9 +305,11 @@ export default class Room { this.data = d; - if ( d.set ) + if ( d.set ) { + if ( ! this.emote_sets ) + this.emote_sets = new SourcedSet; this.emote_sets.set('main', d.set); - else + } else if ( this.emote_sets ) this.emote_sets.delete('main'); @@ -342,6 +344,9 @@ export default class Room { if ( this.destroyed ) return; + if ( ! this.emote_sets ) + this.emote_sets = new SourcedSet; + let changed = false; if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) { this.emote_sets.push(provider, set_id); @@ -357,7 +362,7 @@ export default class Room { } removeSet(provider, set_id) { - if ( this.destroyed ) + if ( this.destroyed || ! this.emote_sets ) return; if ( this.emote_sets.sourceIncludes(provider, set_id) ) { diff --git a/src/modules/chat/tokenizers.jsx b/src/modules/chat/tokenizers.jsx index 027968ef..28bca406 100644 --- a/src/modules/chat/tokenizers.jsx +++ b/src/modules/chat/tokenizers.jsx @@ -464,7 +464,7 @@ export const BlockedUsers = { type: 'user_block', priority: 100, - process(tokens, msg, user) { + process(tokens, msg, user, haltable) { if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') ) return tokens; @@ -476,39 +476,69 @@ export const BlockedUsers = { if ( regexes[1] && (regexes[1].test(u.login) || regexes[1].test(u.displayName)) ) { msg.deleted = true; msg.ffz_removed = true; - } + if ( haltable ) + msg.ffz_halt_tokens = true; - if ( ! msg.deleted && regexes[0] && (regexes[0].test(u.login) || regexes[0].test(u.displayName)) ) + } else if ( ! msg.deleted && regexes[0] && (regexes[0].test(u.login) || regexes[0].test(u.displayName)) ) msg.deleted = true; return tokens; } } -export const BadgeHighlights = { - type: 'badge_highlight', +function getBadgeIDs(msg) { + let keys = msg.badges ? Object.keys(msg.badges) : null; + if ( ! msg.ffz_badges ) + return keys; + + if ( ! keys ) + keys = []; + + for(const badge of msg.ffz_badges) + if ( badge?.id ) + keys.push(badge.id); + + return keys; +} + +export const BadgeStuff = { + type: 'badge_stuff', priority: 80, - process(tokens, msg, user) { + process(tokens, msg, user, haltable) { if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') ) return tokens; - const badges = msg.badges; - if ( ! badges ) + const colors = this.context.get('chat.filtering.highlight-basic-badges--colors'), + list = this.context.get('chat.filtering.highlight-basic-badges-blocked--list'); + + if ( ! colors && ! list ) return tokens; - const colors = this.context.get('chat.filtering.highlight-basic-badges--colors'); - if ( ! colors || ! colors.size ) + const keys = getBadgeIDs(msg); + if ( ! keys || ! keys.length ) return tokens; - for(const badge of Object.keys(badges)) { - if ( colors.has(badge) ) { + for(const badge of keys) { + if ( list && list[1].includes(badge) ) { + msg.deleted = true; + msg.ffz_removed = true; + if ( haltable ) + msg.ffz_halt_tokens = true; + return tokens; + } + + if ( list && ! msg.deleted && list[0].includes(badge) ) + msg.deleted = true; + + if ( colors && colors.has(badge) ) { const color = colors.get(badge); (msg.highlights = (msg.highlights || new Set())).add('badge'); msg.mentioned = true; if ( color ) { msg.mention_color = color; - return tokens; + if ( ! list ) + return tokens; } } } @@ -517,25 +547,27 @@ export const BadgeHighlights = { } } -export const BlockedBadges = { +/*export const BlockedBadges = { type: 'badge_block', priority: 100, - process(tokens, msg, user) { + process(tokens, msg, user, haltable) { if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') ) return tokens; - const badges = msg.badges; - if ( ! badges ) - return tokens; - const list = this.context.get('chat.filtering.highlight-basic-badges-blocked--list'); if ( ! list || (! list[0].length && ! list[1].length) ) return tokens; - for(const badge of Object.keys(badges)) { + const keys = getBadgeIDs(msg); + if ( ! keys || ! keys.length ) + return tokens; + + for(const badge of keys) { if ( list[1].includes(badge) ) { msg.deleted = true; msg.ffz_removed = true; + if ( haltable ) + msg.ffz_halt_tokens = true; return tokens; } @@ -545,7 +577,7 @@ export const BlockedBadges = { return tokens; } -} +}*/ export const CustomHighlights = { type: 'highlight', @@ -649,7 +681,7 @@ export const CustomHighlights = { } -function blocked_process(tokens, msg, regex, do_remove) { +function blocked_process(tokens, msg, regexes, do_remove, haltable) { const out = []; for(const token of tokens) { if ( token.type !== 'text' ) { @@ -657,11 +689,23 @@ function blocked_process(tokens, msg, regex, do_remove) { continue; } - regex.lastIndex = 0; const text = token.text; let idx = 0, match; - while((match = regex.exec(text))) { + while(idx < text.length) { + if ( regexes[0] ) + regexes[0].lastIndex = idx; + if ( regexes[1] ) + regexes[1].lastIndex = idx; + + match = regexes[0] ? regexes[0].exec(text) : null; + const second = regexes[1] ? regexes[1].exec(text) : null; + if ( second && (! match || match.index > second.index) ) + match = second; + + if ( ! match ) + break; + const raw_nix = match.index, offset = match[1] ? match[1].length : 0, nix = raw_nix + offset; @@ -669,15 +713,18 @@ function blocked_process(tokens, msg, regex, do_remove) { if ( idx !== nix ) out.push({type: 'text', text: text.slice(idx, nix)}); + if ( do_remove ) { + msg.ffz_removed = true; + if ( haltable ) + return tokens; + } + out.push({ type: 'blocked', text: match[0].slice(offset) }); - if ( do_remove ) - msg.ffz_removed = true; - - idx = raw_nix + match[0].length; + idx = raw_nix + match[0].length } if ( idx < text.length ) @@ -715,7 +762,7 @@ export const BlockedTerms = { ] }, - process(tokens, msg, user) { + process(tokens, msg, user, haltable) { if ( ! tokens || ! tokens.length ) return tokens; @@ -726,11 +773,16 @@ export const BlockedTerms = { if ( ! regexes ) return tokens; - if ( regexes[0] ) - tokens = blocked_process(tokens, msg, regexes[0], false); + if ( regexes.remove ) { + tokens = blocked_process(tokens, msg, regexes.remove, true, haltable); + if ( haltable && msg.ffz_removed ) { + msg.ffz_halt_tokens = true; + return tokens; + } + } - if ( regexes[1] ) - tokens = blocked_process(tokens, msg, regexes[1], true); + if ( regexes.non ) + tokens = blocked_process(tokens, msg, regexes.non, false, haltable); return tokens; } @@ -792,7 +844,7 @@ export const AutomoddedTerms = { ]; }, - process(tokens, msg) { + process(tokens, msg, user, haltable) { if ( ! tokens || ! tokens.length || ! msg.flags || ! Array.isArray(msg.flags.list) ) return tokens; @@ -827,6 +879,8 @@ export const AutomoddedTerms = { if ( remove ) { msg.ffz_removed = true; + if ( haltable ) + msg.ffz_halt_tokens = true; return tokens; } diff --git a/src/modules/chat/user.js b/src/modules/chat/user.js index ba4318ac..6f71b40c 100644 --- a/src/modules/chat/user.js +++ b/src/modules/chat/user.js @@ -11,8 +11,8 @@ export default class User { this.manager = manager; this.room = room; - this.emote_sets = new SourcedSet; - this.badges = new SourcedSet; + this.emote_sets = null; //new SourcedSet; + this.badges = null; // new SourcedSet; this._id = id; this.login = login; @@ -31,6 +31,9 @@ export default class User { this.emote_sets = null; } + if ( this.badges ) + this.badges = null; + const parent = this.room || this.manager; if ( parent ) { @@ -100,11 +103,17 @@ export default class User { // ======================================================================== addBadge(provider, badge_id, data) { + if ( this.destroyed ) + return; + if ( data ) data.id = badge_id; else data = {id: badge_id}; + if ( ! this.badges ) + this.badges = new SourcedSet; + if ( this.badges.has(provider) ) for(const old_b of this.badges.get(provider)) if ( old_b.id == badge_id ) { @@ -119,6 +128,9 @@ export default class User { getBadge(badge_id) { + if ( ! this.badges ) + return null; + for(const badge of this.badges._cache) if ( badge.id == badge_id ) return badge; @@ -126,7 +138,7 @@ export default class User { removeBadge(provider, badge_id) { - if ( ! this.badges.has(provider) ) + if ( ! this.badges || ! this.badges.has(provider) ) return false; for(const old_b of this.badges.get(provider)) @@ -147,6 +159,9 @@ export default class User { if ( this.destroyed ) return; + if ( ! this.emote_sets ) + this.emote_sets = new SourcedSet; + if ( ! this.emote_sets.sourceIncludes(provider, set_id) ) { this.emote_sets.push(provider, set_id); this.manager.emotes.refSet(set_id); @@ -156,7 +171,7 @@ export default class User { } removeSet(provider, set_id) { - if ( this.destroyed ) + if ( this.destroyed || ! this.emote_sets ) return; if ( this.emote_sets.sourceIncludes(provider, set_id) ) { diff --git a/src/modules/main_menu/components/badge-term-editor.vue b/src/modules/main_menu/components/badge-term-editor.vue index 6585b1aa..5f45977b 100644 --- a/src/modules/main_menu/components/badge-term-editor.vue +++ b/src/modules/main_menu/components/badge-term-editor.vue @@ -3,8 +3,9 @@
-
+
+ + + + +
+ {{ t('setting.terms.remove.on', 'Remove matching messages from chat.') }} +
+
+