noot
This commit is contained in:
parent
4f89dd103b
commit
24c7c2b9b1
@ -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.
@ -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",
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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
24
ts/src/discord/bot.ts
Normal 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
|
||||
},
|
||||
})
|
||||
189
ts/src/discord/botevent/command_parser.ts
Normal file
189
ts/src/discord/botevent/command_parser.ts
Normal 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");
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -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
|
||||
@ -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[]
|
||||
@ -1,4 +1,4 @@
|
||||
import {BotType} from "#/bot"
|
||||
import {BotType} from "#/discord"
|
||||
|
||||
|
||||
export type BotEventsType = BotType['events']
|
||||
@ -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;
|
||||
}
|
||||
@ -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', () => {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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> {
|
||||
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
export * from "./discord";
|
||||
export * from "./guild-messages";
|
||||
export * from "./guild_messages";
|
||||
export * from "./guilds";
|
||||
export * from "./items";
|
||||
export * from "./players";
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user