Github online editor FTW
This commit is contained in:
		
						commit
						a14104ad14
					
				
					 29 changed files with 759 additions and 159 deletions
				
			
		
							
								
								
									
										19
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
										
									
									
									
								
							|  | @ -10,15 +10,12 @@ | ||||||
| <br> | <br> | ||||||
| 
 | 
 | ||||||
| - Lightweight and easy to use. | - Lightweight and easy to use. | ||||||
| - Built-in Command Framework, | - Complete Object-Oriented approach. | ||||||
|   - Easily build Commands on the fly. | - Slash Commands supported. | ||||||
|   - Completely Customizable. | - Built-in Commands framework. | ||||||
|   - Complete Object-Oriented approach. | - Customizable Caching, with Redis support. | ||||||
| - 100% Discord API Coverage. | - Use `@decorators` to easily make things! | ||||||
| - Customizable caching. | - Made with ❤️ TypeScript. | ||||||
|   - Built in support for Redis. |  | ||||||
|   - Write Custom Cache Adapters. |  | ||||||
| - Complete TypeScript support. |  | ||||||
| 
 | 
 | ||||||
| ## Table of Contents | ## Table of Contents | ||||||
| 
 | 
 | ||||||
|  | @ -102,13 +99,14 @@ client.connect('super secret token comes here', Intents.All) | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| Or with Decorators! | Or with Decorators! | ||||||
|  | 
 | ||||||
| ```ts | ```ts | ||||||
| import { | import { | ||||||
|   Client, |   Client, | ||||||
|   event, |   event, | ||||||
|   Intents, |   Intents, | ||||||
|   command, |   command, | ||||||
|   CommandContext, |   CommandContext | ||||||
| } from 'https://deno.land/x/harmony/mod.ts' | } from 'https://deno.land/x/harmony/mod.ts' | ||||||
| 
 | 
 | ||||||
