Bulk fetch timezones to not spam the API

Although this doesn't currently work as CORS is fucking us over.
This commit is contained in:
ArjixWasTaken 2023-02-19 01:16:07 +02:00
parent 0d656f5e00
commit ddd22b4029
2 changed files with 72 additions and 24 deletions

View file

@ -19,6 +19,7 @@
const PreloadedUserSettings = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings"); const PreloadedUserSettings = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { debounce } from "@utils/debounce";
import { findLazy } from "@webpack"; import { findLazy } from "@webpack";
export const DATASTORE_KEY = "plugins.Timezones.savedTimezones"; export const DATASTORE_KEY = "plugins.Timezones.savedTimezones";
import type { timezones } from "./all_timezones"; import type { timezones } from "./all_timezones";
@ -28,31 +29,78 @@ export interface TimezoneDB {
[userId: string]: typeof timezones[number]; [userId: string]: typeof timezones[number];
} }
const API_URL = "https://timezonedb.catvibers.me/"; const API_URL = "https://timezonedb.catvibers.me";
const Cache = new Map<string, string | null>(); const Cache: Record<string, typeof timezones[number]> = {};
export async function getUserTimezone(discordID: string): Promise<string | null> {
const timezone = (await DataStore.get(DATASTORE_KEY) as TimezoneDB | undefined)?.[discordID];
if (timezone) return timezone;
if (Cache.has(discordID)) {
return Cache.get(discordID) as string | null;
}
const response = await fetch(API_URL + "api/user/" + discordID);
const timezone_res = await response.json();
if (response.status !== 200) {
Cache.set(discordID, null);
return null;
}
Cache.set(discordID, timezone_res.timezoneId);
return timezone_res.timezoneId;
}
export function getTimeString(timezone: string, timestamp = new Date()): string { export function getTimeString(timezone: string, timestamp = new Date()): string {
const locale = PreloadedUserSettings.getCurrentValue().localization.locale.value; const locale = PreloadedUserSettings.getCurrentValue().localization.locale.value;
return new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "numeric", timeZone: timezone }).format(timestamp); // we hate javascript return new Intl.DateTimeFormat(locale, { hour: "numeric", minute: "numeric", timeZone: timezone }).format(timestamp); // we hate javascript
} }
// A map of ids and callbacks that should be triggered on fetch
const requestQueue: Record<string, ((timezone: typeof timezones[number]) => void)[]> = {};
async function bulkFetchTimezones(ids: string[]): Promise<TimezoneDB | undefined> {
try {
const req = await fetch(`${API_URL}/api/user/bulk`, {
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(ids)
});
return await req.json()
.then((res: { [userId: string]: { timezoneId: string; } | null; }) => {
const tzs = (Object.keys(res).map(userId => {
return res[userId] && { [userId]: res[userId]!.timezoneId as typeof timezones[number] };
}).filter(Boolean) as TimezoneDB[]).reduce((acc, cur) => ({ ...acc, ...cur }), {});
Object.assign(Cache, tzs);
return tzs;
});
} catch (e) {
console.error("Timezone fetching failed: ", e);
}
}
// Executes all queued requests and calls their callbacks
const bulkFetch = debounce(async () => {
const ids = Object.keys(requestQueue);
const timezones = await bulkFetchTimezones(ids);
if (!timezones) {
// retry after 15 seconds
setTimeout(bulkFetch, 15000);
return;
}
for (const id of ids) {
// Call all callbacks for the id
requestQueue[id].forEach(c => c(timezones[id]));
delete requestQueue[id];
}
});
export function getUserTimezone(discordID: string): Promise<typeof timezones[number] | undefined> {
return new Promise(res => {
if (discordID in Cache) res(Cache[discordID]);
const timezone = (DataStore.get(DATASTORE_KEY) as Promise<TimezoneDB | undefined>).then(tzs => tzs?.[discordID]);
timezone.then(tz => {
if (tz) res(tz);
else {
if (discordID in requestQueue) requestQueue[discordID].push(res);
// If not already added, then add it and call the debounced function to make sure the request gets executed
else {
requestQueue[discordID] = [res];
bulkFetch();
}
}
});
});
}

View file

@ -183,7 +183,7 @@ export default definePlugin({
const user = e.user as User; const user = e.user as User;
const [timezone, setTimezone] = React.useState<string | null>(null); const [timezone, setTimezone] = React.useState<string | undefined>();
React.useEffect(() => { React.useEffect(() => {
getUserTimezone(user.id).then(timezone => setTimezone(timezone)); getUserTimezone(user.id).then(timezone => setTimezone(timezone));
@ -219,7 +219,7 @@ export default definePlugin({
const message = e.message as Message; const message = e.message as Message;
const [timezone, setTimezone] = React.useState<string | null>(null); const [timezone, setTimezone] = React.useState<string | undefined>();
React.useEffect(() => { React.useEffect(() => {
getUserTimezone(e.message.author.id).then(timezone => setTimezone(timezone)); getUserTimezone(e.message.author.id).then(timezone => setTimezone(timezone));