diff --git a/src/backend/common/infrastructure/config/client/discord.ts b/src/backend/common/infrastructure/config/client/discord.ts index df204442..599b65f5 100644 --- a/src/backend/common/infrastructure/config/client/discord.ts +++ b/src/backend/common/infrastructure/config/client/discord.ts @@ -7,7 +7,24 @@ export const statusTypeSchema = z.union([z.literal("online"), z.literal("idle"), export type StatusType = z.infer; -const ipcLocationTupleSchema = z.custom<[number, string]>((val) => Array.isArray(val) && val.length === 2 && typeof val[0] === 'number' && typeof val[1] === 'string'); +const HOST_PORT_LOOSE_REGEX = new RegExp(/.+:\d+/i) + +const ipcTransform = z.transform((val: string, ctx) => { + const sp = val.split(':'); + if(sp.length === 1) { + // assume its a path + return val; + } + if(!HOST_PORT_LOOSE_REGEX.test(val)) { + ctx.issues.push({ + code: 'custom', + message: "Not shaped like host:port", + input: val + }); + return z.NEVER; + } + return [parseInt(sp[1]), sp[0]] +}) export const discordDataSchema = z.object({ token: z.string().optional(), @@ -16,7 +33,7 @@ export const discordDataSchema = z.object({ artworkDefaultUrl: z.string().optional(), statusOverrideAllow: z.array(statusTypeSchema).optional(), listeningActivityAllow: z.union([z.string(), z.array(z.string())]).optional(), - ipcLocations: z.union([z.string(), z.array(z.union([z.string(), ipcLocationTupleSchema]))]).optional() + ipcLocations: z.array(z.string().pipe(ipcTransform)).optional() }); export type DiscordData = z.infer; @@ -25,10 +42,10 @@ const envDataSchema = z.object({ DISCORD_TOKEN: discordDataSchema.shape.token, DISCORD_ARTWORK: discordDataSchema.shape.artwork, DISCORD_APPLICATION_ID: discordDataSchema.shape.applicationId, - DISCORD_IPC_LOCATIONS: discordDataSchema.shape.ipcLocations, + DISCORD_IPC_LOCATIONS: z.string().optional().pipe(transformSplitMaybeString), DISCORD_ARTWORK_DEFAULT_URL: discordDataSchema.shape.artworkDefaultUrl, DISCORD_STATUS_OVERRIDE_ALLOW: z.string().optional().pipe(transformSplitMaybeString).meta(discordDataSchema.shape.statusOverrideAllow.meta()), - DISCORD_LISTENING_ACTIVITY_ALLOW: discordDataSchema.shape.listeningActivityAllow, + DISCORD_LISTENING_ACTIVITY_ALLOW: z.string().optional().pipe(transformSplitMaybeString).meta(discordDataSchema.shape.listeningActivityAllow.meta()), }); export const envSchemas: EnvClientSchema = { @@ -86,7 +103,7 @@ export const discordStrongDataSchema = discordDataSchema.extend({ artwork: z.union([z.boolean(), z.array(z.string())]).optional(), statusOverrideAllow: z.array(statusTypeSchema).optional(), listeningActivityAllow: z.array(z.string()).optional(), - ipcLocations: z.array(z.union([z.string(), ipcLocationTupleSchema])).optional() + //ipcLocations: z.array(z.union([z.string(), z.tuple([z.number(),z.string()])])).optional() }); export type DiscordStrongData = z.infer; @@ -102,7 +119,7 @@ export const discordIPCDataSchema = discordStrongDataSchema.extend({ //ipcLocations: (string | [number, string])[] }); -export type DiscordIPCData = z.infer; +export type DiscordIPCData = Omit, 'ipcLocations'> & {ipcLocations: (string | [number, string])[]}; export interface ActivityAssets { largeImage?: string diff --git a/src/backend/common/vendor/discord/DiscordIPCClient.ts b/src/backend/common/vendor/discord/DiscordIPCClient.ts index 21106a7b..a837d255 100644 --- a/src/backend/common/vendor/discord/DiscordIPCClient.ts +++ b/src/backend/common/vendor/discord/DiscordIPCClient.ts @@ -185,7 +185,8 @@ export const configToIPCConfig = (data: DiscordStrongData): DiscordIPCData => { const parsedPaths: (string | [number, string])[] = []; for(const p of ipcLocations) { if(Array.isArray(p)) { - parsedPaths.push(p) + if(typeof p[0] === 'number' && typeof p[1] === 'string') + parsedPaths.push([p[0],p[1]]); } else { const sp = p.split(':'); if(sp.length > 1) { diff --git a/src/backend/tests/config/config.test.ts b/src/backend/tests/config/config.test.ts index 94506f59..9c9efd60 100644 --- a/src/backend/tests/config/config.test.ts +++ b/src/backend/tests/config/config.test.ts @@ -19,7 +19,6 @@ import { prettifyError, ZodError } from 'zod'; import { getClientEnvSchema, validateClientJson } from '../../common/infrastructure/config/client/clientsMap.ts'; import type { MSBackendEventMap } from '../../common/infrastructure/MSBackendEventMap.ts'; import { zocker } from "zocker"; -import pEvent from 'p-event'; import { generateCommonComponentEnvConfigSchema } from '../../common/infrastructure/config/common.ts'; import { serializeError } from 'serialize-error'; @@ -113,7 +112,7 @@ describe('Sample Configs', function () { }); it(`Sample ${componentType} ENV parses and validates in ScrobbleSources`, async function () { - this.timeout(500000); + this.timeout(5000); const emitter = new WildcardEmitter(); @@ -202,7 +201,7 @@ describe('Sample Configs', function () { }); it(`Sample ${componentType}.json parses and validates in ScrobbleClients`, async function () { - this.timeout(500000); + this.timeout(5000); const emitter = new WildcardEmitter(); await copyFile(samplePath(componentType), `${componentType}.json`); @@ -229,7 +228,8 @@ describe('Sample Configs', function () { zocObj = zocObj.supply(envSchema.env.shape.ROCKSKY_TOKEN, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkaWQ') break; case 'discord': - zocObj = zocObj.supply(envSchema.env.shape.DISCORD_STATUS_OVERRIDE_ALLOW, 'online') + zocObj = zocObj.supply(envSchema.env.shape.DISCORD_STATUS_OVERRIDE_ALLOW, 'online'); + zocObj = zocObj.supply(envSchema.env.shape.DISCORD_IPC_LOCATIONS, '/path/to/ipc,192.168.0.100:8999') } const componentMockData = zocObj.generate(); const primitives = generateCommonComponentEnvConfigSchema(envSchema.prefix.toLocaleUpperCase()); diff --git a/src/backend/utils/StringUtils.ts b/src/backend/utils/StringUtils.ts index 8affc5a3..3cc9d687 100644 --- a/src/backend/utils/StringUtils.ts +++ b/src/backend/utils/StringUtils.ts @@ -415,9 +415,9 @@ interface ArrParseOpts { lower?: boolean } -export const parseArrayFromMaybeString = (value: string | string[] = '', opts: ArrParseOpts = {}) => { +export const parseArrayFromMaybeString = (value: string | string[] = '', opts: ArrParseOpts = {}): string[] => { const {lower = false} = opts; - let arr: string[] = []; + let arr: string[]; if (Array.isArray(value)) { arr = value; } else if (value.trim() === '') {