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;
|
||||
|
||||
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__,
|
||||
build: __webpack_hash__,
|
||||
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.
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
|
@ -303,6 +330,29 @@ export default class Emotes extends Module {
|
|||
|
||||
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 = {};
|
||||
|
||||
// Bind for JSX stuff
|
||||
this.clickToReveal = this.clickToReveal.bind(this);
|
||||
|
||||
this.style = new ManagedStyle;
|
||||
|
||||
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', {
|
||||
default: false,
|
||||
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(),
|
||||
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
|
||||
if ( ! msg )
|
||||
return msg;
|
||||
|
|
|
@ -382,6 +382,7 @@ export const BlockedTerms = {
|
|||
data-text={token.text}
|
||||
data-tooltip-type="blocked"
|
||||
class="ffz-tooltip ffz--blocked"
|
||||
onClick={this.clickToReveal}
|
||||
>
|
||||
×××
|
||||
</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
|
||||
|
|
|
@ -143,6 +143,10 @@ export default class Twilight extends BaseSite {
|
|||
core = this.web_munch.getModule('core-2');
|
||||
if ( core )
|
||||
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,
|
||||
'core-1': n => n.o && n.o.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,
|
||||
'extension-service': n => n.extensionService,
|
||||
'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 ) {
|
||||
const user = original.message.user;
|
||||
const user = original.message.user,
|
||||
flags = original.message.flags;
|
||||
if ( user )
|
||||
message.emotes = user.emotes;
|
||||
|
||||
if ( flags && this.getFilterFlagOptions )
|
||||
message.flags = this.getFilterFlagOptions(flags);
|
||||
|
||||
if ( typeof original.action === 'string' )
|
||||
message.message = original.action;
|
||||
else
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue