1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-27 21:05:53 +00:00
* Added: Setting to automatically accept content warnings when opening a stream.
* Added: Three settings to control the appearance of Hype Chat messages.
* Changed: Added a few keywords to settings entries in the `Data Management` category for better search support.
* Developer: The chat and pubsub logging utility can now inject pubsub messages for testing.
This commit is contained in:
SirStendec 2023-06-26 13:11:27 -04:00
parent eea9d51bdc
commit 7f109b4b48
11 changed files with 273 additions and 16 deletions

View file

@ -1,7 +1,7 @@
{
"name": "frankerfacez",
"author": "Dan Salvato LLC",
"version": "4.48.1",
"version": "4.49.0",
"description": "FrankerFaceZ is a Twitch enhancement suite.",
"private": true,
"license": "Apache-2.0",

View file

@ -25,12 +25,18 @@
<option
v-for="(sample, idx) in samples"
:key="idx"
:selected="sample.data === message"
:value="sample.data"
:selected="sample.data === message && sample.topic === topic"
:value="idx"
>
{{ sample.name }}
</option>
</select>
<input
ref="topic"
class="tw-block tw-font-size-6 tw-full-width ffz-textarea ffz-mg-t-1p"
@blur="updateMessage"
@input="onMessageChange"
/>
<textarea
ref="message"
class="tw-block tw-font-size-6 tw-full-width ffz-textarea ffz-mg-t-1p tw-border-bottom-left-radius-medium tw-border-bottom-right-radius-medium"
@ -220,14 +226,20 @@ export default {
const state = window.history.state;
const samples = deep_copy(LOADED_SAMPLES);
const message = state?.ffz_ct_message ?? samples[0].data;
const topic = state?.ffz_ct_topic ?? samples[0].topic ?? '';
let is_custom = true;
for(const item of samples) {
if (item.data === message) {
/*for(const item of samples) {
if ( ! item.topic )
item.topic = '';
if ( typeof item.data !== 'string' )
item.data = JSON.stringify(item.data, null, 4);
if (item.data === message && item.topic === topic) {
is_custom = false;
break;
}
}
}*/
return {
has_client: false,
@ -235,6 +247,7 @@ export default {
samples,
is_custom,
message,
topic,
replay_fix: state?.ffz_ct_replay ?? true,
ignore_privmsg: state?.ffz_ct_privmsg ?? false,
@ -252,6 +265,11 @@ export default {
this.$refs.message.value = this.message;
},
topic() {
if ( ! this.is_custom )
this.$refs.topic.value = this.topic;
},
capture_chat() {
if ( this.capture_chat )
this.listenChat();
@ -292,6 +310,7 @@ export default {
mounted() {
this.$refs.message.value = this.message;
this.$refs.topic.value = this.topic;
},
methods: {
@ -336,8 +355,12 @@ export default {
has_loaded_samples = true;
for(const item of values) {
if ( ! item.topic )
item.topic = '';
if ( Array.isArray(item.data) )
item.data = item.data.join('\n\n');
else if ( typeof item.data !== 'string' )
item.data = JSON.stringify(item.data, null, 4);
}
LOADED_SAMPLES = values;
@ -345,7 +368,7 @@ export default {
let is_custom = true;
for(const item of this.samples) {
if (item.data === this.message) {
if (item.data === this.message && item.topic === this.topic) {
is_custom = false;
break;
}
@ -503,29 +526,34 @@ export default {
// Event Handlers
onSelectChange() {
const raw_value = this.$refs.selector.value;
const idx = this.$refs.selector.value,
item = this.samples[idx];
if ( raw_value && raw_value !== 'custom' ) {
this.message = raw_value;
if ( idx !== 'custom' && item?.data ) {
this.message = item.data;
this.topic = item.topic ?? '';
this.is_custom = false;
} else
this.is_custom = true;
},
updateMessage() {
const value = this.$refs.message.value;
const value = this.$refs.message.value,
topic = this.$refs.topic.value;
let is_custom = true;
for(const item of this.samples) {
if (item.data === value) {
if (item.data === value && item.topic === topic) {
is_custom = false;
break;
}
}
this.is_custom = is_custom;
if ( this.is_custom )
if ( this.is_custom ) {
this.topic = topic;
this.message = value;
}
},
onMessageChange() {
@ -581,6 +609,26 @@ export default {
},
playMessage() {
// Check for PubSub
if ( this.topic.trim().length > 0 ) {
let data;
try {
data = JSON.parse(this.message);
} catch(err) {
console.error(err);
alert("Unable to parse message.");
return;
}
this.replayItem({
pubsub: true,
topic: this.topic,
data
});
return;
}
const msgs = [];
const parts = this.message.split(/\r?\n/g);
@ -601,6 +649,18 @@ export default {
},
replayItem(item) {
if ( item.pubsub ) {
const channel = this.chat.ChatService.first?.props?.channelID;
if ( this.replay_fix ) {
item.topic = item.topic.replace(/<channel>/gi, channel);
// TODO: Crawl, replacing ids.
// TODO: Update timestamps for pinned chat?
}
this.chat.resolve('site.subpump').inject(item.topic, item.data);
}
if ( item.chat ) {
// While building the string, also build colors for the console log.
const out = [];

View file

@ -63,18 +63,21 @@ export default class MainMenu extends Module {
this.settings.addUI('backup', {
path: 'Data Management > Backup and Restore @{"profile_warning": false}',
component: 'backup-restore',
getExtraTerms: () => ['restore'],
getFFZ: () => this.resolve('core')
});
this.settings.addUI('clear', {
path: 'Data Management > Storage @{"profile_warning": false} >> tabs ~> Clear',
component: 'clear-settings',
getExtraTerms: () => ['reset'],
force_seen: true
});
this.settings.addUI('provider', {
path: 'Data Management > Storage >> tabs ~> Provider',
component: 'provider',
getExtraTerms: () => ['storage', 'local', 'indexeddb', 'localstorage'],
force_seen: true
});

View file

@ -25,5 +25,18 @@
"@badge-info=;badges=premium/1;color=;display-name=HellbirDza;emotes=;flags=;id=f694b0fc-0b5e-4adf-8002-03dae340e9b5;login=hellbirdza;mod=0;msg-id=subgift;msg-param-gift-months=1;msg-param-months=1;msg-param-origin-id=ee\\se2\\s3f\\s01\\s75\\s45\\se1\\sfa\\s24\\s47\\s29\\s08\\sdb\\sf8\\sab\\se1\\s56\\s27\\s83\\s2e;msg-param-recipient-display-name=corette0;msg-param-recipient-id=149790291;msg-param-recipient-user-name=corette0;msg-param-sender-count=0;msg-param-sub-plan-name=Channel\\sSubscription\\s(asmongold);msg-param-sub-plan=1000;room-id=26261471;subscriber=0;system-msg=HellbirDza\\sgifted\\sa\\sTier\\s1\\ssub\\sto\\scorette0!;tmi-sent-ts=1671302976767;user-id=28678305;user-type= :tmi.twitch.tv USERNOTICE #asmongold",
"@badge-info=;badges=premium/1;color=;display-name=HellbirDza;emotes=;flags=;id=5893d8a8-5eb3-46d6-9737-f1b2b76400d4;login=hellbirdza;mod=0;msg-id=subgift;msg-param-gift-months=1;msg-param-months=2;msg-param-origin-id=ee\\se2\\s3f\\s01\\s75\\s45\\se1\\sfa\\s24\\s47\\s29\\s08\\sdb\\sf8\\sab\\se1\\s56\\s27\\s83\\s2e;msg-param-recipient-display-name=yo_adg;msg-param-recipient-id=465861822;msg-param-recipient-user-name=yo_adg;msg-param-sender-count=0;msg-param-sub-plan-name=Channel\\sSubscription\\s(asmongold);msg-param-sub-plan=1000;room-id=26261471;subscriber=0;system-msg=HellbirDza\\sgifted\\sa\\sTier\\s1\\ssub\\sto\\syo_adg!;tmi-sent-ts=1671302976798;user-id=28678305;user-type= :tmi.twitch.tv USERNOTICE #asmongold"
]
},
{
"name": "Hype Chat (Message)",
"data": "@badge-info=;badges=partner/1;color=#D6A3FF;display-name=GalaxyAUS;emotes=;first-msg=0;flags=;id=ab02f65f-c600-4e95-a97d-48163f686830;mod=0;pinned-chat-paid-amount=170;pinned-chat-paid-canonical-amount=170;pinned-chat-paid-currency=AUD;pinned-chat-paid-exponent=2;pinned-chat-paid-is-system-message=0;pinned-chat-paid-level=ONE;returning-chatter=0;room-id=29158331;subscriber=0;tmi-sent-ts=1687494193366;turbo=0;user-id=68458131;user-type= :galaxyaus!galaxyaus@galaxyaus.tmi.twitch.tv PRIVMSG #cardboard_cowboy :SCIENCE"
},
{
"name": "Hype Chat (No Message)",
"data": "@badge-info=;badges=;color=#00FF7F;display-name=옥메왁까;emotes=;first-msg=0;flags=;id=36999eea-314c-4bb8-8cc2-8be1175c3e31;mod=0;pinned-chat-paid-amount=7500;pinned-chat-paid-canonical-amount=7500;pinned-chat-paid-currency=KRW;pinned-chat-paid-exponent=0;pinned-chat-paid-is-system-message=1;pinned-chat-paid-level=TWO;returning-chatter=0;room-id=148057505;subscriber=0;tmi-sent-ts=1687458800209;turbo=0;user-id=184488771;user-type= :okmewaka!okmewaka@okmewaka.tmi.twitch.tv PRIVMSG #tmxk319 :User sent Hype Chat"
},
{
"name": "Hype Chat (PubSub)",
"topic": "pinned-chat-updates-v1.<channel>",
"data": {"type":"pin-message","data":{"id":"deea93ac-66c7-4500-aa38-d9cfb82f14bc","pinned_by":{"id":"128900149","display_name":"JailBreakRules"},"message":{"id":"deea93ac-66c7-4500-aa38-d9cfb82f14bc","sender":{"id":"128900149","display_name":"JailBreakRules","badges":[{"id":"subscriber","version":"12"},{"id":"glhf-pledge","version":"1"}],"chat_color":"#F31995"},"content":{"text":"xQc gets 70 cents off this lmfao","fragments":[{"text":"xQc gets 70 cents off this lmfao"}]},"type":"PAID","starts_at":1687737336,"updated_at":1687737336,"ends_at":1687737366,"sent_at":1687737336,"metadata":{"amount":"100","canonical-amount":"100","currency":"USD","exponent":"2","isSystemMessage":"false","level":"ONE"}}}}
}
]

View file

@ -105,6 +105,21 @@ export default class PlayerBase extends Module {
changed: val => this.css_tweaks.toggle('player-fade-paused', val)
});
this.settings.add('player.disable-content-warnings', {
default: false,
ui: {
path: 'Player > General >> General',
title: 'Do not display content warnings.',
description: 'When this is enabled, FFZ will automatically skip content warnings. This feature is intended for use by adults only.',
component: 'setting-check-box'
},
changed: () => {
for(const inst of this.Player.instances)
this.skipContentWarnings(inst);
}
});
if ( HAS_COMPRESSOR ) {
this.settings.add('player.compressor.enable', {
default: true,
@ -1208,7 +1223,19 @@ export default class PlayerBase extends Module {
this.css_tweaks.toggleHide('player-ext', val === 2);
}
skipContentWarnings(inst) {
if ( ! this.settings.get('player.disable-content-warnings') )
return;
const cont = this.fine.getHostNode(inst),
btn = cont && cont.querySelector('button[data-a-target="content-classification-gate-overlay-start-watching-button"]');
if ( btn )
btn.click();
}
updateGUI(inst) {
this.skipContentWarnings(inst);
this.addPiPButton(inst);
this.addResetButton(inst);
this.addCompressorButton(inst, false);

View file

@ -599,6 +599,21 @@ export default class ChatHook extends Module {
}
});
this.settings.add('chat.hype.message-style', {
default: 1,
ui: {
path: 'Chat > Hype Chat >> Appearance',
title: 'Hype Chat Style',
component: 'setting-select-box',
description: '**Note**: Hype Chats that include messages will always have their messages displayed, regardless of setting. Changes made to this setting may not affect existing chat messages.',
data: [
{value: 0, title: 'Do Not Display'},
{value: 1, title: 'Standard Twitch (Large, Colored, Limited FFZ Support)'},
{value: 2, title: 'Minimal (Marked with System Message, No Colors)' }
]
}
});
this.settings.add('chat.subs.show', {
default: 3,
ui: {
@ -1421,6 +1436,11 @@ export default class ChatHook extends Module {
if ( event.prefix !== 'community-points-channel-v1' || this.disable_handling )
return;
if ( event.prefix === 'pinned-chat-updates-v1' ) {
this.log.info('Pinned Chat', event);
return;
}
const service = this.ChatService.first,
message = event.message,
data = message?.data?.redemption;
@ -2359,6 +2379,43 @@ export default class ChatHook extends Module {
return old_state.call(i, e);
}
const old_pinned = this.onPinnedChatEvent;
this.onPinnedChatEvent = function(e) {
try {
const setting = t.chat.context.get('chat.hype.message-style');
if ( setting !== 1 ) {
// Drop messages with no message if we're not displaying them.
if ( e.isSystemMessage && setting === 0 )
return;
const out = i.convertMessage(e);
out.ffz_type = 'hype';
out.hype_amount = e.amount;
out.hype_canonical_amount = e.canonical_amount;
out.hype_currency = e.currency;
out.hype_exponent = e.exponent;
out.hype_level = e.level;
if ( e.isSystemMessage ) {
// Delete the message it comes with.
out.message = '';
out.messageBody = '';
out.messageParts = [];
e.message.body = '';
}
//t.log.info('Pinned Event', e, out);
return i.postMessageToCurrentChannel(e, out);
}
} catch(err) {
t.log.capture(err, {extra: e});
}
return old_pinned.call(i, e);
}
const old_resub = this.onResubscriptionEvent;
this.onResubscriptionEvent = function(e) {
try {

View file

@ -72,6 +72,15 @@ export default class Input extends Module {
// Settings
this.settings.add('chat.hype.display-input', {
default: true,
ui: {
path: 'Chat > Hype Chat >> Input',
title: 'Allow the Hype Chat button to appear in the chat input element.',
component: 'setting-check-box'
}
});
this.settings.add('chat.inline-preview.enabled', {
default: true,
ui: {
@ -203,6 +212,7 @@ export default class Input extends Module {
}
async onEnable() {
this.chat.context.on('changed:chat.hype.display-input', () => this.ChatInput.forceUpdate());
this.chat.context.on('changed:chat.actions.room', () => this.ChatInput.forceUpdate());
this.chat.context.on('changed:chat.actions.room-above', () => this.ChatInput.forceUpdate());
this.chat.context.on('changed:chat.tab-complete.emotes-without-colon', enabled => {
@ -242,7 +252,15 @@ export default class Input extends Module {
cls.prototype.render = function() {
const out = old_render.call(this);
try {
const hide_hype = ! t.chat.context.get('chat.hype.display-input');
if ( hide_hype ) {
const frag = findReactFragment(out, n => n.key === 'paidPinnedMessage');
if ( frag )
frag.type = () => null;
}
const above = t.chat.context.get('chat.actions.room-above'),
state = t.chat.context.get('context.chat_state') || {},
container = above ? findReactFragment(out, n => n.props && Array.isArray(n.props.children)) : findReactFragment(out, n => n.props && n.props.className === 'chat-input__buttons-container');

View file

@ -46,6 +46,61 @@ export default class ChatLine extends Module {
}
};
this.line_types.hype = {
renderNotice: (msg, current_user, room, inst, e) => {
const setting = this.chat.context.get('chat.hype.message-style');
if ( setting === 0 )
return null;
// We need to get the message's tokens to see if it has a message or not.
const user = msg.user,
tokens = msg.ffz_tokens = msg.ffz_tokens || this.chat.tokenizeMessage(msg, current_user),
has_message = tokens.length > 0;
let amount = msg.hype_amount;
const digits = msg.hype_exponent ?? 0;
if ( digits > 0 )
amount /= Math.pow(10, digits);
try {
// TODO: Cache formatter?
const fmt = new Intl.NumberFormat(navigator.languages, {
style: 'currency',
currency: msg.hype_currency,
compactDisplay: 'short',
minimumFractionDigits: digits,
maximumFractionDigits: digits
});
amount = fmt.format(amount);
} catch(err) {
amount = `${msg.hype_currency} ${amount}`;
}
if (! has_message)
return this.i18n.tList('chat.hype-chat.user', '{user} sent a Hype Chat for {amount}!', {
amount,
user: e('span', {
role: 'button',
className: 'chatter-name',
onClick: inst.ffz_user_click_handler,
onContextMenu: this.actions.handleUserContext
}, e('span', {
className: 'tw-c-text-base tw-strong'
}, user.displayName))
});
return this.i18n.tList(
'chat.hype-chat',
'Sent a Hype Chat for {amount}!',
{
amount
}
)
}
};
this.line_types.cheer = {
renderNotice: (msg, current_user, room, inst, e) => {
return this.i18n.tList(

View file

@ -50,6 +50,8 @@ const CLASSES = {
'last-x-events': '.last-x-events_container',
'pinned-hype-chat': '.paid-pinned-chat-message-list',
'ci-mod-view': '.chat-input__buttons-container a[href*="/moderator"]',
'ci-highlight-settings': '.chat-input__buttons-container button[data-highlight-selector="chat-highlights-shortcut"]',
'ci-shield-mode': '.chat-input__buttons-container > div:last-child button[class|="ScCoreButton"]:not([data-highlight-selector]):not([data-a-target])'
@ -364,6 +366,16 @@ export default class CSSTweaks extends Module {
}
});
this.settings.add('chat.hype.show-pinned', {
default: true,
ui: {
path: 'Chat > Hype Chat >> Appearance',
title: 'Allow Hype Chat messages to appear pinned at the top of chat.',
component: 'setting-check-box'
},
changed: val => this.toggleHide('pinned-hype-chat', ! val)
});
this.settings.add('chat.bits.show', {
default: true,
ui: {
@ -466,6 +478,8 @@ export default class CSSTweaks extends Module {
this.toggleHide('top-discover', !this.settings.get('layout.discover'));
this.toggle('hide-unfollow-button', this.settings.get('channel.hide-unfollow'));
this.toggleHide('pinned-hype-chat', ! this.settings.get('chat.hype.show-pinned'));
this.toggle('square-avatars', ! this.settings.get('channel.round-avatars'));
//this.toggleHide('not-live-bar', this.settings.get('channel.hide-not-live-bar'));
this.toggleHide('channel-live-ind', this.settings.get('channel.hide-live-indicator'));

View file

@ -86,10 +86,10 @@ export default {
if ( ! this.item || ! this.item.tabs )
return [];
if ( ! this.context.matches_only )
//if ( ! this.context.matches_only )
return this.item.tabs;
return this.item.tabs.filter(tab => this.shouldShow(tab));
//return this.item.tabs.filter(tab => this.shouldShow(tab));
},
visibleContents() {
@ -189,6 +189,15 @@ export default {
},
select(idx) {
/*const selected = this.visibleTabs[idx];
if ( this.item.tabs[idx] !== selected )
for(let i = 0; i < this.item.tabs.length; i++) {
if ( selected === this.item.tabs[i] ) {
idx = i;
break;
}
}*/
this.selected = idx;
this.markSeen();
},

View file

@ -111,7 +111,8 @@ export const RERENDER_SETTINGS = [
'chat.filtering.display-mod-action',
'chat.replies.style',
'chat.bits.cheer-notice',
'chat.filtering.hidden-tokens'
'chat.filtering.hidden-tokens',
'chat.hype.message-style'
];
export const UPDATE_BADGE_SETTINGS = [