| class MyClient extends CommandClient { | class MyClient extends CommandClient { | ||||||
|  | @ -141,6 +139,7 @@ Documentation is available for `main` (branch) and `stable` (release). | ||||||
| 
 | 
 | ||||||
| - [Main](https://doc.deno.land/https/raw.githubusercontent.com/harmony-org/harmony/main/mod.ts) | - [Main](https://doc.deno.land/https/raw.githubusercontent.com/harmony-org/harmony/main/mod.ts) | ||||||
| - [Stable](https://doc.deno.land/https/deno.land/x/harmony/mod.ts) | - [Stable](https://doc.deno.land/https/deno.land/x/harmony/mod.ts) | ||||||
|  | - [Guide](https://harmony-org.github.io) | ||||||
| 
 | 
 | ||||||
| ## Found a bug or want support? Join our discord server! | ## Found a bug or want support? Join our discord server! | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								deps.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								deps.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | ||||||
|  | export { EventEmitter } from 'https://deno.land/std@0.82.0/node/events.ts' | ||||||
|  | export { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts' | ||||||
|  | export { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts' | ||||||
|  | export { parse } from 'https://deno.land/x/mutil@0.1.2/mod.ts' | ||||||
|  | export { connect } from 'https://deno.land/x/redis@v0.14.1/mod.ts' | ||||||
|  | export type { | ||||||
|  |   Redis, | ||||||
|  |   RedisConnectOptions | ||||||
|  | } from 'https://deno.land/x/redis@v0.14.1/mod.ts' | ||||||
|  | export { | ||||||
|  |   Manager, | ||||||
|  |   Player | ||||||
|  | } from 'https://raw.githubusercontent.com/Lavaclient/lavadeno/master/mod.ts' | ||||||
							
								
								
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								mod.ts
									
										
									
									
									
								
							|  | @ -1,5 +1,4 @@ | ||||||
| export { GatewayIntents } from './src/types/gateway.ts' | 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 { 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 type { ClientEvents } from './src/gateway/handlers/index.ts' | ||||||
|  | @ -66,7 +65,7 @@ export { | ||||||
|   ActivityTypes |   ActivityTypes | ||||||
| } from './src/structures/presence.ts' | } from './src/structures/presence.ts' | ||||||
| export { Role } from './src/structures/role.ts' | export { Role } from './src/structures/role.ts' | ||||||
| export { Snowflake } from './src/structures/snowflake.ts' | export { Snowflake } from './src/utils/snowflake.ts' | ||||||
| export { TextChannel, GuildTextChannel } from './src/structures/textChannel.ts' | export { TextChannel, GuildTextChannel } from './src/structures/textChannel.ts' | ||||||
| export { MessageReaction } from './src/structures/messageReaction.ts' | export { MessageReaction } from './src/structures/messageReaction.ts' | ||||||
| export { User } from './src/structures/user.ts' | export { User } from './src/structures/user.ts' | ||||||
|  |  | ||||||
|  | @ -17,23 +17,29 @@ export const guildEmojiUpdate: GatewayEventHandler = async ( | ||||||
|     const _updated: EmojiPayload[] = [] |     const _updated: EmojiPayload[] = [] | ||||||
| 
 | 
 | ||||||
|     for (const raw of d.emojis) { |     for (const raw of d.emojis) { | ||||||
|       const has = emojis.get(raw.id) |       const emojiID = raw.id !== null ? raw.id : raw.name | ||||||
|  |       const has = emojis.get(emojiID) | ||||||
|       if (has === undefined) { |       if (has === undefined) { | ||||||
|         await guild.emojis.set(raw.id, raw) |         await guild.emojis.set(emojiID, raw) | ||||||
|         const emoji = (await guild.emojis.get(raw.id)) as Emoji |         const emoji = (await guild.emojis.get(emojiID)) as Emoji | ||||||
|         added.push(emoji) |         added.push(emoji) | ||||||
|       } else _updated.push(raw) |       } else _updated.push(raw) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (const emoji of emojis.values()) { |     for (const emoji of emojis.values()) { | ||||||
|       const find = _updated.find((e) => emoji.id === e.id) |       const emojiID = emoji.id !== null ? emoji.id : emoji.name | ||||||
|  |       const find = _updated.find((e) => { | ||||||
|  |         const eID = e.id !== null ? e.id : e.name | ||||||
|  |         return emojiID === eID | ||||||
|  |       }) | ||||||
|       if (find === undefined) { |       if (find === undefined) { | ||||||
|         await guild.emojis.delete(emoji.id) |         await guild.emojis.delete(emojiID) | ||||||
|         deleted.push(emoji) |         deleted.push(emoji) | ||||||
|       } else { |       } else { | ||||||
|         const before = (await guild.emojis.get(find.id)) as Emoji |         const foundID = find.id !== null ? find.id : find.name | ||||||
|         await guild.emojis.set(find.id, find) |         const before = (await guild.emojis.get(foundID)) as Emoji | ||||||
|         const after = (await guild.emojis.get(find.id)) as Emoji |         await guild.emojis.set(foundID, find) | ||||||
|  |         const after = (await guild.emojis.get(foundID)) as Emoji | ||||||
|         updated.push({ before, after }) |         updated.push({ before, after }) | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -29,15 +29,16 @@ export const messageReactionAdd: GatewayEventHandler = async ( | ||||||
|     } else return |     } else return | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   let reaction = await message.reactions.get(d.emoji.id) |   const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name | ||||||
|  |   let reaction = await message.reactions.get(emojiID) | ||||||
|   if (reaction === undefined) { |   if (reaction === undefined) { | ||||||
|     await message.reactions.set(d.emoji.id, { |     await message.reactions.set(emojiID, { | ||||||
|       count: 1, |       count: 1, | ||||||
|       emoji: d.emoji, |       emoji: d.emoji, | ||||||
|       me: d.user_id === gateway.client.user?.id |       me: d.user_id === gateway.client.user?.id | ||||||
|     }) |     }) | ||||||
|     reaction = ((await message.reactions.get( |     reaction = ((await message.reactions.get( | ||||||
|       d.emoji.id |       emojiID | ||||||
|     )) as unknown) as MessageReaction |     )) as unknown) as MessageReaction | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,7 +27,8 @@ export const messageReactionRemove: GatewayEventHandler = async ( | ||||||
|     } else return |     } else return | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const reaction = await message.reactions.get(d.emoji.id) |   const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name | ||||||
|  |   const reaction = await message.reactions.get(emojiID) | ||||||
|   if (reaction === undefined) return |   if (reaction === undefined) return | ||||||
| 
 | 
 | ||||||
|   reaction.users.delete(d.user_id) |   reaction.users.delete(d.user_id) | ||||||
|  |  | ||||||
|  | @ -19,7 +19,8 @@ export const messageReactionRemoveEmoji: GatewayEventHandler = async ( | ||||||
|     } else return |     } else return | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const reaction = await message.reactions.get(d.emoji.id) |   const emojiID = d.emoji.id !== null ? d.emoji.id : d.emoji.name | ||||||
|  |   const reaction = await message.reactions.get(emojiID) | ||||||
|   if (reaction === undefined) return |   if (reaction === undefined) return | ||||||
| 
 | 
 | ||||||
|   await reaction.users.flush() |   await reaction.users.flush() | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts' | import { unzlib, EventEmitter } from '../../deps.ts' | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { | import { | ||||||
|   DISCORD_GATEWAY_URL, |   DISCORD_GATEWAY_URL, | ||||||
|  | @ -18,7 +18,6 @@ import { GatewayCache } from '../managers/gatewayCache.ts' | ||||||
| import { delay } from '../utils/delay.ts' | import { delay } from '../utils/delay.ts' | ||||||
| import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | import { VoiceChannel } from '../structures/guildVoiceChannel.ts' | ||||||
| import { Guild } from '../structures/guild.ts' | import { Guild } from '../structures/guild.ts' | ||||||
| import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' |  | ||||||
| 
 | 
 | ||||||
| export interface RequestMembersOptions { | export interface RequestMembersOptions { | ||||||
|   limit?: number |   limit?: number | ||||||
|  | @ -177,55 +176,61 @@ export class Gateway extends EventEmitter { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async onclose(event: CloseEvent): Promise<void> { |   private async onclose({ reason, code }: CloseEvent): Promise<void> { | ||||||
|     if (event.reason === RECONNECT_REASON) return |     if (reason === RECONNECT_REASON) return | ||||||
|     this.emit('close', event.code, event.reason) |     this.emit('close', code, reason) | ||||||
|     this.debug(`Connection Closed with code: ${event.code}`) |     this.debug(`Connection Closed with code: ${code}`) | ||||||
| 
 | 
 | ||||||
|     if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) { |     switch (code) { | ||||||
|       this.debug('API has encountered Unknown Error. Reconnecting...') |       case GatewayCloseCodes.UNKNOWN_ERROR: | ||||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 |         this.debug('API has encountered Unknown Error. Reconnecting...') | ||||||
|       this.reconnect() |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|     } else if (event.code === GatewayCloseCodes.UNKNOWN_OPCODE) { |         this.reconnect() | ||||||
|       throw new Error("Unknown OP Code was sent. This shouldn't happen!") |         break | ||||||
|     } else if (event.code === GatewayCloseCodes.DECODE_ERROR) { |       case GatewayCloseCodes.UNKNOWN_OPCODE: | ||||||
|       throw new Error("Invalid Payload was sent. This shouldn't happen!") |         throw new Error("Unknown OP Code was sent. This shouldn't happen!") | ||||||
|     } else if (event.code === GatewayCloseCodes.NOT_AUTHENTICATED) { |       case GatewayCloseCodes.DECODE_ERROR: | ||||||
|       throw new Error('Not Authorized: Payload was sent before Identifying.') |         throw new Error("Invalid Payload was sent. This shouldn't happen!") | ||||||
|     } else if (event.code === GatewayCloseCodes.AUTHENTICATION_FAILED) { |       case GatewayCloseCodes.NOT_AUTHENTICATED: | ||||||
|       throw new Error('Invalid Token provided!') |         throw new Error('Not Authorized: Payload was sent before Identifying.') | ||||||
|     } else if (event.code === GatewayCloseCodes.INVALID_SEQ) { |       case GatewayCloseCodes.AUTHENTICATION_FAILED: | ||||||
|       this.debug('Invalid Seq was sent. Reconnecting.') |         throw new Error('Invalid Token provided!') | ||||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 |       case GatewayCloseCodes.INVALID_SEQ: | ||||||
|       this.reconnect() |         this.debug('Invalid Seq was sent. Reconnecting.') | ||||||
|     } else if (event.code === GatewayCloseCodes.RATE_LIMITED) { |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       throw new Error("You're ratelimited. Calm down.") |         this.reconnect() | ||||||
|     } else if (event.code === GatewayCloseCodes.SESSION_TIMED_OUT) { |         break | ||||||
|       this.debug('Session Timeout. Reconnecting.') |       case GatewayCloseCodes.RATE_LIMITED: | ||||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 |         throw new Error("You're ratelimited. Calm down.") | ||||||
|       this.reconnect(true) |       case GatewayCloseCodes.SESSION_TIMED_OUT: | ||||||
|     } else if (event.code === GatewayCloseCodes.INVALID_SHARD) { |         this.debug('Session Timeout. Reconnecting.') | ||||||
|       this.debug('Invalid Shard was sent. Reconnecting.') |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises
 |         this.reconnect(true) | ||||||
|       this.reconnect() |         break | ||||||
|     } else if (event.code === GatewayCloseCodes.SHARDING_REQUIRED) { |       case GatewayCloseCodes.INVALID_SHARD: | ||||||
|       throw new Error("Couldn't connect. Sharding is required!") |         this.debug('Invalid Shard was sent. Reconnecting.') | ||||||
|     } else if (event.code === GatewayCloseCodes.INVALID_API_VERSION) { |         // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|       throw new Error("Invalid API Version was used. This shouldn't happen!") |         this.reconnect() | ||||||
|     } else if (event.code === GatewayCloseCodes.INVALID_INTENTS) { |         break | ||||||
|       throw new Error('Invalid Intents') |       case GatewayCloseCodes.SHARDING_REQUIRED: | ||||||
|     } else if (event.code === GatewayCloseCodes.DISALLOWED_INTENTS) { |         throw new Error("Couldn't connect. Sharding is required!") | ||||||
|       throw new Error("Given Intents aren't allowed") |       case GatewayCloseCodes.INVALID_API_VERSION: | ||||||
|     } else { |         throw new Error("Invalid API Version was used. This shouldn't happen!") | ||||||
|       this.debug( |       case GatewayCloseCodes.INVALID_INTENTS: | ||||||
|         'Unknown Close code, probably connection error. Reconnecting in 5s.' |         throw new Error('Invalid Intents') | ||||||
|       ) |       case GatewayCloseCodes.DISALLOWED_INTENTS: | ||||||
|       if (this.timedIdentify !== null) { |         throw new Error("Given Intents aren't allowed") | ||||||
|         clearTimeout(this.timedIdentify) |       default: | ||||||
|         this.debug('Timed Identify found. Cleared timeout.') |         this.debug( | ||||||
|       } |           'Unknown Close code, probably connection error. Reconnecting in 5s.' | ||||||
|       await delay(5000) |         ) | ||||||
|       await this.reconnect(true) |         if (this.timedIdentify !== null) { | ||||||
|  |           clearTimeout(this.timedIdentify) | ||||||
|  |           this.debug('Timed Identify found. Cleared timeout.') | ||||||
|  |         } | ||||||
|  |         await delay(5000) | ||||||
|  |         await this.reconnect(true) | ||||||
|  |         break | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import { EmojiPayload } from '../types/emoji.ts' | ||||||
| import { CHANNEL, GUILD_EMOJI, GUILD_EMOJIS } from '../types/endpoint.ts' | import { CHANNEL, GUILD_EMOJI, GUILD_EMOJIS } from '../types/endpoint.ts' | ||||||
| import { BaseChildManager } from './baseChild.ts' | import { BaseChildManager } from './baseChild.ts' | ||||||
| import { EmojisManager } from './emojis.ts' | import { EmojisManager } from './emojis.ts' | ||||||
| import { fetchAuto } from 'https://raw.githubusercontent.com/DjDeveloperr/fetch-base64/main/mod.ts' | import { fetchAuto } from '../../deps.ts' | ||||||
| 
 | 
 | ||||||
| export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> { | export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> { | ||||||
|   guild: Guild |   guild: Guild | ||||||
|  | @ -87,7 +87,8 @@ export class GuildEmojisManager extends BaseChildManager<EmojiPayload, Emoji> { | ||||||
|   async flush(): Promise<boolean> { |   async flush(): Promise<boolean> { | ||||||
|     const arr = await this.array() |     const arr = await this.array() | ||||||
|     for (const elem of arr) { |     for (const elem of arr) { | ||||||
|       this.parent.delete(elem.id) |       const emojiID = elem.id !== null ? elem.id : elem.name | ||||||
|  |       this.parent.delete(emojiID) | ||||||
|     } |     } | ||||||
|     return true |     return true | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -21,7 +21,9 @@ export class MessageReactionsManager extends BaseManager< | ||||||
|     const raw = await this._get(id) |     const raw = await this._get(id) | ||||||
|     if (raw === undefined) return |     if (raw === undefined) return | ||||||
| 
 | 
 | ||||||
|     let emoji = await this.client.emojis.get(raw.emoji.id) |     const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name | ||||||
|  | 
 | ||||||
|  |     let emoji = await this.client.emojis.get(emojiID) | ||||||
|     if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) |     if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) | ||||||
| 
 | 
 | ||||||
|     const reaction = new MessageReaction(this.client, raw, this.message, emoji) |     const reaction = new MessageReaction(this.client, raw, this.message, emoji) | ||||||
|  | @ -43,7 +45,8 @@ export class MessageReactionsManager extends BaseManager< | ||||||
| 
 | 
 | ||||||
|     return await Promise.all( |     return await Promise.all( | ||||||
|       arr.map(async (raw) => { |       arr.map(async (raw) => { | ||||||
|         let emoji = await this.client.emojis.get(raw.emoji.id) |         const emojiID = raw.emoji.id !== null ? raw.emoji.id : raw.emoji.name | ||||||
|  |         let emoji = await this.client.emojis.get(emojiID) | ||||||
|         if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) |         if (emoji === undefined) emoji = new Emoji(this.client, raw.emoji) | ||||||
| 
 | 
 | ||||||
|         return new MessageReaction(this.client, raw, this.message, emoji) |         return new MessageReaction(this.client, raw, this.message, emoji) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ import { | ||||||
|   connect, |   connect, | ||||||
|   Redis, |   Redis, | ||||||
|   RedisConnectOptions |   RedisConnectOptions | ||||||
| } from 'https://denopkg.com/keroxp/deno-redis/mod.ts' | } from '../../deps.ts' | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * ICacheAdapter is the interface to be implemented by Cache Adapters for them to be usable with Harmony. |  * ICacheAdapter is the interface to be implemented by Cache Adapters for them to be usable with Harmony. | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ import { User } from '../structures/user.ts' | ||||||
| import { GatewayIntents } from '../types/gateway.ts' | import { GatewayIntents } from '../types/gateway.ts' | ||||||
| import { Gateway } from '../gateway/index.ts' | import { Gateway } from '../gateway/index.ts' | ||||||
| import { RESTManager } from './rest.ts' | import { RESTManager } from './rest.ts' | ||||||
| import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' | import { EventEmitter } from '../../deps.ts' | ||||||
| import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts' | import { DefaultCacheAdapter, ICacheAdapter } from './cacheAdapter.ts' | ||||||
| import { UsersManager } from '../managers/users.ts' | import { UsersManager } from '../managers/users.ts' | ||||||
| import { GuildManager } from '../managers/guilds.ts' | import { GuildManager } from '../managers/guilds.ts' | ||||||
|  | @ -247,6 +247,7 @@ export function event(name?: string) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** Decorator to create a Slash Command handler */ | ||||||
| export function slash(name?: string, guild?: string) { | export function slash(name?: string, guild?: string) { | ||||||
|   return function (client: Client | SlashModule, prop: string) { |   return function (client: Client | SlashModule, prop: string) { | ||||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] |     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||||
|  | @ -262,6 +263,7 @@ export function slash(name?: string, guild?: string) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** Decorator to create a Sub-Slash Command handler */ | ||||||
| export function subslash(parent: string, name?: string, guild?: string) { | export function subslash(parent: string, name?: string, guild?: string) { | ||||||
|   return function (client: Client | SlashModule, prop: string) { |   return function (client: Client | SlashModule, prop: string) { | ||||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] |     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||||
|  | @ -279,13 +281,14 @@ export function subslash(parent: string, name?: string, guild?: string) { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** Decorator to create a Grouped Slash Command handler */ | ||||||
| export function groupslash( | export function groupslash( | ||||||
|   parent: string, |   parent: string, | ||||||
|   group: string, |   group: string, | ||||||
|   name?: string, |   name?: string, | ||||||
|   guild?: string |   guild?: string | ||||||
| ) { | ) { | ||||||
|   return function (client: Client | SlashModule, prop: string) { |   return function (client: Client | SlashModule | SlashClient, prop: string) { | ||||||
|     if (client._decoratedSlash === undefined) client._decoratedSlash = [] |     if (client._decoratedSlash === undefined) client._decoratedSlash = [] | ||||||
|     const item = (client as { [name: string]: any })[prop] |     const item = (client as { [name: string]: any })[prop] | ||||||
|     if (typeof item !== 'function') { |     if (typeof item !== 'function') { | ||||||
|  | @ -303,6 +306,7 @@ export function groupslash( | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** Decorator to add a Slash Module to Client */ | ||||||
| export function slashModule() { | export function slashModule() { | ||||||
|   return function (client: Client, prop: string) { |   return function (client: Client, prop: string) { | ||||||
|     if (client._decoratedSlashModules === undefined) |     if (client._decoratedSlashModules === undefined) | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ import { User } from '../structures/user.ts' | ||||||
| import { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| import { CommandClient } from './commandClient.ts' | import { CommandClient } from './commandClient.ts' | ||||||
| import { Extension } from './extensions.ts' | import { Extension } from './extensions.ts' | ||||||
| import { parse } from 'https://deno.land/x/mutil@0.1.2/mod.ts' | import { parse } from '../../deps.ts' | ||||||
| 
 | 
 | ||||||
| export interface CommandContext { | export interface CommandContext { | ||||||
|   /** The Client object */ |   /** The Client object */ | ||||||
|  |  | ||||||
|  | @ -97,6 +97,7 @@ export interface RESTOptions { | ||||||
|   token?: string |   token?: string | ||||||
|   headers?: { [name: string]: string | undefined } |   headers?: { [name: string]: string | undefined } | ||||||
|   canary?: boolean |   canary?: boolean | ||||||
|  |   version?: 6 | 7 | 8 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class RESTManager { | export class RESTManager { | ||||||
|  | @ -111,6 +112,7 @@ export class RESTManager { | ||||||
|   constructor(client?: RESTOptions) { |   constructor(client?: RESTOptions) { | ||||||
|     this.client = client |     this.client = client | ||||||
|     this.api = builder(this) |     this.api = builder(this) | ||||||
|  |     if (client?.version !== undefined) this.version = client.version | ||||||
|     // eslint-disable-next-line @typescript-eslint/no-floating-promises
 |     // eslint-disable-next-line @typescript-eslint/no-floating-promises
 | ||||||
|     this.handleRateLimits() |     this.handleRateLimits() | ||||||
|   } |   } | ||||||
|  | @ -408,6 +410,7 @@ export class RESTManager { | ||||||
|           const query = |           const query = | ||||||
|             method === 'get' && body !== undefined |             method === 'get' && body !== undefined | ||||||
|               ? Object.entries(body as any) |               ? Object.entries(body as any) | ||||||
|  |                   .filter(([k, v]) => v !== undefined) | ||||||
|                   .map( |                   .map( | ||||||
|                     ([key, value]) => |                     ([key, value]) => | ||||||
|                       `${encodeURIComponent(key)}=${encodeURIComponent( |                       `${encodeURIComponent(key)}=${encodeURIComponent( | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| import { Client, ClientOptions } from './client.ts' | import { Client, ClientOptions } from './client.ts' | ||||||
| import EventEmitter from 'https://deno.land/std@0.74.0/node/events.ts' | import {EventEmitter} from '../../deps.ts' | ||||||
| import { RESTManager } from './rest.ts' | import { RESTManager } from './rest.ts' | ||||||
| // import { GATEWAY_BOT } from '../types/endpoint.ts'
 | // import { GATEWAY_BOT } from '../types/endpoint.ts'
 | ||||||
| // import { GatewayBotPayload } from '../types/gatewayBot.ts'
 | // import { GatewayBotPayload } from '../types/gatewayBot.ts'
 | ||||||
|  |  | ||||||
|  | @ -1,20 +1,24 @@ | ||||||
| import { Guild } from '../structures/guild.ts' | import { Guild } from '../structures/guild.ts' | ||||||
| import { Interaction } from '../structures/slash.ts' | import { Interaction } from '../structures/slash.ts' | ||||||
| import { |  | ||||||
|   APPLICATION_COMMAND, |  | ||||||
|   APPLICATION_COMMANDS, |  | ||||||
|   APPLICATION_GUILD_COMMAND, |  | ||||||
|   APPLICATION_GUILD_COMMANDS |  | ||||||
| } from '../types/endpoint.ts' |  | ||||||
| import { | import { | ||||||
|   InteractionType, |   InteractionType, | ||||||
|  |   SlashCommandChoice, | ||||||
|   SlashCommandOption, |   SlashCommandOption, | ||||||
|  |   SlashCommandOptionType, | ||||||
|   SlashCommandPartial, |   SlashCommandPartial, | ||||||
|   SlashCommandPayload |   SlashCommandPayload | ||||||
| } from '../types/slash.ts' | } from '../types/slash.ts' | ||||||
| import { Collection } from '../utils/collection.ts' | import { Collection } from '../utils/collection.ts' | ||||||
| import { Client } from './client.ts' | import { Client } from './client.ts' | ||||||
| import { RESTManager } from './rest.ts' | import { RESTManager } from './rest.ts' | ||||||
|  | import { SlashModule } from './slashModule.ts' | ||||||
|  | import { verify as edverify } from 'https://deno.land/x/ed25519/mod.ts' | ||||||
|  | import { Buffer } from 'https://deno.land/std@0.80.0/node/buffer.ts' | ||||||
|  | import { | ||||||
|  |   Request as ORequest, | ||||||
|  |   Response as OResponse | ||||||
|  | } from 'https://deno.land/x/opine@1.0.0/src/types.ts' | ||||||
|  | import { Context } from 'https://deno.land/x/oak@v6.4.0/mod.ts' | ||||||
| 
 | 
 | ||||||
| export class SlashCommand { | export class SlashCommand { | ||||||
|   slash: SlashCommandsManager |   slash: SlashCommandsManager | ||||||
|  | @ -41,6 +45,158 @@ export class SlashCommand { | ||||||
|   async edit(data: SlashCommandPartial): Promise<void> { |   async edit(data: SlashCommandPartial): Promise<void> { | ||||||
|     await this.slash.edit(this.id, data, this._guild) |     await this.slash.edit(this.id, data, this._guild) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /** Create a handler for this Slash Command */ | ||||||
|  |   handle( | ||||||
|  |     func: SlashCommandHandlerCallback, | ||||||
|  |     options?: { parent?: string; group?: string } | ||||||
|  |   ): SlashCommand { | ||||||
|  |     this.slash.slash.handle({ | ||||||
|  |       name: this.name, | ||||||
|  |       parent: options?.parent, | ||||||
|  |       group: options?.group, | ||||||
|  |       guild: this._guild, | ||||||
|  |       handler: func | ||||||
|  |     }) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export interface CreateOptions { | ||||||
|  |   name: string | ||||||
|  |   description?: string | ||||||
|  |   options?: Array<SlashCommandOption | SlashOptionCallable> | ||||||
|  |   choices?: Array<SlashCommandChoice | string> | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function createSlashOption( | ||||||
|  |   type: SlashCommandOptionType, | ||||||
|  |   data: CreateOptions | ||||||
|  | ): SlashCommandOption { | ||||||
|  |   return { | ||||||
|  |     name: data.name, | ||||||
|  |     type, | ||||||
|  |     description: | ||||||
|  |       type === 0 || type === 1 | ||||||
|  |         ? undefined | ||||||
|  |         : data.description ?? 'No description.', | ||||||
|  |     options: data.options?.map((e) => | ||||||
|  |       typeof e === 'function' ? e(SlashOption) : e | ||||||
|  |     ), | ||||||
|  |     choices: | ||||||
|  |       data.choices === undefined | ||||||
|  |         ? undefined | ||||||
|  |         : data.choices.map((e) => | ||||||
|  |             typeof e === 'string' ? { name: e, value: e } : e | ||||||
|  |           ) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // eslint-disable-next-line @typescript-eslint/no-extraneous-class
 | ||||||
|  | export class SlashOption { | ||||||
|  |   static string(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.STRING, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static bool(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.BOOLEAN, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static subCommand(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.SUB_COMMAND, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static subCommandGroup(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.SUB_COMMAND_GROUP, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static role(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.ROLE, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static channel(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.CHANNEL, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static user(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.USER, data) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static number(data: CreateOptions): SlashCommandOption { | ||||||
|  |     return createSlashOption(SlashCommandOptionType.INTEGER, data) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export type SlashOptionCallable = (o: typeof SlashOption) => SlashCommandOption | ||||||
|  | 
 | ||||||
|  | export type SlashBuilderOptionsData = | ||||||
|  |   | Array<SlashCommandOption | SlashOptionCallable> | ||||||
|  |   | { | ||||||
|  |       [name: string]: | ||||||
|  |         | { | ||||||
|  |             description: string | ||||||
|  |             type: SlashCommandOptionType | ||||||
|  |             options?: SlashCommandOption[] | ||||||
|  |             choices?: SlashCommandChoice[] | ||||||
|  |           } | ||||||
|  |         | SlashOptionCallable | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | function buildOptionsArray( | ||||||
|  |   options: SlashBuilderOptionsData | ||||||
|  | ): SlashCommandOption[] { | ||||||
|  |   return Array.isArray(options) | ||||||
|  |     ? options.map((op) => (typeof op === 'function' ? op(SlashOption) : op)) | ||||||
|  |     : Object.entries(options).map((entry) => | ||||||
|  |         typeof entry[1] === 'function' | ||||||
|  |           ? entry[1](SlashOption) | ||||||
|  |           : Object.assign(entry[1], { name: entry[0] }) | ||||||
|  |       ) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export class SlashBuilder { | ||||||
|  |   data: SlashCommandPartial | ||||||
|  | 
 | ||||||
|  |   constructor( | ||||||
|  |     name?: string, | ||||||
|  |     description?: string, | ||||||
|  |     options?: SlashBuilderOptionsData | ||||||
|  |   ) { | ||||||
|  |     this.data = { | ||||||
|  |       name: name ?? '', | ||||||
|  |       description: description ?? 'No description.', | ||||||
|  |       options: options === undefined ? [] : buildOptionsArray(options) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   name(name: string): SlashBuilder { | ||||||
|  |     this.data.name = name | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   description(desc: string): SlashBuilder { | ||||||
|  |     this.data.description = desc | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   option(option: SlashOptionCallable | SlashCommandOption): SlashBuilder { | ||||||
|  |     if (this.data.options === undefined) this.data.options = [] | ||||||
|  |     this.data.options.push( | ||||||
|  |       typeof option === 'function' ? option(SlashOption) : option | ||||||
|  |     ) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   options(options: SlashBuilderOptionsData): SlashBuilder { | ||||||
|  |     this.data.options = buildOptionsArray(options) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   export(): SlashCommandPartial { | ||||||
|  |     if (this.data.name === '') | ||||||
|  |       throw new Error('Name was not provided in Slash Builder') | ||||||
|  |     return this.data | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class SlashCommandsManager { | export class SlashCommandsManager { | ||||||
|  | @ -58,9 +214,9 @@ export class SlashCommandsManager { | ||||||
|   async all(): Promise<Collection<string, SlashCommand>> { |   async all(): Promise<Collection<string, SlashCommand>> { | ||||||
|     const col = new Collection<string, SlashCommand>() |     const col = new Collection<string, SlashCommand>() | ||||||
| 
 | 
 | ||||||
|     const res = (await this.rest.get( |     const res = (await this.rest.api.applications[ | ||||||
|       APPLICATION_COMMANDS(this.slash.getID()) |       this.slash.getID() | ||||||
|     )) as SlashCommandPayload[] |     ].commands.get()) as SlashCommandPayload[] | ||||||
|     if (!Array.isArray(res)) return col |     if (!Array.isArray(res)) return col | ||||||
| 
 | 
 | ||||||
|     for (const raw of res) { |     for (const raw of res) { | ||||||
|  | @ -77,12 +233,9 @@ export class SlashCommandsManager { | ||||||
|   ): Promise<Collection<string, SlashCommand>> { |   ): Promise<Collection<string, SlashCommand>> { | ||||||
|     const col = new Collection<string, SlashCommand>() |     const col = new Collection<string, SlashCommand>() | ||||||
| 
 | 
 | ||||||
|     const res = (await this.rest.get( |     const res = (await this.rest.api.applications[this.slash.getID()].guilds[ | ||||||
|       APPLICATION_GUILD_COMMANDS( |       typeof guild === 'string' ? guild : guild.id | ||||||
|         this.slash.getID(), |     ].commands.get()) as SlashCommandPayload[] | ||||||
|         typeof guild === 'string' ? guild : guild.id |  | ||||||
|       ) |  | ||||||
|     )) as SlashCommandPayload[] |  | ||||||
|     if (!Array.isArray(res)) return col |     if (!Array.isArray(res)) return col | ||||||
| 
 | 
 | ||||||
|     for (const raw of res) { |     for (const raw of res) { | ||||||
|  | @ -99,15 +252,14 @@ export class SlashCommandsManager { | ||||||
|     data: SlashCommandPartial, |     data: SlashCommandPartial, | ||||||
|     guild?: Guild | string |     guild?: Guild | string | ||||||
|   ): Promise<SlashCommand> { |   ): Promise<SlashCommand> { | ||||||
|     const payload = await this.rest.post( |     const route = | ||||||
|       guild === undefined |       guild === undefined | ||||||
|         ? APPLICATION_COMMANDS(this.slash.getID()) |         ? this.rest.api.applications[this.slash.getID()].commands | ||||||
|         : APPLICATION_GUILD_COMMANDS( |         : this.rest.api.applications[this.slash.getID()].guilds[ | ||||||
|             this.slash.getID(), |  | ||||||
|             typeof guild === 'string' ? guild : guild.id |             typeof guild === 'string' ? guild : guild.id | ||||||
|           ), |           ].commands | ||||||
|       data | 
 | ||||||
|     ) |     const payload = await route.post(data) | ||||||
| 
 | 
 | ||||||
|     const cmd = new SlashCommand(this, payload) |     const cmd = new SlashCommand(this, payload) | ||||||
|     cmd._guild = |     cmd._guild = | ||||||
|  | @ -122,16 +274,14 @@ export class SlashCommandsManager { | ||||||
|     data: SlashCommandPartial, |     data: SlashCommandPartial, | ||||||
|     guild?: Guild | string |     guild?: Guild | string | ||||||
|   ): Promise<SlashCommandsManager> { |   ): Promise<SlashCommandsManager> { | ||||||
|     await this.rest.patch( |     const route = | ||||||
|       guild === undefined |       guild === undefined | ||||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) |         ? this.rest.api.applications[this.slash.getID()].commands[id] | ||||||
|         : APPLICATION_GUILD_COMMAND( |         : this.rest.api.applications[this.slash.getID()].guilds[ | ||||||
|             this.slash.getID(), |             typeof guild === 'string' ? guild : guild.id | ||||||
|             typeof guild === 'string' ? guild : guild.id, |           ].commands[id] | ||||||
|             id | 
 | ||||||
|           ), |     await route.patch(data) | ||||||
|       data |  | ||||||
|     ) |  | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -140,29 +290,28 @@ export class SlashCommandsManager { | ||||||
|     id: string, |     id: string, | ||||||
|     guild?: Guild | string |     guild?: Guild | string | ||||||
|   ): Promise<SlashCommandsManager> { |   ): Promise<SlashCommandsManager> { | ||||||
|     await this.rest.delete( |     const route = | ||||||
|       guild === undefined |       guild === undefined | ||||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) |         ? this.rest.api.applications[this.slash.getID()].commands[id] | ||||||
|         : APPLICATION_GUILD_COMMAND( |         : this.rest.api.applications[this.slash.getID()].guilds[ | ||||||
|             this.slash.getID(), |             typeof guild === 'string' ? guild : guild.id | ||||||
|             typeof guild === 'string' ? guild : guild.id, |           ].commands[id] | ||||||
|             id | 
 | ||||||
|           ) |     await route.delete() | ||||||
|     ) |  | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Get a Slash Command (global or Guild) */ |   /** Get a Slash Command (global or Guild) */ | ||||||
|   async get(id: string, guild?: Guild | string): Promise<SlashCommand> { |   async get(id: string, guild?: Guild | string): Promise<SlashCommand> { | ||||||
|     const data = await this.rest.get( |     const route = | ||||||
|       guild === undefined |       guild === undefined | ||||||
|         ? APPLICATION_COMMAND(this.slash.getID(), id) |         ? this.rest.api.applications[this.slash.getID()].commands[id] | ||||||
|         : APPLICATION_GUILD_COMMAND( |         : this.rest.api.applications[this.slash.getID()].guilds[ | ||||||
|             this.slash.getID(), |             typeof guild === 'string' ? guild : guild.id | ||||||
|             typeof guild === 'string' ? guild : guild.id, |           ].commands[id] | ||||||
|             id | 
 | ||||||
|           ) |     const data = await route.get() | ||||||
|     ) | 
 | ||||||
|     return new SlashCommand(this, data) |     return new SlashCommand(this, data) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | @ -182,6 +331,7 @@ export interface SlashOptions { | ||||||
|   enabled?: boolean |   enabled?: boolean | ||||||
|   token?: string |   token?: string | ||||||
|   rest?: RESTManager |   rest?: RESTManager | ||||||
|  |   publicKey?: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class SlashClient { | export class SlashClient { | ||||||
|  | @ -192,6 +342,18 @@ export class SlashClient { | ||||||
|   commands: SlashCommandsManager |   commands: SlashCommandsManager | ||||||
|   handlers: SlashCommandHandler[] = [] |   handlers: SlashCommandHandler[] = [] | ||||||
|   rest: RESTManager |   rest: RESTManager | ||||||
|  |   modules: SlashModule[] = [] | ||||||
|  |   publicKey?: string | ||||||
|  | 
 | ||||||
|  |   _decoratedSlash?: Array<{ | ||||||
|  |     name: string | ||||||
|  |     guild?: string | ||||||
|  |     parent?: string | ||||||
|  |     group?: string | ||||||
|  |     handler: (interaction: Interaction) => any | ||||||
|  |   }> | ||||||
|  | 
 | ||||||
|  |   _decoratedSlashModules?: SlashModule[] | ||||||
| 
 | 
 | ||||||
|   constructor(options: SlashOptions) { |   constructor(options: SlashOptions) { | ||||||
|     let id = options.id |     let id = options.id | ||||||
|  | @ -202,6 +364,7 @@ export class SlashClient { | ||||||
|     this.client = options.client |     this.client = options.client | ||||||
|     this.token = options.token |     this.token = options.token | ||||||
|     this.commands = new SlashCommandsManager(this) |     this.commands = new SlashCommandsManager(this) | ||||||
|  |     this.publicKey = options.publicKey | ||||||
| 
 | 
 | ||||||
|     if (options !== undefined) { |     if (options !== undefined) { | ||||||
|       this.enabled = options.enabled ?? true |       this.enabled = options.enabled ?? true | ||||||
|  | @ -213,6 +376,24 @@ export class SlashClient { | ||||||
|       }) |       }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (this.client?._decoratedSlashModules !== undefined) { | ||||||
|  |       this.client._decoratedSlashModules.forEach((e) => { | ||||||
|  |         this.modules.push(e) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (this._decoratedSlash !== undefined) { | ||||||
|  |       this._decoratedSlash.forEach((e) => { | ||||||
|  |         this.handlers.push(e) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (this._decoratedSlashModules !== undefined) { | ||||||
|  |       this._decoratedSlashModules.forEach((e) => { | ||||||
|  |         this.modules.push(e) | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     this.rest = |     this.rest = | ||||||
|       options.client === undefined |       options.client === undefined | ||||||
|         ? options.rest === undefined |         ? options.rest === undefined | ||||||
|  | @ -237,8 +418,28 @@ export class SlashClient { | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   loadModule(module: SlashModule): SlashClient { | ||||||
|  |     this.modules.push(module) | ||||||
|  |     return this | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getHandlers(): SlashCommandHandler[] { | ||||||
|  |     let res = this.handlers | ||||||
|  |     for (const mod of this.modules) { | ||||||
|  |       if (mod === undefined) continue | ||||||
|  |       res = [ | ||||||
|  |         ...res, | ||||||
|  |         ...mod.commands.map((cmd) => { | ||||||
|  |           cmd.handler = cmd.handler.bind(mod) | ||||||
|  |           return cmd | ||||||
|  |         }) | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|  |     return res | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private _getCommand(i: Interaction): SlashCommandHandler | undefined { |   private _getCommand(i: Interaction): SlashCommandHandler | undefined { | ||||||
|     return this.handlers.find((e) => { |     return this.getHandlers().find((e) => { | ||||||
|       const hasGroupOrParent = e.group !== undefined || e.parent !== undefined |       const hasGroupOrParent = e.group !== undefined || e.parent !== undefined | ||||||
|       const groupMatched = |       const groupMatched = | ||||||
|         e.group !== undefined && e.parent !== undefined |         e.group !== undefined && e.parent !== undefined | ||||||
|  | @ -271,4 +472,78 @@ export class SlashClient { | ||||||
| 
 | 
 | ||||||
|     cmd.handler(interaction) |     cmd.handler(interaction) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async verifyKey( | ||||||
|  |     rawBody: string | Uint8Array | Buffer, | ||||||
|  |     signature: string, | ||||||
|  |     timestamp: string | ||||||
|  |   ): Promise<boolean> { | ||||||
|  |     if (this.publicKey === undefined) | ||||||
|  |       throw new Error('Public Key is not present') | ||||||
|  |     return edverify( | ||||||
|  |       signature, | ||||||
|  |       Buffer.concat([ | ||||||
|  |         Buffer.from(timestamp, 'utf-8'), | ||||||
|  |         Buffer.from( | ||||||
|  |           rawBody instanceof Uint8Array | ||||||
|  |             ? new TextDecoder().decode(rawBody) | ||||||
|  |             : rawBody | ||||||
|  |         ) | ||||||
|  |       ]), | ||||||
|  |       this.publicKey | ||||||
|  |     ).catch(() => false) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async verifyOpineRequest(req: ORequest): Promise<boolean> { | ||||||
|  |     const signature = req.headers.get('x-signature-ed25519') | ||||||
|  |     const timestamp = req.headers.get('x-signature-timestamp') | ||||||
|  |     const contentLength = req.headers.get('content-length') | ||||||
|  | 
 | ||||||
|  |     if (signature === null || timestamp === null || contentLength === null) | ||||||
|  |       return false | ||||||
|  | 
 | ||||||
|  |     const body = new Uint8Array(parseInt(contentLength)) | ||||||
|  |     await req.body.read(body) | ||||||
|  | 
 | ||||||
|  |     const verified = await this.verifyKey(body, signature, timestamp) | ||||||
|  |     if (!verified) return false | ||||||
|  | 
 | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** Middleware to verify request in Opine framework. */ | ||||||
|  |   async verifyOpineMiddleware( | ||||||
|  |     req: ORequest, | ||||||
|  |     res: OResponse, | ||||||
|  |     next: CallableFunction | ||||||
|  |   ): Promise<any> { | ||||||
|  |     const verified = await this.verifyOpineRequest(req) | ||||||
|  |     if (!verified) return res.setStatus(401).end() | ||||||
|  | 
 | ||||||
|  |     await next() | ||||||
|  |     return true | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // TODO: create verifyOakMiddleware too
 | ||||||
|  |   /** Method to verify Request from Oak server "Context". */ | ||||||
|  |   async verifyOakRequest(ctx: Context): Promise<any> { | ||||||
|  |     const signature = ctx.request.headers.get('x-signature-ed25519') | ||||||
|  |     const timestamp = ctx.request.headers.get('x-signature-timestamp') | ||||||
|  |     const contentLength = ctx.request.headers.get('content-length') | ||||||
|  | 
 | ||||||
|  |     if ( | ||||||
|  |       signature === null || | ||||||
|  |       timestamp === null || | ||||||
|  |       contentLength === null || | ||||||
|  |       ctx.request.hasBody !== true | ||||||
|  |     ) { | ||||||
|  |       return false | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const body = await ctx.request.body().value | ||||||
|  | 
 | ||||||
|  |     const verified = await this.verifyKey(body as any, signature, timestamp) | ||||||
|  |     if (!verified) return false | ||||||
|  |     return true | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,9 +18,13 @@ export class Emoji extends Base { | ||||||
|   available?: boolean |   available?: boolean | ||||||
| 
 | 
 | ||||||
|   get getEmojiString(): string { |   get getEmojiString(): string { | ||||||
|     if (this.animated === false) { |     if (this.id === null) { | ||||||
|       return `<:${this.name}:${this.id}>` |       return this.name as string; | ||||||
|     } else return `<a:${this.name}:${this.id}>` |     } else { | ||||||
|  |       if (this.animated === false) { | ||||||
|  |         return `<:${this.name}:${this.id}>` | ||||||
|  |       } else return `<a:${this.name}:${this.id}>` | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toString(): string { |   toString(): string { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import { Client } from '../models/client.ts' | import { Client } from '../models/client.ts' | ||||||
| import { ChannelPayload } from '../types/channel.ts' | import { ChannelPayload } from '../types/channel.ts' | ||||||
|  | import { INVITE } from '../types/endpoint.ts' | ||||||
| import { GuildPayload } from '../types/guild.ts' | import { GuildPayload } from '../types/guild.ts' | ||||||
| import { InvitePayload } from '../types/invite.ts' | import { InvitePayload } from '../types/invite.ts' | ||||||
| import { UserPayload } from '../types/user.ts' | import { UserPayload } from '../types/user.ts' | ||||||
|  | @ -31,6 +32,12 @@ export class Invite extends Base { | ||||||
|     this.approximatePresenceCount = data.approximate_presence_count |     this.approximatePresenceCount = data.approximate_presence_count | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /** Delete an invite. Requires the MANAGE_CHANNELS permission on the channel this invite belongs to, or MANAGE_GUILD to remove any invite across the guild. Returns an invite object on success. Fires a Invite Delete Gateway event. */ | ||||||
|  |   async delete(): Promise<Invite> { | ||||||
|  |     const res = await this.client.rest.delete(INVITE(this.code)) | ||||||
|  |     return new Invite(this.client, res) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   readFromData(data: InvitePayload): void { |   readFromData(data: InvitePayload): void { | ||||||
|     this.code = data.code ?? this.code |     this.code = data.code ?? this.code | ||||||
|     this.guild = data.guild ?? this.guild |     this.guild = data.guild ?? this.guild | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import { TextChannel } from './textChannel.ts' | ||||||
| import { Guild } from './guild.ts' | import { Guild } from './guild.ts' | ||||||
| import { MessageReactionsManager } from '../managers/messageReactions.ts' | import { MessageReactionsManager } from '../managers/messageReactions.ts' | ||||||
| import { MessageSticker } from './messageSticker.ts' | import { MessageSticker } from './messageSticker.ts' | ||||||
|  | import { Emoji } from './emoji.ts' | ||||||
| 
 | 
 | ||||||
| type AllMessageOptions = MessageOption | Embed | type AllMessageOptions = MessageOption | Embed | ||||||
| 
 | 
 | ||||||
|  | @ -46,6 +47,10 @@ export class Message extends Base { | ||||||
|   flags?: number |   flags?: number | ||||||
|   stickers?: MessageSticker[] |   stickers?: MessageSticker[] | ||||||
| 
 | 
 | ||||||
|  |   get createdAt(): Date { | ||||||
|  |     return new Date(this.timestamp) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   constructor( |   constructor( | ||||||
|     client: Client, |     client: Client, | ||||||
|     data: MessagePayload, |     data: MessagePayload, | ||||||
|  | @ -108,25 +113,52 @@ export class Message extends Base { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Edits this message. */ |   /** Edits this message. */ | ||||||
|   async edit(text?: string, option?: MessageOption): Promise<Message> { |   async edit( | ||||||
|  |     content?: string | AllMessageOptions, | ||||||
|  |     option?: AllMessageOptions | ||||||
|  |   ): Promise<Message> { | ||||||
|  |     if (typeof content === 'object') { | ||||||
|  |       option = content | ||||||
|  |       content = undefined | ||||||
|  |     } | ||||||
|  |     if (content === undefined && option === undefined) { | ||||||
|  |       throw new Error('Either text or option is necessary.') | ||||||
|  |     } | ||||||
|  |     if (option instanceof Embed) { | ||||||
|  |       option = { | ||||||
|  |         embed: option | ||||||
|  |       } | ||||||
|  |     } | ||||||
|     if ( |     if ( | ||||||
|       this.client.user !== undefined && |       this.client.user !== undefined && | ||||||
|       this.author.id !== this.client.user?.id |       this.author.id !== this.client.user?.id | ||||||
|     ) |     ) { | ||||||
|       throw new Error("Cannot edit other users' messages") |       throw new Error("Cannot edit other users' messages") | ||||||
|     return this.channel.editMessage(this.id, text, option) |     } | ||||||
|  |     return this.channel.editMessage(this.id, content, option) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Creates a Reply to this Message. */ |   /** Creates a Reply to this Message. */ | ||||||
|   async reply( |   async reply( | ||||||
|     text?: string | AllMessageOptions, |     content?: string | AllMessageOptions, | ||||||
|     option?: AllMessageOptions |     option?: AllMessageOptions | ||||||
|   ): Promise<Message> { |   ): Promise<Message> { | ||||||
|     return this.channel.send(text, option, this) |     return this.channel.send(content, option, this) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   /** Deletes the Message. */ |   /** Deletes the Message. */ | ||||||
|   async delete(): Promise<void> { |   async delete(): Promise<void> { | ||||||
|     return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) |     return this.client.rest.delete(CHANNEL_MESSAGE(this.channelID, this.id)) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async addReaction(emoji: string | Emoji): Promise<void> { | ||||||
|  |     return this.channel.addReaction(this, emoji) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async removeReaction( | ||||||
|  |     emoji: string | Emoji, | ||||||
|  |     user?: User | Member | string | ||||||
|  |   ): Promise<void> { | ||||||
|  |     return this.channel.removeReaction(this, emoji, user) | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										70
									
								
								src/structures/template.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/structures/template.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | ||||||
|  | import { Client } from '../models/client.ts' | ||||||
|  | import { TEMPLATE } from '../types/endpoint.ts' | ||||||
|  | import { TemplatePayload } from '../types/template.ts' | ||||||
|  | import { Base } from './base.ts' | ||||||
|  | import { Guild } from './guild.ts' | ||||||
|  | import { User } from './user.ts' | ||||||
|  | 
 | ||||||
|  | export class Template extends Base { | ||||||
|  |   /** The template code (unique ID) */ | ||||||
|  |   code: string | ||||||
|  |   /** The template name */ | ||||||
|  |   name: string | ||||||
|  |   /** The description for the template */ | ||||||
|  |   description: string | null | ||||||
|  |   /** Number of times this template has been used */ | ||||||
|  |   usageCount: number | ||||||
|  |   /** The ID of the user who created the template */ | ||||||
|  |   creatorID: string | ||||||
|  |   /** The user who created the template */ | ||||||
|  |   creator: User | ||||||
|  |   /** When this template was created (in ms) */ | ||||||
|  |   createdAt: number | ||||||
|  |   /** When this template was last synced to the source guild (in ms) */ | ||||||
|  |   updatedAt: number | ||||||
|  |   /** The ID of the guild this template is based on */ | ||||||
|  |   sourceGuildID: string | ||||||
|  |   /** The guild snapshot this template contains */ | ||||||
|  |   serializedSourceGuild: Guild | ||||||
|  |   /** Whether the template has unsynced changes */ | ||||||
|  |   isDirty: boolean | null | ||||||
|  | 
 | ||||||
|  |   constructor(client: Client, data: TemplatePayload) { | ||||||
|  |     super(client, data) | ||||||
|  |     this.code = data.code | ||||||
|  |     this.name = data.name | ||||||
|  |     this.description = data.description | ||||||
|  |     this.usageCount = data.usage_count | ||||||
|  |     this.creatorID = data.creator_id | ||||||
|  |     this.creator = new User(client, data.creator) | ||||||
|  |     this.createdAt = Date.parse(data.created_at) | ||||||
|  |     this.updatedAt = Date.parse(data.updated_at) | ||||||
|  |     this.sourceGuildID = data.source_guild_id | ||||||
|  |     this.serializedSourceGuild = new Guild(client, data.serialized_source_guild) | ||||||
|  |     this.isDirty = Boolean(data.is_dirty) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** Modifies the template's metadata. Requires the MANAGE_GUILD permission. Returns the template object on success. */ | ||||||
|  |   async edit(data: ModifyGuildTemplateParams): Promise<Template> { | ||||||
|  |     const res = await this.client.rest.patch(TEMPLATE(this.code), data) | ||||||
|  |     return new Template(this.client, res) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** Deletes the template. Requires the MANAGE_GUILD permission. Returns the deleted template object on success. */ | ||||||
|  |   async delete(): Promise<Template> { | ||||||
|  |     const res = await this.client.rest.delete(TEMPLATE(this.code)) | ||||||
|  |     return new Template(this.client, res) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** Syncs the template to the guild's current state. Requires the MANAGE_GUILD permission. Returns the template object on success. */ | ||||||
|  |   async sync(): Promise<Template> { | ||||||
|  |     const res = await this.client.rest.put(TEMPLATE(this.code)) | ||||||
|  |     return new Template(this.client, res) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** https://discord.com/developers/docs/resources/template#modify-guild-template-json-params */ | ||||||
|  | export interface ModifyGuildTemplateParams { | ||||||
|  |   name?: string | ||||||
|  |   description?: string | null | ||||||
|  | } | ||||||
|  | @ -3,6 +3,7 @@ import { Client } from '../models/client.ts' | ||||||
| import { | import { | ||||||
|   GuildTextChannelPayload, |   GuildTextChannelPayload, | ||||||
|   MessageOption, |   MessageOption, | ||||||
|  |   MessagePayload, | ||||||
|   MessageReference, |   MessageReference, | ||||||
|   ModifyGuildTextChannelOption, |   ModifyGuildTextChannelOption, | ||||||
|   ModifyGuildTextChannelPayload, |   ModifyGuildTextChannelPayload, | ||||||
|  | @ -12,12 +13,18 @@ import { | ||||||
| import { | import { | ||||||
|   CHANNEL, |   CHANNEL, | ||||||
|   CHANNEL_MESSAGE, |   CHANNEL_MESSAGE, | ||||||
|   CHANNEL_MESSAGES |   CHANNEL_MESSAGES, | ||||||
|  |   MESSAGE_REACTION_ME, | ||||||
|  |   MESSAGE_REACTION_USER | ||||||
| } from '../types/endpoint.ts' | } from '../types/endpoint.ts' | ||||||
|  | import { Collection } from '../utils/collection.ts' | ||||||
| import { Channel } from './channel.ts' | import { Channel } from './channel.ts' | ||||||
| import { Embed } from './embed.ts' | import { Embed } from './embed.ts' | ||||||
|  | import { Emoji } from './emoji.ts' | ||||||
| import { Guild } from './guild.ts' | import { Guild } from './guild.ts' | ||||||
|  | import { Member } from './member.ts' | ||||||
| import { Message } from './message.ts' | import { Message } from './message.ts' | ||||||
|  | import { User } from './user.ts' | ||||||
| 
 | 
 | ||||||
| export type AllMessageOptions = MessageOption | Embed | export type AllMessageOptions = MessageOption | Embed | ||||||
| 
 | 
 | ||||||
|  | @ -41,29 +48,30 @@ export class TextChannel extends Channel { | ||||||
| 
 | 
 | ||||||
|   /** |   /** | ||||||
|    * |    * | ||||||
|    * @param text Text content of the Message to send. |    * @param content Text content of the Message to send. | ||||||
|    * @param option Various other Message options. |    * @param option Various other Message options. | ||||||
|    * @param reply Reference to a Message object to reply-to. |    * @param reply Reference to a Message object to reply-to. | ||||||
|    */ |    */ | ||||||
|   async send( |   async send( | ||||||
|     text?: string | AllMessageOptions, |     content?: string | AllMessageOptions, | ||||||
|     option?: AllMessageOptions, |     option?: AllMessageOptions, | ||||||
|     reply?: Message |     reply?: Message | ||||||
|   ): Promise<Message> { |   ): Promise<Message> { | ||||||
|     if (typeof text === 'object') { |     if (typeof content === 'object') { | ||||||
|       option = text |       option = content | ||||||
|       text = undefined |       content = undefined | ||||||
|     } |     } | ||||||
|     if (text === undefined && option === undefined) { |     if (content === undefined && option === undefined) { | ||||||
|       throw new Error('Either text or option is necessary.') |       throw new Error('Either text or option is necessary.') | ||||||
|     } |     } | ||||||
|     if (option instanceof Embed) |     if (option instanceof Embed) { | ||||||
|       option = { |       option = { | ||||||
|         embed: option |         embed: option | ||||||
|       } |       } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     const payload: any = { |     const payload: any = { | ||||||
|       content: text, |       content: content, | ||||||
|       embed: option?.embed, |       embed: option?.embed, | ||||||
|       file: option?.file, |       file: option?.file, | ||||||
|       tts: option?.tts, |       tts: option?.tts, | ||||||
|  | @ -124,6 +132,96 @@ export class TextChannel extends Channel { | ||||||
|     await res.mentions.fromPayload(newMsg) |     await res.mentions.fromPayload(newMsg) | ||||||
|     return res |     return res | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async addReaction( | ||||||
|  |     message: Message | string, | ||||||
|  |     emoji: Emoji | string | ||||||
|  |   ): Promise<void> { | ||||||
|  |     if (emoji instanceof Emoji) { | ||||||
|  |       emoji = emoji.getEmojiString | ||||||
|  |     } | ||||||
|  |     if (message instanceof Message) { | ||||||
|  |       message = message.id | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const encodedEmoji = encodeURI(emoji) | ||||||
|  | 
 | ||||||
|  |     await this.client.rest.put( | ||||||
|  |       MESSAGE_REACTION_ME(this.id, message, encodedEmoji) | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async removeReaction( | ||||||
|  |     message: Message | string, | ||||||
|  |     emoji: Emoji | string, | ||||||
|  |     user?: User | Member | string | ||||||
|  |   ): Promise<void> { | ||||||
|  |     if (emoji instanceof Emoji) { | ||||||
|  |       emoji = emoji.getEmojiString | ||||||
|  |     } | ||||||
|  |     if (message instanceof Message) { | ||||||
|  |       message = message.id | ||||||
|  |     } | ||||||
|  |     if (user !== undefined) { | ||||||
|  |       if (typeof user !== 'string') { | ||||||
|  |         user = user.id | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const encodedEmoji = encodeURI(emoji) | ||||||
|  | 
 | ||||||
|  |     if (user === undefined) { | ||||||
|  |       await this.client.rest.delete( | ||||||
|  |         MESSAGE_REACTION_ME(this.id, message, encodedEmoji) | ||||||
|  |       ) | ||||||
|  |     } else { | ||||||
|  |       await this.client.rest.delete( | ||||||
|  |         MESSAGE_REACTION_USER(this.id, message, encodedEmoji, user) | ||||||
|  |       ) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Fetch Messages of a Channel | ||||||
|  |    * @param options Options to configure fetching Messages | ||||||
|  |    */ | ||||||
|  |   async fetchMessages(options?: { | ||||||
|  |     limit?: number | ||||||
|  |     around?: Message | string | ||||||
|  |     before?: Message | string | ||||||
|  |     after?: Message | string | ||||||
|  |   }): Promise<Collection<string, Message>> { | ||||||
|  |     const res = new Collection<string, Message>() | ||||||
|  |     const raws = (await this.client.rest.api.channels[this.id].messages.get({ | ||||||
|  |       limit: options?.limit ?? 50, | ||||||
|  |       around: | ||||||
|  |         options?.around === undefined | ||||||
|  |           ? undefined | ||||||
|  |           : typeof options.around === 'string' | ||||||
|  |           ? options.around | ||||||
|  |           : options.around.id, | ||||||
|  |       before: | ||||||
|  |         options?.before === undefined | ||||||
|  |           ? undefined | ||||||
|  |           : typeof options.before === 'string' | ||||||
|  |           ? options.before | ||||||
|  |           : options.before.id, | ||||||
|  |       after: | ||||||
|  |         options?.after === undefined | ||||||
|  |           ? undefined | ||||||
|  |           : typeof options.after === 'string' | ||||||
|  |           ? options.after | ||||||
|  |           : options.after.id | ||||||
|  |     })) as MessagePayload[] | ||||||
|  | 
 | ||||||
|  |     for (const raw of raws) { | ||||||
|  |       await this.messages.set(raw.id, raw) | ||||||
|  |       const msg = ((await this.messages.get(raw.id)) as unknown) as Message | ||||||
|  |       res.set(msg.id, msg) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return res | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class GuildTextChannel extends TextChannel { | export class GuildTextChannel extends TextChannel { | ||||||
|  | @ -185,4 +283,40 @@ export class GuildTextChannel extends TextChannel { | ||||||
| 
 | 
 | ||||||
|     return new GuildTextChannel(this.client, resp, this.guild) |     return new GuildTextChannel(this.client, resp, this.guild) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Bulk Delete Messages in a Guild Text Channel | ||||||
|  |    * @param messages Messages to delete. Can be a number, or Array of Message or IDs | ||||||
|  |    */ | ||||||
|  |   async bulkDelete( | ||||||
|  |     messages: Array<Message | string> | number | ||||||
|  |   ): Promise<GuildTextChannel> { | ||||||
|  |     let ids: string[] = [] | ||||||
|  | 
 | ||||||
|  |     if (Array.isArray(messages)) | ||||||
|  |       ids = messages.map((e) => (typeof e === 'string' ? e : e.id)) | ||||||
|  |     else { | ||||||
|  |       let list = await this.messages.array() | ||||||
|  |       if (list.length < messages) list = (await this.fetchMessages()).array() | ||||||
|  |       ids = list | ||||||
|  |         .sort((b, a) => a.createdAt.getTime() - b.createdAt.getTime()) | ||||||
|  |         .filter((e, i) => i < messages) | ||||||
|  |         .filter( | ||||||
|  |           (e) => | ||||||
|  |             new Date().getTime() - e.createdAt.getTime() <= | ||||||
|  |             1000 * 60 * 60 * 24 * 14 | ||||||
|  |         ) | ||||||
|  |         .map((e) => e.id) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ids = [...new Set(ids)] | ||||||
|  |     if (ids.length < 2 || ids.length > 100) | ||||||
|  |       throw new Error('bulkDelete can only delete messages in range 2-100') | ||||||
|  | 
 | ||||||
|  |     await this.client.rest.api.channels[this.id].messages['bulk-delete'].post({ | ||||||
|  |       messages: ids | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     return this | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ import { Embed } from './embed.ts' | ||||||
| import { Message } from './message.ts' | 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 '../../deps.ts' | ||||||
| import { WEBHOOK_MESSAGE } from '../types/endpoint.ts' | import { WEBHOOK_MESSAGE } from '../types/endpoint.ts' | ||||||
| 
 | 
 | ||||||
| export interface WebhookMessageOptions extends MessageOption { | export interface WebhookMessageOptions extends MessageOption { | ||||||
|  |  | ||||||
|  | @ -112,6 +112,17 @@ client.on('messageCreate', async (msg: Message) => { | ||||||
|     } else { |     } else { | ||||||
|       msg.channel.send('Failed...') |       msg.channel.send('Failed...') | ||||||
|     } |     } | ||||||
|  |   } else if (msg.content === '!react') { | ||||||
|  |     msg.addReaction('🤔') | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  | 
 | ||||||
|  | client.on('messageReactionRemove', (reaction, user) => { | ||||||
|  |   const msg = reaction.message | ||||||
|  | 
 | ||||||
|  |   // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
 | ||||||
|  |   if (reaction.me && reaction.emoji.getEmojiString === '🤔') { | ||||||
|  |     msg.removeReaction(reaction.emoji) | ||||||
|   } |   } | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,13 +7,14 @@ import { | ||||||
|   groupslash, |   groupslash, | ||||||
|   CommandContext, |   CommandContext, | ||||||
|   Extension, |   Extension, | ||||||
|   Collection |   Collection, | ||||||
|  |   GuildTextChannel | ||||||
| } from '../../mod.ts' | } from '../../mod.ts' | ||||||
| import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts' | import { LL_IP, LL_PASS, LL_PORT, TOKEN } from './config.ts' | ||||||
| import { | import { | ||||||
|   Manager, |   Manager, | ||||||
|   Player |   Player | ||||||
| } from 'https://raw.githubusercontent.com/Lavaclient/lavadeno/master/mod.ts' | } from '../../deps.ts' | ||||||
| import { Interaction } from '../structures/slash.ts' | import { Interaction } from '../structures/slash.ts' | ||||||
| import { slash } from '../models/client.ts' | import { slash } from '../models/client.ts' | ||||||
| // import { SlashCommandOptionType } from '../types/slash.ts'
 | // import { SlashCommandOptionType } from '../types/slash.ts'
 | ||||||
|  | @ -69,6 +70,17 @@ class MyClient extends CommandClient { | ||||||
|     d.respond({ content: 'sub-cmd-group worked' }) |     d.respond({ content: 'sub-cmd-group worked' }) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @command() | ||||||
|  |   rmrf(ctx: CommandContext): any { | ||||||
|  |     if (ctx.author.id !== '422957901716652033') return | ||||||
|  |     ;((ctx.channel as any) as GuildTextChannel) | ||||||
|  |       .bulkDelete(3) | ||||||
|  |       .then((chan) => { | ||||||
|  |         ctx.channel.send(`Bulk deleted 2 in ${chan}`) | ||||||
|  |       }) | ||||||
|  |       .catch((e) => ctx.channel.send(`${e.message}`)) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @slash() |   @slash() | ||||||
|   run(d: Interaction): void { |   run(d: Interaction): void { | ||||||
|     console.log(d.name) |     console.log(d.name) | ||||||
|  | @ -205,6 +217,10 @@ class VCExtension extends Extension { | ||||||
| 
 | 
 | ||||||
| const client = new MyClient() | const client = new MyClient() | ||||||
| 
 | 
 | ||||||
|  | client.on('raw', (e, d) => { | ||||||
|  |   if (e === 'GUILD_MEMBER_ADD' || e === 'GUILD_MEMBER_UPDATE') console.log(e, d) | ||||||
|  | }) | ||||||
|  | 
 | ||||||
| client.extensions.load(VCExtension) | client.extensions.load(VCExtension) | ||||||
| 
 | 
 | ||||||
| client.connect(TOKEN, Intents.None) | client.connect(TOKEN, Intents.All) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,16 @@ | ||||||
| import { SlashClient } from '../models/slashClient.ts' | import { SlashClient } from '../models/slashClient.ts' | ||||||
|  | import { SlashCommandPartial } from '../types/slash.ts' | ||||||
| import { TOKEN } from './config.ts' | import { TOKEN } from './config.ts' | ||||||
| 
 | 
 | ||||||
| const slash = new SlashClient({ token: TOKEN }) | export const slash = new SlashClient({ token: TOKEN }) | ||||||
| 
 | 
 | ||||||
| slash.commands.all().then(console.log) | // Cmd objects come here
 | ||||||
|  | const commands: SlashCommandPartial[] = [] | ||||||
|  | 
 | ||||||
|  | console.log('Creating...') | ||||||
|  | commands.forEach((cmd) => { | ||||||
|  |   slash.commands | ||||||
|  |     .create(cmd, '!! Your testing guild ID comes here !!') | ||||||
|  |     .then((c) => console.log(`Created command ${c.name}!`)) | ||||||
|  |     .catch((e) => `Failed to create ${cmd.name} - ${e.message}`) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ export interface MemberPayload { | ||||||
|   premium_since?: string |   premium_since?: string | ||||||
|   deaf: boolean |   deaf: boolean | ||||||
|   mute: boolean |   mute: boolean | ||||||
|  |   pending?: boolean | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum MessageNotification { | export enum MessageNotification { | ||||||
|  | @ -113,6 +114,9 @@ export type GuildFeatures = | ||||||
|   | 'FEATURABLE' |   | 'FEATURABLE' | ||||||
|   | 'ANIMATED_ICON' |   | 'ANIMATED_ICON' | ||||||
|   | 'BANNER' |   | 'BANNER' | ||||||
|  |   | 'WELCOME_SCREEN_ENABLED' | ||||||
|  |   | 'MEMBER_VERIFICATION_GATE_ENABLED' | ||||||
|  |   | 'PREVIEW_ENABLED' | ||||||
| 
 | 
 | ||||||
| export enum IntegrationExpireBehavior { | export enum IntegrationExpireBehavior { | ||||||
|   REMOVE_ROLE = 0, |   REMOVE_ROLE = 0, | ||||||
|  |  | ||||||
|  | @ -50,7 +50,7 @@ export interface SlashCommandChoice { | ||||||
|   /** (Display) name of the Choice */ |   /** (Display) name of the Choice */ | ||||||
|   name: string |   name: string | ||||||
|   /** Actual value to be sent in Interaction */ |   /** Actual value to be sent in Interaction */ | ||||||
|   value: string |   value: any | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export enum SlashCommandOptionType { | export enum SlashCommandOptionType { | ||||||
|  | @ -66,7 +66,8 @@ export enum SlashCommandOptionType { | ||||||
| 
 | 
 | ||||||
| export interface SlashCommandOption { | export interface SlashCommandOption { | ||||||
|   name: string |   name: string | ||||||
|   description: string |   /** Description not required in Sub-Command or Sub-Command-Group */ | ||||||
|  |   description?: string | ||||||
|   type: SlashCommandOptionType |   type: SlashCommandOptionType | ||||||
|   required?: boolean |   required?: boolean | ||||||
|   default?: boolean |   default?: boolean | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ import { UserPayload } from './user.ts' | ||||||
| export interface TemplatePayload { | export interface TemplatePayload { | ||||||
|   code: string |   code: string | ||||||
|   name: string |   name: string | ||||||
|   description: string | undefined |   description: string | null | ||||||
|   usage_count: number |   usage_count: number | ||||||
|   creator_id: string |   creator_id: string | ||||||
|   creator: UserPayload |   creator: UserPayload | ||||||
|  | @ -12,5 +12,5 @@ export interface TemplatePayload { | ||||||
|   updated_at: string |   updated_at: string | ||||||
|   source_guild_id: string |   source_guild_id: string | ||||||
|   serialized_source_guild: GuildPayload |   serialized_source_guild: GuildPayload | ||||||
|   is_dirty: boolean | undefined |   is_dirty: boolean | null | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue