mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-09-17 02:16:54 +00:00
The In-Line Actions Update
* Add extensible actions system. * Add extensive UI for configuring the actions system. * Add setting to disable channel hosting. * Fix the stupid Rooms thing popping up every time you open a channel. * Fix how we grab chat types from React. * Refactor how we handle incoming chat messages. * Add a hook for outgoing chat messages. * Fix emoji appearing squished with baseline emote alignment. * Display arrows on balloons. * Fix an issue generating emoji URLs. * Do not use the default values for settings with merge strategies if profiles have those settings, just empty. * Display a message in the chat settings menu if we tried opening FFZ's settings and failed. * Wait a bit for webpack's loader if it's not immediately there for some reason. * Probably other stuff. * Not mod cards. Yet.
This commit is contained in:
parent
e9214bb46a
commit
fdde05030f
67 changed files with 7689 additions and 226 deletions
|
@ -4,7 +4,7 @@
|
|||
// Chat Hooks
|
||||
// ============================================================================
|
||||
|
||||
import {ColorAdjuster} from 'utilities/color';
|
||||
import {ColorAdjuster, Color} from 'utilities/color';
|
||||
import {setChildren} from 'utilities/dom';
|
||||
import {has, split_chars} from 'utilities/object';
|
||||
|
||||
|
@ -19,75 +19,79 @@ import EmoteMenu from './emote_menu';
|
|||
import TabCompletion from './tab_completion';
|
||||
|
||||
|
||||
const CHAT_TYPES = (e => {
|
||||
const MESSAGE_TYPES = ((e = {}) => {
|
||||
e[e.Post = 0] = 'Post';
|
||||
e[e.Action = 1] = 'Action';
|
||||
e[e.PostWithMention = 2] = 'PostWithMention';
|
||||
e[e.Ban = 3] = 'Ban';
|
||||
e[e.Timeout = 4] = 'Timeout';
|
||||
e[e.AutoModRejectedPrompt = 5] = 'AutoModRejectedPrompt';
|
||||
e[e.AutoModMessageRejected = 6] = 'AutoModMessageRejected';
|
||||
e[e.AutoModMessageAllowed = 7] = 'AutoModMessageAllowed';
|
||||
e[e.AutoModMessageDenied = 8] = 'AutoModMessageDenied';
|
||||
e[e.Connected = 9] = 'Connected';
|
||||
e[e.Disconnected = 10] = 'Disconnected';
|
||||
e[e.Reconnect = 11] = 'Reconnect';
|
||||
e[e.Hosting = 12] = 'Hosting';
|
||||
e[e.Unhost = 13] = 'Unhost';
|
||||
e[e.Subscription = 14] = 'Subscription';
|
||||
e[e.Resubscription = 15] = 'Resubscription';
|
||||
e[e.SubGift = 16] = 'SubGift';
|
||||
e[e.Clear = 17] = 'Clear';
|
||||
e[e.SubscriberOnlyMode = 18] = 'SubscriberOnlyMode';
|
||||
e[e.FollowerOnlyMode = 19] = 'FollowerOnlyMode';
|
||||
e[e.SlowMode = 20] = 'SlowMode';
|
||||
e[e.RoomMods = 21] = 'RoomMods';
|
||||
e[e.RoomState = 22] = 'RoomState';
|
||||
e[e.Raid = 23] = 'Raid';
|
||||
e[e.Unraid = 24] = 'Unraid';
|
||||
e[e.Notice = 25] = 'Notice';
|
||||
e[e.Info = 26] = 'Info';
|
||||
e[e.BadgesUpdated = 27] = 'BadgesUpdated';
|
||||
e[e.Purchase = 28] = 'Purchase';
|
||||
return e;
|
||||
})({});
|
||||
})();
|
||||
|
||||
|
||||
const MOD_TYPES = ((e = {}) => {
|
||||
e[e.Ban = 0] = 'Ban';
|
||||
e[e.Timeout = 1] = 'Timeout';
|
||||
return e;
|
||||
})();
|
||||
|
||||
|
||||
const AUTOMOD_TYPES = ((e = {}) => {
|
||||
e[e.MessageRejectedPrompt = 0] = 'MessageRejectedPrompt';
|
||||
e[e.MessageRejected = 1] = 'MessageRejected';
|
||||
e[e.MessageAllowed = 2] = 'MessageAllowed';
|
||||
e[e.MessageDenied = 3] = 'MessageDenied';
|
||||
return e;
|
||||
})();
|
||||
|
||||
|
||||
const CHAT_TYPES = ((e = {}) => {
|
||||
e[e.Message = 0] = 'Message';
|
||||
e[e.Moderation = 1] = 'Moderation';
|
||||
e[e.ModerationAction = 2] = 'ModerationAction';
|
||||
e[e.TargetedModerationAction = 3] = 'TargetedModerationAction';
|
||||
e[e.AutoMod = 4] = 'AutoMod';
|
||||
e[e.Connected = 5] = 'Connected';
|
||||
e[e.Disconnected = 6] = 'Disconnected';
|
||||
e[e.Reconnect = 7] = 'Reconnect';
|
||||
e[e.Hosting = 8] = 'Hosting';
|
||||
e[e.Unhost = 9] = 'Unhost';
|
||||
e[e.Hosted = 10] = 'Hosted';
|
||||
e[e.Subscription = 11] = 'Subscription';
|
||||
e[e.Resubscription = 12] = 'Resubscription';
|
||||
e[e.SubGift = 13] = 'SubGift';
|
||||
e[e.Clear = 14] = 'Clear';
|
||||
e[e.SubscriberOnlyMode = 15] = 'SubscriberOnlyMode';
|
||||
e[e.FollowerOnlyMode = 16] = 'FollowerOnlyMode';
|
||||
e[e.SlowMode = 17] = 'SlowMode';
|
||||
e[e.EmoteOnlyMode = 18] = 'EmoteOnlyMode';
|
||||
e[e.RoomMods = 19] = 'RoomMods';
|
||||
e[e.RoomState = 20] = 'RoomState';
|
||||
e[e.Raid = 21] = 'Raid';
|
||||
e[e.Unraid = 22] = 'Unraid';
|
||||
e[e.Ritual = 23] = 'Ritual';
|
||||
e[e.Notice = 24] = 'Notice';
|
||||
e[e.Info = 25] = 'Info';
|
||||
e[e.BadgesUpdated = 26] = 'BadgesUpdated';
|
||||
e[e.Purchase = 27] = 'Purchase';
|
||||
e[e.BitsCharity = 28] = 'BitsCharity';
|
||||
e[e.CrateGift = 29] = 'CrateGift'
|
||||
return e;
|
||||
})();
|
||||
|
||||
|
||||
const NULL_TYPES = [
|
||||
'Reconnect',
|
||||
'RoomState',
|
||||
'BadgesUpdated'
|
||||
'BadgesUpdated',
|
||||
'Clear'
|
||||
];
|
||||
|
||||
|
||||
const EVENTS = [
|
||||
'onJoinedEvent',
|
||||
'onDisconnectedEvent',
|
||||
'onReconnectingEvent',
|
||||
'onChatMessageEvent',
|
||||
'onChatNoticeEvent',
|
||||
'onChatActionEvent',
|
||||
const MISBEHAVING_EVENTS = [
|
||||
'onBitsCharityEvent',
|
||||
//'onRitualEvent', -- handled by conversion to Message event
|
||||
'onBadgesUpdatedEvent',
|
||||
'onHostingEvent',
|
||||
'onUnhostEvent',
|
||||
'onPurchaseEvent',
|
||||
'onCrateEvent',
|
||||
//'onRitualEvent',
|
||||
'onSubscriptionEvent',
|
||||
//'onResubscriptionEvent',
|
||||
'onSubscriptionGiftEvent',
|
||||
'onTimeoutEvent',
|
||||
'onBanEvent',
|
||||
'onClearChatEvent',
|
||||
'onRaidEvent',
|
||||
'onUnraidEvent',
|
||||
'onRoomModsEvent',
|
||||
'onRoomStateEvent',
|
||||
'onFollowerOnlyModeEvent',
|
||||
'onSlowModeEvent',
|
||||
'onSubscriberOnlyModeEvent',
|
||||
'onEmoteOnlyModeEvent',
|
||||
'onBitsCharityEvent'
|
||||
'onCrateEvent'
|
||||
];
|
||||
|
||||
|
||||
|
@ -98,6 +102,7 @@ export default class ChatHook extends Module {
|
|||
this.should_enable = true;
|
||||
|
||||
this.colors = new ColorAdjuster;
|
||||
this.inverse_colors = new ColorAdjuster;
|
||||
|
||||
this.inject('settings');
|
||||
|
||||
|
@ -134,6 +139,12 @@ export default class ChatHook extends Module {
|
|||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
this.RoomPicker = this.fine.define(
|
||||
'chat-picker',
|
||||
n => n.closeRoomPicker && n.handleRoomSelect,
|
||||
Twilight.CHAT_ROUTES
|
||||
);
|
||||
|
||||
|
||||
// Settings
|
||||
|
||||
|
@ -262,7 +273,8 @@ export default class ChatHook extends Module {
|
|||
const is_dark = this.chat.context.get('theme.is-dark'),
|
||||
mode = this.chat.context.get('chat.adjustment-mode'),
|
||||
contrast = this.chat.context.get('chat.adjustment-contrast'),
|
||||
c = this.colors;
|
||||
c = this.colors,
|
||||
ic = this.inverse_colors;
|
||||
|
||||
// TODO: Get the background color from the theme system.
|
||||
// Updated: Use the lightest/darkest colors from alternating rows for better readibility.
|
||||
|
@ -270,6 +282,10 @@ export default class ChatHook extends Module {
|
|||
c.mode = mode;
|
||||
c.contrast = contrast;
|
||||
|
||||
ic._base = is_dark ? '#dad8de' : '#19171c';
|
||||
ic.mode = mode;
|
||||
ic.contrast = is_dark ? 13 : 16;
|
||||
|
||||
this.updateChatLines();
|
||||
}
|
||||
|
||||
|
@ -313,14 +329,20 @@ export default class ChatHook extends Module {
|
|||
}
|
||||
|
||||
|
||||
onEnable() {
|
||||
this.on('site.web_munch:loaded', () => {
|
||||
const ct = this.web_munch.getModule('chat-types');
|
||||
this.chat_types = ct && ct.a || CHAT_TYPES;
|
||||
})
|
||||
|
||||
grabTypes() {
|
||||
const ct = this.web_munch.getModule('chat-types');
|
||||
this.chat_types = ct && ct.a || CHAT_TYPES;
|
||||
if ( ct ) {
|
||||
this.automod_types = ct && ct.a || AUTOMOD_TYPES;
|
||||
this.chat_types = ct && ct.b || CHAT_TYPES;
|
||||
this.message_types = ct && ct.c || MESSAGE_TYPES;
|
||||
this.mod_types = ct && ct.e || MOD_TYPES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onEnable() {
|
||||
this.on('site.web_munch:loaded', this.grabTypes);
|
||||
this.grabTypes();
|
||||
|
||||
this.chat.context.on('changed:chat.width', this.updateChatCSS, this);
|
||||
this.chat.context.on('changed:chat.font-size', this.updateChatCSS, this);
|
||||
|
@ -447,6 +469,19 @@ export default class ChatHook extends Module {
|
|||
for(const inst of instances)
|
||||
this.fixPinnedCheer(inst);
|
||||
});
|
||||
|
||||
|
||||
this.RoomPicker.ready((cls, instances) => {
|
||||
for(const inst of instances)
|
||||
this.closeRoomPicker(inst);
|
||||
});
|
||||
|
||||
this.RoomPicker.on('mount', this.closeRoomPicker, this);
|
||||
}
|
||||
|
||||
|
||||
closeRoomPicker(inst) { // eslint-disable-line class-methods-use-this
|
||||
inst.closeRoomPicker();
|
||||
}
|
||||
|
||||
|
||||
|
@ -480,18 +515,52 @@ export default class ChatHook extends Module {
|
|||
}
|
||||
|
||||
|
||||
sendMessage(room, message) {
|
||||
const controller = this.ChatController.first,
|
||||
service = controller && controller.chatService;
|
||||
|
||||
if ( ! service )
|
||||
return null;
|
||||
|
||||
if ( room.startsWith('#') )
|
||||
room = room.slice(1);
|
||||
|
||||
if ( room.toLowerCase() !== service.channelLogin.toLowerCase() )
|
||||
return null;
|
||||
|
||||
service.sendMessage(message);
|
||||
}
|
||||
|
||||
|
||||
wrapChatService(cls) {
|
||||
const t = this,
|
||||
old_handler = cls.prototype.connectHandlers;
|
||||
old_handler = cls.prototype.connectHandlers,
|
||||
old_send = cls.prototype.sendMessage;
|
||||
|
||||
cls.prototype._ffz_was_here = true;
|
||||
|
||||
|
||||
cls.prototype.sendMessage = function(raw_msg) {
|
||||
const msg = raw_msg.replace(/\n/g, '');
|
||||
|
||||
if ( msg.startsWith('/ffz') ) {
|
||||
this.postMessage({
|
||||
type: t.chat_types.Notice,
|
||||
message: 'The /ffz command is not yet re-implemented.'
|
||||
})
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return old_send.call(this, msg);
|
||||
}
|
||||
|
||||
|
||||
cls.prototype.connectHandlers = function(...args) {
|
||||
if ( ! this._ffz_init ) {
|
||||
const i = this,
|
||||
pm = this.postMessage;
|
||||
const i = this;
|
||||
|
||||
for(const key of EVENTS) {
|
||||
for(const key of MISBEHAVING_EVENTS) {
|
||||
const original = this[key];
|
||||
if ( original )
|
||||
this[key] = function(e, t) {
|
||||
|
@ -510,10 +579,7 @@ export default class ChatHook extends Module {
|
|||
out.sub_months = e.months;
|
||||
out.sub_plan = e.methods;
|
||||
|
||||
i._wrapped = e;
|
||||
const ret = i.postMessage(out);
|
||||
i._wrapped = null;
|
||||
return ret;
|
||||
return i.postMessageToCurrentChannel(e, out);
|
||||
|
||||
} catch(err) {
|
||||
t.log.capture(err, {extra: e});
|
||||
|
@ -528,10 +594,7 @@ export default class ChatHook extends Module {
|
|||
out.ffz_type = 'ritual';
|
||||
out.ritual = e.type;
|
||||
|
||||
i._wrapped = e;
|
||||
const ret = i.postMessage(out);
|
||||
i._wrapped = null;
|
||||
return ret;
|
||||
return i.postMessageToCurrentChannel(e, out);
|
||||
|
||||
} catch(err) {
|
||||
t.log.capture(err, {extra: e});
|
||||
|
@ -551,38 +614,13 @@ export default class ChatHook extends Module {
|
|||
return old_unhost.call(i, e, _t);
|
||||
}
|
||||
|
||||
const old_post = this.postMessage;
|
||||
this.postMessage = function(e) {
|
||||
const original = this._wrapped;
|
||||
if ( original ) {
|
||||
// Check that the message is relevant to this channel.
|
||||
if ( original.channel && this.channelLogin && original.channel.slice(1) !== this.channelLogin.toLowerCase() )
|
||||
return;
|
||||
if ( original && ! e._ffz_checked )
|
||||
return this.postMessageToCurrentChannel(original, e);
|
||||
|
||||
const c = e.channel = original.channel;
|
||||
if ( c )
|
||||
e.roomLogin = c.charAt(0) === '#' ? c.slice(1) : c;
|
||||
|
||||
if ( original.message ) {
|
||||
const u = original.message.user;
|
||||
if ( u )
|
||||
e.emotes = u.emotes;
|
||||
|
||||
if ( typeof original.action === 'string' )
|
||||
e.message = original.action;
|
||||
else
|
||||
e.message = original.message.body;
|
||||
|
||||
// Twitch doesn't generate a proper emote tag for echoed back
|
||||
// actions, so we have to regenerate it. Fun. :D
|
||||
if ( u && u.username === i.userLogin )
|
||||
e.emotes = findEmotes(e.message, i.selfEmotes);
|
||||
}
|
||||
|
||||
//e.original = original;
|
||||
}
|
||||
|
||||
//t.log.info('postMessage', e);
|
||||
return pm.call(this, e);
|
||||
return old_post.call(this, e);
|
||||
}
|
||||
|
||||
this._ffz_init = true;
|
||||
|
@ -590,6 +628,39 @@ export default class ChatHook extends Module {
|
|||
|
||||
return old_handler.apply(this, ...args);
|
||||
}
|
||||
|
||||
cls.prototype.postMessageToCurrentChannel = function(original, message) {
|
||||
message._ffz_checked = true;
|
||||
|
||||
if ( original.channel ) {
|
||||
let chan = message.channel = original.channel.toLowerCase();
|
||||
if ( chan.startsWith('#') )
|
||||
chan = chan.slice(1);
|
||||
|
||||
if ( chan !== this.channelLogin.toLowerCase() )
|
||||
return;
|
||||
|
||||
message.roomLogin = chan;
|
||||
}
|
||||
|
||||
if ( original.message ) {
|
||||
const user = original.message.user;
|
||||
if ( user )
|
||||
message.emotes = user.emotes;
|
||||
|
||||
if ( typeof original.action === 'string' )
|
||||
message.message = original.action;
|
||||
else
|
||||
message.message = original.message.body;
|
||||
|
||||
// Twitch doesn't generate a proper emote tag for echoed back
|
||||
// actions, so we have to regenerate it. Fun. :D
|
||||
if ( user && user.username === this.userLogin )
|
||||
message.emotes = findEmotes(message.message, this.selfEmotes);
|
||||
}
|
||||
|
||||
this.postMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue