Merge pull request #56 from DjDeveloperr/slash
feat: Slash Commands & Interactions [WIP?]
This commit is contained in:
commit
55ddc64187
23 changed files with 1282 additions and 105 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -113,3 +113,5 @@ src/test/config.ts
|
|||
|
||||
# macOS is shit xD
|
||||
**/.DS_Store
|
||||
|
||||
src/test/music.mp3
|
43
mod.ts
43
mod.ts
|
@ -2,7 +2,9 @@ export { GatewayIntents } from './src/types/gateway.ts'
|
|||
export { default as EventEmitter } from 'https://deno.land/std@0.74.0/node/events.ts'
|
||||
export { Base } from './src/structures/base.ts'
|
||||
export { Gateway } from './src/gateway/index.ts'
|
||||
export type { ClientEvents } from './src/gateway/handlers/index.ts'
|
||||
export * from './src/models/client.ts'
|
||||
export * from './src/models/slashClient.ts'
|
||||
export { RESTManager } from './src/models/rest.ts'
|
||||
export * from './src/models/cacheAdapter.ts'
|
||||
export {
|
||||
|
@ -18,6 +20,7 @@ export {
|
|||
ExtensionCommands,
|
||||
ExtensionsManager
|
||||
} from './src/models/extensions.ts'
|
||||
export { SlashModule } from './src/models/slashModule.ts'
|
||||
export { CommandClient, command } from './src/models/commandClient.ts'
|
||||
export type { CommandClientOptions } from './src/models/commandClient.ts'
|
||||
export { BaseManager } from './src/managers/base.ts'
|
||||
|
@ -28,6 +31,8 @@ export { GatewayCache } from './src/managers/gatewayCache.ts'
|
|||
export { GuildChannelsManager } from './src/managers/guildChannels.ts'
|
||||
export type { GuildChannel } from './src/managers/guildChannels.ts'
|
||||
export { GuildManager } from './src/managers/guilds.ts'
|
||||
export * from './src/structures/slash.ts'
|
||||
export * from './src/types/slash.ts'
|
||||
export { GuildEmojisManager } from './src/managers/guildEmojis.ts'
|
||||
export { MembersManager } from './src/managers/members.ts'
|
||||
export { MessageReactionsManager } from './src/managers/messageReactions.ts'
|
||||
|
@ -80,3 +85,41 @@ export type {
|
|||
StatusType
|
||||
} from './src/types/presence.ts'
|
||||
export { ChannelTypes } from './src/types/channel.ts'
|
||||
export type { ApplicationPayload } from './src/types/application.ts'
|
||||
export type { ImageFormats, ImageSize } from './src/types/cdn.ts'
|
||||
export type {
|
||||
ChannelMention,
|
||||
ChannelPayload,
|
||||
FollowedChannel,
|
||||
GuildNewsChannelPayload,
|
||||
GuildChannelCategoryPayload,
|
||||
GuildChannelPayload,
|
||||
GuildTextChannelPayload,
|
||||
GuildVoiceChannelPayload,
|
||||
GroupDMChannelPayload
|
||||
} from './src/types/channel.ts'
|
||||
export type { EmojiPayload } from './src/types/emoji.ts'
|
||||
export type {
|
||||
GuildBanPayload,
|
||||
GuildFeatures,
|
||||
GuildIntegrationPayload,
|
||||
GuildPayload
|
||||
} from './src/types/guild.ts'
|
||||
export type { InvitePayload, PartialInvitePayload } from './src/types/invite.ts'
|
||||
export { PermissionFlags } from './src/types/permissionFlags.ts'
|
||||
export type {
|
||||
ActivityAssets,
|
||||
ActivityEmoji,
|
||||
ActivityFlags,
|
||||
ActivityParty,
|
||||
ActivityPayload,
|
||||
ActivitySecrets,
|
||||
ActivityTimestamps,
|
||||
ActivityType
|
||||
} from './src/types/presence.ts'
|
||||
export type { RolePayload } from './src/types/role.ts'
|
||||
export type { TemplatePayload } from './src/types/template.ts'
|
||||
export type { UserPayload } from './src/types/user.ts'
|
||||
export { UserFlags } from './src/types/userFlags.ts'
|
||||
export type { VoiceStatePayload } from './src/types/voice.ts'
|
||||
export type { WebhookPayload } from './src/types/webhook.ts'
|
||||
|
|
|
@ -27,7 +27,7 @@ import { webhooksUpdate } from './webhooksUpdate.ts'
|
|||
import { messageDeleteBulk } from './messageDeleteBulk.ts'
|
||||
import { userUpdate } from './userUpdate.ts'
|
||||
import { typingStart } from './typingStart.ts'
|
||||
import { GuildTextChannel } from '../../structures/textChannel.ts'
|
||||
import { GuildTextChannel, TextChannel } from '../../structures/textChannel.ts'
|
||||
import { Guild } from '../../structures/guild.ts'
|
||||
import { User } from '../../structures/user.ts'
|
||||
import { Emoji } from '../../structures/emoji.ts'
|
||||
|
@ -53,6 +53,8 @@ import {
|
|||
EveryChannelTypes,
|
||||
EveryTextChannelTypes
|
||||
} from '../../utils/getChannelByType.ts'
|
||||
import { interactionCreate } from './interactionCreate.ts'
|
||||
import { Interaction } from '../../structures/slash.ts'
|
||||
|
||||
export const gatewayHandlers: {
|
||||
[eventCode in GatewayEvents]: GatewayEventHandler | undefined
|
||||
|
@ -93,7 +95,8 @@ export const gatewayHandlers: {
|
|||
USER_UPDATE: userUpdate,
|
||||
VOICE_STATE_UPDATE: voiceStateUpdate,
|
||||
VOICE_SERVER_UPDATE: voiceServerUpdate,
|
||||
WEBHOOKS_UPDATE: webhooksUpdate
|
||||
WEBHOOKS_UPDATE: webhooksUpdate,
|
||||
INTERACTION_CREATE: interactionCreate
|
||||
}
|
||||
|
||||
export interface EventTypes {
|
||||
|
@ -107,56 +110,227 @@ export interface VoiceServerUpdateData {
|
|||
}
|
||||
|
||||
export interface ClientEvents extends EventTypes {
|
||||
/** When Client has successfully connected to Discord */
|
||||
ready: () => void
|
||||
/** When a successful reconnect has been made */
|
||||
reconnect: () => void
|
||||
/** When a successful session resume has been done */
|
||||
resumed: () => void
|
||||
/**
|
||||
* When a new Channel is created
|
||||
* @param channel New Channel object
|
||||
*/
|
||||
channelCreate: (channel: EveryChannelTypes) => void
|
||||
/**
|
||||
* When a Channel was deleted
|
||||
* @param channel Channel object which was deleted
|
||||
*/
|
||||
channelDelete: (channel: EveryChannelTypes) => void
|
||||
/**
|
||||
* Channel's Pinned Messages were updated
|
||||
* @param before Channel object before update
|
||||
* @param after Channel object after update
|
||||
*/
|
||||
channelPinsUpdate: (
|
||||
before: EveryTextChannelTypes,
|
||||
after: EveryTextChannelTypes
|
||||
) => void
|
||||
/**
|
||||
* A Channel was updated
|
||||
* @param before Channel object before update
|
||||
* @param after Channel object after update
|
||||
*/
|
||||
channelUpdate: (before: EveryChannelTypes, after: EveryChannelTypes) => void
|
||||
/**
|
||||
* A User was banned from a Guild
|
||||
* @param guild The Guild from which User was banned
|
||||
* @param user The User who was banned
|
||||
*/
|
||||
guildBanAdd: (guild: Guild, user: User) => void
|
||||
/**
|
||||
* A ban from a User in Guild was elevated
|
||||
* @param guild Guild from which ban was removed
|
||||
* @param user User of which ban was elevated
|
||||
*/
|
||||
guildBanRemove: (guild: Guild, user: User) => void
|
||||
/**
|
||||
* Client has joined a new Guild.
|
||||
* @param guild The new Guild object
|
||||
*/
|
||||
guildCreate: (guild: Guild) => void
|
||||
/**
|
||||
* A Guild in which Client was either deleted, or bot was kicked
|
||||
* @param guild The Guild object
|
||||
*/
|
||||
guildDelete: (guild: Guild) => void
|
||||
/**
|
||||
* A new Emoji was added to Guild
|
||||
* @param guild Guild in which Emoji was added
|
||||
* @param emoji The Emoji which was added
|
||||
*/
|
||||
guildEmojiAdd: (guild: Guild, emoji: Emoji) => void
|
||||
/**
|
||||
* An Emoji was deleted from Guild
|
||||
* @param guild Guild from which Emoji was deleted
|
||||
* @param emoji Emoji which was deleted
|
||||
*/
|
||||
guildEmojiDelete: (guild: Guild, emoji: Emoji) => void
|
||||
/**
|
||||
* An Emoji in a Guild was updated
|
||||
* @param guild Guild in which Emoji was updated
|
||||
* @param before Emoji object before update
|
||||
* @param after Emoji object after update
|
||||
*/
|
||||
guildEmojiUpdate: (guild: Guild, before: Emoji, after: Emoji) => void
|
||||
/**
|
||||
* Guild's Integrations were updated
|
||||
* @param guild The Guild object
|
||||
*/
|
||||
guildIntegrationsUpdate: (guild: Guild) => void
|
||||
/**
|
||||
* A new Member has joined a Guild
|
||||
* @param member The Member object
|
||||
*/
|
||||
guildMemberAdd: (member: Member) => void
|
||||
/**
|
||||
* A Guild Member has either left or was kicked from Guild
|
||||
* @param member The Member object
|
||||
*/
|
||||
guildMemberRemove: (member: Member) => void
|
||||
/**
|
||||
* A Guild Member was updated. Nickname changed, role assigned, etc.
|
||||
* @param before Member object before update
|
||||
* @param after Meber object after update
|
||||
*/
|
||||
guildMemberUpdate: (before: Member, after: Member) => void
|
||||
/**
|
||||
* A new Role was created in Guild
|
||||
* @param role The new Role object
|
||||
*/
|
||||
guildRoleCreate: (role: Role) => void
|
||||
/**
|
||||
* A Role was deleted from the Guild
|
||||
* @param role The Role object
|
||||
*/
|
||||
guildRoleDelete: (role: Role) => void
|
||||
/**
|
||||
* A Role was updated in a Guild
|
||||
* @param before Role object before update
|
||||
* @param after Role object after updated
|
||||
*/
|
||||
guildRoleUpdate: (before: Role, after: Role) => void
|
||||
/**
|
||||
* A Guild has been updated. For example name, icon, etc.
|
||||
* @param before Guild object before update
|
||||
* @param after Guild object after update
|
||||
*/
|
||||
guildUpdate: (before: Guild, after: Guild) => void
|
||||
/**
|
||||
* A new Message was created (sent)
|
||||
* @param message The new Message object
|
||||
*/
|
||||
messageCreate: (message: Message) => void
|
||||
/**
|
||||
* A Message was deleted.
|
||||
* @param message The Message object
|
||||
*/
|
||||
messageDelete: (message: Message) => void
|
||||
/**
|
||||
* Messages were bulk deleted in a Guild Text Channel
|
||||
* @param channel Channel in which Messages were deleted
|
||||
* @param messages Collection of Messages deleted
|
||||
* @param uncached Set of Messages deleted's IDs which were not cached
|
||||
*/
|
||||
messageDeleteBulk: (
|
||||
channel: GuildTextChannel,
|
||||
messages: Collection<string, Message>,
|
||||
uncached: Set<string>
|
||||
) => void
|
||||
/**
|
||||
* A Message was updated. For example content, embed, etc.
|
||||
* @param before Message object before update
|
||||
* @param after Message object after update
|
||||
*/
|
||||
messageUpdate: (before: Message, after: Message) => void
|
||||
/**
|
||||
* Reaction was added to a Message
|
||||
* @param reaction Reaction object
|
||||
* @param user User who added the reaction
|
||||
*/
|
||||
messageReactionAdd: (reaction: MessageReaction, user: User) => void
|
||||
/**
|
||||
* Reaction was removed fro a Message
|
||||
* @param reaction Reaction object
|
||||
* @param user User to who removed the reaction
|
||||
*/
|
||||
messageReactionRemove: (reaction: MessageReaction, user: User) => void
|
||||
/**
|
||||
* All reactions were removed from a Message
|
||||
* @param message Message from which reactions were removed
|
||||
*/
|
||||
messageReactionRemoveAll: (message: Message) => void
|
||||
/**
|
||||
* All reactions of a single Emoji were removed
|
||||
* @param message The Message object
|
||||
* @param emoji The Emoji object
|
||||
*/
|
||||
messageReactionRemoveEmoji: (message: Message, emoji: Emoji) => void
|
||||
/**
|
||||
* A User has started typing in a Text Channel
|
||||
*/
|
||||
typingStart: (
|
||||
user: User,
|
||||
channel: EveryChannelTypes,
|
||||
channel: TextChannel,
|
||||
at: Date,
|
||||
guildData?: TypingStartGuildData
|
||||
) => void
|
||||
/**
|
||||
* A new Invite was created
|
||||
* @param invite New Invite object
|
||||
*/
|
||||
inviteCreate: (invite: Invite) => void
|
||||
/**
|
||||
* An Invite was deleted
|
||||
* @param invite Invite object
|
||||
*/
|
||||
inviteDelete: (invite: Invite) => void
|
||||
/**
|
||||
* A User was updated. For example username, avatar, etc.
|
||||
* @param before The User object before update
|
||||
* @param after The User object after update
|
||||
*/
|
||||
userUpdate: (before: User, after: User) => void
|
||||
/**
|
||||
* Client has received credentials for establishing connection to Voice Server
|
||||
*/
|
||||
voiceServerUpdate: (data: VoiceServerUpdateData) => void
|
||||
/**
|
||||
* A User has joined a Voice Channel
|
||||
*/
|
||||
voiceStateAdd: (state: VoiceState) => void
|
||||
/**
|
||||
* A User has left a Voice Channel
|
||||
*/
|
||||
voiceStateRemove: (state: VoiceState) => void
|
||||
/**
|
||||
* Voice State of a User has been updated
|
||||
* @param before Voice State object before update
|
||||
* @param after Voice State object after update
|
||||
*/
|
||||
voiceStateUpdate: (state: VoiceState, after: VoiceState) => void
|
||||
/**
|
||||
* A User's presence has been updated
|
||||
* @param presence New Presence
|
||||
*/
|
||||
presenceUpdate: (presence: Presence) => void
|
||||
/**
|
||||
* Webhooks of a Channel in a Guild has been updated
|
||||
* @param guild Guild in which Webhooks were updated
|
||||
* @param channel Channel of which Webhooks were updated
|
||||
*/
|
||||
webhooksUpdate: (guild: Guild, channel: GuildTextChannel) => void
|
||||
/**
|
||||
* A Slash Command was triggered
|
||||
*/
|
||||
interactionCreate: (interaction: Interaction) => void
|
||||
}
|
||||
|
|
29
src/gateway/handlers/interactionCreate.ts
Normal file
29
src/gateway/handlers/interactionCreate.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { Member } from '../../structures/member.ts'
|
||||
import { Interaction } from '../../structures/slash.ts'
|
||||
import { GuildTextChannel } from '../../structures/textChannel.ts'
|
||||
import { InteractionPayload } from '../../types/slash.ts'
|
||||
import { Gateway, GatewayEventHandler } from '../index.ts'
|
||||
|
||||
export const interactionCreate: GatewayEventHandler = async (
|
||||
gateway: Gateway,
|
||||
d: InteractionPayload
|
||||
) => {
|
||||
const guild = await gateway.client.guilds.get(d.guild_id)
|
||||
if (guild === undefined) return
|
||||
|
||||
await guild.members.set(d.member.user.id, d.member)
|
||||
const member = ((await guild.members.get(
|
||||
d.member.user.id
|
||||
)) as unknown) as Member
|
||||
|
||||
const channel =
|
||||
(await gateway.client.channels.get<GuildTextChannel>(d.channel_id)) ??
|
||||
(await gateway.client.channels.fetch<GuildTextChannel>(d.channel_id))
|
||||
|
||||
const interaction = new Interaction(gateway.client, d, {
|
||||
member,
|
||||
guild,
|
||||
channel
|
||||
})
|
||||
gateway.client.emit('interactionCreate', interaction)
|
||||
}
|
|
@ -252,9 +252,9 @@ class Gateway {
|
|||
const payload: IdentityPayload = {
|
||||
token: this.token,
|
||||
properties: {
|
||||
$os: Deno.build.os,
|
||||
$browser: 'harmony',
|
||||
$device: 'harmony'
|
||||
$os: this.client.clientProperties.os ?? Deno.build.os,
|
||||
$browser: this.client.clientProperties.browser ?? 'harmony',
|
||||
$device: this.client.clientProperties.device ?? 'harmony'
|
||||
},
|
||||
compress: true,
|
||||
shard: [0, 1], // TODO: Make sharding possible
|
||||
|
|
|
@ -12,6 +12,16 @@ import { EmojisManager } from '../managers/emojis.ts'
|
|||
import { ActivityGame, ClientActivity } from '../types/presence.ts'
|
||||
import { ClientEvents } from '../gateway/handlers/index.ts'
|
||||
import { Extension } from './extensions.ts'
|
||||
import { SlashClient } from './slashClient.ts'
|
||||
import { Interaction } from '../structures/slash.ts'
|
||||
import { SlashModule } from './slashModule.ts'
|
||||
|
||||
/** OS related properties sent with Gateway Identify */
|
||||
export interface ClientProperties {
|
||||
os?: 'darwin' | 'windows' | 'linux' | 'custom_os' | string
|
||||
browser?: 'harmony' | string
|
||||
device?: 'harmony' | string
|
||||
}
|
||||
|
||||
/** Some Client Options to modify behaviour */
|
||||
export interface ClientOptions {
|
||||
|
@ -33,6 +43,10 @@ export interface ClientOptions {
|
|||
reactionCacheLifetime?: number
|
||||
/** Whether to fetch Uncached Message of Reaction or not? */
|
||||
fetchUncachedReactions?: boolean
|
||||
/** Client Properties */
|
||||
clientProperties?: ClientProperties
|
||||
/** Enable/Disable Slash Commands Integration (enabled by default) */
|
||||
enableSlash?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,6 +75,10 @@ export class Client extends EventEmitter {
|
|||
reactionCacheLifetime: number = 3600000
|
||||
/** Whether to fetch Uncached Message of Reaction or not? */
|
||||
fetchUncachedReactions: boolean = false
|
||||
/** Client Properties */
|
||||
clientProperties: ClientProperties
|
||||
/** Slash-Commands Management client */
|
||||
slash: SlashClient
|
||||
|
||||
users: UsersManager = new UsersManager(this)
|
||||
guilds: GuildManager = new GuildManager(this)
|
||||
|
@ -72,6 +90,13 @@ export class Client extends EventEmitter {
|
|||
/** Client's presence. Startup one if set before connecting */
|
||||
presence: ClientPresence = new ClientPresence()
|
||||
_decoratedEvents?: { [name: string]: (...args: any[]) => any }
|
||||
_decoratedSlash?: Array<{
|
||||
name: string
|
||||
guild?: string
|
||||
handler: (interaction: Interaction) => any
|
||||
}>
|
||||
|
||||
_decoratedSlashModules?: SlashModule[]
|
||||
|
||||
private readonly _untypedOn = this.on
|
||||
|
||||
|
@ -113,6 +138,19 @@ export class Client extends EventEmitter {
|
|||
})
|
||||
this._decoratedEvents = undefined
|
||||
}
|
||||
|
||||
this.clientProperties =
|
||||
options.clientProperties === undefined
|
||||
? {
|
||||
os: Deno.build.os,
|
||||
browser: 'harmony',
|
||||
device: 'harmony'
|
||||
}
|
||||
: options.clientProperties
|
||||
|
||||
this.slash = new SlashClient(this, {
|
||||
enabled: options.enableSlash
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -171,7 +209,34 @@ export function event(name?: string) {
|
|||
const listener = ((client as unknown) as {
|
||||
[name: string]: (...args: any[]) => any
|
||||
})[prop]
|
||||
if (typeof listener !== 'function')
|
||||
throw new Error('@event decorator requires a function')
|
||||
if (client._decoratedEvents === undefined) client._decoratedEvents = {}
|
||||
client._decoratedEvents[name === undefined ? prop : name] = listener
|
||||
}
|
||||
}
|
||||
|
||||
export function slash(name?: string, guild?: string) {
|
||||
return function (client: Client | SlashModule, prop: string) {
|
||||
if (client._decoratedSlash === undefined) client._decoratedSlash = []
|
||||
const item = (client as { [name: string]: any })[prop]
|
||||
if (typeof item !== 'function') {
|
||||
client._decoratedSlash.push(item)
|
||||
} else
|
||||
client._decoratedSlash.push({
|
||||
name: name ?? prop,
|
||||
guild,
|
||||
handler: item
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function slashModule() {
|
||||
return function (client: Client, prop: string) {
|
||||
if (client._decoratedSlashModules === undefined)
|
||||
client._decoratedSlashModules = []
|
||||
|
||||
const mod = ((client as unknown) as { [key: string]: any })[prop]
|
||||
client._decoratedSlashModules.push(mod)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,18 +383,26 @@ export class CommandClient extends Client implements CommandClientOptions {
|
|||
|
||||
export function command(options?: CommandOptions) {
|
||||
return function (target: CommandClient | Extension, name: string) {
|
||||
if (target._decoratedCommands === undefined) target._decoratedCommands = {}
|
||||
|
||||
const prop = ((target as unknown) as {
|
||||
[name: string]: (ctx: CommandContext) => any
|
||||
})[name]
|
||||
|
||||
if (prop instanceof Command) {
|
||||
target._decoratedCommands[prop.name] = prop
|
||||
return
|
||||
}
|
||||
|
||||
const command = new Command()
|
||||
|
||||
command.name = name
|
||||
command.execute = ((target as unknown) as {
|
||||
[name: string]: (ctx: CommandContext) => any
|
||||
})[name]
|
||||
command.execute = prop
|
||||
|
||||
if (options !== undefined) Object.assign(command, options)
|
||||
|
||||
if (target instanceof Extension) command.extension = target
|
||||
|
||||
if (target._decoratedCommands === undefined) target._decoratedCommands = {}
|
||||
target._decoratedCommands[command.name] = command
|
||||
}
|
||||
}
|
||||
|
|
|
@ -277,11 +277,11 @@ export class RESTManager {
|
|||
message: body?.message,
|
||||
errors: Object.fromEntries(
|
||||
Object.entries(
|
||||
body?.errors as {
|
||||
(body?.errors as {
|
||||
[name: string]: {
|
||||
_errors: Array<{ code: string; message: string }>
|
||||
}
|
||||
}
|
||||
}) ?? {}
|
||||
).map((entry) => {
|
||||
return [entry[0], entry[1]._errors]
|
||||
})
|
||||
|
|
222
src/models/slashClient.ts
Normal file
222
src/models/slashClient.ts
Normal file
|
@ -0,0 +1,222 @@
|
|||
import { Guild } from '../structures/guild.ts'
|
||||
import { Interaction } from '../structures/slash.ts'
|
||||
import {
|
||||
APPLICATION_COMMAND,
|
||||
APPLICATION_COMMANDS,
|
||||
APPLICATION_GUILD_COMMAND,
|
||||
APPLICATION_GUILD_COMMANDS
|
||||
} from '../types/endpoint.ts'
|
||||
import {
|
||||
InteractionType,
|
||||
SlashCommandOption,
|
||||
SlashCommandPartial,
|
||||
SlashCommandPayload
|
||||
} from '../types/slash.ts'
|
||||
import { Collection } from '../utils/collection.ts'
|
||||
import { Client } from './client.ts'
|
||||
|
||||
export interface SlashOptions {
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
export class SlashCommand {
|
||||
slash: SlashCommandsManager
|
||||
id: string
|
||||
applicationID: string
|
||||
name: string
|
||||
description: string
|
||||
options: SlashCommandOption[]
|
||||
_guild?: string
|
||||
|
||||
constructor(manager: SlashCommandsManager, data: SlashCommandPayload) {
|
||||
this.slash = manager
|
||||
this.id = data.id
|
||||
this.applicationID = data.application_id
|
||||
this.name = data.name
|
||||
this.description = data.description
|
||||
this.options = data.options
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
await this.slash.delete(this.id, this._guild)
|
||||
}
|
||||
|
||||
async edit(data: SlashCommandPartial): Promise<void> {
|
||||
await this.slash.edit(this.id, data, this._guild)
|
||||
}
|
||||
}
|
||||
|
||||
export class SlashCommandsManager {
|
||||
client: Client
|
||||
slash: SlashClient
|
||||
|
||||
constructor(client: Client) {
|
||||
this.client = client
|
||||
this.slash = client.slash
|
||||
}
|
||||
|
||||
/** Get all Global Slash Commands */
|
||||
async all(): Promise<Collection<string, SlashCommand>> {
|
||||
const col = new Collection<string, SlashCommand>()
|
||||
|
||||
const res = (await this.client.rest.get(
|
||||
APPLICATION_COMMANDS(this.client.user?.id as string)
|
||||
)) as SlashCommandPayload[]
|
||||
if (!Array.isArray(res)) return col
|
||||
|
||||
for (const raw of res) {
|
||||
const cmd = new SlashCommand(this, raw)
|
||||
col.set(raw.id, cmd)
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
/** Get a Guild's Slash Commands */
|
||||
async guild(
|
||||
guild: Guild | string
|
||||
): Promise<Collection<string, SlashCommand>> {
|
||||
const col = new Collection<string, SlashCommand>()
|
||||
|
||||
const res = (await this.client.rest.get(
|
||||
APPLICATION_GUILD_COMMANDS(
|
||||
this.client.user?.id as string,
|
||||
typeof guild === 'string' ? guild : guild.id
|
||||
)
|
||||
)) as SlashCommandPayload[]
|
||||
if (!Array.isArray(res)) return col
|
||||
|
||||
for (const raw of res) {
|
||||
const cmd = new SlashCommand(this, raw)
|
||||
cmd._guild = typeof guild === 'string' ? guild : guild.id
|
||||
col.set(raw.id, cmd)
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
/** Create a Slash Command (global or Guild) */
|
||||
async create(
|
||||
data: SlashCommandPartial,
|
||||
guild?: Guild | string
|
||||
): Promise<SlashCommand> {
|
||||
const payload = await this.client.rest.post(
|
||||
guild === undefined
|
||||
? APPLICATION_COMMANDS(this.client.user?.id as string)
|
||||
: APPLICATION_GUILD_COMMANDS(
|
||||
this.client.user?.id as string,
|
||||
typeof guild === 'string' ? guild : guild.id
|
||||
),
|
||||
data
|
||||
)
|
||||
|
||||
const cmd = new SlashCommand(this, payload)
|
||||
cmd._guild =
|
||||
typeof guild === 'string' || guild === undefined ? guild : guild.id
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
/** Edit a Slash Command (global or Guild) */
|
||||
async edit(
|
||||
id: string,
|
||||
data: SlashCommandPartial,
|
||||
guild?: Guild | string
|
||||
): Promise<SlashCommandsManager> {
|
||||
await this.client.rest.patch(
|
||||
guild === undefined
|
||||
? APPLICATION_COMMAND(this.client.user?.id as string, id)
|
||||
: APPLICATION_GUILD_COMMAND(
|
||||
this.client.user?.id as string,
|
||||
typeof guild === 'string' ? guild : guild.id,
|
||||
id
|
||||
),
|
||||
data
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
/** Delete a Slash Command (global or Guild) */
|
||||
async delete(
|
||||
id: string,
|
||||
guild?: Guild | string
|
||||
): Promise<SlashCommandsManager> {
|
||||
await this.client.rest.delete(
|
||||
guild === undefined
|
||||
? APPLICATION_COMMAND(this.client.user?.id as string, id)
|
||||
: APPLICATION_GUILD_COMMAND(
|
||||
this.client.user?.id as string,
|
||||
typeof guild === 'string' ? guild : guild.id,
|
||||
id
|
||||
)
|
||||
)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
export type SlashCommandHandlerCallback = (interaction: Interaction) => any
|
||||
export interface SlashCommandHandler {
|
||||
name: string
|
||||
guild?: string
|
||||
handler: SlashCommandHandlerCallback
|
||||
}
|
||||
|
||||
export class SlashClient {
|
||||
client: Client
|
||||
enabled: boolean = true
|
||||
commands: SlashCommandsManager
|
||||
handlers: SlashCommandHandler[] = []
|
||||
|
||||
constructor(client: Client, options?: SlashOptions) {
|
||||
this.client = client
|
||||
this.commands = new SlashCommandsManager(client)
|
||||
|
||||
if (options !== undefined) {
|
||||
this.enabled = options.enabled ?? true
|
||||
}
|
||||
|
||||
if (this.client._decoratedSlash !== undefined) {
|
||||
this.client._decoratedSlash.forEach((e) => {
|
||||
this.handlers.push(e)
|
||||
})
|
||||
}
|
||||
|
||||
this.client.on('interactionCreate', (interaction) =>
|
||||
this.process(interaction)
|
||||
)
|
||||
}
|
||||
|
||||
/** Adds a new Slash Command Handler */
|
||||
handle(
|
||||
name: string,
|
||||
handler: SlashCommandHandlerCallback,
|
||||
guild?: string
|
||||
): SlashClient {
|
||||
this.handlers.push({
|
||||
name,
|
||||
guild,
|
||||
handler
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/** Process an incoming Slash Command (interaction) */
|
||||
private process(interaction: Interaction): void {
|
||||
if (!this.enabled) return
|
||||
|
||||
if (interaction.type !== InteractionType.APPLICATION_COMMAND) return
|
||||
|
||||
let cmd
|
||||
|
||||
if (interaction.guild !== undefined)
|
||||
cmd =
|
||||
this.handlers.find(
|
||||
(e) => e.guild !== undefined && e.name === interaction.name
|
||||
) ?? this.handlers.find((e) => e.name === interaction.name)
|
||||
else cmd = this.handlers.find((e) => e.name === interaction.name)
|
||||
|
||||
if (cmd === undefined) return
|
||||
|
||||
cmd.handler(interaction)
|
||||
}
|
||||
}
|
18
src/models/slashModule.ts
Normal file
18
src/models/slashModule.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { SlashCommandHandler } from './slashClient.ts'
|
||||
|
||||
export class SlashModule {
|
||||
name: string = ''
|
||||
commands: SlashCommandHandler[] = []
|
||||
_decoratedSlash?: SlashCommandHandler[]
|
||||
|
||||
constructor() {
|
||||
if (this._decoratedSlash !== undefined) {
|
||||
this.commands = this._decoratedSlash
|
||||
}
|
||||
}
|
||||
|
||||
add(handler: SlashCommandHandler): SlashModule {
|
||||
this.commands.push(handler)
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -50,17 +50,23 @@ export class Presence extends Base {
|
|||
}
|
||||
}
|
||||
|
||||
interface StatusPayload extends StatusUpdatePayload {
|
||||
client_status?: ClientStatus
|
||||
}
|
||||
|
||||
export class ClientPresence {
|
||||
status: StatusType = 'online'
|
||||
activity?: ActivityGame | ActivityGame[]
|
||||
since?: number | null
|
||||
afk?: boolean
|
||||
clientStatus?: ClientStatus
|
||||
|
||||
constructor(data?: ClientActivity | StatusUpdatePayload | ActivityGame) {
|
||||
constructor(data?: ClientActivity | StatusPayload | ActivityGame) {
|
||||
if (data !== undefined) {
|
||||
if ((data as ClientActivity).activity !== undefined) {
|
||||
Object.assign(this, data)
|
||||
} else if ((data as StatusUpdatePayload).activities !== undefined) {
|
||||
} else if ((data as StatusPayload).activities !== undefined) {
|
||||
this.parse(data as StatusPayload)
|
||||
} else if ((data as ActivityGame).name !== undefined) {
|
||||
if (this.activity === undefined) {
|
||||
this.activity = data as ActivityGame
|
||||
|
@ -71,11 +77,12 @@ export class ClientPresence {
|
|||
}
|
||||
}
|
||||
|
||||
parse(payload: StatusUpdatePayload): ClientPresence {
|
||||
parse(payload: StatusPayload): ClientPresence {
|
||||
this.afk = payload.afk
|
||||
this.activity = payload.activities ?? undefined
|
||||
this.since = payload.since
|
||||
this.status = payload.status
|
||||
// this.clientStatus = payload.client_status
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -83,12 +90,13 @@ export class ClientPresence {
|
|||
return new ClientPresence().parse(payload)
|
||||
}
|
||||
|
||||
create(): StatusUpdatePayload {
|
||||
create(): StatusPayload {
|
||||
return {
|
||||
afk: this.afk === undefined ? false : this.afk,
|
||||
activities: this.createActivity(),
|
||||
since: this.since === undefined ? null : this.since,
|
||||
status: this.status === undefined ? 'online' : this.status
|
||||
// client_status: this.clientStatus
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,4 +152,13 @@ export class ClientPresence {
|
|||
this.since = since
|
||||
return this
|
||||
}
|
||||
|
||||
// setClientStatus(
|
||||
// client: 'desktop' | 'web' | 'mobile',
|
||||
// status: StatusType
|
||||
// ): ClientPresence {
|
||||
// if (this.clientStatus === undefined) this.clientStatus = {}
|
||||
// this.clientStatus[client] = status
|
||||
// return this
|
||||
// }
|
||||
}
|
||||
|
|
233
src/structures/slash.ts
Normal file
233
src/structures/slash.ts
Normal file
|
@ -0,0 +1,233 @@
|
|||
import { Client } from '../models/client.ts'
|
||||
import { MessageOption } from '../types/channel.ts'
|
||||
import { INTERACTION_CALLBACK, WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
||||
import {
|
||||
InteractionData,
|
||||
InteractionPayload,
|
||||
InteractionResponsePayload,
|
||||
InteractionResponseType
|
||||
} from '../types/slash.ts'
|
||||
import { Embed } from './embed.ts'
|
||||
import { Guild } from './guild.ts'
|
||||
import { Member } from './member.ts'
|
||||
import { Message } from './message.ts'
|
||||
import { GuildTextChannel, TextChannel } from './textChannel.ts'
|
||||
import { User } from './user.ts'
|
||||
import { Webhook } from './webhook.ts'
|
||||
|
||||
interface WebhookMessageOptions extends MessageOption {
|
||||
embeds?: Embed[]
|
||||
name?: string
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
type AllWebhookMessageOptions = string | WebhookMessageOptions
|
||||
|
||||
export interface InteractionResponse {
|
||||
type?: InteractionResponseType
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
tts?: boolean
|
||||
flags?: number
|
||||
temp?: boolean
|
||||
allowedMentions?: {
|
||||
parse?: string
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
everyone?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export class Interaction {
|
||||
client: Client
|
||||
type: number
|
||||
token: string
|
||||
id: string
|
||||
data: InteractionData
|
||||
channel: GuildTextChannel
|
||||
guild: Guild
|
||||
member: Member
|
||||
_savedHook?: Webhook
|
||||
|
||||
constructor(
|
||||
client: Client,
|
||||
data: InteractionPayload,
|
||||
others: {
|
||||
channel: GuildTextChannel
|
||||
guild: Guild
|
||||
member: Member
|
||||
}
|
||||
) {
|
||||
this.client = client
|
||||
this.type = data.type
|
||||
this.token = data.token
|
||||
this.member = others.member
|
||||
this.id = data.id
|
||||
this.data = data.data
|
||||
this.guild = others.guild
|
||||
this.channel = others.channel
|
||||
}
|
||||
|
||||
get user(): User {
|
||||
return this.member.user
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this.data.name
|
||||
}
|
||||
|
||||
option<T = any>(name: string): T {
|
||||
return this.data.options.find((e) => e.name === name)?.value
|
||||
}
|
||||
|
||||
async respond(data: InteractionResponse): Promise<Interaction> {
|
||||
const payload: InteractionResponsePayload = {
|
||||
type: data.type ?? InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data:
|
||||
data.type === undefined ||
|
||||
data.type === InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE ||
|
||||
data.type === InteractionResponseType.CHANNEL_MESSAGE
|
||||
? {
|
||||
content: data.content ?? '',
|
||||
embeds: data.embeds,
|
||||
tts: data.tts ?? false,
|
||||
flags: data.temp === true ? 64 : data.flags ?? undefined,
|
||||
allowed_mentions: (data.allowedMentions ?? undefined) as any
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
||||
await this.client.rest.post(
|
||||
INTERACTION_CALLBACK(this.id, this.token),
|
||||
payload
|
||||
)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
async editResponse(data: {
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
}): Promise<Interaction> {
|
||||
const url = WEBHOOK_MESSAGE(
|
||||
this.client.user?.id as string,
|
||||
this.token,
|
||||
'@original'
|
||||
)
|
||||
await this.client.rest.patch(url, {
|
||||
content: data.content ?? '',
|
||||
embeds: data.embeds ?? []
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
async deleteResponse(): Promise<Interaction> {
|
||||
const url = WEBHOOK_MESSAGE(
|
||||
this.client.user?.id as string,
|
||||
this.token,
|
||||
'@original'
|
||||
)
|
||||
await this.client.rest.delete(url)
|
||||
return this
|
||||
}
|
||||
|
||||
get url(): string {
|
||||
return `https://discord.com/api/v8/webhooks/${this.client.user?.id}/${this.token}`
|
||||
}
|
||||
|
||||
async send(
|
||||
text?: string | AllWebhookMessageOptions,
|
||||
option?: AllWebhookMessageOptions
|
||||
): Promise<Message> {
|
||||
if (typeof text === 'object') {
|
||||
option = text
|
||||
text = undefined
|
||||
}
|
||||
|
||||
if (text === undefined && option === undefined) {
|
||||
throw new Error('Either text or option is necessary.')
|
||||
}
|
||||
|
||||
if (option instanceof Embed)
|
||||
option = {
|
||||
embeds: [option]
|
||||
}
|
||||
|
||||
const payload: any = {
|
||||
content: text,
|
||||
embeds:
|
||||
(option as WebhookMessageOptions)?.embed !== undefined
|
||||
? [(option as WebhookMessageOptions).embed]
|
||||
: (option as WebhookMessageOptions)?.embeds !== undefined
|
||||
? (option as WebhookMessageOptions).embeds
|
||||
: undefined,
|
||||
file: (option as WebhookMessageOptions)?.file,
|
||||
tts: (option as WebhookMessageOptions)?.tts,
|
||||
allowed_mentions: (option as WebhookMessageOptions)?.allowedMentions
|
||||
}
|
||||
|
||||
if ((option as WebhookMessageOptions)?.name !== undefined) {
|
||||
payload.username = (option as WebhookMessageOptions)?.name
|
||||
}
|
||||
|
||||
if ((option as WebhookMessageOptions)?.avatar !== undefined) {
|
||||
payload.avatar = (option as WebhookMessageOptions)?.avatar
|
||||
}
|
||||
|
||||
if (
|
||||
payload.embeds !== undefined &&
|
||||
payload.embeds instanceof Array &&
|
||||
payload.embeds.length > 10
|
||||
)
|
||||
throw new Error(
|
||||
`Cannot send more than 10 embeds through Interaction Webhook`
|
||||
)
|
||||
|
||||
const resp = await this.client.rest.post(`${this.url}?wait=true`, payload)
|
||||
|
||||
const res = new Message(
|
||||
this.client,
|
||||
resp,
|
||||
(this as unknown) as TextChannel,
|
||||
(this as unknown) as User
|
||||
)
|
||||
await res.mentions.fromPayload(resp)
|
||||
return res
|
||||
}
|
||||
|
||||
async editMessage(
|
||||
msg: Message | string,
|
||||
data: {
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
file?: any
|
||||
allowed_mentions?: {
|
||||
parse?: string
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
everyone?: boolean
|
||||
}
|
||||
}
|
||||
): Promise<Interaction> {
|
||||
await this.client.rest.patch(
|
||||
WEBHOOK_MESSAGE(
|
||||
this.client.user?.id as string,
|
||||
this.token ?? this.client.token,
|
||||
typeof msg === 'string' ? msg : msg.id
|
||||
),
|
||||
data
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
async deleteMessage(msg: Message | string): Promise<Interaction> {
|
||||
await this.client.rest.delete(
|
||||
WEBHOOK_MESSAGE(
|
||||
this.client.user?.id as string,
|
||||
this.token ?? this.client.token,
|
||||
typeof msg === 'string' ? msg : msg.id
|
||||
)
|
||||
)
|
||||
return this
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@ export class VoiceState extends Base {
|
|||
sessionID: string
|
||||
deaf: boolean
|
||||
mute: boolean
|
||||
selfDeaf: boolean
|
||||
selfMute: boolean
|
||||
stream?: boolean
|
||||
video: boolean
|
||||
suppress: boolean
|
||||
|
@ -38,8 +40,8 @@ export class VoiceState extends Base {
|
|||
this.guild = _data.guild
|
||||
this.deaf = data.deaf
|
||||
this.mute = data.mute
|
||||
this.deaf = data.self_deaf
|
||||
this.mute = data.self_mute
|
||||
this.selfDeaf = data.self_deaf
|
||||
this.selfMute = data.self_mute
|
||||
this.stream = data.self_stream
|
||||
this.video = data.self_video
|
||||
this.suppress = data.suppress
|
||||
|
@ -52,6 +54,8 @@ export class VoiceState extends Base {
|
|||
this.mute = data.mute ?? this.mute
|
||||
this.deaf = data.self_deaf ?? this.deaf
|
||||
this.mute = data.self_mute ?? this.mute
|
||||
this.selfDeaf = data.self_deaf ?? this.selfDeaf
|
||||
this.selfMute = data.self_mute ?? this.selfMute
|
||||
this.stream = data.self_stream ?? this.stream
|
||||
this.video = data.self_video ?? this.video
|
||||
this.suppress = data.suppress ?? this.suppress
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Message } from './message.ts'
|
|||
import { TextChannel } from './textChannel.ts'
|
||||
import { User } from './user.ts'
|
||||
import { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts'
|
||||
import { WEBHOOK_MESSAGE } from '../types/endpoint.ts'
|
||||
|
||||
export interface WebhookMessageOptions extends MessageOption {
|
||||
embeds?: Embed[]
|
||||
|
@ -191,4 +192,40 @@ export class Webhook {
|
|||
if (resp.response.status !== 204) return false
|
||||
else return true
|
||||
}
|
||||
|
||||
async editMessage(
|
||||
message: string | Message,
|
||||
data: {
|
||||
content?: string
|
||||
embeds?: Embed[]
|
||||
file?: any
|
||||
allowed_mentions?: {
|
||||
parse?: string
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
everyone?: boolean
|
||||
}
|
||||
}
|
||||
): Promise<Webhook> {
|
||||
await this.client?.rest.patch(
|
||||
WEBHOOK_MESSAGE(
|
||||
this.id,
|
||||
(this.token ?? this.client.token) as string,
|
||||
typeof message === 'string' ? message : message.id
|
||||
),
|
||||
data
|
||||
)
|
||||
return this
|
||||
}
|
||||
|
||||
async deleteMessage(message: string | Message): Promise<Webhook> {
|
||||
await this.client?.rest.delete(
|
||||
WEBHOOK_MESSAGE(
|
||||
this.id,
|
||||
(this.token ?? this.client.token) as string,
|
||||
typeof message === 'string' ? message : message.id
|
||||
)
|
||||
)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import {
|
|||
Client,
|
||||
Intents,
|
||||
Message,
|
||||
ClientPresence,
|
||||
Member,
|
||||
Role,
|
||||
GuildChannel,
|
||||
|
@ -15,10 +14,9 @@ import {
|
|||
import { TOKEN } from './config.ts'
|
||||
|
||||
const client = new Client({
|
||||
presence: new ClientPresence({
|
||||
name: 'Pokémon Sword',
|
||||
type: 'COMPETING'
|
||||
})
|
||||
clientProperties: {
|
||||
browser: 'Discord iOS'
|
||||
}
|
||||
// bot: false,
|
||||
// cache: new RedisCacheAdapter({
|
||||
// hostname: '127.0.0.1',
|
||||
|
|
137
src/test/music.ts
Normal file
137
src/test/music.ts
Normal file
|
@ -0,0 +1,137 @@
|
|||
import {
|
||||
CommandClient,
|
||||
event,
|
||||
Intents,
|
||||
command,
|
||||
CommandContext,
|
||||
Extension,
|
||||
Collection
|
||||
} from '../../mod.ts'
|
||||
import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts'
|
||||
import {
|
||||
Manager,
|
||||
Player
|
||||
} from 'https://raw.githubusercontent.com/DjDeveloperr/lavaclient-deno/master/mod.ts'
|
||||
|
||||
export const nodes = [
|
||||
{
|
||||
id: 'main',
|
||||
host: LL_IP,
|
||||
port: LL_PORT,
|
||||
password: LL_PASS
|
||||
}
|
||||
]
|
||||
|
||||
class MyClient extends CommandClient {
|
||||
manager: Manager
|
||||
|
||||
constructor() {
|
||||
super({
|
||||
prefix: ['.'],
|
||||
caseSensitive: false
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const client = this
|
||||
|
||||
this.manager = new Manager(nodes, {
|
||||
send(id, payload) {
|
||||
// Sharding not added yet
|
||||
client.gateway?.send(payload)
|
||||
}
|
||||
})
|
||||
|
||||
this.manager.on('socketError', ({ id }, error) =>
|
||||
console.error(`${id} ran into an error`, error)
|
||||
)
|
||||
this.manager.on('socketReady', (node) =>
|
||||
console.log(`${node.id} connected.`)
|
||||
)
|
||||
|
||||
this.on('raw', (evt: string, d: any) => {
|
||||
if (evt === 'VOICE_SERVER_UPDATE') this.manager.serverUpdate(d)
|
||||
else if (evt === 'VOICE_STATE_UPDATE') this.manager.stateUpdate(d)
|
||||
})
|
||||
}
|
||||
|
||||
@event()
|
||||
ready(): void {
|
||||
console.log(`Logged in as ${this.user?.tag}!`)
|
||||
this.manager.init(this.user?.id as string)
|
||||
}
|
||||
}
|
||||
|
||||
const players = new Collection<string, Player>()
|
||||
|
||||
class VCExtension extends Extension {
|
||||
name = 'VC'
|
||||
subPrefix = 'vc'
|
||||
|
||||
@command()
|
||||
async join(ctx: CommandContext): Promise<any> {
|
||||
if (players.has(ctx.guild?.id as string) === true)
|
||||
return ctx.message.reply(`Already playing in this server!`)
|
||||
|
||||
ctx.argString = ctx.argString.slice(4).trim()
|
||||
|
||||
if (ctx.argString === '')
|
||||
return ctx.message.reply('You gave nothing to search.')
|
||||
|
||||
const userVS = await ctx.guild?.voiceStates.get(ctx.author.id)
|
||||
if (userVS === undefined) {
|
||||
ctx.message.reply("You're not in VC.")
|
||||
return
|
||||
}
|
||||
|
||||
const player = (ctx.client as MyClient).manager.create(
|
||||
ctx.guild?.id as string
|
||||
)
|
||||
|
||||
await player.connect(userVS.channel?.id as string, { selfDeaf: true })
|
||||
|
||||
ctx.message.reply(`Joined VC channel - ${userVS.channel?.name}!`)
|
||||
|
||||
players.set(ctx.guild?.id as string, player)
|
||||
|
||||
ctx.channel.send(`Loading...`)
|
||||
|
||||
ctx.channel.send(`Searching for ${ctx.argString}...`)
|
||||
|
||||
const { track, info } = await player.manager
|
||||
.search(`ytsearch:${ctx.argString}`)
|
||||
.then((e) => e.tracks[0])
|
||||
|
||||
await player.play(track)
|
||||
|
||||
ctx.channel.send(`Now playing ${info.title}!`)
|
||||
}
|
||||
|
||||
@command()
|
||||
async leave(ctx: CommandContext): Promise<any> {
|
||||
const userVS = await ctx.guild?.voiceStates.get(
|
||||
(ctx.client.user?.id as unknown) as string
|
||||
)
|
||||
if (userVS === undefined) {
|
||||
ctx.message.reply("I'm not in VC.")
|
||||
return
|
||||
}
|
||||
userVS.channel?.leave()
|
||||
ctx.message.reply(`Left VC channel - ${userVS.channel?.name}!`)
|
||||
|
||||
if (players.has(ctx.guild?.id as string) !== true)
|
||||
return ctx.message.reply('Not playing anything in this server.')
|
||||
|
||||
const player = (players.get(ctx.guild?.id as string) as unknown) as Player
|
||||
await player.stop()
|
||||
await player.destroy()
|
||||
|
||||
players.delete(ctx.guild?.id as string)
|
||||
ctx.message.reply('Stopped player')
|
||||
}
|
||||
}
|
||||
|
||||
const client = new MyClient()
|
||||
|
||||
client.extensions.load(VCExtension)
|
||||
|
||||
client.connect(TOKEN, Intents.None)
|
51
src/test/slash-cmd.ts
Normal file
51
src/test/slash-cmd.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { TOKEN } from './config.ts'
|
||||
|
||||
export const CMD = {
|
||||
name: 'blep',
|
||||
description: 'Send a random adorable animal photo',
|
||||
options: [
|
||||
{
|
||||
name: 'animal',
|
||||
description: 'The type of animal',
|
||||
type: 3,
|
||||
required: true,
|
||||
choices: [
|
||||
{
|
||||
name: 'Dog',
|
||||
value: 'animal_dog'
|
||||
},
|
||||
{
|
||||
name: 'Cat',
|
||||
value: 'animal_dog'
|
||||
},
|
||||
{
|
||||
name: 'Penguin',
|
||||
value: 'animal_penguin'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'only_smol',
|
||||
description: 'Whether to show only baby animals',
|
||||
type: 5,
|
||||
required: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// fetch('https://discord.com/api/v8/applications/783937840752099332/commands', {
|
||||
fetch(
|
||||
'https://discord.com/api/v8/applications/783937840752099332/guilds/783319033205751809/commands',
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(CMD),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization:
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
'Bot ' + TOKEN
|
||||
}
|
||||
}
|
||||
)
|
||||
.then((r) => r.json())
|
||||
.then(console.log)
|
96
src/test/slash.ts
Normal file
96
src/test/slash.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import { Client, Intents, event, slash } from '../../mod.ts'
|
||||
import { Embed } from '../structures/embed.ts'
|
||||
import { Interaction } from '../structures/slash.ts'
|
||||
import { TOKEN } from './config.ts'
|
||||
|
||||
export class MyClient extends Client {
|
||||
@event()
|
||||
ready(): void {
|
||||
console.log(`Logged in as ${this.user?.tag}!`)
|
||||
}
|
||||
|
||||
@slash()
|
||||
send(d: Interaction): void {
|
||||
d.respond({
|
||||
content: d.data.options.find((e) => e.name === 'content')?.value
|
||||
})
|
||||
}
|
||||
|
||||
@slash()
|
||||
async eval(d: Interaction): Promise<void> {
|
||||
if (
|
||||
d.user.id !== '422957901716652033' &&
|
||||
d.user.id !== '682849186227552266'
|
||||
) {
|
||||
d.respond({
|
||||
content: 'This command can only be used by owner!'
|
||||
})
|
||||
} else {
|
||||
const code = d.data.options.find((e) => e.name === 'code')
|
||||
?.value as string
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
let evaled = eval(code)
|
||||
if (evaled instanceof Promise) evaled = await evaled
|
||||
if (typeof evaled === 'object') evaled = Deno.inspect(evaled)
|
||||
let res = `${evaled}`.substring(0, 1990)
|
||||
while (client.token !== undefined && res.includes(client.token)) {
|
||||
res = res.replace(client.token, '[REMOVED]')
|
||||
}
|
||||
d.respond({
|
||||
content: '```js\n' + `${res}` + '\n```'
|
||||
}).catch(() => {})
|
||||
} catch (e) {
|
||||
d.respond({
|
||||
content: '```js\n' + `${e.stack}` + '\n```'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@slash()
|
||||
async hug(d: Interaction): Promise<void> {
|
||||
const id = d.data.options.find((e) => e.name === 'user')?.value as string
|
||||
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
|
||||
const url = await fetch('https://nekos.life/api/v2/img/hug')
|
||||
.then((r) => r.json())
|
||||
.then((e) => e.url)
|
||||
|
||||
d.respond({
|
||||
embeds: [
|
||||
new Embed()
|
||||
.setTitle(`${d.user.username} hugged ${user?.username}!`)
|
||||
.setImage({ url })
|
||||
.setColor(0x2f3136)
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
@slash()
|
||||
async kiss(d: Interaction): Promise<void> {
|
||||
const id = d.data.options.find((e) => e.name === 'user')?.value as string
|
||||
const user = (await client.users.get(id)) ?? (await client.users.fetch(id))
|
||||
const url = await fetch('https://nekos.life/api/v2/img/kiss')
|
||||
.then((r) => r.json())
|
||||
.then((e) => e.url)
|
||||
|
||||
d.respond({
|
||||
embeds: [
|
||||
new Embed()
|
||||
.setTitle(`${d.user.username} kissed ${user?.username}!`)
|
||||
.setImage({ url })
|
||||
.setColor(0x2f3136)
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
@slash('ping')
|
||||
pingCmd(d: Interaction): void {
|
||||
d.respond({
|
||||
content: `Pong!`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const client = new MyClient()
|
||||
client.connect(TOKEN, Intents.None)
|
|
@ -191,81 +191,35 @@ const VOICE_REGIONS = (guildID: string): string =>
|
|||
const CLIENT_USER = (): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me`
|
||||
|
||||
export default [
|
||||
GUILDS,
|
||||
GUILD,
|
||||
GUILD_AUDIT_LOGS,
|
||||
GUILD_WIDGET,
|
||||
GUILD_EMOJI,
|
||||
GUILD_ROLE,
|
||||
GUILD_ROLES,
|
||||
GUILD_INTEGRATION,
|
||||
GUILD_INTEGRATIONS,
|
||||
GUILD_INTEGARTION_SYNC,
|
||||
GUILD_WIDGET_IMAGE,
|
||||
GUILD_BAN,
|
||||
GUILD_BANS,
|
||||
GUILD_CHANNEL,
|
||||
GUILD_CHANNELS,
|
||||
GUILD_MEMBER,
|
||||
CLIENT_USER,
|
||||
GUILD_MEMBERS,
|
||||
GUILD_MEMBER_ROLE,
|
||||
GUILD_INVITES,
|
||||
GUILD_LEAVE,
|
||||
GUILD_PRUNE,
|
||||
GUILD_VANITY_URL,
|
||||
GUILD_NICK,
|
||||
GUILD_PREVIEW,
|
||||
CHANNEL,
|
||||
CHANNELS,
|
||||
CHANNEL_MESSAGE,
|
||||
CHANNEL_MESSAGES,
|
||||
CHANNEL_CROSSPOST,
|
||||
MESSAGE_REACTIONS,
|
||||
MESSAGE_REACTION,
|
||||
MESSAGE_REACTION_ME,
|
||||
MESSAGE_REACTION_USER,
|
||||
CHANNEL_BULK_DELETE,
|
||||
CHANNEL_FOLLOW,
|
||||
CHANNEL_INVITES,
|
||||
CHANNEL_PIN,
|
||||
CHANNEL_PINS,
|
||||
CHANNEL_PERMISSION,
|
||||
CHANNEL_TYPING,
|
||||
GROUP_RECIPIENT,
|
||||
CURRENT_USER,
|
||||
CURRENT_USER_GUILDS,
|
||||
USER_DM,
|
||||
USER_CONNECTIONS,
|
||||
LEAVE_GUILD,
|
||||
USER,
|
||||
CHANNEL_WEBHOOKS,
|
||||
GUILD_WEBHOOK,
|
||||
WEBHOOK,
|
||||
WEBHOOK_WITH_TOKEN,
|
||||
SLACK_WEBHOOK,
|
||||
GITHUB_WEBHOOK,
|
||||
GATEWAY,
|
||||
GATEWAY_BOT,
|
||||
CUSTOM_EMOJI,
|
||||
GUILD_ICON,
|
||||
GUILD_SPLASH,
|
||||
GUILD_DISCOVERY_SPLASH,
|
||||
GUILD_BANNER,
|
||||
DEFAULT_USER_AVATAR,
|
||||
USER_AVATAR,
|
||||
APPLICATION_ASSET,
|
||||
ACHIEVEMENT_ICON,
|
||||
TEAM_ICON,
|
||||
EMOJI,
|
||||
GUILD_EMOJIS,
|
||||
TEMPLATE,
|
||||
INVITE,
|
||||
VOICE_REGIONS
|
||||
]
|
||||
const APPLICATION_COMMANDS = (id: string): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands`
|
||||
|
||||
const APPLICATION_COMMAND = (id: string, cmdID: string): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands/${cmdID}`
|
||||
|
||||
const APPLICATION_GUILD_COMMANDS = (id: string, guildID: string): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands`
|
||||
|
||||
const APPLICATION_GUILD_COMMAND = (
|
||||
id: string,
|
||||
guildID: string,
|
||||
cmdID: string
|
||||
): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands/${cmdID}`
|
||||
|
||||
const WEBHOOK_MESSAGE = (id: string, token: string, msgID: string): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/webhooks/${id}/${token}/messages/${msgID}`
|
||||
|
||||
const INTERACTION_CALLBACK = (id: string, token: string): string =>
|
||||
`${DISCORD_API_URL}/v${DISCORD_API_VERSION}/interactions/${id}/${token}/callback`
|
||||
|
||||
export {
|
||||
INTERACTION_CALLBACK,
|
||||
APPLICATION_COMMAND,
|
||||
APPLICATION_GUILD_COMMAND,
|
||||
WEBHOOK_MESSAGE,
|
||||
APPLICATION_COMMANDS,
|
||||
APPLICATION_GUILD_COMMANDS,
|
||||
GUILDS,
|
||||
GUILD,
|
||||
GUILD_AUDIT_LOGS,
|
||||
|
|
|
@ -105,7 +105,8 @@ export enum GatewayEvents {
|
|||
User_Update = 'USER_UPDATE',
|
||||
Voice_Server_Update = 'VOICE_SERVER_UPDATE',
|
||||
Voice_State_Update = 'VOICE_STATE_UPDATE',
|
||||
Webhooks_Update = 'WEBHOOKS_UPDATE'
|
||||
Webhooks_Update = 'WEBHOOKS_UPDATE',
|
||||
Interaction_Create = 'INTERACTION_CREATE'
|
||||
}
|
||||
|
||||
export interface IdentityPayload {
|
||||
|
@ -120,11 +121,11 @@ export interface IdentityPayload {
|
|||
}
|
||||
|
||||
export interface IdentityConnection {
|
||||
$os: 'darwin' | 'windows' | 'linux' | 'custom os'
|
||||
$browser: 'harmony' | 'Firefox'
|
||||
$device: 'harmony' | ''
|
||||
$referrer?: ''
|
||||
$referring_domain?: ''
|
||||
$os: 'darwin' | 'windows' | 'linux' | 'custom os' | string
|
||||
$browser: 'harmony' | 'Firefox' | string
|
||||
$device: 'harmony' | string
|
||||
$referrer?: '' | string
|
||||
$referring_domain?: '' | string
|
||||
}
|
||||
|
||||
export interface Resume {
|
||||
|
|
90
src/types/slash.ts
Normal file
90
src/types/slash.ts
Normal file
|
@ -0,0 +1,90 @@
|
|||
import { EmbedPayload } from './channel.ts'
|
||||
import { MemberPayload } from './guild.ts'
|
||||
|
||||
export interface InteractionOption {
|
||||
name: string
|
||||
value?: any
|
||||
options?: any[]
|
||||
}
|
||||
|
||||
export interface InteractionData {
|
||||
name: string
|
||||
id: string
|
||||
options: InteractionOption[]
|
||||
}
|
||||
|
||||
export enum InteractionType {
|
||||
PING = 1,
|
||||
APPLICATION_COMMAND = 2
|
||||
}
|
||||
|
||||
export interface InteractionPayload {
|
||||
type: InteractionType
|
||||
token: string
|
||||
member: MemberPayload
|
||||
id: string
|
||||
data: InteractionData
|
||||
guild_id: string
|
||||
channel_id: string
|
||||
}
|
||||
|
||||
export interface SlashCommandChoice {
|
||||
name: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export enum SlashCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8
|
||||
}
|
||||
|
||||
export interface SlashCommandOption {
|
||||
name: string
|
||||
description: string
|
||||
type: SlashCommandOptionType
|
||||
required: boolean
|
||||
choices?: SlashCommandChoice[]
|
||||
options?: SlashCommandOption[]
|
||||
}
|
||||
|
||||
export interface SlashCommandPartial {
|
||||
name: string
|
||||
description: string
|
||||
options: SlashCommandOption[]
|
||||
}
|
||||
|
||||
export interface SlashCommandPayload extends SlashCommandPartial {
|
||||
id: string
|
||||
application_id: string
|
||||
}
|
||||
|
||||
export enum InteractionResponseType {
|
||||
PONG = 1,
|
||||
ACKNOWLEDGE = 2,
|
||||
CHANNEL_MESSAGE = 3,
|
||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||
ACK_WITH_SOURCE = 5
|
||||
}
|
||||
|
||||
export interface InteractionResponsePayload {
|
||||
type: InteractionResponseType
|
||||
data?: InteractionResponseDataPayload
|
||||
}
|
||||
|
||||
export interface InteractionResponseDataPayload {
|
||||
tts?: boolean
|
||||
content: string
|
||||
embeds?: EmbedPayload[]
|
||||
allowed_mentions?: {
|
||||
parse?: 'everyone' | 'users' | 'roles'
|
||||
roles?: string[]
|
||||
users?: string[]
|
||||
}
|
||||
flags?: number
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
export const UserFlags = {
|
||||
DISCORD_EMPLOYEE: 1 << 0,
|
||||
PARTNERED_SERVER_OWNER: 1 << 1,
|
||||
DISCORD_PARTNER: 1 << 1,
|
||||
HYPESQUAD_EVENTS: 1 << 2,
|
||||
BUGHUNTER_LEVEL_1: 1 << 3,
|
||||
HOUSE_BRAVERY: 1 << 6,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice
|
||||
import { MemberPayload } from './guild.ts'
|
||||
|
||||
export enum VoiceOpcodes { // add VoiceOpcodes - UnderC -
|
||||
export enum VoiceOpcodes {
|
||||
IDENTIFY = 0,
|
||||
SELECT_PROTOCOL = 1,
|
||||
READY = 2,
|
||||
|
|
Loading…
Reference in a new issue