From b5d86bf2d651dae1a09e9ba5f0239dabab04ecd7 Mon Sep 17 00:00:00 2001 From: Samuel Elliott Date: Sun, 7 Jan 2024 08:47:04 +0000 Subject: [PATCH] Show Splatoon 3 Splatfest team image in presence embed --- docs/cli.md | 2 ++ src/cli/util/presence-embed-render.ts | 10 +++++++++- src/cli/util/presence-embed-server.ts | 12 +++++++++--- src/cli/util/presence-embed.ts | 24 ++++++++++++++++++++---- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 1255cb0..383065a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -702,6 +702,8 @@ curl http://[::1]:12345/api/presence/0123456789abcdef/embed?transparent=1 > embe curl http://[::1]:12345/api/presence/0123456789abcdef/embed?width=800 > embed.svg # ... with Splatoon 3 presence curl http://[::1]:12345/api/presence/0123456789abcdef/embed?include-splatoon3=1 > embed.svg +# ... with Splatoon 3 Splatfest team +curl 'http://[::1]:12345/api/presence/0123456789abcdef/embed?include-splatoon3=1&show-splatoon3-fest-team=1' > embed.svg ``` Example EventStream use: diff --git a/src/cli/util/presence-embed-render.ts b/src/cli/util/presence-embed-render.ts index acd9fc4..d1fa84a 100644 --- a/src/cli/util/presence-embed-render.ts +++ b/src/cli/util/presence-embed-render.ts @@ -1,3 +1,4 @@ +import { FestVoteState } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../util.js'; import createDebug from '../../util/debug.js'; import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js'; @@ -26,6 +27,10 @@ export function builder(yargs: Argv) { }).option('friend-code', { describe: 'Friend code', type: 'string', + }).option('show-splatoon3-fest-team', { + describe: 'Show Splatoon 3 Splatfest team', + type: 'boolean', + default: false, }).option('scale', { describe: 'Image scale', type: 'number', @@ -64,6 +69,7 @@ export async function handler(argv: ArgumentsCamelCase) { const image_urls = [result.friend.imageUri]; if ('imageUri' in result.friend.presence.game) image_urls.push(result.friend.presence.game.imageUri); + if (argv.showSplatoon3FestTeam && result.splatoon3_fest_team?.myVoteState === FestVoteState.VOTED) image_urls.push(result.splatoon3_fest_team.image.url); const url_map: Record = {}; @@ -77,7 +83,9 @@ export async function handler(argv: ArgumentsCamelCase) { url_map[url] = [url, data, 'image/jpeg']; })); - const svg = renderUserEmbedSvg(result, url_map, theme, argv.friendCode, argv.scale, argv.transparent, width); + const svg = renderUserEmbedSvg(result, url_map, theme, argv.friendCode, { + show_splatoon3_fest_team: argv.showSplatoon3FestTeam, + }, argv.scale, argv.transparent, width); const [image, type] = await renderUserEmbedImage(svg, format); console.warn('output type', type); diff --git a/src/cli/util/presence-embed-server.ts b/src/cli/util/presence-embed-server.ts index 44973da..93c59ce 100644 --- a/src/cli/util/presence-embed-server.ts +++ b/src/cli/util/presence-embed-server.ts @@ -2,6 +2,7 @@ import * as os from 'node:os'; import * as net from 'node:net'; import express, { Request, Response } from 'express'; import { createHash } from 'node:crypto'; +import { FestVoteState } from 'splatnet3-types/splatnet3'; import type { Arguments as ParentArguments } from '../util.js'; import createDebug from '../../util/debug.js'; import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js'; @@ -100,7 +101,7 @@ class Server extends HttpServer { const [presence, user, data] = await getPresenceFromUrl(this.base_url + '/' + presence_user_nsaid + qs); const result = data as PresenceResponse; - const {theme, friend_code, transparent, width} = getUserEmbedOptionsFromRequest(req); + const {theme, friend_code, transparent, width, options} = getUserEmbedOptionsFromRequest(req); const etag = createHash('sha256').update(JSON.stringify({ data, @@ -109,6 +110,7 @@ class Server extends HttpServer { friend_code, transparent, width, + options, v: version + '-' + git?.revision, })).digest('base64url'); @@ -121,6 +123,7 @@ class Server extends HttpServer { const image_urls = [result.friend.imageUri]; if ('imageUri' in result.friend.presence.game) image_urls.push(result.friend.presence.game.imageUri); + if (options.show_splatoon3_fest_team && result.splatoon3_fest_team?.image.url) image_urls.push(result.splatoon3_fest_team.image.url); const url_map: Record = {}; @@ -132,7 +135,7 @@ class Server extends HttpServer { url_map[url] = [url, data, 'image/jpeg']; })); - const svg = renderUserEmbedSvg(result, url_map, theme, friend_code, 1, transparent, width); + const svg = renderUserEmbedSvg(result, url_map, theme, friend_code, options, 1, transparent, width); const [image, type] = await renderUserEmbedImage(svg, format); res.setHeader('Content-Type', type); @@ -159,6 +162,7 @@ class Server extends HttpServer { const image_urls = [result.friend.imageUri]; if ('imageUri' in result.friend.presence.game) image_urls.push(result.friend.presence.game.imageUri); + if (result.splatoon3_fest_team?.myVoteState === FestVoteState.VOTED) image_urls.push(result.splatoon3_fest_team.image.url); const url_map: Record = {}; @@ -170,7 +174,9 @@ class Server extends HttpServer { url_map[url] = [url, data, 'image/jpeg']; })); - const svg = renderUserEmbedSvg(result, url_map, PresenceEmbedTheme.LIGHT, undefined, 1, true, 800); + const svg = renderUserEmbedSvg(result, url_map, PresenceEmbedTheme.LIGHT, undefined, { + show_splatoon3_fest_team: true, + }, 2, true, 800); res.setHeader('Content-Type', 'text/html'); res.write(``); diff --git a/src/cli/util/presence-embed.ts b/src/cli/util/presence-embed.ts index fc4637c..76415ce 100644 --- a/src/cli/util/presence-embed.ts +++ b/src/cli/util/presence-embed.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import { Request } from 'express'; import sharp from 'sharp'; -import { CoopRule, FriendOnlineState, StageScheduleResult } from 'splatnet3-types/splatnet3'; +import { CoopRule, FestVoteState, FriendOnlineState, StageScheduleResult } from 'splatnet3-types/splatnet3'; import { dir } from '../../util/product.js'; import createDebug from '../../util/debug.js'; import { Game, PresenceState } from '../../api/coral-types.js'; @@ -50,11 +50,16 @@ const embed_themes: Record = { }, }; +interface UserEmbedOptions { + show_splatoon3_fest_team?: boolean; +} + const embed_titles: Partial, image: (url: string) => string | undefined, theme?: PresenceEmbedTheme, + options?: UserEmbedOptions, ) => readonly [svg: string, height: number, override_description: string | null]>> = { '0100c2500fc20000': renderUserSplatoon3EmbedPartialSvg, }; @@ -65,7 +70,7 @@ export function getUserEmbedOptionsFromRequest(req: Request) { const theme = url.searchParams.get('theme') === 'dark' ? PresenceEmbedTheme.DARK : PresenceEmbedTheme.LIGHT; const friend_code = url.searchParams.getAll('friend-code').find(c => c.match(/^\d{4}-\d{4}-\d{4}$/)); const transparent = url.searchParams.get('transparent') === '1'; - + let width = url.searchParams.getAll('width') .map(w => parseInt(w)) .map(w => transparent ? w + 60 : w) @@ -74,7 +79,11 @@ export function getUserEmbedOptionsFromRequest(req: Request) { if (!width) width = 500; if (width > 1500) width = 1500; - return {theme, friend_code, transparent, width}; + const options: UserEmbedOptions = { + show_splatoon3_fest_team: url.searchParams.get('show-splatoon3-fest-team') === '1', + }; + + return {theme, friend_code, transparent, width, options}; } export async function renderUserEmbedImage( @@ -112,6 +121,7 @@ export function renderUserEmbedSvg( url_map: Record, theme = PresenceEmbedTheme.LIGHT, friend_code?: string, + options?: UserEmbedOptions, scale = 1, transparent = false, width = 500, @@ -132,7 +142,7 @@ export function renderUserEmbedSvg( Buffer.from(url_map[url][1]).toString('base64') : url_map[url] as string | undefined; - const title_extra = result.title ? embed_titles[result.title.id]?.call(null, result, url_map, image, theme) : null; + const title_extra = result.title ? embed_titles[result.title.id]?.call(null, result, url_map, image, theme, options) : null; if (title_extra) height += title_extra[1]; return htmlentities` @@ -181,6 +191,11 @@ export function renderUserEmbedSvg( Friend code: SW-${friend_code} ` : ''}} + ${{[RawValueSymbol]: options?.show_splatoon3_fest_team && result.splatoon3_fest_team?.myVoteState === FestVoteState.VOTED ? htmlentities` + + ` : ''}} + ${{[RawValueSymbol]: title_extra?.[0] ?? ''}} `; @@ -212,6 +227,7 @@ function renderUserSplatoon3EmbedPartialSvg( url_map: Record, image: (url: string) => string | undefined, theme = PresenceEmbedTheme.LIGHT, + options?: UserEmbedOptions, ) { if (result.splatoon3?.vsMode && ( result.splatoon3.onlineState === FriendOnlineState.VS_MODE_FIGHTING ||