noot
This commit is contained in:
parent
97d85d7fc9
commit
e55886cd8f
@ -31,7 +31,7 @@ export async function update_wynn_items() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
found_new = true
|
found_new = true
|
||||||
log.info(`updating wynn with new hash`, { old: currenthash, new: dataHash })
|
log.info('updating wynn with new hash', { old: currenthash, new: dataHash })
|
||||||
for (const [displayName, item] of Object.entries(parsed)) {
|
for (const [displayName, item] of Object.entries(parsed)) {
|
||||||
const json = stringify(item)
|
const json = stringify(item)
|
||||||
if (!json) {
|
if (!json) {
|
||||||
|
|||||||
@ -27,7 +27,7 @@ select * from ranked
|
|||||||
where ranked.uid = ${guild_id}
|
where ranked.uid = ${guild_id}
|
||||||
`
|
`
|
||||||
|
|
||||||
if (result.length == 0) {
|
if (result.length === 0) {
|
||||||
return {
|
return {
|
||||||
content: 'No guild found.',
|
content: 'No guild found.',
|
||||||
}
|
}
|
||||||
@ -36,9 +36,9 @@ where ranked.uid = ${guild_id}
|
|||||||
const guild = result[0]
|
const guild = result[0]
|
||||||
|
|
||||||
const output = [
|
const output = [
|
||||||
`# 🏰 Guild Information`,
|
'# 🏰 Guild Information',
|
||||||
`## **[${guild.prefix}] ${guild.name}**\n`,
|
`## **[${guild.prefix}] ${guild.name}**\n`,
|
||||||
`### 📊 Statistics`,
|
'### 📊 Statistics',
|
||||||
`> **Level:** \`${guild.level}\``,
|
`> **Level:** \`${guild.level}\``,
|
||||||
`> **Total XP:** \`${formatNumber(guild.xp)}\``,
|
`> **Total XP:** \`${formatNumber(guild.xp)}\``,
|
||||||
`> **XP Rank:** \`#${guild.xp_rank >= 1000 ? '1000+' : guild.xp_rank}\``,
|
`> **XP Rank:** \`#${guild.xp_rank >= 1000 ? '1000+' : guild.xp_rank}\``,
|
||||||
@ -81,7 +81,7 @@ export async function formGuildOnlineMessage(guild_id: string): Promise<CreateMe
|
|||||||
.array()
|
.array()
|
||||||
.assert(result)
|
.assert(result)
|
||||||
|
|
||||||
if (members.length == 0) {
|
if (members.length === 0) {
|
||||||
return {
|
return {
|
||||||
content: '😴 No guild members are currently online.',
|
content: '😴 No guild members are currently online.',
|
||||||
}
|
}
|
||||||
@ -97,7 +97,7 @@ export async function formGuildOnlineMessage(guild_id: string): Promise<CreateMe
|
|||||||
// Group members by server
|
// Group members by server
|
||||||
const membersByServer = members.reduce(
|
const membersByServer = members.reduce(
|
||||||
(acc, member) => {
|
(acc, member) => {
|
||||||
if (acc[member.server] == undefined) {
|
if (acc[member.server] === undefined) {
|
||||||
acc[member.server] = []
|
acc[member.server] = []
|
||||||
}
|
}
|
||||||
acc[member.server].push(member)
|
acc[member.server].push(member)
|
||||||
@ -122,7 +122,7 @@ export async function formGuildOnlineMessage(guild_id: string): Promise<CreateMe
|
|||||||
})
|
})
|
||||||
|
|
||||||
const output = [
|
const output = [
|
||||||
`# 🟢 Online Guild Members`,
|
'# 🟢 Online Guild Members',
|
||||||
`**[${guildPrefix}] ${guildName}**\n`,
|
`**[${guildPrefix}] ${guildName}**\n`,
|
||||||
`📊 **Total Online:** \`${members.length}\` members across \`${sortedServers.length}\` servers\n`,
|
`📊 **Total Online:** \`${members.length}\` members across \`${sortedServers.length}\` servers\n`,
|
||||||
...serverSections,
|
...serverSections,
|
||||||
@ -203,12 +203,12 @@ export async function formGuildLeaderboardMessage(guild_id: string): Promise<Cre
|
|||||||
const avgContribution = Math.floor(totalXP / members.length)
|
const avgContribution = Math.floor(totalXP / members.length)
|
||||||
|
|
||||||
const output = [
|
const output = [
|
||||||
`# 📊 Guild XP Leaderboard`,
|
'# 📊 Guild XP Leaderboard',
|
||||||
`**[${guildPrefix}] ${guildName}**\n`,
|
`**[${guildPrefix}] ${guildName}**\n`,
|
||||||
`📈 **Total Guild XP:** \`${totalXP.toLocaleString()}\``,
|
`📈 **Total Guild XP:** \`${totalXP.toLocaleString()}\``,
|
||||||
`👥 **Total Members:** \`${members.length}\``,
|
`👥 **Total Members:** \`${members.length}\``,
|
||||||
`📊 **Average Contribution:** \`${avgContribution.toLocaleString()}\`\n`,
|
`📊 **Average Contribution:** \`${avgContribution.toLocaleString()}\`\n`,
|
||||||
`### Top Contributors`,
|
'### Top Contributors',
|
||||||
'```',
|
'```',
|
||||||
leaderboardTable,
|
leaderboardTable,
|
||||||
'```',
|
'```',
|
||||||
|
|||||||
@ -100,7 +100,6 @@ export const scrape_online_players = async () => {
|
|||||||
log.warn(`failed to get uuid for ${playerName}`, {
|
log.warn(`failed to get uuid for ${playerName}`, {
|
||||||
err: e,
|
err: e,
|
||||||
})
|
})
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +112,6 @@ export const scrape_online_players = async () => {
|
|||||||
log.warn(`failed to update server for ${playerName}`, {
|
log.warn(`failed to update server for ${playerName}`, {
|
||||||
err: e,
|
err: e,
|
||||||
})
|
})
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { c } from '#/di'
|
|||||||
|
|
||||||
// di
|
// di
|
||||||
import '#/services/temporal'
|
import '#/services/temporal'
|
||||||
import path from 'path'
|
import path from 'node:path'
|
||||||
import { Client, ScheduleNotFoundError, type ScheduleOptions, ScheduleOverlapPolicy } from '@temporalio/client'
|
import { Client, ScheduleNotFoundError, type ScheduleOptions, ScheduleOverlapPolicy } from '@temporalio/client'
|
||||||
import { NativeConnection, Worker } from '@temporalio/worker'
|
import { NativeConnection, Worker } from '@temporalio/worker'
|
||||||
import { PG } from '#/services/pg'
|
import { PG } from '#/services/pg'
|
||||||
@ -129,7 +129,7 @@ export class WorkerCommand extends Command {
|
|||||||
if (!config.resolve.alias) config.resolve.alias = {}
|
if (!config.resolve.alias) config.resolve.alias = {}
|
||||||
config.resolve.alias = {
|
config.resolve.alias = {
|
||||||
'#': path.resolve(process.cwd(), 'src/'),
|
'#': path.resolve(process.cwd(), 'src/'),
|
||||||
...config.resolve!.alias,
|
...config.resolve?.alias,
|
||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
|
|||||||
@ -111,27 +111,44 @@ const TEST_COMMANDS = [
|
|||||||
},
|
},
|
||||||
] as const satisfies CreateApplicationCommand[]
|
] as const satisfies CreateApplicationCommand[]
|
||||||
|
|
||||||
|
// Helper function to create a mock handler structure with all required commands
|
||||||
|
function createMockHandlers() {
|
||||||
|
return {
|
||||||
|
simple: vi.fn(),
|
||||||
|
complex: {
|
||||||
|
list: vi.fn(),
|
||||||
|
create: vi.fn(),
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
user: {
|
||||||
|
ban: vi.fn(),
|
||||||
|
kick: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to create a complete test setup
|
||||||
|
function createTestSetup() {
|
||||||
|
const handlers = createMockHandlers()
|
||||||
|
const notFoundHandler = vi.fn()
|
||||||
|
|
||||||
|
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
||||||
|
commands: TEST_COMMANDS,
|
||||||
|
handler: handlers,
|
||||||
|
notFoundHandler,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
handlers,
|
||||||
|
notFoundHandler,
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
describe('createCommandHandler', () => {
|
describe('createCommandHandler', () => {
|
||||||
it('should handle a simple command with required string argument', async () => {
|
it('should handle a simple command with required string argument', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const { handlers, notFoundHandler, handler } = createTestSetup()
|
||||||
const notFoundHandler = vi.fn()
|
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: simpleHandler,
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'simple',
|
name: 'simple',
|
||||||
@ -140,31 +157,12 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(simpleHandler).toHaveBeenCalledWith({ message: 'Hello world' })
|
expect(handlers.simple).toHaveBeenCalledWith({ message: 'Hello world' })
|
||||||
expect(notFoundHandler).not.toHaveBeenCalled()
|
expect(notFoundHandler).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle a simple command with optional arguments', async () => {
|
it('should handle a simple command with optional arguments', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const { handlers, handler } = createTestSetup()
|
||||||
const notFoundHandler = vi.fn()
|
|
||||||
let a: ExtractCommands<typeof TEST_COMMANDS>
|
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: simpleHandler,
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'simple',
|
name: 'simple',
|
||||||
@ -176,30 +174,11 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(simpleHandler).toHaveBeenCalledWith({ message: 'Hello', count: 5 })
|
expect(handlers.simple).toHaveBeenCalledWith({ message: 'Hello', count: 5 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle subcommands correctly', async () => {
|
it('should handle subcommands correctly', async () => {
|
||||||
const listHandler = vi.fn()
|
const { handlers, notFoundHandler, handler } = createTestSetup()
|
||||||
const createHandler = vi.fn()
|
|
||||||
const notFoundHandler = vi.fn()
|
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: vi.fn(),
|
|
||||||
complex: {
|
|
||||||
list: listHandler,
|
|
||||||
create: createHandler,
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'complex',
|
name: 'complex',
|
||||||
@ -217,30 +196,13 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(createHandler).toHaveBeenCalledWith({ name: 'Test Item', enabled: true })
|
expect(handlers.complex.create).toHaveBeenCalledWith({ name: 'Test Item', enabled: true })
|
||||||
expect(listHandler).not.toHaveBeenCalled()
|
expect(handlers.complex.list).not.toHaveBeenCalled()
|
||||||
expect(notFoundHandler).not.toHaveBeenCalled()
|
expect(notFoundHandler).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call notFoundHandler for unknown commands', async () => {
|
it('should call notFoundHandler for unknown commands', async () => {
|
||||||
const notFoundHandler = vi.fn()
|
const { notFoundHandler, handler } = createTestSetup()
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: vi.fn(),
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'unknown',
|
name: 'unknown',
|
||||||
@ -253,24 +215,7 @@ describe('createCommandHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should call notFoundHandler for unknown subcommands', async () => {
|
it('should call notFoundHandler for unknown subcommands', async () => {
|
||||||
const notFoundHandler = vi.fn()
|
const { notFoundHandler, handler } = createTestSetup()
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: vi.fn(),
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'complex',
|
name: 'complex',
|
||||||
@ -289,24 +234,7 @@ describe('createCommandHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle missing interaction data', async () => {
|
it('should handle missing interaction data', async () => {
|
||||||
const notFoundHandler = vi.fn()
|
const { notFoundHandler, handler } = createTestSetup()
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: vi.fn(),
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
await handler(null as any)
|
await handler(null as any)
|
||||||
expect(notFoundHandler).toHaveBeenCalledWith({})
|
expect(notFoundHandler).toHaveBeenCalledWith({})
|
||||||
@ -316,26 +244,7 @@ describe('createCommandHandler', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle subcommand groups recursively', async () => {
|
it('should handle subcommand groups recursively', async () => {
|
||||||
const banHandler = vi.fn()
|
const { handlers, notFoundHandler, handler } = createTestSetup()
|
||||||
const kickHandler = vi.fn()
|
|
||||||
const notFoundHandler = vi.fn()
|
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: vi.fn(),
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: banHandler,
|
|
||||||
kick: kickHandler,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler,
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'admin',
|
name: 'admin',
|
||||||
@ -359,30 +268,13 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(banHandler).toHaveBeenCalledWith({ user: '123456789', reason: 'Spam' })
|
expect(handlers.admin.user.ban).toHaveBeenCalledWith({ user: '123456789', reason: 'Spam' })
|
||||||
expect(kickHandler).not.toHaveBeenCalled()
|
expect(handlers.admin.user.kick).not.toHaveBeenCalled()
|
||||||
expect(notFoundHandler).not.toHaveBeenCalled()
|
expect(notFoundHandler).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle commands without options', async () => {
|
it('should handle commands without options', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const { handlers, handler } = createTestSetup()
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: simpleHandler,
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler: vi.fn(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'simple',
|
name: 'simple',
|
||||||
@ -391,28 +283,11 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(simpleHandler).toHaveBeenCalledWith({})
|
expect(handlers.simple).toHaveBeenCalledWith({})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore subcommand options when parsing top-level args', async () => {
|
it('should ignore subcommand options when parsing top-level args', async () => {
|
||||||
const simpleHandler = vi.fn()
|
const { handlers, handler } = createTestSetup()
|
||||||
|
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
|
||||||
handler: {
|
|
||||||
simple: simpleHandler,
|
|
||||||
complex: {
|
|
||||||
list: vi.fn(),
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
admin: {
|
|
||||||
user: {
|
|
||||||
ban: vi.fn(),
|
|
||||||
kick: vi.fn(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notFoundHandler: vi.fn(),
|
|
||||||
})
|
|
||||||
|
|
||||||
const interactionData: InteractionData = {
|
const interactionData: InteractionData = {
|
||||||
name: 'simple',
|
name: 'simple',
|
||||||
@ -425,7 +300,7 @@ describe('createCommandHandler', () => {
|
|||||||
|
|
||||||
await handler(interactionData)
|
await handler(interactionData)
|
||||||
|
|
||||||
expect(simpleHandler).toHaveBeenCalledWith({ message: 'Hello' })
|
expect(handlers.simple).toHaveBeenCalledWith({ message: 'Hello' })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -482,6 +357,7 @@ describe('ExtractCommands type utility', () => {
|
|||||||
it('should infer types correctly without manual typing', () => {
|
it('should infer types correctly without manual typing', () => {
|
||||||
// This test verifies that args are inferred correctly without manual type annotations
|
// This test verifies that args are inferred correctly without manual type annotations
|
||||||
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
const handler = createCommandHandler<typeof TEST_COMMANDS>({
|
||||||
|
commands: TEST_COMMANDS,
|
||||||
notFoundHandler: async () => {},
|
notFoundHandler: async () => {},
|
||||||
handler: {
|
handler: {
|
||||||
simple: async (args) => {
|
simple: async (args) => {
|
||||||
@ -551,7 +427,9 @@ describe('ExtractCommands type utility', () => {
|
|||||||
expect(handlers).toBeDefined()
|
expect(handlers).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle different option types', () => {
|
it('should handle different option types', async () => {
|
||||||
|
const { type } = await import('arktype')
|
||||||
|
|
||||||
const TYPE_TEST_COMMANDS = [
|
const TYPE_TEST_COMMANDS = [
|
||||||
{
|
{
|
||||||
name: 'types',
|
name: 'types',
|
||||||
@ -573,31 +451,54 @@ describe('ExtractCommands type utility', () => {
|
|||||||
|
|
||||||
type TypeHandlers = ExtractCommands<typeof TYPE_TEST_COMMANDS>
|
type TypeHandlers = ExtractCommands<typeof TYPE_TEST_COMMANDS>
|
||||||
|
|
||||||
|
// Define arktype validators for each expected type
|
||||||
|
const argsValidator = type({
|
||||||
|
str: 'string',
|
||||||
|
int: 'number.integer',
|
||||||
|
bool: 'boolean',
|
||||||
|
user: 'string', // User IDs are strings
|
||||||
|
channel: 'string', // Channel IDs are strings
|
||||||
|
role: 'string', // Role IDs are strings
|
||||||
|
num: 'number',
|
||||||
|
mention: 'string', // Mentionable IDs are strings
|
||||||
|
attach: 'string', // Attachment IDs are strings
|
||||||
|
})
|
||||||
|
|
||||||
const handlers: TypeHandlers = {
|
const handlers: TypeHandlers = {
|
||||||
types: async (args) => {
|
types: async (args) => {
|
||||||
// Test that all types are correctly mapped
|
// Validate the args using arktype
|
||||||
const str: string = args.str
|
const result = argsValidator(args)
|
||||||
const int: number = args.int
|
|
||||||
const bool: boolean = args.bool
|
|
||||||
const user: string = args.user
|
|
||||||
const channel: string = args.channel
|
|
||||||
const role: string = args.role
|
|
||||||
const num: number = args.num
|
|
||||||
const mention: string = args.mention
|
|
||||||
const attach: string = args.attach
|
|
||||||
|
|
||||||
expect(str).toBeDefined()
|
// Check if validation passed (result is the validated object, not wrapped)
|
||||||
expect(int).toBeDefined()
|
if (result instanceof type.errors) {
|
||||||
expect(bool).toBeDefined()
|
expect.fail(`Validation failed: ${result.summary}`)
|
||||||
expect(user).toBeDefined()
|
} else {
|
||||||
expect(channel).toBeDefined()
|
// Test that all types are correctly mapped
|
||||||
expect(role).toBeDefined()
|
expect(typeof result.str).toBe('string')
|
||||||
expect(num).toBeDefined()
|
expect(typeof result.int).toBe('number')
|
||||||
expect(mention).toBeDefined()
|
expect(Number.isInteger(result.int)).toBe(true)
|
||||||
expect(attach).toBeDefined()
|
expect(typeof result.bool).toBe('boolean')
|
||||||
|
expect(typeof result.user).toBe('string')
|
||||||
|
expect(typeof result.channel).toBe('string')
|
||||||
|
expect(typeof result.role).toBe('string')
|
||||||
|
expect(typeof result.num).toBe('number')
|
||||||
|
expect(typeof result.mention).toBe('string')
|
||||||
|
expect(typeof result.attach).toBe('string')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(handlers).toBeDefined()
|
// Test with actual values
|
||||||
|
await handlers.types({
|
||||||
|
str: 'hello',
|
||||||
|
int: 42,
|
||||||
|
bool: true,
|
||||||
|
user: '123456789',
|
||||||
|
channel: '987654321',
|
||||||
|
role: '555555555',
|
||||||
|
num: 3.14,
|
||||||
|
mention: '111111111',
|
||||||
|
attach: '999999999',
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { ApplicationCommandOptionTypes, DiscordApplicationCommandOption, type CreateApplicationCommand, type DiscordInteractionDataOption } from '@discordeno/types'
|
import { ApplicationCommandOptionTypes } from '@discordeno/types'
|
||||||
import type { InteractionData } from '..'
|
import type {CreateApplicationCommand,
|
||||||
|
DiscordInteractionDataOption,
|
||||||
|
DiscordInteractionData,
|
||||||
|
DiscordApplicationCommandOption
|
||||||
|
}from '@discordeno/types'
|
||||||
import type { SLASH_COMMANDS } from './slash_commands'
|
import type { SLASH_COMMANDS } from './slash_commands'
|
||||||
|
|
||||||
// Map option types to their TypeScript types
|
// Map option types to their TypeScript types
|
||||||
@ -21,12 +25,12 @@ type GetOption<Options, Name> = Options extends readonly DiscordApplicationComma
|
|||||||
// Extract the argument types from command options
|
// Extract the argument types from command options
|
||||||
export type ExtractArgs<Options extends readonly DiscordApplicationCommandOption[]> = {
|
export type ExtractArgs<Options extends readonly DiscordApplicationCommandOption[]> = {
|
||||||
[K in Options[number]['name']]: GetOption<Options, K> extends { type: infer T; required?: infer R }
|
[K in Options[number]['name']]: GetOption<Options, K> extends { type: infer T; required?: infer R }
|
||||||
? T extends keyof OptionTypeMap
|
? T extends keyof OptionTypeMap
|
||||||
? R extends true
|
? R extends true
|
||||||
? OptionTypeMap[T]
|
? OptionTypeMap[T]
|
||||||
: OptionTypeMap[T] | undefined
|
: OptionTypeMap[T] | undefined
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler function type that accepts typed arguments
|
// Handler function type that accepts typed arguments
|
||||||
@ -36,36 +40,36 @@ type HandlerFunction<Args = Record<string, never>> = (args: Args) => Promise<voi
|
|||||||
|
|
||||||
// Get subcommand or subcommand group by name
|
// Get subcommand or subcommand group by name
|
||||||
type GetSubcommandOrGroup<Options, Name> = Options extends readonly DiscordApplicationCommandOption[]
|
type GetSubcommandOrGroup<Options, Name> = Options extends readonly DiscordApplicationCommandOption[]
|
||||||
? Options[number] extends infer O
|
? Options[number] extends infer O
|
||||||
? O extends { name: Name; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
? O extends { name: Name; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
||||||
? O
|
? O
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
|
|
||||||
// Check if all options are subcommands or subcommand groups
|
// Check if all options are subcommands or subcommand groups
|
||||||
type HasOnlySubcommands<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup } ? true : false
|
type HasOnlySubcommands<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup } ? true : false
|
||||||
|
|
||||||
// Extract subcommand or subcommand group names from options
|
// Extract subcommand or subcommand group names from options
|
||||||
type SubcommandNames<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { name: infer N; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
type SubcommandNames<Options extends readonly DiscordApplicationCommandOption[]> = Options[number] extends { name: infer N; type: ApplicationCommandOptionTypes.SubCommand | ApplicationCommandOptionTypes.SubCommandGroup }
|
||||||
? N extends string
|
? N extends string
|
||||||
? N
|
? N
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
|
|
||||||
// Type to extract subcommand handlers (recursive for groups)
|
// Type to extract subcommand handlers (recursive for groups)
|
||||||
export type SubcommandHandlers<Options extends readonly DiscordApplicationCommandOption[]> = {
|
export type SubcommandHandlers<Options extends readonly DiscordApplicationCommandOption[]> = {
|
||||||
[K in SubcommandNames<Options>]: GetSubcommandOrGroup<Options, K> extends { type: infer T; options?: infer SubOpts }
|
[K in SubcommandNames<Options>]: GetSubcommandOrGroup<Options, K> extends { type: infer T; options?: infer SubOpts }
|
||||||
? T extends ApplicationCommandOptionTypes.SubCommandGroup
|
? T extends ApplicationCommandOptionTypes.SubCommandGroup
|
||||||
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
||||||
? SubcommandHandlers<SubOpts>
|
? SubcommandHandlers<SubOpts>
|
||||||
: never
|
: never
|
||||||
: T extends ApplicationCommandOptionTypes.SubCommand
|
: T extends ApplicationCommandOptionTypes.SubCommand
|
||||||
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
? SubOpts extends readonly DiscordApplicationCommandOption[]
|
||||||
? HandlerFunction<ExtractArgs<SubOpts>>
|
? HandlerFunction<ExtractArgs<SubOpts>>
|
||||||
: HandlerFunction<Record<string, never>>
|
: HandlerFunction<Record<string, never>>
|
||||||
: never
|
: never
|
||||||
: never
|
: never
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get command by name from array
|
// Get command by name from array
|
||||||
@ -74,12 +78,12 @@ type GetCommand<Commands extends readonly any[], Name> = Commands[number] extend
|
|||||||
// Main type to extract command handlers from slash commands
|
// Main type to extract command handlers from slash commands
|
||||||
export type ExtractCommands<T extends readonly CreateApplicationCommand[]> = {
|
export type ExtractCommands<T extends readonly CreateApplicationCommand[]> = {
|
||||||
[Name in T[number]['name']]: GetCommand<T, Name> extends { options?: infer Options }
|
[Name in T[number]['name']]: GetCommand<T, Name> extends { options?: infer Options }
|
||||||
? Options extends readonly DiscordApplicationCommandOption[]
|
? Options extends readonly DiscordApplicationCommandOption[]
|
||||||
? HasOnlySubcommands<Options> extends true
|
? HasOnlySubcommands<Options> extends true
|
||||||
? SubcommandHandlers<Options>
|
? SubcommandHandlers<Options>
|
||||||
: HandlerFunction<ExtractArgs<Options>>
|
: HandlerFunction<ExtractArgs<Options>>
|
||||||
: HandlerFunction<Record<string, never>>
|
: HandlerFunction<Record<string, never>>
|
||||||
: HandlerFunction<Record<string, never>>
|
: HandlerFunction<Record<string, never>>
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type representing the possible output of ExtractCommands
|
// Type representing the possible output of ExtractCommands
|
||||||
@ -149,11 +153,11 @@ export function createCommandHandler<T extends readonly CreateApplicationCommand
|
|||||||
handler,
|
handler,
|
||||||
notFoundHandler,
|
notFoundHandler,
|
||||||
}: {
|
}: {
|
||||||
commands: T,
|
commands: T,
|
||||||
handler: ExtractCommands<T>
|
handler: ExtractCommands<T>
|
||||||
notFoundHandler: HandlerFunction<{path?: string}>
|
notFoundHandler: HandlerFunction<{path?: string}>
|
||||||
}) {
|
}) {
|
||||||
return async (data: InteractionData): Promise<void> => {
|
return async (data: DiscordInteractionData): Promise<void> => {
|
||||||
if (!data || !data.name) {
|
if (!data || !data.name) {
|
||||||
await notFoundHandler({})
|
await notFoundHandler({})
|
||||||
return
|
return
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { ApplicationCommandOptionTypes, ApplicationCommandTypes, type CreateAppl
|
|||||||
|
|
||||||
export const SLASH_COMMANDS = [
|
export const SLASH_COMMANDS = [
|
||||||
{
|
{
|
||||||
name: `guild`,
|
name: 'guild',
|
||||||
description: 'guild commands',
|
description: 'guild commands',
|
||||||
type: ApplicationCommandTypes.ChatInput,
|
type: ApplicationCommandTypes.ChatInput,
|
||||||
options: [
|
options: [
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Intents, type InteractionTypes } from '@discordeno/types'
|
import { Intents, type InteractionTypes } from '@discordeno/types'
|
||||||
|
import type { DiscordInteractionData} from '@discordeno/types'
|
||||||
import type { Bot, CompleteDesiredProperties, DesiredPropertiesBehavior } from 'discordeno'
|
import type { Bot, CompleteDesiredProperties, DesiredPropertiesBehavior } from 'discordeno'
|
||||||
export const intents = [
|
export const intents = [
|
||||||
Intents.GuildModeration,
|
Intents.GuildModeration,
|
||||||
@ -56,11 +57,8 @@ export interface InteractionRef {
|
|||||||
acknowledged?: boolean
|
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
|
// Type for the complete interaction handling payload
|
||||||
export interface InteractionCreatePayload {
|
export interface InteractionCreatePayload {
|
||||||
ref: InteractionRef
|
ref: InteractionRef
|
||||||
data: InteractionData
|
data: DiscordInteractionData
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { c } from '#/di'
|
import { c } from '#/di'
|
||||||
|
|
||||||
export class EventMux {
|
export class EventMux {
|
||||||
constructor() {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,10 +6,10 @@ export class TabWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(row: string[]) {
|
add(row: string[]) {
|
||||||
if (this.columns.length == 0) {
|
if (this.columns.length === 0) {
|
||||||
this.columns = new Array(row.length).fill(0).map(() => [])
|
this.columns = new Array(row.length).fill(0).map(() => [])
|
||||||
}
|
}
|
||||||
if (row.length != this.columns.length) {
|
if (row.length !== this.columns.length) {
|
||||||
throw new Error(`Row length ${row.length} does not match columns length ${this.columns.length}`)
|
throw new Error(`Row length ${row.length} does not match columns length ${this.columns.length}`)
|
||||||
}
|
}
|
||||||
for (let i = 0; i < row.length; i++) {
|
for (let i = 0; i < row.length; i++) {
|
||||||
@ -19,7 +19,7 @@ export class TabWriter {
|
|||||||
|
|
||||||
build() {
|
build() {
|
||||||
let out = ''
|
let out = ''
|
||||||
if (this.columns.length == 0) {
|
if (this.columns.length === 0) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const columnWidths = this.columns.map((col) => col.reduce((a, b) => Math.max(a, b.length + this.spacing), 0))
|
const columnWidths = this.columns.map((col) => col.reduce((a, b) => Math.max(a, b.length + this.spacing), 0))
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export class WApi {
|
|||||||
: // When a stale state has a determined value to expire, we can use it.
|
: // When a stale state has a determined value to expire, we can use it.
|
||||||
// Or if the cached value cannot enter in stale state.
|
// Or if the cached value cannot enter in stale state.
|
||||||
(value.state === 'stale' && value.ttl) || (value.state === 'cached' && !canStale(value))
|
(value.state === 'stale' && value.ttl) || (value.state === 'cached' && !canStale(value))
|
||||||
? value.createdAt + value.ttl!
|
? value.createdAt + (value.ttl || 0)
|
||||||
: // otherwise, we can't determine when it should expire, so we keep
|
: // otherwise, we can't determine when it should expire, so we keep
|
||||||
// it indefinitely.
|
// it indefinitely.
|
||||||
undefined
|
undefined
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export class SuperJsonPayloadConverter implements PayloadConverterWithEncoding {
|
|||||||
|
|
||||||
public toPayload(value: unknown): Payload | undefined {
|
public toPayload(value: unknown): Payload | undefined {
|
||||||
if (value === undefined) return undefined
|
if (value === undefined) return undefined
|
||||||
let ejson
|
let ejson: any
|
||||||
try {
|
try {
|
||||||
ejson = superjson.stringify(value)
|
ejson = superjson.stringify(value)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -26,13 +26,13 @@ const workflowHandleApplicationCommand = async (payload: InteractionCreatePayloa
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (!data || !data.name) {
|
if (!data || !data.name) {
|
||||||
await notFoundHandler(`Invalid command data`)
|
await notFoundHandler('Invalid command data')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const commandHandler = createCommandHandler({
|
const commandHandler = createCommandHandler({
|
||||||
commands: SLASH_COMMANDS,
|
commands: SLASH_COMMANDS,
|
||||||
notFoundHandler: async () => {
|
notFoundHandler: async () => {
|
||||||
await notFoundHandler(`command not found`)
|
await notFoundHandler('command not found')
|
||||||
},
|
},
|
||||||
handler: {
|
handler: {
|
||||||
player: {
|
player: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user