1
0
Fork 0
mirror of https://github.com/FrankerFaceZ/FrankerFaceZ.git synced 2025-06-28 15:27:43 +00:00
* 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:
SirStendec 2024-03-01 01:48:50 -05:00
parent 909fdc25f2
commit 3aeb70f0fb
14 changed files with 143 additions and 44 deletions

View file

@ -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",

View file

@ -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},

View file

@ -229,6 +229,7 @@ export default class ExperimentManager extends Module<'experiments', ExperimentE
this.cache = new Map;
}
getControlsLocked() {
if ( DEBUG )
return false;

View file

@ -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: {

View file

@ -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;

View file

@ -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;

View file

@ -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 )

View file

@ -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;
}
}
}

View file

@ -1,3 +1,7 @@
button[data-test-selector="unfollow-button"] {
display: none !important;
}
}
div[data-target="channel-header-right"] > div {
margin-right: unset !important;
}

View file

@ -251,6 +251,7 @@
.ffz--giftee-name {
cursor: pointer;
outline: none;
word-break: keep-all;
&:hover {
text-decoration: underline;

View file

@ -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;

View file

@ -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);

View file

@ -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);
};

View file

@ -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 {
});
}
}
}