mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.22.0
* Added: Highlight rules now have priorities. Priorities allow for finer control over which color is used to highlight a message, as well as giving priority to specific rules when highlighting individual words. * Added: Setting to set a priority for the built-in highlighting when someone mentions you by name. * Changed: The `chat:receive-message` event no longer fires if there are no registered event listeners, to improve performance. * Changed: Attempt to more accurately display re-sub notices in Chat on Videos by parsing out the re-sub message from the start of the message. * Changed: Apply the user's selected `Action Style` to Chat on Videos. * Fixed: Messages being re-tokenized incorrectly sometimes due to an incorrect flag being passed by the chat line component. * Fixed: Update the logic for inserting a "Live Message Separator" to duplicate the logic used by Twitch. * API Added: `chat:buffer-message` event that is fired when a chat message is copied into the buffer of visible messages. If you wish to run an event when a chat message becomes visible to the user, this is what you want to use. * API Added: `FFZEvent` instances now have a `_reset()` method for resetting their defaultPrevented / propagationStopped state. For use by the constructing method only. This is intended for allowing the re-use of FFZEvent instances when firing many events in succession. * API Changed: The various settings used for applying filters during message tokenization have been renamed to start with `__filter:`
This commit is contained in:
parent
e7803c7db1
commit
ae85bf76b9
11 changed files with 452 additions and 106 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.21.4",
|
||||
"version": "4.22.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -24,6 +24,17 @@ import Actions from './actions';
|
|||
|
||||
export const SEPARATORS = '[\\s`~<>!-#%-\\x2A,-/:;\\x3F@\\x5B-\\x5D_\\x7B}\\u00A1\\u00A7\\u00AB\\u00B6\\u00B7\\u00BB\\u00BF\\u037E\\u0387\\u055A-\\u055F\\u0589\\u058A\\u05BE\\u05C0\\u05C3\\u05C6\\u05F3\\u05F4\\u0609\\u060A\\u060C\\u060D\\u061B\\u061E\\u061F\\u066A-\\u066D\\u06D4\\u0700-\\u070D\\u07F7-\\u07F9\\u0830-\\u083E\\u085E\\u0964\\u0965\\u0970\\u0AF0\\u0DF4\\u0E4F\\u0E5A\\u0E5B\\u0F04-\\u0F12\\u0F14\\u0F3A-\\u0F3D\\u0F85\\u0FD0-\\u0FD4\\u0FD9\\u0FDA\\u104A-\\u104F\\u10FB\\u1360-\\u1368\\u1400\\u166D\\u166E\\u169B\\u169C\\u16EB-\\u16ED\\u1735\\u1736\\u17D4-\\u17D6\\u17D8-\\u17DA\\u1800-\\u180A\\u1944\\u1945\\u1A1E\\u1A1F\\u1AA0-\\u1AA6\\u1AA8-\\u1AAD\\u1B5A-\\u1B60\\u1BFC-\\u1BFF\\u1C3B-\\u1C3F\\u1C7E\\u1C7F\\u1CC0-\\u1CC7\\u1CD3\\u2010-\\u2027\\u2030-\\u2043\\u2045-\\u2051\\u2053-\\u205E\\u207D\\u207E\\u208D\\u208E\\u2329\\u232A\\u2768-\\u2775\\u27C5\\u27C6\\u27E6-\\u27EF\\u2983-\\u2998\\u29D8-\\u29DB\\u29FC\\u29FD\\u2CF9-\\u2CFC\\u2CFE\\u2CFF\\u2D70\\u2E00-\\u2E2E\\u2E30-\\u2E3B\\u3001-\\u3003\\u3008-\\u3011\\u3014-\\u301F\\u3030\\u303D\\u30A0\\u30FB\\uA4FE\\uA4FF\\uA60D-\\uA60F\\uA673\\uA67E\\uA6F2-\\uA6F7\\uA874-\\uA877\\uA8CE\\uA8CF\\uA8F8-\\uA8FA\\uA92E\\uA92F\\uA95F\\uA9C1-\\uA9CD\\uA9DE\\uA9DF\\uAA5C-\\uAA5F\\uAADE\\uAADF\\uAAF0\\uAAF1\\uABEB\\uFD3E\\uFD3F\\uFE10-\\uFE19\\uFE30-\\uFE52\\uFE54-\\uFE61\\uFE63\\uFE68\\uFE6A\\uFE6B\\uFF01-\\uFF03\\uFF05-\\uFF0A\\uFF0C-\\uFF0F\\uFF1A\\uFF1B\\uFF1F\\uFF20\\uFF3B-\\uFF3D\\uFF3F\\uFF5B\\uFF5D\\uFF5F-\\uFF65]';
|
||||
|
||||
function sortPriorityColorTerms(list) {
|
||||
list.sort((a,b) => {
|
||||
if ( a[0] < b[0] ) return 1;
|
||||
if ( a[0] > b[0] ) return -1;
|
||||
if ( ! a[1] && b[1] ) return 1;
|
||||
if ( a[1] && ! b[1] ) return -1;
|
||||
return 0;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
function addSeparators(str) {
|
||||
return `(^|.*?${SEPARATORS})(?:${str})(?=$|${SEPARATORS})`
|
||||
}
|
||||
|
@ -251,7 +262,8 @@ export default class Chat extends Module {
|
|||
path: 'Chat > Filtering > Highlight',
|
||||
sort: 1000,
|
||||
component: 'setting-spacer',
|
||||
top: '30rem'
|
||||
top: '30rem',
|
||||
force_seen: true
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.click-to-reveal', {
|
||||
|
@ -402,14 +414,15 @@ export default class Chat extends Module {
|
|||
type: 'array_merge',
|
||||
always_inherit: true,
|
||||
ui: {
|
||||
path: 'Chat > Filtering > Highlight >> Users',
|
||||
path: 'Chat > Filtering > Highlight @{"description": "These settings allow you to highlight messages in chat based on their contents. Setting priorities on rules allows you to determine which highlight color should be applied if a message matches multiple rules. Rules with a higher priority take priority over rules with lower priorities."} >> Users',
|
||||
component: 'basic-terms',
|
||||
colored: true,
|
||||
words: false
|
||||
words: false,
|
||||
priority: true
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-users--color-regex', {
|
||||
this.settings.add('__filter:highlight-users', {
|
||||
requires: ['chat.filtering.highlight-basic-users'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -417,10 +430,11 @@ export default class Chat extends Module {
|
|||
if ( ! val || ! val.length )
|
||||
return null;
|
||||
|
||||
const colors = new Map;
|
||||
const temp = new Map;
|
||||
|
||||
for(const item of val) {
|
||||
const c = item.c || null,
|
||||
p = item.p || 0,
|
||||
t = item.t;
|
||||
|
||||
let v = item.v;
|
||||
|
@ -440,6 +454,12 @@ export default class Chat extends Module {
|
|||
continue;
|
||||
}
|
||||
|
||||
let colors = temp.get(p);
|
||||
if ( ! colors ) {
|
||||
colors = new Map;
|
||||
temp.set(p, colors);
|
||||
}
|
||||
|
||||
if ( colors.has(c) )
|
||||
colors.get(c).push(v);
|
||||
else {
|
||||
|
@ -447,11 +467,19 @@ export default class Chat extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
for(const [key, list] of colors) {
|
||||
colors.set(key, new RegExp(`^(?:${list.join('|')})$`, 'gi'));
|
||||
const out = [];
|
||||
for(const [priority, list] of temp) {
|
||||
for(const [color, entries] of list) {
|
||||
out.push([
|
||||
priority,
|
||||
color,
|
||||
new RegExp(`^(?:${entries.join('|')})$`, 'gi')
|
||||
]);
|
||||
//list.set(k, new RegExp(`^(?:${entries.join('|')})$`, 'gi'));
|
||||
}
|
||||
}
|
||||
|
||||
return colors;
|
||||
return sortPriorityColorTerms(out);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -469,7 +497,7 @@ export default class Chat extends Module {
|
|||
});
|
||||
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-users-blocked--regex', {
|
||||
this.settings.add('__filter:block-users', {
|
||||
requires: ['chat.filtering.highlight-basic-users-blocked'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -513,12 +541,13 @@ export default class Chat extends Module {
|
|||
path: 'Chat > Filtering > Highlight >> Badges',
|
||||
component: 'badge-highlighting',
|
||||
colored: true,
|
||||
priority: true,
|
||||
data: () => this.badges.getSettingsBadges(true)
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-badges--colors', {
|
||||
this.settings.add('__filter:highlight-badges', {
|
||||
requires: ['chat.filtering.highlight-basic-badges'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -526,17 +555,19 @@ export default class Chat extends Module {
|
|||
if ( ! val || ! val.length )
|
||||
return null;
|
||||
|
||||
const colors = new Map;
|
||||
const badges = new Map;
|
||||
|
||||
for(const item of val) {
|
||||
const c = item.c || null,
|
||||
p = item.p || 0,
|
||||
v = item.v;
|
||||
|
||||
if ( ! colors.has(v) )
|
||||
colors.set(v, c);
|
||||
const existing = badges.get(v);
|
||||
if ( ! existing || existing[0] < p || (c && ! existing[1] && existing[0] <= p) )
|
||||
badges.set(v, [p, c]);
|
||||
}
|
||||
|
||||
return colors;
|
||||
return badges;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -553,7 +584,7 @@ export default class Chat extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-badges-blocked--list', {
|
||||
this.settings.add('__filter:block-badges', {
|
||||
requires: ['chat.filtering.highlight-basic-badges-blocked'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -582,11 +613,12 @@ export default class Chat extends Module {
|
|||
path: 'Chat > Filtering > Highlight >> Terms @{"description": "Please see [Chat > Filtering > Syntax Help](~) for details on how to use terms."}',
|
||||
component: 'basic-terms',
|
||||
colored: true,
|
||||
priority: true,
|
||||
highlight: true
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-terms--color-regex', {
|
||||
this.settings.add('__filter:highlight-terms', {
|
||||
requires: ['chat.filtering.highlight-tokens', 'chat.filtering.highlight-basic-terms'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -595,12 +627,14 @@ export default class Chat extends Module {
|
|||
if ( ! val || ! val.length )
|
||||
return null;
|
||||
|
||||
const colors = new Map;
|
||||
const temp = new Map;
|
||||
//const colors = new Map;
|
||||
let has_highlight = false,
|
||||
has_non = false;
|
||||
|
||||
for(const item of val) {
|
||||
const c = item.c || null,
|
||||
p = item.p || 0,
|
||||
highlight = can_highlight && (has(item, 'h') ? item.h : true),
|
||||
sensitive = item.s,
|
||||
t = item.t,
|
||||
|
@ -628,6 +662,12 @@ export default class Chat extends Module {
|
|||
else
|
||||
has_non = true;
|
||||
|
||||
let colors = temp.get(p);
|
||||
if ( ! colors ) {
|
||||
colors = new Map;
|
||||
temp.set(p, colors);
|
||||
}
|
||||
|
||||
let data = colors.get(c);
|
||||
if ( ! data )
|
||||
colors.set(c, data = [
|
||||
|
@ -656,21 +696,37 @@ export default class Chat extends Module {
|
|||
return null;
|
||||
|
||||
const out = {
|
||||
hl: has_highlight ? new Map : null,
|
||||
non: has_non ? new Map : null
|
||||
hl: has_highlight ? [] : null,
|
||||
non: has_non ? [] : null
|
||||
};
|
||||
|
||||
for(const [key, list] of colors) {
|
||||
const highlights = formatTerms(list[0]),
|
||||
non_highlights = formatTerms(list[1]);
|
||||
for(const [priority, colors] of temp) {
|
||||
for(const [color, list] of colors) {
|
||||
const highlights = formatTerms(list[0]),
|
||||
non_highlights = formatTerms(list[1]);
|
||||
|
||||
if ( highlights[0] || highlights[1] )
|
||||
out.hl.set(key, highlights);
|
||||
if ( highlights[0] || highlights[1] )
|
||||
out.hl.push([
|
||||
priority,
|
||||
color,
|
||||
highlights
|
||||
]);
|
||||
|
||||
if ( non_highlights[0] || non_highlights[1] )
|
||||
out.non.set(key, non_highlights);
|
||||
if ( non_highlights[0] || non_highlights[1] )
|
||||
out.non.push([
|
||||
priority,
|
||||
color,
|
||||
non_highlights
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( has_highlight )
|
||||
sortPriorityColorTerms(out.hl);
|
||||
|
||||
if ( has_non )
|
||||
sortPriorityColorTerms(out.non);
|
||||
|
||||
return out;
|
||||
}
|
||||
});
|
||||
|
@ -688,7 +744,7 @@ export default class Chat extends Module {
|
|||
});
|
||||
|
||||
|
||||
this.settings.add('chat.filtering.highlight-basic-blocked--regex', {
|
||||
this.settings.add('__filter:block-terms', {
|
||||
requires: ['chat.filtering.highlight-basic-blocked'],
|
||||
equals: 'requirements',
|
||||
process(ctx) {
|
||||
|
@ -780,6 +836,18 @@ export default class Chat extends Module {
|
|||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.mention-priority', {
|
||||
default: 0,
|
||||
ui: {
|
||||
path: 'Chat > Filtering > General >> Appearance',
|
||||
title: 'Mention Priority',
|
||||
component: 'setting-text-box',
|
||||
type: 'number',
|
||||
process: 'to_int',
|
||||
description: 'Mentions of your name have this priority for the purpose of highlighting. See [Chat > Filtering > Highlight](~) for more details.'
|
||||
}
|
||||
});
|
||||
|
||||
this.settings.add('chat.filtering.mention-color', {
|
||||
default: '',
|
||||
ui: {
|
||||
|
@ -1546,7 +1614,7 @@ export default class Chat extends Module {
|
|||
|
||||
for(let i=0; i < l; i++) {
|
||||
const part = parts[i],
|
||||
content = part.content;
|
||||
content = part.ffz_content ?? part.content;
|
||||
|
||||
if ( ! content )
|
||||
continue;
|
||||
|
@ -1696,6 +1764,9 @@ export default class Chat extends Module {
|
|||
if ( ! this.context.get('chat.rich.enabled') || this.context.get('chat.rich.minimum-level') > this.getUserLevel(msg) )
|
||||
return;
|
||||
|
||||
if ( ! Array.isArray(tokens) )
|
||||
return;
|
||||
|
||||
const providers = this.__rich_providers;
|
||||
|
||||
for(const token of tokens) {
|
||||
|
@ -1728,7 +1799,7 @@ export default class Chat extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
return tokens || [];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -346,7 +346,8 @@ export const Mentions = {
|
|||
if ( ! tokens || ! tokens.length )
|
||||
return tokens;
|
||||
|
||||
const can_highlight_user = user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own');
|
||||
const can_highlight_user = user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own'),
|
||||
priority = this.context.get('chat.filtering.mention-priority');
|
||||
|
||||
let regex, login, display, mentionable = false;
|
||||
if ( user && user.login && ! can_highlight_user ) {
|
||||
|
@ -408,6 +409,10 @@ export const Mentions = {
|
|||
if ( mentioned ) {
|
||||
(msg.highlights = (msg.highlights || new Set())).add('mention');
|
||||
msg.mentioned = true;
|
||||
if ( msg.color_priority == null || priority > msg.color_priority ) {
|
||||
msg.mention_color = null;
|
||||
msg.color_priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
// Push the remaining text from the token.
|
||||
|
@ -438,17 +443,20 @@ export const UserHighlights = {
|
|||
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||
return tokens;
|
||||
|
||||
const colors = this.context.get('chat.filtering.highlight-basic-users--color-regex');
|
||||
if ( ! colors || ! colors.size )
|
||||
const list = this.context.get('__filter:highlight-users');
|
||||
if ( ! list || ! list.length )
|
||||
return tokens;
|
||||
|
||||
const u = msg.user;
|
||||
for(const [color, regex] of colors) {
|
||||
for(const [priority, color, regex] of list) {
|
||||
if ( regex.test(u.login) || regex.test(u.displayName) ) {
|
||||
(msg.highlights = (msg.highlights || new Set())).add('user');
|
||||
msg.mentioned = true;
|
||||
if ( color ) {
|
||||
msg.mention_color = color;
|
||||
if ( msg.color_priority == null || priority > msg.color_priority ) {
|
||||
msg.mention_color = color;
|
||||
msg.color_priority = priority;
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
|
@ -467,7 +475,7 @@ export const BlockedUsers = {
|
|||
return tokens;
|
||||
|
||||
const u = msg.user,
|
||||
regexes = this.context.get('chat.filtering.highlight-basic-users-blocked--regex');
|
||||
regexes = this.context.get('__filter:block-users');
|
||||
if ( ! regexes )
|
||||
return tokens;
|
||||
|
||||
|
@ -501,16 +509,16 @@ function getBadgeIDs(msg) {
|
|||
|
||||
export const BadgeStuff = {
|
||||
type: 'badge_stuff',
|
||||
priority: 80,
|
||||
priority: 97,
|
||||
|
||||
process(tokens, msg, user, haltable) {
|
||||
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||
return tokens;
|
||||
|
||||
const colors = this.context.get('chat.filtering.highlight-basic-badges--colors'),
|
||||
list = this.context.get('chat.filtering.highlight-basic-badges-blocked--list');
|
||||
const highlights = this.context.get('__filter:highlight-badges'),
|
||||
list = this.context.get('__filter:block-badges');
|
||||
|
||||
if ( ! colors && ! list )
|
||||
if ( ! highlights && ! list )
|
||||
return tokens;
|
||||
|
||||
const keys = getBadgeIDs(msg);
|
||||
|
@ -529,12 +537,15 @@ export const BadgeStuff = {
|
|||
if ( list && ! msg.deleted && list[0].includes(badge) )
|
||||
msg.deleted = true;
|
||||
|
||||
if ( colors && colors.has(badge) ) {
|
||||
const color = colors.get(badge);
|
||||
if ( highlights && highlights.has(badge) ) {
|
||||
const details = highlights.get(badge);
|
||||
(msg.highlights = (msg.highlights || new Set())).add('badge');
|
||||
msg.mentioned = true;
|
||||
if ( color ) {
|
||||
msg.mention_color = color;
|
||||
if ( details[1] ) {
|
||||
if ( msg.color_priority == null || details[0] > msg.color_priority ) {
|
||||
msg.mention_color = details[1];
|
||||
msg.color_priority = details[0];
|
||||
}
|
||||
if ( ! list )
|
||||
return tokens;
|
||||
}
|
||||
|
@ -552,7 +563,7 @@ export const BadgeStuff = {
|
|||
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||
return tokens;
|
||||
|
||||
const list = this.context.get('chat.filtering.highlight-basic-badges-blocked--list');
|
||||
const list = this.context.get('__filter:block-badges');
|
||||
if ( ! list || (! list[0].length && ! list[1].length) )
|
||||
return tokens;
|
||||
|
||||
|
@ -594,12 +605,16 @@ export const CustomHighlights = {
|
|||
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||
return tokens;
|
||||
|
||||
const data = this.context.get('chat.filtering.highlight-basic-terms--color-regex');
|
||||
const data = this.context.get('__filter:highlight-terms');
|
||||
if ( ! data )
|
||||
return tokens;
|
||||
|
||||
let had_match = false;
|
||||
if ( data.non ) {
|
||||
for(const [color, regexes] of data.non) {
|
||||
for(const [priority, color, regexes] of data.non) {
|
||||
if ( had_match && msg.color_priority != null && msg.color_priority > priority )
|
||||
break;
|
||||
|
||||
let matched = false;
|
||||
if ( regexes[0] ) {
|
||||
regexes[0].lastIndex = 0;
|
||||
|
@ -613,8 +628,15 @@ export const CustomHighlights = {
|
|||
if ( matched ) {
|
||||
(msg.highlights = (msg.highlights || new Set())).add('term');
|
||||
msg.mentioned = true;
|
||||
msg.mention_color = color || msg.mention_color;
|
||||
break;
|
||||
had_match = true;
|
||||
if ( color ) {
|
||||
if ( msg.color_priority == null || priority > msg.color_priority ) {
|
||||
msg.mention_color = color;
|
||||
msg.color_priority = priority;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -622,7 +644,7 @@ export const CustomHighlights = {
|
|||
if ( ! data.hl )
|
||||
return tokens;
|
||||
|
||||
for(const [color, regexes] of data.hl) {
|
||||
for(const [priority, color, regexes] of data.hl) {
|
||||
const out = [];
|
||||
for(const token of tokens) {
|
||||
if ( token.type !== 'text' ) {
|
||||
|
@ -656,8 +678,10 @@ export const CustomHighlights = {
|
|||
|
||||
(msg.highlights = (msg.highlights || new Set())).add('term');
|
||||
msg.mentioned = true;
|
||||
if ( ! msg.mention_color )
|
||||
if ( color && (msg.color_priority == null || priority > msg.color_priority) ) {
|
||||
msg.mention_color = color;
|
||||
msg.color_priority = priority;
|
||||
}
|
||||
|
||||
out.push({
|
||||
type: 'highlight',
|
||||
|
@ -767,7 +791,7 @@ export const BlockedTerms = {
|
|||
if ( user && user.login && user.login == msg.user.login && ! this.context.get('chat.filtering.process-own') )
|
||||
return tokens;
|
||||
|
||||
const regexes = this.context.get('chat.filtering.highlight-basic-blocked--regex');
|
||||
const regexes = this.context.get('__filter:block-terms');
|
||||
if ( ! regexes )
|
||||
return tokens;
|
||||
|
||||
|
@ -800,7 +824,7 @@ const AM_DESCRIPTIONS = {
|
|||
|
||||
export const AutomoddedTerms = {
|
||||
type: 'amterm',
|
||||
priority: 99,
|
||||
priority: 95,
|
||||
|
||||
component: () => import(/* webpackChunkName: 'vue-chat' */ './components/chat-automod-blocked.vue'),
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
:term="default_term"
|
||||
:badges="data"
|
||||
:colored="item.colored"
|
||||
:priority="item.priority"
|
||||
:removable="item.removable"
|
||||
:adding="true"
|
||||
@save="new_term"
|
||||
|
@ -18,6 +19,7 @@
|
|||
:term="term.v"
|
||||
:badges="data"
|
||||
:colored="item.colored"
|
||||
:priority="item.priority"
|
||||
:removable="item.removable"
|
||||
@remove="remove(term)"
|
||||
@save="save(term, $event)"
|
||||
|
@ -29,7 +31,7 @@
|
|||
<script>
|
||||
|
||||
import SettingMixin from '../setting-mixin';
|
||||
import {deep_copy} from 'utilities/object';
|
||||
import {deep_copy, has} from 'utilities/object';
|
||||
|
||||
let last_id = 0;
|
||||
|
||||
|
@ -42,6 +44,7 @@ export default {
|
|||
default_term: {
|
||||
v: '',
|
||||
c: '',
|
||||
p: 0,
|
||||
remove: false
|
||||
}
|
||||
}
|
||||
|
@ -60,9 +63,31 @@ export default {
|
|||
const out = [];
|
||||
|
||||
if ( Array.isArray(this.val) )
|
||||
for(const term of this.val)
|
||||
for(const term of this.val) {
|
||||
if ( term && term.v ) {
|
||||
if ( ! has(term.v, 'p') )
|
||||
term.v.p = 0;
|
||||
}
|
||||
|
||||
if ( term && term.t !== 'inherit' )
|
||||
out.push(term);
|
||||
}
|
||||
|
||||
if ( this.item.priority || this.item.colored ) {
|
||||
out.sort((a,b) => {
|
||||
if ( a.v && b.v ) {
|
||||
if ( this.item.priority ) {
|
||||
if ( a.v.p < b.v.p ) return 1;
|
||||
if ( a.v.p > b.v.p ) return -1;
|
||||
}
|
||||
if ( this.item.colored ) {
|
||||
if ( ! a.v.c && b.v.c ) return 1;
|
||||
if ( a.v.c && ! b.v.c ) return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
|
|
@ -51,6 +51,24 @@
|
|||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="priority"
|
||||
:class="editing ? 'tw-mg-r-05' : 'tw-mg-x-05'"
|
||||
class="tw-flex-shrink-0 tw-relative tw-tooltip__container"
|
||||
>
|
||||
<span v-if="! editing">{{ term.p }}</span>
|
||||
<input
|
||||
v-else
|
||||
v-model.number="edit_data.p"
|
||||
type="number"
|
||||
step="1"
|
||||
class="tw-block tw-border-radius-medium tw-font-size-6 ffz-min-width-unset ffz-input tw-pd-x-1 tw-pd-y-05"
|
||||
style="width: 5rem"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('settings.terms.priority.tip', 'Priority') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="removable && (editing || display.remove)"
|
||||
class="tw-flex-shrink-0 tw-mg-r-05 tw-mg-y-05 tw-flex tw-align-items-center ffz-checkbox tw-relative tw-tooltip__container"
|
||||
|
@ -180,6 +198,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
priority: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
removable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
@ -258,6 +280,13 @@ export default {
|
|||
},
|
||||
|
||||
save() {
|
||||
if ( this.priority && this.edit_data.p ) {
|
||||
if ( typeof this.edit_data.p === 'number' )
|
||||
this.edit_data.p = Math.floor(this.edit_data.p);
|
||||
else
|
||||
this.edit_data.p = 0;
|
||||
}
|
||||
|
||||
if ( this.valid )
|
||||
this.$emit('save', this.edit_data);
|
||||
this.cancel();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
:highlight="item.highlight"
|
||||
:words="item.words"
|
||||
:removable="item.removable"
|
||||
:priority="item.priority"
|
||||
:adding="true"
|
||||
@save="new_term"
|
||||
/>
|
||||
|
@ -21,6 +22,7 @@
|
|||
:highlight="item.highlight"
|
||||
:words="item.words"
|
||||
:removable="item.removable"
|
||||
:priority="item.priority"
|
||||
@remove="remove(term)"
|
||||
@save="save(term, $event)"
|
||||
/>
|
||||
|
@ -48,6 +50,7 @@ export default {
|
|||
s: false,
|
||||
h: false,
|
||||
w: true,
|
||||
p: 0,
|
||||
remove: false
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +77,9 @@ export default {
|
|||
if ( ! has(term.v, 'h') )
|
||||
term.v.h = true;
|
||||
|
||||
if ( ! has(term.v, 'p') )
|
||||
term.v.p = 0;
|
||||
|
||||
if ( term.v.t === 'raw' )
|
||||
term.v.t = 'regex';
|
||||
}
|
||||
|
@ -82,6 +88,32 @@ export default {
|
|||
out.push(term);
|
||||
}
|
||||
|
||||
out.sort((a,b) => {
|
||||
if ( a.v && b.v ) {
|
||||
if ( this.item.removable ) {
|
||||
if ( ! a.v.remove && b.v.remove ) return 1;
|
||||
if ( a.v.remove && ! b.v.remove ) return -1;
|
||||
}
|
||||
if ( this.item.priority ) {
|
||||
if ( a.v.p < b.v.p ) return 1;
|
||||
if ( a.v.p > b.v.p ) return -1;
|
||||
}
|
||||
if ( this.item.colored ) {
|
||||
if ( ! a.v.c && b.v.c ) return 1;
|
||||
if ( a.v.c && ! b.v.c ) return -1;
|
||||
}
|
||||
if ( this.item.highlight ) {
|
||||
if ( ! a.v.h && b.v.h ) return -1;
|
||||
if ( a.v.h && ! b.v.h ) return 1;
|
||||
}
|
||||
if ( this.item.words ?? true ) {
|
||||
if ( ! a.v.w && b.v.w ) return -1;
|
||||
if ( a.v.w && ! b.v.w ) return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
|
|
|
@ -56,6 +56,24 @@
|
|||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div
|
||||
v-if="priority"
|
||||
:class="editing ? 'tw-mg-r-05' : 'tw-mg-x-05'"
|
||||
class="tw-flex-shrink-0 tw-relative tw-tooltip__container"
|
||||
>
|
||||
<span v-if="! editing">{{ term.p }}</span>
|
||||
<input
|
||||
v-else
|
||||
v-model.number="edit_data.p"
|
||||
type="number"
|
||||
step="1"
|
||||
class="tw-block tw-border-radius-medium tw-font-size-6 ffz-min-width-unset ffz-input tw-pd-x-1 tw-pd-y-05"
|
||||
style="width: 5rem"
|
||||
>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
{{ t('settings.terms.priority.tip', 'Priority') }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="editing || display.s"
|
||||
class="tw-flex-shrink-0 tw-mg-r-05 tw-mg-y-05 tw-flex tw-align-items-center ffz-checkbox tw-relative tw-tooltip__container"
|
||||
|
@ -243,6 +261,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
priority: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
words: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
|
@ -360,6 +382,13 @@ export default {
|
|||
},
|
||||
|
||||
save() {
|
||||
if ( this.priority && this.edit_data.p ) {
|
||||
if ( typeof this.edit_data.p === 'number' )
|
||||
this.edit_data.p = Math.floor(this.edit_data.p);
|
||||
else
|
||||
this.edit_data.p = 0;
|
||||
}
|
||||
|
||||
this.$emit('save', this.edit_data);
|
||||
this.cancel();
|
||||
}
|
||||
|
|
|
@ -1492,27 +1492,27 @@ export default class ChatHook extends Module {
|
|||
room = m.roomLogin = r.login;
|
||||
}
|
||||
|
||||
const u = t.site.getUser(),
|
||||
r = {id: room_id, login: room};
|
||||
|
||||
const u = t.site.getUser();
|
||||
if ( u && cont ) {
|
||||
u.moderator = cont.props.isCurrentUserModerator;
|
||||
u.staff = cont.props.isStaff;
|
||||
}
|
||||
|
||||
m.ffz_tokens = m.ffz_tokens || t.chat.tokenizeMessage(m, u, r);
|
||||
m.ffz_tokens = m.ffz_tokens || t.chat.tokenizeMessage(m, u, true);
|
||||
if ( m.ffz_removed )
|
||||
return;
|
||||
|
||||
const event = new FFZEvent({
|
||||
message: m,
|
||||
channel: room,
|
||||
channelID: room_id
|
||||
});
|
||||
if ( t.hasListeners('chat:receive-message') ) {
|
||||
const event = new FFZEvent({
|
||||
message: m,
|
||||
channel: room,
|
||||
channelID: room_id
|
||||
});
|
||||
|
||||
t.emit('chat:receive-message', event);
|
||||
if ( event.defaultPrevented || m.ffz_removed )
|
||||
return;
|
||||
t.emit('chat:receive-message', event);
|
||||
if ( event.defaultPrevented || m.ffz_removed )
|
||||
return;
|
||||
}
|
||||
|
||||
} else if ( msg.type === types.ModerationAction && false && inst.markUserEventDeleted && inst.unsetModeratedUser ) {
|
||||
if ( !((! msg.level || ! msg.level.length) && msg.targetUserLogin && msg.targetUserLogin === inst.props.currentUserLogin) ) {
|
||||
|
@ -1793,7 +1793,9 @@ export default class ChatHook extends Module {
|
|||
has_newer = this.hasNewerLeft(),
|
||||
paused = this.isPaused(),
|
||||
max_size = t.chat.context.get('chat.scrollback-length'),
|
||||
do_remove = t.chat.context.get('chat.filtering.remove-deleted');
|
||||
do_remove = t.chat.context.get('chat.filtering.remove-deleted'),
|
||||
|
||||
event = t.hasListeners('chat:buffer-message') ? new FFZEvent() : null;
|
||||
|
||||
let added = 0,
|
||||
buffered = this.slidingWindowEnd,
|
||||
|
@ -1804,17 +1806,38 @@ export default class ChatHook extends Module {
|
|||
if ( do_remove !== 0 && (do_remove > 1 || ! see_deleted) && this.isDeletable(msg.event) && msg.event.deleted )
|
||||
continue;
|
||||
|
||||
if ( event ) {
|
||||
event._reset();
|
||||
event.message = msg.event;
|
||||
|
||||
t.emit('chat:buffer-message', event);
|
||||
if ( event.defaultPrevented || msg.event.ffz_removed )
|
||||
continue;
|
||||
}
|
||||
|
||||
const last = this.buffer[this.buffer.length - 1],
|
||||
type = last?.type;
|
||||
|
||||
if ( type === ct.Connected ) {
|
||||
if ( !(
|
||||
! this.props.isLoadingHistoricalMessages &&
|
||||
! this.props.historicalMessages ||
|
||||
type !== ct.Connected ||
|
||||
msg.event.type === ct.Connected ||
|
||||
this.buffer.find(e => e.type === ct.LiveMessageSeparator)
|
||||
) )
|
||||
this.buffer.push({
|
||||
type: ct.LiveMessageSeparator,
|
||||
id: 'live-message-separator'
|
||||
});
|
||||
|
||||
/*if ( type === ct.Connected ) {
|
||||
const non_null = this.buffer.filter(x => x && ct[x.type] && ! NULL_TYPES.includes(ct[x.type]));
|
||||
if ( non_null.length > 1 )
|
||||
this.buffer.push({
|
||||
type: ct.LiveMessageSeparator,
|
||||
id: 'live-message-separator'
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
this.buffer.push(msg.event);
|
||||
changed = true;
|
||||
|
|
|
@ -82,12 +82,13 @@ export default class ChatLine extends Module {
|
|||
this.chat.context.on('changed:chat.filtering.show-deleted', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.process-own', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.timestamp-format', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-terms--color-regex', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-users--color-regex', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-badges--colors', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-blocked--regex', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-users-blocked--regex', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.highlight-basic-badges-blocked--list', this.updateLines, this);
|
||||
this.chat.context.on('changed:chat.filtering.mention-priority', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:highlight-terms', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:highlight-users', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:highlight-badges', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:block-terms', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:block-users', this.updateLines, this);
|
||||
this.chat.context.on('changed:__filter:block-badges', this.updateLines, this);
|
||||
|
||||
this.on('chat:get-tab-commands', e => {
|
||||
if ( this.experiments.getTwitchAssignmentByName('chat_replies') === 'control' )
|
||||
|
@ -197,7 +198,7 @@ export default class ChatLine extends Module {
|
|||
raw_color = t.overrides.getColor(user.id) || user.color,
|
||||
color = t.parent.colors.process(raw_color),
|
||||
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, null, null),
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, null),
|
||||
contents = t.chat.renderTokens(tokens, e),
|
||||
|
||||
override_name = t.overrides.getName(user.id);
|
||||
|
@ -438,7 +439,7 @@ other {# messages were deleted by a moderator.}
|
|||
}
|
||||
|
||||
if ( ! room_id && room ) {
|
||||
const r = t.chat.getRoom(null, room_id, true);
|
||||
const r = t.chat.getRoom(null, room, true);
|
||||
if ( r && r.id )
|
||||
room_id = msg.roomId = r.id;
|
||||
}
|
||||
|
@ -460,7 +461,7 @@ other {# messages were deleted by a moderator.}
|
|||
u.can_reply = reply_mode === 2 && can_reply;
|
||||
}
|
||||
|
||||
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u, r),
|
||||
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
||||
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg),
|
||||
bg_css = msg.mentioned && msg.mention_color ? t.parent.inverse_colors.process(msg.mention_color) : null;
|
||||
|
||||
|
@ -973,7 +974,7 @@ other {# messages were deleted by a moderator.}
|
|||
const u = t.site.getUser(),
|
||||
r = {id: this.props.channelID, login: room},
|
||||
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u, r),
|
||||
tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
||||
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg),
|
||||
bg_css = msg.mentioned && msg.mention_color ? t.parent.inverse_colors.process(msg.mention_color) : null;
|
||||
|
||||
|
@ -1033,7 +1034,7 @@ other {# messages were deleted by a moderator.}
|
|||
if ( user && ((id && id == user.id) || (login && login == user.login)) ) {
|
||||
msg.ffz_tokens = null;
|
||||
msg.ffz_badges = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = msg.color_priority = null;
|
||||
inst.forceUpdate();
|
||||
}
|
||||
}
|
||||
|
@ -1060,7 +1061,7 @@ other {# messages were deleted by a moderator.}
|
|||
if ( msg ) {
|
||||
msg.ffz_tokens = null;
|
||||
msg.ffz_badges = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = msg.color_priority = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1069,7 +1070,7 @@ other {# messages were deleted by a moderator.}
|
|||
if ( msg ) {
|
||||
msg.ffz_tokens = null;
|
||||
msg.ffz_badges = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = msg.color_priority = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,25 @@ import {formatBitsConfig} from '../chat';
|
|||
|
||||
import Module from 'utilities/module';
|
||||
|
||||
const SUB_REGEX = /^([^\s]+) subscribed ([^.]+)\. They've subscribed for (\d+) months(?:[^!]+streak)?!/;
|
||||
const SUB_TIERS = {
|
||||
1000: 1,
|
||||
2000: 2,
|
||||
3000: 3
|
||||
};
|
||||
|
||||
function parseParamInt(param) {
|
||||
try {
|
||||
if ( /^[\d-]+$/.test(param) )
|
||||
param = parseInt(param, 10);
|
||||
} catch(err) { /* no-op */ }
|
||||
|
||||
if ( typeof param !== 'number' || isNaN(param) || ! isFinite(param) )
|
||||
param = 0;
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
|
||||
export default class VideoChatHook extends Module {
|
||||
constructor(...args) {
|
||||
|
@ -212,6 +231,9 @@ export default class VideoChatHook extends Module {
|
|||
|
||||
cls.prototype.ffzRenderMessage = function(msg, reply) {
|
||||
const is_action = msg.is_action,
|
||||
action_style = is_action ? t.chat.context.get('chat.me-style') : 0,
|
||||
action_italic = action_style >= 2,
|
||||
action_color = action_style === 1 || action_style === 3,
|
||||
user = msg.user,
|
||||
color = t.site_chat.colors.process(user.color),
|
||||
|
||||
|
@ -222,31 +244,99 @@ export default class VideoChatHook extends Module {
|
|||
u.staff = u.roles && u.roles.isStaff;
|
||||
}
|
||||
|
||||
let system_msg;
|
||||
|
||||
if ( msg.system_msg === true ) {
|
||||
const params = msg.params || {},
|
||||
msg_id = params['msg-id'];
|
||||
if ( msg_id === 'resub' ) {
|
||||
const setting = t.chat.context.get('chat.subs.show'),
|
||||
|
||||
raw_months = parseParamInt(params['msg-param-months']),
|
||||
cumulative_months = parseParamInt(params['msg-param-cumulative-months']),
|
||||
months = cumulative_months || raw_months;
|
||||
|
||||
t.log.info('resub-notice setting:', setting, 'months:', months, 'cumulative:', cumulative_months, 'raw:', raw_months);
|
||||
t.log.info('-> params:', params);
|
||||
|
||||
if ( setting === 3 || (months > 1 && setting > 0) ) {
|
||||
const share = parseParamInt(params['msg-param-should-share-streak']) === 1,
|
||||
plan = params['msg-param-sub-plan'],
|
||||
prime = plan === 'Prime',
|
||||
tier = SUB_TIERS[plan] || 1;
|
||||
|
||||
t.log.info('-> share:', share, 'plan:', plan, 'tier:', tier);
|
||||
|
||||
system_msg = t.i18n.tList('chat.sub.main', '{user} subscribed {plan}. ', {
|
||||
user: <span class="tw-c-text-base tw-strong">{user.displayName}</span>,
|
||||
plan: prime ?
|
||||
t.i18n.t('chat.sub.twitch-prime', 'with Prime Gaming') :
|
||||
t.i18n.t('chat.sub.plan', 'at Tier {tier}', {tier})
|
||||
});
|
||||
|
||||
if ( share && raw_months > 1 )
|
||||
system_msg.push(t.i18n.t(
|
||||
'chat.sub.cumulative-months',
|
||||
"They've subscribed for {cumulative,number} months, currently on a {streak,number} month streak!",
|
||||
{
|
||||
cumulative: cumulative_months,
|
||||
streak: raw_months
|
||||
}
|
||||
));
|
||||
else if ( months > 1 )
|
||||
system_msg.push(t.i18n.t(
|
||||
'chat.sub.months',
|
||||
"They've subscribed for {count,number} months!",
|
||||
{
|
||||
count: months
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
} else if ( msg.system_msg )
|
||||
system_msg = msg.system_msg;
|
||||
|
||||
const tokens = msg.ffz_tokens = msg.ffz_tokens || t.chat.tokenizeMessage(msg, u),
|
||||
rich_content = FFZRichContent && t.chat.pluckRichContent(tokens, msg);
|
||||
|
||||
return (<div class="tw-align-items-start tw-flex tw-flex-nowrap tw-c-text-base">
|
||||
<div class="tw-flex-grow-1" data-room-id={msg.roomID} data-room={msg.roomLogin} data-user-id={user.id} data-user={user.login}>
|
||||
<span class="chat-line__message--badges">{
|
||||
t.chat.badges.render(msg, createElement)
|
||||
}</span>
|
||||
<a
|
||||
class="video-chat__message-author notranslate"
|
||||
data-test-selector="comment-author-selector"
|
||||
href={`/${user.login}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{color}}
|
||||
let out = (<div class="tw-flex-grow-1" data-room-id={msg.roomID} data-room={msg.roomLogin} data-user-id={user.id} data-user={user.login}>
|
||||
<span class="chat-line__message--badges">{
|
||||
t.chat.badges.render(msg, createElement)
|
||||
}</span>
|
||||
<a
|
||||
class="video-chat__message-author notranslate"
|
||||
data-test-selector="comment-author-selector"
|
||||
href={`/${user.login}`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
style={{color}}
|
||||
>
|
||||
<span class="chat-author__display-name" data-a-target="chat-message-username" data-a-user={user.login} data-test-selector="message-username">{ user.displayName }</span>
|
||||
{user.isIntl && <span class="chat-author__intl-login" data-test-selector="message-username-canonical"> ({ user.login})</span>}
|
||||
</a>
|
||||
<div data-test-selector="comment-message-selector" class="tw-inline video-chat__message">
|
||||
<span>{is_action ? ' ' : ': '}</span>
|
||||
<span
|
||||
class={`message ${action_italic ? 'chat-line__message-body--italicized' : ''}`}
|
||||
style={{color: action_color ? color : null}}
|
||||
>
|
||||
<span class="chat-author__display-name" data-a-target="chat-message-username" data-a-user={user.login} data-test-selector="message-username">{ user.displayName }</span>
|
||||
{user.isIntl && <span class="chat-author__intl-login" data-test-selector="message-username-canonical"> ({ user.login})</span>}
|
||||
</a>
|
||||
<div data-test-selector="comment-message-selector" class="tw-inline video-chat__message">
|
||||
<span>{is_action ? ' ' : ': '}</span>
|
||||
<span class="message" style={{color: is_action ? color : null}}>{ t.chat.renderTokens(tokens, createElement) }</span>
|
||||
{rich_content && createElement(FFZRichContent, rich_content)}
|
||||
</div>
|
||||
{ t.chat.renderTokens(tokens, createElement) }
|
||||
</span>
|
||||
{rich_content && createElement(FFZRichContent, rich_content)}
|
||||
</div>
|
||||
</div>);
|
||||
|
||||
if ( system_msg )
|
||||
out = (<div class="tw-flex-grow-1">
|
||||
<div class="tw-flex tw-c-text-alt-2">
|
||||
<div>{system_msg}</div>
|
||||
</div>
|
||||
{out}
|
||||
</div>);
|
||||
|
||||
return (<div class="tw-align-items-start tw-flex tw-flex-nowrap tw-c-text-base">
|
||||
{ out }
|
||||
{ reply ? (<t.MenuContainer
|
||||
context={reply}
|
||||
isCurrentUserModerator={this.props.isCurrentUserModerator}
|
||||
|
@ -412,12 +502,29 @@ export default class VideoChatHook extends Module {
|
|||
is_action: comment.message.isAction,
|
||||
more_replies: comment.moreReplies,
|
||||
timestamp: comment.createdAt,
|
||||
ffz_context: 'video',
|
||||
is_sub: msg_id === 'sub' || msg_id === 'resub',
|
||||
highlight: msg_id === 'highlighted-message'
|
||||
highlight: msg_id === 'highlighted-message',
|
||||
params
|
||||
};
|
||||
|
||||
// TODO: We need to strip the sub message from chat messages
|
||||
// because Twitch is dumb.
|
||||
// We need to strip the sub message from chat messages
|
||||
// because Twitch is dumb. This might need updating to
|
||||
// handle system messages with different syntax.
|
||||
if ( Array.isArray(out.messageParts) && out.messageParts.length && msg_id === 'resub' ) {
|
||||
let content = out.messageParts[0].content;
|
||||
if ( typeof content === 'string' ) {
|
||||
const match = SUB_REGEX.exec(content);
|
||||
if ( match ) {
|
||||
content = content.slice(match[0].length).trimLeft();
|
||||
if ( content.length ) {
|
||||
out.messageParts[0].ffz_content = content;
|
||||
out.system_msg = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.chat.detokenizeMessage(out);
|
||||
|
||||
|
|
|
@ -354,6 +354,11 @@ export class FFZEvent {
|
|||
Object.assign(this, data);
|
||||
}
|
||||
|
||||
_reset() {
|
||||
this.defaultPrevented = false;
|
||||
this.propagationStopped = false;
|
||||
}
|
||||
|
||||
stopPropagation() {
|
||||
this.propagationStopped = true;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue