mirror of
https://github.com/jaronpate/unicord.git
synced 2026-10-18 21:47:40 +00:00
init for rewrite
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
name: CI
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
name: Build, and test on Node ${{ matrix.node }} and ${{ matrix.os }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
node: ['12.x', '14.x', '16.x']
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node ${{ matrix.node }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install deps and build (with cache)
|
||||
uses: bahmutov/npm-install@v1
|
||||
|
||||
- name: Test
|
||||
run: yarn test --ci --coverage --maxWorkers=2
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
@@ -1,17 +0,0 @@
|
||||
name: Publish
|
||||
on: workflow_dispatch
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: yarn
|
||||
- run: yarn build
|
||||
# - run: yarn test
|
||||
- run: yarn publish --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
+174
-2
@@ -1,3 +1,175 @@
|
||||
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Caches
|
||||
|
||||
.cache
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
/dist
|
||||
/node_modules
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"semi": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
+8
-21
@@ -1,27 +1,14 @@
|
||||
{
|
||||
"name": "@jaronp/discord.ts",
|
||||
"version": "0.1.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"author": "Jaron Pate",
|
||||
"license": "Unlicense",
|
||||
"repository": "https://github.com/jaronpate/discord.ts",
|
||||
"files": [
|
||||
"/dist"
|
||||
],
|
||||
"name": "discord.ts-rewrite",
|
||||
"module": "src/index.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "npx ts-node test/main.test.ts",
|
||||
"start:watch": "npx ts-node-dev --respawn test/main.test.ts",
|
||||
"build": "npx tsc"
|
||||
"test": "dotenvx run -f .env -- bun run testing/demo.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.5.3",
|
||||
"dotenv": "^16.0.2",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^4.8.3"
|
||||
"@types/bun": "latest"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"ws": "^8.8.1"
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-177
@@ -1,177 +1,5 @@
|
||||
import { ComponentStyle, Context, Gateway, Guilds, Message, Embed } from './lib';
|
||||
import { SlashCommand, SlashCommandFunction, ChatCommand, Command, CommandFunction, Intent, Presence } from './types';
|
||||
export {
|
||||
ComponentStyle,
|
||||
Context,
|
||||
Gateway,
|
||||
Guilds,
|
||||
Message,
|
||||
Embed,
|
||||
Command,
|
||||
CommandFunction,
|
||||
ChatCommand,
|
||||
Intent,
|
||||
Presence
|
||||
};
|
||||
|
||||
type Config = {
|
||||
token: string;
|
||||
application_id: string;
|
||||
prefix?: string;
|
||||
intents?: Intent[];
|
||||
};
|
||||
|
||||
export class Client {
|
||||
readonly prefix: string;
|
||||
readonly token: string;
|
||||
readonly application_id: string;
|
||||
private readonly gateway: Gateway;
|
||||
private readonly client_commands: Map<string, ChatCommand | CommandFunction> = new Map();
|
||||
private readonly slash_commands: Map<string, SlashCommand | SlashCommandFunction> = new Map();
|
||||
readonly guilds: Guilds;
|
||||
|
||||
constructor({ token, application_id, prefix, intents }: Config) {
|
||||
this.token = token;
|
||||
this.application_id = application_id;
|
||||
this.prefix = prefix ?? '!';
|
||||
this.gateway = new Gateway({ token, intents, client: this });
|
||||
this.guilds = new Guilds({ token, gateway: this.gateway });
|
||||
// Do some default stuff
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this.gateway.user;
|
||||
}
|
||||
|
||||
get handlers() {
|
||||
return this.gateway.handlers;
|
||||
}
|
||||
|
||||
get interactions() {
|
||||
return this.gateway.interactions;
|
||||
}
|
||||
|
||||
get api() {
|
||||
return this.gateway.api;
|
||||
}
|
||||
|
||||
public connect = () => {
|
||||
this.gateway.connect();
|
||||
return this;
|
||||
};
|
||||
|
||||
public reconnect = () => {
|
||||
this.gateway.reconnect();
|
||||
return this;
|
||||
};
|
||||
|
||||
public setPresence = (presence: Presence) => {
|
||||
this.gateway.setPresence(presence);
|
||||
return this;
|
||||
};
|
||||
|
||||
send = (channel_id: string, message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
return this.api.post(`/channels/${channel_id}/messages`, message);
|
||||
};
|
||||
|
||||
public commands = {
|
||||
chat: {
|
||||
register: (command: ChatCommand | string, handler?: CommandFunction) => {
|
||||
if (typeof command === 'string') {
|
||||
this.client_commands.set(command, handler);
|
||||
} else {
|
||||
this.client_commands.set(command.name, command);
|
||||
}
|
||||
},
|
||||
get: (command_name: string) => {
|
||||
return this.client_commands.get(command_name);
|
||||
},
|
||||
execute: (command_name: string, context: Context, ...args: any[]) => {
|
||||
const command = this.client_commands.get(command_name);
|
||||
if (command) {
|
||||
command instanceof ChatCommand
|
||||
? command.execute(this, context, args.slice(1))
|
||||
: command(this, context, args.slice(1));
|
||||
}
|
||||
},
|
||||
delete: (command_name: string) => {
|
||||
this.client_commands.delete(command_name);
|
||||
},
|
||||
list: () => {
|
||||
return Array.from(this.client_commands.entries());
|
||||
},
|
||||
map: () => {
|
||||
return this.client_commands;
|
||||
}
|
||||
},
|
||||
application: {
|
||||
register: (command: SlashCommand | Record<string, string>, handler?: SlashCommandFunction) => {
|
||||
const payload =
|
||||
command instanceof SlashCommand
|
||||
? command.toJSON()
|
||||
: {
|
||||
name: command.name,
|
||||
description: command.description,
|
||||
type: command.type ?? 1
|
||||
};
|
||||
this.api
|
||||
.post(`/applications/${this.application_id}/commands`, payload)
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
if (command instanceof SlashCommand) {
|
||||
this.slash_commands.set(command.name, command);
|
||||
} else {
|
||||
this.slash_commands.set(command.name, handler);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.log.error('Error registering application command', error);
|
||||
});
|
||||
},
|
||||
get: (command_name: string) => {
|
||||
return this.slash_commands.get(command_name);
|
||||
},
|
||||
list: () => {
|
||||
return Array.from(this.slash_commands.entries());
|
||||
},
|
||||
map: () => {
|
||||
return this.slash_commands;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private log = {
|
||||
error: (error_or_message: Error | string, error?: Error) => {
|
||||
if (error && typeof error_or_message === 'string') {
|
||||
console.error(`[discord.ts] ${error_or_message}:`, error);
|
||||
} else {
|
||||
console.error('[discord.ts] Error:', error_or_message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private initialize = () => {
|
||||
// Register default help command
|
||||
this.commands.chat.register('help', (client, context, _args) => {
|
||||
const embed = new Embed()
|
||||
.setTitle('Commands')
|
||||
.setDescription('This is a list of commands you can use.')
|
||||
.setColor('#f6c221');
|
||||
|
||||
const commands = client.commands.chat.list();
|
||||
|
||||
for (const [name, command] of commands) {
|
||||
embed.addField({
|
||||
name: name,
|
||||
value: command instanceof ChatCommand ? command.description : 'No description provided'
|
||||
});
|
||||
}
|
||||
|
||||
context.reply(embed.toMessage());
|
||||
});
|
||||
};
|
||||
}
|
||||
export { Client } from './services/client';
|
||||
export { Intent } from './types/common';
|
||||
export { BaseHandler, type Handler } from './types/hander';
|
||||
export type { ClientConfig } from './types/common';
|
||||
export type { Context } from './types/context';
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Context, Gateway } from '.';
|
||||
|
||||
export class ObjectCache<T> {
|
||||
cache = new Map<string, T>();
|
||||
private readonly token: string;
|
||||
private readonly type: string;
|
||||
private readonly gateway: Gateway;
|
||||
|
||||
constructor({ token, gateway, type, events }: { token: string; gateway: Gateway; type: string; events: string[] }) {
|
||||
this.token = token;
|
||||
this.type = type;
|
||||
this.gateway = gateway;
|
||||
gateway.handlers.register(events, (_client, _context: Context, e: Record<string, any>) => {
|
||||
this.cache.set(e.id, e as T);
|
||||
});
|
||||
}
|
||||
|
||||
async fetch(object_id: string): Promise<T> {
|
||||
return this.gateway.api.get(`${this.type}/${object_id}`).then((res) => {
|
||||
const object: T & { id: string } = res.data;
|
||||
this.cache.set(object.id, object);
|
||||
return object;
|
||||
});
|
||||
}
|
||||
|
||||
get(object_id: string): T | Promise<T> {
|
||||
if (this.cache.has(object_id)) {
|
||||
return Promise.resolve(this.cache.get(object_id)!);
|
||||
} else {
|
||||
return this.fetch(object_id);
|
||||
}
|
||||
}
|
||||
|
||||
list(): Map<string, T> {
|
||||
return this.cache;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Client } from '..';
|
||||
import { Message } from '../lib';
|
||||
|
||||
export class Context {
|
||||
private readonly client: Client;
|
||||
// Public properties
|
||||
message_id: string;
|
||||
channel_id: string;
|
||||
guild_id: string;
|
||||
|
||||
constructor({ client, message }: { client: Client; message: Partial<Message> }) {
|
||||
this.client = client;
|
||||
this.message_id = message.id;
|
||||
this.channel_id = message.channel_id;
|
||||
this.guild_id = message.guild_id;
|
||||
}
|
||||
|
||||
get guild() {
|
||||
return this.client.guilds.get(this.guild_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to the message that this command
|
||||
* @param message The message to send to the channel
|
||||
* @param reference If true the message will be a reference the the message that triggered the command
|
||||
* @returns The message that was sent
|
||||
*/
|
||||
public reply = (message: Message | string, reference: boolean = false) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
if (reference) {
|
||||
message.message_reference = {
|
||||
message_id: this.message_id,
|
||||
channel_id: this.channel_id,
|
||||
guild_id: this.guild_id
|
||||
};
|
||||
}
|
||||
return this.client.send(this.channel_id, message);
|
||||
};
|
||||
|
||||
public delete = (message_id?: string) => {
|
||||
return this.client.api.delete(`/channels/${this.channel_id}/messages/${message_id ?? this.message_id}`);
|
||||
};
|
||||
|
||||
public edit = (message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
return this.client.api.patch(`/channels/${this.channel_id}/messages/${this.message_id}`, message);
|
||||
};
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
import { WebSocket, Data } from 'ws';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import { Client } from '..';
|
||||
import {
|
||||
Session,
|
||||
User,
|
||||
Payload,
|
||||
Intent,
|
||||
Handler,
|
||||
HandlerFunction,
|
||||
Command,
|
||||
Status,
|
||||
Presence,
|
||||
ActivityType,
|
||||
CommandFunction,
|
||||
Interaction,
|
||||
InteractionFunction,
|
||||
SlashCommand
|
||||
} from '../types';
|
||||
import { Context } from './context';
|
||||
import { InteractionContext } from './interaction';
|
||||
|
||||
export class Gateway {
|
||||
socket: WebSocket;
|
||||
axios: AxiosInstance;
|
||||
// Config
|
||||
readonly token: string;
|
||||
readonly client: Client;
|
||||
private intents: number = 513;
|
||||
private commandNotFound: CommandFunction = null;
|
||||
// Discord gateway data
|
||||
private readonly url: string = 'wss://gateway.discord.gg/?v=10&encoding=json';
|
||||
private heartbeat_timeout: NodeJS.Timeout;
|
||||
private heartbeat_function: NodeJS.Timer;
|
||||
private heartbeat_interval: number | null = null;
|
||||
private session: Session = null;
|
||||
public user: User = null;
|
||||
// Utils
|
||||
public event_handlers: Map<string, Array<Handler | HandlerFunction>> = new Map();
|
||||
public interaction_handlers: Map<string, Array<Interaction | InteractionFunction>> = new Map();
|
||||
|
||||
constructor({
|
||||
token,
|
||||
intents,
|
||||
client,
|
||||
commandNotFound
|
||||
}: {
|
||||
token: string;
|
||||
intents?: Intent[];
|
||||
client: Client;
|
||||
commandNotFound?: CommandFunction;
|
||||
}) {
|
||||
// Copy config
|
||||
this.token = token;
|
||||
this.client = client;
|
||||
this.commandNotFound = commandNotFound;
|
||||
// Initialize socket
|
||||
this.socket = new WebSocket(this.url);
|
||||
// Initialize axios
|
||||
this.axios = axios.create({
|
||||
headers: {
|
||||
'User-Agent': 'DiscordBot (discord.ts, 0.0.1)',
|
||||
Authorization: `Bot ${this.token}`
|
||||
},
|
||||
baseURL: 'https://discord.com/api/v10'
|
||||
});
|
||||
this.axios.interceptors.response.use((response) => response.data);
|
||||
// Calculate intents
|
||||
if (intents) {
|
||||
this.intents = intents.reduce((acc, intent) => acc | intent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public connect = () => {
|
||||
// Initialize socket
|
||||
this.socket = new WebSocket(this.url);
|
||||
// Register the listeners
|
||||
this.socket.on('open', this.onOpen);
|
||||
this.socket.on('message', this.onMessage);
|
||||
// this.socket.on('close', this.onClose);
|
||||
// this.socket.on('error', this.onError);
|
||||
return this;
|
||||
};
|
||||
|
||||
public reconnect = () => {
|
||||
this.socket.terminate();
|
||||
return this.connect();
|
||||
};
|
||||
|
||||
public setPresence = (presence: Presence) => {
|
||||
this.send({
|
||||
op: 3,
|
||||
d: presence
|
||||
});
|
||||
};
|
||||
|
||||
public handlers = {
|
||||
register: (events: string | string[], handler: Handler | HandlerFunction) => {
|
||||
this.registerHandler(events, handler, 'command');
|
||||
},
|
||||
get: (event: string) => {
|
||||
return this.event_handlers.get(event);
|
||||
}
|
||||
};
|
||||
|
||||
public interactions = {
|
||||
register: (custom_ids: string | string[], handler: Interaction | InteractionFunction) => {
|
||||
this.registerHandler(custom_ids, handler, 'interaction');
|
||||
},
|
||||
get: (event: string) => {
|
||||
return this.interaction_handlers.get(event);
|
||||
}
|
||||
};
|
||||
|
||||
public api = {
|
||||
get: (url: string, params?: any) => {
|
||||
return this.axios.get<unknown, Record<string, any>>(url, { params });
|
||||
},
|
||||
post: (url: string, data?: any) => {
|
||||
return this.axios.post<unknown, Record<string, any>>(url, data);
|
||||
},
|
||||
patch: (url: string, data?: any) => {
|
||||
return this.axios.patch<unknown, Record<string, any>>(url, data);
|
||||
},
|
||||
delete: (url: string, data?: any) => {
|
||||
return this.axios.delete<unknown, Record<string, any>>(url, data);
|
||||
}
|
||||
};
|
||||
|
||||
// prettier-ignore
|
||||
private registerHandler(events: string | string[], handler: Handler | HandlerFunction, type: 'command'): void;
|
||||
// prettier-ignore
|
||||
private registerHandler(events: string | string[], handler: Interaction | InteractionFunction, type: 'interaction'): void;
|
||||
|
||||
private registerHandler(events: string | string[], handler: any, type: 'command' | 'interaction') {
|
||||
const handler_map = type === 'command' ? this.event_handlers : this.interaction_handlers;
|
||||
if (Array.isArray(events)) {
|
||||
// If Array register handler for each event
|
||||
for (const event of events) {
|
||||
const current_handlers = handler_map.get(event) ?? [];
|
||||
handler_map.set(event, [...current_handlers, handler]);
|
||||
}
|
||||
} else {
|
||||
// Otherwise register for the single event
|
||||
const current_handlers = handler_map.get(events) ?? [];
|
||||
handler_map.set(events, [...current_handlers, handler]);
|
||||
}
|
||||
}
|
||||
private send = (payload: Partial<Payload>) => {
|
||||
payload.s = this.session?.s ?? null;
|
||||
console.log('sending: %s', payload);
|
||||
this.socket.send(JSON.stringify(payload));
|
||||
};
|
||||
|
||||
private indentify = () => {
|
||||
this.send({
|
||||
op: 2,
|
||||
d: {
|
||||
token: this.token,
|
||||
intents: this.intents,
|
||||
properties: {
|
||||
os: process.platform,
|
||||
browser: 'discord.ts',
|
||||
device: 'discord.ts'
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private heartbeat = () => {
|
||||
if (!this.heartbeat_interval) {
|
||||
throw new Error('No heartbeat interval set');
|
||||
}
|
||||
if (this.heartbeat_function) {
|
||||
clearInterval(this.heartbeat_function);
|
||||
}
|
||||
if (this.heartbeat_timeout) {
|
||||
clearTimeout(this.heartbeat_timeout);
|
||||
}
|
||||
this.heartbeat_function = setInterval(() => {
|
||||
this.send({
|
||||
op: 1,
|
||||
d: {}
|
||||
});
|
||||
}, this.heartbeat_interval);
|
||||
this.heartbeat_timeout = setTimeout(() => {
|
||||
this.reconnect();
|
||||
}, this.heartbeat_interval * 2);
|
||||
};
|
||||
|
||||
private initialize = (payload: Payload) => {
|
||||
this.session = payload.d.session;
|
||||
this.user = payload.d.user;
|
||||
this.user.tag = `${this.user.username}#${this.user.discriminator}`;
|
||||
// Construct presence
|
||||
const status: Presence = {
|
||||
status: Status.Online,
|
||||
since: null,
|
||||
activities: [
|
||||
{
|
||||
name: payload.d.guilds.length + ' servers',
|
||||
type: ActivityType.Watching,
|
||||
created_at: Date.now()
|
||||
}
|
||||
],
|
||||
afk: false
|
||||
};
|
||||
// Set presence
|
||||
this.setPresence(status);
|
||||
// Cache guilds? (these are not whole guilds, just guild ids)
|
||||
// for (const guild of payload.d.guilds) {
|
||||
// this.guilds.set(guild.id, guild);
|
||||
// }
|
||||
console.log('session: %s', this.session);
|
||||
console.log('user: %s', this.user);
|
||||
};
|
||||
|
||||
private parseEvent = (payload: Payload) => {
|
||||
// If not a dispatch event, return
|
||||
if (!payload.t) {
|
||||
return;
|
||||
}
|
||||
// Run default handlers
|
||||
switch (payload.t) {
|
||||
case 'READY':
|
||||
this.initialize(payload);
|
||||
break;
|
||||
case 'INTERACTION_CREATE':
|
||||
this.handleInteractionCreate(payload);
|
||||
break;
|
||||
case 'MESSAGE_CREATE':
|
||||
this.handleMessageCreate(payload);
|
||||
break;
|
||||
case 'MESSAGE_UPDATE':
|
||||
this.handleMessageCreate(payload);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Call event handlers
|
||||
const handlers = this.handlers.get(payload.t);
|
||||
const context = new Context({ client: this.client, message: payload.d });
|
||||
if (handlers) {
|
||||
for (const handler of handlers) {
|
||||
handler instanceof Handler
|
||||
? handler.execute(this.client, context, payload.d)
|
||||
: handler(this.client, context, payload.d);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleInteractionCreate = (payload: Payload) => {
|
||||
if (payload.d.type === 2) {
|
||||
this.handleShashInteraction(payload);
|
||||
} else {
|
||||
this.handleChatInteraction(payload);
|
||||
}
|
||||
};
|
||||
|
||||
private handleShashInteraction = (payload: Payload) => {
|
||||
console.log(payload.d);
|
||||
const context = new InteractionContext({ client: this.client, interaction: payload.d });
|
||||
const handler = this.client.commands.application.get(payload.d.data.name);
|
||||
if (handler) {
|
||||
if (handler instanceof SlashCommand) {
|
||||
handler.execute(this.client, context, payload.d);
|
||||
} else {
|
||||
handler(this.client, context, payload.d);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleChatInteraction = (payload: Payload) => {
|
||||
const context = new InteractionContext({ client: this.client, interaction: payload.d });
|
||||
const handlers = this.client.interactions.get(payload.d.data.custom_id);
|
||||
if (handlers) {
|
||||
for (const handler of handlers) {
|
||||
if (handler instanceof Interaction) {
|
||||
handler.execute(this.client, context, payload.d);
|
||||
} else {
|
||||
handler(this.client, context, payload.d);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private handleMessageCreate = async (payload: Payload) => {
|
||||
// Ignore bots
|
||||
if (payload.d.author.bot) {
|
||||
return;
|
||||
}
|
||||
console.log(payload.d);
|
||||
const content = payload.d.content.trim();
|
||||
// Is this a command?
|
||||
if (content.startsWith(this.client.prefix) | content.startsWith(`<@${this.client.user.id}>`)) {
|
||||
// Parse command
|
||||
const args = content
|
||||
.slice(this.client.prefix.length)
|
||||
.trim()
|
||||
.split(/\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g);
|
||||
// Check if command exists
|
||||
const handler = this.client.commands.chat.get(args[0]);
|
||||
// Generate context
|
||||
const context = new Context({ client: this.client, message: payload.d });
|
||||
if (handler) {
|
||||
// Execute command
|
||||
handler instanceof Command
|
||||
? handler.execute(this.client, context, args.slice(1))
|
||||
: handler(this.client, context, args.slice(1));
|
||||
} else {
|
||||
let reply = null;
|
||||
// Send error message if command does not exist
|
||||
if (this.commandNotFound) {
|
||||
// If custom command not found handler is set, use that
|
||||
reply = this.commandNotFound(this.client, context);
|
||||
} else {
|
||||
// Otherwise send default message
|
||||
reply = await context.reply('Command not found :(', true);
|
||||
}
|
||||
if (reply) {
|
||||
// If reply is set, delete it after 3 seconds
|
||||
setTimeout(() => {
|
||||
context.delete(reply.id);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private parseOpCode = (payload: Payload) => {
|
||||
switch (payload.op) {
|
||||
case 0:
|
||||
this.parseEvent(payload);
|
||||
break;
|
||||
case 9:
|
||||
setTimeout(this.reconnect, 500);
|
||||
break;
|
||||
case 10:
|
||||
this.heartbeat_interval = payload.d.heartbeat_interval;
|
||||
this.heartbeat();
|
||||
this.indentify();
|
||||
break;
|
||||
case 11:
|
||||
console.log('heartbeat acknowledged');
|
||||
this.heartbeat_timeout.refresh();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
private onMessage = (packet: Data): void => {
|
||||
console.log('============================');
|
||||
let data: Payload | null = null;
|
||||
try {
|
||||
data = JSON.parse(packet.toString());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
if (data) {
|
||||
console.log('received: %s', data);
|
||||
console.log('received data: %s', data.d);
|
||||
if (this.session && data.s && data.s > this.session.s) {
|
||||
this.session.s = data.s;
|
||||
} else {
|
||||
this.parseOpCode(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onOpen = () => {
|
||||
console.log('Socket open');
|
||||
};
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Gateway, ObjectCache } from ".";
|
||||
import { Guild } from "../types";
|
||||
|
||||
export class Guilds extends ObjectCache<Guild> {
|
||||
constructor ({ token, gateway }: { token: string, gateway: Gateway }) {
|
||||
super({token, gateway, type: 'guilds', events: ['GUILD_CREATE', 'GUILD_UPDATE']});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export * from './gateway';
|
||||
export * from './cache';
|
||||
export * from './guilds';
|
||||
export * from './context';
|
||||
export * from './message';
|
||||
export * from './embed';
|
||||
export * from './interaction';
|
||||
@@ -1,61 +0,0 @@
|
||||
import { Client } from "..";
|
||||
import { InteractionResponseType } from "../types";
|
||||
import { Message } from "./message";
|
||||
|
||||
export class InteractionContext {
|
||||
public client: Client;
|
||||
public interaction: Record<string, any>;
|
||||
private is_loading: boolean;
|
||||
constructor({ client, interaction, is_loading = false }: { client: Client; interaction: Record<string, any>, is_loading?: boolean }) {
|
||||
this.client = client;
|
||||
this.interaction = interaction;
|
||||
this.is_loading = is_loading;
|
||||
}
|
||||
|
||||
ack = (type: InteractionResponseType = InteractionResponseType.Pong, data?: Record<string, any>) => {
|
||||
return this.client.api.post(`/interactions/${this.interaction.id}/${this.interaction.token}/callback`, {
|
||||
type,
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
reply = (message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
return this.ack(InteractionResponseType.Reply, {
|
||||
...message
|
||||
});
|
||||
};
|
||||
|
||||
edit = (message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
if (!this.is_loading) {
|
||||
return this.ack(InteractionResponseType.UpdateMessage, {
|
||||
...message
|
||||
});
|
||||
} else {
|
||||
return this.client.api.patch(
|
||||
`/webhooks/${this.client.application_id}/${this.interaction.token}/messages/@original`,
|
||||
{
|
||||
type: InteractionResponseType.DeferredUpdateMessage,
|
||||
...message
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
deleteOriginal = () => {
|
||||
return this.client.api.delete(
|
||||
`/webhooks/${this.client.application_id}/${this.interaction.token}/messages/@original`
|
||||
);
|
||||
};
|
||||
|
||||
loading = async () => {
|
||||
await this.ack(InteractionResponseType.DeferredReply);
|
||||
return new InteractionContext({ client: this.client, interaction: this.interaction, is_loading: true });
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { Client } from "./client";
|
||||
|
||||
export class API {
|
||||
private readonly base_url: string = 'https://discord.com/api/v10';
|
||||
|
||||
constructor(private client: Client) {}
|
||||
|
||||
async request<T = any>(method: string, path: string, data?: any): Promise<T> {
|
||||
const url = `${this.base_url}${path}`;
|
||||
const headers = {
|
||||
Authorization: `Bot ${this.client.token}`,
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
console.log(`API Request: ${method} ${url}`);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers,
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch: ${response.statusText} ${response.url} ${response.status}`);
|
||||
console.error(response.json());
|
||||
throw new Error(`Failed to fetch: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const body = await response.json();
|
||||
|
||||
console.log('API Response:', JSON.stringify(body, null, 4));
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
async get<T = any>(path: string): Promise<T> {
|
||||
return this.request('GET', path);
|
||||
}
|
||||
|
||||
async post<T = any>(path: string, data: any): Promise<T> {
|
||||
return this.request('POST', path, data);
|
||||
}
|
||||
|
||||
async patch<T = any>(path: string, data: any): Promise<T> {
|
||||
return this.request('PATCH', path, data);
|
||||
}
|
||||
|
||||
async delete<T = any>(path: string): Promise<T> {
|
||||
return this.request('DELETE', path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { Context } from "../types/context";
|
||||
import { exists } from "../utils";
|
||||
import type { API } from "./api";
|
||||
import type { Processor } from "./processor";
|
||||
|
||||
|
||||
export class ObjectCache<T> {
|
||||
cache = new Map<string, T>();
|
||||
private readonly type: string;
|
||||
|
||||
constructor(private api: API, processor: Processor, type: string, events: string[]) {
|
||||
this.type = type;
|
||||
processor.events.register(events, (_context: Context, e: Record<string, any>) => {
|
||||
this.cache.set(e.id, e as T);
|
||||
});
|
||||
}
|
||||
|
||||
set(object_id: string, object: T): void {
|
||||
this.cache.set(object_id, object);
|
||||
}
|
||||
|
||||
// update(object_id: string, object: Partial<T>): void {
|
||||
// const existing = this.cache.get(object_id);
|
||||
|
||||
// if (exists(existing)) {
|
||||
// // TODO: Idk wtf happens if this is called on two classes
|
||||
// this.cache.set(object_id, { ...existing, ...object });
|
||||
// } else {
|
||||
// this.cache.set(object_id, object as T);
|
||||
// }
|
||||
// }
|
||||
|
||||
async fetch(object_id: string): Promise<T> {
|
||||
return this.api.get(`${this.type}/${object_id}`).then((res) => {
|
||||
const object: T & { id: string } = res.data;
|
||||
this.cache.set(object.id, object);
|
||||
return object;
|
||||
});
|
||||
}
|
||||
|
||||
get(object_id: string): T | Promise<T> {
|
||||
if (this.cache.has(object_id)) {
|
||||
return Promise.resolve(this.cache.get(object_id)!);
|
||||
} else {
|
||||
return this.fetch(object_id);
|
||||
}
|
||||
}
|
||||
|
||||
list(): Map<string, T> {
|
||||
return this.cache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import { API } from './api';
|
||||
import { Gateway } from './gateway';
|
||||
import { Processor } from './processor';
|
||||
import { Intent, type ClientConfig } from '../types/common';
|
||||
import { exists, isNil } from '../utils/index';
|
||||
import type { Handler } from '../types/hander';
|
||||
import { Message } from '../types/message';
|
||||
import { Guilds } from './guilds';
|
||||
import { Users } from './users';
|
||||
|
||||
export class Client {
|
||||
readonly token: string;
|
||||
readonly application_id: string;
|
||||
readonly intents: Intent[] = [Intent.DEFAULT];
|
||||
prefix: string;
|
||||
// Internal services
|
||||
protected _api: API;
|
||||
protected _processor: Processor;
|
||||
protected _gateway: Gateway;
|
||||
guilds: Guilds;
|
||||
users: Users;
|
||||
|
||||
constructor(config: ClientConfig) {
|
||||
// Validate the configuration
|
||||
Client.validateConfig(config);
|
||||
// Save the configuration
|
||||
this.token = config.token;
|
||||
this.application_id = config.application_id;
|
||||
this.intents = config.intents ?? this.intents;
|
||||
this.prefix = config.prefix ?? '!';
|
||||
// Initialize internal services
|
||||
this._api = new API(this);
|
||||
this._processor = new Processor(this);
|
||||
this._gateway = new Gateway(this, this.processor, this.api);
|
||||
this.guilds = new Guilds(this.api, this.processor);
|
||||
this.users = new Users(this.api, this.processor);
|
||||
}
|
||||
|
||||
private get api() {
|
||||
return this._api;
|
||||
}
|
||||
|
||||
private get gateway() {
|
||||
return this._gateway;
|
||||
}
|
||||
|
||||
private get processor() {
|
||||
return this._processor;
|
||||
}
|
||||
|
||||
private static validateConfig(config: ClientConfig) {
|
||||
if (isNil(config.token)) {
|
||||
throw new Error('Token is required');
|
||||
}
|
||||
|
||||
if (isNil(config.application_id)) {
|
||||
throw new Error('Application ID is required');
|
||||
}
|
||||
|
||||
if (exists(config.prefix) && typeof config.prefix !== 'string') {
|
||||
throw new Error('Prefix must be a string');
|
||||
}
|
||||
|
||||
if (exists(config.intents)) {
|
||||
if (!Array.isArray(config.intents)) {
|
||||
throw new Error('Intents must be an array');
|
||||
}
|
||||
|
||||
for (const intent of config.intents) {
|
||||
if (!Intent[intent]) {
|
||||
throw new Error(`Invalid intent: ${intent}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage = async (channel_id: string, message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
|
||||
return Message.fromAPIResponse(await this.api.post(`/channels/${channel_id}/messages`, message.toJSON()));
|
||||
}
|
||||
|
||||
chatCommands = {
|
||||
register: (name: string | string[], handler: Handler) => this.processor.commands.register(name, handler),
|
||||
unregister: (name: string | string[]) => this.processor.commands.unregister(name),
|
||||
has: (name: string) => this.processor.commands.has(name)
|
||||
}
|
||||
|
||||
connect = () => this.gateway.connect();
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import { WebSocket, type Data } from 'ws';
|
||||
import type { Client } from "./client";
|
||||
import type { Payload } from "../types/common";
|
||||
import { exists, isNil, isObject, log } from "../utils";
|
||||
import type { API } from "./api";
|
||||
import type { Processor } from './processor';
|
||||
import { Context } from '../types/context';
|
||||
import { Message } from '../types/message';
|
||||
import { User } from '../types/user';
|
||||
|
||||
export class Gateway {
|
||||
private socket: WebSocket | null = null;
|
||||
private heartbeat_interval: number | null = null;
|
||||
private heartbeat_timeout: Timer | null = null;
|
||||
private sequence: number | null = null;
|
||||
// Internal state
|
||||
private user: any;
|
||||
|
||||
constructor(private client: Client, private processor: Processor, private api: API) {}
|
||||
|
||||
private onOpen = () => {
|
||||
log('Connected to gateway');
|
||||
}
|
||||
|
||||
private onMessage = (packet: Data) => {
|
||||
// console.log(packet);
|
||||
// Allocate a variable to store the parsed payload
|
||||
let data: Payload | null = null;
|
||||
|
||||
try {
|
||||
// Attempt to parse the packet data
|
||||
data = JSON.parse(packet.toString());
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
log('='.repeat(25));
|
||||
log('received: ', data.op);
|
||||
log('received event: ', data.t ?? 'no event');
|
||||
log('received data: ', exists(data.d) ? Object.keys(data.d) : 'no data');
|
||||
log('='.repeat(25));
|
||||
// If the data is successfully parsed check the opcode
|
||||
const code = data.op;
|
||||
if (code === 0) {
|
||||
// Event received
|
||||
this.parseEvent(data);
|
||||
} else if (code === 1) {
|
||||
// Heartbeat requested
|
||||
this.send({ op: 1, d: this.sequence });
|
||||
} else if (code === 9) {
|
||||
// Reconnect requested
|
||||
} else if (code === 10) {
|
||||
// Hello
|
||||
if (!isObject(data.d)) {
|
||||
throw new Error('Invalid HELLO payload received');
|
||||
}
|
||||
// Store the heartbeat interval
|
||||
this.heartbeat_interval = data.d?.heartbeat_interval;
|
||||
// Kick off the heartbeat loop
|
||||
this.heartbeat();
|
||||
setTimeout(this.indentify, 2500);
|
||||
} else if (code === 11) {
|
||||
// Heartbeat ACK
|
||||
log('Heartbeat ACK');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private heartbeat() {
|
||||
log('Sending heartbeat');
|
||||
if (exists(this.heartbeat_interval)) {
|
||||
// If a previous heartbeat timeout exists, clear it
|
||||
if (exists(this.heartbeat_timeout)) {
|
||||
clearTimeout(this.heartbeat_timeout);
|
||||
}
|
||||
// Set a timeout to ensure the heartbeat is sent within the interval
|
||||
this.heartbeat_timeout = setTimeout(() => {
|
||||
this.send({ op: 1, d: this.sequence });
|
||||
this.heartbeat();
|
||||
}, this.heartbeat_interval * Math.random());
|
||||
} else {
|
||||
throw new Error('Heartbeat interval not set');
|
||||
}
|
||||
}
|
||||
|
||||
protected send = (payload: Partial<Payload>) => {
|
||||
if (isNil(this.socket)) {
|
||||
throw new Error('Socket is not connected');
|
||||
}
|
||||
|
||||
log('='.repeat(25));
|
||||
log('sending: ', JSON.stringify(payload, null, 4));
|
||||
log('='.repeat(25));
|
||||
|
||||
this.socket.send(JSON.stringify(payload));
|
||||
};
|
||||
|
||||
private indentify = () => {
|
||||
this.send({
|
||||
op: 2,
|
||||
d: {
|
||||
token: this.client.token,
|
||||
intents: this.client.intents.reduce((acc, intent) => acc | intent, 0),
|
||||
properties: {
|
||||
os: process.platform,
|
||||
browser: 'discord.ts',
|
||||
device: 'discord.ts'
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private parseEvent(payload: Payload) {
|
||||
if (isNil(payload.t)) {
|
||||
throw new Error('Event name is required');
|
||||
}
|
||||
|
||||
log('Event:', payload.t);
|
||||
|
||||
const event_name = payload.t;
|
||||
const event_data = payload.d;
|
||||
|
||||
if (!isObject(event_data)) {
|
||||
throw new Error('Invalid event data received');
|
||||
}
|
||||
|
||||
if (event_name === 'READY') {
|
||||
this.user = event_data.user;
|
||||
log('User:', this.user);
|
||||
} else if (event_name === 'INTERACTION_CREATE') {
|
||||
// this.handleInteractionCreate(payload);
|
||||
} else if (event_name === 'MESSAGE_CREATE') {
|
||||
this.handleMessageCreate(payload as Payload);
|
||||
} else if (event_name === 'MESSAGE_UPDATE') {
|
||||
// this.handleMessageCreate(payload);
|
||||
}
|
||||
|
||||
// Allocate a var to store the context
|
||||
let context = null;
|
||||
|
||||
if (['MESSAGE_CREATE', 'MESSAGE_UPDATE'].includes(event_name)) {
|
||||
// If the event is a message event, create a message object
|
||||
const message = Message.fromAPIResponse(event_data);
|
||||
// And then generate the context
|
||||
context = new Context(this.client, this.api, message);
|
||||
}
|
||||
|
||||
// Call event handlers
|
||||
this.processor.events.execute(event_name, context, event_data);
|
||||
}
|
||||
|
||||
private handleMessageCreate = async (payload: Payload) => {
|
||||
if (!isObject(payload.d)) {
|
||||
throw new Error('Invalid MESSAGE_CREATE payload received');
|
||||
}
|
||||
|
||||
// Ignore bots
|
||||
if (payload.d.author.bot) {
|
||||
return;
|
||||
}
|
||||
// Ignore self
|
||||
if (payload.d.author.id === this.user.id) {
|
||||
return;
|
||||
}
|
||||
// Extract the author
|
||||
const author = User.fromAPIResponse(payload.d.author);
|
||||
// Store the author in the cache
|
||||
this.client.users.set(author.id, author);
|
||||
// Extract the content
|
||||
const content = payload.d.content.trim();
|
||||
// Is this a command?
|
||||
if (content.startsWith(this.client.prefix) || content.startsWith(`<@${this.user.id}>`)) {
|
||||
// Determine the prefix
|
||||
const prefix = content.startsWith(this.client.prefix) ? this.client.prefix : `<@${this.user.id}> `;
|
||||
// Parse command
|
||||
const args = content
|
||||
.slice(prefix.length)
|
||||
.trim()
|
||||
.split(/\s+(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/g);
|
||||
// Ignore empty commands
|
||||
if (args.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Extract the command name
|
||||
// TODO: Case insensitive config?
|
||||
const command = args.shift()!;
|
||||
// Create a new message object from the payload
|
||||
const message = Message.fromAPIResponse(payload.d);
|
||||
// Generate context
|
||||
const context = new Context(this.client, this.api, message);
|
||||
// Check if command exists
|
||||
if (this.processor.commands.has(command)) {
|
||||
// Execute the handler
|
||||
this.processor.commands.execute(command, context, args);
|
||||
} else {
|
||||
// TODO: Custom unknown command handler
|
||||
context.reply(`Unknown command: ${command}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async connect() {
|
||||
// Fetch the Discord gateway URL
|
||||
const response = await this.api.request('GET', '/gateway/bot');
|
||||
// Append the query parameters to the URL
|
||||
const url = new URL(response.url);
|
||||
url.searchParams.append('v', '10');
|
||||
url.searchParams.append('encoding', 'json');
|
||||
// Open a new WebSocket connection to the Discord gateway
|
||||
this.socket = new WebSocket(url);
|
||||
// Register event listeners
|
||||
this.socket.on('open', this.onOpen);
|
||||
this.socket.on('message', this.onMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { Guild } from "../types/guild";
|
||||
import type { API } from "./api";
|
||||
import { ObjectCache } from "./cache";
|
||||
import type { Processor } from "./processor";
|
||||
|
||||
export class Guilds extends ObjectCache<Guild> {
|
||||
constructor (api: API, processor: Processor) {
|
||||
super(api, processor, 'guilds', ['GUILD_CREATE', 'GUILD_UPDATE']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { Client } from "./client";
|
||||
import type { Context } from "../types/context";
|
||||
import { HandlerType, type EventPayload, type Payload } from "../types/common";
|
||||
import { BaseHandler, type Handler } from "../types/hander";
|
||||
|
||||
export class Processor {
|
||||
private handlers = {
|
||||
[HandlerType.Events]: new Map<string, Handler[]>(),
|
||||
[HandlerType.Commands]: new Map<string, Handler[]>(),
|
||||
[HandlerType.Interactions]: new Map<string, Handler[]>()
|
||||
};
|
||||
|
||||
constructor(private client: Client) {};
|
||||
|
||||
public register(type: HandlerType, event: string | string[], handler: Handler) {
|
||||
// Normalize events into an array
|
||||
const events = Array.isArray(event) ? event : [event];
|
||||
// Register the handler for each event
|
||||
for (const e of events) {
|
||||
const handlers = this.handlers[type].get(e) || [];
|
||||
handlers.push(handler);
|
||||
this.handlers[type].set(e, handlers);
|
||||
}
|
||||
};
|
||||
|
||||
public unregister(type: HandlerType, event: string | string[], handler?: Handler) {
|
||||
// Normalize events into an array
|
||||
const events = Array.isArray(event) ? event : [event];
|
||||
// Unregister the handler for each event
|
||||
for (const e of events) {
|
||||
if (handler) {
|
||||
// Get the handlers for the event
|
||||
const handlers = this.handlers[type].get(e);
|
||||
if (handlers) {
|
||||
// Find the index of the handler
|
||||
const index = handlers.indexOf(handler);
|
||||
if (index !== -1) {
|
||||
// Remove the handler from the array
|
||||
handlers.splice(index, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If no handler is provided, clear all handlers for the event
|
||||
this.handlers[type].clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public execute(type: HandlerType.Events, event: string, context: Context | null, payload: EventPayload): void
|
||||
public execute(type: Omit<HandlerType, HandlerType.Events>, event: string, context: Context, args: any[]): void
|
||||
public execute(type: HandlerType, event: string, context: Context | null, argsOrPayload: any[] | EventPayload) {
|
||||
const handlers = this.handlers[type].get(event);
|
||||
|
||||
if (handlers) {
|
||||
for (const handler of handlers) {
|
||||
if (handler instanceof BaseHandler) {
|
||||
handler.execute(context, argsOrPayload);
|
||||
} else {
|
||||
// TODO: Fix ts error
|
||||
handler(context, argsOrPayload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HandlerType.Events] = {
|
||||
register: (event: string | string[], handler: Handler) => this.register(HandlerType.Events, event, handler),
|
||||
unregister: (event: string | string[], handler?: Handler) => this.unregister(HandlerType.Events, event, handler),
|
||||
execute: (event: string, context: Context | null, payload: EventPayload) => this.execute(HandlerType.Events, event, context, payload),
|
||||
has: (event: string) => this.handlers[HandlerType.Events].has(event) && this.handlers[HandlerType.Events].get(event)?.length! > 0
|
||||
};
|
||||
|
||||
[HandlerType.Commands] = {
|
||||
register: (event: string | string[], handler: Handler) => this.register(HandlerType.Commands, event, handler),
|
||||
unregister: (event: string | string[], handler?: Handler) => this.unregister(HandlerType.Commands, event, handler),
|
||||
execute: (event: string, context: Context, args: any[]) => this.execute(HandlerType.Commands, event, context, args),
|
||||
has: (event: string) => this.handlers[HandlerType.Commands].has(event) ?? this.handlers[HandlerType.Commands].get(event)?.length! > 0
|
||||
};
|
||||
|
||||
[HandlerType.Interactions] = {
|
||||
register: (event: string | string[], handler: Handler) => this.register(HandlerType.Interactions, event, handler),
|
||||
unregister: (event: string | string[], handler?: Handler) => this.unregister(HandlerType.Interactions, event, handler),
|
||||
execute: (event: string, context: Context, args: any[]) => this.execute(HandlerType.Interactions, event, context, args),
|
||||
has: (event: string) => this.handlers[HandlerType.Interactions].has(event) ?? this.handlers[HandlerType.Interactions].get(event)?.length! > 0
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { User } from "../types/user";
|
||||
import type { API } from "./api";
|
||||
import { ObjectCache } from "./cache";
|
||||
import type { Processor } from "./processor";
|
||||
|
||||
export class Users extends ObjectCache<User> {
|
||||
constructor (api: API, processor: Processor) {
|
||||
super(api, processor, 'users', []);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export class Channel {
|
||||
version: number;
|
||||
type: number;
|
||||
position: number;
|
||||
permission_overwrites: any[];
|
||||
name: string;
|
||||
id: string;
|
||||
flags: number;
|
||||
topic?: string | null;
|
||||
rate_limit_per_user?: number;
|
||||
parent_id?: string;
|
||||
last_message_id?: string | null;
|
||||
user_limit?: number;
|
||||
rtc_region?: string | null;
|
||||
bitrate?: number;
|
||||
last_pin_timestamp?: string;
|
||||
|
||||
constructor(data: any) {
|
||||
this.version = data.version;
|
||||
this.type = data.type;
|
||||
this.position = data.position;
|
||||
this.permission_overwrites = data.permission_overwrites;
|
||||
this.name = data.name;
|
||||
this.id = data.id;
|
||||
this.flags = data.flags;
|
||||
this.topic = data.topic;
|
||||
this.rate_limit_per_user = data.rate_limit_per_user;
|
||||
this.parent_id = data.parent_id;
|
||||
this.last_message_id = data.last_message_id;
|
||||
this.user_limit = data.user_limit;
|
||||
this.rtc_region = data.rtc_region;
|
||||
this.bitrate = data.bitrate;
|
||||
this.last_pin_timestamp = data.last_pin_timestamp;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): Channel {
|
||||
return new Channel(data);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Client } from '..';
|
||||
import { Context } from '../lib';
|
||||
|
||||
export abstract class Command {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly execute: CommandFunction;
|
||||
constructor(data: Command) {
|
||||
this.name = data.name;
|
||||
this.description = data.description;
|
||||
this.execute = data.execute;
|
||||
}
|
||||
}
|
||||
|
||||
export type CommandFunction = (client: Client, context: Context, ...args: any[]) => void;
|
||||
|
||||
export class ChatCommand extends Command {
|
||||
constructor(data: ChatCommand) {
|
||||
super(data);
|
||||
}
|
||||
}
|
||||
+35
-76
@@ -1,81 +1,40 @@
|
||||
import type { Client } from "../services/client";
|
||||
import type { Context } from "./context";
|
||||
import type { BaseHandler } from "./hander";
|
||||
|
||||
export type ClientConfig = {
|
||||
token: string;
|
||||
application_id: string;
|
||||
prefix?: string;
|
||||
intents?: Intent[];
|
||||
}
|
||||
export enum HandlerType {
|
||||
Events = 'events',
|
||||
Commands = 'commands',
|
||||
Interactions = 'interactions'
|
||||
}
|
||||
|
||||
|
||||
export type EventPayload = any;
|
||||
|
||||
export type Primitive = string | number | boolean | null;
|
||||
|
||||
/**
|
||||
* Represents a payload structure used in the Discord API.
|
||||
*
|
||||
* @typedef {Object} Payload
|
||||
* @property {number} op - The opcode of the payload.
|
||||
* @property {Record<string, any>} d - The data associated with the payload.
|
||||
* @property {number | null} s - The sequence number, or null if not applicable.
|
||||
* @property {string | null} t - The event name, or null if not applicable.
|
||||
*/
|
||||
export type Payload = {
|
||||
op: number; // opcode
|
||||
d: Record<string, any>; // data
|
||||
s: number | null; // sequence number
|
||||
t: string | null; // event name
|
||||
op: number;
|
||||
d: Record<string, any> | Primitive | null;
|
||||
s: number | null;
|
||||
t: string | null;
|
||||
};
|
||||
|
||||
export type Session = {
|
||||
s: number; // sequence number
|
||||
session_id: string;
|
||||
session_type: string;
|
||||
resume_gateway_url: string;
|
||||
};
|
||||
|
||||
export type Guild = Record<string, any>;
|
||||
|
||||
export type User = Record<string, any>;
|
||||
|
||||
export enum Status {
|
||||
Online = "online",
|
||||
DND = "dnd",
|
||||
Idle = "idle",
|
||||
Invisible = "invisible",
|
||||
Offline = "offline"
|
||||
}
|
||||
|
||||
export type Presence = {
|
||||
since: number | null;
|
||||
activities: Activity[];
|
||||
status: Status;
|
||||
afk: boolean;
|
||||
}
|
||||
|
||||
export type Activity = {
|
||||
name: string;
|
||||
type: number;
|
||||
created_at: number;
|
||||
url?: string;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
application_id?: string;
|
||||
details?: string;
|
||||
state?: string;
|
||||
emoji?: {
|
||||
name: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
};
|
||||
party?: {
|
||||
id?: string;
|
||||
size?: [number, number];
|
||||
};
|
||||
assets?: {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
};
|
||||
secrets?: {
|
||||
join?: string;
|
||||
spectate?: string;
|
||||
match?: string;
|
||||
};
|
||||
instance?: boolean;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export enum ActivityType {
|
||||
Game,
|
||||
Streaming,
|
||||
Listening,
|
||||
Watching,
|
||||
Custom ,
|
||||
Competing
|
||||
}
|
||||
|
||||
export enum Intent {
|
||||
GUILDS = 1 << 0,
|
||||
GUILD_MEMBERS = 1 << 1,
|
||||
@@ -97,7 +56,7 @@ export enum Intent {
|
||||
ALL = 32767
|
||||
}
|
||||
|
||||
export const IntentGroup : {[key in Intent]: [string[], number]} = {
|
||||
export const IntentGroup : {[key in Intent]?: [string[], number]} = {
|
||||
[Intent.GUILDS]: [[
|
||||
"GUILD_CREATE",
|
||||
"GUILD_UPDATE",
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import type { API } from "../services/api";
|
||||
import type { Client } from "../services/client";
|
||||
import { isNil } from "../utils";
|
||||
import { Message } from "./message";
|
||||
/**
|
||||
* Represents the context of a message in a Discord channel.
|
||||
* Provides methods to interact with the message and the channel it was sent in.
|
||||
*
|
||||
* @class Context
|
||||
*
|
||||
* @property {string} message_id - The ID of the message that this context is associated with.
|
||||
* @property {string} channel_id - The ID of the channel where the message was sent.
|
||||
* @property {string} [guild_id] - The ID of the guild where the message was sent, if applicable.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Client} client - The client instance that is interacting with the Discord API.
|
||||
* @param {API} api - The API instance used to make requests to the Discord API.
|
||||
* @param {Message} message - The message object that this context is based on.
|
||||
*
|
||||
* @throws {Error} If the message ID is not provided.
|
||||
* @throws {Error} If the channel ID is not provided.
|
||||
*/
|
||||
export class Context {
|
||||
// Public properties
|
||||
message_id: string;
|
||||
channel_id: string;
|
||||
guild_id?: string;
|
||||
|
||||
constructor(private client: Client, private api: API, private _message: Message) {
|
||||
if (isNil(_message.id)) {
|
||||
throw new Error('Message ID is required to create a context');
|
||||
}
|
||||
|
||||
if (isNil(_message.channel_id)) {
|
||||
throw new Error('Channel ID is required to create a context');
|
||||
}
|
||||
|
||||
this.client = client;
|
||||
this.message_id = _message.id;
|
||||
this.channel_id = _message.channel_id;
|
||||
this.guild_id = _message.guild_id;
|
||||
}
|
||||
|
||||
// get author() {
|
||||
// return this.client.users.get(this.author_id);
|
||||
// };
|
||||
|
||||
get message(): Message & { getReference: () => Promise<Message | null> } {
|
||||
return Object.assign(this._message, {
|
||||
getReference: async () => {
|
||||
const reference = this._message.message_reference;
|
||||
if (isNil(reference)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Message.fromAPIResponse(await this.api.get(`/channels/${reference.channel_id}/messages/${reference.message_id}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fetchGuild() {
|
||||
if (isNil(this.guild_id)) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return this.client.guilds.get(this.guild_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reply to the message that this command
|
||||
* @param message The message to send to the channel
|
||||
* @param reference If true the message will be a reference the the message that triggered the command
|
||||
* @returns The message that was sent
|
||||
*/
|
||||
public reply = (message: Message | string, reference: boolean = true) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
|
||||
if (reference) {
|
||||
message.message_reference = {
|
||||
message_id: this.message_id,
|
||||
channel_id: this.channel_id,
|
||||
guild_id: this.guild_id
|
||||
};
|
||||
}
|
||||
|
||||
return this.client.sendMessage(this.channel_id, message);
|
||||
};
|
||||
|
||||
/**
|
||||
* @name sendMessage
|
||||
* @description Sends a message to the channel that this context is associated with.
|
||||
* @param {Message | string} message - The message to send to the channel.
|
||||
* @returns {Promise<Message>} The message that was sent.
|
||||
*/
|
||||
public sendMessage = (message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
return this.client.sendMessage(this.channel_id, message);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @name delete
|
||||
* @description Deletes a message in the channel that this context is associated with.
|
||||
* @param {string} [message_id] - The ID of the message to delete. If not provided, the message ID of this context will be used.
|
||||
* @returns {Promise<void>} A promise that resolves when the message is deleted.
|
||||
*/
|
||||
public delete = (message_id?: string) => {
|
||||
return this.api.delete(`/channels/${this.channel_id}/messages/${message_id ?? this.message_id}`);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @name edit
|
||||
* @description Edits a message in the channel that this context is associated with.
|
||||
* @param {Message | string} message - The updated message content.
|
||||
* @returns {Promise<Message>} The updated message.
|
||||
*/
|
||||
public edit = async (message: Message | string) => {
|
||||
if (typeof message === 'string') {
|
||||
message = new Message().setContent(message);
|
||||
}
|
||||
return Message.fromAPIResponse(await this.api.patch(`/channels/${this.channel_id}/messages/${this.message_id}`, message));
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Message } from '.';
|
||||
import type { Message } from './message';
|
||||
|
||||
type EmbedFooter = {
|
||||
text: string;
|
||||
@@ -0,0 +1,25 @@
|
||||
export class Emoji {
|
||||
version: number;
|
||||
roles: string[];
|
||||
require_colons: boolean;
|
||||
name: string;
|
||||
managed: boolean;
|
||||
id: string;
|
||||
available: boolean;
|
||||
animated: boolean;
|
||||
|
||||
constructor(data: any) {
|
||||
this.version = data.version;
|
||||
this.roles = data.roles;
|
||||
this.require_colons = data.require_colons;
|
||||
this.name = data.name;
|
||||
this.managed = data.managed;
|
||||
this.id = data.id;
|
||||
this.available = data.available;
|
||||
this.animated = data.animated;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): Emoji {
|
||||
return new Emoji(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import { Channel } from "./channel";
|
||||
import { Emoji } from "./emoji";
|
||||
import { Member } from "./member";
|
||||
import { Role } from "./role";
|
||||
|
||||
export class Guild {
|
||||
system_channel_id: string | null;
|
||||
premium_progress_bar_enabled: boolean;
|
||||
region: string;
|
||||
application_id: string | null;
|
||||
premium_tier: number;
|
||||
presences: any[];
|
||||
discovery_splash: string | null;
|
||||
latest_onboarding_question_id: string | null;
|
||||
unavailable: boolean;
|
||||
afk_channel_id: string | null;
|
||||
large: boolean;
|
||||
nsfw: boolean;
|
||||
max_video_channel_users: number;
|
||||
stickers: any[];
|
||||
id: string;
|
||||
premium_subscription_count: number;
|
||||
stage_instances: any[];
|
||||
preferred_locale: string;
|
||||
guild_scheduled_events: any[];
|
||||
member_count: number;
|
||||
name: string;
|
||||
default_message_notifications: number;
|
||||
system_channel_flags: number;
|
||||
hub_type: string | null;
|
||||
lazy: boolean;
|
||||
soundboard_sounds: any[];
|
||||
threads: any[];
|
||||
banner: string | null;
|
||||
description: string | null;
|
||||
clan: string | null;
|
||||
incidents_data: string | null;
|
||||
application_command_counts: any;
|
||||
embedded_activities: any[];
|
||||
icon: string | null;
|
||||
home_header: string | null;
|
||||
joined_at: string;
|
||||
safety_alerts_channel_id: string | null;
|
||||
verification_level: number;
|
||||
max_stage_video_channel_users: number;
|
||||
emojis: Emoji[];
|
||||
owner_id: string;
|
||||
public_updates_channel_id: string | null;
|
||||
features: string[];
|
||||
rules_channel_id: string | null;
|
||||
roles: Role[];
|
||||
afk_timeout: number;
|
||||
inventory_settings: any;
|
||||
activity_instances: any[];
|
||||
max_members: number;
|
||||
channels: Channel[];
|
||||
voice_states: any[];
|
||||
splash: string | null;
|
||||
mfa_level: number;
|
||||
members: Member[];
|
||||
version: number;
|
||||
vanity_url_code: string | null;
|
||||
nsfw_level: number;
|
||||
explicit_content_filter: number;
|
||||
|
||||
constructor(data: any) {
|
||||
this.system_channel_id = data.system_channel_id;
|
||||
this.premium_progress_bar_enabled = data.premium_progress_bar_enabled;
|
||||
this.region = data.region;
|
||||
this.application_id = data.application_id;
|
||||
this.premium_tier = data.premium_tier;
|
||||
this.presences = data.presences;
|
||||
this.discovery_splash = data.discovery_splash;
|
||||
this.latest_onboarding_question_id = data.latest_onboarding_question_id;
|
||||
this.unavailable = data.unavailable;
|
||||
this.afk_channel_id = data.afk_channel_id;
|
||||
this.large = data.large;
|
||||
this.nsfw = data.nsfw;
|
||||
this.max_video_channel_users = data.max_video_channel_users;
|
||||
this.stickers = data.stickers;
|
||||
this.id = data.id;
|
||||
this.premium_subscription_count = data.premium_subscription_count;
|
||||
this.stage_instances = data.stage_instances;
|
||||
this.preferred_locale = data.preferred_locale;
|
||||
this.guild_scheduled_events = data.guild_scheduled_events;
|
||||
this.member_count = data.member_count;
|
||||
this.name = data.name;
|
||||
this.default_message_notifications = data.default_message_notifications;
|
||||
this.system_channel_flags = data.system_channel_flags;
|
||||
this.hub_type = data.hub_type;
|
||||
this.lazy = data.lazy;
|
||||
this.soundboard_sounds = data.soundboard_sounds;
|
||||
this.threads = data.threads;
|
||||
this.banner = data.banner;
|
||||
this.description = data.description;
|
||||
this.clan = data.clan;
|
||||
this.incidents_data = data.incidents_data;
|
||||
this.application_command_counts = data.application_command_counts;
|
||||
this.embedded_activities = data.embedded_activities;
|
||||
this.icon = data.icon;
|
||||
this.home_header = data.home_header;
|
||||
this.joined_at = data.joined_at;
|
||||
this.safety_alerts_channel_id = data.safety_alerts_channel_id;
|
||||
this.verification_level = data.verification_level;
|
||||
this.max_stage_video_channel_users = data.max_stage_video_channel_users;
|
||||
this.emojis = data.emojis.map((emoji: any) => Emoji.fromAPIResponse(emoji));
|
||||
this.owner_id = data.owner_id;
|
||||
this.public_updates_channel_id = data.public_updates_channel_id;
|
||||
this.features = data.features;
|
||||
this.rules_channel_id = data.rules_channel_id;
|
||||
this.roles = data.roles.map((role: any) => Role.fromAPIResponse(role));
|
||||
this.afk_timeout = data.afk_timeout;
|
||||
this.inventory_settings = data.inventory_settings;
|
||||
this.activity_instances = data.activity_instances;
|
||||
this.max_members = data.max_members;
|
||||
this.channels = data.channels.map((channel: any) => Channel.fromAPIResponse(channel));
|
||||
this.voice_states = data.voice_states;
|
||||
this.splash = data.splash;
|
||||
this.mfa_level = data.mfa_level;
|
||||
this.members = data.members.map((member: any) => Member.fromAPIResponse(member));
|
||||
this.version = data.version;
|
||||
this.vanity_url_code = data.vanity_url_code;
|
||||
this.nsfw_level = data.nsfw_level;
|
||||
this.explicit_content_filter = data.explicit_content_filter;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): Guild {
|
||||
return new Guild(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { EventPayload } from "./common";
|
||||
import type { Context } from "./context";
|
||||
|
||||
export class BaseHandler {
|
||||
constructor() {};
|
||||
|
||||
public execute(context: Context | null, args: any[]) {};
|
||||
}
|
||||
|
||||
export type CommandHandlerFunction = (context: Context, args: any[]) => void;
|
||||
export type EventHandlerFunction = (context: Context | null, payload: EventPayload) => void;
|
||||
|
||||
export type HandlerFunction = CommandHandlerFunction | EventHandlerFunction;
|
||||
export type Handler = CommandHandlerFunction | EventHandlerFunction | BaseHandler;
|
||||
@@ -1,8 +0,0 @@
|
||||
import { Client, Context } from '..';
|
||||
|
||||
export abstract class Handler {
|
||||
abstract events: string | string[];
|
||||
abstract execute: HandlerFunction;
|
||||
}
|
||||
|
||||
export type HandlerFunction = (client: Client, context: Context, data: Record<string, any>) => void;
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from './common';
|
||||
export * from './command';
|
||||
export * from './slash_command';
|
||||
export * from './handler';
|
||||
export * from './interaction';
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Client } from '..';
|
||||
import { InteractionContext } from '../lib';
|
||||
|
||||
export abstract class Interaction {
|
||||
abstract custom_id: string | string[];
|
||||
abstract execute: InteractionFunction;
|
||||
}
|
||||
|
||||
export type InteractionFunction = (client: Client, context: InteractionContext, data: Record<string, any>) => void;
|
||||
|
||||
export enum InteractionResponseType {
|
||||
Pong = 1,
|
||||
Reply = 4,
|
||||
DeferredReply = 5,
|
||||
DeferredUpdateMessage = 6,
|
||||
UpdateMessage = 7,
|
||||
AutocompleteResult = 8,
|
||||
Modal = 9
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export class Member {
|
||||
user: any;
|
||||
roles: string[];
|
||||
premium_since: string | null;
|
||||
pending: boolean;
|
||||
nick: string | null;
|
||||
mute: boolean;
|
||||
joined_at: string;
|
||||
flags: number;
|
||||
deaf: boolean;
|
||||
communication_disabled_until: string | null;
|
||||
banner: string | null;
|
||||
avatar: string | null;
|
||||
|
||||
constructor(data: any) {
|
||||
this.user = data.user;
|
||||
this.roles = data.roles;
|
||||
this.premium_since = data.premium_since;
|
||||
this.pending = data.pending;
|
||||
this.nick = data.nick;
|
||||
this.mute = data.mute;
|
||||
this.joined_at = data.joined_at;
|
||||
this.flags = data.flags;
|
||||
this.deaf = data.deaf;
|
||||
this.communication_disabled_until = data.communication_disabled_until;
|
||||
this.banner = data.banner;
|
||||
this.avatar = data.avatar;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): Member {
|
||||
return new Member(data);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Embed } from '.';
|
||||
import { isNil } from '../utils';
|
||||
import { Embed } from './embed';
|
||||
import { User } from './user';
|
||||
|
||||
export type Component = Partial<Button | SelectMenu> & {
|
||||
type: ComponentType;
|
||||
@@ -70,24 +72,50 @@ export enum ComponentType {
|
||||
ActionRow = 1,
|
||||
Button,
|
||||
SelectMenu,
|
||||
TextInput
|
||||
TextInput,
|
||||
UserSelect,
|
||||
RoleSelect,
|
||||
MentionableSelect,
|
||||
ChannelSelect
|
||||
}
|
||||
|
||||
export class Message {
|
||||
id?: string;
|
||||
author?: User;
|
||||
channel_id?: string;
|
||||
guild_id?: string;
|
||||
public content?: string;
|
||||
public embeds: Embed[] = [];
|
||||
public components: Component[] = [];
|
||||
public message_reference?: {
|
||||
message_id?: string;
|
||||
channel_id?: string;
|
||||
type?: number;
|
||||
message_id: string;
|
||||
channel_id: string;
|
||||
guild_id?: string;
|
||||
fail_if_not_exists?: boolean;
|
||||
};
|
||||
|
||||
constructor() {}
|
||||
constructor(data?: Partial<Message>) {
|
||||
if (data) {
|
||||
this.id = data.id;
|
||||
this.author = User.fromAPIResponse(data.author);
|
||||
this.channel_id = data.channel_id;
|
||||
this.guild_id = data.guild_id;
|
||||
this.content = data.content;
|
||||
this.message_reference = data.message_reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static fromAPIResponse = (data: any) => {
|
||||
return new Message({
|
||||
id: data.id,
|
||||
author: data.author,
|
||||
channel_id: data.channel_id,
|
||||
guild_id: data.guild_id,
|
||||
content: data.content,
|
||||
message_reference: data.message_reference
|
||||
});
|
||||
}
|
||||
|
||||
public static button(config: Button): Component {
|
||||
return {
|
||||
@@ -115,7 +143,6 @@ export class Message {
|
||||
|
||||
public addComponent = (content: Component | string, ...components: Component[]) => {
|
||||
if (typeof content === 'string') {
|
||||
console.log('string');
|
||||
this.components.push({
|
||||
type: ComponentType.ActionRow,
|
||||
content,
|
||||
@@ -131,6 +158,10 @@ export class Message {
|
||||
};
|
||||
|
||||
public setReference = (message: Partial<Message>) => {
|
||||
if (isNil(message.id) || isNil(message.channel_id)) {
|
||||
throw new Error('Message ID and Channel ID are required');
|
||||
}
|
||||
|
||||
this.message_reference = {
|
||||
message_id: message.id,
|
||||
channel_id: message.channel_id,
|
||||
@@ -0,0 +1,35 @@
|
||||
export class Role {
|
||||
version: number;
|
||||
unicode_emoji: string | null;
|
||||
tags: any;
|
||||
position: number;
|
||||
permissions: string;
|
||||
name: string;
|
||||
mentionable: boolean;
|
||||
managed: boolean;
|
||||
id: string;
|
||||
icon: string | null;
|
||||
hoist: boolean;
|
||||
flags: number;
|
||||
color: number;
|
||||
|
||||
constructor(data: any) {
|
||||
this.version = data.version;
|
||||
this.unicode_emoji = data.unicode_emoji;
|
||||
this.tags = data.tags;
|
||||
this.position = data.position;
|
||||
this.permissions = data.permissions;
|
||||
this.name = data.name;
|
||||
this.mentionable = data.mentionable;
|
||||
this.managed = data.managed;
|
||||
this.id = data.id;
|
||||
this.icon = data.icon;
|
||||
this.hoist = data.hoist;
|
||||
this.flags = data.flags;
|
||||
this.color = data.color;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): Role {
|
||||
return new Role(data);
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Client } from '..';
|
||||
import { InteractionContext } from '../lib';
|
||||
|
||||
enum SlashCommandType {
|
||||
ChatInput = 1,
|
||||
User,
|
||||
Message
|
||||
}
|
||||
|
||||
enum SlashCommandOptionType {
|
||||
SubCommand = 1,
|
||||
SubCommandGroup,
|
||||
String,
|
||||
Integer,
|
||||
Boolean,
|
||||
User,
|
||||
Channel,
|
||||
Role,
|
||||
Mentionable,
|
||||
Number,
|
||||
ATTACHMENT
|
||||
}
|
||||
|
||||
type SlashCommandOptionChoice = {
|
||||
name: string;
|
||||
value: string | number;
|
||||
};
|
||||
|
||||
type SlashCommandOption = {
|
||||
type: SlashCommandOptionType;
|
||||
name: string;
|
||||
description: string;
|
||||
required?: boolean;
|
||||
choices?: SlashCommandOptionChoice[];
|
||||
options?: SlashCommandOption[];
|
||||
min_value?: number;
|
||||
max_value?: number;
|
||||
min_length?: number;
|
||||
max_length?: number;
|
||||
autocomplete?: boolean;
|
||||
// channel_types?: number[];
|
||||
};
|
||||
|
||||
export class SlashCommand {
|
||||
type: SlashCommandType;
|
||||
name: string;
|
||||
description: string;
|
||||
options: SlashCommandOption[];
|
||||
guild_id?: string;
|
||||
dm_permission?: boolean;
|
||||
default_member_permissions?: string;
|
||||
execute: SlashCommandFunction;
|
||||
|
||||
constructor(data: SlashCommand) {
|
||||
this.type = data.type;
|
||||
this.name = data.name;
|
||||
this.description = data.description;
|
||||
this.options = data.options;
|
||||
this.guild_id = data.guild_id;
|
||||
this.dm_permission = data.dm_permission;
|
||||
this.default_member_permissions = data.default_member_permissions;
|
||||
this.execute = data.execute;
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
return {
|
||||
type: this.type,
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
options: this.options,
|
||||
guild_id: this.guild_id,
|
||||
dm_permission: this.dm_permission,
|
||||
default_member_permissions: this.default_member_permissions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type SlashCommandFunction = (client: Client, context: InteractionContext, ...args: any[]) => void;
|
||||
@@ -0,0 +1,79 @@
|
||||
// id snowflake the user's id identify
|
||||
// username string the user's username, not unique across the platform identify
|
||||
// discriminator string the user's Discord-tag identify
|
||||
// global_name ?string the user's display name, if it is set. For bots, this is the application name identify
|
||||
// avatar ?string the user's avatar hash identify
|
||||
// bot? boolean whether the user belongs to an OAuth2 application identify
|
||||
// system? boolean whether the user is an Official Discord System user (part of the urgent message system) identify
|
||||
// mfa_enabled? boolean whether the user has two factor enabled on their account identify
|
||||
// banner? ?string the user's banner hash identify
|
||||
// accent_color? ?integer the user's banner color encoded as an integer representation of hexadecimal color code identify
|
||||
// locale? string the user's chosen language option identify
|
||||
// verified? boolean whether the email on this account has been verified email
|
||||
// email? ?string the user's email email
|
||||
// flags? integer the flags on a user's account identify
|
||||
// premium_type? integer the type of Nitro subscription on a user's account identify
|
||||
// public_flags? integer the public flags on a user's account identify
|
||||
// avatar_decoration_data?
|
||||
|
||||
export class User {
|
||||
id: string;
|
||||
username: string;
|
||||
discriminator: string;
|
||||
global_name: string;
|
||||
avatar: string;
|
||||
bot?: boolean;
|
||||
system?: boolean;
|
||||
mfa_enabled?: boolean;
|
||||
banner?: string;
|
||||
accent_color?: number;
|
||||
locale?: string;
|
||||
verified?: boolean;
|
||||
email?: string;
|
||||
flags?: number;
|
||||
premium_type?: number;
|
||||
public_flags?: number;
|
||||
avatar_decoration_data?: Record<string, any>;
|
||||
|
||||
constructor(data: User) {
|
||||
this.id = data.id;
|
||||
this.username = data.username;
|
||||
this.discriminator = data.discriminator;
|
||||
this.global_name = data.global_name;
|
||||
this.avatar = data.avatar;
|
||||
this.bot = data.bot;
|
||||
this.system = data.system;
|
||||
this.mfa_enabled = data.mfa_enabled;
|
||||
this.banner = data.banner;
|
||||
this.accent_color = data.accent_color;
|
||||
this.locale = data.locale;
|
||||
this.verified = data.verified;
|
||||
this.email = data.email;
|
||||
this.flags = data.flags;
|
||||
this.premium_type = data.premium_type;
|
||||
this.public_flags = data.public_flags;
|
||||
this.avatar_decoration_data = data.avatar_decoration_data;
|
||||
}
|
||||
|
||||
static fromAPIResponse(data: any): User {
|
||||
return new User({
|
||||
id: data.id,
|
||||
username: data.username,
|
||||
discriminator: data.discriminator,
|
||||
global_name: data.global_name,
|
||||
avatar: data.avatar,
|
||||
bot: data.bot,
|
||||
system: data.system,
|
||||
mfa_enabled: data.mfa_enabled,
|
||||
banner: data.banner,
|
||||
accent_color: data.accent_color,
|
||||
locale: data.locale,
|
||||
verified: data.verified,
|
||||
email: data.email,
|
||||
flags: data.flags,
|
||||
premium_type: data.premium_type,
|
||||
public_flags: data.public_flags,
|
||||
avatar_decoration_data: data.avatar_decoration_data
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export const isNil = (value: any): value is null | undefined => value === null || value === undefined;
|
||||
export const exists = <T>(value: T | null | undefined): value is T => !isNil(value);
|
||||
export const isObject = (value: any): value is Record<string, any> => typeof value === 'object' && !Array.isArray(value);
|
||||
export const log = (...args: any[]) => console.log('\x1b[35m[discord.ts]\x1b[0m', ...args);
|
||||
@@ -1,162 +0,0 @@
|
||||
import * as dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
import { Client, ChatCommand, ComponentStyle, Intent, Message } from '../src';
|
||||
|
||||
const bot = new Client({
|
||||
token: process.env.BOT_TOKEN!,
|
||||
application_id: process.env.APPLICATION_ID!,
|
||||
intents: [Intent.GUILDS, Intent.GUILD_MESSAGES, Intent.GUILD_MESSAGE_REACTIONS, Intent.MESSAGE_CONTENT]
|
||||
});
|
||||
|
||||
bot.commands.chat.register(new ChatCommand({
|
||||
name: 'ping',
|
||||
description: 'Check if he is alive',
|
||||
execute: (client, context, args) => {
|
||||
context.reply('Pong!', true);
|
||||
}
|
||||
}))
|
||||
|
||||
bot.commands.application.register({
|
||||
name: 'ping',
|
||||
description: 'Check if he is alive'
|
||||
}, (client, context, args) => {
|
||||
context.reply('Pong!');
|
||||
});
|
||||
|
||||
// bot.commands.register('help', (client, context, args) => {
|
||||
// context.reply('Help yourself.');
|
||||
// });
|
||||
|
||||
bot.commands.chat.register('demo', (_client, context, _args) => {
|
||||
const msg = new Message()
|
||||
.setContent('Hello World!')
|
||||
.addComponent(
|
||||
'This is an action row',
|
||||
// Max buttons in a row is 5
|
||||
// Custom ID is required and must be unique
|
||||
// Non-Link buttons can not have a URL
|
||||
// Link buttons can not have a custom ID
|
||||
Message.button({
|
||||
label: 'Click me!',
|
||||
style: ComponentStyle.Primary,
|
||||
custom_id: '1'
|
||||
}),
|
||||
Message.button({
|
||||
label: 'No me!',
|
||||
style: ComponentStyle.Secondary,
|
||||
custom_id: '2',
|
||||
disabled: true
|
||||
}),
|
||||
Message.button({
|
||||
label: 'Never, me!',
|
||||
style: ComponentStyle.Success,
|
||||
custom_id: '3'
|
||||
}),
|
||||
// Link requires url and can not have custom_id
|
||||
Message.button({
|
||||
label: 'Screw Them.',
|
||||
style: ComponentStyle.Link,
|
||||
url: 'https://google.com'
|
||||
}),
|
||||
Message.button({
|
||||
label: 'Emoji',
|
||||
style: ComponentStyle.Secondary,
|
||||
custom_id: '4',
|
||||
emoji: {
|
||||
name: '👍',
|
||||
id: null,
|
||||
animated: false
|
||||
}
|
||||
})
|
||||
)
|
||||
.addComponent(
|
||||
Message.button({
|
||||
label: 'Bad :(',
|
||||
style: ComponentStyle.Danger,
|
||||
custom_id: '5'
|
||||
}),
|
||||
Message.button({
|
||||
label: 'More Bad :(',
|
||||
style: ComponentStyle.Danger,
|
||||
custom_id: '6'
|
||||
})
|
||||
)
|
||||
.addComponent(
|
||||
Message.selectMenu({
|
||||
custom_id: 'select_1',
|
||||
options: [
|
||||
{
|
||||
label: 'Select me',
|
||||
value: 'select_me',
|
||||
description: 'This is a description',
|
||||
emoji: {
|
||||
name: '👍',
|
||||
id: null,
|
||||
animated: false
|
||||
}
|
||||
}
|
||||
],
|
||||
placeholder: 'Select an option',
|
||||
min_values: 1,
|
||||
max_values: 1
|
||||
})
|
||||
);
|
||||
context.reply(msg);
|
||||
});
|
||||
|
||||
bot.commands.chat.register('test', (_client, context, _args) => {
|
||||
const msg = new Message().setContent('This is a test interaction!').addComponent(
|
||||
'This is an action row',
|
||||
Message.button({
|
||||
label: 'Click me!',
|
||||
style: ComponentStyle.Primary,
|
||||
custom_id: 'click_me',
|
||||
emoji: {
|
||||
name: '🔥',
|
||||
id: null,
|
||||
animated: false
|
||||
}
|
||||
}),
|
||||
Message.button({
|
||||
label: 'Never, me!',
|
||||
style: ComponentStyle.Success,
|
||||
custom_id: 'never_me'
|
||||
}),
|
||||
// Link requires url and can not have custom_id
|
||||
Message.button({
|
||||
label: 'Screw Them.',
|
||||
style: ComponentStyle.Link,
|
||||
url: 'https://google.com'
|
||||
})
|
||||
);
|
||||
context.reply(msg);
|
||||
});
|
||||
|
||||
bot.handlers.register('READY', (client, context, data) => {
|
||||
console.log(`Logged in as ${client.user.tag}`);
|
||||
});
|
||||
|
||||
bot.interactions.register('never_me', async (client, context, interaction) => {
|
||||
const reply = await context.loading();
|
||||
setTimeout(() => {
|
||||
reply.edit(`You clicked me! Thank you ${interaction.member.user.username} <3`);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
bot.interactions.register('click_me', async (client, context, interaction) => {
|
||||
const reply = await context.loading();
|
||||
setTimeout(() => {
|
||||
reply.edit(
|
||||
new Message().addComponent(
|
||||
Message.button({
|
||||
label: 'Click me!',
|
||||
style: ComponentStyle.Primary,
|
||||
custom_id: '1'
|
||||
})
|
||||
)
|
||||
);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
bot.connect();
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Client, type Context, Intent } from '../src/index';
|
||||
|
||||
const client = new Client({
|
||||
token: process.env.BOT_TOKEN!,
|
||||
application_id: process.env.APPLICATION_ID!,
|
||||
intents: [
|
||||
Intent.GUILDS,
|
||||
Intent.GUILD_MESSAGES,
|
||||
Intent.GUILD_MESSAGE_REACTIONS,
|
||||
Intent.MESSAGE_CONTENT
|
||||
],
|
||||
prefix: '??'
|
||||
});
|
||||
|
||||
client.chatCommands.register('ping', async (context: Context, args: any[]) => {
|
||||
await context.reply(`Pong!: got ${args.join(', ')}`, true)
|
||||
});
|
||||
|
||||
client.chatCommands.register('here', async (context: Context, _args: any[]) => {
|
||||
const guild = await context.fetchGuild();
|
||||
if (guild !== null) {
|
||||
await context.reply(`You are in ${guild?.name}`, true);
|
||||
} else {
|
||||
await context.reply('You are not in a guild', true);
|
||||
}
|
||||
});
|
||||
|
||||
client.chatCommands.register('quote', async (context: Context, args: any[]) => {
|
||||
const referenced = await context.message.getReference();
|
||||
if (referenced === null) {
|
||||
await context.reply('No message to quote', true);
|
||||
return;
|
||||
} else {
|
||||
await context.reply(`> "${referenced.content}"\n> — ${referenced.author?.global_name}`, true);
|
||||
}
|
||||
});
|
||||
|
||||
client.connect();
|
||||
+26
-10
@@ -1,11 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
"compilerOptions": {
|
||||
// Enable latest features
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,495 +0,0 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@cspotcode/source-map-support@^0.8.0":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
|
||||
integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "0.3.9"
|
||||
|
||||
"@jridgewell/resolve-uri@^3.0.3":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
|
||||
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||
version "1.4.14"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
|
||||
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
|
||||
|
||||
"@jridgewell/trace-mapping@0.3.9":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
|
||||
integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
|
||||
integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
|
||||
|
||||
"@tsconfig/node12@^1.0.7":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
|
||||
integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
|
||||
|
||||
"@tsconfig/node14@^1.0.0":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
|
||||
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
|
||||
|
||||
"@tsconfig/node16@^1.0.2":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
||||
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
||||
|
||||
"@types/node@*":
|
||||
version "18.7.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.18.tgz#633184f55c322e4fb08612307c274ee6d5ed3154"
|
||||
integrity sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==
|
||||
|
||||
"@types/strip-bom@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"
|
||||
integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==
|
||||
|
||||
"@types/strip-json-comments@0.0.30":
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
|
||||
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
|
||||
|
||||
"@types/ws@^8.5.3":
|
||||
version "8.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
|
||||
integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
acorn-walk@^8.1.1:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
|
||||
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
|
||||
|
||||
acorn@^8.4.1:
|
||||
version "8.8.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
|
||||
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
arg@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
axios@^0.27.2:
|
||||
version "0.27.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
|
||||
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.9"
|
||||
form-data "^4.0.0"
|
||||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
buffer-from@^1.0.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||
|
||||
chokidar@^3.5.1:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
|
||||
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
create-require@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
dotenv@^16.0.2:
|
||||
version "16.0.2"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.2.tgz#0b0f8652c016a3858ef795024508cddc4bffc5bf"
|
||||
integrity sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==
|
||||
|
||||
dynamic-dedupe@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1"
|
||||
integrity sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==
|
||||
dependencies:
|
||||
xtend "^4.0.0"
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
follow-redirects@^1.14.9:
|
||||
version "1.15.2"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
|
||||
function-bind@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
glob-parent@~5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
|
||||
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
|
||||
dependencies:
|
||||
is-glob "^4.0.1"
|
||||
|
||||
glob@^7.1.3:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.1.1"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
|
||||
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
|
||||
dependencies:
|
||||
binary-extensions "^2.0.0"
|
||||
|
||||
is-core-module@^2.9.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
|
||||
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-extglob@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
|
||||
is-glob@^4.0.1, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
|
||||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-number@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
|
||||
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
minimatch@^3.1.1:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimist@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
||||
|
||||
mkdirp@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||
|
||||
path-parse@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
readdirp@~3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
resolve@^1.0.0:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
|
||||
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
|
||||
dependencies:
|
||||
is-core-module "^2.9.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
rimraf@^2.6.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
||||
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
source-map-support@^0.5.12:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
source-map "^0.6.0"
|
||||
|
||||
source-map@^0.6.0:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
strip-bom@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
|
||||
integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
|
||||
|
||||
strip-json-comments@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==
|
||||
|
||||
supports-preserve-symlinks-flag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tree-kill@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
|
||||
|
||||
ts-node-dev@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065"
|
||||
integrity sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==
|
||||
dependencies:
|
||||
chokidar "^3.5.1"
|
||||
dynamic-dedupe "^0.3.0"
|
||||
minimist "^1.2.6"
|
||||
mkdirp "^1.0.4"
|
||||
resolve "^1.0.0"
|
||||
rimraf "^2.6.1"
|
||||
source-map-support "^0.5.12"
|
||||
tree-kill "^1.2.2"
|
||||
ts-node "^10.4.0"
|
||||
tsconfig "^7.0.0"
|
||||
|
||||
ts-node@^10.4.0:
|
||||
version "10.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
|
||||
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
|
||||
dependencies:
|
||||
"@cspotcode/source-map-support" "^0.8.0"
|
||||
"@tsconfig/node10" "^1.0.7"
|
||||
"@tsconfig/node12" "^1.0.7"
|
||||
"@tsconfig/node14" "^1.0.0"
|
||||
"@tsconfig/node16" "^1.0.2"
|
||||
acorn "^8.4.1"
|
||||
acorn-walk "^8.1.1"
|
||||
arg "^4.1.0"
|
||||
create-require "^1.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
v8-compile-cache-lib "^3.0.1"
|
||||
yn "3.1.1"
|
||||
|
||||
tsconfig@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
|
||||
integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==
|
||||
dependencies:
|
||||
"@types/strip-bom" "^3.0.0"
|
||||
"@types/strip-json-comments" "0.0.30"
|
||||
strip-bom "^3.0.0"
|
||||
strip-json-comments "^2.0.0"
|
||||
|
||||
typescript@^4.8.3:
|
||||
version "4.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.3.tgz#d59344522c4bc464a65a730ac695007fdb66dd88"
|
||||
integrity sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==
|
||||
|
||||
v8-compile-cache-lib@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
|
||||
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@^8.8.1:
|
||||
version "8.8.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
|
||||
integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
|
||||
|
||||
xtend@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
|
||||
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
Reference in New Issue
Block a user