mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-06-28 15:27:43 +00:00
4.68.2
* Fixed: The icon not being hidden when hiding the native Twitch viewer count on channel pages. * Fixed: Certain Twitch native code modules failing to load in the new React 18 build, causing issues with minor features failing to work. * Fixed: The React button overlapping when using the setting to hide the unfollow button. * Changed: Disable word wrapping within names when displaying a condensed list of users for a mass subscription. * Experiments Changed: Added another experiment on how to host PubSub things.
This commit is contained in:
parent
909fdc25f2
commit
3aeb70f0fb
14 changed files with 143 additions and 44 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "frankerfacez",
|
||||
"author": "Dan Salvato LLC",
|
||||
"version": "4.68.1",
|
||||
"version": "4.68.2",
|
||||
"description": "FrankerFaceZ is a Twitch enhancement suite.",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -16,8 +16,16 @@
|
|||
{"value": false, "weight": 0}
|
||||
]
|
||||
},
|
||||
"emqx_pubsub": {
|
||||
"name": "EMQX MQTT-Based PubSub",
|
||||
"description": "An experimental pubsub system running on an EMQX cluster, to see how that performs.",
|
||||
"groups": [
|
||||
{"value": true, "weight": 25},
|
||||
{"value": false, "weight": 75}
|
||||
]
|
||||
},
|
||||
"cf_pubsub": {
|
||||
"name": "MQTT-Based PubSub",
|
||||
"name": "CF MQTT-Based PubSub",
|
||||
"description": "An experimental new pubsub system that should be more reliable than the existing socket cluster.",
|
||||
"groups": [
|
||||
{"value": true, "weight": 0},
|
||||
|
|
|
@ -229,6 +229,7 @@ export default class ExperimentManager extends Module<'experiments', ExperimentE
|
|||
this.cache = new Map;
|
||||
}
|
||||
|
||||
|
||||
getControlsLocked() {
|
||||
if ( DEBUG )
|
||||
return false;
|
||||
|
|
|
@ -104,7 +104,7 @@ export default class EmoteCard extends Module {
|
|||
|
||||
canReportTwitch() {
|
||||
const site = this.resolve('site'),
|
||||
core = site.getCore?.(),
|
||||
//core = site.getCore?.(),
|
||||
user = site.getUser(),
|
||||
web_munch = this.resolve('site.web_munch');
|
||||
|
||||
|
@ -115,13 +115,13 @@ export default class EmoteCard extends Module {
|
|||
return false;
|
||||
}
|
||||
|
||||
return !! report_form && !! user?.id && core?.store?.dispatch;
|
||||
return !! report_form && !! user?.id && site?.store?.dispatch;
|
||||
}
|
||||
|
||||
|
||||
reportTwitchEmote(id, channel) {
|
||||
const site = this.resolve('site'),
|
||||
core = site.getCore(),
|
||||
//core = site.getCore(),
|
||||
user = site.getUser(),
|
||||
web_munch = this.resolve('site.web_munch');
|
||||
|
||||
|
@ -132,10 +132,10 @@ export default class EmoteCard extends Module {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( ! user?.id || ! core?.store?.dispatch )
|
||||
if ( ! user?.id || ! site?.store?.dispatch )
|
||||
return false;
|
||||
|
||||
core.store.dispatch({
|
||||
site.store.dispatch({
|
||||
type: 'core.modal.MODAL_SHOWN',
|
||||
modalComponent: report_form,
|
||||
modalProps: {
|
||||
|
|
|
@ -65,6 +65,8 @@ export default class PubSub extends Module<'pubsub', PubSubEvents> {
|
|||
|
||||
this.settings.add('pubsub.use-cluster', {
|
||||
default: () => {
|
||||
if ( this.experiments.getAssignment('emqx_pubsub') )
|
||||
return 'EMQXTest';
|
||||
if ( this.experiments.getAssignment('cf_pubsub') )
|
||||
return 'Staging';
|
||||
return null;
|
||||
|
|
|
@ -265,6 +265,12 @@ Twilight.KNOWN_MODULES = {
|
|||
stack: n.fQ,
|
||||
dispatch: n.vJ
|
||||
};
|
||||
|
||||
if ( has(n.fQ?._currentValue, 'highlights') && typeof n.vJ?._currentValue === 'function' )
|
||||
return {
|
||||
stack: n.fQ,
|
||||
dispatch: n.vJ
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,6 +278,7 @@ Twilight.KNOWN_MODULES = {
|
|||
const VEND_CORE = n => ! n || n.includes('vendor') || n.includes('core');
|
||||
|
||||
Twilight.KNOWN_MODULES.core.use_result = true;
|
||||
Twilight.KNOWN_MODULES.core.skip_cache = true;
|
||||
//Twilight.KNOWN_MODULES.core.chunks = 'core';
|
||||
|
||||
Twilight.KNOWN_MODULES.simplebar.chunks = VEND_CORE;
|
||||
|
|
|
@ -334,6 +334,19 @@ export default class Channel extends Module {
|
|||
}
|
||||
}
|
||||
|
||||
if ( ! el.querySelector('ffz--native-viewers-container') ) {
|
||||
let i = 0,
|
||||
vel = el.querySelector('p[data-a-target="animated-channel-viewers-count"]');
|
||||
while(vel && vel != el && i < 5) {
|
||||
if ( vel.querySelector('svg') ) {
|
||||
vel.classList.add('ffz--native-viewers-container');
|
||||
break;
|
||||
}
|
||||
vel = vel.parentElement;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
const react = this.fine.getReactInstance(el);
|
||||
let props = react?.child?.memoizedProps;
|
||||
if ( ! props?.channelLogin )
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.ffz--meta-tray {
|
||||
.tw-svg svg[type="color-text-accessible-red"],
|
||||
.ffz--native-viewers-container,
|
||||
p[data-a-target="animated-channel-viewers-count"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
button[data-test-selector="unfollow-button"] {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
div[data-target="channel-header-right"] > div {
|
||||
margin-right: unset !important;
|
||||
}
|
||||
|
|
|
@ -251,6 +251,7 @@
|
|||
.ffz--giftee-name {
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
word-break: keep-all;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
|
|
|
@ -16,7 +16,7 @@ const regex_cache = {};
|
|||
|
||||
function getRequireRegex(name) {
|
||||
if ( ! regex_cache[name] )
|
||||
regex_cache[name] = new RegExp(`\\b${name}\\(([0-9e_+]+)\\)`, 'g');
|
||||
regex_cache[name] = new RegExp(`\\b(?<!\\.)${name}\\(([0-9e_+]+)\\)`, 'g');
|
||||
|
||||
return regex_cache[name];
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ export default class WebMunch extends Module {
|
|||
try {
|
||||
const mod = this._require(id);
|
||||
for(const key in mod)
|
||||
if ( mod[key] && predicate(mod[key]) ) {
|
||||
if ( mod[key] && predicate(mod[key], mod, key) ) {
|
||||
this.log.info(`Found in key "${key}" of module "${id}" (${this.chunkNameForModule(id)})`);
|
||||
if ( ! multi )
|
||||
return mod;
|
||||
|
@ -683,4 +683,4 @@ export default class WebMunch extends Module {
|
|||
|
||||
}
|
||||
|
||||
WebMunch.Requires = Requires;
|
||||
WebMunch.Requires = Requires;
|
||||
|
|
|
@ -307,9 +307,22 @@ export const LINK_DATA_HOSTS = {
|
|||
export const PUBSUB_CLUSTERS = {
|
||||
Production: `https://pubsub.frankerfacez.com`,
|
||||
Staging: `https://pubsub-staging-alt.frankerfacez.com`,
|
||||
EMQXTest: 'emqx-test',
|
||||
Development: `https://stendec.dev/ps/`
|
||||
}
|
||||
|
||||
export const EMQX_SERVERS = [
|
||||
//'catbag.frankerfacez.com',
|
||||
//'pubsub-staging.frankerfacez.com',
|
||||
'ayaya.frankerfacez.com',
|
||||
'champ.frankerfacez.com',
|
||||
'lilz.frankerfacez.com',
|
||||
'pog.frankerfacez.com',
|
||||
'yoohoo.frankerfacez.com',
|
||||
'andknuckles.frankerfacez.com'
|
||||
];
|
||||
|
||||
|
||||
/** Whether or not we're running on macOS */
|
||||
export const IS_OSX = navigator.platform ? navigator.platform.indexOf('Mac') !== -1 : /OS X/.test(navigator.userAgent);
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ var _Bytes = class _Bytes {
|
|||
var CONNACK = 2;
|
||||
function readConnack(reader, controlPacketFlags) {
|
||||
const { DEBUG } = Mqtt;
|
||||
checkEqual("controlPacketFlags", controlPacketFlags, 0);
|
||||
//checkEqual("controlPacketFlags", controlPacketFlags, 0);
|
||||
const connectAcknowledgeFlags = reader.readUint8();
|
||||
const sessionPresent = (connectAcknowledgeFlags & 1) === 1;
|
||||
const connectAcknowledgeFlagsReserved = connectAcknowledgeFlags & 254;
|
||||
|
@ -352,7 +352,7 @@ var _Bytes = class _Bytes {
|
|||
var PUBLISH = 3;
|
||||
function readPublish(reader, controlPacketFlags) {
|
||||
const { DEBUG } = Mqtt;
|
||||
checkEqual("controlPacketFlags", controlPacketFlags, 0);
|
||||
//checkEqual("controlPacketFlags", controlPacketFlags, 0);
|
||||
const dup = (controlPacketFlags & 8) === 8;
|
||||
const qosLevel = (controlPacketFlags & 6) >> 1;
|
||||
const retain = (controlPacketFlags & 1) === 1;
|
||||
|
@ -704,13 +704,13 @@ var _Bytes = class _Bytes {
|
|||
}
|
||||
static async create(opts) {
|
||||
const { DEBUG } = Mqtt;
|
||||
const { hostname, port } = opts;
|
||||
const { hostname, port, pathname } = opts;
|
||||
if ("accept" in WebSocket.prototype) {
|
||||
if (DEBUG)
|
||||
console.log("Found WebSocket.accept, using Cloudflare workaround");
|
||||
if (port !== 443)
|
||||
throw new Error(`Cloudflare Workers only support outgoing WebSocket requests on port 443 (https)`);
|
||||
const url2 = `https://${hostname}`;
|
||||
const url2 = `https://${hostname}${port ? `:${port}` : ''}${pathname ?? ''}`;
|
||||
if (DEBUG)
|
||||
console.log(`Fetching ${url2}`);
|
||||
const resp = await fetch(url2, { headers: { upgrade: "websocket" } });
|
||||
|
@ -724,7 +724,7 @@ var _Bytes = class _Bytes {
|
|||
console.log("Accepted!");
|
||||
return new _WebSocketConnection(webSocket);
|
||||
}
|
||||
const url = `wss://${hostname}:${port}`;
|
||||
const url = `wss://${hostname}${port ? `:${port}` : ''}${pathname ?? ''}`;
|
||||
const ws = new WebSocket(url, "mqtt");
|
||||
if (DEBUG)
|
||||
console.log(`new WebSocket('${url}', 'mqtt')`);
|
||||
|
@ -790,9 +790,10 @@ var _Bytes = class _Bytes {
|
|||
this.receivedDisconnect = false;
|
||||
/** @internal */
|
||||
this.nextPacketId = 1;
|
||||
const { hostname, port, protocol, maxMessagesPerSecond } = opts;
|
||||
const { hostname, port, pathname, protocol, maxMessagesPerSecond } = opts;
|
||||
this.hostname = hostname;
|
||||
this.port = port;
|
||||
this.pathname = pathname;
|
||||
this.protocol = protocol;
|
||||
this.maxMessagesPerSecond = maxMessagesPerSecond;
|
||||
}
|
||||
|
@ -837,9 +838,9 @@ var _Bytes = class _Bytes {
|
|||
async connect(opts) {
|
||||
const { DEBUG } = Mqtt;
|
||||
const { clientId = "", username, password, keepAlive = DEFAULT_KEEP_ALIVE_SECONDS, clean = false } = opts;
|
||||
const { protocol, hostname, port } = this;
|
||||
const { protocol, hostname, port, pathname } = this;
|
||||
if (!this.connection) {
|
||||
this.connection = await _MqttClient.protocolHandlers[protocol]({ hostname, port });
|
||||
this.connection = await _MqttClient.protocolHandlers[protocol]({ hostname, port, pathname });
|
||||
this.connection.onRead = (bytes) => {
|
||||
this.processBytes(bytes);
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { EventEmitter } from "./events";
|
|||
|
||||
import { MqttClient, DISCONNECT } from './custom_denoflare_mqtt'; // "denoflare-mqtt";
|
||||
import { b64ToArrayBuffer, debounce, importRsaKey, make_enum, sleep } from "./object";
|
||||
import { EMQX_SERVERS } from "./constants";
|
||||
|
||||
// Only match 1-4 digit numbers, to avoid matching twitch IDs.
|
||||
// 9999 gives us millions of clients on a topic, so we're never
|
||||
|
@ -112,30 +113,46 @@ export default class PubSubClient extends EventEmitter {
|
|||
|
||||
async _loadData() {
|
||||
let response, data;
|
||||
try {
|
||||
// TODO: Send removed topics.
|
||||
response = await fetch(this.server, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: this.id,
|
||||
user: this.user ?? null,
|
||||
topics: this.topics
|
||||
})
|
||||
});
|
||||
|
||||
if ( response.ok )
|
||||
data = await response.json();
|
||||
if ( this.server === 'emqx-test' ) {
|
||||
// Hard-coded data.
|
||||
const server = EMQX_SERVERS[Math.floor(Math.random() * EMQX_SERVERS.length)];
|
||||
|
||||
} catch(err) {
|
||||
throw new Error(
|
||||
'Unable to load PubSub data from server.',
|
||||
{
|
||||
cause: err
|
||||
}
|
||||
);
|
||||
data = {
|
||||
require_signing: false,
|
||||
endpoint: `wss://${server}:8084/mqtt`,
|
||||
|
||||
username: 'anonymous',
|
||||
password: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhbm9ueW1vdXMifQ.5DZP1bScMz4-MV_jGZveUKq4pFy9x_PJF9gSzAvj-wA`,
|
||||
topics: Object.fromEntries(this.topics.map(x => [x, 0]))
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
// TODO: Send removed topics.
|
||||
response = await fetch(this.server, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: this.id,
|
||||
user: this.user ?? null,
|
||||
topics: this.topics
|
||||
})
|
||||
});
|
||||
|
||||
if ( response.ok )
|
||||
data = await response.json();
|
||||
|
||||
} catch(err) {
|
||||
throw new Error(
|
||||
'Unable to load PubSub data from server.',
|
||||
{
|
||||
cause: err
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! data?.endpoint )
|
||||
|
@ -411,6 +428,7 @@ export default class PubSubClient extends EventEmitter {
|
|||
const client = this._client = new MqttClient({
|
||||
hostname: url.hostname,
|
||||
port: url.port ?? undefined,
|
||||
pathname: url.pathname ?? undefined,
|
||||
protocol: 'wss',
|
||||
maxMessagesPerSecond: 10
|
||||
});
|
||||
|
@ -444,9 +462,13 @@ export default class PubSubClient extends EventEmitter {
|
|||
return;
|
||||
}
|
||||
|
||||
let payload = message.payload;
|
||||
if ( payload instanceof Uint8Array )
|
||||
payload = new TextDecoder().decode(payload);
|
||||
|
||||
let msg;
|
||||
try {
|
||||
msg = JSON.parse(message.payload);
|
||||
msg = JSON.parse(payload);
|
||||
} catch(err) {
|
||||
if ( this.logger )
|
||||
this.logger.warn(`Error decoding PubSub message on topic "${topic}":`, err);
|
||||
|
@ -496,10 +518,12 @@ export default class PubSubClient extends EventEmitter {
|
|||
|
||||
return this._client.connect({
|
||||
clientId: data.client_id,
|
||||
username: data.username,
|
||||
password: data.password,
|
||||
keepAlive: 120,
|
||||
clean: true
|
||||
}).then(msg => {
|
||||
this._conn_failures = 0;
|
||||
this._state = State.Connected;
|
||||
this.emit('connect', msg);
|
||||
|
||||
|
@ -523,6 +547,30 @@ export default class PubSubClient extends EventEmitter {
|
|||
});
|
||||
|
||||
return this._sendSubscribes()
|
||||
}).catch(err => {
|
||||
if ( this.logger )
|
||||
this.logger.debug('Error connecting to MQTT.', err);
|
||||
|
||||
disconnected = true;
|
||||
this.emit('disconnect', null);
|
||||
|
||||
this._destroyClient();
|
||||
|
||||
if ( ! this._should_connect )
|
||||
return;
|
||||
|
||||
this._conn_failures = (this._conn_failures || 0) + 1;
|
||||
let delay = (this._conn_failures * Math.floor(Math.random() * 10) + 2) * 1000;
|
||||
if ( delay > 60000 )
|
||||
delay = (Math.floor(Math.random() * 60) + 30) * 1000;
|
||||
|
||||
if ( delay <= 2000 )
|
||||
delay = 2000;
|
||||
|
||||
return sleep(delay).then(() => {
|
||||
if ( this._should_connect && ! this._client )
|
||||
this._createClient(data);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -673,4 +721,4 @@ export default class PubSubClient extends EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue