mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-27 21:05:53 +00:00
4.69.0
* API Added: When adding a command to tab-completion with the `chat:get-tab-commands` event, you can now specify a `prefix` for your command. Valid options: `!` and `/`. Defaults to `/` if not specified. * API Added: New `chat:update-line` event. Signature: `(messageId: string, clearTokens: boolean = true, clearBadges?: boolean)`. To re-render a chatline without re-tokenizing it, pass `false` as the second argument. * API Changed: Whisper / video chat messages now have their message IDs correctly added to their standardized message objects.
This commit is contained in:
parent
3aeb70f0fb
commit
8807e09ea3
6 changed files with 133 additions and 5 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.68.2",
|
||||
"version": "4.69.0",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -1895,6 +1895,7 @@ export default class Chat extends Module {
|
|||
offset = is_action ? 4 : 0,
|
||||
|
||||
out = msg._ffz_message = {
|
||||
id: msg.id,
|
||||
user: {...msg.from}, // Apollo seals this~
|
||||
message: msg.content.slice(offset),
|
||||
is_action,
|
||||
|
|
|
@ -38,6 +38,7 @@ export default class Line extends Module {
|
|||
onEnable() {
|
||||
this.on('chat.overrides:changed', id => this.updateLinesByUser(id, null, false, false), this);
|
||||
this.on('chat:update-lines-by-user', this.updateLinesByUser, this);
|
||||
this.on('chat:update-line', this.updateLineById, this);
|
||||
this.on('chat:update-lines', this.updateLines, this);
|
||||
this.on('chat:rerender-lines', this.rerenderLines, this);
|
||||
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
||||
|
@ -174,6 +175,21 @@ export default class Line extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
updateLineById(id, clear_tokens = true, clear_badges = null) {
|
||||
if ( clear_badges == null )
|
||||
clear_badges = clear_tokens;
|
||||
|
||||
for(const inst of this.ChatLine.instances) {
|
||||
const msg = inst.props.node;
|
||||
if ( msg?.id === id ) {
|
||||
if ( clear_tokens || clear_badges )
|
||||
this.messages.delete(msg);
|
||||
|
||||
inst.forceUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maybeUpdateLines() {
|
||||
if ( this.chat.context.get('chat.rich.all-links') )
|
||||
|
|
|
@ -12,6 +12,11 @@ import { TWITCH_POINTS_SETS, TWITCH_GLOBAL_SETS, TWITCH_PRIME_SETS, KNOWN_CODES,
|
|||
|
||||
import Twilight from 'site';
|
||||
|
||||
const COMMAND_KEYS = [
|
||||
'!',
|
||||
'/'
|
||||
];
|
||||
|
||||
// Prefer using these statically-allocated collators to String.localeCompare
|
||||
const locale = Intl.Collator();
|
||||
const localeCaseInsensitive = Intl.Collator(undefined, {sensitivity: 'accent'});
|
||||
|
@ -771,9 +776,22 @@ export default class Input extends Module {
|
|||
|
||||
inst._ffz_override = true;
|
||||
inst.oldCommands = inst.getCommands;
|
||||
inst.oldMatches = inst.getMatches;
|
||||
inst.oldReplacement = inst.determineReplacement;
|
||||
|
||||
const t = this;
|
||||
|
||||
inst.getMatches = function(input, unknown, index) {
|
||||
try {
|
||||
return index === 0 && COMMAND_KEYS.includes(input[0])
|
||||
? inst.getCommands(input) : null;
|
||||
|
||||
} catch(err) {
|
||||
t.log.error('Error getting matches from command handler.', err);
|
||||
return inst.oldCommands(input, unknown, index);
|
||||
}
|
||||
}
|
||||
|
||||
inst.getCommands = function(input) { try {
|
||||
const commands = inst.props.getCommands(inst.props.permissionLevel, {
|
||||
isEditor: inst.props.isCurrentUserEditor
|
||||
|
@ -792,9 +810,13 @@ export default class Input extends Module {
|
|||
return null;
|
||||
|
||||
// Trim off the starting /
|
||||
const i = input.slice(1);
|
||||
const prefix = input[0],
|
||||
i = input.slice(1);
|
||||
|
||||
const sorted = commands
|
||||
.filter(cmd => prefix === (cmd.prefix ?? '/') && inst.doesCommandMatchTerm(cmd, i))
|
||||
.sort(inst.sortCommands);
|
||||
|
||||
const sorted = commands.filter(cmd => inst.doesCommandMatchTerm(cmd, i)).sort(inst.sortCommands);
|
||||
const out = [];
|
||||
for(const cmd of sorted) {
|
||||
const arg = cmd.commandArgs?.[0];
|
||||
|
@ -813,12 +835,44 @@ export default class Input extends Module {
|
|||
});
|
||||
}
|
||||
|
||||
// If we're working with a non-/ prefix, and have no items,
|
||||
// return null so we don't display ANY UI.
|
||||
if ( prefix !== '/' && ! out.length )
|
||||
return null;
|
||||
return out;
|
||||
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
return inst.oldCommands(input);
|
||||
}}
|
||||
} }
|
||||
|
||||
inst.determineReplacement = function(cmd) {
|
||||
const out = inst.oldReplacement(cmd);
|
||||
if ( (cmd.prefix ?? '/') !== '/' && out.startsWith('/') )
|
||||
return cmd.prefix + out.slice(1);
|
||||
return out;
|
||||
}
|
||||
|
||||
const React = this.site.getReact(),
|
||||
createElement = React?.createElement;
|
||||
|
||||
if ( createElement )
|
||||
inst.renderCommandSuggestion = function(cmd, input) {
|
||||
const args = Array.isArray(cmd?.commandArgs)
|
||||
? cmd.commandArgs.map(arg => (<div class={`tw-mg-r-05${arg.isRequired ? '' : ' tw-c-text-alt'}`}>[{arg.name}]</div>))
|
||||
: null;
|
||||
|
||||
return (<div class="tw-pd-05">
|
||||
<div class="tw-flex">
|
||||
<div class="tw-mg-r-05">
|
||||
<span>{ cmd.prefix ?? '/' }</span>
|
||||
<span class="tw-semibold">{ cmd.name }</span>
|
||||
</div>
|
||||
{args}
|
||||
</div>
|
||||
<p class="tw-c-text-alt-2 tw-font-size-7">{ cmd.description }</p>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -486,6 +486,7 @@ export default class ChatLine extends Module {
|
|||
async onEnable() {
|
||||
this.on('chat.overrides:changed', id => this.updateLinesByUser(id, null, false, false), this);
|
||||
this.on('chat:update-lines-by-user', this.updateLinesByUser, this);
|
||||
this.on('chat:update-line', this.updateLineById, this);
|
||||
this.on('chat:update-lines', this.updateLines, this);
|
||||
this.on('chat:rerender-lines', this.rerenderLines, this);
|
||||
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
||||
|
@ -1440,6 +1441,39 @@ other {# messages were deleted by a moderator.}
|
|||
}
|
||||
}
|
||||
|
||||
updateLineById(id, clear_tokens = true, clear_badges = null) {
|
||||
if ( clear_badges == null )
|
||||
clear_badges = clear_tokens;
|
||||
|
||||
for(const inst of this.ChatLine.instances) {
|
||||
const msg = inst.props.message;
|
||||
if ( msg?.id === id ) {
|
||||
if ( clear_badges )
|
||||
msg.ffz_badges = msg.ffz_badge_cache = null;
|
||||
|
||||
if ( clear_tokens ) {
|
||||
msg.ffz_tokens = null;
|
||||
msg.ffz_reply = null;
|
||||
msg.highlights = msg.mentioned = msg.mention_color = msg.color_priority = null;
|
||||
}
|
||||
|
||||
inst.forceUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for(const inst of this.WhisperLine.instances) {
|
||||
const msg = inst.props.message?._ffz_message;
|
||||
if ( msg?.id === id ) {
|
||||
// TODO: Better support for clear_tokens and clear_badges
|
||||
if ( clear_badges || clear_tokens )
|
||||
msg._ffz_message = null;
|
||||
|
||||
inst.forceUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLinesByUser(id, login, clear_tokens = true, clear_badges = true) {
|
||||
for(const inst of this.ChatLine.instances) {
|
||||
|
|
|
@ -99,6 +99,7 @@ export default class VideoChatHook extends Module {
|
|||
this.chat.context.on('changed:chat.video-chat.timestamps', this.rerenderLines, this);
|
||||
this.on('chat.overrides:changed', id => this.updateLinesByUser(id, null, false, false), this);
|
||||
this.on('chat:update-lines-by-user', this.updateLinesByUser, this);
|
||||
this.on('chat:update-line', this.updateLineById, this);
|
||||
this.on('chat:update-lines', this.updateLines, this);
|
||||
this.on('chat:rerender-lines', this.rerenderLines, this);
|
||||
this.on('chat:update-line-tokens', this.updateLineTokens, this);
|
||||
|
@ -488,6 +489,27 @@ export default class VideoChatHook extends Module {
|
|||
}
|
||||
|
||||
|
||||
updateLineById(id, clear_tokens = true, clear_badges = null) {
|
||||
if ( clear_badges == null )
|
||||
clear_badges = clear_tokens;
|
||||
|
||||
for(const inst of this.VideoChatLine.instances) {
|
||||
const context = inst.props.messageContext;
|
||||
if ( ! context.comment )
|
||||
continue;
|
||||
|
||||
if ( context.comment?.id === id ) {
|
||||
// TODO: Better support for clear_tokens and clear_badges
|
||||
if ( clear_tokens || clear_badges )
|
||||
context.comment._ffz_message = null;
|
||||
|
||||
inst.forceUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
checkEffects() {
|
||||
for(const inst of this.VideoChatLine.instances) {
|
||||
const context = inst.props.messageContext,
|
||||
|
@ -516,6 +538,7 @@ export default class VideoChatHook extends Module {
|
|||
msg_id = params && params['msg-id'];
|
||||
|
||||
const out = comment._ffz_message = {
|
||||
id: comment.id,
|
||||
user: {
|
||||
color: comment.message.userColor,
|
||||
id: author.id,
|
||||
|
@ -668,4 +691,4 @@ export default class VideoChatHook extends Module {
|
|||
|
||||
room.updateBitsConfig(formatBitsConfig(config));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue