Merge pull request #27 from DjDeveloperr/main
Added CommandClient, Update README with latest examples, remove client from Adapters, simplify Intents
This commit is contained in:
commit
5dc03895a6
19 changed files with 685 additions and 120 deletions
75
README.md
75
README.md
|
@ -1,10 +1,22 @@
|
||||||
# discord-deno
|
# discord-deno
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[](https://github.com/RichardLitt/standard-readme)
|
[](https://github.com/RichardLitt/standard-readme)
|
||||||
|
|
||||||
**An easy to use Discord API Library for Deno**
|
**An easy to use Discord API Library for Deno.**
|
||||||
|
* Lightweight and easy to use.
|
||||||
|
* Built-in Command Framework,
|
||||||
|
* Easily build Commands on the fly.
|
||||||
|
* Compltely Customizable.
|
||||||
|
* Complete Object-Oriented approach.
|
||||||
|
* 100% Discord API Coverage.
|
||||||
|
* Customizable caching.
|
||||||
|
* Built in support for Redis.
|
||||||
|
* Write Custom Cache Adapters.
|
||||||
|
* Complete TypeScript support.
|
||||||
|
|
||||||
|
Note: Library is yet under development and not completely usable. You're still always welcome to use, but there may be breaking changes.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
|
@ -15,25 +27,70 @@
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
Right now, the package is not published anywhere, as its not completely usable.
|
||||||
|
You can import it from this Raw GitHub URL: https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts
|
||||||
|
|
||||||
|
For a quick example, run this:
|
||||||
|
```bash
|
||||||
|
deno run --allow-net https://raw.githubusercontent.com/discord-deno/discord.deno/main/examples/ping.ts
|
||||||
|
```
|
||||||
|
And input your bot's token and Intents.
|
||||||
|
|
||||||
|
Here is a small example of how to use discord.deno,
|
||||||
```ts
|
```ts
|
||||||
import { Client } from 'https://deno.land/x/discord-deno/models/client.ts'
|
import { Client, Message, Intents } from 'https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts'
|
||||||
import { Message } from 'https://deno.land/x/discord-deno/structures/message.ts'
|
|
||||||
|
|
||||||
const bot = new Client()
|
const client = new Client()
|
||||||
|
|
||||||
bot.on('messageCreate', (msg: Message): void => {
|
// Listen for event when client is ready (Identified through gateway / Resumed)
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log(`Ready! User: ${client.user?.tag}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for event whenever a Message is sent
|
||||||
|
client.on('messageCreate', (msg: Message): void => {
|
||||||
if (msg.content === '!ping') {
|
if (msg.content === '!ping') {
|
||||||
msg.channel.send(`Pong! ping: ${bot.ping}`)
|
msg.channel.send(`Pong! WS Ping: ${client.ping}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.connect(TOKEN, [GatewayIntents.GUILD_MESSAGES])
|
// Connect to gateway
|
||||||
|
// Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers)
|
||||||
|
client.connect('super secret token comes here', Intents.All)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with CommandClient!
|
||||||
|
```ts
|
||||||
|
import { CommandClient, Command, CommandContext, Message, Intents } from 'https://raw.githubusercontent.com/discord-deno/discord.deno/main/mod.ts'
|
||||||
|
|
||||||
|
const client = new CommandClient({
|
||||||
|
prefix: '!'
|
||||||
|
})
|
||||||
|
|
||||||
|
// Listen for event when client is ready (Identified through gateway / Resumed)
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log(`Ready! User: ${client.user?.tag}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create a new Command
|
||||||
|
class PingCommand extends Command {
|
||||||
|
name = "ping"
|
||||||
|
|
||||||
|
execute(ctx: CommandContext) {
|
||||||
|
ctx.message.reply(`pong! Ping: ${ctx.client.ping}ms`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.commands.add(PingCommand)
|
||||||
|
|
||||||
|
// Connect to gateway
|
||||||
|
// Replace with your bot's token and intents (Intents.All, Intents.Presence, Intents.GuildMembers)
|
||||||
|
client.connect('super secret token comes here', Intents.All)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docs
|
## Docs
|
||||||
|
|
||||||
Not made yet
|
Not made yet.
|
||||||
|
|
||||||
## Maintainer
|
## Maintainer
|
||||||
|
|
||||||
|
|
41
examples/ping.ts
Normal file
41
examples/ping.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { Client, Message, Intents } from '../mod.ts'
|
||||||
|
|
||||||
|
const client = new Client();
|
||||||
|
|
||||||
|
client.on("ready", () => {
|
||||||
|
console.log(`Logged in as ${client.user?.tag}!`);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on("messageCreate", (msg: Message) => {
|
||||||
|
if (msg.content === "!ping") {
|
||||||
|
console.log("Command Used: Ping");
|
||||||
|
msg.reply("pong!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("discord.deno - ping example");
|
||||||
|
|
||||||
|
const token = prompt("Input Bot Token:");
|
||||||
|
if (token === null) {
|
||||||
|
console.log("No token provided");
|
||||||
|
Deno.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const intents = prompt("Input Intents (0 = All, 1 = Presence, 2 = Server Members, 3 = None):");
|
||||||
|
if (intents === null || !["0", "1", "2", "3"].includes(intents)) {
|
||||||
|
console.log("No intents provided");
|
||||||
|
Deno.exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
let ints;
|
||||||
|
if (intents === "0") {
|
||||||
|
ints = Intents.All;
|
||||||
|
} else if (intents === "1") {
|
||||||
|
ints = Intents.Presence;
|
||||||
|
} else if (intents === "2") {
|
||||||
|
ints = Intents.GuildMembers;
|
||||||
|
} else {
|
||||||
|
ints = Intents.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
client.connect(token, ints);
|
3
mod.ts
3
mod.ts
|
@ -3,6 +3,8 @@ export * from './src/models/client.ts'
|
||||||
export * from './src/models/rest.ts'
|
export * from './src/models/rest.ts'
|
||||||
export * from './src/models/cacheAdapter.ts'
|
export * from './src/models/cacheAdapter.ts'
|
||||||
export * from './src/models/shard.ts'
|
export * from './src/models/shard.ts'
|
||||||
|
export * from './src/models/command.ts'
|
||||||
|
export * from './src/models/commandClient.ts'
|
||||||
export * from './src/managers/base.ts'
|
export * from './src/managers/base.ts'
|
||||||
export * from './src/managers/baseChild.ts'
|
export * from './src/managers/baseChild.ts'
|
||||||
export * from './src/managers/channels.ts'
|
export * from './src/managers/channels.ts'
|
||||||
|
@ -53,3 +55,4 @@ export * from './src/types/user.ts'
|
||||||
export * from './src/types/voice.ts'
|
export * from './src/types/voice.ts'
|
||||||
export * from './src/types/webhook.ts'
|
export * from './src/types/webhook.ts'
|
||||||
export * from './src/utils/collection.ts'
|
export * from './src/utils/collection.ts'
|
||||||
|
export * from './src/utils/intents.ts'
|
|
@ -13,7 +13,7 @@ export const channelUpdate: GatewayEventHandler = async (
|
||||||
if (oldChannel !== undefined) {
|
if (oldChannel !== undefined) {
|
||||||
await gateway.client.channels.set(d.id, d)
|
await gateway.client.channels.set(d.id, d)
|
||||||
let guild: undefined | Guild;
|
let guild: undefined | Guild;
|
||||||
if('guild_id' in d) {
|
if ('guild_id' in d) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
guild = await gateway.client.guilds.get((d as GuildChannelPayload).guild_id) as Guild | undefined
|
guild = await gateway.client.guilds.get((d as GuildChannelPayload).guild_id) as Guild | undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const messageCreate: GatewayEventHandler = async (
|
||||||
const user = new User(gateway.client, d.author)
|
const user = new User(gateway.client, d.author)
|
||||||
await gateway.client.users.set(d.author.id, d.author)
|
await gateway.client.users.set(d.author.id, d.author)
|
||||||
let guild
|
let guild
|
||||||
if(d.guild_id !== undefined) {
|
if (d.guild_id !== undefined) {
|
||||||
guild = await gateway.client.guilds.get(d.guild_id)
|
guild = await gateway.client.guilds.get(d.guild_id)
|
||||||
}
|
}
|
||||||
const mentions = new MessageMentions()
|
const mentions = new MessageMentions()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { unzlib } from 'https://deno.land/x/denoflate/mod.ts'
|
import { unzlib } from 'https://deno.land/x/denoflate@1.1/mod.ts'
|
||||||
import { Client } from '../models/client.ts'
|
import { Client } from '../models/client.ts'
|
||||||
import {
|
import {
|
||||||
DISCORD_GATEWAY_URL,
|
DISCORD_GATEWAY_URL,
|
||||||
|
@ -76,21 +76,10 @@ class Gateway {
|
||||||
this.debug(
|
this.debug(
|
||||||
`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`
|
`Received HELLO. Heartbeat Interval: ${this.heartbeatInterval}`
|
||||||
)
|
)
|
||||||
this.heartbeatIntervalID = setInterval(() => {
|
|
||||||
if (this.heartbeatServerResponded) {
|
|
||||||
this.heartbeatServerResponded = false
|
|
||||||
} else {
|
|
||||||
clearInterval(this.heartbeatIntervalID)
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
||||||
this.reconnect()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.send({
|
this.sendHeartbeat()
|
||||||
op: GatewayOpcodes.HEARTBEAT,
|
this.heartbeatIntervalID = setInterval(() => {
|
||||||
d: this.sequenceID ?? null
|
this.heartbeat()
|
||||||
})
|
|
||||||
this.lastPingTimestamp = Date.now()
|
|
||||||
}, this.heartbeatInterval)
|
}, this.heartbeatInterval)
|
||||||
|
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
|
@ -151,7 +140,7 @@ class Gateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onclose (event: CloseEvent): void {
|
private onclose(event: CloseEvent): void {
|
||||||
this.debug(`Connection Closed with code: ${event.code}`)
|
this.debug(`Connection Closed with code: ${event.code}`)
|
||||||
|
|
||||||
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
if (event.code === GatewayCloseCodes.UNKNOWN_ERROR) {
|
||||||
|
@ -200,28 +189,31 @@ class Gateway {
|
||||||
console.log(eventError)
|
console.log(eventError)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendIdentify (forceNewSession?: boolean): Promise<void> {
|
private async sendIdentify(forceNewSession?: boolean): Promise<void> {
|
||||||
this.debug('Fetching /gateway/bot...')
|
if (this.client.bot === true) {
|
||||||
const info = await this.client.rest.get(GATEWAY_BOT())
|
this.debug('Fetching /gateway/bot...')
|
||||||
if (info.session_start_limit.remaining === 0)
|
const info = await this.client.rest.get(GATEWAY_BOT())
|
||||||
throw new Error(
|
if (info.session_start_limit.remaining === 0)
|
||||||
`Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms`
|
throw new Error(
|
||||||
|
`Session Limit Reached. Retry After ${info.session_start_limit.reset_after}ms`
|
||||||
|
)
|
||||||
|
this.debug(`Recommended Shards: ${info.shards}`)
|
||||||
|
this.debug('=== Session Limit Info ===')
|
||||||
|
this.debug(
|
||||||
|
`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`
|
||||||
)
|
)
|
||||||
this.debug(`Recommended Shards: ${info.shards}`)
|
this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`)
|
||||||
this.debug('=== Session Limit Info ===')
|
if (forceNewSession === undefined || !forceNewSession) {
|
||||||
this.debug(
|
const sessionIDCached = await this.cache.get('session_id')
|
||||||
`Remaining: ${info.session_start_limit.remaining}/${info.session_start_limit.total}`
|
if (sessionIDCached !== undefined) {
|
||||||
)
|
this.debug(`Found Cached SessionID: ${sessionIDCached}`)
|
||||||
this.debug(`Reset After: ${info.session_start_limit.reset_after}ms`)
|
this.sessionID = sessionIDCached
|
||||||
if (forceNewSession === undefined || !forceNewSession) {
|
return await this.sendResume()
|
||||||
const sessionIDCached = await this.cache.get('session_id')
|
}
|
||||||
if (sessionIDCached !== undefined) {
|
|
||||||
this.debug(`Found Cached SessionID: ${sessionIDCached}`)
|
|
||||||
this.sessionID = sessionIDCached
|
|
||||||
return await this.sendResume()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.send({
|
|
||||||
|
const payload: any = {
|
||||||
op: GatewayOpcodes.IDENTIFY,
|
op: GatewayOpcodes.IDENTIFY,
|
||||||
d: {
|
d: {
|
||||||
token: this.token,
|
token: this.token,
|
||||||
|
@ -238,7 +230,24 @@ class Gateway {
|
||||||
),
|
),
|
||||||
presence: this.client.presence.create()
|
presence: this.client.presence.create()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if (this.client.bot === false) {
|
||||||
|
// TODO: Complete Selfbot support
|
||||||
|
this.debug("Modify Identify Payload for Self-bot..")
|
||||||
|
// delete payload.d['intents']
|
||||||
|
// payload.d.intents = Intents.None
|
||||||
|
payload.d.presence = null
|
||||||
|
payload.d.properties = {
|
||||||
|
$os: "Windows",
|
||||||
|
$browser: "Firefox",
|
||||||
|
$device: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
this.debug("Warn: Support for selfbots is incomplete")
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendResume(): Promise<void> {
|
private async sendResume(): Promise<void> {
|
||||||
|
@ -259,11 +268,11 @@ class Gateway {
|
||||||
this.send(resumePayload)
|
this.send(resumePayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug (msg: string): void {
|
debug(msg: string): void {
|
||||||
this.client.debug('Gateway', msg)
|
this.client.debug('Gateway', msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
async reconnect (forceNew?: boolean): Promise<void> {
|
async reconnect(forceNew?: boolean): Promise<void> {
|
||||||
clearInterval(this.heartbeatIntervalID)
|
clearInterval(this.heartbeatIntervalID)
|
||||||
if (forceNew === undefined || !forceNew)
|
if (forceNew === undefined || !forceNew)
|
||||||
await this.cache.delete('session_id')
|
await this.cache.delete('session_id')
|
||||||
|
@ -305,6 +314,29 @@ class Gateway {
|
||||||
d: data
|
d: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendHeartbeat(): void {
|
||||||
|
const payload = {
|
||||||
|
op: GatewayOpcodes.HEARTBEAT,
|
||||||
|
d: this.sequenceID ?? null
|
||||||
|
};
|
||||||
|
|
||||||
|
this.send(payload)
|
||||||
|
this.lastPingTimestamp = Date.now()
|
||||||
|
}
|
||||||
|
|
||||||
|
heartbeat(): void {
|
||||||
|
if (this.heartbeatServerResponded) {
|
||||||
|
this.heartbeatServerResponded = false
|
||||||
|
} else {
|
||||||
|
clearInterval(this.heartbeatIntervalID)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.reconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendHeartbeat()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GatewayEventHandler = (gateway: Gateway, d: any) => void
|
export type GatewayEventHandler = (gateway: Gateway, d: any) => void
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Collection } from '../utils/collection.ts'
|
import { Collection } from '../utils/collection.ts'
|
||||||
import { Client } from './client.ts'
|
|
||||||
import {
|
import {
|
||||||
connect,
|
connect,
|
||||||
Redis,
|
Redis,
|
||||||
|
@ -7,7 +6,6 @@ import {
|
||||||
} from 'https://denopkg.com/keroxp/deno-redis/mod.ts'
|
} from 'https://denopkg.com/keroxp/deno-redis/mod.ts'
|
||||||
|
|
||||||
export interface ICacheAdapter {
|
export interface ICacheAdapter {
|
||||||
client: Client
|
|
||||||
get: (cacheName: string, key: string) => Promise<any> | any
|
get: (cacheName: string, key: string) => Promise<any> | any
|
||||||
set: (cacheName: string, key: string, value: any) => Promise<any> | any
|
set: (cacheName: string, key: string, value: any) => Promise<any> | any
|
||||||
delete: (cacheName: string, key: string) => Promise<boolean> | boolean
|
delete: (cacheName: string, key: string) => Promise<boolean> | boolean
|
||||||
|
@ -16,15 +14,10 @@ export interface ICacheAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DefaultCacheAdapter implements ICacheAdapter {
|
export class DefaultCacheAdapter implements ICacheAdapter {
|
||||||
client: Client
|
|
||||||
data: {
|
data: {
|
||||||
[name: string]: Collection<string, any>
|
[name: string]: Collection<string, any>
|
||||||
} = {}
|
} = {}
|
||||||
|
|
||||||
constructor (client: Client) {
|
|
||||||
this.client = client
|
|
||||||
}
|
|
||||||
|
|
||||||
async get (cacheName: string, key: string): Promise<undefined | any> {
|
async get (cacheName: string, key: string): Promise<undefined | any> {
|
||||||
const cache = this.data[cacheName]
|
const cache = this.data[cacheName]
|
||||||
if (cache === undefined) return
|
if (cache === undefined) return
|
||||||
|
@ -59,13 +52,11 @@ export class DefaultCacheAdapter implements ICacheAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RedisCacheAdapter implements ICacheAdapter {
|
export class RedisCacheAdapter implements ICacheAdapter {
|
||||||
client: Client
|
|
||||||
_redis: Promise<Redis>
|
_redis: Promise<Redis>
|
||||||
redis?: Redis
|
redis?: Redis
|
||||||
ready: boolean = false
|
ready: boolean = false
|
||||||
|
|
||||||
constructor (client: Client, options: RedisConnectOptions) {
|
constructor (options: RedisConnectOptions) {
|
||||||
this.client = client
|
|
||||||
this._redis = connect(options)
|
this._redis = connect(options)
|
||||||
this._redis.then(
|
this._redis.then(
|
||||||
redis => {
|
redis => {
|
||||||
|
|
|
@ -18,6 +18,8 @@ export interface ClientOptions {
|
||||||
cache?: ICacheAdapter,
|
cache?: ICacheAdapter,
|
||||||
forceNewSession?: boolean,
|
forceNewSession?: boolean,
|
||||||
presence?: ClientPresence | ClientActivity | ActivityGame
|
presence?: ClientPresence | ClientActivity | ActivityGame
|
||||||
|
bot?: boolean
|
||||||
|
canary?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +31,7 @@ export class Client extends EventEmitter {
|
||||||
user?: User
|
user?: User
|
||||||
ping = 0
|
ping = 0
|
||||||
token?: string
|
token?: string
|
||||||
cache: ICacheAdapter = new DefaultCacheAdapter(this)
|
cache: ICacheAdapter = new DefaultCacheAdapter()
|
||||||
intents?: GatewayIntents[]
|
intents?: GatewayIntents[]
|
||||||
forceNewSession?: boolean
|
forceNewSession?: boolean
|
||||||
users: UserManager = new UserManager(this)
|
users: UserManager = new UserManager(this)
|
||||||
|
@ -37,6 +39,8 @@ export class Client extends EventEmitter {
|
||||||
channels: ChannelsManager = new ChannelsManager(this)
|
channels: ChannelsManager = new ChannelsManager(this)
|
||||||
messages: MessagesManager = new MessagesManager(this)
|
messages: MessagesManager = new MessagesManager(this)
|
||||||
emojis: EmojisManager = new EmojisManager(this)
|
emojis: EmojisManager = new EmojisManager(this)
|
||||||
|
bot: boolean = true
|
||||||
|
canary: boolean = false
|
||||||
|
|
||||||
presence: ClientPresence = new ClientPresence()
|
presence: ClientPresence = new ClientPresence()
|
||||||
|
|
||||||
|
@ -47,6 +51,8 @@ export class Client extends EventEmitter {
|
||||||
this.forceNewSession = options.forceNewSession
|
this.forceNewSession = options.forceNewSession
|
||||||
if (options.cache !== undefined) this.cache = options.cache
|
if (options.cache !== undefined) this.cache = options.cache
|
||||||
if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence)
|
if (options.presence !== undefined) this.presence = options.presence instanceof ClientPresence ? options.presence : new ClientPresence(options.presence)
|
||||||
|
if (options.bot === false) this.bot = false
|
||||||
|
if (options.canary === true) this.canary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
setAdapter (adapter: ICacheAdapter): Client {
|
setAdapter (adapter: ICacheAdapter): Client {
|
||||||
|
|
126
src/models/command.ts
Normal file
126
src/models/command.ts
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import { Message } from "../structures/message.ts"
|
||||||
|
import { TextChannel } from "../structures/textChannel.ts"
|
||||||
|
import { User } from "../structures/user.ts"
|
||||||
|
import { Collection } from "../utils/collection.ts"
|
||||||
|
import { CommandClient } from "./commandClient.ts"
|
||||||
|
|
||||||
|
export interface CommandContext {
|
||||||
|
/** The Client object */
|
||||||
|
client: CommandClient
|
||||||
|
/** Message which was parsed for Command */
|
||||||
|
message: Message
|
||||||
|
/** The Author of the Message */
|
||||||
|
author: User
|
||||||
|
/** The Channel in which Command was used */
|
||||||
|
channel: TextChannel
|
||||||
|
/** Prefix which was used */
|
||||||
|
prefix: string
|
||||||
|
/** Oject of Command which was used */
|
||||||
|
command: Command
|
||||||
|
/** Name of Command which was used */
|
||||||
|
name: string
|
||||||
|
/** Array of Arguments used with Command */
|
||||||
|
args: string[]
|
||||||
|
/** Complete Raw String of Arguments */
|
||||||
|
argString: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Command {
|
||||||
|
/** Name of the Command */
|
||||||
|
name: string = ""
|
||||||
|
/** Description of the Command */
|
||||||
|
description?: string
|
||||||
|
/** Array of Aliases of Command, or only string */
|
||||||
|
aliases?: string | string[]
|
||||||
|
/** Usage of Command, only Argument Names */
|
||||||
|
usage?: string | string[]
|
||||||
|
/** Usage Example of Command, only Arguments (without Prefix and Name) */
|
||||||
|
examples?: string | string[]
|
||||||
|
/** Does the Command take Arguments? Maybe number of required arguments? */
|
||||||
|
args?: number | boolean
|
||||||
|
/** Permission(s) required for using Command */
|
||||||
|
permissions?: string | string[]
|
||||||
|
/** Whether the Command can only be used in Guild (if allowed in DMs) */
|
||||||
|
guildOnly?: boolean
|
||||||
|
/** Whether the Command can only be used in Bot's DMs (if allowed) */
|
||||||
|
dmOnly?: boolean
|
||||||
|
/** Whether the Command can only be used by Bot Owners */
|
||||||
|
ownerOnly?: boolean
|
||||||
|
|
||||||
|
execute(ctx?: CommandContext): any { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CommandsManager {
|
||||||
|
client: CommandClient
|
||||||
|
list: Collection<string, Command> = new Collection()
|
||||||
|
|
||||||
|
constructor(client: CommandClient) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find a Command by name/alias */
|
||||||
|
find(search: string): Command | undefined {
|
||||||
|
if (this.client.caseSensitive === false) search = search.toLowerCase()
|
||||||
|
return this.list.find((cmd: Command): boolean => {
|
||||||
|
const name = this.client.caseSensitive === true ? cmd.name : cmd.name.toLowerCase()
|
||||||
|
if (name === search) return true
|
||||||
|
else if (cmd.aliases !== undefined) {
|
||||||
|
let aliases: string[]
|
||||||
|
if (typeof cmd.aliases === "string") aliases = [cmd.aliases]
|
||||||
|
else aliases = cmd.aliases
|
||||||
|
if (this.client.caseSensitive === false) aliases = aliases.map(e => e.toLowerCase())
|
||||||
|
return aliases.includes(search)
|
||||||
|
} else return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check whether a Command exists or not */
|
||||||
|
exists(search: Command | string): boolean {
|
||||||
|
let exists = false
|
||||||
|
if (typeof search === "string") return this.find(search) !== undefined
|
||||||
|
else {
|
||||||
|
exists = this.find(search.name) !== undefined
|
||||||
|
if (search.aliases !== undefined) {
|
||||||
|
const aliases: string[] = typeof search.aliases === "string" ? [search.aliases] : search.aliases
|
||||||
|
exists = aliases.map(alias => this.find(alias) !== undefined).find(e => e) ?? false
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add a Command */
|
||||||
|
add(cmd: Command | typeof Command): boolean {
|
||||||
|
// eslint-disable-next-line new-cap
|
||||||
|
if (!(cmd instanceof Command)) cmd = new cmd()
|
||||||
|
if (this.exists(cmd)) return false
|
||||||
|
this.list.set(cmd.name, cmd)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete a Command */
|
||||||
|
delete(cmd: string | Command): boolean {
|
||||||
|
const find = typeof cmd === "string" ? this.find(cmd) : cmd
|
||||||
|
if (find === undefined) return false
|
||||||
|
else return this.list.delete(find.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParsedCommand {
|
||||||
|
name: string
|
||||||
|
args: string[]
|
||||||
|
argString: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseCommand = (client: CommandClient, msg: Message, prefix: string): ParsedCommand => {
|
||||||
|
let content = msg.content.slice(prefix.length)
|
||||||
|
if (client.spacesAfterPrefix === true) content = content.trim()
|
||||||
|
const args = content.split(client.betterArgs === true ? /[\S\s]*/ : / +/)
|
||||||
|
const name = args.shift() as string
|
||||||
|
const argString = content.slice(name.length).trim()
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
argString
|
||||||
|
}
|
||||||
|
}
|
161
src/models/commandClient.ts
Normal file
161
src/models/commandClient.ts
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
import { Embed, Message } from '../../mod.ts'
|
||||||
|
import { Client, ClientOptions } from './client.ts'
|
||||||
|
import { CommandContext, CommandsManager, parseCommand } from './command.ts'
|
||||||
|
|
||||||
|
type PrefixReturnType = string | string[] | Promise<string | string[]>
|
||||||
|
|
||||||
|
export interface CommandClientOptions extends ClientOptions {
|
||||||
|
prefix: string | string[]
|
||||||
|
mentionPrefix?: boolean
|
||||||
|
getGuildPrefix?: (guildID: string) => PrefixReturnType
|
||||||
|
getUserPrefix?: (userID: string) => PrefixReturnType
|
||||||
|
spacesAfterPrefix?: boolean
|
||||||
|
betterArgs?: boolean
|
||||||
|
owners?: string[]
|
||||||
|
allowBots?: boolean
|
||||||
|
allowDMs?: boolean
|
||||||
|
caseSensitive?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandText = string | Embed
|
||||||
|
|
||||||
|
export interface CommandTexts {
|
||||||
|
GUILD_ONLY?: CommandText
|
||||||
|
OWNER_ONLY?: CommandText
|
||||||
|
DMS_ONLY?: CommandText
|
||||||
|
ERROR?: CommandText
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DefaultCommandTexts: CommandTexts = {
|
||||||
|
GUILD_ONLY: 'This command can only be used in a Server!',
|
||||||
|
OWNER_ONLY: 'This command can only be used by Bot Owners!',
|
||||||
|
DMS_ONLY: 'This command can only be used in Bot\'s DMs!',
|
||||||
|
ERROR: 'An error occured while executing command!'
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Replaces {
|
||||||
|
[name: string]: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const massReplace = (text: string, replaces: Replaces): string => {
|
||||||
|
Object.entries(replaces).forEach(replace => {
|
||||||
|
text = text.replace(new RegExp(`{{${replace[0]}}}`, 'g'), replace[1])
|
||||||
|
})
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CommandClient extends Client {
|
||||||
|
prefix: string | string[]
|
||||||
|
mentionPrefix: boolean
|
||||||
|
getGuildPrefix: (guildID: string) => PrefixReturnType
|
||||||
|
getUserPrefix: (userID: string) => PrefixReturnType
|
||||||
|
spacesAfterPrefix: boolean
|
||||||
|
betterArgs: boolean
|
||||||
|
owners: string[]
|
||||||
|
allowBots: boolean
|
||||||
|
allowDMs: boolean
|
||||||
|
caseSensitive: boolean
|
||||||
|
commands: CommandsManager = new CommandsManager(this)
|
||||||
|
texts: CommandTexts = DefaultCommandTexts
|
||||||
|
|
||||||
|
constructor(options: CommandClientOptions) {
|
||||||
|
super(options)
|
||||||
|
this.prefix = options.prefix
|
||||||
|
this.mentionPrefix = options.mentionPrefix === undefined ? false : options.mentionPrefix
|
||||||
|
this.getGuildPrefix = options.getGuildPrefix === undefined ? (id: string) => this.prefix : options.getGuildPrefix
|
||||||
|
this.getUserPrefix = options.getUserPrefix === undefined ? (id: string) => this.prefix : options.getUserPrefix
|
||||||
|
this.spacesAfterPrefix = options.spacesAfterPrefix === undefined ? false : options.spacesAfterPrefix
|
||||||
|
this.betterArgs = options.betterArgs === undefined ? false : options.betterArgs
|
||||||
|
this.owners = options.owners === undefined ? [] : options.owners
|
||||||
|
this.allowBots = options.allowBots === undefined ? false : options.allowBots
|
||||||
|
this.allowDMs = options.allowDMs === undefined ? true : options.allowDMs
|
||||||
|
this.caseSensitive = options.caseSensitive === undefined ? false : options.caseSensitive
|
||||||
|
|
||||||
|
this.on('messageCreate', async (msg: Message) => await this.processMessage(msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
async processMessage(msg: Message): Promise<any> {
|
||||||
|
if (!this.allowBots && msg.author.bot === true) return
|
||||||
|
|
||||||
|
let prefix: string | string[] = this.prefix
|
||||||
|
|
||||||
|
if (msg.guild !== undefined) {
|
||||||
|
let guildPrefix = this.getGuildPrefix(msg.guild.id)
|
||||||
|
if (guildPrefix instanceof Promise) guildPrefix = await guildPrefix
|
||||||
|
prefix = guildPrefix
|
||||||
|
} else {
|
||||||
|
let userPrefix = this.getUserPrefix(msg.author.id)
|
||||||
|
if (userPrefix instanceof Promise) userPrefix = await userPrefix
|
||||||
|
prefix = userPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof prefix === 'string') {
|
||||||
|
if (msg.content.startsWith(prefix) === false) return
|
||||||
|
} else {
|
||||||
|
const usedPrefix = prefix.find(v => msg.content.startsWith(v))
|
||||||
|
if (usedPrefix === undefined) return
|
||||||
|
else prefix = usedPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = parseCommand(this, msg, prefix)
|
||||||
|
const command = this.commands.find(parsed.name)
|
||||||
|
|
||||||
|
if (command === undefined) return
|
||||||
|
|
||||||
|
const baseReplaces: Replaces = {
|
||||||
|
command: command.name,
|
||||||
|
nameUsed: parsed.name,
|
||||||
|
prefix,
|
||||||
|
username: msg.author.username,
|
||||||
|
tag: msg.author.tag,
|
||||||
|
mention: msg.author.mention,
|
||||||
|
id: msg.author.id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.guildOnly === true && msg.guild === undefined) {
|
||||||
|
if (this.texts.GUILD_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.GUILD_ONLY, baseReplaces)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (command.dmOnly === true && msg.guild !== undefined) {
|
||||||
|
if (this.texts.DMS_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.DMS_ONLY, baseReplaces)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (command.ownerOnly === true && !this.owners.includes(msg.author.id)) {
|
||||||
|
if (this.texts.OWNER_ONLY !== undefined) return this.sendProcessedText(msg, this.texts.OWNER_ONLY, baseReplaces)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx: CommandContext = {
|
||||||
|
client: this,
|
||||||
|
name: parsed.name,
|
||||||
|
prefix,
|
||||||
|
args: parsed.args,
|
||||||
|
argString: parsed.argString,
|
||||||
|
message: msg,
|
||||||
|
author: msg.author,
|
||||||
|
command,
|
||||||
|
channel: msg.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.emit('commandUsed', { context: ctx })
|
||||||
|
command.execute(ctx)
|
||||||
|
} catch (e) {
|
||||||
|
if (this.texts.ERROR !== undefined) return this.sendProcessedText(msg, this.texts.ERROR, Object.assign(baseReplaces, { error: e.message }))
|
||||||
|
this.emit('commandError', { command, parsed, error: e })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendProcessedText(msg: Message, text: CommandText, replaces: Replaces): any {
|
||||||
|
if (typeof text === "string") {
|
||||||
|
text = massReplace(text, replaces)
|
||||||
|
return msg.channel.send(text)
|
||||||
|
} else {
|
||||||
|
if (text.description !== undefined) text.description = massReplace(text.description, replaces)
|
||||||
|
if (text.title !== undefined) text.description = massReplace(text.title, replaces)
|
||||||
|
if (text.author?.name !== undefined) text.description = massReplace(text.author.name, replaces)
|
||||||
|
if (text.footer?.text !== undefined) text.description = massReplace(text.footer.text, replaces)
|
||||||
|
return msg.channel.send(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { delay } from '../utils/index.ts'
|
import { delay } from '../utils/index.ts'
|
||||||
import * as baseEndpoints from '../consts/urlsAndVersions.ts'
|
import * as baseEndpoints from '../consts/urlsAndVersions.ts'
|
||||||
import { Client } from './client.ts'
|
import { Client } from './client.ts'
|
||||||
|
import { getBuildInfo } from "../utils/buildInfo.ts"
|
||||||
|
|
||||||
export enum HttpResponseCode {
|
export enum HttpResponseCode {
|
||||||
Ok = 200,
|
Ok = 200,
|
||||||
|
@ -175,11 +176,29 @@ export class RESTManager {
|
||||||
headers['Content-Type'] = 'application/json'
|
headers['Content-Type'] = 'application/json'
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const data: { [name: string]: any } = {
|
||||||
headers,
|
headers,
|
||||||
body: body?.file ?? JSON.stringify(body),
|
body: body?.file ?? JSON.stringify(body),
|
||||||
method: method.toUpperCase()
|
method: method.toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.client.bot === false) {
|
||||||
|
// This is a selfbot. Use requests similar to Discord Client
|
||||||
|
data.headers.authorization = this.client.token as string
|
||||||
|
data.headers['accept-language'] = 'en-US'
|
||||||
|
data.headers.accept = '*/*'
|
||||||
|
data.headers['sec-fetch-dest'] = 'empty'
|
||||||
|
data.headers['sec-fetch-mode'] = 'cors'
|
||||||
|
data.headers['sec-fetch-site'] = 'same-origin'
|
||||||
|
data.headers['x-super-properties'] = btoa(JSON.stringify(getBuildInfo(this.client)))
|
||||||
|
delete data.headers['User-Agent']
|
||||||
|
delete data.headers.Authorization
|
||||||
|
headers.credentials = 'include'
|
||||||
|
headers.mode = 'cors'
|
||||||
|
headers.referrerPolicy = 'no-referrer-when-downgrade'
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkRatelimits (url: string): Promise<number | false> {
|
async checkRatelimits (url: string): Promise<number | false> {
|
||||||
|
@ -230,9 +249,14 @@ export class RESTManager {
|
||||||
)
|
)
|
||||||
.join('&')
|
.join('&')
|
||||||
: ''
|
: ''
|
||||||
const urlToUse =
|
let urlToUse =
|
||||||
method === 'get' && query !== '' ? `${url}?${query}` : url
|
method === 'get' && query !== '' ? `${url}?${query}` : url
|
||||||
|
|
||||||
|
if (this.client.canary === true) {
|
||||||
|
const split = urlToUse.split('//')
|
||||||
|
urlToUse = split[0] + '//canary.' + split[1]
|
||||||
|
}
|
||||||
|
|
||||||
const requestData = this.createRequestBody(body, method)
|
const requestData = this.createRequestBody(body, method)
|
||||||
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
|
@ -304,7 +328,7 @@ export class RESTManager {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
this.logErrors(response, errorStack)
|
this.logErrors(response, errorStack)
|
||||||
|
|
||||||
if(status === HttpResponseCode.Unauthorized) throw new Error("Request was not successful. Invalid Token.")
|
if (status === HttpResponseCode.Unauthorized) throw new Error("Request was not successful. Invalid Token.")
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case HttpResponseCode.BadRequest:
|
case HttpResponseCode.BadRequest:
|
||||||
|
|
|
@ -88,7 +88,7 @@ export class Message extends Base {
|
||||||
this.flags = data.flags
|
this.flags = data.flags
|
||||||
this.channel = channel
|
this.channel = channel
|
||||||
// TODO: Cache in Gateway Event Code
|
// TODO: Cache in Gateway Event Code
|
||||||
// if(!noSave) this.client.messages.set(this.id, data)
|
// if (!noSave) this.client.messages.set(this.id, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readFromData (data: MessagePayload): void {
|
protected readFromData (data: MessagePayload): void {
|
||||||
|
|
|
@ -2,9 +2,12 @@ import { Client } from '../models/client.ts'
|
||||||
import { MessageOption, TextChannelPayload } from '../types/channel.ts'
|
import { MessageOption, TextChannelPayload } from '../types/channel.ts'
|
||||||
import { CHANNEL_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts'
|
import { CHANNEL_MESSAGE, CHANNEL_MESSAGES } from '../types/endpoint.ts'
|
||||||
import { Channel } from './channel.ts'
|
import { Channel } from './channel.ts'
|
||||||
|
import { Embed } from "./embed.ts"
|
||||||
import { Message } from './message.ts'
|
import { Message } from './message.ts'
|
||||||
import { MessageMentions } from './messageMentions.ts'
|
import { MessageMentions } from './messageMentions.ts'
|
||||||
|
|
||||||
|
type AllMessageOptions = MessageOption | Embed
|
||||||
|
|
||||||
export class TextChannel extends Channel {
|
export class TextChannel extends Channel {
|
||||||
lastMessageID?: string
|
lastMessageID?: string
|
||||||
lastPinTimestamp?: string
|
lastPinTimestamp?: string
|
||||||
|
@ -23,10 +26,18 @@ export class TextChannel extends Channel {
|
||||||
this.lastPinTimestamp = data.last_pin_timestamp ?? this.lastPinTimestamp
|
this.lastPinTimestamp = data.last_pin_timestamp ?? this.lastPinTimestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
async send (text?: string, option?: MessageOption): Promise<Message> {
|
async send (text?: string | AllMessageOptions, option?: AllMessageOptions): Promise<Message> {
|
||||||
|
if(typeof text === "object") {
|
||||||
|
option = text
|
||||||
|
text = undefined
|
||||||
|
}
|
||||||
if (text === undefined && option === undefined) {
|
if (text === 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) option = {
|
||||||
|
embed: option
|
||||||
|
}
|
||||||
|
|
||||||
const resp = await this.client.rest.post(CHANNEL_MESSAGES(this.id), {
|
const resp = await this.client.rest.post(CHANNEL_MESSAGES(this.id), {
|
||||||
content: text,
|
content: text,
|
||||||
embed: option?.embed,
|
embed: option?.embed,
|
||||||
|
|
18
src/test/cmd.ts
Normal file
18
src/test/cmd.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { CommandClient, Intents } from '../../mod.ts';
|
||||||
|
import PingCommand from "./cmds/ping.ts";
|
||||||
|
import { TOKEN } from './config.ts'
|
||||||
|
|
||||||
|
const client = new CommandClient({
|
||||||
|
prefix: ["pls", "!"],
|
||||||
|
spacesAfterPrefix: true
|
||||||
|
})
|
||||||
|
|
||||||
|
client.on('debug', console.log)
|
||||||
|
|
||||||
|
client.on('ready', () => {
|
||||||
|
console.log(`[Login] Logged in as ${client.user?.tag}!`)
|
||||||
|
})
|
||||||
|
|
||||||
|
client.commands.add(PingCommand)
|
||||||
|
|
||||||
|
client.connect(TOKEN, Intents.All)
|
11
src/test/cmds/ping.ts
Normal file
11
src/test/cmds/ping.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { Command } from "../../../mod.ts";
|
||||||
|
import { CommandContext } from "../../models/command.ts";
|
||||||
|
|
||||||
|
export default class PingCommand extends Command {
|
||||||
|
name = "ping"
|
||||||
|
dmOnly = true
|
||||||
|
|
||||||
|
execute(ctx: CommandContext): void {
|
||||||
|
ctx.message.reply(`pong! Latency: ${ctx.client.ping}ms`)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +1,62 @@
|
||||||
import { Client } from '../models/client.ts'
|
import { Client, Intents, GuildTextChannel, Message, ClientPresence, Member, Role, GuildChannel, Embed, Guild } from '../../mod.ts';
|
||||||
import { GatewayIntents } from '../types/gateway.ts'
|
|
||||||
import { TOKEN } from './config.ts'
|
import { TOKEN } from './config.ts'
|
||||||
import { Message } from "../structures/message.ts"
|
|
||||||
import { DefaultCacheAdapter } from "../models/cacheAdapter.ts"
|
|
||||||
import { ClientPresence } from "../structures/presence.ts"
|
|
||||||
import { Member } from "../structures/member.ts"
|
|
||||||
import { Role } from "../structures/role.ts"
|
|
||||||
import { GuildChannel } from "../managers/guildChannels.ts"
|
|
||||||
import { TextChannel } from "../structures/textChannel.ts"
|
|
||||||
import { Embed } from "../structures/embed.ts"
|
|
||||||
import { Guild } from "../structures/guild.ts"
|
|
||||||
|
|
||||||
const bot = new Client({
|
const client = new Client({
|
||||||
presence: new ClientPresence({
|
presence: new ClientPresence({
|
||||||
activity: {
|
name: 'Pokémon Sword',
|
||||||
name: "Testing",
|
|
||||||
type: 'COMPETING'
|
type: 'COMPETING'
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
|
// bot: false,
|
||||||
|
// cache: new RedisCacheAdapter({
|
||||||
|
// hostname: '127.0.0.1',
|
||||||
|
// port: 6379
|
||||||
|
// }) // Defaults to in-memory Caching
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.setAdapter(new DefaultCacheAdapter(bot))
|
client.on('ready', () => {
|
||||||
|
console.log(`[Login] Logged in as ${client.user?.tag}!`)
|
||||||
bot.on('ready', () => {
|
|
||||||
console.log(`[Login] Logged in as ${bot.user?.tag}!`)
|
|
||||||
bot.setPresence({
|
|
||||||
name: "Test After Ready",
|
|
||||||
type: 'COMPETING'
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.on('debug', console.log)
|
client.on('debug', console.log)
|
||||||
|
|
||||||
bot.on('channelPinsUpdate', (before: TextChannel, after: TextChannel) => {
|
client.on('channelUpdate', (before: GuildTextChannel, after: GuildTextChannel) => {
|
||||||
console.log(before.send('', {
|
console.log(before.send('', {
|
||||||
embed: new Embed({
|
embed: new Embed({
|
||||||
title: 'Test',
|
title: 'Channel Update',
|
||||||
description: 'Test Embed'
|
description: `Name Before: ${before.name}\nName After: ${after.name}`
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.on('messageCreate', async (msg: Message) => {
|
client.on('messageCreate', async (msg: Message) => {
|
||||||
if (msg.author.bot === true) return
|
if (msg.author.bot === true) return
|
||||||
if (msg.content === "!ping") {
|
console.log(`${msg.author.tag}: ${msg.content}`)
|
||||||
msg.reply(`Pong! Ping: ${bot.ping}ms`)
|
if (msg.content === '!ping') {
|
||||||
} else if (msg.content === "!members") {
|
msg.reply(`Pong! Ping: ${client.ping}ms`)
|
||||||
|
} else if (msg.content === '!members') {
|
||||||
const col = await msg.guild?.members.collection()
|
const col = await msg.guild?.members.collection()
|
||||||
const data = col?.array().map((c: Member, i: number) => {
|
const data = col?.array().map((c: Member, i: number) => {
|
||||||
return `${i + 1}. ${c.user.tag}`
|
return `${i + 1}. ${c.user.tag}`
|
||||||
}).join("\n") as string
|
}).join("\n") as string
|
||||||
msg.channel.send("Member List:\n" + data)
|
msg.channel.send("Member List:\n" + data)
|
||||||
} else if (msg.content === "!guilds") {
|
} else if (msg.content === '!guilds') {
|
||||||
const guilds = await msg.client.guilds.collection()
|
const guilds = await msg.client.guilds.collection()
|
||||||
msg.channel.send("Guild List:\n" + (guilds.array().map((c: Guild, i: number) => {
|
msg.channel.send('Guild List:\n' + (guilds.array().map((c: Guild, i: number) => {
|
||||||
return `${i + 1}. ${c.name} - ${c.memberCount} members`
|
return `${i + 1}. ${c.name} - ${c.memberCount} members`
|
||||||
}).join("\n") as string))
|
}).join("\n") as string))
|
||||||
} else if (msg.content === "!roles") {
|
} else if (msg.content === '!roles') {
|
||||||
const col = await msg.guild?.roles.collection()
|
const col = await msg.guild?.roles.collection()
|
||||||
const data = col?.array().map((c: Role, i: number) => {
|
const data = col?.array().map((c: Role, i: number) => {
|
||||||
return `${i + 1}. ${c.name}`
|
return `${i + 1}. ${c.name}`
|
||||||
}).join("\n") as string
|
}).join("\n") as string
|
||||||
msg.channel.send("Roles List:\n" + data)
|
msg.channel.send("Roles List:\n" + data)
|
||||||
} else if (msg.content === "!channels") {
|
} else if (msg.content === '!channels') {
|
||||||
const col = await msg.guild?.channels.array()
|
const col = await msg.guild?.channels.array()
|
||||||
const data = col?.map((c: GuildChannel, i: number) => {
|
const data = col?.map((c: GuildChannel, i: number) => {
|
||||||
return `${i + 1}. ${c.name}`
|
return `${i + 1}. ${c.name}`
|
||||||
}).join("\n") as string
|
}).join("\n") as string
|
||||||
msg.channel.send("Channels List:\n" + data)
|
msg.channel.send('Channels List:\n' + data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
bot.connect(TOKEN, [
|
client.connect(TOKEN, Intents.None)
|
||||||
GatewayIntents.GUILD_MEMBERS,
|
|
||||||
GatewayIntents.GUILD_PRESENCES,
|
|
||||||
GatewayIntents.GUILD_MESSAGES,
|
|
||||||
GatewayIntents.DIRECT_MESSAGES,
|
|
||||||
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
|
|
||||||
GatewayIntents.DIRECT_MESSAGE_TYPING,
|
|
||||||
GatewayIntents.GUILDS,
|
|
||||||
GatewayIntents.GUILD_BANS,
|
|
||||||
GatewayIntents.GUILD_EMOJIS,
|
|
||||||
GatewayIntents.GUILD_INTEGRATIONS,
|
|
||||||
GatewayIntents.GUILD_INVITES,
|
|
||||||
GatewayIntents.GUILD_MESSAGE_REACTIONS,
|
|
||||||
GatewayIntents.GUILD_MESSAGE_TYPING,
|
|
||||||
GatewayIntents.GUILD_VOICE_STATES,
|
|
||||||
GatewayIntents.GUILD_WEBHOOKS
|
|
||||||
])
|
|
||||||
|
|
40
src/utils/buildInfo.ts
Normal file
40
src/utils/buildInfo.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
import { Client } from "../models/client.ts";
|
||||||
|
|
||||||
|
export const getBuildInfo = (client: Client): {
|
||||||
|
os: string
|
||||||
|
os_version: string
|
||||||
|
browser: string
|
||||||
|
browser_version: string
|
||||||
|
browser_user_agent: string
|
||||||
|
client_build_number: number
|
||||||
|
client_event_source: null
|
||||||
|
release_channel: string
|
||||||
|
} => {
|
||||||
|
const os = 'Windows'
|
||||||
|
const os_version = '10'
|
||||||
|
let client_build_number = 71073
|
||||||
|
const client_event_source = null
|
||||||
|
let release_channel = 'stable'
|
||||||
|
if (client.canary === true) {
|
||||||
|
release_channel = 'canary'
|
||||||
|
client_build_number = 71076
|
||||||
|
}
|
||||||
|
const browser = 'Firefox'
|
||||||
|
const browser_version = '83.0'
|
||||||
|
const browser_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 ' + browser + '/' + browser_version
|
||||||
|
// TODO: Use current OS properties, but also browser_user_agent accordingly
|
||||||
|
// if (Deno.build.os === 'darwin') os = 'MacOS'
|
||||||
|
// else if (Deno.build.os === 'linux') os = 'Ubuntu'
|
||||||
|
|
||||||
|
return {
|
||||||
|
os,
|
||||||
|
os_version,
|
||||||
|
browser,
|
||||||
|
browser_version,
|
||||||
|
browser_user_agent,
|
||||||
|
client_build_number,
|
||||||
|
client_event_source,
|
||||||
|
release_channel,
|
||||||
|
}
|
||||||
|
};
|
72
src/utils/intents.ts
Normal file
72
src/utils/intents.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import { GatewayIntents } from "../types/gateway.ts";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
||||||
|
export class Intents {
|
||||||
|
static All: number[] = [
|
||||||
|
GatewayIntents.GUILD_MEMBERS,
|
||||||
|
GatewayIntents.GUILD_PRESENCES,
|
||||||
|
GatewayIntents.GUILD_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILDS,
|
||||||
|
GatewayIntents.GUILD_BANS,
|
||||||
|
GatewayIntents.GUILD_EMOJIS,
|
||||||
|
GatewayIntents.GUILD_INTEGRATIONS,
|
||||||
|
GatewayIntents.GUILD_INVITES,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILD_VOICE_STATES,
|
||||||
|
GatewayIntents.GUILD_WEBHOOKS
|
||||||
|
];
|
||||||
|
|
||||||
|
static Presence: number[] = [
|
||||||
|
GatewayIntents.GUILD_PRESENCES,
|
||||||
|
GatewayIntents.GUILD_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILDS,
|
||||||
|
GatewayIntents.GUILD_BANS,
|
||||||
|
GatewayIntents.GUILD_EMOJIS,
|
||||||
|
GatewayIntents.GUILD_INTEGRATIONS,
|
||||||
|
GatewayIntents.GUILD_INVITES,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILD_VOICE_STATES,
|
||||||
|
GatewayIntents.GUILD_WEBHOOKS
|
||||||
|
];
|
||||||
|
|
||||||
|
static GuildMembers: number[] = [
|
||||||
|
GatewayIntents.GUILD_MEMBERS,
|
||||||
|
GatewayIntents.GUILD_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILDS,
|
||||||
|
GatewayIntents.GUILD_BANS,
|
||||||
|
GatewayIntents.GUILD_EMOJIS,
|
||||||
|
GatewayIntents.GUILD_INTEGRATIONS,
|
||||||
|
GatewayIntents.GUILD_INVITES,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILD_VOICE_STATES,
|
||||||
|
GatewayIntents.GUILD_WEBHOOKS
|
||||||
|
];
|
||||||
|
|
||||||
|
static None: number[] = [
|
||||||
|
GatewayIntents.GUILD_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGES,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.DIRECT_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILDS,
|
||||||
|
GatewayIntents.GUILD_BANS,
|
||||||
|
GatewayIntents.GUILD_EMOJIS,
|
||||||
|
GatewayIntents.GUILD_INTEGRATIONS,
|
||||||
|
GatewayIntents.GUILD_INVITES,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_REACTIONS,
|
||||||
|
GatewayIntents.GUILD_MESSAGE_TYPING,
|
||||||
|
GatewayIntents.GUILD_VOICE_STATES,
|
||||||
|
GatewayIntents.GUILD_WEBHOOKS
|
||||||
|
]
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue