mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-05 02:28:31 +00:00
4.0.0-rc5
* Added: Remove messages entirely using Blocked Terms. * Fixed: Position of emotes in locally echoed chat messages containing emoji. Added a `chat:receive-message` event for processing incoming chat messages before they're added to the buffer.
This commit is contained in:
parent
84589231c8
commit
f15d3c1d4f
7 changed files with 190 additions and 52 deletions
|
@ -1,3 +1,9 @@
|
|||
<div class="list-header">4.0.0-rc5<span>@a36c49ab78f754fcd1c6</span> <time datetime="2018-07-14">(2018-07-14)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Added: Remove messages entirely using Blocked Terms.</li>
|
||||
<li>Fixed: Position of emotes in locally echoed chat messages containing emoji.</li>
|
||||
</ul>
|
||||
|
||||
<div class="list-header">4.0.0-rc4.7<span>@f9f030a275798072a22e</span> <time datetime="2018-07-13">(2018-07-13)</time></div>
|
||||
<ul class="chat-menu-content menu-side-padding">
|
||||
<li>Fixed: Handling of action messages in chat.</li>
|
||||
|
|
|
@ -100,7 +100,7 @@ class FrankerFaceZ extends Module {
|
|||
FrankerFaceZ.Logger = Logger;
|
||||
|
||||
const VER = FrankerFaceZ.version_info = {
|
||||
major: 4, minor: 0, revision: 0, extra: '-rc4.7',
|
||||
major: 4, minor: 0, revision: 0, extra: '-rc5',
|
||||
build: __webpack_hash__,
|
||||
toString: () =>
|
||||
`${VER.major}.${VER.minor}.${VER.revision}${VER.extra || ''}${DEBUG ? '-dev' : ''}`
|
||||
|
|
|
@ -19,6 +19,8 @@ import * as RICH_PROVIDERS from './rich_providers';
|
|||
|
||||
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]';
|
||||
|
||||
|
||||
export default class Chat extends Module {
|
||||
constructor(...args) {
|
||||
|
@ -164,7 +166,7 @@ export default class Chat extends Module {
|
|||
|
||||
for(const [key, list] of colors) {
|
||||
if ( list[0].length )
|
||||
list[1].push(`\\b(?:${list[0].join('|')})\\b`);
|
||||
list[1].push(`(^|.*?${SEPARATORS})(?:${list[0].join('|')})(?=$|${SEPARATORS})`);
|
||||
|
||||
colors.set(key, new RegExp(list[1].join('|'), 'gi'));
|
||||
}
|
||||
|
@ -180,7 +182,8 @@ export default class Chat extends Module {
|
|||
always_inherit: true,
|
||||
ui: {
|
||||
path: 'Chat > Filtering >> Blocked Terms',
|
||||
component: 'basic-terms'
|
||||
component: 'basic-terms',
|
||||
removable: true
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -192,7 +195,10 @@ export default class Chat extends Module {
|
|||
if ( ! val || ! val.length )
|
||||
return null;
|
||||
|
||||
const out = [[], []];
|
||||
const out = [
|
||||
[[], []],
|
||||
[[], []]
|
||||
];
|
||||
|
||||
for(const item of val) {
|
||||
const t = item.t;
|
||||
|
@ -210,16 +216,15 @@ export default class Chat extends Module {
|
|||
if ( ! v || ! v.length )
|
||||
continue;
|
||||
|
||||
out[word ? 0 : 1].push(v);
|
||||
out[item.remove ? 1 : 0][word ? 0 : 1].push(v);
|
||||
}
|
||||
|
||||
if ( out[0].length )
|
||||
out[1].push(`\\b(?:${out[0].join('|')})\\b`);
|
||||
return out.map(data => {
|
||||
if ( data[0].length )
|
||||
data[1].push(`(^|.*?${SEPARATORS})(?:${data[0].join('|')})(?=$|${SEPARATORS})`);
|
||||
|
||||
if ( ! out[1].length )
|
||||
return;
|
||||
|
||||
return new RegExp(out[1].join('|'), 'gi');
|
||||
return data[1].length ? new RegExp(data[1].join('|'), 'gi') : null;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -295,7 +295,9 @@ export const CustomHighlights = {
|
|||
let idx = 0, match;
|
||||
|
||||
while((match = regex.exec(text))) {
|
||||
const nix = match.index;
|
||||
const raw_nix = match.index,
|
||||
offset = match[1] ? match[1].length : 0,
|
||||
nix = raw_nix + offset;
|
||||
|
||||
if ( idx !== nix )
|
||||
out.push({type: 'text', text: text.slice(idx, nix)});
|
||||
|
@ -305,10 +307,10 @@ export const CustomHighlights = {
|
|||
|
||||
out.push({
|
||||
type: 'highlight',
|
||||
text: match[0]
|
||||
text: match[0].slice(offset)
|
||||
});
|
||||
|
||||
idx = nix + match[0].length;
|
||||
idx = raw_nix + match[0].length;
|
||||
}
|
||||
|
||||
if ( idx < text.length )
|
||||
|
@ -323,6 +325,45 @@ export const CustomHighlights = {
|
|||
}
|
||||
|
||||
|
||||
function blocked_process(tokens, msg, regex, do_remove) {
|
||||
const out = [];
|
||||
for(const token of tokens) {
|
||||
if ( token.type !== 'text' ) {
|
||||
out.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
regex.lastIndex = 0;
|
||||
const text = token.text;
|
||||
let idx = 0, match;
|
||||
|
||||
while((match = regex.exec(text))) {
|
||||
const raw_nix = match.index,
|
||||
offset = match[1] ? match[1].length : 0,
|
||||
nix = raw_nix + offset;
|
||||
|
||||
if ( idx !== nix )
|
||||
out.push({type: 'text', text: text.slice(idx, nix)});
|
||||
|
||||
out.push({
|
||||
type: 'blocked',
|
||||
text: match[0].slice(offset)
|
||||
});
|
||||
|
||||
if ( do_remove )
|
||||
msg.ffz_removed = true;
|
||||
|
||||
idx = raw_nix + match[0].length;
|
||||
}
|
||||
|
||||
if ( idx < text.length )
|
||||
out.push({type: 'text', text: text.slice(idx)});
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
export const BlockedTerms = {
|
||||
type: 'blocked',
|
||||
priority: 99,
|
||||
|
@ -349,44 +390,21 @@ export const BlockedTerms = {
|
|||
]
|
||||
},
|
||||
|
||||
process(tokens) {
|
||||
process(tokens, msg) {
|
||||
if ( ! tokens || ! tokens.length )
|
||||
return tokens;
|
||||
|
||||
const regex = this.context.get('chat.filtering.highlight-basic-blocked--regex');
|
||||
if ( ! regex )
|
||||
const regexes = this.context.get('chat.filtering.highlight-basic-blocked--regex');
|
||||
if ( ! regexes )
|
||||
return tokens;
|
||||
|
||||
const out = [];
|
||||
for(const token of tokens) {
|
||||
if ( token.type !== 'text' ) {
|
||||
out.push(token);
|
||||
continue;
|
||||
}
|
||||
if ( regexes[0] )
|
||||
tokens = blocked_process(tokens, msg, regexes[0], false);
|
||||
|
||||
regex.lastIndex = 0;
|
||||
const text = token.text;
|
||||
let idx = 0, match;
|
||||
if ( regexes[1] )
|
||||
tokens = blocked_process(tokens, msg, regexes[1], true);
|
||||
|
||||
while((match = regex.exec(text))) {
|
||||
const nix = match.index;
|
||||
|
||||
if ( idx !== nix )
|
||||
out.push({type: 'text', text: text.slice(idx, nix)});
|
||||
|
||||
out.push({
|
||||
type: 'blocked',
|
||||
text: match[0]
|
||||
});
|
||||
|
||||
idx = nix + match[0].length;
|
||||
}
|
||||
|
||||
if ( idx < text.length )
|
||||
out.push({type: 'text', text: text.slice(idx)});
|
||||
}
|
||||
|
||||
return out;
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<term-editor
|
||||
:term="default_term"
|
||||
:colored="item.colored"
|
||||
:removable="item.removable"
|
||||
:adding="true"
|
||||
@save="new_term"
|
||||
/>
|
||||
|
@ -16,6 +17,7 @@
|
|||
:key="term.id"
|
||||
:term="term.v"
|
||||
:colored="item.colored"
|
||||
:removable="item.removable"
|
||||
@remove="remove(term)"
|
||||
@save="save(term, $event)"
|
||||
/>
|
||||
|
@ -39,7 +41,8 @@ export default {
|
|||
default_term: {
|
||||
v: '',
|
||||
t: 'text',
|
||||
c: ''
|
||||
c: '',
|
||||
remove: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -48,6 +48,31 @@
|
|||
<option value="raw">{{ t('setting.terms.type.regex', 'Regex') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="removable" class="tw-flex-shrink-0 tw-mg-r-05 tw-tooltip-wrapper">
|
||||
<button
|
||||
v-if="editing"
|
||||
:class="{active: edit_data.remove}"
|
||||
class="tw-button ffz-directory-toggle-block"
|
||||
@click="toggleRemove"
|
||||
>
|
||||
<span
|
||||
:class="edit_data.remove ? 'ffz-i-eye-off' : 'ffz-i-eye'"
|
||||
class="tw-button__text"
|
||||
/>
|
||||
</button>
|
||||
<span
|
||||
v-else-if="term.remove"
|
||||
class="ffz-i-eye-off tw-pd-x-1"
|
||||
/>
|
||||
<div class="tw-tooltip tw-tooltip--down tw-tooltip--align-right">
|
||||
<span v-if="display.remove">
|
||||
{{ t('setting.terms.remove.on', 'Remove matching messages from chat.') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ t('setting.terms.remove.off', 'Do not remove matching messages from chat.') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="adding" class="tw-flex-shrink-0">
|
||||
<button class="tw-button" @click="save">
|
||||
<span class="tw-button__text">
|
||||
|
@ -107,6 +132,8 @@ import safety from 'safe-regex';
|
|||
|
||||
import {deep_copy, glob_to_regex, escape_regex} from 'utilities/object';
|
||||
|
||||
let id = 0;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
term: Object,
|
||||
|
@ -114,6 +141,10 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
removable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
adding: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
@ -123,12 +154,14 @@ export default {
|
|||
data() {
|
||||
if ( this.adding )
|
||||
return {
|
||||
editor_id: id++,
|
||||
deleting: false,
|
||||
editing: true,
|
||||
edit_data: deep_copy(this.term)
|
||||
};
|
||||
|
||||
return {
|
||||
editor_id: id++,
|
||||
deleting: false,
|
||||
editing: false,
|
||||
edit_data: null
|
||||
|
@ -136,8 +169,12 @@ export default {
|
|||
},
|
||||
|
||||
computed: {
|
||||
display() {
|
||||
return this.editing ? this.edit_data : this.term;
|
||||
},
|
||||
|
||||
is_valid() {
|
||||
const data = this.editing ? this.edit_data : this.term,
|
||||
const data = this.display,
|
||||
t = data.t;
|
||||
|
||||
let v = data.v;
|
||||
|
@ -157,7 +194,7 @@ export default {
|
|||
},
|
||||
|
||||
is_safe() {
|
||||
const data = this.editing ? this.edit_data : this.term,
|
||||
const data = this.display,
|
||||
t = data.t;
|
||||
|
||||
let v = data.v;
|
||||
|
@ -195,6 +232,11 @@ export default {
|
|||
this.edit_data = deep_copy(this.term);
|
||||
},
|
||||
|
||||
toggleRemove() {
|
||||
if ( this.editing )
|
||||
this.edit_data.remove = ! this.edit_data.remove;
|
||||
},
|
||||
|
||||
cancel() {
|
||||
if ( this.adding ) {
|
||||
this.edit_data = deep_copy(this.term);
|
||||
|
|
|
@ -424,7 +424,7 @@ export default class ChatHook extends Module {
|
|||
this.updateMentionCSS();
|
||||
|
||||
this.ChatController.on('mount', this.chatMounted, this);
|
||||
this.ChatController.on('unmount', this.removeRoom, this);
|
||||
this.ChatController.on('unmount', this.chatUmounted, this);
|
||||
this.ChatController.on('receive-props', this.chatUpdated, this);
|
||||
|
||||
this.ChatController.ready((cls, instances) => {
|
||||
|
@ -471,6 +471,9 @@ export default class ChatHook extends Module {
|
|||
if ( ! buffer._ffz_was_here )
|
||||
this.wrapChatBuffer(buffer.constructor);
|
||||
|
||||
buffer.consumeChatEvent = buffer.ffzConsumeChatEvent.bind(buffer);
|
||||
buffer.ffzController = inst;
|
||||
|
||||
service.client.events.removeAll();
|
||||
service.connectHandlers();
|
||||
|
||||
|
@ -530,10 +533,57 @@ export default class ChatHook extends Module {
|
|||
|
||||
|
||||
wrapChatBuffer(cls) {
|
||||
const t = this;
|
||||
const t = this,
|
||||
old_consume = cls.prototype.consumeChatEvent;
|
||||
|
||||
cls.prototype._ffz_was_here = true;
|
||||
|
||||
cls.prototype.ffzConsumeChatEvent = cls.prototype.consumeChatEvent = function(msg) {
|
||||
if ( msg ) {
|
||||
try {
|
||||
const types = t.chat_types || {};
|
||||
|
||||
if ( msg.type === types.Message ) {
|
||||
const m = t.chat.standardizeMessage(msg),
|
||||
cont = this.ffzController;
|
||||
|
||||
let room = m.roomLogin = m.roomLogin ? m.roomLogin : m.channel ? m.channel.slice(1) : cont && cont.props.channelLogin,
|
||||
room_id = cont && cont.props.channelID;
|
||||
|
||||
if ( ! room && room_id ) {
|
||||
const r = t.chat.getRoom(room_id, null, true);
|
||||
if ( r && r.login )
|
||||
room = m.roomLogin = r.login;
|
||||
}
|
||||
|
||||
const u = t.site.getUser(),
|
||||
r = {id: room_id, login: room};
|
||||
|
||||
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);
|
||||
|
||||
const event = new FFZEvent({
|
||||
message: m,
|
||||
channel: room
|
||||
});
|
||||
|
||||
t.emit('chat:receive-message', event);
|
||||
if ( event.defaultPrevented || m.ffz_removed )
|
||||
return;
|
||||
}
|
||||
|
||||
} catch(err) {
|
||||
t.log.capture(err, {extra: {msg}})
|
||||
}
|
||||
}
|
||||
|
||||
return old_consume.call(this, msg);
|
||||
}
|
||||
|
||||
cls.prototype.toArray = function() {
|
||||
const buf = this.buffer,
|
||||
size = t.chat.context.get('chat.scrollback-length'),
|
||||
|
@ -655,7 +705,7 @@ export default class ChatHook extends Module {
|
|||
this.onChatMessageEvent = function(e) {
|
||||
if ( e && e.sentByCurrentUser ) {
|
||||
try {
|
||||
e.message.ffz_emotes = findEmotes(
|
||||
e.message.user.emotes = findEmotes(
|
||||
e.message.body,
|
||||
i.ffzGetEmotes()
|
||||
);
|
||||
|
@ -673,7 +723,7 @@ export default class ChatHook extends Module {
|
|||
this.onChatActionEvent = function(e) {
|
||||
if ( e && e.sentByCurrentUser ) {
|
||||
try {
|
||||
e.message.ffz_emotes = findEmotes(
|
||||
e.message.user.emotes = findEmotes(
|
||||
e.message.body.slice(8, -1),
|
||||
i.ffzGetEmotes()
|
||||
);
|
||||
|
@ -853,6 +903,9 @@ export default class ChatHook extends Module {
|
|||
// ========================================================================
|
||||
|
||||
chatMounted(chat, props) {
|
||||
if ( chat.chatBuffer )
|
||||
chat.chatBuffer.ffzController = chat;
|
||||
|
||||
if ( ! props )
|
||||
props = chat.props;
|
||||
|
||||
|
@ -863,7 +916,18 @@ export default class ChatHook extends Module {
|
|||
}
|
||||
|
||||
|
||||
chatUmounted(chat) {
|
||||
if ( chat.chatBuffer && chat.chatBuffer.ffzController === this )
|
||||
chat.chatBuffer.ffzController = null;
|
||||
|
||||
this.removeRoom(chat);
|
||||
}
|
||||
|
||||
|
||||
chatUpdated(chat, props) {
|
||||
if ( chat.chatBuffer )
|
||||
chat.chatBuffer.ffzController = chat;
|
||||
|
||||
if ( props.channelID !== chat.props.channelID ) {
|
||||
this.removeRoom(chat);
|
||||
this.chatMounted(chat, props);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue