diff --git a/CHANGELOG-nightly.md b/CHANGELOG-nightly.md index b24a117c..bb13e973 100644 --- a/CHANGELOG-nightly.md +++ b/CHANGELOG-nightly.md @@ -1,3 +1,7 @@ +## 3.1.25.1000 + +- Updated 7TV API logic + ### 3.1.24.1000 - Added full support for Twitch GIF messages diff --git a/CHANGELOG.md b/CHANGELOG.md index f88ca94b..e05d1b03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.25 + +- Updated 7TV API logic + ### 3.1.24 - Added full support for Twitch GIF messages diff --git a/package.json b/package.json index f3da765f..146b2242 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "7TV", "description": "Improve your viewing experience on Twitch & Kick with new features, emotes, vanity and performance.", "private": true, - "version": "3.1.24", + "version": "3.1.25", "dev_version": "1.0", "scripts": { "start": "cross-env NODE_ENV=dev yarn build:dev && cross-env NODE_ENV=dev vite --mode dev", diff --git a/src/types/app.d.ts b/src/types/app.d.ts index c7d79481..13d89285 100644 --- a/src/types/app.d.ts +++ b/src/types/app.d.ts @@ -214,6 +214,12 @@ declare namespace SevenTV { user?: User; } + interface UserLookupResponse extends Omit { + emote_set: null; + emote_set_id: ObjectID | null; + user: User; + } + interface Cosmetic { id: ObjectID; kind: K; diff --git a/src/worker/worker.http.ts b/src/worker/worker.http.ts index ccd3308b..ebeee1e3 100644 --- a/src/worker/worker.http.ts +++ b/src/worker/worker.http.ts @@ -25,6 +25,10 @@ enum ProviderPriority { TWITCH, } +const USER_LOOKUP_HEADERS = { + "X-7tv-Missing-EmoteSet-Aware": "1", +}; + export class WorkerHttp { private lastPresenceAt: Map = new Map(); static imageFormat: SevenTV.ImageFormat = "WEBP"; @@ -116,9 +120,12 @@ export class WorkerHttp { // setup fetching promises const userPromise = seventv.loadUserConnection(port.platform ?? "TWITCH", channel.id).catch(() => void 0); + const emoteSetPromise = userPromise + .then((user) => (user?.emote_set_id ? seventv.loadEmoteSet(user.emote_set_id) : null)) + .catch(() => void 0); const promises = [ - ["7TV", () => userPromise.then((es) => (es ? es.emote_set : null)).catch(() => void 0)], + ["7TV", () => emoteSetPromise], ["FFZ", () => frankerfacez.loadUserEmoteSet(channel.id).catch(() => void 0)], ["BTTV", () => betterttv.loadUserEmoteSet(channel.id).catch(() => void 0)], ] as [SevenTV.Provider, () => Promise][]; @@ -248,33 +255,19 @@ export class WorkerHttp { } export const seventv = { - async loadUserConnection(platform: Platform, id: string): Promise { - const resp = await doRequest(API_BASE.SEVENTV, `users/${platform.toLowerCase()}/${id}`).catch((err) => - Promise.reject(err), - ); + async loadUserConnection(platform: Platform, id: string): Promise { + const resp = await doRequest( + API_BASE.SEVENTV, + `users/${platform.toLowerCase()}/${id}`, + "GET", + undefined, + USER_LOOKUP_HEADERS, + ).catch((err) => Promise.reject(err)); if (!resp || resp.status !== 200) { return Promise.reject(resp); } - const data = (await resp.json()) as SevenTV.UserConnection; - - const set = structuredClone(data.emote_set) as SevenTV.EmoteSet; - - set.provider = "7TV"; - set.scope = "CHANNEL"; - set.priority = ProviderPriority.SEVENTV; - - set.emotes.map((ae) => { - ae.provider = set.provider; - ae.scope = "CHANNEL"; - - if (ae.data) ae.data.host.srcset = imageHostToSrcset(ae.data.host, "7TV", WorkerHttp.imageFormat, 2); - return ae; - }); - - data.emote_set = set; - - return Promise.resolve(data); + return Promise.resolve((await resp.json()) as SevenTV.UserLookupResponse); }, async loadEmoteSet(id: string): Promise { @@ -289,7 +282,7 @@ export const seventv = { if (id === "global") set.scope = "GLOBAL"; else if (BitField(EmoteSetFlags, set.flags ?? 0).has("Personal")) set.scope = "PERSONAL"; else set.scope = "CHANNEL"; - set.priority = ProviderPriority.SEVENTV_GLOBAL; + set.priority = set.scope === "GLOBAL" ? ProviderPriority.SEVENTV_GLOBAL : ProviderPriority.SEVENTV; set.emotes.map((ae) => { ae.provider = set.provider; @@ -304,14 +297,18 @@ export const seventv = { }, async loadUserData(platform: Platform, id: string): Promise { - const resp = await doRequest(API_BASE.SEVENTV, `users/${platform.toLowerCase()}/${id}`).catch((err) => - Promise.reject(err), - ); + const resp = await doRequest( + API_BASE.SEVENTV, + `users/${platform.toLowerCase()}/${id}`, + "GET", + undefined, + USER_LOOKUP_HEADERS, + ).catch((err) => Promise.reject(err)); if (!resp || resp.status !== 200) { return Promise.reject(resp); } - const userConn = (await resp.json()) as SevenTV.UserConnection; + const userConn = (await resp.json()) as SevenTV.UserLookupResponse; if (!userConn.user) return Promise.reject(new Error("No user was returned!")); return Promise.resolve(userConn.user); @@ -444,15 +441,20 @@ export const betterttv = { }, }; -async function doRequest(base: string, path: string, method?: string, body?: T): Promise { +async function doRequest( + base: string, + path: string, + method?: string, + body?: T, + headers?: Record, +): Promise { return fetch(`${base}/${path}`, { method, body: body ? JSON.stringify(body) : undefined, - headers: body - ? { - "Content-Type": "application/json", - } - : undefined, + headers: { + ...(body ? { "Content-Type": "application/json" } : {}), + ...headers, + }, referrer: location.origin, referrerPolicy: "origin", }).then(async (resp) => {