Show Splatoon 3 Splatfest team image in presence embed

This commit is contained in:
Samuel Elliott 2024-01-07 08:47:04 +00:00
parent 597d3ba554
commit b5d86bf2d6
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
4 changed files with 40 additions and 8 deletions

View File

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

View File

@ -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<ParentArguments>) {
}).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<Arguments>) {
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<string, readonly [name: string, data: Uint8Array, type: string]> = {};
@ -77,7 +83,9 @@ export async function handler(argv: ArgumentsCamelCase<Arguments>) {
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);

View File

@ -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<string, readonly [name: string, data: Uint8Array, type: string]> = {};
@ -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<string, readonly [name: string, data: Uint8Array, type: string]> = {};
@ -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(`<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width,initial-scale=1"/><style>body{margin:0;min-height:100vh;width:100vw;min-width:fit-content;display:flex;align-items:center;justify-content:center}</style></head>`);

View File

@ -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<PresenceEmbedTheme, PresenceEmbedThemeColours> = {
},
};
interface UserEmbedOptions {
show_splatoon3_fest_team?: boolean;
}
const embed_titles: Partial<Record<string, (
result: PresenceResponse,
url_map: Record<string, string | readonly [url: string, data: Uint8Array, type: string]>,
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<string, string | readonly [url: string, data: Uint8Array, type: string]>,
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`<?xml version="1.0" encoding="UTF-8" standalone="no"?>
@ -181,6 +191,11 @@ export function renderUserEmbedSvg(
<text x="30" y="186" fill="${colours.text}" font-size="14" font-family="${font_family}" font-weight="400">Friend code: SW-${friend_code}</text>
` : ''}}
${{[RawValueSymbol]: options?.show_splatoon3_fest_team && result.splatoon3_fest_team?.myVoteState === FestVoteState.VOTED ? htmlentities`
<image x="${width - 60}" y="33" width="30" height="30"
href="${image(result.splatoon3_fest_team.image.url) ?? result.splatoon3_fest_team.image.url}" />
` : ''}}
${{[RawValueSymbol]: title_extra?.[0] ?? ''}}
</svg>
`;
@ -212,6 +227,7 @@ function renderUserSplatoon3EmbedPartialSvg(
url_map: Record<string, string | readonly [url: string, data: Uint8Array, type: string]>,
image: (url: string) => string | undefined,
theme = PresenceEmbedTheme.LIGHT,
options?: UserEmbedOptions,
) {
if (result.splatoon3?.vsMode && (
result.splatoon3.onlineState === FriendOnlineState.VS_MODE_FIGHTING ||