noot
Some checks failed
commit-tag / commit-tag-image (map[context:./migrations file:./migrations/Dockerfile name:migrations]) (push) Successful in 19s
commit-tag / commit-tag-image (map[context:./ts file:./ts/Dockerfile name:ts]) (push) Failing after 58s

This commit is contained in:
a 2025-06-15 03:14:42 -05:00
parent a43c1959c0
commit 542070a4cb
No known key found for this signature in database
GPG Key ID: 2F22877AA4DFDADB
14 changed files with 202 additions and 114 deletions

Binary file not shown.

View File

@ -49,7 +49,7 @@
"pino": "^9.6.0", "pino": "^9.6.0",
"pino-logfmt": "^0.1.1", "pino-logfmt": "^0.1.1",
"pino-pretty": "^13.0.0", "pino-pretty": "^13.0.0",
"postgres": "^3.4.5", "postgres": "^3.4.7",
"superjson": "^2.2.2", "superjson": "^2.2.2",
"ts-markdown-builder": "^0.4.0", "ts-markdown-builder": "^0.4.0",
"why-is-node-running": "^3.2.2", "why-is-node-running": "^3.2.2",

View File

@ -1,12 +1,13 @@
import { type InteractionCallbackData, type InteractionCallbackOptions, MessageFlags } from 'discordeno' import { type InteractionCallbackData, type InteractionCallbackOptions, MessageFlags } from 'discordeno'
import { c } from '#/di' import { c } from '#/di'
import type { InteractionRef } from '#/discord' import type { InteractionRef } from '#/discord'
import { InteractionResponseTypes } from '@discordeno/types'
import { Bot } from '#/discord/bot' import { Bot } from '#/discord/bot'
// from https://github.com/discordeno/discordeno/blob/21.0.0/packages/bot/src/transformers/interaction.ts#L33 // from https://github.com/discordeno/discordeno/blob/21.0.0/packages/bot/src/transformers/interaction.ts#L33
export const reply_to_interaction = async (props: { export const reply_to_interaction = async (props: {
ref: InteractionRef ref: InteractionRef
type: number type: InteractionResponseTypes
options?: InteractionCallbackOptions & { isPrivate?: boolean; content?: string } options?: InteractionCallbackOptions & { isPrivate?: boolean; content?: string }
}) => { }) => {
const bot = await c.getAsync(Bot) const bot = await c.getAsync(Bot)
@ -19,9 +20,16 @@ export const reply_to_interaction = async (props: {
data.flags = MessageFlags.Ephemeral data.flags = MessageFlags.Ephemeral
} }
if (ref.acknowledged) { switch (type) {
return await bot.helpers.sendFollowupMessage(ref.token, data) case InteractionResponseTypes.UpdateMessage:
if(ref.acknowledged) {
return await bot.helpers.editOriginalInteractionResponse(ref.token, data)
}
default:
if (ref.acknowledged) {
const followUp = await bot.helpers.sendFollowupMessage(ref.token, data)
return followUp
}
return await bot.helpers.sendInteractionResponse(ref.id, ref.token, { type, data }, { withResponse: options?.withResponse })
} }
return await bot.helpers.sendInteractionResponse(ref.id, ref.token, { type, data }, { withResponse: options?.withResponse })
} }

View File

@ -1,35 +1,43 @@
import { c } from '#/di' import { c } from '#/di'
import { PG } from '#/services/pg' import { PG } from '#/services/pg'
import { DiscordGuildSettingsService, DISCORD_GUILD_SETTING_KEYS } from '#/services/guild_settings' import { DiscordGuildSettingsService, DiscordGuildSettingKey, DiscordGuildSettingValue } from '#/services/guild_settings'
import { logger } from '#/logger' import { logger } from '#/logger'
export interface SetDiscordGuildSettingParams<T> { export interface SetDiscordGuildSettingParams<K extends DiscordGuildSettingKey> {
discordGuildId: bigint guildId: bigint
key: string key: K
value: T value: DiscordGuildSettingValue<K>
} }
export async function set_discord_guild_setting<T>(params: SetDiscordGuildSettingParams<T>): Promise<void> { export async function set_discord_guild_setting<K extends DiscordGuildSettingKey>(
params: SetDiscordGuildSettingParams<K>
): Promise<void> {
const settingsService = await c.getAsync(DiscordGuildSettingsService) const settingsService = await c.getAsync(DiscordGuildSettingsService)
await settingsService.setSetting(params.discordGuildId, params.key, params.value) await settingsService.setSetting(params.guildId, params.key, params.value)
logger.info({ logger.info({
discordGuildId: params.discordGuildId, guildId: params.guildId,
key: params.key, key: params.key,
}, 'discord guild setting updated') }, 'discord guild setting updated')
} }
export async function get_discord_guild_setting<T>(discordGuildId: bigint, key: string): Promise<T | null> { export async function get_discord_guild_setting<K extends DiscordGuildSettingKey>(
guildId: bigint,
key: K
): Promise<DiscordGuildSettingValue<K> | null> {
const settingsService = await c.getAsync(DiscordGuildSettingsService) const settingsService = await c.getAsync(DiscordGuildSettingsService)
return await settingsService.getSetting<T>(discordGuildId, key) return await settingsService.getSetting(guildId, key)
} }
export async function delete_discord_guild_setting(discordGuildId: bigint, key: string): Promise<boolean> { export async function delete_discord_guild_setting(
guildId: bigint,
key: DiscordGuildSettingKey
): Promise<boolean> {
const settingsService = await c.getAsync(DiscordGuildSettingsService) const settingsService = await c.getAsync(DiscordGuildSettingsService)
return await settingsService.deleteSetting(discordGuildId, key) return await settingsService.deleteSetting(guildId, key)
} }
export async function get_wynn_guild_info(guildNameOrTag: string): Promise<{ uid: string; name: string; prefix: string } | null> { export async function get_wynn_guild_info(guildNameOrTag: string): Promise<{ uid: string; name: string; prefix: string } | null> {

View File

@ -2,12 +2,27 @@ import { InjectionToken } from '@needle-di/core'
import { createBot } from 'discordeno' import { createBot } from 'discordeno'
import { config } from '#/config' import { config } from '#/config'
import { c } from '#/di' import { c } from '#/di'
import { type BotType, createBotParameters } from './index' import { logger } from '#/logger'
import { type BotType, desiredProperties, intents } from './index'
const createBotWithToken = (token: string) => { const createBotWithToken = (token: string) => {
return createBot({ return createBot({
...createBotParameters, intents: intents.reduce((acc, curr) => acc | curr, 1 as any),
desiredProperties,
token, token,
loggerFactory: (name: 'REST' | 'GATEWAY' | 'BOT') => {
// Create a child logger with the component name
const childLogger = logger.child({ component: `discordeno-${name.toLowerCase()}` })
// Return logger object with required methods
return {
debug: (message: string, ...args: any[]) => childLogger.debug({ args }, message),
info: (message: string, ...args: any[]) => childLogger.info({ args }, message),
warn: (message: string, ...args: any[]) => childLogger.warn({ args }, message),
error: (message: string, ...args: any[]) => childLogger.error({ args }, message),
fatal: (message: string, ...args: any[]) => childLogger.fatal({ args }, message),
}
},
}) })
} }
export const Bot = new InjectionToken<BotType>('DISCORD_BOT') export const Bot = new InjectionToken<BotType>('DISCORD_BOT')

View File

@ -31,6 +31,8 @@ export const events = () => {
token: interaction.token, token: interaction.token,
type: interaction.type, type: interaction.type,
acknowledged: interaction.acknowledged, acknowledged: interaction.acknowledged,
guildId: interaction.guildId,
channelId: interaction.channel.id || interaction.channelId,
}, },
data, data,
}, },
@ -59,5 +61,5 @@ export const events = () => {
], ],
}) })
}, },
} as BotType['events'] } as const satisfies BotType['events']
} }

View File

@ -1,5 +1,5 @@
import { Intents, type InteractionTypes } from '@discordeno/types' import { Intents, type InteractionTypes } from '@discordeno/types'
import type { Bot, CompleteDesiredProperties, DesiredPropertiesBehavior, DiscordInteractionContextType, Guild, Interaction, InteractionData, Member, Message } from 'discordeno' import type { Bot, CompleteDesiredProperties, DesiredPropertiesBehavior, DiscordInteractionContextType, InteractionData, RecursivePartial, TransformersDesiredProperties } from 'discordeno'
export const intents = [ export const intents = [
Intents.GuildModeration, Intents.GuildModeration,
Intents.GuildWebhooks, Intents.GuildWebhooks,
@ -18,68 +18,61 @@ export const intents = [
Intents.GuildMessages, Intents.GuildMessages,
] as const ] as const
export const desiredProperties = {
interaction: {
id: true,
data: true,
type: true,
token: true,
message: true,
channelId: true,
channel: true,
guildId: true,
guild: true,
user: true,
member: true,
context: true,
},
message: {
id: true,
member: true,
guildId: true,
},
} as const satisfies RecursivePartial<TransformersDesiredProperties>
export type DesiredProperties = typeof desiredProperties
export const createBotParameters = { export const createBotParameters = {
intents: intents.reduce((acc, curr) => acc | curr, Intents.Guilds), intents: intents.reduce((acc, curr) => acc | curr, Intents.Guilds),
desiredProperties: { desiredProperties,
interaction: {
id: true,
data: true,
type: true,
token: true,
message: true,
channelId: true,
channel: true,
guildId: true,
guild: true,
user: true,
member: true,
},
message: {
id: true,
member: true,
guildId: true,
},
},
} as const } as const
// Extract the type of desired properties from our parameters export type BotType = Bot<CompleteDesiredProperties<DesiredProperties>, DesiredPropertiesBehavior.RemoveKey>
type ExtractedDesiredProperties = typeof createBotParameters.desiredProperties
// The BotType uses the CompleteDesiredProperties helper to fill in the missing properties
export type BotType = Bot<CompleteDesiredProperties<ExtractedDesiredProperties>, DesiredPropertiesBehavior.RemoveKey>
// Type for the interaction reference passed to workflows/activities // Type for the interaction reference passed to workflows/activities
export interface InteractionRef { export interface InteractionRef {
// id of the interaction
id: bigint id: bigint
/** A continuation token for responding to the interaction */
token: string token: string
type: InteractionTypes type: InteractionTypes
acknowledged?: boolean acknowledged?: boolean
/** Id of the application this interaction is for */
applicationId: bigint;
/** Guild that the interaction was sent from */
/** The guild it was sent from */ /** The guild it was sent from */
guildId: bigint; guildId?: bigint;
/** channelId?: bigint;
* The ID of channel it was sent from
*
* @remarks
* It is recommended that you begin using this channel field to identify the source channel of the interaction as they may deprecate the existing channel_id field in the future.
*/
channelId: bigint;
/** Guild member data for the invoking user, including permissions */ /** Guild member data for the invoking user, including permissions */
memberId?: bigint ; memberId?: bigint ;
/** User object for the invoking user, if invoked in a DM */ /** User object for the invoking user, if invoked in a DM */
userId?: bigint; userId?: bigint;
/** A continuation token for responding to the interaction */
/** Read-only property, always `1` */
version: 1;
/** For the message the button was attached to */ /** For the message the button was attached to */
messageId?: bigint; messageId?: bigint;
// locale of the interaction
locale?: string; locale?: string;
/** The guild's preferred locale, if invoked in a guild */ /** The guild's preferred locale, if invoked in a guild */
guildLocale?: string; guildLocale?: string;
/** The computed permissions for a bot or app in the context of a specific interaction (including channel overwrites) */
appPermissions: bigint;
/** Context where the interaction was triggered from */ /** Context where the interaction was triggered from */
context?: DiscordInteractionContextType; context?: DiscordInteractionContextType;
} }

View File

@ -3,6 +3,8 @@ import { c } from '#/di'
import { PG } from '#/services/pg' import { PG } from '#/services/pg'
import { logger } from '#/logger' import { logger } from '#/logger'
import { JSONValue } from 'postgres' import { JSONValue } from 'postgres'
import { snowflake } from '#/utils/types'
import { inject } from '@needle-di/core'
// Define the guild settings types // Define the guild settings types
export const DiscordGuildSettingSchema = type({ export const DiscordGuildSettingSchema = type({
@ -13,35 +15,61 @@ export const DiscordGuildSettingSchema = type({
export type DiscordGuildSetting = typeof DiscordGuildSettingSchema.infer export type DiscordGuildSetting = typeof DiscordGuildSettingSchema.infer
// Common setting keys as constants
export const DISCORD_GUILD_SETTING_KEYS = { // Define setting schemas with arktype
WYNN_GUILD: 'wynn_guild', // The associated Wynncraft guild ID export const DISCORD_GUILD_SETTING_SCHEMAS = {
ANNOUNCEMENT_CHANNEL: 'announcement_channel', wynn_guild: type('string.uuid'),
MEMBER_ROLE: 'member_role', rank_roles: type({
OFFICER_ROLE: 'officer_role', member: snowflake.optional(),
NOTIFICATION_SETTINGS: 'notification_settings', recruiter: snowflake.optional(),
FEATURES: 'features', captain: snowflake.optional(),
strategist: snowflake.optional(),
chief: snowflake.optional(),
owner: snowflake.optional(),
}),
features: type({
autoRole: 'boolean',
warTracking: 'boolean',
activityTracking: 'boolean',
leaderboard: 'boolean',
}),
} as const } as const
export type DiscordGuildSettingKey = typeof DISCORD_GUILD_SETTING_KEYS[keyof typeof DISCORD_GUILD_SETTING_KEYS] export type DiscordGuildSettingKey = keyof typeof DISCORD_GUILD_SETTING_SCHEMAS
export type DiscordGuildSettingValue<K extends DiscordGuildSettingKey> =
typeof DISCORD_GUILD_SETTING_SCHEMAS[K]['infer']
export class DiscordGuildSettingsService { export class DiscordGuildSettingsService {
constructor(private readonly pg: PG) {} constructor(private readonly pg = inject(PG)) {}
/** /**
* Get a specific setting for a guild * Get a specific setting for a guild
*/ */
async getSetting<T>(guildId: bigint, key: string): Promise<T | null> { async getSetting<K extends DiscordGuildSettingKey>(
guildId: bigint,
key: K
): Promise<DiscordGuildSettingValue<K> | null> {
const { sql } = this.pg const { sql } = this.pg
const result = await sql<{ value: T }[]>` const result = await sql<{ value: unknown }[]>`
SELECT value SELECT value
FROM discord.guild_settings FROM discord.guild_settings
WHERE guild_id = ${guildId.toString()} AND key = ${key} WHERE guild_id = ${guildId.toString()} AND key = ${key}
` `
if (result.length === 0) return null if (result.length === 0) return null
return result[0].value
// Validate the value with arktype
const schema = DISCORD_GUILD_SETTING_SCHEMAS[key]
const parsed = schema(result[0].value)
if (parsed instanceof type.errors) {
logger.error({ guildId, key, errors: parsed.summary }, 'Invalid guild setting value in database')
return null
}
return parsed as DiscordGuildSettingValue<K>
} }
/** /**
@ -62,12 +90,24 @@ export class DiscordGuildSettingsService {
/** /**
* Set a setting for a guild (upsert) * Set a setting for a guild (upsert)
*/ */
async setSetting<T extends JSONValue>(guildId: bigint, key: string, value: T): Promise<void> { async setSetting<K extends DiscordGuildSettingKey>(
guildId: bigint,
key: K,
value: DiscordGuildSettingValue<K>
): Promise<void> {
const { sql } = this.pg const { sql } = this.pg
// Validate the value with arktype
const schema = DISCORD_GUILD_SETTING_SCHEMAS[key]
const parsed = schema(value)
if (parsed instanceof type.errors) {
throw new Error(`Invalid value for guild setting ${key}: ${parsed.summary}`)
}
await sql` await sql`
INSERT INTO discord.guild_settings (guild_id, key, value) INSERT INTO discord.guild_settings (guild_id, key, value)
VALUES (${guildId.toString()}, ${key}, ${sql.json(value)}) VALUES (${guildId.toString()}, ${key}, ${sql.json(parsed as JSONValue)})
ON CONFLICT (guild_id, key) ON CONFLICT (guild_id, key)
DO UPDATE SET DO UPDATE SET
value = EXCLUDED.value value = EXCLUDED.value
@ -79,7 +119,7 @@ export class DiscordGuildSettingsService {
/** /**
* Delete a specific setting for a guild * Delete a specific setting for a guild
*/ */
async deleteSetting(guildId: bigint, key: string): Promise<boolean> { async deleteSetting(guildId: bigint, key: DiscordGuildSettingKey): Promise<boolean> {
const { sql } = this.pg const { sql } = this.pg
const result = await sql` const result = await sql`

View File

@ -0,0 +1 @@
export * from './snowflake'

View File

@ -0,0 +1,15 @@
import { type } from 'arktype'
// Custom arktype for Discord snowflake IDs (bigint)
export const snowflake = type('string|bigint').pipe((value, ctx) => {
if (typeof value === 'bigint') return value
// Parse string as base 10 bigint
try {
return BigInt(value)
} catch {
return ctx.error(`Invalid snowflake ID: ${value}`)
}
})
export type Snowflake = bigint

View File

@ -50,7 +50,7 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
info: async (args) => { info: async (args) => {
const { workflowId } = workflowInfo() const { workflowId } = workflowInfo()
const handle = await startChild(handleCommandGuildInfo, { const handle = await startChild(handleCommandGuildInfo, {
args: [{ ref, discordGuildId: payload.guildId ? BigInt(payload.guildId) : undefined }], args: [{ ref }],
workflowId: `${workflowId}-guild-info`, workflowId: `${workflowId}-guild-info`,
}) })
await handle.result() await handle.result()
@ -58,7 +58,7 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
online: async (args) => { online: async (args) => {
const { workflowId } = workflowInfo() const { workflowId } = workflowInfo()
const handle = await startChild(handleCommandGuildOnline, { const handle = await startChild(handleCommandGuildOnline, {
args: [{ ref, discordGuildId: payload.guildId ? BigInt(payload.guildId) : undefined }], args: [{ ref }],
workflowId: `${workflowId}-guild-online`, workflowId: `${workflowId}-guild-online`,
}) })
await handle.result() await handle.result()
@ -66,7 +66,7 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
leaderboard: async (args) => { leaderboard: async (args) => {
const { workflowId } = workflowInfo() const { workflowId } = workflowInfo()
const handle = await startChild(handleCommandGuildLeaderboard, { const handle = await startChild(handleCommandGuildLeaderboard, {
args: [{ ref, discordGuildId: payload.guildId ? BigInt(payload.guildId) : undefined }], args: [{ ref }],
workflowId: `${workflowId}-guild-leaderboard`, workflowId: `${workflowId}-guild-leaderboard`,
}) })
await handle.result() await handle.result()
@ -79,7 +79,6 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
args: [{ args: [{
ref, ref,
args, args,
discordGuildId: BigInt(payload.guildId!),
}], }],
}) })
await handle.result() await handle.result()

View File

@ -2,7 +2,6 @@ import { proxyActivities } from '@temporalio/workflow'
import type * as activities from '#/activities' import type * as activities from '#/activities'
import { WYNN_GUILD_ID } from '#/constants' import { WYNN_GUILD_ID } from '#/constants'
import type { InteractionRef } from '#/discord' import type { InteractionRef } from '#/discord'
import { DISCORD_GUILD_SETTING_KEYS } from '#/services/guild_settings'
const { formGuildInfoMessage, formGuildOnlineMessage, formGuildLeaderboardMessage, reply_to_interaction, get_discord_guild_setting } = proxyActivities<typeof activities>({ const { formGuildInfoMessage, formGuildOnlineMessage, formGuildLeaderboardMessage, reply_to_interaction, get_discord_guild_setting } = proxyActivities<typeof activities>({
startToCloseTimeout: '30 seconds', startToCloseTimeout: '30 seconds',
@ -19,7 +18,7 @@ export async function handleCommandGuildInfo(payload: CommandPayload): Promise<v
// Try to get the associated Wynncraft guild for this Discord guild // Try to get the associated Wynncraft guild for this Discord guild
let guildId = WYNN_GUILD_ID // Default fallback let guildId = WYNN_GUILD_ID // Default fallback
if (discordGuildId) { if (discordGuildId) {
const wynnGuild = await get_discord_guild_setting<{ uid: string }>(discordGuildId, DISCORD_GUILD_SETTING_KEYS.WYNN_GUILD) const wynnGuild = await get_discord_guild_setting(discordGuildId, 'wynn_guild')
if (wynnGuild?.uid) { if (wynnGuild?.uid) {
guildId = wynnGuild.uid guildId = wynnGuild.uid
} }
@ -39,7 +38,7 @@ export async function handleCommandGuildOnline(payload: CommandPayload): Promise
// Try to get the associated Wynncraft guild for this Discord guild // Try to get the associated Wynncraft guild for this Discord guild
let guildId = WYNN_GUILD_ID // Default fallback let guildId = WYNN_GUILD_ID // Default fallback
if (discordGuildId) { if (discordGuildId) {
const wynnGuild = await get_discord_guild_setting<{ uid: string }>(discordGuildId, DISCORD_GUILD_SETTING_KEYS.WYNN_GUILD) const wynnGuild = await get_discord_guild_setting(discordGuildId, 'wynn_guild')
if (wynnGuild?.uid) { if (wynnGuild?.uid) {
guildId = wynnGuild.uid guildId = wynnGuild.uid
} }
@ -59,7 +58,7 @@ export async function handleCommandGuildLeaderboard(payload: CommandPayload): Pr
// Try to get the associated Wynncraft guild for this Discord guild // Try to get the associated Wynncraft guild for this Discord guild
let guildId = WYNN_GUILD_ID // Default fallback let guildId = WYNN_GUILD_ID // Default fallback
if (discordGuildId) { if (discordGuildId) {
const wynnGuild = await get_discord_guild_setting<{ uid: string }>(discordGuildId, DISCORD_GUILD_SETTING_KEYS.WYNN_GUILD) const wynnGuild = await get_discord_guild_setting(discordGuildId, 'wynn_guild')
if (wynnGuild?.uid) { if (wynnGuild?.uid) {
guildId = wynnGuild.uid guildId = wynnGuild.uid
} }

View File

@ -1,7 +1,7 @@
import { proxyActivities } from '@temporalio/workflow' import { proxyActivities } from '@temporalio/workflow'
import type * as activities from '#/activities' import type * as activities from '#/activities'
import type { InteractionRef } from '#/discord' import type { InteractionRef } from '#/discord'
import { DISCORD_GUILD_SETTING_KEYS } from '#/services/guild_settings' import { InteractionResponseTypes } from '@discordeno/types'
const { reply_to_interaction, set_discord_guild_setting, get_wynn_guild_info } = proxyActivities<typeof activities>({ const { reply_to_interaction, set_discord_guild_setting, get_wynn_guild_info } = proxyActivities<typeof activities>({
startToCloseTimeout: '10 seconds', startToCloseTimeout: '10 seconds',
@ -12,17 +12,30 @@ export interface SetWynnGuildPayload {
args: { args: {
guild: string guild: string
} }
discordGuildId: bigint
} }
export async function handleCommandSetWynnGuild(payload: SetWynnGuildPayload): Promise<void> { export async function handleCommandSetWynnGuild(payload: SetWynnGuildPayload): Promise<void> {
const { ref, args, discordGuildId } = payload const { ref, args } = payload
// Defer the response since this might take a moment // Defer the response since this might take a moment
await reply_to_interaction({ await reply_to_interaction({
ref, ref,
type: 5, // Deferred response type: InteractionResponseTypes.DeferredChannelMessageWithSource,
}) })
ref.acknowledged = true
if (!ref.guildId) {
await reply_to_interaction({
ref,
type: InteractionResponseTypes.UpdateMessage,
options: {
content: `❌ Could not find discord guild. Please try again.`,
isPrivate: true,
},
})
return
}
try { try {
// Validate the Wynncraft guild exists // Validate the Wynncraft guild exists
@ -31,7 +44,7 @@ export async function handleCommandSetWynnGuild(payload: SetWynnGuildPayload): P
if (!guildInfo) { if (!guildInfo) {
await reply_to_interaction({ await reply_to_interaction({
ref, ref,
type: 7, // Update the deferred response type: InteractionResponseTypes.UpdateMessage, // Update the deferred response
options: { options: {
content: `❌ Could not find Wynncraft guild "${args.guild}". Please check the guild name and try again.`, content: `❌ Could not find Wynncraft guild "${args.guild}". Please check the guild name and try again.`,
isPrivate: true, isPrivate: true,
@ -42,19 +55,14 @@ export async function handleCommandSetWynnGuild(payload: SetWynnGuildPayload): P
// Set the association in the database using the generic activity // Set the association in the database using the generic activity
await set_discord_guild_setting({ await set_discord_guild_setting({
discordGuildId, guildId: ref.guildId,
key: DISCORD_GUILD_SETTING_KEYS.WYNN_GUILD, key: 'wynn_guild',
value: { value: guildInfo.uid,
uid: guildInfo.uid,
name: guildInfo.name,
prefix: guildInfo.prefix,
linkedAt: new Date().toISOString(),
},
}) })
await reply_to_interaction({ await reply_to_interaction({
ref, ref,
type: 7, // Update the deferred response type: InteractionResponseTypes.UpdateMessage, // Update the deferred response
options: { options: {
content: `✅ Successfully linked this Discord server to Wynncraft guild **[${guildInfo.prefix}] ${guildInfo.name}**`, content: `✅ Successfully linked this Discord server to Wynncraft guild **[${guildInfo.prefix}] ${guildInfo.name}**`,
isPrivate: true, isPrivate: true,
@ -63,7 +71,7 @@ export async function handleCommandSetWynnGuild(payload: SetWynnGuildPayload): P
} catch (error) { } catch (error) {
await reply_to_interaction({ await reply_to_interaction({
ref, ref,
type: 7, // Update the deferred response type: InteractionResponseTypes.UpdateMessage, // Update the deferred response
options: { options: {
content: `❌ An error occurred while setting the guild: ${error}`, content: `❌ An error occurred while setting the guild: ${error}`,
isPrivate: true, isPrivate: true,

View File

@ -1922,7 +1922,7 @@ __metadata:
pino: "npm:^9.6.0" pino: "npm:^9.6.0"
pino-logfmt: "npm:^0.1.1" pino-logfmt: "npm:^0.1.1"
pino-pretty: "npm:^13.0.0" pino-pretty: "npm:^13.0.0"
postgres: "npm:^3.4.5" postgres: "npm:^3.4.7"
rollup: "npm:^4.34.8" rollup: "npm:^4.34.8"
superjson: "npm:^2.2.2" superjson: "npm:^2.2.2"
ts-markdown-builder: "npm:^0.4.0" ts-markdown-builder: "npm:^0.4.0"
@ -4178,10 +4178,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postgres@npm:^3.4.5": "postgres@npm:^3.4.7":
version: 3.4.5 version: 3.4.7
resolution: "postgres@npm:3.4.5" resolution: "postgres@npm:3.4.7"
checksum: 10c0/53415acea77e97bdc1eeb861048f34964e2236e4d4d42f408fc9b901e62bfcf7443a487ebfdad18b57b468c6e297bf8d22097106a200f62eb1262eb5a71355df checksum: 10c0/b2e61b1064d38e7e1df8291f6d5a7e11f892a3240e00cf2b5e5542bf9abbfe97f3963164aeb56b42c1ab6b8aae3454c57f5bbc1791df0769375542740a7cde72
languageName: node languageName: node
linkType: hard linkType: hard