Skip to content

Commit

Permalink
clean shutdown for emote plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
goto-bus-stop committed Nov 26, 2024
1 parent 0b2cf35 commit a2ff2fe
Showing 1 changed file with 52 additions and 23 deletions.
75 changes: 52 additions & 23 deletions src/plugins/emotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ const schema = JSON.parse(
fs.readFileSync(new URL('../schemas/emotes.json', import.meta.url), 'utf8'),
);

/** @param {unknown} error */
function isAbortError (error) {

Check failure on line 14 in src/plugins/emotes.js

View workflow job for this annotation

GitHub Actions / Code style

Unexpected space before function parentheses

Check failure on line 14 in src/plugins/emotes.js

View workflow job for this annotation

GitHub Actions / Code style

Unexpected space before function parentheses
return error instanceof Error && error.name === 'AbortError';
}

/**
* @typedef {{
* clientId: string | null,
Expand Down Expand Up @@ -58,10 +63,11 @@ class EmoteMap extends Map {
/**
* @template {object} T
* @param {URL|string} url
* @param {import('node-fetch').RequestInit} [init]
* @returns {Promise<T>}
*/
async function fetchJSON(url) {
const res = await nodeFetch(url);
async function fetchJSON(url, init) {
const res = await nodeFetch(url, init);

if (!res.ok) {
if (res.status === 404) {
Expand All @@ -86,21 +92,25 @@ function fromBTTVEmote(bttv) {
};
}

async function getBTTVGlobalEmotes() {
/**
* @param {AbortSignal} [signal]
*/
async function getBTTVGlobalEmotes(signal) {
/** @type {BTTVEmote[]} */
const emotes = await fetchJSON('https://api.betterttv.net/3/cached/emotes/global');
const emotes = await fetchJSON('https://api.betterttv.net/3/cached/emotes/global', { signal });
return emotes.map(fromBTTVEmote);
}

/**
* @param {string} channelId
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getBTTVChannelEmotes(channelId) {
async function getBTTVChannelEmotes(channelId, signal) {
let channel = null;
try {
channel = /** @type {{ channelEmotes: BTTVEmote[], sharedEmotes: BTTVEmote[] }} */ (
await fetchJSON(`https://api.betterttv.net/3/cached/users/twitch/${channelId}`)
await fetchJSON(`https://api.betterttv.net/3/cached/users/twitch/${channelId}`, { signal })
);
} catch (err) {
if (!(err instanceof NotFound)) {
Expand All @@ -118,12 +128,13 @@ async function getBTTVChannelEmotes(channelId) {

/**
* @param {string[]} channels
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getBTTVEmotes(channels) {
async function getBTTVEmotes(channels, signal) {
const list = await Promise.all([
getBTTVGlobalEmotes(),
...channels.map((channelId) => getBTTVChannelEmotes(channelId)),
getBTTVGlobalEmotes(signal),
...channels.map((channelId) => getBTTVChannelEmotes(channelId, signal)),
]);

return list.flat();
Expand All @@ -142,13 +153,14 @@ function fromFFZEmote(emote) {

/**
* @param {string} channelName
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getFFZChannelEmotes(channelName) {
async function getFFZChannelEmotes(channelName, signal) {
let channel = null;
try {
channel = /** @type {{ sets: Record<number, FFZEmoteSet> }} */ (
await fetchJSON(`https://api.frankerfacez.com/v1/room/${channelName}`)
await fetchJSON(`https://api.frankerfacez.com/v1/room/${channelName}`, { signal })
);
} catch (err) {
if (!(err instanceof NotFound)) {
Expand All @@ -166,10 +178,11 @@ async function getFFZChannelEmotes(channelName) {

/**
* @param {string[]} channels
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getFFZEmotes(channels) {
const list = await Promise.all(channels.map((channelId) => getFFZChannelEmotes(channelId)));
async function getFFZEmotes(channels, signal) {
const list = await Promise.all(channels.map((channelId) => getFFZChannelEmotes(channelId, signal)));

Check failure on line 185 in src/plugins/emotes.js

View workflow job for this annotation

GitHub Actions / Code style

This line has a length of 102. Maximum allowed is 100

Check failure on line 185 in src/plugins/emotes.js

View workflow job for this annotation

GitHub Actions / Code style

This line has a length of 102. Maximum allowed is 100

return list.flat();
}
Expand All @@ -188,13 +201,14 @@ function fromSevenTVEmote(emote) {

/**
* @param {string} channelId
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getSevenTVChannelEmotes(channelId) {
async function getSevenTVChannelEmotes(channelId, signal) {
let channel = null;
try {
channel = /** @type {{ emote_set?: { emotes: SevenTVEmote[] } }} */ (
await fetchJSON(`https://7tv.io/v3/users/twitch/${channelId}`)
await fetchJSON(`https://7tv.io/v3/users/twitch/${channelId}`, { signal })
);
} catch (err) {
if (!(err instanceof NotFound)) {
Expand All @@ -210,14 +224,15 @@ async function getSevenTVChannelEmotes(channelId) {

/**
* @param {string[]} channels
* @param {AbortSignal} [signal]
* @returns {Promise<Emote[]>}
*/
async function getSevenTVEmotes(channels) {
async function getSevenTVEmotes(channels, signal) {
/** @type {Promise<{ emotes: SevenTVEmote[] }>} */
const global = fetchJSON('https://7tv.io/v3/emote-sets/global');
const global = fetchJSON('https://7tv.io/v3/emote-sets/global', { signal });
const emotes = await Promise.all([
global.then((data) => data.emotes.map(fromSevenTVEmote)),
...channels.map((channelId) => getSevenTVChannelEmotes(channelId)),
...channels.map((channelId) => getSevenTVChannelEmotes(channelId, signal)),
]);

return emotes.flat();
Expand Down Expand Up @@ -250,6 +265,8 @@ class Emotes {

#ready = Promise.resolve();

#shutdownSignal;

/**
* @param {import('../Uwave.js').Boot} uw
*/
Expand All @@ -264,7 +281,14 @@ class Emotes {
this.#ready = this.#reloadEmotes();
},
);
uw.onClose(unsubscribe);

const controller = new AbortController();
uw.onClose(() => {
unsubscribe();
controller.abort();
});

this.#shutdownSignal = controller.signal;

this.#ready = this.#reloadEmotes();
}
Expand Down Expand Up @@ -312,15 +336,15 @@ class Emotes {
}

if (options.bttv) {
promises.push(getBTTVEmotes(channels));
promises.push(getBTTVEmotes(channels, this.#shutdownSignal));
}

if (options.ffz) {
promises.push(getFFZEmotes(options.channels));
promises.push(getFFZEmotes(options.channels, this.#shutdownSignal));
}

if (options.seventv) {
promises.push(getSevenTVEmotes(channels));
promises.push(getSevenTVEmotes(channels, this.#shutdownSignal));
}

const emotes = new EmoteMap();
Expand All @@ -331,7 +355,7 @@ class Emotes {
for (const emote of result.value) {
emotes.insert(emote);
}
} else {
} else if (!isAbortError(result.reason)) {
this.#logger.warn(result.reason);
}
}
Expand All @@ -348,6 +372,11 @@ class Emotes {
this.#emotes = await this.#loadTTVEmotes(config.twitch);
}

if (this.#shutdownSignal.aborted) {
this.#logger.info('emote reload aborted due to server shutdown');
return;
}

this.#uw.publish('emotes:reload', null);
}
}
Expand Down

0 comments on commit a2ff2fe

Please sign in to comment.