mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 15:27:43 +00:00
4.0.0-rc13.22
* Added: Support for clicking Twitch emotes to open information cards. * Added: Option to allow clicking blocked terms in chat to reveal the term. * Fixed: Twitch Experiments not being detected properly.
This commit is contained in:
parent
360d19e4bf
commit
9b95efb459
7 changed files with 265 additions and 21 deletions
|
@ -149,7 +149,7 @@ ${typeof x[1] === 'string' ? x[1] : JSON.stringify(x[1], null, 4)}`
|
||||||
FrankerFaceZ.Logger = Logger;
|
FrankerFaceZ.Logger = Logger;
|
||||||
|
|
||||||
const VER = FrankerFaceZ.version_info = {
|
const VER = FrankerFaceZ.version_info = {
|
||||||
major: 4, minor: 0, revision: 0, extra: '-rc13.21',
|
major: 4, minor: 0, revision: 0, extra: '-rc13.22',
|
||||||
commit: __git_commit__,
|
commit: __git_commit__,
|
||||||
build: __webpack_hash__,
|
build: __webpack_hash__,
|
||||||
toString: () =>
|
toString: () =>
|
||||||
|
|
10
src/modules/chat/components/chat-automod-blocked.vue
Normal file
10
src/modules/chat/components/chat-automod-blocked.vue
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<template functional>
|
||||||
|
<strong
|
||||||
|
:data-text="props.token.text"
|
||||||
|
:data-categories="JSON.stringify(props.token.categories)"
|
||||||
|
data-tooltip-type="amterm"
|
||||||
|
class="ffz-tooltip ffz--blocked"
|
||||||
|
>
|
||||||
|
×××
|
||||||
|
</strong>
|
||||||
|
</template>
|
|
@ -93,6 +93,33 @@ export default class Emotes extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Because this may be used elsewhere.
|
// Because this may be used elsewhere.
|
||||||
this.handleClick = this.handleClick.bind(this);
|
this.handleClick = this.handleClick.bind(this);
|
||||||
|
@ -303,6 +330,29 @@ export default class Emotes extends Module {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( provider === 'twitch' && this.parent.context.get('chat.emote-dialogs') ) {
|
||||||
|
const fine = this.resolve('site.fine');
|
||||||
|
if ( ! fine )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const chat = fine.searchParent(target, n => n.props && n.props.onEmoteClick);
|
||||||
|
if ( ! chat || ! chat.props || ! chat.props.message )
|
||||||
|
return;
|
||||||
|
|
||||||
|
const props = chat.props;
|
||||||
|
props.onEmoteClick({
|
||||||
|
channelID: props.channelID || '',
|
||||||
|
channelLogin: props.channelLogin || '',
|
||||||
|
emoteID: ds.id,
|
||||||
|
emoteCode: target.alt,
|
||||||
|
sourceID: 'chat',
|
||||||
|
referrerID: '',
|
||||||
|
initialTopOffset: target.getBoundingClientRect().bottom
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,9 @@ export default class Chat extends Module {
|
||||||
|
|
||||||
this._link_info = {};
|
this._link_info = {};
|
||||||
|
|
||||||
|
// Bind for JSX stuff
|
||||||
|
this.clickToReveal = this.clickToReveal.bind(this);
|
||||||
|
|
||||||
this.style = new ManagedStyle;
|
this.style = new ManagedStyle;
|
||||||
|
|
||||||
this.context = this.settings.context({});
|
this.context = this.settings.context({});
|
||||||
|
@ -169,6 +172,15 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.settings.add('chat.filtering.click-to-reveal', {
|
||||||
|
default: false,
|
||||||
|
ui: {
|
||||||
|
path: 'Chat > Filtering >> Behavior',
|
||||||
|
title: 'Click to reveal deleted terms.',
|
||||||
|
component: 'setting-check-box'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.settings.add('chat.filtering.show-deleted', {
|
this.settings.add('chat.filtering.show-deleted', {
|
||||||
default: false,
|
default: false,
|
||||||
ui: {
|
ui: {
|
||||||
|
@ -540,25 +552,6 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const ts = new Date(0).toLocaleTimeString().toUpperCase(),
|
const ts = new Date(0).toLocaleTimeString().toUpperCase(),
|
||||||
default_24 = ts.lastIndexOf('PM') === -1 && ts.lastIndexOf('AM') === -1;
|
default_24 = ts.lastIndexOf('PM') === -1 && ts.lastIndexOf('AM') === -1;
|
||||||
|
|
||||||
|
@ -758,6 +751,21 @@ export default class Chat extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clickToReveal(event) {
|
||||||
|
const target = event.target;
|
||||||
|
if ( target ) {
|
||||||
|
if ( target._ffz_visible )
|
||||||
|
target.textContent = '×××';
|
||||||
|
else if ( ! this.context.get('chat.filtering.click-to-reveal') )
|
||||||
|
return;
|
||||||
|
else if ( target.dataset )
|
||||||
|
target.textContent = target.dataset.text;
|
||||||
|
|
||||||
|
target._ffz_visible = ! target._ffz_visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
standardizeWhisper(msg) { // eslint-disable-line class-methods-use-this
|
standardizeWhisper(msg) { // eslint-disable-line class-methods-use-this
|
||||||
if ( ! msg )
|
if ( ! msg )
|
||||||
return msg;
|
return msg;
|
||||||
|
|
|
@ -382,6 +382,7 @@ export const BlockedTerms = {
|
||||||
data-text={token.text}
|
data-text={token.text}
|
||||||
data-tooltip-type="blocked"
|
data-tooltip-type="blocked"
|
||||||
class="ffz-tooltip ffz--blocked"
|
class="ffz-tooltip ffz--blocked"
|
||||||
|
onClick={this.clickToReveal}
|
||||||
>
|
>
|
||||||
×××
|
×××
|
||||||
</strong>);
|
</strong>);
|
||||||
|
@ -419,6 +420,172 @@ export const BlockedTerms = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// AutoMod Filtering
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
const AM_DESCRIPTIONS = {
|
||||||
|
A: 'Hostility',
|
||||||
|
I: 'Discrimination',
|
||||||
|
P: 'Profanity',
|
||||||
|
S: 'Sexually Explicit Language'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AutomoddedTerms = {
|
||||||
|
type: 'amterm',
|
||||||
|
priority: 99,
|
||||||
|
|
||||||
|
component: () => import(/* webpackChunkName: 'vue-chat' */ './components/chat-automod-blocked.vue'),
|
||||||
|
|
||||||
|
render(token, createElement) {
|
||||||
|
return (<strong
|
||||||
|
data-text={token.text}
|
||||||
|
data-categories={JSON.stringify(token.categories)}
|
||||||
|
data-tooltip-type="amterm"
|
||||||
|
class="ffz-tooltip ffz--blocked"
|
||||||
|
onClick={this.clickToReveal}
|
||||||
|
>
|
||||||
|
×××
|
||||||
|
</strong>);
|
||||||
|
},
|
||||||
|
|
||||||
|
tooltip(target) {
|
||||||
|
const ds = target.dataset,
|
||||||
|
flags = [];
|
||||||
|
|
||||||
|
let cats;
|
||||||
|
try {
|
||||||
|
cats = JSON.parse(ds.categories);
|
||||||
|
for(const key in cats) {
|
||||||
|
if ( cats[key] && AM_DESCRIPTIONS[key] )
|
||||||
|
flags.push(this.i18n.t(`chat.filtering.automod.${key}`, AM_DESCRIPTIONS[key]))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(err) {
|
||||||
|
flags.push('Parse Error');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return [
|
||||||
|
(<div class="tw-border-b tw-mg-b-05">{ // eslint-disable-line react/jsx-key
|
||||||
|
this.i18n.t('chat.filtering.automod-term', 'AutoMod Blocked Term')
|
||||||
|
}</div>),
|
||||||
|
this.i18n.t('chat.filtering.automod-why', 'This was flagged as: '),
|
||||||
|
flags.join(', ')
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
process(tokens, msg) {
|
||||||
|
if ( ! tokens || ! tokens.length || ! msg.flags || ! Array.isArray(msg.flags.list) )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const cats = msg.flags.preferences,
|
||||||
|
flagged = msg.flags.list.filter(x => {
|
||||||
|
if ( ! x || x.startIndex == null || x.endIndex == null )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const y = x.categories;
|
||||||
|
if ( ! y )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for(const key in y) {
|
||||||
|
if ( y[key] && cats[key] )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
f_length = flagged.length;
|
||||||
|
|
||||||
|
if ( ! f_length )
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const out = [];
|
||||||
|
let idx = 0,
|
||||||
|
fix = 0;
|
||||||
|
|
||||||
|
for(const token of tokens) {
|
||||||
|
const length = token.length || (token.text && split_chars(token.text).length) || 0,
|
||||||
|
t_start = idx,
|
||||||
|
t_end = idx + length;
|
||||||
|
|
||||||
|
if ( token.type !== 'text' ) {
|
||||||
|
out.push(token);
|
||||||
|
idx = t_end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = split_chars(token.text);
|
||||||
|
|
||||||
|
while ( fix < f_length ) {
|
||||||
|
const flag = flagged[fix],
|
||||||
|
f_start = flag.startIndex,
|
||||||
|
f_end = flag.endIndex + 1;
|
||||||
|
|
||||||
|
// Did this flagged term already end? Skip it!
|
||||||
|
if ( f_end < t_start ) {
|
||||||
|
fix++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does this flagged term start after this token?
|
||||||
|
if ( f_start > t_end ) {
|
||||||
|
// Just dump this token and move on.
|
||||||
|
out.push(token);
|
||||||
|
idx = t_end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's text at the beginning of the token that isn't part of
|
||||||
|
// this flagged term, output it.
|
||||||
|
if ( f_start > idx )
|
||||||
|
out.push({
|
||||||
|
type: 'text',
|
||||||
|
text: text.slice(idx - t_start, f_start - t_start).join('')
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clamp the start of the filtered term to the start of this token.
|
||||||
|
let fs = f_start - t_start;
|
||||||
|
if ( fs < 0 )
|
||||||
|
fs = 0;
|
||||||
|
|
||||||
|
// Add the token.
|
||||||
|
out.push({
|
||||||
|
type: 'amterm',
|
||||||
|
categories: flag.categories,
|
||||||
|
text: text.slice(fs, f_end - t_start).join('')
|
||||||
|
});
|
||||||
|
|
||||||
|
// Does this flagged term extend past the end of this token?
|
||||||
|
if ( f_end > t_end ) {
|
||||||
|
// Don't go to the next term, just continue processing on the
|
||||||
|
// next token.
|
||||||
|
idx = t_end;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = f_end;
|
||||||
|
fix++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've finished processing terms. If there's any remaining
|
||||||
|
// text in the token, push it out.
|
||||||
|
if ( idx < t_end ) {
|
||||||
|
if ( t_start === idx )
|
||||||
|
out.push(token);
|
||||||
|
else
|
||||||
|
out.push({
|
||||||
|
type: 'text',
|
||||||
|
text: text.slice(idx - t_start).join('')
|
||||||
|
});
|
||||||
|
|
||||||
|
idx = t_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Cheers
|
// Cheers
|
||||||
|
|
|
@ -143,6 +143,10 @@ export default class Twilight extends BaseSite {
|
||||||
core = this.web_munch.getModule('core-2');
|
core = this.web_munch.getModule('core-2');
|
||||||
if ( core )
|
if ( core )
|
||||||
return this._core = core.p;
|
return this._core = core.p;
|
||||||
|
|
||||||
|
core = this.web_munch.getModule('core-3');
|
||||||
|
if ( core )
|
||||||
|
return this._core = core.q;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +156,7 @@ Twilight.KNOWN_MODULES = {
|
||||||
react: n => n.Component && n.createElement,
|
react: n => n.Component && n.createElement,
|
||||||
'core-1': n => n.o && n.o.experiments,
|
'core-1': n => n.o && n.o.experiments,
|
||||||
'core-2': n => n.p && n.p.experiments,
|
'core-2': n => n.p && n.p.experiments,
|
||||||
|
'core-3': n => n.q && n.q.experiments,
|
||||||
cookie: n => n && n.set && n.get && n.getJSON && n.withConverter,
|
cookie: n => n && n.set && n.get && n.getJSON && n.withConverter,
|
||||||
'extension-service': n => n.extensionService,
|
'extension-service': n => n.extensionService,
|
||||||
'chat-types': n => n.b && has(n.b, 'Message') && has(n.b, 'RoomMods'),
|
'chat-types': n => n.b && has(n.b, 'Message') && has(n.b, 'RoomMods'),
|
||||||
|
|
|
@ -954,10 +954,14 @@ export default class ChatHook extends Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( original.message ) {
|
if ( original.message ) {
|
||||||
const user = original.message.user;
|
const user = original.message.user,
|
||||||
|
flags = original.message.flags;
|
||||||
if ( user )
|
if ( user )
|
||||||
message.emotes = user.emotes;
|
message.emotes = user.emotes;
|
||||||
|
|
||||||
|
if ( flags && this.getFilterFlagOptions )
|
||||||
|
message.flags = this.getFilterFlagOptions(flags);
|
||||||
|
|
||||||
if ( typeof original.action === 'string' )
|
if ( typeof original.action === 'string' )
|
||||||
message.message = original.action;
|
message.message = original.action;
|
||||||
else
|
else
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue