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

This commit is contained in:
a 2025-06-14 00:36:37 -05:00
parent 4f89dd103b
commit 24c7c2b9b1
No known key found for this signature in database
GPG Key ID: 2F22877AA4DFDADB
22 changed files with 331 additions and 312 deletions

View File

@ -1,160 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
call-bind@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c"
integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==
dependencies:
call-bind-apply-helpers "^1.0.0"
es-define-property "^1.0.0"
get-intrinsic "^1.2.4"
set-function-length "^1.2.2"
call-bound@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.3.tgz#41cfd032b593e39176a71533ab4f384aa04fd681"
integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==
dependencies:
call-bind-apply-helpers "^1.0.1"
get-intrinsic "^1.2.6"
define-data-property@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
dependencies:
es-define-property "^1.0.0"
es-errors "^1.3.0"
gopd "^1.0.1"
dunder-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
dependencies:
call-bind-apply-helpers "^1.0.1"
es-errors "^1.3.0"
gopd "^1.2.0"
es-define-property@^1.0.0, es-define-property@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
dependencies:
es-errors "^1.3.0"
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
get-intrinsic@^1.2.4, get-intrinsic@^1.2.6:
version "1.3.0"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
dependencies:
call-bind-apply-helpers "^1.0.2"
es-define-property "^1.0.1"
es-errors "^1.3.0"
es-object-atoms "^1.1.1"
function-bind "^1.1.2"
get-proto "^1.0.1"
gopd "^1.2.0"
has-symbols "^1.1.0"
hasown "^2.0.2"
math-intrinsics "^1.1.0"
get-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
dependencies:
dunder-proto "^1.0.1"
es-object-atoms "^1.0.0"
gopd@^1.0.1, gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
has-property-descriptors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
dependencies:
es-define-property "^1.0.0"
has-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
hasown@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
isarray@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
json-stable-stringify@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.2.1.tgz#addb683c2b78014d0b78d704c2fcbdf0695a60e2"
integrity sha512-Lp6HbbBgosLmJbjx0pBLbgvx68FaFU1sdkmBuckmhhJ88kL13OA51CDtR2yJB50eCNMH9wRqtQNNiAqQH4YXnA==
dependencies:
call-bind "^1.0.8"
call-bound "^1.0.3"
isarray "^2.0.5"
jsonify "^0.0.1"
object-keys "^1.1.1"
jsonify@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978"
integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==
math-intrinsics@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
set-function-length@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
dependencies:
define-data-property "^1.1.4"
es-errors "^1.3.0"
function-bind "^1.1.2"
get-intrinsic "^1.2.4"
gopd "^1.0.1"
has-property-descriptors "^1.0.2"

Binary file not shown.

View File

@ -12,6 +12,7 @@
"typescript": "5.7.3"
},
"dependencies": {
"@discordeno/types": "^21.0.0",
"@needle-di/core": "^0.10.1",
"@temporalio/activity": "^1.11.7",
"@temporalio/client": "^1.11.7",

View File

@ -1,14 +1,7 @@
import { Bot } from "#/bot";
import { Bot } from "#/discord/bot";
import {c} from "#/di"
import { InteractionResponseTypes, InteractionCallbackOptions, InteractionCallbackData, InteractionTypes, MessageFlags } from "discordeno";
export interface InteractionRef {
id: bigint
token: string
type: InteractionTypes
acknowledged?: boolean
}
import { InteractionRef } from "#/discord";
// from https://github.com/discordeno/discordeno/blob/21.0.0/packages/bot/src/transformers/interaction.ts#L33
export const reply_to_interaction = async (props: {

View File

@ -4,7 +4,7 @@
export * from "./database";
export * from "./discord";
export * from "./guild-messages";
export * from "./guild";
export * from "./guild_messages";
export * from "./leaderboards";
export * from "./players";

View File

@ -4,9 +4,9 @@ import { Command } from 'clipanion';
// di
import "#/services/pg"
import { DISCORD_GUILD_ID } from '#/constants';
import { Bot } from '#/bot';
import { events } from '#/bot/botevent/handler';
import { SLASH_COMMANDS } from '#/bot/botevent/slash_commands';
import { Bot } from '#/discord/bot';
import { events } from '#/discord/botevent/handler';
import { SLASH_COMMANDS } from '#/discord/botevent/slash_commands';
import { c } from '#/di';
import { config } from '#/config';

View File

@ -117,7 +117,7 @@ export class WorkerCommand extends Command {
namespace: config.TEMPORAL_NAMESPACE,
workflowsPath: require.resolve('../workflows'),
dataConverter: {
payloadConverterPath: require.resolve('../payload-converter'),
payloadConverterPath: require.resolve('../payload_converter'),
},
bundlerOptions: {
webpackConfigHook: (config)=>{

24
ts/src/discord/bot.ts Normal file
View File

@ -0,0 +1,24 @@
import { config } from "#/config";
import { c } from "#/di";
import { InjectionToken } from "@needle-di/core";
import { createBot, } from "discordeno";
import { BotType, createBotParameters } from "./index";
const createBotWithToken = (token: string) => {
return createBot({
...createBotParameters,
token,
})
}
export const Bot = new InjectionToken<BotType>("DISCORD_BOT")
c.bind({
provide: Bot,
useFactory: () => {
let token = config.DISCORD_TOKEN
if(!token) {
throw new Error('no discord token found. bot cant start');
}
const bot = createBotWithToken(token)
return bot
},
})

View File

@ -0,0 +1,189 @@
import { ApplicationCommandOptionTypes, CreateApplicationCommand, DiscordInteractionDataOption} from "@discordeno/types";
import { SLASH_COMMANDS } from "./slash_commands";
import { InteractionData } from "..";
// Map option types to their TypeScript types
type OptionTypeMap = {
[ApplicationCommandOptionTypes.String]: string;
[ApplicationCommandOptionTypes.Integer]: number;
[ApplicationCommandOptionTypes.Boolean]: boolean;
[ApplicationCommandOptionTypes.User]: string; // user ID
[ApplicationCommandOptionTypes.Channel]: string; // channel ID
[ApplicationCommandOptionTypes.Role]: string; // role ID
[ApplicationCommandOptionTypes.Number]: number;
[ApplicationCommandOptionTypes.Mentionable]: string; // ID
[ApplicationCommandOptionTypes.Attachment]: string; // attachment ID
};
// Extract the argument types from command options
type ExtractArgs<Options extends readonly any[]> = {
[K in Options[number] as K extends { name: infer N; type: infer T }
? T extends keyof OptionTypeMap
? N extends string
? N
: never
: never
: never]: Options[number] extends { name: K; type: infer T; required?: infer R }
? T extends keyof OptionTypeMap
? R extends true
? OptionTypeMap[T]
: OptionTypeMap[T] | undefined
: never
: never;
};
// Handler function type that accepts typed arguments
type HandlerFunction<Args = {}> = (args: Args) => Promise<void> | void;
// Helper types to extract command structure with proper argument types
type ExtractSubcommands<T> = T extends { options: infer O }
? O extends readonly any[]
? {
[K in O[number] as K extends { name: infer N; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
? N extends string
? N
: never
: never]: O[number] extends { name: K; options?: infer SubO }
? SubO extends readonly any[]
? HandlerFunction<ExtractArgs<SubO>>
: HandlerFunction<{}>
: HandlerFunction<{}>
}
: never
: never;
type ExtractCommands<T extends readonly CreateApplicationCommand[]> = {
[K in T[number] as K['name']]: ExtractSubcommands<K> extends never
? T[number] extends { name: K; options?: infer O }
? O extends readonly any[]
? HandlerFunction<ExtractArgs<O>>
: HandlerFunction<{}>
: HandlerFunction<{}>
: ExtractSubcommands<K>
};
// The actual command handler type based on SLASH_COMMANDS
export type CommandHandlers = ExtractCommands<typeof SLASH_COMMANDS>;
// Helper function to parse option values from interaction data
function parseOptions(options?: DiscordInteractionDataOption[]): Record<string, any> {
if (!options) return {};
const args: Record<string, any> = {};
for (const option of options) {
if (option.type === ApplicationCommandOptionTypes.SubCommand ||
option.type === ApplicationCommandOptionTypes.SubCommandGroup) {
continue;
}
args[option.name] = option.value;
}
return args;
}
// Helper function to create command handlers with type safety
export function createCommandHandler<T extends readonly CreateApplicationCommand[]>(
{handler, notFoundHandler}:{
handler: ExtractCommands<T>
notFoundHandler: HandlerFunction<{}>
}) {
return async (data: InteractionData): Promise<void> => {
if (!data || !data.name) {
await notFoundHandler({});
return;
}
const commandName = data.name as keyof typeof handler;
const commandHandler = handler[commandName];
if (!commandHandler) {
await notFoundHandler({});
return;
}
// Check if it's a direct command or has subcommands
if (typeof commandHandler === 'function') {
// Parse arguments from top-level options
const args = parseOptions(data.options);
await commandHandler(args);
} else {
// Handle subcommands
const subcommand = data.options?.find(
opt => opt.type === ApplicationCommandOptionTypes.SubCommand ||
opt.type === ApplicationCommandOptionTypes.SubCommandGroup
);
if (!subcommand) {
await notFoundHandler({});
return;
}
const subHandler = commandHandler[subcommand.name as keyof typeof commandHandler];
if (!subHandler || typeof subHandler !== 'function') {
await notFoundHandler({});
return;
}
// Parse arguments from subcommand options
const args = parseOptions(subcommand.options);
await subHandler(args);
}
}
}
// Function to validate that all commands are implemented
export function validateCommandImplementation(handlers: CommandHandlers): void {
for (const command of SLASH_COMMANDS) {
const commandName = command.name;
if (!(commandName in handlers)) {
throw new Error(`Command "${commandName}" is not implemented`);
}
// Check subcommands if they exist
if (command.options) {
const subcommands = command.options.filter(
opt => opt.type === ApplicationCommandOptionTypes.SubCommand
);
if (subcommands.length > 0) {
const handlerObj = handlers[commandName as keyof CommandHandlers];
if (typeof handlerObj === 'function') {
throw new Error(`Command "${commandName}" has subcommands but is implemented as a function`);
}
for (const subcommand of subcommands) {
if (!(subcommand.name in handlerObj)) {
throw new Error(`Subcommand "${commandName}.${subcommand.name}" is not implemented`);
}
}
}
}
}
}
// Helper function to create command handlers with type safety
export function createCommandHandlers<T extends CommandHandlers>(handlers: T): T {
return handlers;
}
// Example usage showing the type-safe implementation
export const exampleHandlers = createCommandHandlers({
guild: {
leaderboard: async (args: {}) => {
console.log("Guild leaderboard command");
},
info: async (args: {}) => {
console.log("Guild info command");
},
online: async (args: {}) => {
console.log("Guild online command");
},
},
admin: {
set_wynn_guild: async (args: {}) => {
console.log("Admin set_wynn_guild command");
},
},
});

View File

@ -1,10 +1,11 @@
import { Bot, BotType } from "#/bot"
import { Bot } from "#/discord/bot"
import { ActivityTypes, InteractionTypes } from "discordeno"
import { c } from "#/di"
import { Client } from "@temporalio/client"
import { workflowHandleInteractionCreate } from "#/workflows"
import { BotType } from "#/discord"
export const events = () => {return {
export const events = () => {return {
interactionCreate: async (interaction) => {
if(interaction.acknowledged) {
return

View File

@ -1,9 +1,6 @@
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, CreateApplicationCommand } from "discordeno"
import { ApplicationCommandOptionTypes, ApplicationCommandTypes, CreateApplicationCommand } from "@discordeno/types"
const createCommands = <T extends CreateApplicationCommand[]>(commands: T): T => {
return commands
}
export const SLASH_COMMANDS = createCommands([
export const SLASH_COMMANDS = [
{
name: `guild`,
description: "guild commands",
@ -41,4 +38,4 @@ export const SLASH_COMMANDS = createCommands([
},
],
}
])
] as const satisfies CreateApplicationCommand[]

View File

@ -1,4 +1,4 @@
import {BotType} from "#/bot"
import {BotType} from "#/discord"
export type BotEventsType = BotType['events']

View File

@ -1,10 +1,6 @@
import { config } from "#/config";
import { c } from "#/di";
import { InjectionToken } from "@needle-di/core";
import {createBot, Intents} from "discordeno"
const intents = [
import { Intents, InteractionTypes } from "@discordeno/types";
import type { Bot, DesiredPropertiesBehavior, CompleteDesiredProperties } from "discordeno";
export const intents = [
Intents.GuildModeration ,
Intents.GuildWebhooks ,
Intents.GuildExpressions ,
@ -22,9 +18,8 @@ const intents = [
Intents.GuildMessages,
] as const
export const createBotWithToken = (token: string) => createBot({
export const createBotParameters = {
intents: intents.reduce((acc, curr) => acc | curr, Intents.Guilds),
token: token,
desiredProperties: {
interaction: {
id: true,
@ -45,18 +40,28 @@ export const createBotWithToken = (token: string) => createBot({
guildId: true,
},
}
})
} as const
export type BotType = ReturnType<typeof createBotWithToken>
export const Bot = new InjectionToken<BotType>("DISCORD_BOT")
c.bind({
provide: Bot,
async: true,
useFactory: async () => {
let token = config.DISCORD_TOKEN
if(!token) {
throw new Error('no discord token found. bot cant start');
}
return createBotWithToken(token)
},
})
// Extract the type of desired properties from our parameters
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
export interface InteractionRef {
id: bigint;
token: string;
type: InteractionTypes;
acknowledged?: boolean;
}
// Type for the interaction data payload
export type InteractionData = Parameters<NonNullable<BotType['events']['interactionCreate']>>[0]['data'];
// Type for the complete interaction handling payload
export interface InteractionCreatePayload {
ref: InteractionRef;
data: InteractionData;
}

View File

@ -13,7 +13,7 @@ c.bind({
connection,
namespace: config.TEMPORAL_NAMESPACE,
dataConverter: {
payloadConverterPath: require.resolve('../../payload-converter'),
payloadConverterPath: require.resolve('../../payload_converter'),
},
});
process.on('exit', () => {

View File

@ -1,119 +1,86 @@
import { proxyActivities, startChild, workflowInfo } from '@temporalio/workflow';
import type * as activities from '#/activities';
import { ApplicationCommandOptionTypes, InteractionTypes } from 'discordeno';
import { handleCommandGuildInfo, handleCommandGuildOnline, handleCommandGuildLeaderboard } from './guild-messages';
import { BotType } from '#/bot';
import { InteractionTypes } from '@discordeno/types';
import { handleCommandGuildInfo, handleCommandGuildOnline, handleCommandGuildLeaderboard } from './guild_messages';
import { createCommandHandler } from '#/discord/botevent/command_parser';
import { SLASH_COMMANDS } from '#/discord/botevent/slash_commands';
import { InteractionCreatePayload} from '#/discord';
const { reply_to_interaction } = proxyActivities<typeof activities>({
startToCloseTimeout: '1 minute',
});
interface HandleInteractionCreatePayload {
ref: activities.InteractionRef
data: Parameters<NonNullable<BotType['events']['interactionCreate']>>[0]['data']
}
// Define command handlers with type safety
const workflowHandleApplicationCommand = async (
payload: HandleInteractionCreatePayload,
payload: InteractionCreatePayload,
) => {
const { ref, data } = payload;
const notFoundHandler = async (content: string) => {
await reply_to_interaction({
ref,
type: 4,
options: {
content: content,
isPrivate: true,
}
});
}
if (!data || !data.name) {
await reply_to_interaction({
ref,
type: 4,
options: {
content: "Invalid command data",
isPrivate: true,
}
});
return;
await notFoundHandler(`Invalid command data`);
return
}
// Build command path
const commandPath: string[] = [data.name];
if (data.options) {
for (const option of data.options) {
if (option.type === ApplicationCommandOptionTypes.SubCommand) {
commandPath.push(option.name);
}
const commandHandler = createCommandHandler<typeof SLASH_COMMANDS>({
notFoundHandler: async () => {
await notFoundHandler(`command not found`);
},
handler:{
guild: {
info: async (args: {}) => {
const { workflowId } = workflowInfo();
const handle = await startChild(handleCommandGuildInfo, {
args: [{ ref }],
workflowId: `${workflowId}-guild-info`,
});
await handle.result();
},
online: async (args: {}) => {
const { workflowId } = workflowInfo();
const handle = await startChild(handleCommandGuildOnline, {
args: [{ ref }],
workflowId: `${workflowId}-guild-online`,
});
await handle.result();
},
leaderboard: async (args: {}) => {
const { workflowId } = workflowInfo();
const handle = await startChild(handleCommandGuildLeaderboard, {
args: [{ ref }],
workflowId: `${workflowId}-guild-leaderboard`,
});
await handle.result();
},
},
admin: {
set_wynn_guild: async (args: {}) => {
await reply_to_interaction({
ref,
type: 4,
options: {
content: "Not implemented yet",
isPrivate: true,
}
});
},
},
}
}
});
// Route to appropriate child workflow based on command path
const fullCommand = commandPath.join('.');
const { workflowId } = workflowInfo();
try {
switch (fullCommand) {
case 'guild.info': {
const handle = await startChild(handleCommandGuildInfo, {
args: [{ ref }],
workflowId: `${workflowId}-guild-info`,
});
await handle.result();
break;
}
case 'guild.online': {
const handle = await startChild(handleCommandGuildOnline, {
args: [{ ref }],
workflowId: `${workflowId}-guild-online`,
});
await handle.result();
break;
}
case 'guild.leaderboard': {
const handle = await startChild(handleCommandGuildLeaderboard, {
args: [{ ref }],
workflowId: `${workflowId}-guild-leaderboard`,
});
await handle.result();
break;
}
case 'admin.set_wynn_guild': {
await reply_to_interaction({
ref,
type: 4,
options: {
content: "Not implemented yet",
isPrivate: true,
}
});
break;
}
default: {
await reply_to_interaction({
ref,
type: 4,
options: {
content: `Command not implemented: ${fullCommand}`,
isPrivate: true,
}
});
}
}
} catch (error) {
await reply_to_interaction({
ref,
type: 4,
options: {
content: `Error executing command: ${error}`,
isPrivate: true,
}
});
}
await commandHandler(data);
}
export const workflowHandleInteractionCreate = async (
payload: HandleInteractionCreatePayload,
payload: InteractionCreatePayload,
) => {
const {ref, data} = payload

View File

@ -1,6 +1,7 @@
import { proxyActivities } from "@temporalio/workflow";
import type * as activities from "#/activities";
import { WYNN_GUILD_ID } from "#/constants";
import { InteractionRef } from "#/discord";
const {
formGuildInfoMessage,
@ -12,7 +13,7 @@ const {
});
interface CommandPayload {
ref: activities.InteractionRef;
ref: InteractionRef;
}
export async function handleCommandGuildInfo(payload: CommandPayload): Promise<void> {

View File

@ -3,7 +3,7 @@
*/
export * from "./discord";
export * from "./guild-messages";
export * from "./guild_messages";
export * from "./guilds";
export * from "./items";
export * from "./players";

View File

@ -74,7 +74,7 @@ __metadata:
languageName: node
linkType: hard
"@discordeno/types@npm:21.0.0":
"@discordeno/types@npm:21.0.0, @discordeno/types@npm:^21.0.0":
version: 21.0.0
resolution: "@discordeno/types@npm:21.0.0"
checksum: 10c0/5d47321e75cca4aa60f69d8b146c1c87fae4c4f7065a422a41ef17d34747b6d933d1699567a887beca5e6fc086b5cba0db9ca67b34b81f348afdd9432ea2979d
@ -1324,6 +1324,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "backend@workspace:."
dependencies:
"@discordeno/types": "npm:^21.0.0"
"@needle-di/core": "npm:^0.10.1"
"@temporalio/activity": "npm:^1.11.7"
"@temporalio/client": "npm:^1.11.7"