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 | # macOS is shit xD | ||||||
| **/.DS_Store | **/.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 { default as EventEmitter } from 'https://deno.land/std@0.74.0/node/events.ts' | ||||||
| export { Base } from './src/structures/base.ts' | export { Base } from './src/structures/base.ts' | ||||||
| export { Gateway } from './src/gateway/index.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/client.ts' | ||||||
|  | export * from './src/models/slashClient.ts' | ||||||
| export { RESTManager } from './src/models/rest.ts' | export { RESTManager } from './src/models/rest.ts' | ||||||
| export * from './src/models/cacheAdapter.ts' | export * from './src/models/cacheAdapter.ts' | ||||||
| export { | export { | ||||||
|  | @ -18,6 +20,7 @@ export { | ||||||
|   ExtensionCommands, |   ExtensionCommands, | ||||||
|   ExtensionsManager |   ExtensionsManager | ||||||
| } from './src/models/extensions.ts' | } from './src/models/extensions.ts' | ||||||
|  | export { SlashModule } from './src/models/slashModule.ts' | ||||||
| export { CommandClient, command } from './src/models/commandClient.ts' | export { CommandClient, command } from './src/models/commandClient.ts' | ||||||
| export type { CommandClientOptions } from './src/models/commandClient.ts' | export type { CommandClientOptions } from './src/models/commandClient.ts' | ||||||
| export { BaseManager } from './src/managers/base.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 { GuildChannelsManager } from './src/managers/guildChannels.ts' | ||||||
| export type { GuildChannel } from './src/managers/guildChannels.ts' | export type { GuildChannel } from './src/managers/guildChannels.ts' | ||||||
| export { GuildManager } from './src/managers/guilds.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 { GuildEmojisManager } from './src/managers/guildEmojis.ts' | ||||||
| export { MembersManager } from './src/managers/members.ts' | export { MembersManager } from './src/managers/members.ts' | ||||||
| export { MessageReactionsManager } from './src/managers/messageReactions.ts' | export { MessageReactionsManager } from './src/managers/messageReactions.ts' | ||||||
|  | @ -80,3 +85,41 @@ export type { | ||||||
|   StatusType |   StatusType | ||||||
| } from './src/types/presence.ts' | } from './src/types/presence.ts' | ||||||
| export { ChannelTypes } from './src/types/channel.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 { messageDeleteBulk } from './messageDeleteBulk.ts' | ||||||
| import { userUpdate } from './userUpdate.ts' | import { userUpdate } from './userUpdate.ts' | ||||||
| import { typingStart } from './typingStart.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 { Guild } from '../../structures/guild.ts' | ||||||
| import { User } from '../../structures/user.ts' | import { User } from '../../structures/user.ts' | ||||||
| import { Emoji } from '../../structures/emoji.ts' | import { Emoji } from '../../structures/emoji.ts' | ||||||
|  | @ -53,6 +53,8 @@ import { | ||||||
|   EveryChannelTypes, |   EveryChannelTypes, | ||||||
|   EveryTextChannelTypes |   EveryTextChannelTypes | ||||||
| } from '../../utils/getChannelByType.ts' | } from '../../utils/getChannelByType.ts' | ||||||
|  | import { interactionCreate } from './interactionCreate.ts' | ||||||
|  | import { Interaction } from '../../structures/slash.ts' | ||||||
| 
 | 
 | ||||||
| export const gatewayHandlers: { | export const gatewayHandlers: { | ||||||
|   [eventCode in GatewayEvents]: GatewayEventHandler | undefined |   [eventCode in GatewayEvents]: GatewayEventHandler | undefined | ||||||
|  | @ -93,7 +95,8 @@ export const gatewayHandlers: { | ||||||
|   USER_UPDATE: userUpdate, |   USER_UPDATE: userUpdate, | ||||||
|   VOICE_STATE_UPDATE: voiceStateUpdate, |   VOICE_STATE_UPDATE: voiceStateUpdate, | ||||||
|   VOICE_SERVER_UPDATE: voiceServerUpdate, |   VOICE_SERVER_UPDATE: voiceServerUpdate, | ||||||
|   WEBHOOKS_UPDATE: webhooksUpdate |   WEBHOOKS_UPDATE: webhooksUpdate, | ||||||
|  |   INTERACTION_CREATE: interactionCreate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface EventTypes { | export interface EventTypes { | ||||||
|  | @ -107,56 +110,227 @@ export interface VoiceServerUpdateData { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface ClientEvents extends EventTypes { | export interface ClientEvents extends EventTypes { | ||||||
|  |   /** When Client has successfully connected to Discord */ | ||||||
|   ready: () => void |   ready: () => void | ||||||
|  |   /** When a successful reconnect has been made */ | ||||||
|   reconnect: () => void |   reconnect: () => void | ||||||
|  |   /** When a successful session resume has been done */ | ||||||
|   resumed: () => void |   resumed: () => void | ||||||
|  |   /** | ||||||
|  |    * When a new Channel is created | ||||||
|  |    * @param channel New Channel object | ||||||
|  |    */ | ||||||
|   channelCreate: (channel: EveryChannelTypes) => void |   channelCreate: (channel: EveryChannelTypes) => void | ||||||
|  |   /** | ||||||
|  |    * When a Channel was deleted | ||||||
|  |    * @param channel Channel object which was deleted | ||||||
|  |    */ | ||||||
|   channelDelete: (channel: EveryChannelTypes) => void |   channelDelete: (channel: EveryChannelTypes) => void | ||||||
|  |   /** | ||||||
|  |    * Channel's Pinned Messages were updated | ||||||
|  |    * @param before Channel object before update | ||||||
|  |    * @param after Channel object after update | ||||||
|  |    */ | ||||||
|   channelPinsUpdate: ( |   channelPinsUpdate: ( | ||||||
|     before: EveryTextChannelTypes, |     before: EveryTextChannelTypes, | ||||||
|     after: EveryTextChannelTypes |     after: EveryTextChannelTypes | ||||||
|   ) => void |   ) => void | ||||||
|  |   /** | ||||||
|  |    * A Channel was updated | ||||||
|  |    * @param before Channel object before update | ||||||
|  |    * @param after Channel object after update | ||||||
|  |    */ | ||||||
|   channelUpdate: (before: EveryChannelTypes, after: EveryChannelTypes) => void |   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 |   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 |   guildBanRemove: (guild: Guild, user: User) => void | ||||||
|  |   /** | ||||||
|  |    * Client has joined a new Guild. | ||||||
|  |    * @param guild The new Guild object | ||||||
|  |    */ | ||||||
|   guildCreate: (guild: Guild) => void |   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 |   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 |   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 |   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 |   guildEmojiUpdate: (guild: Guild, before: Emoji, after: Emoji) => void | ||||||
|  |   /** | ||||||
|  |    * Guild's Integrations were updated | ||||||
|  |    * @param guild The Guild object | ||||||
|  |    */ | ||||||
|   guildIntegrationsUpdate: (guild: Guild) => void |   guildIntegrationsUpdate: (guild: Guild) => void | ||||||
|  |   /** | ||||||
|  |    * A new Member has joined a Guild | ||||||
|  |    * @param member The Member object | ||||||
|  |    */ | ||||||
|   guildMemberAdd: (member: Member) => void |   guildMemberAdd: (member: Member) => void | ||||||
|  |   /** | ||||||
|  |    * A Guild Member has either left or was kicked from Guild | ||||||
|  |    * @param member The Member object | ||||||
|  |    */ | ||||||
|   guildMemberRemove: (member: Member) => void |   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 |   guildMemberUpdate: (before: Member, after: Member) => void | ||||||
|  |   /** | ||||||
|  |    * A new Role was created in Guild | ||||||
|  |    * @param role The new Role object | ||||||
|  |    */ | ||||||
|   guildRoleCreate: (role: Role) => void |   guildRoleCreate: (role: Role) => void | ||||||
|  |   /** | ||||||
|  |    * A Role was deleted from the Guild | ||||||
|  |    * @param role The Role object | ||||||
|  |    */ | ||||||
|   guildRoleDelete: (role: Role) => void |   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 |   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 |   guildUpdate: (before: Guild, after: Guild) => void | ||||||
|  |   /** | ||||||
|  |    * A new Message was created (sent) | ||||||
|  |    * @param message The new Message object | ||||||
|  |    */ | ||||||
|   messageCreate: (message: Message) => void |   messageCreate: (message: Message) => void | ||||||
|  |   /** | ||||||
|  |    * A Message was deleted. | ||||||
|  |    * @param message The Message object | ||||||
|  |    */ | ||||||
|   messageDelete: (message: Message) => void |   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: ( |   messageDeleteBulk: ( | ||||||
|     channel: GuildTextChannel, |     channel: GuildTextChannel, | ||||||
|     messages: Collection<string, Message>, |     messages: Collection<string, Message>, | ||||||
|     uncached: Set<string> |     uncached: Set<string> | ||||||
|   ) => void |   ) => 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 |   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 |   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 |   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 |   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 |   messageReactionRemoveEmoji: (message: Message, emoji: Emoji) => void | ||||||
|  |   /** | ||||||
|  |    * A User has started typing in a Text Channel | ||||||
|  |    */ | ||||||
|   typingStart: ( |   typingStart: ( | ||||||
|     user: User, |     user: User, | ||||||
|     channel: EveryChannelTypes, |     channel: TextChannel, | ||||||
|     at: Date, |     at: Date, | ||||||
|     guildData?: TypingStartGuildData |     guildData?: TypingStartGuildData | ||||||
|   ) => void |   ) => void | ||||||
|  |   /** | ||||||
|  |    * A new Invite was created | ||||||
|  |    * @param invite New Invite object | ||||||
|  |    */ | ||||||
|   inviteCreate: (invite: Invite) => void |   inviteCreate: (invite: Invite) => void | ||||||
|  |   /** | ||||||
|  |    * An Invite was deleted | ||||||
|  |    * @param invite Invite object | ||||||
|  |    */ | ||||||
|   inviteDelete: (invite: Invite) => void |   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 |   userUpdate: (before: User, after: User) => void | ||||||
|  |   /** | ||||||
|  |    * Client has received credentials for establishing connection to Voice Server | ||||||
|  |    */ | ||||||
|   voiceServerUpdate: (data: VoiceServerUpdateData) => void |   voiceServerUpdate: (data: VoiceServerUpdateData) => void | ||||||
|  |   /** | ||||||
|  |    * A User has joined a Voice Channel | ||||||
|  |    */ | ||||||
|   voiceStateAdd: (state: VoiceState) => void |   voiceStateAdd: (state: VoiceState) => void | ||||||
|  |   /** | ||||||
|  |    * A User has left a Voice Channel | ||||||
|  |    */ | ||||||
|   voiceStateRemove: (state: VoiceState) => void |   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 |   voiceStateUpdate: (state: VoiceState, after: VoiceState) => void | ||||||
|  |   /** | ||||||
|  |    * A User's presence has been updated | ||||||
|  |    * @param presence New Presence | ||||||
|  |    */ | ||||||
|   presenceUpdate: (presence: Presence) => void |   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 |   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 = { |     const payload: IdentityPayload = { | ||||||
|       token: this.token, |       token: this.token, | ||||||
|       properties: { |       properties: { | ||||||
|         $os: Deno.build.os, |         $os: this.client.clientProperties.os ?? Deno.build.os, | ||||||
|         $browser: 'harmony', |         $browser: this.client.clientProperties.browser ?? 'harmony', | ||||||
|         $device: 'harmony' |         $device: this.client.clientProperties.device ?? 'harmony' | ||||||
|       }, |       }, | ||||||
|       compress: true, |       compress: true, | ||||||
|       shard: [0, 1], // TODO: Make sharding possible
 |       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 { ActivityGame, ClientActivity } from '../types/presence.ts' | ||||||
| import { ClientEvents } from '../gateway/handlers/index.ts' | import { ClientEvents } from '../gateway/handlers/index.ts' | ||||||
| import { Extension } from './extensions.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 */ | /** Some Client Options to modify behaviour */ | ||||||
| export interface ClientOptions { | export interface ClientOptions { | ||||||
|  | @ -33,6 +43,10 @@ export interface ClientOptions { | ||||||
|   reactionCacheLifetime?: number |   reactionCacheLifetime?: number | ||||||
|   /** Whether to fetch Uncached Message of Reaction or not? */ |   /** Whether to fetch Uncached Message of Reaction or not? */ | ||||||
|   fetchUncachedReactions?: boolean |   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 |   reactionCacheLifetime: number = 3600000 | ||||||
|   /** Whether to fetch Uncached Message of Reaction or not? */ |   /** Whether to fetch Uncached Message of Reaction or not? */ | ||||||
|   fetchUncachedReactions: boolean = false |   fetchUncachedReactions: boolean = false | ||||||
|  |   /** Client Properties */ | ||||||
|  |   clientProperties: ClientProperties | ||||||
|  |   /** Slash-Commands Management client */ | ||||||
|  |   slash: SlashClient | ||||||
| 
 | 
 | ||||||
|   users: UsersManager = new UsersManager(this) |   users: UsersManager = new UsersManager(this) | ||||||
|   guilds: GuildManager = new GuildManager(this) |   guilds: GuildManager = new GuildManager(this) | ||||||
|  | @ -72,6 +90,13 @@ export class Client extends EventEmitter { | ||||||
|   /** Client's presence. Startup one if set before connecting */ |   /** Client's presence. Startup one if set before connecting */ | ||||||
|   presence: ClientPresence = new ClientPresence() |   presence: ClientPresence = new ClientPresence() | ||||||
|   _decoratedEvents?: { [name: string]: (...args: any[]) => any } |   _decoratedEvents?: { [name: string]: (...args: any[]) => any } | ||||||
|  |   _decoratedSlash?: Array<{ | ||||||
|  |     name: string | ||||||
|  |     guild?: string | ||||||
|  |     handler: (interaction: Interaction) => any | ||||||
|  |   }> | ||||||
|  | 
 | ||||||
|  |   _decoratedSlashModules?: SlashModule[] | ||||||
| 
 | 
 | ||||||
|   private readonly _untypedOn = this.on |   private readonly _untypedOn = this.on | ||||||
| 
 | 
 | ||||||
|  | @ -113,6 +138,19 @@ export class Client extends EventEmitter { | ||||||
|       }) |       }) | ||||||
|       this._decoratedEvents = undefined |       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 { |     const listener = ((client as unknown) as { | ||||||
|       [name: string]: (...args: any[]) => any |       [name: string]: (...args: any[]) => any | ||||||
|     })[prop] |     })[prop] | ||||||
|  |     if (typeof listener !== 'function') | ||||||
|  |       throw new Error('@event decorator requires a function') | ||||||
|     if (client._decoratedEvents === undefined) client._decoratedEvents = {} |     if (client._decoratedEvents === undefined) client._decoratedEvents = {} | ||||||
|     client._decoratedEvents[name === undefined ? prop : name] = listener |     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) { | export function command(options?: CommandOptions) { | ||||||
|   return function (target: CommandClient | Extension, name: string) { |   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() |     const command = new Command() | ||||||
| 
 | 
 | ||||||
|     command.name = name |     command.name = name | ||||||
|     command.execute = ((target as unknown) as { |     command.execute = prop | ||||||
|       [name: string]: (ctx: CommandContext) => any |  | ||||||
|     })[name] |  | ||||||
| 
 | 
 | ||||||
|     if (options !== undefined) Object.assign(command, options) |     if (options !== undefined) Object.assign(command, options) | ||||||
| 
 | 
 | ||||||
|     if (target instanceof Extension) command.extension = target |     if (target instanceof Extension) command.extension = target | ||||||
| 
 | 
 | ||||||
|     if (target._decoratedCommands === undefined) target._decoratedCommands = {} |  | ||||||
|     target._decoratedCommands[command.name] = command |     target._decoratedCommands[command.name] = command | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -277,11 +277,11 @@ export class RESTManager { | ||||||
|       message: body?.message, |       message: body?.message, | ||||||
|       errors: Object.fromEntries( |       errors: Object.fromEntries( | ||||||
|         Object.entries( |         Object.entries( | ||||||
|           body?.errors as { |           (body?.errors as { | ||||||
|             [name: string]: { |             [name: string]: { | ||||||
|               _errors: Array<{ code: string; message: string }> |               _errors: Array<{ code: string; message: string }> | ||||||
|             } |             } | ||||||
|           } |           }) ?? {} | ||||||
|         ).map((entry) => { |         ).map((entry) => { | ||||||
|           return [entry[0], entry[1]._errors] |           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 { | export class ClientPresence { | ||||||
|   status: StatusType = 'online' |   status: StatusType = 'online' | ||||||
|   activity?: ActivityGame | ActivityGame[] |   activity?: ActivityGame | ActivityGame[] | ||||||
|   since?: number | null |   since?: number | null | ||||||
|   afk?: boolean |   afk?: boolean | ||||||
|  |   clientStatus?: ClientStatus | ||||||
| 
 | 
 | ||||||
|   constructor(data?: ClientActivity | StatusUpdatePayload | ActivityGame) { |   constructor(data?: ClientActivity | StatusPayload | ActivityGame) { | ||||||
|     if (data !== undefined) { |     if (data !== undefined) { | ||||||
|       if ((data as ClientActivity).activity !== undefined) { |       if ((data as ClientActivity).activity !== undefined) { | ||||||
|         Object.assign(this, data) |         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) { |       } else if ((data as ActivityGame).name !== undefined) { | ||||||
|         if (this.activity === undefined) { |         if (this.activity === undefined) { | ||||||
|           this.activity = data as ActivityGame |           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.afk = payload.afk | ||||||
|     this.activity = payload.activities ?? undefined |     this.activity = payload.activities ?? undefined | ||||||
|     this.since = payload.since |     this.since = payload.since | ||||||
|     this.status = payload.status |     this.status = payload.status | ||||||
|  |     // this.clientStatus = payload.client_status
 | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -83,12 +90,13 @@ export class ClientPresence { | ||||||
|     return new ClientPresence().parse(payload) |     return new ClientPresence().parse(payload) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   create(): StatusUpdatePayload { |   create(): StatusPayload { | ||||||
|     return { |     return { | ||||||
|       afk: this.afk === undefined ? false : this.afk, |       afk: this.afk === undefined ? false : this.afk, | ||||||
|       activities: this.createActivity(), |       activities: this.createActivity(), | ||||||
|       since: this.since === undefined ? null : this.since, |       since: this.since === undefined ? null : this.since, | ||||||
|       status: this.status === undefined ? 'online' : this.status |       status: this.status === undefined ? 'online' : this.status | ||||||
|  |       // client_status: this.clientStatus
 | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -144,4 +152,13 @@ export class ClientPresence { | ||||||
|     this.since = since |     this.since = since | ||||||
|     return this |     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 |   sessionID: string | ||||||
|   deaf: boolean |   deaf: boolean | ||||||
|   mute: boolean |   mute: boolean | ||||||
|  |   selfDeaf: boolean | ||||||
|  |   selfMute: boolean | ||||||
|   stream?: boolean |   stream?: boolean | ||||||
|   video: boolean |   video: boolean | ||||||
|   suppress: boolean |   suppress: boolean | ||||||
|  | @ -38,8 +40,8 @@ export class VoiceState extends Base { | ||||||
|     this.guild = _data.guild |     this.guild = _data.guild | ||||||
|     this.deaf = data.deaf |     this.deaf = data.deaf | ||||||
|     this.mute = data.mute |     this.mute = data.mute | ||||||
|     this.deaf = data.self_deaf |     this.selfDeaf = data.self_deaf | ||||||
|     this.mute = data.self_mute |     this.selfMute = data.self_mute | ||||||
|     this.stream = data.self_stream |     this.stream = data.self_stream | ||||||
|     this.video = data.self_video |     this.video = data.self_video | ||||||
|     this.suppress = data.suppress |     this.suppress = data.suppress | ||||||
|  | @ -52,6 +54,8 @@ export class VoiceState extends Base { | ||||||
|     this.mute = data.mute ?? this.mute |     this.mute = data.mute ?? this.mute | ||||||
|     this.deaf = data.self_deaf ?? this.deaf |     this.deaf = data.self_deaf ?? this.deaf | ||||||
|     this.mute = data.self_mute ?? this.mute |     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.stream = data.self_stream ?? this.stream | ||||||
|     this.video = data.self_video ?? this.video |     this.video = data.self_video ?? this.video | ||||||
|     this.suppress = data.suppress ?? this.suppress |     this.suppress = data.suppress ?? this.suppress | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import { Message } from './message.ts' | ||||||
| import { TextChannel } from './textChannel.ts' | import { TextChannel } from './textChannel.ts' | ||||||
| import { User } from './user.ts' | import { User } from './user.ts' | ||||||
| import { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.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 { | export interface WebhookMessageOptions extends MessageOption { | ||||||
|   embeds?: Embed[] |   embeds?: Embed[] | ||||||
|  | @ -191,4 +192,40 @@ export class Webhook { | ||||||
|     if (resp.response.status !== 204) return false |     if (resp.response.status !== 204) return false | ||||||
|     else return true |     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, |   Client, | ||||||
|   Intents, |   Intents, | ||||||
|   Message, |   Message, | ||||||
|   ClientPresence, |  | ||||||
|   Member, |   Member, | ||||||
|   Role, |   Role, | ||||||
|   GuildChannel, |   GuildChannel, | ||||||
|  | @ -15,10 +14,9 @@ import { | ||||||
| import { TOKEN } from './config.ts' | import { TOKEN } from './config.ts' | ||||||
| 
 | 
 | ||||||
| const client = new Client({ | const client = new Client({ | ||||||
|   presence: new ClientPresence({ |   clientProperties: { | ||||||
|     name: 'Pokémon Sword', |     browser: 'Discord iOS' | ||||||
|     type: 'COMPETING' |   } | ||||||
|   }) |  | ||||||
|   // bot: false,
 |   // bot: false,
 | ||||||
|   // cache: new RedisCacheAdapter({
 |   // cache: new RedisCacheAdapter({
 | ||||||
|   //   hostname: '127.0.0.1',
 |   //   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 => | const CLIENT_USER = (): string => | ||||||
|   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me` |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/users/@me` | ||||||
| 
 | 
 | ||||||
| export default [ | const APPLICATION_COMMANDS = (id: string): string => | ||||||
|   GUILDS, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands` | ||||||
|   GUILD, | 
 | ||||||
|   GUILD_AUDIT_LOGS, | const APPLICATION_COMMAND = (id: string, cmdID: string): string => | ||||||
|   GUILD_WIDGET, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/commands/${cmdID}` | ||||||
|   GUILD_EMOJI, | 
 | ||||||
|   GUILD_ROLE, | const APPLICATION_GUILD_COMMANDS = (id: string, guildID: string): string => | ||||||
|   GUILD_ROLES, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands` | ||||||
|   GUILD_INTEGRATION, | 
 | ||||||
|   GUILD_INTEGRATIONS, | const APPLICATION_GUILD_COMMAND = ( | ||||||
|   GUILD_INTEGARTION_SYNC, |   id: string, | ||||||
|   GUILD_WIDGET_IMAGE, |   guildID: string, | ||||||
|   GUILD_BAN, |   cmdID: string | ||||||
|   GUILD_BANS, | ): string => | ||||||
|   GUILD_CHANNEL, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/applications/${id}/guilds/${guildID}/commands/${cmdID}` | ||||||
|   GUILD_CHANNELS, | 
 | ||||||
|   GUILD_MEMBER, | const WEBHOOK_MESSAGE = (id: string, token: string, msgID: string): string => | ||||||
|   CLIENT_USER, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/webhooks/${id}/${token}/messages/${msgID}` | ||||||
|   GUILD_MEMBERS, | 
 | ||||||
|   GUILD_MEMBER_ROLE, | const INTERACTION_CALLBACK = (id: string, token: string): string => | ||||||
|   GUILD_INVITES, |   `${DISCORD_API_URL}/v${DISCORD_API_VERSION}/interactions/${id}/${token}/callback` | ||||||
|   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 |  | ||||||
| ] |  | ||||||
| 
 | 
 | ||||||
| export { | export { | ||||||
|  |   INTERACTION_CALLBACK, | ||||||
|  |   APPLICATION_COMMAND, | ||||||
|  |   APPLICATION_GUILD_COMMAND, | ||||||
|  |   WEBHOOK_MESSAGE, | ||||||
|  |   APPLICATION_COMMANDS, | ||||||
|  |   APPLICATION_GUILD_COMMANDS, | ||||||
|   GUILDS, |   GUILDS, | ||||||
|   GUILD, |   GUILD, | ||||||
|   GUILD_AUDIT_LOGS, |   GUILD_AUDIT_LOGS, | ||||||
|  |  | ||||||
|  | @ -105,7 +105,8 @@ export enum GatewayEvents { | ||||||
|   User_Update = 'USER_UPDATE', |   User_Update = 'USER_UPDATE', | ||||||
|   Voice_Server_Update = 'VOICE_SERVER_UPDATE', |   Voice_Server_Update = 'VOICE_SERVER_UPDATE', | ||||||
|   Voice_State_Update = 'VOICE_STATE_UPDATE', |   Voice_State_Update = 'VOICE_STATE_UPDATE', | ||||||
|   Webhooks_Update = 'WEBHOOKS_UPDATE' |   Webhooks_Update = 'WEBHOOKS_UPDATE', | ||||||
|  |   Interaction_Create = 'INTERACTION_CREATE' | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IdentityPayload { | export interface IdentityPayload { | ||||||
|  | @ -120,11 +121,11 @@ export interface IdentityPayload { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface IdentityConnection { | export interface IdentityConnection { | ||||||
|   $os: 'darwin' | 'windows' | 'linux' | 'custom os' |   $os: 'darwin' | 'windows' | 'linux' | 'custom os' | string | ||||||
|   $browser: 'harmony' | 'Firefox' |   $browser: 'harmony' | 'Firefox' | string | ||||||
|   $device: 'harmony' | '' |   $device: 'harmony' | string | ||||||
|   $referrer?: '' |   $referrer?: '' | string | ||||||
|   $referring_domain?: '' |   $referring_domain?: '' | string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export interface Resume { | 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 = { | export const UserFlags = { | ||||||
|   DISCORD_EMPLOYEE: 1 << 0, |   DISCORD_EMPLOYEE: 1 << 0, | ||||||
|   PARTNERED_SERVER_OWNER: 1 << 1, |   PARTNERED_SERVER_OWNER: 1 << 1, | ||||||
|   DISCORD_PARTNER: 1 << 1, |  | ||||||
|   HYPESQUAD_EVENTS: 1 << 2, |   HYPESQUAD_EVENTS: 1 << 2, | ||||||
|   BUGHUNTER_LEVEL_1: 1 << 3, |   BUGHUNTER_LEVEL_1: 1 << 3, | ||||||
|   HOUSE_BRAVERY: 1 << 6, |   HOUSE_BRAVERY: 1 << 6, | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| // https://discord.com/developers/docs/topics/opcodes-and-status-codes#voice
 |  | ||||||
| import { MemberPayload } from './guild.ts' | import { MemberPayload } from './guild.ts' | ||||||
| 
 | 
 | ||||||
| export enum VoiceOpcodes { // add VoiceOpcodes - UnderC -
 | export enum VoiceOpcodes { | ||||||
|   IDENTIFY = 0, |   IDENTIFY = 0, | ||||||
|   SELECT_PROTOCOL = 1, |   SELECT_PROTOCOL = 1, | ||||||
|   READY = 2, |   READY = 2, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue