mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-16 18:06:55 +00:00
4.70.0
* Added: Option to prevent Pinned Messages from being displayed. Closes #1470 * Removed: Setting to allow the Golden Kappa Train to appear. This wasn't working correctly, so nothing of value has been lost. * Fixed: Blocking chat users by username worked inconsistently. * Fixed: Hiding channels in the directory by title worked inconsistently. Closes #1473 * API Changed: The `chat:get-tab-commands` event now has a `channel` object with the ID and login of the current channel, as well as a reference to the relevant input component. * API Added: The `chat` module now has `addTabCommandPrefix(prefix: string | string[])` and `removeTabCommandPrefix(prefix: string | string[])` methods. By default, tab-completion for chat commands only triggers with the `/` and `!` prefix for performance reasons. This can be used to add additional prefix characters.
This commit is contained in:
parent
8807e09ea3
commit
10d35468a9
6 changed files with 104 additions and 33 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "frankerfacez",
|
"name": "frankerfacez",
|
||||||
"author": "Dan Salvato LLC",
|
"author": "Dan Salvato LLC",
|
||||||
"version": "4.69.0",
|
"version": "4.70.0",
|
||||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|
|
@ -11,7 +11,7 @@ import Module, { buildAddonProxy } from 'utilities/module';
|
||||||
import {Color} from 'utilities/color';
|
import {Color} from 'utilities/color';
|
||||||
import {createElement, ManagedStyle} from 'utilities/dom';
|
import {createElement, ManagedStyle} from 'utilities/dom';
|
||||||
import {getFontsList} from 'utilities/fonts';
|
import {getFontsList} from 'utilities/fonts';
|
||||||
import {timeout, has, addWordSeparators, glob_to_regex, escape_regex, split_chars, makeAddonIdChecker, deep_copy} from 'utilities/object';
|
import {timeout, has, addWordSeparators, glob_to_regex, escape_regex, split_chars, makeAddonIdChecker, deep_copy, SourcedSet} from 'utilities/object';
|
||||||
|
|
||||||
import Badges from './badges';
|
import Badges from './badges';
|
||||||
import Emotes from './emotes';
|
import Emotes from './emotes';
|
||||||
|
@ -93,6 +93,9 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
this.context = this.settings.context({});
|
this.context = this.settings.context({});
|
||||||
|
|
||||||
|
this.CommandPrefixes = new SourcedSet(true);
|
||||||
|
this.CommandPrefixes.set('ffz', ['/', '!']);
|
||||||
|
|
||||||
this.rooms = {};
|
this.rooms = {};
|
||||||
this.users = {};
|
this.users = {};
|
||||||
|
|
||||||
|
@ -614,7 +617,7 @@ export default class Chat extends Module {
|
||||||
if ( ! data.length )
|
if ( ! data.length )
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new RegExp(`^(?:${data.join('|')})$`, 'gi');
|
return new RegExp(`^(?:${data.join('|')})$`, 'i');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1414,6 +1417,24 @@ export default class Chat extends Module {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overrides.addTabCommandPrefix = (prefix, provider = null) => {
|
||||||
|
if ( provider == null )
|
||||||
|
provider = addon_id;
|
||||||
|
if ( is_dev && provider !== addon_id )
|
||||||
|
module.log.warn('[DEV-CHECK] Used addTabCommandPrefix with incorrect provider.');
|
||||||
|
|
||||||
|
return this.addTabCommandPrefix(prefix, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
overrides.removeTabCommandPrefix = (prefix, provider = null) => {
|
||||||
|
if ( provider == null )
|
||||||
|
provider = addon_id;
|
||||||
|
if ( is_dev && provider !== addon_id )
|
||||||
|
module.log.warn('[DEV-CHECK] Used removeTabCommandPrefix with incorrect provider.');
|
||||||
|
|
||||||
|
return this.removeTabCommandPrefix(prefix, provider);
|
||||||
|
}
|
||||||
|
|
||||||
overrides.addTokenizer = tokenizer => {
|
overrides.addTokenizer = tokenizer => {
|
||||||
if ( tokenizer )
|
if ( tokenizer )
|
||||||
tokenizer.__source = addon_id;
|
tokenizer.__source = addon_id;
|
||||||
|
@ -1580,6 +1601,8 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.CommandPrefixes.delete(addon_id);
|
||||||
|
|
||||||
for(const item of this.iterateAllRoomsAndUsers())
|
for(const item of this.iterateAllRoomsAndUsers())
|
||||||
removed += item._unloadAddon(addon_id) ?? 0;
|
removed += item._unloadAddon(addon_id) ?? 0;
|
||||||
|
|
||||||
|
@ -2305,6 +2328,26 @@ export default class Chat extends Module {
|
||||||
return Object.values(this._hl_reasons);
|
return Object.values(this._hl_reasons);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addTabCommandPrefix(prefix, source = 'ffz') {
|
||||||
|
if ( ! Array.isArray(prefix) )
|
||||||
|
prefix = [prefix];
|
||||||
|
|
||||||
|
for(const item of prefix) {
|
||||||
|
if ( typeof item !== 'string' || item.length !== 1 )
|
||||||
|
throw new Error('Invalid command prefix. Must be string of length 1.');
|
||||||
|
|
||||||
|
this.CommandPrefixes.push(source, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeTabCommandPrefix(prefix, source = 'ffz') {
|
||||||
|
if ( ! Array.isArray(prefix) )
|
||||||
|
prefix = [prefix];
|
||||||
|
|
||||||
|
for(const item of prefix)
|
||||||
|
this.CommandPrefixes.remove(source, item);
|
||||||
|
}
|
||||||
|
|
||||||
addTokenizer(tokenizer) {
|
addTokenizer(tokenizer) {
|
||||||
const type = tokenizer.type;
|
const type = tokenizer.type;
|
||||||
if ( has(this.tokenizers, type) ) {
|
if ( has(this.tokenizers, type) ) {
|
||||||
|
|
|
@ -461,7 +461,7 @@ export default class ChatHook extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('chat.banners.kappa-train', {
|
/*this.settings.add('chat.banners.kappa-train', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
path: 'Chat > Appearance >> Community',
|
path: 'Chat > Appearance >> Community',
|
||||||
|
@ -469,6 +469,15 @@ export default class ChatHook extends Module {
|
||||||
description: '**Note**: This setting is currently theoretical and may not work, or may cause non-Kappa hype trains to appear. Due to the infrequent nature of hype trains, and especially the golden kappa hype train, it is very hard to test.',
|
description: '**Note**: This setting is currently theoretical and may not work, or may cause non-Kappa hype trains to appear. Due to the infrequent nature of hype trains, and especially the golden kappa hype train, it is very hard to test.',
|
||||||
component: 'setting-check-box'
|
component: 'setting-check-box'
|
||||||
}
|
}
|
||||||
|
});*/
|
||||||
|
|
||||||
|
this.settings.add('chat.banners.pinned-message', {
|
||||||
|
default: true,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Appearance >> Community',
|
||||||
|
title: 'Allow Pinned Messages to be displayed in chat.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.settings.add('chat.banners.drops', {
|
this.settings.add('chat.banners.drops', {
|
||||||
|
@ -1042,6 +1051,7 @@ export default class ChatHook extends Module {
|
||||||
this.chat.context.on('changed:chat.banners.polls', this.cleanHighlights, this);
|
this.chat.context.on('changed:chat.banners.polls', this.cleanHighlights, this);
|
||||||
this.chat.context.on('changed:chat.banners.prediction', this.cleanHighlights, this);
|
this.chat.context.on('changed:chat.banners.prediction', this.cleanHighlights, this);
|
||||||
this.chat.context.on('changed:chat.banners.drops', this.cleanHighlights, this);
|
this.chat.context.on('changed:chat.banners.drops', this.cleanHighlights, this);
|
||||||
|
this.chat.context.on('changed:chat.banners.pinned-message', this.cleanHighlights, this);
|
||||||
|
|
||||||
this.chat.context.on('changed:chat.disable-handling', this.updateDisableHandling, this);
|
this.chat.context.on('changed:chat.disable-handling', this.updateDisableHandling, this);
|
||||||
|
|
||||||
|
@ -1722,6 +1732,7 @@ export default class ChatHook extends Module {
|
||||||
'hype_train': this.chat.context.get('chat.banners.hype-train'),
|
'hype_train': this.chat.context.get('chat.banners.hype-train'),
|
||||||
'prediction': this.chat.context.get('chat.banners.prediction'),
|
'prediction': this.chat.context.get('chat.banners.prediction'),
|
||||||
'poll': this.chat.context.get('chat.banners.polls'),
|
'poll': this.chat.context.get('chat.banners.polls'),
|
||||||
|
'pinned_chat': this.chat.context.get('chat.banners.pinned-message'),
|
||||||
'mw-drop-available': this.chat.context.get('chat.banners.drops')
|
'mw-drop-available': this.chat.context.get('chat.banners.drops')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1736,8 +1747,8 @@ export default class ChatHook extends Module {
|
||||||
const type = entry.event.type;
|
const type = entry.event.type;
|
||||||
if ( type && has(types, type) && ! types[type] ) {
|
if ( type && has(types, type) && ! types[type] ) {
|
||||||
// Attempt to allow Golden Kappa hype trains?
|
// Attempt to allow Golden Kappa hype trains?
|
||||||
if ( type === 'hype_train' && entry.event.typeDetails === '0' && this.chat.context.get('chat.banners.kappa-train') )
|
//if ( type === 'hype_train' && entry.event.typeDetails === '0' && this.chat.context.get('chat.banners.kappa-train') )
|
||||||
continue;
|
// continue;
|
||||||
|
|
||||||
this.log.info('Removing community highlight: ', type, '#', entry.id);
|
this.log.info('Removing community highlight: ', type, '#', entry.id);
|
||||||
this.community_dispatch({
|
this.community_dispatch({
|
||||||
|
|
|
@ -7,15 +7,11 @@
|
||||||
import Module from 'utilities/module';
|
import Module from 'utilities/module';
|
||||||
import { findReactFragment } from 'utilities/dom';
|
import { findReactFragment } from 'utilities/dom';
|
||||||
|
|
||||||
import { getTwitchEmoteSrcSet } from 'utilities/object';
|
import { SourcedSet, getTwitchEmoteSrcSet } from 'utilities/object';
|
||||||
import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES, REPLACEMENTS, REPLACEMENT_BASE, KEYS } from 'utilities/constants';
|
import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES, REPLACEMENTS, REPLACEMENT_BASE, KEYS } from 'utilities/constants';
|
||||||
|
|
||||||
import Twilight from 'site';
|
import Twilight from 'site';
|
||||||
|
|
||||||
const COMMAND_KEYS = [
|
|
||||||
'!',
|
|
||||||
'/'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Prefer using these statically-allocated collators to String.localeCompare
|
// Prefer using these statically-allocated collators to String.localeCompare
|
||||||
const locale = Intl.Collator();
|
const locale = Intl.Collator();
|
||||||
|
@ -75,7 +71,6 @@ export default class Input extends Module {
|
||||||
this.inject('site.fine');
|
this.inject('site.fine');
|
||||||
this.inject('site');
|
this.inject('site');
|
||||||
|
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
|
|
||||||
this.settings.add('chat.hype.display-input', {
|
this.settings.add('chat.hype.display-input', {
|
||||||
|
@ -783,7 +778,7 @@ export default class Input extends Module {
|
||||||
|
|
||||||
inst.getMatches = function(input, unknown, index) {
|
inst.getMatches = function(input, unknown, index) {
|
||||||
try {
|
try {
|
||||||
return index === 0 && COMMAND_KEYS.includes(input[0])
|
return index === 0 && t.chat.CommandPrefixes.includes(input[0])
|
||||||
? inst.getCommands(input) : null;
|
? inst.getCommands(input) : null;
|
||||||
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
@ -797,11 +792,22 @@ export default class Input extends Module {
|
||||||
isEditor: inst.props.isCurrentUserEditor
|
isEditor: inst.props.isCurrentUserEditor
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get the parent-input so we can do stuff.
|
||||||
|
const parent = t.fine.searchParent(inst,
|
||||||
|
n => n?.props?.channelID && n?.props?.setTray, 50);
|
||||||
|
|
||||||
const event = t.makeEvent({
|
const event = t.makeEvent({
|
||||||
input,
|
input,
|
||||||
permissionLevel: inst.props.permissionLevel,
|
permissionLevel: inst.props.permissionLevel,
|
||||||
isEditor: inst.props.isCurrentUserEditor,
|
isEditor: inst.props.isCurrentUserEditor,
|
||||||
commands
|
commands,
|
||||||
|
|
||||||
|
// Extra details, if we managed to find our parent.
|
||||||
|
__input: parent,
|
||||||
|
channel: parent ? {
|
||||||
|
id: parent.props.channelID,
|
||||||
|
login: parent.props.channelLogin
|
||||||
|
} : null
|
||||||
});
|
});
|
||||||
|
|
||||||
t.emit('chat:get-tab-commands', event);
|
t.emit('chat:get-tab-commands', event);
|
||||||
|
|
|
@ -616,17 +616,17 @@ export default class Directory extends Module {
|
||||||
// Are we getting a clip, a video, or a stream?
|
// Are we getting a clip, a video, or a stream?
|
||||||
if ( props.slug ) {
|
if ( props.slug ) {
|
||||||
// Clip
|
// Clip
|
||||||
console.log('need flags for clip', props.slug);
|
//console.log('need flags for clip', props.slug);
|
||||||
el._ffz_flags = [];
|
el._ffz_flags = [];
|
||||||
|
|
||||||
} else if ( props.vodID ) {
|
} else if ( props.vodID ) {
|
||||||
// Video
|
// Video
|
||||||
console.log('need flags for vod', props.vodID);
|
//console.log('need flags for vod', props.vodID);
|
||||||
el._ffz_flags = [];
|
el._ffz_flags = [];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Stream?
|
// Stream?
|
||||||
console.log('need flags for stream', props.channelLogin);
|
//console.log('need flags for stream', props.channelLogin);
|
||||||
this.twitch_data.getStreamFlags(null, props.channelLogin).then(data => {
|
this.twitch_data.getStreamFlags(null, props.channelLogin).then(data => {
|
||||||
el._ffz_flags = data ?? [];
|
el._ffz_flags = data ?? [];
|
||||||
this.updateCard(el);
|
this.updateCard(el);
|
||||||
|
@ -670,11 +670,15 @@ export default class Directory extends Module {
|
||||||
}
|
}
|
||||||
if ( ! should_blur ) {
|
if ( ! should_blur ) {
|
||||||
const regexes = this.settings.get('__filter:directory.blur-titles');
|
const regexes = this.settings.get('__filter:directory.blur-titles');
|
||||||
if ( regexes &&
|
if ( regexes ) {
|
||||||
(( regexes[0] && regexes[0].test(props.title) ) ||
|
if ( regexes[0] )
|
||||||
( regexes[1] && regexes[1].test(props.title) ))
|
regexes[0].lastIndex = -1;
|
||||||
)
|
if ( regexes[1] )
|
||||||
should_blur = true;
|
regexes[1].lastIndex = -1;
|
||||||
|
|
||||||
|
if (( regexes[0] && regexes[0].test(props.title) ) || ( regexes[1] && regexes[1].test(props.title) ))
|
||||||
|
should_blur = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
el.classList.toggle('ffz-hide-thumbnail', should_blur);
|
el.classList.toggle('ffz-hide-thumbnail', should_blur);
|
||||||
|
@ -700,11 +704,15 @@ export default class Directory extends Module {
|
||||||
|
|
||||||
if ( ! should_hide ) {
|
if ( ! should_hide ) {
|
||||||
const regexes = this.settings.get('__filter:directory.block-titles');
|
const regexes = this.settings.get('__filter:directory.block-titles');
|
||||||
if ( regexes &&
|
if ( regexes ) {
|
||||||
(( regexes[0] && regexes[0].test(props.title) ) ||
|
if ( regexes[0] )
|
||||||
( regexes[1] && regexes[1].test(props.title) ))
|
regexes[0].lastIndex = -1;
|
||||||
)
|
if ( regexes[1] )
|
||||||
should_hide = true;
|
regexes[1].lastIndex = -1;
|
||||||
|
|
||||||
|
if (( regexes[0] && regexes[0].test(props.title) ) || ( regexes[1] && regexes[1].test(props.title) ))
|
||||||
|
should_hide = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -385,11 +385,14 @@ export default class Layout extends Module {
|
||||||
else {
|
else {
|
||||||
const regexes = this.settings.get('__filter:directory.block-titles');
|
const regexes = this.settings.get('__filter:directory.block-titles');
|
||||||
const title = stream?.broadcaster?.broadcastSettings?.title;
|
const title = stream?.broadcaster?.broadcastSettings?.title;
|
||||||
if ( regexes && title &&
|
if ( regexes && title ) {
|
||||||
(( regexes[0] && regexes[0].test(title) ) ||
|
if ( regexes[0] )
|
||||||
( regexes[1] && regexes[1].test(title) ))
|
regexes[0].lastIndex = -1;
|
||||||
)
|
if ( regexes[1] )
|
||||||
should_hide = true;
|
regexes[1].lastIndex = -1;
|
||||||
|
if ( (regexes[0] && regexes[0].test(title)) || (regexes[1] && regexes[1].test(title)) )
|
||||||
|
should_hide = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
card.classList.toggle('ffz--side-nav-card-rerun', rerun);
|
card.classList.toggle('ffz--side-nav-card-rerun', rerun);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue