mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +00:00
Next: A complete Biscuit rewrite (#131)
* FINALLY WE REACHED AN AGREEMENT * chore: d-types adapt * websocket sucks, rest * changes * new look for core * 💀 * fix cdn routes, more structures Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> * CDN routes * chore: change to rome Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> * Oh shit, here we go again Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> * fixes * mixin, handler events, ws Co-authored-by: Yuzu <yuzudev@users.noreply.github.com> Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> * change type * Error system (#133) * Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> * chore: biscuit rebase * token leak goes brrrr * fix: events * chore: road to raw data * fix: managers typing * chore: fix gateway typing * feat: helpers * style: linter * Types for routes (#134) * typing for routes * managers Co-authored-by: Marcos Susaña <marcosjgs03@gmail.com> * Types for routes (#134) * I wanna cry * Next (#136) * Merge #137 * chore: lineWidth to 140 * chore: README update --------- Co-authored-by: Yuzu <yuzuru@programmer.net> Co-authored-by: Free 公園 <FreeAoi@users.noreply.github.com> Co-authored-by: ThisIsAName <46913407+NejireSupremacy@users.noreply.github.com> Co-authored-by: MARCROCK22 <57925328+MARCROCK22@users.noreply.github.com> Co-authored-by: MARCROCK22 <marcos22dev@gmail.com>
This commit is contained in:
parent
13bf6a0101
commit
87cdad0b03
@ -5,9 +5,9 @@ charset = utf-8
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
quote_type = single
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
|
@ -1,5 +0,0 @@
|
||||
node_modules/
|
||||
build
|
||||
dist
|
||||
examples/**
|
||||
tsup.config.ts
|
224
.eslintrc.yml
224
.eslintrc.yml
@ -1,224 +0,0 @@
|
||||
root: true
|
||||
env:
|
||||
browser: true
|
||||
es2020: true
|
||||
node: true
|
||||
|
||||
extends:
|
||||
- 'prettier'
|
||||
- 'eslint:recommended'
|
||||
- 'plugin:@typescript-eslint/eslint-recommended'
|
||||
- 'plugin:@typescript-eslint/recommended'
|
||||
|
||||
ignorePatterns:
|
||||
- 'node_modules'
|
||||
- 'dist'
|
||||
- 'coverage'
|
||||
- '**/*.js'
|
||||
- '**/*.d.ts'
|
||||
- '__tests__'
|
||||
- '__test__'
|
||||
|
||||
parser: '@typescript-eslint/parser'
|
||||
|
||||
parserOptions:
|
||||
project: './packages/**/tsconfig.json'
|
||||
sourceType: 'module'
|
||||
|
||||
plugins:
|
||||
- '@typescript-eslint'
|
||||
|
||||
# silly eslint bug
|
||||
overrides:
|
||||
- files: ['*.ts']
|
||||
rules:
|
||||
no-undef: 'off'
|
||||
|
||||
rules:
|
||||
'@typescript-eslint/consistent-type-imports': 'error'
|
||||
'@typescript-eslint/no-duplicate-imports': 'error'
|
||||
'@typescript-eslint/prefer-optional-chain': 'error'
|
||||
'@typescript-eslint/no-explicit-any': 'off'
|
||||
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||
'@typescript-eslint/no-non-null-assertion': 'off'
|
||||
'@typescript-eslint/ban-ts-comment': 'off'
|
||||
'@typescript-eslint/no-unused-vars': 'off'
|
||||
'@typescript-eslint/naming-convention':
|
||||
- 'error'
|
||||
- selector: 'default'
|
||||
format: null
|
||||
- selector: 'variable'
|
||||
format:
|
||||
- 'camelCase'
|
||||
- 'PascalCase'
|
||||
- 'UPPER_CASE'
|
||||
- selector: 'typeLike'
|
||||
format: ['PascalCase']
|
||||
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off'
|
||||
'@typescript-eslint/no-empty-interface': 'off'
|
||||
'@typescript-eslint/adjacent-overload-signatures': 'error'
|
||||
'@typescript-eslint/consistent-type-assertions': 'error'
|
||||
'@typescript-eslint/no-array-constructor': 'error'
|
||||
'@typescript-eslint/no-empty-function': 'error'
|
||||
'@typescript-eslint/no-inferrable-types': 'error'
|
||||
'@typescript-eslint/no-misused-new': 'error'
|
||||
'@typescript-eslint/no-namespace': 'error'
|
||||
'@typescript-eslint/no-this-alias': 'error'
|
||||
'@typescript-eslint/no-use-before-define': 'off'
|
||||
'@typescript-eslint/no-var-requires': 'error'
|
||||
'@typescript-eslint/triple-slash-reference': 'error'
|
||||
'@typescript-eslint/type-annotation-spacing': 'error'
|
||||
'@typescript-eslint/array-type': 'error'
|
||||
'@typescript-eslint/no-unnecessary-qualifier': 'error'
|
||||
'@typescript-eslint/no-unnecessary-type-arguments': 'off' # disabled as it started to be buggy
|
||||
'@typescript-eslint/quotes':
|
||||
- 'error'
|
||||
- 'single'
|
||||
- avoidEscape: true
|
||||
allowTemplateLiterals: true
|
||||
|
||||
'@typescript-eslint/semi':
|
||||
- 'error'
|
||||
- 'always'
|
||||
|
||||
'@typescript-eslint/no-useless-constructor': 'error'
|
||||
'@typescript-eslint/no-redeclare':
|
||||
- 'error'
|
||||
|
||||
'@typescript-eslint/member-delimiter-style':
|
||||
- 'error'
|
||||
- multiline:
|
||||
delimiter: 'semi'
|
||||
requireLast: true
|
||||
singleline:
|
||||
delimiter: 'semi'
|
||||
requireLast: false
|
||||
|
||||
'@typescript-eslint/space-before-function-paren':
|
||||
- 'error'
|
||||
- anonymous: 'always'
|
||||
named: 'never'
|
||||
asyncArrow: 'always'
|
||||
|
||||
'arrow-parens':
|
||||
- 'error'
|
||||
- 'as-needed'
|
||||
|
||||
'no-var': 'error'
|
||||
'prefer-const': 'error'
|
||||
'prefer-rest-params': 'error'
|
||||
'prefer-spread': 'error'
|
||||
'constructor-super': 'error'
|
||||
'for-direction': 'error'
|
||||
'getter-return': 'error'
|
||||
'no-async-promise-executor': 'error'
|
||||
'no-case-declarations': 'error'
|
||||
'no-class-assign': 'error'
|
||||
'no-compare-neg-zero': 'error'
|
||||
'no-cond-assign': 'error'
|
||||
'no-const-assign': 'error'
|
||||
'no-constant-condition': 'error'
|
||||
'no-control-regex': 'error'
|
||||
'no-debugger': 'error'
|
||||
'no-delete-var': 'error'
|
||||
'no-dupe-args': 'error'
|
||||
'no-dupe-keys': 'error'
|
||||
'no-duplicate-case': 'error'
|
||||
'no-empty': 'error'
|
||||
'no-empty-character-class': 'error'
|
||||
'no-empty-pattern': 'error'
|
||||
'no-ex-assign': 'error'
|
||||
'no-extra-boolean-cast': 'error'
|
||||
'no-extra-semi': 'error'
|
||||
'no-fallthrough': 'error'
|
||||
'no-func-assign': 'error'
|
||||
'no-global-assign': 'error'
|
||||
'no-inner-declarations': 'error'
|
||||
'no-invalid-regexp': 'error'
|
||||
'no-irregular-whitespace': 'error'
|
||||
'no-misleading-character-class': 'error'
|
||||
'no-mixed-spaces-and-tabs': 'error'
|
||||
'no-new-symbol': 'error'
|
||||
'no-obj-calls': 'error'
|
||||
'no-octal': 'error'
|
||||
'no-prototype-builtins': 'error'
|
||||
'no-redeclare': 'off'
|
||||
'no-regex-spaces': 'error'
|
||||
'no-self-assign': 'error'
|
||||
'no-shadow-restricted-names': 'error'
|
||||
'no-sparse-arrays': 'error'
|
||||
'no-this-before-super': 'error'
|
||||
'no-undef': 'error'
|
||||
'no-unexpected-multiline': 'error'
|
||||
'no-unreachable': 'error'
|
||||
'no-unsafe-finally': 'error'
|
||||
'no-unsafe-negation': 'error'
|
||||
'no-unused-labels': 'error'
|
||||
'no-useless-catch': 'error'
|
||||
'no-useless-escape': 'error'
|
||||
'no-with': 'error'
|
||||
'require-yield': 'error'
|
||||
'use-isnan': 'error'
|
||||
'valid-typeof': 'error'
|
||||
# 'comma-dangle': ['error', 'never']
|
||||
'dot-notation': 'error'
|
||||
'eol-last': 'error'
|
||||
eqeqeq:
|
||||
- 'error'
|
||||
- 'always'
|
||||
- 'null': 'ignore'
|
||||
|
||||
'no-console': 'warn'
|
||||
'no-duplicate-imports': 'off'
|
||||
'no-multiple-empty-lines': 'error'
|
||||
'no-throw-literal': 'error'
|
||||
'no-trailing-spaces': 'error'
|
||||
'no-undef-init': 'error'
|
||||
'object-shorthand': 'error'
|
||||
'quote-props':
|
||||
- 'error'
|
||||
- 'consistent-as-needed'
|
||||
|
||||
'spaced-comment': 'error'
|
||||
yoda: 'error'
|
||||
curly: 'error'
|
||||
'object-curly-spacing':
|
||||
- 'error'
|
||||
- 'always'
|
||||
|
||||
'lines-between-class-members':
|
||||
- 'error'
|
||||
- 'always'
|
||||
- exceptAfterSingleLine: true
|
||||
|
||||
'no-else-return': 'error'
|
||||
|
||||
# always
|
||||
'padded-blocks':
|
||||
- 'error'
|
||||
- classes: 'never'
|
||||
|
||||
'block-spacing':
|
||||
- 'error'
|
||||
- 'always'
|
||||
|
||||
'space-before-blocks':
|
||||
- 'error'
|
||||
- 'always'
|
||||
|
||||
'brace-style':
|
||||
- 'error'
|
||||
- '1tbs'
|
||||
- allowSingleLine: true
|
||||
|
||||
'keyword-spacing':
|
||||
- 'error'
|
||||
- before: true
|
||||
after: true
|
||||
|
||||
'space-in-parens':
|
||||
- 'error'
|
||||
- 'never'
|
||||
|
||||
# settings: {}
|
@ -1,2 +0,0 @@
|
||||
arrowParens: 'avoid'
|
||||
singleQuote: true
|
19
package.json
19
package.json
@ -6,22 +6,21 @@
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"clean": "turbo run clean",
|
||||
"lint": "turbo run lint",
|
||||
"check": "rome check ./packages/",
|
||||
"check:apply": "rome check ./packages/ --apply",
|
||||
"lint": "rome format ./packages/ --write --quote-style single --trailing-comma none",
|
||||
"dev": "turbo run dev --parallel"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=7.0.0",
|
||||
"node": ">=14.0.0"
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.24.1",
|
||||
"@types/node": "^18.6.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.32.0",
|
||||
"@typescript-eslint/parser": "^5.32.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"rome": "^12.0.0",
|
||||
"turbo": "^1.4.2",
|
||||
"typescript": "^4.7.4",
|
||||
"@changesets/cli": "^2.24.1"
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"packageManager": "npm@8.14.0",
|
||||
"bugs": {
|
||||
@ -57,6 +56,10 @@
|
||||
{
|
||||
"name": "Drylozu",
|
||||
"url": "https://github.com/Drylozu"
|
||||
},
|
||||
{
|
||||
"name": "FreeAoi",
|
||||
"url": "https://github.com/FreeAoi"
|
||||
}
|
||||
],
|
||||
"homepage": "https://biscuitjs.com",
|
||||
|
@ -1,62 +0,0 @@
|
||||
# @biscuitland/api-types
|
||||
|
||||
## 2.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- fix TODO
|
||||
|
||||
## 2.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bug fixes
|
||||
|
||||
## 2.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- rename guildLocales to guildLocale in interactions
|
||||
|
||||
## 2.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- select menu options now can be empty since the latest Discord API update
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Functionality to delete ephemeral messages added, select menus were updated
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- minor changes
|
||||
|
||||
## 2.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- dumb hotfix that LH asked for (blame Yuzu)
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Changes to cache and forum channels ✨
|
||||
- Forum channels and updates to @biscuitland/cache ✨
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Minor fixes
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Major Changes
|
||||
|
||||
- publish
|
@ -1,38 +0,0 @@
|
||||
# @biscuitland/api-types
|
||||
|
||||
## Most importantly, api-types is:
|
||||
|
||||
1:1 type definitions package for the [Discord](https://discord.com/developers/docs/intro) API.
|
||||
|
||||
[<img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white">](https://github.com/oasisjs/biscuit)
|
||||
[<img src="https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white">](https://discord.gg/XNw2RZFzaP)
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/oasisjs/biscuit/main/assets/icon.svg" alt="biscuit"/>
|
||||
|
||||
## Install (for [node18](https://nodejs.org/en/download/))
|
||||
|
||||
```sh-session
|
||||
npm install @biscuitland/api-types
|
||||
yarn add @biscuitland/api-types
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import type { DiscordUser } from '@biscuitland/api-types';
|
||||
```
|
||||
|
||||
## Example for [Deno](https://deno.land/)
|
||||
|
||||
```ts
|
||||
import type { DiscordUser } from "https://unpkg.com/@biscuitland/api-types@2.0.5/dist/index.d.ts";
|
||||
```
|
||||
|
||||
We deliver this package through [unpkg](https://unpkg.com/) and it does contain constants and routes too
|
||||
|
||||
## Links
|
||||
|
||||
- [Website](https://biscuitjs.com/)
|
||||
- [Documentation](https://docs.biscuitjs.com/)
|
||||
- [Discord](https://discord.gg/XNw2RZFzaP)
|
||||
- [core](https://www.npmjs.com/package/@biscuitland/core) | [cache](https://www.npmjs.com/package/@biscuitland/cache) | [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers)
|
@ -1,71 +0,0 @@
|
||||
{
|
||||
"name": "@biscuitland/api-types",
|
||||
"version": "2.3.0",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf dist && rm -rf .turbo",
|
||||
"dev": "tsup --watch"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsup": "^6.1.3"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "Yuzuru <yuzuru@programmer.net>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Yuzuru",
|
||||
"url": "https://github.com/yuzudev",
|
||||
"author": true
|
||||
},
|
||||
{
|
||||
"name": "miia",
|
||||
"url": "https://github.com/dragurimu"
|
||||
},
|
||||
{
|
||||
"name": "n128",
|
||||
"url": "https://github.com/nicolito128"
|
||||
},
|
||||
{
|
||||
"name": "socram03",
|
||||
"url": "https://github.com/socram03"
|
||||
},
|
||||
{
|
||||
"name": "Drylozu",
|
||||
"url": "https://github.com/Drylozu"
|
||||
}
|
||||
],
|
||||
"homepage": "https://biscuitjs.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/oasisjs/biscuit.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/oasisjs/biscuit"
|
||||
},
|
||||
"keywords": [
|
||||
"api",
|
||||
"discord",
|
||||
"bots",
|
||||
"typescript",
|
||||
"botdev"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
||||
export * as Constants from './utils/constants';
|
||||
export * from './utils/routes';
|
||||
|
||||
export * from './v10/index';
|
||||
export * from './common';
|
@ -1,29 +0,0 @@
|
||||
import type { Snowflake } from '../common';
|
||||
import { baseEndpoints as Endpoints } from './constants';
|
||||
|
||||
export function USER_AVATAR(userId: Snowflake, icon: string): string {
|
||||
return `${Endpoints.CDN_URL}/avatars/${userId}/${icon}`;
|
||||
}
|
||||
|
||||
export function EMOJI_URL(id: Snowflake, animated = false): string {
|
||||
return `https://cdn.discordapp.com/emojis/${id}.${animated ? 'gif' : 'png'}`;
|
||||
}
|
||||
|
||||
export function USER_DEFAULT_AVATAR(
|
||||
/** user discriminator */
|
||||
altIcon: number,
|
||||
): string {
|
||||
return `${Endpoints.CDN_URL}/embed/avatars/${altIcon}.png`;
|
||||
}
|
||||
|
||||
export function GUILD_BANNER(guildId: Snowflake, icon: string): string {
|
||||
return `${Endpoints.CDN_URL}/banners/${guildId}/${icon}`;
|
||||
}
|
||||
|
||||
export function GUILD_SPLASH(guildId: Snowflake, icon: string): string {
|
||||
return `${Endpoints.CDN_URL}/splashes/${guildId}/${icon}`;
|
||||
}
|
||||
|
||||
export function GUILD_ICON(guildId: Snowflake, icon: string): string {
|
||||
return `${Endpoints.CDN_URL}/icons/${guildId}/${icon}`;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
/** https://discord.com/developers/docs/reference#api-reference-base-url */
|
||||
export const BASE_URL = 'https://discord.com/api';
|
||||
|
||||
/** https://discord.com/developers/docs/reference#api-versioning-api-versions */
|
||||
export const API_VERSION = 10;
|
||||
|
||||
/** https://github.com/oasisjs/biscuit/releases */
|
||||
export const BISCUIT_VERSION = '2.0.5';
|
||||
|
||||
/** https://discord.com/developers/docs/reference#user-agent */
|
||||
export const USER_AGENT = `DiscordBot (https://github.com/oasisjs/biscuit, v${BISCUIT_VERSION})`;
|
||||
|
||||
/** https://discord.com/developers/docs/reference#image-formatting-image-base-url */
|
||||
export const IMAGE_BASE_URL = 'https://cdn.discordapp.com';
|
||||
|
||||
// This can be modified by big brain bots and use a proxy
|
||||
export const baseEndpoints = {
|
||||
BASE_URL: `${BASE_URL}/v${API_VERSION}`,
|
||||
CDN_URL: IMAGE_BASE_URL
|
||||
};
|
||||
|
||||
export const SLASH_COMMANDS_NAME_REGEX = /^[-_\p{L}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
|
||||
export const CONTEXT_MENU_COMMANDS_NAME_REGEX = /^[\w-\s]{1,32}$/;
|
||||
export const CHANNEL_MENTION_REGEX = /<#[0-9]+>/g;
|
||||
export const DISCORD_SNOWFLAKE_REGEX = /^(?<id>\d{17,19})$/;
|
@ -1,519 +0,0 @@
|
||||
import type { AuditLogEvents, Snowflake } from '../common';
|
||||
export * from './cdn';
|
||||
|
||||
export function USER(userId?: Snowflake): string {
|
||||
if (!userId) { return '/users/@me'; }
|
||||
return `/users/${userId}`;
|
||||
}
|
||||
|
||||
export function GATEWAY_BOT(): string {
|
||||
return '/gateway/bot';
|
||||
}
|
||||
|
||||
export interface GetMessagesOptions {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface GetMessagesOptions {
|
||||
around?: Snowflake;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface GetMessagesOptions {
|
||||
before?: Snowflake;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface GetMessagesOptions {
|
||||
after?: Snowflake;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export function CHANNEL(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_INVITES(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/invites`;
|
||||
}
|
||||
|
||||
export function CHANNEL_TYPING(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/typing`;
|
||||
}
|
||||
|
||||
export function CHANNEL_CREATE_THREAD(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/threads`;
|
||||
}
|
||||
|
||||
export function MESSAGE_CREATE_THREAD(channelId: Snowflake, messageId: Snowflake): string {
|
||||
return `/channels/${channelId}/messages/${messageId}/threads`;
|
||||
}
|
||||
|
||||
/** used to send messages */
|
||||
export function CHANNEL_MESSAGES(channelId: Snowflake, options?: GetMessagesOptions): string {
|
||||
let url = `/channels/${channelId}/messages?`;
|
||||
|
||||
if (options) {
|
||||
if (options.after) { url += `after=${options.after}`; }
|
||||
if (options.before) { url += `&before=${options.before}`; }
|
||||
if (options.around) { url += `&around=${options.around}`; }
|
||||
if (options.limit) { url += `&limit=${options.limit}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/** used to edit messages */
|
||||
export function CHANNEL_MESSAGE(channelId: Snowflake, messageId: Snowflake): string {
|
||||
return `/channels/${channelId}/messages/${messageId}`;
|
||||
}
|
||||
|
||||
/** used to kick members */
|
||||
export function GUILD_MEMBER(guildId: Snowflake, userId: Snowflake): string {
|
||||
return `/guilds/${guildId}/members/${userId}`;
|
||||
}
|
||||
|
||||
export interface ListGuildMembers {
|
||||
limit?: number;
|
||||
after?: string;
|
||||
}
|
||||
|
||||
export function GUILD_MEMBERS(guildId: Snowflake, options?: ListGuildMembers) {
|
||||
let url = `/guilds/${guildId}/members?`;
|
||||
|
||||
if (options?.limit) { url += `limit=${options.limit}`; }
|
||||
if (options?.after) { url += `&after=${options.after}`; }
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/** used to ban members */
|
||||
export function GUILD_BAN(guildId: Snowflake, userId: Snowflake): string {
|
||||
return `/guilds/${guildId}/bans/${userId}`;
|
||||
}
|
||||
|
||||
export interface GetBans {
|
||||
limit?: number;
|
||||
before?: Snowflake;
|
||||
after?: Snowflake;
|
||||
}
|
||||
|
||||
/** used to unban members */
|
||||
export function GUILD_BANS(guildId: Snowflake, options?: GetBans): string {
|
||||
let url = `/guilds/${guildId}/bans?`;
|
||||
|
||||
if (options) {
|
||||
if (options.limit) { url += `limit=${options.limit}`; }
|
||||
if (options.after) { url += `&after=${options.after}`; }
|
||||
if (options.before) { url += `&before=${options.before}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function GUILD_ROLE(guildId: Snowflake, roleId: Snowflake): string {
|
||||
return `/guilds/${guildId}/roles/${roleId}`;
|
||||
}
|
||||
|
||||
export function GUILD_ROLES(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/roles`;
|
||||
}
|
||||
|
||||
export function USER_GUILDS(guildId?: Snowflake): string {
|
||||
if (guildId) { return `/users/@me/guilds/${guildId}`; }
|
||||
return `/users/@me/guilds`;
|
||||
}
|
||||
|
||||
export function USER_DM() {
|
||||
return `/users/@me/channels`;
|
||||
}
|
||||
|
||||
export function GUILD_EMOJIS(guildId: Snowflake, emojiId?: Snowflake): string {
|
||||
if (emojiId) { return `/guilds/${guildId}/emojis/${emojiId}`; }
|
||||
return `/guilds/${guildId}/emojis`;
|
||||
}
|
||||
|
||||
export interface GetAuditLogs {
|
||||
userId?: Snowflake;
|
||||
actionType?: AuditLogEvents;
|
||||
before?: Snowflake;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export function GUILD_AUDIT_LOGS(guildId: Snowflake, options?: GetAuditLogs) {
|
||||
let url = `/guilds/${guildId}/audit-logs?`;
|
||||
if (options) {
|
||||
const obj = {
|
||||
user_id: options.userId,
|
||||
action_type: options.actionType,
|
||||
before: options.before,
|
||||
limit: options.limit
|
||||
};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (!value) { continue; }
|
||||
url += `&${key}=${value}`;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function GUILD_EMOJI(guildId: Snowflake, emojiId: Snowflake): string {
|
||||
return `/guilds/${guildId}/emojis/${emojiId}`;
|
||||
}
|
||||
|
||||
export interface GetInvite {
|
||||
withCounts?: boolean;
|
||||
withExpiration?: boolean;
|
||||
scheduledEventId?: Snowflake;
|
||||
}
|
||||
|
||||
export function GUILDS(guildId?: Snowflake): string {
|
||||
if (guildId) { return `/guilds/${guildId}`; }
|
||||
return `/guilds`;
|
||||
}
|
||||
|
||||
export function AUTO_MODERATION_RULES(guildId: Snowflake, ruleId?: Snowflake): string {
|
||||
if (ruleId) {
|
||||
return `/guilds/${guildId}/auto-moderation/rules/${ruleId}`;
|
||||
}
|
||||
return `/guilds/${guildId}/auto-moderation/rules`;
|
||||
}
|
||||
|
||||
export function INVITE(inviteCode: string, options?: GetInvite): string {
|
||||
let url = `/invites/${inviteCode}?`;
|
||||
|
||||
if (options) {
|
||||
if (options.withCounts) { url += `with_counts=${options.withCounts}`; }
|
||||
if (options.withExpiration) { url += `&with_expiration=${options.withExpiration}`; }
|
||||
if (options.scheduledEventId) { url += `&guild_scheduled_event_id=${options.scheduledEventId}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function GUILD_INVITES(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/invites`;
|
||||
}
|
||||
|
||||
export function INTERACTION_ID_TOKEN(interactionId: Snowflake, token: string): string {
|
||||
return `/interactions/${interactionId}/${token}/callback`;
|
||||
}
|
||||
|
||||
export function WEBHOOK_MESSAGE_ORIGINAL(webhookId: Snowflake, token: string, options?: { threadId?: bigint }): string {
|
||||
let url = `/webhooks/${webhookId}/${token}/messages/@original?`;
|
||||
|
||||
if (options) {
|
||||
if (options.threadId) { url += `thread_id=${options.threadId}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function WEBHOOK_MESSAGE(
|
||||
webhookId: Snowflake,
|
||||
token: string,
|
||||
messageId: Snowflake,
|
||||
options?: { threadId?: Snowflake },
|
||||
): string {
|
||||
let url = `/webhooks/${webhookId}/${token}/messages/${messageId}?`;
|
||||
|
||||
if (options) {
|
||||
if (options.threadId) { url += `thread_id=${options.threadId}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function WEBHOOK_TOKEN(webhookId: Snowflake, token?: string): string {
|
||||
if (!token) { return `/webhooks/${webhookId}`; }
|
||||
return `/webhooks/${webhookId}/${token}`;
|
||||
}
|
||||
|
||||
export interface WebhookOptions {
|
||||
wait?: boolean;
|
||||
threadId?: Snowflake;
|
||||
}
|
||||
|
||||
export function WEBHOOK(webhookId: Snowflake, token: string, options?: WebhookOptions): string {
|
||||
let url = `/webhooks/${webhookId}/${token}`;
|
||||
|
||||
if (options?.wait) { url += `?wait=${options.wait}`; }
|
||||
if (options?.threadId) { url += `?thread_id=${options.threadId}`; }
|
||||
if (options?.wait && options.threadId) { url += `?wait=${options.wait}&thread_id=${options.threadId}`; }
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function USER_NICK(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/members/@me`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-prune-count
|
||||
*/
|
||||
export interface GetGuildPruneCountQuery {
|
||||
days?: number;
|
||||
includeRoles?: Snowflake | Snowflake[];
|
||||
}
|
||||
|
||||
export function GUILD_PRUNE(guildId: Snowflake, options?: GetGuildPruneCountQuery): string {
|
||||
let url = `/guilds/${guildId}/prune?`;
|
||||
|
||||
if (options?.days) { url += `days=${options.days}`; }
|
||||
if (options?.includeRoles) { url += `&include_roles=${options.includeRoles}`; }
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function CHANNEL_PIN(channelId: Snowflake, messageId: Snowflake): string {
|
||||
return `/channels/${channelId}/pins/${messageId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_PINS(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/pins`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION_ME(channelId: Snowflake, messageId: Snowflake, emoji: string): string {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/@me`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION_USER(
|
||||
channelId: Snowflake,
|
||||
messageId: Snowflake,
|
||||
emoji: string,
|
||||
userId: Snowflake,
|
||||
) {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/${userId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTIONS(channelId: Snowflake, messageId: Snowflake) {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#get-reactions-query-string-params
|
||||
*/
|
||||
export interface GetReactions {
|
||||
after?: string;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION(
|
||||
channelId: Snowflake,
|
||||
messageId: Snowflake,
|
||||
emoji: string,
|
||||
options?: GetReactions,
|
||||
): string {
|
||||
let url = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}?`;
|
||||
|
||||
if (options?.after) { url += `after=${options.after}`; }
|
||||
if (options?.limit) { url += `&limit=${options.limit}`; }
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_CROSSPOST(channelId: Snowflake, messageId: Snowflake): string {
|
||||
return `/channels/${channelId}/messages/${messageId}/crosspost`;
|
||||
}
|
||||
|
||||
export function GUILD_MEMBER_ROLE(guildId: Snowflake, memberId: Snowflake, roleId: Snowflake): string {
|
||||
return `/guilds/${guildId}/members/${memberId}/roles/${roleId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_WEBHOOKS(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/webhooks`;
|
||||
}
|
||||
|
||||
export function THREAD_START_PUBLIC(channelId: Snowflake, messageId: Snowflake): string {
|
||||
return `/channels/${channelId}/messages/${messageId}/threads`;
|
||||
}
|
||||
|
||||
export function THREAD_START_PRIVATE(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/threads`;
|
||||
}
|
||||
|
||||
export function THREAD_ACTIVE(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/threads/active`;
|
||||
}
|
||||
|
||||
export interface ListArchivedThreads {
|
||||
before?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export function THREAD_ME(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/thread-members/@me`;
|
||||
}
|
||||
|
||||
export function THREAD_MEMBERS(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/thread-members`;
|
||||
}
|
||||
|
||||
export function THREAD_USER(channelId: Snowflake, userId: Snowflake): string {
|
||||
return `/channels/${channelId}/thread-members/${userId}`;
|
||||
}
|
||||
|
||||
export function THREAD_ARCHIVED(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/threads/archived`;
|
||||
}
|
||||
|
||||
export function THREAD_ARCHIVED_PUBLIC(channelId: Snowflake, options?: ListArchivedThreads): string {
|
||||
let url = `/channels/${channelId}/threads/archived/public?`;
|
||||
|
||||
if (options) {
|
||||
if (options.before) { url += `before=${new Date(options.before).toISOString()}`; }
|
||||
if (options.limit) { url += `&limit=${options.limit}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function THREAD_ARCHIVED_PRIVATE(channelId: Snowflake, options?: ListArchivedThreads): string {
|
||||
let url = `/channels/${channelId}/threads/archived/private?`;
|
||||
|
||||
if (options) {
|
||||
if (options.before) { url += `before=${new Date(options.before).toISOString()}`; }
|
||||
if (options.limit) { url += `&limit=${options.limit}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function THREAD_ARCHIVED_PRIVATE_JOINED(channelId: Snowflake, options?: ListArchivedThreads): string {
|
||||
let url = `/channels/${channelId}/users/@me/threads/archived/private?`;
|
||||
|
||||
if (options) {
|
||||
if (options.before) { url += `before=${new Date(options.before).toISOString()}`; }
|
||||
if (options.limit) { url += `&limit=${options.limit}`; }
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function FORUM_START(channelId: Snowflake): string {
|
||||
return `/channels/${channelId}/threads?has_message=true`;
|
||||
}
|
||||
|
||||
export function STAGE_INSTANCES(): string {
|
||||
return `/stage-instances`;
|
||||
}
|
||||
|
||||
export function STAGE_INSTANCE(channelId: Snowflake): string {
|
||||
return `/stage-instances/${channelId}`;
|
||||
}
|
||||
|
||||
export function APPLICATION_COMMANDS(appId: Snowflake, commandId?: Snowflake): string {
|
||||
if (commandId) { return `/applications/${appId}/commands/${commandId}`; }
|
||||
return `/applications/${appId}/commands`;
|
||||
}
|
||||
|
||||
export function GUILD_APPLICATION_COMMANDS(appId: Snowflake, guildId: Snowflake, commandId?: Snowflake): string {
|
||||
if (commandId) { return `/applications/${appId}/guilds/${guildId}/commands/${commandId}`; }
|
||||
return `/applications/${appId}/guilds/${guildId}/commands`;
|
||||
}
|
||||
|
||||
export function GUILD_APPLICATION_COMMANDS_PERMISSIONS(
|
||||
appId: Snowflake,
|
||||
guildId: Snowflake,
|
||||
commandId?: Snowflake,
|
||||
): string {
|
||||
if (commandId) { return `/applications/${appId}/guilds/${guildId}/commands/${commandId}/permissions`; }
|
||||
return `/applications/${appId}/guilds/${guildId}/commands/permissions`;
|
||||
}
|
||||
|
||||
export function APPLICATION_COMMANDS_LOCALIZATIONS(
|
||||
appId: Snowflake,
|
||||
commandId: Snowflake,
|
||||
withLocalizations?: boolean,
|
||||
): string {
|
||||
let url = `/applications/${appId}/commands/${commandId}?`;
|
||||
|
||||
if (withLocalizations !== undefined) {
|
||||
url += `withLocalizations=${withLocalizations}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function GUILD_APPLICATION_COMMANDS_LOCALIZATIONS(
|
||||
appId: Snowflake,
|
||||
guildId: Snowflake,
|
||||
commandId: Snowflake,
|
||||
withLocalizations?: boolean,
|
||||
): string {
|
||||
let url = `/applications/${appId}/guilds/${guildId}/commands/${commandId}?`;
|
||||
|
||||
if (withLocalizations !== undefined) {
|
||||
url += `with_localizations=${withLocalizations}`;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function STICKER(id: Snowflake): string {
|
||||
return `stickers/${id}`;
|
||||
}
|
||||
|
||||
export function STICKER_PACKS(): string {
|
||||
return `stickers-packs`;
|
||||
}
|
||||
|
||||
export function GUILD_STICKERS(guildId: Snowflake, stickerId?: Snowflake): string {
|
||||
if (stickerId) { return `/guilds/${guildId}/stickers/${stickerId}`; }
|
||||
return `/guilds/${guildId}/stickers`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the widget for the guild.
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
|
||||
*/
|
||||
export interface GetWidget {
|
||||
get: 'json' | 'image' | 'settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* /guilds/{guildId}/widget
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
|
||||
*/
|
||||
export function GUILD_WIDGET(guildId: Snowflake, options: GetWidget = { get: 'settings' }): string {
|
||||
let url = `/guilds/${guildId}/widget`;
|
||||
if (options.get === 'json') {
|
||||
url += '.json';
|
||||
} else if (options.get === 'image') {
|
||||
url += '.png';
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
/** @link https://discord.com/developers/docs/resources/guild#get-guild-voice-regions */
|
||||
export function GUILD_VOICE_REGIONS(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/regions`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-vanity-url
|
||||
* @param guildId The guild
|
||||
* @returns Get vanity URL
|
||||
*/
|
||||
export function GUILD_VANITY(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/vanity-url`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-preview
|
||||
* @param guildId The guild
|
||||
* @returns Get guild preview url
|
||||
*/
|
||||
export function GUILD_PREVIEW(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/preview`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-channels
|
||||
* @param guildId The guild id.
|
||||
* @returns Get guild channels url.
|
||||
*/
|
||||
export function GUILD_CHANNELS(guildId: Snowflake): string {
|
||||
return `/guilds/${guildId}/channels`;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
95
packages/cache/CHANGELOG.md
vendored
95
packages/cache/CHANGELOG.md
vendored
@ -1,95 +0,0 @@
|
||||
# @biscuitland/cache
|
||||
|
||||
## 2.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- fix TODO
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.3.0
|
||||
|
||||
## 2.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bug fixes
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.2.3
|
||||
|
||||
## 2.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- rename guildLocales to guildLocale in interactions
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.2.2
|
||||
|
||||
## 2.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- select menu options now can be empty since the latest Discord API update
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.2.1
|
||||
|
||||
## 2.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Functionality to delete ephemeral messages added, select menus were updated
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.2.0
|
||||
|
||||
## 2.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- minor changes
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.1.2
|
||||
|
||||
## 2.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- dumb hotfix that LH asked for (blame Yuzu)
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.1.1
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Changes to cache and forum channels ✨
|
||||
- Forum channels and updates to @biscuitland/cache ✨
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.1.0
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Minor fixes
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.0.6
|
||||
|
||||
## 2.0.5
|
||||
|
||||
### Major Changes
|
||||
|
||||
- publish
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @biscuitland/api-types@2.0.5
|
39
packages/cache/README.md
vendored
39
packages/cache/README.md
vendored
@ -1,39 +0,0 @@
|
||||
# @biscuitland/cache
|
||||
|
||||
## Most importantly, biscuit's cache is:
|
||||
|
||||
A resource control cache layer, based on carriers and resource-intensive policies
|
||||
|
||||
[<img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white">](https://github.com/oasisjs/biscuit)
|
||||
[<img src="https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white">](https://discord.gg/XNw2RZFzaP)
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/oasisjs/biscuit/main/assets/icon.svg" alt="biscuit"/>
|
||||
|
||||
## Install (for [node18](https://nodejs.org/en/download/))
|
||||
|
||||
```sh-session
|
||||
npm install @biscuitland/cache
|
||||
```
|
||||
|
||||
## Example (Basic)
|
||||
|
||||
```ts
|
||||
import { Cache, MemoryCacheAdapter } from '@biscuitland/cache';
|
||||
|
||||
const bootstrap = async () => {
|
||||
const cache = new Cache({
|
||||
adapter: new MemoryCacheAdapter(),
|
||||
});
|
||||
|
||||
// You can listen to the raw biscuit event
|
||||
|
||||
cache.start(<payloads>);
|
||||
};
|
||||
|
||||
bootstrap();
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Documentation](https://docs.biscuitjs.com/)
|
||||
- [Website](https://biscuitjs.com/)
|
75
packages/cache/package.json
vendored
75
packages/cache/package.json
vendored
@ -1,75 +0,0 @@
|
||||
{
|
||||
"name": "@biscuitland/cache",
|
||||
"version": "2.3.0",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf dist && rm -rf .turbo",
|
||||
"dev": "tsup --watch"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@biscuitland/api-types": "^2.3.0",
|
||||
"ioredis": "^5.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsup": "^6.1.3"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "Yuzuru <yuzuru@programmer.net>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Yuzuru",
|
||||
"url": "https://github.com/yuzudev",
|
||||
"author": true
|
||||
},
|
||||
{
|
||||
"name": "miia",
|
||||
"url": "https://github.com/dragurimu"
|
||||
},
|
||||
{
|
||||
"name": "n128",
|
||||
"url": "https://github.com/nicolito128"
|
||||
},
|
||||
{
|
||||
"name": "socram03",
|
||||
"url": "https://github.com/socram03"
|
||||
},
|
||||
{
|
||||
"name": "Drylozu",
|
||||
"url": "https://github.com/Drylozu"
|
||||
}
|
||||
],
|
||||
"homepage": "https://biscuitjs.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/oasisjs/biscuit.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/oasisjs/biscuit"
|
||||
},
|
||||
"keywords": [
|
||||
"api",
|
||||
"discord",
|
||||
"bots",
|
||||
"typescript",
|
||||
"botdev"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
252
packages/cache/src/cache.ts
vendored
252
packages/cache/src/cache.ts
vendored
@ -1,252 +0,0 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
import type { CacheOptions, CO } from './types';
|
||||
|
||||
import type { CacheAdapter } from './scheme/adapters/cache-adapter';
|
||||
import { MemoryCacheAdapter } from './scheme/adapters/memory-cache-adapter';
|
||||
|
||||
import {
|
||||
ChannelResource,
|
||||
GuildEmojiResource,
|
||||
GuildMemberResource,
|
||||
GuildResource,
|
||||
GuildRoleResource,
|
||||
GuildStickerResource,
|
||||
GuildVoiceResource,
|
||||
PresenceResource,
|
||||
UserResource,
|
||||
} from './resources';
|
||||
|
||||
import { Options } from './utils/options';
|
||||
|
||||
export class Cache {
|
||||
static readonly DEFAULTS = {
|
||||
adapter: new MemoryCacheAdapter(),
|
||||
};
|
||||
|
||||
readonly options: CO;
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
// move to resources assigned
|
||||
|
||||
readonly channels: ChannelResource;
|
||||
|
||||
readonly emojis: GuildEmojiResource;
|
||||
readonly members: GuildMemberResource;
|
||||
readonly guilds: GuildResource;
|
||||
readonly roles: GuildRoleResource;
|
||||
readonly stickers: GuildStickerResource;
|
||||
readonly voices: GuildVoiceResource;
|
||||
|
||||
readonly presences: PresenceResource;
|
||||
readonly users: UserResource;
|
||||
|
||||
constructor(options: CacheOptions) {
|
||||
this.options = Options({}, Cache.DEFAULTS, options);
|
||||
this.#adapter = this.options.adapter;
|
||||
|
||||
this.channels = new ChannelResource(this.#adapter);
|
||||
|
||||
this.emojis = new GuildEmojiResource(this.#adapter);
|
||||
this.members = new GuildMemberResource(this.#adapter);
|
||||
|
||||
this.guilds = new GuildResource(this.#adapter);
|
||||
this.roles = new GuildRoleResource(this.#adapter);
|
||||
|
||||
this.stickers = new GuildStickerResource(this.#adapter);
|
||||
this.voices = new GuildVoiceResource(this.#adapter);
|
||||
|
||||
this.presences = new PresenceResource(this.#adapter);
|
||||
this.users = new UserResource(this.#adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async start(event: any) {
|
||||
let resources: any[] = [];
|
||||
|
||||
let contents: any[] = [];
|
||||
|
||||
switch (event.t) {
|
||||
case 'READY':
|
||||
resources = [];
|
||||
|
||||
await this.users.set(event.d.user.id, event.d.user);
|
||||
|
||||
for (const guild of event.d.guilds) {
|
||||
resources.push(this.guilds.set(guild.id, guild));
|
||||
}
|
||||
|
||||
await Promise.all(resources);
|
||||
|
||||
break;
|
||||
|
||||
case 'USER_UPDATE':
|
||||
await this.users.set(event.d.id, event.d);
|
||||
break;
|
||||
case 'PRESENCE_UPDATE':
|
||||
await this.presences.set(event.d.user?.id, event.d);
|
||||
|
||||
break;
|
||||
|
||||
case 'GUILD_CREATE':
|
||||
case 'GUILD_UPDATE':
|
||||
await this.guilds.set(event.d.id, event.d);
|
||||
break;
|
||||
|
||||
case 'GUILD_DELETE':
|
||||
if (event.d.unavailable) {
|
||||
await this.guilds.set(event.d.id, event.d);
|
||||
} else {
|
||||
await this.guilds.remove(event.d.id);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'CHANNEL_CREATE':
|
||||
case 'CHANNEL_UPDATE':
|
||||
// modify [Add elimination system]
|
||||
await this.channels.set(event.d.id, event.d);
|
||||
break;
|
||||
|
||||
case 'CHANNEL_DELETE':
|
||||
// modify [Add elimination system]
|
||||
await this.channels.remove(event.d.id);
|
||||
break;
|
||||
|
||||
case 'MESSAGE_CREATE':
|
||||
if (event.d.webhook_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.d.author) {
|
||||
await this.users.set(event.d.author.id, event.d.author);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'GUILD_ROLE_CREATE':
|
||||
case 'GUILD_ROLE_UPDATE':
|
||||
await this.roles.set(
|
||||
event.d.role.id,
|
||||
event.d.guild_id,
|
||||
event.d.role
|
||||
);
|
||||
break;
|
||||
|
||||
case 'GUILD_ROLE_DELETE':
|
||||
await this.roles.remove(event.d.role.id, event.d.guild_id);
|
||||
break;
|
||||
|
||||
case 'GUILD_EMOJIS_UPDATE':
|
||||
contents = [];
|
||||
contents = await this.emojis.items(event.d.guild_id);
|
||||
|
||||
for (const emoji of event.d.emojis) {
|
||||
const emote = contents.find(o => o?.id === emoji.id);
|
||||
|
||||
if (!emote || emote !== emoji) {
|
||||
await this.emojis.set(
|
||||
emoji.id,
|
||||
event.d.guild_id,
|
||||
emoji
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const emoji of contents) {
|
||||
const emote = event.d.emojis.find(
|
||||
(o: any) => o.id === emoji?.id
|
||||
);
|
||||
|
||||
if (!emote) {
|
||||
await this.emojis.remove(emote.id, event.d.guild_id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'GUILD_STICKERS_UPDATE':
|
||||
contents = [];
|
||||
contents = await this.stickers.items(event.d.guild_id);
|
||||
|
||||
for (const sticker of event.d.stickers) {
|
||||
const stick = contents.find(o => o?.id === sticker.id);
|
||||
|
||||
if (!stick || stick !== sticker) {
|
||||
await this.stickers.set(
|
||||
sticker.id,
|
||||
event.d.guild_id,
|
||||
sticker
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const sticker of contents) {
|
||||
const stick = event.d.stickers.find(
|
||||
(o: any) => o.id === sticker?.id
|
||||
);
|
||||
|
||||
if (!stick) {
|
||||
await this.stickers.remove(stick.id, event.d.guild_id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'GUILD_MEMBER_ADD':
|
||||
case 'GUILD_MEMBER_UPDATE':
|
||||
await this.members.set(
|
||||
event.d.user.id,
|
||||
event.d.guild_id,
|
||||
event.d
|
||||
);
|
||||
break;
|
||||
|
||||
case 'GUILD_MEMBER_REMOVE':
|
||||
await this.members.remove(event.d.user.id, event.d.guild_id);
|
||||
break;
|
||||
|
||||
case 'GUILD_MEMBERS_CHUNK':
|
||||
resources = [];
|
||||
|
||||
for (const member of event.d.members) {
|
||||
resources.push(
|
||||
this.members.set(
|
||||
member.user.id,
|
||||
event.d.guild_id,
|
||||
member
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(resources);
|
||||
|
||||
break;
|
||||
|
||||
case 'VOICE_STATE_UPDATE':
|
||||
if (!event.d.guild_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.d.guild_id && event.d.member && event.d.user_id) {
|
||||
await this.members.set(event.d.user_id, event.d.guild_id, {
|
||||
guild_id: event.d.guild_id,
|
||||
...event.d.member,
|
||||
});
|
||||
}
|
||||
|
||||
if (event.d.channel_id != null) {
|
||||
await this.members.set(
|
||||
event.d.user_id,
|
||||
event.d.guild_id,
|
||||
event.d
|
||||
);
|
||||
} else {
|
||||
await this.voices.remove(event.d.user_id, event.d.guild_id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
7
packages/cache/src/index.ts
vendored
7
packages/cache/src/index.ts
vendored
@ -1,7 +0,0 @@
|
||||
export { MemoryCacheAdapter } from './scheme/adapters/memory-cache-adapter';
|
||||
export { RedisCacheAdapter } from './scheme/adapters/redis-cache-adapter';
|
||||
|
||||
export { CacheAdapter } from './scheme/adapters/cache-adapter';
|
||||
|
||||
export { RedisOptions, MemoryOptions, CacheOptions } from './types';
|
||||
export { Cache } from './cache';
|
117
packages/cache/src/resources/base-resource.ts
vendored
117
packages/cache/src/resources/base-resource.ts
vendored
@ -1,117 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
|
||||
/**
|
||||
* Base class for all resources
|
||||
* All Methods from BaseResource are also available on every class extends
|
||||
*/
|
||||
|
||||
class Base<T> {
|
||||
/**
|
||||
* Resource name
|
||||
*/
|
||||
|
||||
#namespace = 'base';
|
||||
|
||||
/**
|
||||
* Adapter for storage processes and operations
|
||||
*/
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
/**
|
||||
* Guild linked and assigned to the current entity (resource)
|
||||
*/
|
||||
|
||||
parent?: string;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
||||
constructor(namespace: string, adapter: CacheAdapter) {
|
||||
this.#namespace = namespace;
|
||||
this.#adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity linked
|
||||
*/
|
||||
|
||||
setEntity(entity: T): void {
|
||||
Object.assign(this, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parent linked
|
||||
*/
|
||||
|
||||
setParent(parent: string): void {
|
||||
// rename
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count how many resources there are in the relationships
|
||||
*/
|
||||
|
||||
async count(to: string): Promise<number> {
|
||||
return await this.#adapter.count(this.hashId(to));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the resource is in the relationships
|
||||
*/
|
||||
|
||||
async contains(
|
||||
id: string,
|
||||
guild: string = this.parent as string
|
||||
): Promise<boolean> {
|
||||
return await this.#adapter.contains(this.hashId(guild), id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the resource relationships
|
||||
*/
|
||||
|
||||
async getToRelationship(
|
||||
id: string = this.parent as string
|
||||
): Promise<string[]> {
|
||||
return await this.#adapter.getToRelationship(this.hashId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the resource to relationships
|
||||
*/
|
||||
|
||||
async addToRelationship(
|
||||
id: string,
|
||||
guild: string = this.parent as string
|
||||
): Promise<void> {
|
||||
await this.#adapter.addToRelationship(this.hashId(guild), id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the relationship resource
|
||||
*/
|
||||
|
||||
async removeToRelationship(
|
||||
id: string,
|
||||
guild: string = this.parent as string
|
||||
): Promise<void> {
|
||||
await this.#adapter.removeToRelationship(this.hashId(guild), id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an id consisting of namespace.id
|
||||
*/
|
||||
|
||||
protected hashId(id: string): string {
|
||||
return `${this.#namespace}.${id}`;
|
||||
}
|
||||
}
|
||||
|
||||
export const BaseResource = Base as new <T>(
|
||||
data: string,
|
||||
adapter: CacheAdapter
|
||||
) => Base<T> & T;
|
139
packages/cache/src/resources/channel-resource.ts
vendored
139
packages/cache/src/resources/channel-resource.ts
vendored
@ -1,139 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordChannel } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
import { UserResource } from './user-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an channel of discord
|
||||
*/
|
||||
|
||||
export class ChannelResource extends BaseResource<DiscordChannel> {
|
||||
#namespace = 'channel';
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#users: UserResource;
|
||||
|
||||
constructor(adapter: CacheAdapter, entity?: DiscordChannel | null) {
|
||||
super('channel', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
this.#users = new UserResource(adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string): Promise<ChannelResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashId(id));
|
||||
|
||||
if (kv) {
|
||||
return new ChannelResource(this.#adapter, kv);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(id: string, data: any): Promise<void> {
|
||||
if (data.recipients) {
|
||||
const recipients = [];
|
||||
|
||||
for (const recipient of data.recipients) {
|
||||
recipients.push(this.#users.set(recipient.id, recipient));
|
||||
}
|
||||
|
||||
await Promise.all(recipients);
|
||||
}
|
||||
|
||||
delete data.recipients;
|
||||
delete data.permission_overwrites;
|
||||
|
||||
await this.addToRelationship(id);
|
||||
await this.#adapter.set(this.hashId(id), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(): Promise<ChannelResource[]> {
|
||||
const data = await this.#adapter.items(this.#namespace);
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new ChannelResource(this.#adapter, dt);
|
||||
resource.setParent(resource.id);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(): Promise<number> {
|
||||
return await this.#adapter.count(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.#adapter.remove(this.hashId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async contains(id: string): Promise<boolean> {
|
||||
return await this.#adapter.contains(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getToRelationship(): Promise<string[]> {
|
||||
return await this.#adapter.getToRelationship(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async addToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.addToRelationship(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async removeToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.removeToRelationship(this.#namespace, id);
|
||||
}
|
||||
}
|
128
packages/cache/src/resources/guild-emoji-resource.ts
vendored
128
packages/cache/src/resources/guild-emoji-resource.ts
vendored
@ -1,128 +0,0 @@
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordEmoji } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
import { UserResource } from './user-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an emoji of discord
|
||||
*/
|
||||
|
||||
export class GuildEmojiResource extends BaseResource<DiscordEmoji> {
|
||||
#namespace = 'emoji' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#users: UserResource;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordEmoji | null,
|
||||
parent?: string
|
||||
) {
|
||||
super('emoji', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
this.#users = new UserResource(adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<GuildEmojiResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashGuildId(id, guild));
|
||||
|
||||
if (kv) {
|
||||
return new GuildEmojiResource(this.#adapter, kv, guild);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent,
|
||||
data: any
|
||||
): Promise<void> {
|
||||
if (data.user) {
|
||||
await this.#users.set(data.user.id, data.user);
|
||||
}
|
||||
|
||||
delete data.user;
|
||||
delete data.roles;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id, guild);
|
||||
await this.#adapter.set(this.hashGuildId(id, guild), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<GuildEmojiResource[]> {
|
||||
if (!to && this.parent) {
|
||||
to = this.parent;
|
||||
}
|
||||
|
||||
const data = await this.#adapter.items(this.hashId(to));
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildEmojiResource(this.#adapter, dt);
|
||||
resource.setParent(to);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<void> {
|
||||
await this.removeToRelationship(id, guild);
|
||||
await this.#adapter.remove(this.hashGuildId(id, guild));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected hashGuildId(id: string, guild?: string): string {
|
||||
if (!guild) {
|
||||
return this.hashId(id);
|
||||
}
|
||||
|
||||
return `${this.#namespace}.${guild}.${id}`;
|
||||
}
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordMember } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
import { UserResource } from './user-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an member of discord
|
||||
*/
|
||||
|
||||
export class GuildMemberResource extends BaseResource<DiscordMember> {
|
||||
#namespace = 'member' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#users: UserResource;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordMember | null,
|
||||
parent?: string
|
||||
) {
|
||||
super('member', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
this.#users = new UserResource(adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<GuildMemberResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashGuildId(id, guild));
|
||||
|
||||
if (kv) {
|
||||
return new GuildMemberResource(this.#adapter, kv, guild);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent,
|
||||
data: any
|
||||
): Promise<void> {
|
||||
if (data.user) {
|
||||
await this.#users.set(data.user.id, data.user);
|
||||
}
|
||||
|
||||
delete data.user;
|
||||
delete data.roles;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id, guild);
|
||||
await this.#adapter.set(this.hashGuildId(id, guild), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<GuildMemberResource[]> {
|
||||
if (!to && this.parent) {
|
||||
to = this.parent;
|
||||
}
|
||||
|
||||
const data = await this.#adapter.items(this.hashId(to));
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildMemberResource(this.#adapter, dt);
|
||||
resource.setParent(to);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<void> {
|
||||
await this.removeToRelationship(id, guild);
|
||||
await this.#adapter.remove(this.hashGuildId(id, guild));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected hashGuildId(id: string, guild?: string): string {
|
||||
if (!guild) {
|
||||
return this.hashId(id);
|
||||
}
|
||||
|
||||
return `${this.#namespace}.${guild}.${id}`;
|
||||
}
|
||||
}
|
339
packages/cache/src/resources/guild-resource.ts
vendored
339
packages/cache/src/resources/guild-resource.ts
vendored
@ -1,339 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordGuild } from '@biscuitland/api-types';
|
||||
|
||||
import { ChannelResource } from './channel-resource';
|
||||
import { GuildEmojiResource } from './guild-emoji-resource';
|
||||
import { GuildMemberResource } from './guild-member-resource';
|
||||
import { GuildRoleResource } from './guild-role-resource';
|
||||
import { GuildStickerResource } from './guild-sticker-resource';
|
||||
import { GuildVoiceResource } from './guild-voice-resource';
|
||||
|
||||
import { PresenceResource } from './presence-resource';
|
||||
import { BaseResource } from './base-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an guild of discord
|
||||
*/
|
||||
|
||||
export class GuildResource extends BaseResource<DiscordGuild> {
|
||||
#namespace = 'guild' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#channels: ChannelResource;
|
||||
#emojis: GuildEmojiResource;
|
||||
#members: GuildMemberResource;
|
||||
#roles: GuildRoleResource;
|
||||
#stickers: GuildStickerResource;
|
||||
#voices: GuildVoiceResource;
|
||||
|
||||
#presences: PresenceResource;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordGuild | null,
|
||||
parent?: string,
|
||||
channels?: ChannelResource,
|
||||
emojis?: GuildEmojiResource,
|
||||
members?: GuildMemberResource,
|
||||
roles?: GuildRoleResource,
|
||||
stickers?: GuildStickerResource,
|
||||
voices?: GuildVoiceResource,
|
||||
presences?: PresenceResource
|
||||
) {
|
||||
super('guild', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
|
||||
this.#channels = channels ?? new ChannelResource(adapter);
|
||||
|
||||
this.#emojis = emojis ?? new GuildEmojiResource(adapter);
|
||||
this.#members = members ?? new GuildMemberResource(adapter);
|
||||
|
||||
this.#roles = roles ?? new GuildRoleResource(adapter);
|
||||
|
||||
this.#stickers = stickers ?? new GuildStickerResource(adapter);
|
||||
|
||||
this.#voices = voices ?? new GuildVoiceResource(adapter);
|
||||
this.#presences = presences ?? new PresenceResource(adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string): Promise<GuildResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashId(id));
|
||||
|
||||
if (kv) {
|
||||
return new GuildResource(
|
||||
this.#adapter,
|
||||
kv,
|
||||
id,
|
||||
new ChannelResource(this.#adapter),
|
||||
new GuildEmojiResource(this.#adapter, null, id),
|
||||
new GuildMemberResource(this.#adapter, null, id),
|
||||
new GuildRoleResource(this.#adapter, null, id),
|
||||
new GuildStickerResource(this.#adapter, null, id),
|
||||
new GuildVoiceResource(this.#adapter, null, id),
|
||||
new PresenceResource(this.#adapter)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(id: string, data: any): Promise<void> {
|
||||
if (data.channels) {
|
||||
const channels: unknown[] = [];
|
||||
|
||||
for (const channel of data.channels) {
|
||||
channel.guild_id = id;
|
||||
|
||||
await this.#channels.set(channel.id, channel);
|
||||
}
|
||||
|
||||
await Promise.all(channels);
|
||||
}
|
||||
|
||||
if (data.emojis) {
|
||||
const emojis: unknown[] = [];
|
||||
|
||||
for (const emoji of data.emojis) {
|
||||
emoji.guild_id = id;
|
||||
|
||||
await this.#emojis.set(emoji.id, id, emoji);
|
||||
}
|
||||
|
||||
await Promise.all(emojis);
|
||||
}
|
||||
|
||||
if (data.members) {
|
||||
const members: unknown[] = [];
|
||||
|
||||
for (const member of data.members) {
|
||||
member.guild_id = id;
|
||||
|
||||
await this.#members.set(member.user.id, id, member);
|
||||
}
|
||||
|
||||
await Promise.all(members);
|
||||
}
|
||||
|
||||
if (data.roles) {
|
||||
const roles: unknown[] = [];
|
||||
|
||||
for (const role of data.roles) {
|
||||
role.guild_id = id;
|
||||
|
||||
await this.#roles.set(role.id, id, role);
|
||||
}
|
||||
|
||||
await Promise.all(roles);
|
||||
}
|
||||
|
||||
if (data.stickers) {
|
||||
const stickers: unknown[] = [];
|
||||
|
||||
for (const sticker of data.stickers) {
|
||||
sticker.guild_id = id;
|
||||
|
||||
await this.#stickers.set(sticker.id, id, sticker);
|
||||
}
|
||||
|
||||
await Promise.all(stickers);
|
||||
}
|
||||
|
||||
if (data.voice_states) {
|
||||
const voices: unknown[] = [];
|
||||
|
||||
for (const voice of data.voice_states) {
|
||||
voice.guild_id = id;
|
||||
|
||||
voices.push(this.#voices.set(voice.user_id, id, voice));
|
||||
}
|
||||
|
||||
await Promise.all(voices);
|
||||
}
|
||||
|
||||
if (data.presences) {
|
||||
const presences: unknown[] = [];
|
||||
|
||||
for (const presence of data.presences) {
|
||||
await this.#presences.set(presence.user.id, presence);
|
||||
}
|
||||
|
||||
await Promise.all(presences);
|
||||
}
|
||||
|
||||
delete data.channels;
|
||||
delete data.emojis;
|
||||
delete data.members;
|
||||
delete data.roles;
|
||||
delete data.stickers;
|
||||
|
||||
delete data.voice_states;
|
||||
delete data.guild_hashes;
|
||||
|
||||
delete data.presences;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id);
|
||||
await this.#adapter.set(this.hashId(id), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(): Promise<GuildResource[]> {
|
||||
const data = await this.#adapter.items(this.#namespace);
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildResource(this.#adapter, dt);
|
||||
resource.setParent(resource.id);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(): Promise<number> {
|
||||
return await this.#adapter.count(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
const members = await this.#members.getToRelationship(id);
|
||||
|
||||
for (const member of members) {
|
||||
await this.#members.remove(member, id);
|
||||
}
|
||||
|
||||
const roles = await this.#roles.getToRelationship(id);
|
||||
|
||||
for (const role of roles) {
|
||||
await this.#roles.remove(role, id);
|
||||
}
|
||||
|
||||
const emojis = await this.#emojis.getToRelationship(id);
|
||||
|
||||
for (const emoji of emojis) {
|
||||
await this.#emojis.remove(emoji, id);
|
||||
}
|
||||
|
||||
const stickers = await this.#stickers.getToRelationship(id);
|
||||
|
||||
for (const sticker of stickers) {
|
||||
await this.#stickers.remove(sticker, id);
|
||||
}
|
||||
|
||||
await this.removeToRelationship(id);
|
||||
await this.#adapter.remove(this.hashId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async contains(id: string): Promise<boolean> {
|
||||
return await this.#adapter.contains(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getToRelationship(): Promise<string[]> {
|
||||
return await this.#adapter.getToRelationship(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async addToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.addToRelationship(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async removeToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.removeToRelationship(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getEmojis(): Promise<GuildEmojiResource[]> {
|
||||
return await this.#emojis.items(this.parent as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getMembers(): Promise<GuildMemberResource[]> {
|
||||
return await this.#members.items(this.parent as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getRoles(): Promise<GuildRoleResource[]> {
|
||||
return await this.#roles.items(this.parent as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getStickers(): Promise<GuildStickerResource[]> {
|
||||
return await this.#stickers.items(this.parent as string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getVoiceStates(): Promise<GuildVoiceResource[]> {
|
||||
return await this.#voices.items(this.parent as string);
|
||||
}
|
||||
}
|
133
packages/cache/src/resources/guild-role-resource.ts
vendored
133
packages/cache/src/resources/guild-role-resource.ts
vendored
@ -1,133 +0,0 @@
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordRole } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an role of discord
|
||||
*/
|
||||
|
||||
export class GuildRoleResource extends BaseResource<DiscordRole> {
|
||||
#namespace = 'role' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordRole | null,
|
||||
parent?: string
|
||||
) {
|
||||
super('role', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<GuildRoleResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashGuildId(id, guild));
|
||||
|
||||
if (kv) {
|
||||
return new GuildRoleResource(this.#adapter, kv, guild);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent,
|
||||
data: any
|
||||
): Promise<void> {
|
||||
if (!data.id) {
|
||||
data.id = id;
|
||||
}
|
||||
|
||||
if (!data.guild_id) {
|
||||
data.guild_id = guild;
|
||||
}
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id, guild);
|
||||
await this.#adapter.set(this.hashGuildId(id, guild), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(): Promise<number> {
|
||||
return await this.#adapter.count(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<GuildRoleResource[]> {
|
||||
if (!to && this.parent) {
|
||||
to = this.parent;
|
||||
}
|
||||
|
||||
const data = await this.#adapter.items(this.hashId(to));
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildRoleResource(this.#adapter, dt);
|
||||
resource.setParent(to);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<void> {
|
||||
await this.removeToRelationship(id, guild);
|
||||
await this.#adapter.remove(this.hashGuildId(id, guild));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected hashGuildId(id: string, guild?: string): string {
|
||||
if (!guild) {
|
||||
return this.hashId(id);
|
||||
}
|
||||
|
||||
return `${this.#namespace}.${guild}.${id}`;
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordSticker } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
import { UserResource } from './user-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an sticker of discord
|
||||
*/
|
||||
|
||||
export class GuildStickerResource extends BaseResource<DiscordSticker> {
|
||||
#namespace = 'sticker' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#users: UserResource;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordSticker | null,
|
||||
parent?: string
|
||||
) {
|
||||
super('sticker', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
this.#users = new UserResource(adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string, guild: string): Promise<GuildStickerResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashGuildId(id, guild));
|
||||
|
||||
if (kv) {
|
||||
return new GuildStickerResource(this.#adapter, kv, guild);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent,
|
||||
data: any
|
||||
): Promise<void> {
|
||||
if (data.user) {
|
||||
await this.#users.set(data.user.id, data.user);
|
||||
}
|
||||
|
||||
delete data.user;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id, guild);
|
||||
await this.#adapter.set(this.hashGuildId(id, guild), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<GuildStickerResource[]> {
|
||||
if (!to && this.parent) {
|
||||
to = this.parent;
|
||||
}
|
||||
|
||||
const data = await this.#adapter.items(this.hashId(to));
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildStickerResource(this.#adapter, dt);
|
||||
resource.setParent(to);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<void> {
|
||||
await this.removeToRelationship(id, guild);
|
||||
await this.#adapter.remove(this.hashGuildId(id, guild));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected hashGuildId(id: string, guild?: string): string {
|
||||
if (!guild) {
|
||||
return this.hashId(id);
|
||||
}
|
||||
|
||||
return `${this.#namespace}.${guild}.${id}`;
|
||||
}
|
||||
}
|
127
packages/cache/src/resources/guild-voice-resource.ts
vendored
127
packages/cache/src/resources/guild-voice-resource.ts
vendored
@ -1,127 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordVoiceState } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an voice state of discord
|
||||
*/
|
||||
|
||||
export class GuildVoiceResource extends BaseResource<DiscordVoiceState> {
|
||||
#namespace = 'voice' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
constructor(
|
||||
adapter: CacheAdapter,
|
||||
entity?: DiscordVoiceState | null,
|
||||
parent?: string
|
||||
) {
|
||||
super('voice', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
this.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<GuildVoiceResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashGuildId(id, guild));
|
||||
|
||||
if (kv) {
|
||||
return new GuildVoiceResource(this.#adapter, kv, guild);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent,
|
||||
data: any
|
||||
): Promise<void> {
|
||||
if (!data.guild_id) {
|
||||
data.guild_id = guild;
|
||||
}
|
||||
|
||||
delete data.member;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id, guild);
|
||||
await this.#adapter.set(this.hashGuildId(id, guild), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<GuildVoiceResource[]> {
|
||||
if (!to && this.parent) {
|
||||
to = this.parent;
|
||||
}
|
||||
|
||||
const data = await this.#adapter.items(this.hashId(to));
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => {
|
||||
const resource = new GuildVoiceResource(this.#adapter, dt);
|
||||
resource.setParent(to);
|
||||
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(
|
||||
id: string,
|
||||
guild: string | undefined = this.parent
|
||||
): Promise<void> {
|
||||
await this.removeToRelationship(id, guild);
|
||||
await this.#adapter.remove(this.hashGuildId(id, guild));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected hashGuildId(id: string, guild?: string): string {
|
||||
if (!guild) {
|
||||
return this.hashId(id);
|
||||
}
|
||||
|
||||
return `${this.#namespace}.${guild}.${id}`;
|
||||
}
|
||||
}
|
15
packages/cache/src/resources/index.ts
vendored
15
packages/cache/src/resources/index.ts
vendored
@ -1,15 +0,0 @@
|
||||
export { BaseResource } from './base-resource';
|
||||
export { ChannelResource } from './channel-resource';
|
||||
|
||||
export { GuildEmojiResource } from './guild-emoji-resource';
|
||||
export { GuildMemberResource } from './guild-member-resource';
|
||||
|
||||
export { GuildResource } from './guild-resource';
|
||||
|
||||
export { GuildRoleResource } from './guild-role-resource';
|
||||
|
||||
export { GuildStickerResource } from './guild-sticker-resource';
|
||||
export { GuildVoiceResource } from './guild-voice-resource';
|
||||
|
||||
export { PresenceResource } from './presence-resource';
|
||||
export { UserResource } from './user-resource';
|
135
packages/cache/src/resources/presence-resource.ts
vendored
135
packages/cache/src/resources/presence-resource.ts
vendored
@ -1,135 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordPresenceUpdate } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
import { UserResource } from './user-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an presence of discord
|
||||
*/
|
||||
|
||||
export class PresenceResource extends BaseResource<DiscordPresenceUpdate> {
|
||||
#namespace = 'presence' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
#users: UserResource;
|
||||
|
||||
constructor(adapter: CacheAdapter, entity?: DiscordPresenceUpdate | null) {
|
||||
super('presence', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
this.#users = new UserResource(this.#adapter);
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string): Promise<PresenceResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashId(id));
|
||||
|
||||
if (kv) {
|
||||
return new PresenceResource(this.#adapter, kv);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(id: string, data: any): Promise<void> {
|
||||
if (data.user) {
|
||||
await this.#users.set(data.user.id, data.user);
|
||||
}
|
||||
|
||||
delete data.user;
|
||||
delete data.roles;
|
||||
|
||||
delete data.guild_id;
|
||||
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id);
|
||||
await this.#adapter.set(this.hashId(id), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(): Promise<PresenceResource[]> {
|
||||
const data = await this.#adapter.items(this.#namespace);
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => new PresenceResource(this.#adapter, dt));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(): Promise<number> {
|
||||
return await this.#adapter.count(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.removeToRelationship(id);
|
||||
await this.#adapter.remove(this.hashId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async contains(id: string): Promise<boolean> {
|
||||
return await this.#adapter.contains(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getToRelationship(): Promise<string[]> {
|
||||
return await this.#adapter.getToRelationship(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async addToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.addToRelationship(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async removeToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.removeToRelationship(this.#namespace, id);
|
||||
}
|
||||
}
|
122
packages/cache/src/resources/user-resource.ts
vendored
122
packages/cache/src/resources/user-resource.ts
vendored
@ -1,122 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from '../scheme/adapters/cache-adapter';
|
||||
import type { DiscordUser } from '@biscuitland/api-types';
|
||||
|
||||
import { BaseResource } from './base-resource';
|
||||
|
||||
/**
|
||||
* Resource represented by an user of discord
|
||||
*/
|
||||
|
||||
export class UserResource extends BaseResource<DiscordUser> {
|
||||
#namespace = 'user' as const;
|
||||
|
||||
#adapter: CacheAdapter;
|
||||
|
||||
constructor(adapter: CacheAdapter, entity?: DiscordUser | null) {
|
||||
super('user', adapter);
|
||||
|
||||
this.#adapter = adapter;
|
||||
|
||||
if (entity) {
|
||||
this.setEntity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string): Promise<UserResource | null> {
|
||||
if (this.parent) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const kv = await this.#adapter.get(this.hashId(id));
|
||||
|
||||
if (kv) {
|
||||
return new UserResource(this.#adapter, kv);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(id: string, data: any): Promise<void> {
|
||||
if (this.parent) {
|
||||
this.setEntity(data);
|
||||
}
|
||||
|
||||
await this.addToRelationship(id);
|
||||
await this.#adapter.set(this.hashId(id), data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(): Promise<UserResource[]> {
|
||||
const data = await this.#adapter.items(this.#namespace);
|
||||
|
||||
if (data) {
|
||||
return data.map(dt => new UserResource(this.#adapter, dt));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(): Promise<number> {
|
||||
return await this.#adapter.count(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.removeToRelationship(id);
|
||||
await this.#adapter.remove(this.hashId(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async contains(id: string): Promise<boolean> {
|
||||
return await this.#adapter.contains(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getToRelationship(): Promise<string[]> {
|
||||
return await this.#adapter.getToRelationship(this.#namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async addToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.addToRelationship(this.#namespace, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async removeToRelationship(id: string): Promise<void> {
|
||||
await this.#adapter.removeToRelationship(this.#namespace, id);
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Base class for all adapters
|
||||
* All Methods from CacheAdapter are also available on every class extends
|
||||
*/
|
||||
|
||||
export interface CacheAdapter {
|
||||
/**
|
||||
* Gets the resource to adapter
|
||||
*/
|
||||
|
||||
get(id: string): any | Promise<any>;
|
||||
get(id: string, guild: string): string | Promise<string>;
|
||||
|
||||
/**
|
||||
* Sets the resource to adapter
|
||||
*/
|
||||
|
||||
set(id: string, data: any): void | Promise<void>;
|
||||
set(id: string, guild: string, data: any): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Get the items of a relationship
|
||||
*/
|
||||
|
||||
items(to?: string): any[] | Promise<any[]>;
|
||||
|
||||
/**
|
||||
* Count how many resources there are in the relationships
|
||||
*/
|
||||
|
||||
count(to: string): number | Promise<number>;
|
||||
|
||||
/**
|
||||
* Removes the adapter resource
|
||||
*/
|
||||
|
||||
remove(id: string): void | Promise<void>;
|
||||
remove(id: string, guild: string): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Check if the resource is in the relationships
|
||||
*/
|
||||
|
||||
contains(to: string, id: string): boolean | Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Gets the resource relationships
|
||||
*/
|
||||
|
||||
getToRelationship(to: string): string[] | Promise<string[]>;
|
||||
|
||||
/**
|
||||
* Adds the resource to relationships
|
||||
*/
|
||||
|
||||
addToRelationship(to: string, id: string): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Removes the relationship resource
|
||||
*/
|
||||
|
||||
removeToRelationship(to: string, id: string): void | Promise<void>;
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from './cache-adapter';
|
||||
import type { MemoryOptions, MO } from '../../types';
|
||||
|
||||
import { Options } from '../../utils/options';
|
||||
|
||||
export class MemoryCacheAdapter implements CacheAdapter {
|
||||
static readonly DEFAULTS = {
|
||||
expire: 3600000,
|
||||
};
|
||||
|
||||
readonly relationships = new Map<string, string[]>();
|
||||
readonly storage = new Map<string, { data: any; expire?: number }>();
|
||||
|
||||
readonly options: MO;
|
||||
|
||||
constructor(options?: MemoryOptions) {
|
||||
this.options = Options({}, MemoryCacheAdapter.DEFAULTS, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
get<T = any>(id: string): T | null {
|
||||
const data = this.storage.get(id);
|
||||
|
||||
if (data) {
|
||||
if (data.expire && data.expire < Date.now()) {
|
||||
this.storage.delete(id);
|
||||
} else {
|
||||
return JSON.parse(data.data);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
set(id: string, data: any): void {
|
||||
const expire = this.options.expire;
|
||||
|
||||
if (expire) {
|
||||
this.storage.set(id, {
|
||||
data: JSON.stringify(data),
|
||||
expire: Date.now() + expire,
|
||||
});
|
||||
} else {
|
||||
this.storage.set(id, { data: JSON.stringify(data) });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
items(to: string): any[] {
|
||||
const array: unknown[] = [];
|
||||
let data = this.getToRelationship(to);
|
||||
|
||||
data = data.map(id => `${to}.${id}`);
|
||||
|
||||
for (const key of data) {
|
||||
const content = this.get(key);
|
||||
|
||||
if (content) {
|
||||
array.push(content);
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
count(to: string): number {
|
||||
return this.getToRelationship(to).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
remove(id: string): void {
|
||||
this.storage.delete(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
contains(to: string, id: string): boolean {
|
||||
return this.getToRelationship(to).includes(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
getToRelationship(to: string): string[] {
|
||||
return this.relationships.get(to) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
addToRelationship(to: string, id: string): void {
|
||||
const data = this.getToRelationship(to);
|
||||
|
||||
if (data.includes(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.push(id);
|
||||
|
||||
const has = !!this.relationships.get(to);
|
||||
|
||||
if (!has) {
|
||||
this.relationships.set(to, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
removeToRelationship(to: string, id: string): void {
|
||||
const data = this.getToRelationship(to);
|
||||
|
||||
if (data) {
|
||||
const idx = data.indexOf(id);
|
||||
|
||||
if (idx === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
/**
|
||||
* refactor
|
||||
*/
|
||||
|
||||
import type { CacheAdapter } from './cache-adapter';
|
||||
|
||||
import type { RedisOptions } from 'ioredis';
|
||||
import type Redis from 'ioredis';
|
||||
import IORedis from 'ioredis';
|
||||
|
||||
interface BaseOptions {
|
||||
namespace: string;
|
||||
expire?: number;
|
||||
}
|
||||
|
||||
interface BuildOptions extends BaseOptions, RedisOptions {}
|
||||
|
||||
interface ClientOptions extends BaseOptions {
|
||||
client: Redis;
|
||||
}
|
||||
|
||||
type Options = BuildOptions | ClientOptions;
|
||||
|
||||
export class RedisCacheAdapter implements CacheAdapter {
|
||||
static readonly DEFAULTS = {
|
||||
namespace: 'biscuitland',
|
||||
};
|
||||
|
||||
readonly options: Options;
|
||||
|
||||
readonly client: Redis;
|
||||
|
||||
constructor(options?: Options) {
|
||||
this.options = Object.assign(RedisCacheAdapter.DEFAULTS, options);
|
||||
|
||||
if ((this.options as ClientOptions).client) {
|
||||
this.client = (this.options as ClientOptions).client;
|
||||
} else {
|
||||
const { ...redisOpt } = this.options as BuildOptions;
|
||||
this.client = new IORedis(redisOpt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async get(id: string): Promise<any> {
|
||||
const data = await this.client.get(this.build(id));
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async set(id: string, data: unknown): Promise<void> {
|
||||
const expire = this.options.expire;
|
||||
|
||||
if (expire) {
|
||||
await this.client.set(
|
||||
this.build(id),
|
||||
JSON.stringify(data),
|
||||
'EX',
|
||||
expire
|
||||
);
|
||||
} else {
|
||||
await this.client.set(this.build(id), JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async items(to: string): Promise<any[]> {
|
||||
const array: unknown[] = [];
|
||||
|
||||
let data = await this.getToRelationship(to);
|
||||
data = data.map(id => this.build(`${to}.${id}`));
|
||||
|
||||
if (data && data.length > 0) {
|
||||
const items = await this.client.mget(data);
|
||||
|
||||
for (const item of items) {
|
||||
if (item) {
|
||||
array.push(JSON.parse(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async count(to: string): Promise<number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.scard(this.build(to), (err, result) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return resolve(result || 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.client.del(this.build(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async contains(to: string, id: string): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.sismember(this.build(to), id, (err, result) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
return resolve(result === 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async getToRelationship(to: string): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.smembers(this.build(to), (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(result || []);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async addToRelationship(to: string, id: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.sadd(this.build(to), id, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async removeToRelationship(to: string, id: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.client.srem(this.build(to), id, err => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
protected build(id: string): string {
|
||||
return `${this.options.namespace}:${id}`;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* future update
|
||||
*/
|
||||
|
||||
export interface BaseTransporter {
|
||||
//
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
import type { BaseTransporter } from './base-transporter';
|
||||
|
||||
export class RedisTransporter implements BaseTransporter {}
|
@ -1,3 +0,0 @@
|
||||
import type { BaseTransporter } from './base-transporter';
|
||||
|
||||
export class TcpTransporter implements BaseTransporter {}
|
55
packages/cache/src/types.ts
vendored
55
packages/cache/src/types.ts
vendored
@ -1,55 +0,0 @@
|
||||
import type { Cache } from './cache';
|
||||
import type { CacheAdapter } from './scheme/adapters/cache-adapter';
|
||||
|
||||
import type { MemoryCacheAdapter } from './scheme/adapters/memory-cache-adapter';
|
||||
|
||||
//
|
||||
|
||||
export type CacheOptions = Pick<
|
||||
CO,
|
||||
Exclude<keyof CO, keyof typeof Cache.DEFAULTS>
|
||||
> &
|
||||
Partial<CO>;
|
||||
|
||||
export interface CO {
|
||||
/**
|
||||
* Adapter to be used for storing resources
|
||||
* @default MemoryCacheAdapter
|
||||
*/
|
||||
|
||||
adapter: CacheAdapter;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export type MemoryOptions = Pick<
|
||||
MO,
|
||||
Exclude<keyof MO, keyof typeof MemoryCacheAdapter.DEFAULTS>
|
||||
> &
|
||||
Partial<MO>;
|
||||
|
||||
export interface MO {
|
||||
/**
|
||||
* Time the resource will be stored
|
||||
* @default 3600000
|
||||
*/
|
||||
|
||||
expire: number;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
export type RedisOptions = Pick<
|
||||
RO,
|
||||
Exclude<keyof RO, keyof typeof MemoryCacheAdapter.DEFAULTS>
|
||||
> &
|
||||
Partial<RO>;
|
||||
|
||||
export interface RO {
|
||||
/**
|
||||
* Time the resource will be stored
|
||||
* @default 300
|
||||
*/
|
||||
|
||||
expire: number;
|
||||
}
|
48
packages/cache/src/utils/options.ts
vendored
48
packages/cache/src/utils/options.ts
vendored
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Needs to be moved to a common location
|
||||
* refactor
|
||||
*/
|
||||
|
||||
const isPlainObject = (value: any) => {
|
||||
return (
|
||||
(value !== null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.constructor === 'function' &&
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
(value.constructor.prototype.hasOwnProperty('isPrototypeOf') ||
|
||||
Object.getPrototypeOf(value.constructor.prototype) === null)) ||
|
||||
(value && Object.getPrototypeOf(value) === null)
|
||||
);
|
||||
};
|
||||
|
||||
const isObject = (o: any) => {
|
||||
return !!o && typeof o === 'object' && !Array.isArray(o);
|
||||
};
|
||||
|
||||
export const Options = (defaults: any, ...options: any[]): any => {
|
||||
if (!options.length) {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
const source = options.shift();
|
||||
|
||||
if (isObject(defaults) && isPlainObject(source)) {
|
||||
Object.entries(source).forEach(([key, value]) => {
|
||||
if (typeof value === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) {
|
||||
if (!(key in defaults)) {
|
||||
Object.assign(defaults, { [key]: {} });
|
||||
}
|
||||
|
||||
Options(defaults[key], value);
|
||||
} else {
|
||||
Object.assign(defaults, { [key]: value });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Options(defaults, ...options);
|
||||
};
|
7
packages/cache/tsconfig.json
vendored
7
packages/cache/tsconfig.json
vendored
@ -1,7 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
12
packages/cache/tsup.config.ts
vendored
12
packages/cache/tsup.config.ts
vendored
@ -1,12 +0,0 @@
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
export default defineConfig({
|
||||
clean: true,
|
||||
dts: true,
|
||||
entry: ['src/index.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
minify: isProduction,
|
||||
sourcemap: false,
|
||||
});
|
20
packages/common/README.MD
Normal file
20
packages/common/README.MD
Normal file
@ -0,0 +1,20 @@
|
||||
# @biscuitland/common
|
||||
## Most importantly, biscuit's common is:
|
||||
Custom types, functions and utility classes, a feature-rich package for sharing in the biscuit libraries
|
||||
|
||||
[<img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white">](https://github.com/oasisjs/biscuit)
|
||||
[<img src="https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white">](https://discord.gg/XNw2RZFzaP)
|
||||
|
||||
<img align="right" src="https://raw.githubusercontent.com/oasisjs/biscuit/main/assets/icon.svg" alt="biscuit"/>
|
||||
|
||||
## Install (for [node18](https://nodejs.org/en/download/))
|
||||
|
||||
```sh-session
|
||||
npm install @biscuitland/common
|
||||
yarn add @biscuitland/common
|
||||
```
|
||||
## Links
|
||||
* [Website](https://biscuitjs.com/)
|
||||
* [Documentation](https://docs.biscuitjs.com/)
|
||||
* [Discord](https://discord.gg/XNw2RZFzaP)
|
||||
* [core](https://www.npmjs.com/package/@biscuitland/core) | [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers)
|
75
packages/common/package.json
Normal file
75
packages/common/package.json
Normal file
@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@biscuitland/common",
|
||||
"version": "0.0.1",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts",
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"clean": "rm -rf dist && rm -rf .turbo",
|
||||
"dev": "tsup --watch"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsup": "^6.1.3"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"author": "Yuzuru <yuzuru@programmer.net>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Yuzuru",
|
||||
"url": "https://github.com/yuzudev"
|
||||
},
|
||||
{
|
||||
"name": "miia",
|
||||
"url": "https://github.com/dragurimu"
|
||||
},
|
||||
{
|
||||
"name": "n128",
|
||||
"url": "https://github.com/nicolito128"
|
||||
},
|
||||
{
|
||||
"name": "socram03",
|
||||
"url": "https://github.com/socram03",
|
||||
"author": true
|
||||
},
|
||||
{
|
||||
"name": "Drylozu",
|
||||
"url": "https://github.com/Drylozu"
|
||||
}
|
||||
],
|
||||
"homepage": "https://biscuitjs.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/oasisjs/biscuit.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/oasisjs/biscuit"
|
||||
},
|
||||
"keywords": [
|
||||
"api",
|
||||
"discord",
|
||||
"bots",
|
||||
"typescript",
|
||||
"botdev"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"discord-api-types": "^0.37.39"
|
||||
}
|
||||
}
|
163
packages/common/src/Collection.ts
Normal file
163
packages/common/src/Collection.ts
Normal file
@ -0,0 +1,163 @@
|
||||
// https://github.com/discordeno/discordeno/blob/main/packages/utils/src/Collection.ts
|
||||
export class Collection<K, V> extends Map<K, V> {
|
||||
/**
|
||||
* The maximum amount of items allowed in this collection. To disable cache, set it 0, set to undefined to make it infinite.
|
||||
* @default undefined
|
||||
*/
|
||||
maxSize: number | undefined;
|
||||
/** Handler to remove items from the collection every so often. */
|
||||
sweeper: (CollectionSweeper<K, V> & { intervalId?: NodeJS.Timer }) | undefined;
|
||||
|
||||
constructor(entries?: (ReadonlyArray<readonly [K, V]> | null) | Map<K, V>, options?: CollectionOptions<K, V>) {
|
||||
super(entries ?? []);
|
||||
|
||||
this.maxSize = options?.maxSize;
|
||||
|
||||
if (!options?.sweeper) return;
|
||||
|
||||
this.startSweeper(options.sweeper);
|
||||
}
|
||||
|
||||
startSweeper(options: CollectionSweeper<K, V>): NodeJS.Timer {
|
||||
if (this.sweeper?.intervalId) clearInterval(this.sweeper.intervalId);
|
||||
|
||||
this.sweeper = options;
|
||||
this.sweeper.intervalId = setInterval(() => {
|
||||
this.forEach((value, key) => {
|
||||
if (!this.sweeper?.filter(value, key)) return;
|
||||
|
||||
this.delete(key);
|
||||
return key;
|
||||
});
|
||||
}, options.interval);
|
||||
|
||||
return this.sweeper.intervalId;
|
||||
}
|
||||
|
||||
stopSweeper(): void {
|
||||
clearInterval(this.sweeper?.intervalId);
|
||||
}
|
||||
|
||||
changeSweeperInterval(newInterval: number): void {
|
||||
if (this.sweeper == null) return;
|
||||
|
||||
this.startSweeper({ filter: this.sweeper.filter, interval: newInterval });
|
||||
}
|
||||
|
||||
changeSweeperFilter(newFilter: (value: V, key: K) => boolean): void {
|
||||
if (this.sweeper == null) return;
|
||||
|
||||
this.startSweeper({ filter: newFilter, interval: this.sweeper.interval });
|
||||
}
|
||||
|
||||
/** Add an item to the collection. Makes sure not to go above the maxSize. */
|
||||
set(key: K, value: V): this {
|
||||
// When this collection is maxSized make sure we can add first
|
||||
if ((this.maxSize !== undefined || this.maxSize === 0) && this.size >= this.maxSize) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
/** Add an item to the collection, no matter what the maxSize is. */
|
||||
forceSet(key: K, value: V): this {
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
/** Convert the collection to an array. */
|
||||
array(): V[] {
|
||||
return [...this.values()];
|
||||
}
|
||||
|
||||
/** Retrieve the value of the first element in this collection. */
|
||||
first(): V | undefined {
|
||||
return this.values().next().value;
|
||||
}
|
||||
|
||||
/** Retrieve the value of the last element in this collection. */
|
||||
last(): V | undefined {
|
||||
return [...this.values()][this.size - 1];
|
||||
}
|
||||
|
||||
/** Retrieve the value of a random element in this collection. */
|
||||
random(): V | undefined {
|
||||
const array = [...this.values()];
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
/** Find a specific element in this collection. */
|
||||
find(callback: (value: V, key: K) => boolean): NonNullable<V> | undefined {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Find all elements in this collection that match the given pattern. */
|
||||
filter(callback: (value: V, key: K) => boolean): Collection<K, V> {
|
||||
const relevant = new Collection<K, V>();
|
||||
this.forEach((value, key) => {
|
||||
if (callback(value, key)) relevant.set(key, value);
|
||||
});
|
||||
|
||||
return relevant;
|
||||
}
|
||||
|
||||
/** Converts the collection into an array by running a callback on all items in the collection. */
|
||||
map<T>(callback: (value: V, key: K) => T): T[] {
|
||||
const results = [];
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
results.push(callback(value, key));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Check if one of the items in the collection matches the pattern. */
|
||||
some(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if all of the items in the collection matches the pattern. */
|
||||
every(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
if (!callback(value, key)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Runs a callback on all items in the collection, merging them into a single value. */
|
||||
reduce<T>(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T {
|
||||
let accumulator: T = initialValue!;
|
||||
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!;
|
||||
accumulator = callback(accumulator, value, key);
|
||||
}
|
||||
|
||||
return accumulator;
|
||||
}
|
||||
}
|
||||
|
||||
export interface CollectionOptions<K, V> {
|
||||
/** Handler to clean out the items in the collection every so often. */
|
||||
sweeper?: CollectionSweeper<K, V>;
|
||||
/** The maximum number of items allowed in the collection. */
|
||||
maxSize?: number;
|
||||
}
|
||||
|
||||
export interface CollectionSweeper<K, V> {
|
||||
/** The filter to determine whether an element should be deleted or not */
|
||||
filter: (value: V, key: K, ...args: any[]) => boolean;
|
||||
/** The interval in which the sweeper should run */
|
||||
interval: number;
|
||||
}
|
20
packages/common/src/Constants.ts
Normal file
20
packages/common/src/Constants.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export const DiscordEpoch = 14200704e5;
|
||||
|
||||
export const API_VERSION = '10';
|
||||
|
||||
export const BASE_URL = `/api/v${API_VERSION}`;
|
||||
export const BASE_HOST = 'https://discord.com';
|
||||
|
||||
export const CDN_URL = 'https://cdn.discordapp.com';
|
||||
|
||||
export const GATEWAY_BASE_URL = 'wss://gateway.discord.gg/?v=10&encoding=json';
|
||||
|
||||
export const OK_STATUS_CODES = [200, 201, 204, 304];
|
||||
|
||||
export enum HTTPMethods {
|
||||
Delete = 'DELETE',
|
||||
Get = 'GET',
|
||||
Patch = 'PATCH',
|
||||
Post = 'POST',
|
||||
Put = 'PUT'
|
||||
}
|
128
packages/common/src/Logger.ts
Normal file
128
packages/common/src/Logger.ts
Normal file
@ -0,0 +1,128 @@
|
||||
import { Options, bgBrightWhite, black, bold, cyan, gray, italic, red, yellow } from './Util';
|
||||
|
||||
export enum LogLevels {
|
||||
Debug = 0,
|
||||
Info = 1,
|
||||
Warn = 2,
|
||||
Error = 3,
|
||||
Fatal = 4
|
||||
}
|
||||
|
||||
export enum LogDepth {
|
||||
Minimal = 0,
|
||||
Full = 1
|
||||
}
|
||||
|
||||
export type LoggerOptions = {
|
||||
logLevel?: LogLevels;
|
||||
name?: string;
|
||||
active?: boolean;
|
||||
};
|
||||
|
||||
export class Logger {
|
||||
readonly options: Required<LoggerOptions>;
|
||||
|
||||
constructor(options: LoggerOptions) {
|
||||
this.options = Options(Logger.DEFAULT_OPTIONS, options);
|
||||
}
|
||||
|
||||
set level(level: LogLevels) {
|
||||
this.options.logLevel = level;
|
||||
}
|
||||
|
||||
get level(): LogLevels {
|
||||
return this.options.logLevel;
|
||||
}
|
||||
|
||||
set active(active: boolean) {
|
||||
this.options.active = active;
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this.options.active;
|
||||
}
|
||||
|
||||
set name(name: string) {
|
||||
this.options.name = name;
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return this.options.name;
|
||||
}
|
||||
|
||||
rawLog(level: LogLevels, ...args: unknown[]) {
|
||||
if (!this.active) return;
|
||||
if (level < this.level) return;
|
||||
|
||||
const color = Logger.colorFunctions.get(level) ?? Logger.noColor;
|
||||
|
||||
const date = new Date();
|
||||
const log = [
|
||||
bgBrightWhite(black(`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`)),
|
||||
color(Logger.prefixes.get(level) ?? 'DEBUG'),
|
||||
this.name ? `${this.name} >` : '>',
|
||||
...args
|
||||
];
|
||||
|
||||
switch (level) {
|
||||
case LogLevels.Debug:
|
||||
return console.debug(...log);
|
||||
case LogLevels.Info:
|
||||
return console.info(...log);
|
||||
case LogLevels.Warn:
|
||||
return console.warn(...log);
|
||||
case LogLevels.Error:
|
||||
return console.error(...log);
|
||||
case LogLevels.Fatal:
|
||||
return console.error(...log);
|
||||
default:
|
||||
return console.log(...log);
|
||||
}
|
||||
}
|
||||
|
||||
debug(...args: any[]) {
|
||||
this.rawLog(LogLevels.Debug, ...args);
|
||||
}
|
||||
|
||||
info(...args: any[]) {
|
||||
this.rawLog(LogLevels.Info, ...args);
|
||||
}
|
||||
|
||||
warn(...args: any[]) {
|
||||
this.rawLog(LogLevels.Warn, ...args);
|
||||
}
|
||||
|
||||
error(...args: any[]) {
|
||||
this.rawLog(LogLevels.Error, ...args);
|
||||
}
|
||||
|
||||
fatal(...args: any[]) {
|
||||
this.rawLog(LogLevels.Fatal, ...args);
|
||||
}
|
||||
|
||||
static DEFAULT_OPTIONS: Required<LoggerOptions> = {
|
||||
logLevel: LogLevels.Info,
|
||||
name: 'BISCUIT',
|
||||
active: true
|
||||
};
|
||||
|
||||
static noColor(msg: string) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
static colorFunctions = new Map<LogLevels, (str: string) => string>([
|
||||
[LogLevels.Debug, gray],
|
||||
[LogLevels.Info, cyan],
|
||||
[LogLevels.Warn, yellow],
|
||||
[LogLevels.Error, (str: string) => red(str)],
|
||||
[LogLevels.Fatal, (str: string) => red(bold(italic(str)))]
|
||||
]);
|
||||
|
||||
static prefixes = new Map<LogLevels, string>([
|
||||
[LogLevels.Debug, 'DEBUG'],
|
||||
[LogLevels.Info, 'INFO'],
|
||||
[LogLevels.Warn, 'WARN'],
|
||||
[LogLevels.Error, 'ERROR'],
|
||||
[LogLevels.Fatal, 'FATAL']
|
||||
]);
|
||||
}
|
39
packages/common/src/Types.ts
Normal file
39
packages/common/src/Types.ts
Normal file
@ -0,0 +1,39 @@
|
||||
export type Tail<A> = A extends [unknown, ...infer rest] ? rest : A extends [unknown] ? [] : A extends (infer first)[] ? first[] : never;
|
||||
|
||||
export type ValueOf<T> = T[keyof T];
|
||||
|
||||
export type ArrayFirsElement<A> = A extends [...infer arr] ? arr[0] : never;
|
||||
|
||||
export type RestToKeys<T extends unknown[]> = T extends [infer V, ...infer Keys] ? { [K in Extract<Keys[number], string>]: V } : never;
|
||||
|
||||
export type Identify<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;
|
||||
|
||||
export type TypeArray<T> = T | T[];
|
||||
|
||||
export type When<T extends boolean, A, B = never> = T extends true ? A : B;
|
||||
|
||||
export type PickPartial<T, K extends keyof T> = {
|
||||
[P in keyof T]?: T[P] | undefined;
|
||||
} & {
|
||||
[P in K]: T[P];
|
||||
};
|
||||
|
||||
export type MakeRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
||||
|
||||
export type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
|
||||
? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
|
||||
: Lowercase<S>;
|
||||
|
||||
export type SnakeCase<S extends string> = S extends `${infer A}${infer Rest}`
|
||||
? A extends Uppercase<A>
|
||||
? `_${Lowercase<A>}${SnakeCase<Rest>}`
|
||||
: `${A}${SnakeCase<Rest>}`
|
||||
: Lowercase<S>;
|
||||
|
||||
export type ObjectToLower<T> = Identify<{
|
||||
[K in keyof T as CamelCase<Exclude<K, symbol | number>>]: T[K] extends object ? Identify<ObjectToLower<T[K]>> : T[K];
|
||||
}>;
|
||||
|
||||
export type ObjectToSnake<T> = Identify<{
|
||||
[K in keyof T as SnakeCase<Exclude<K, symbol | number>>]: T[K] extends object ? Identify<ObjectToSnake<T[K]>> : T[K];
|
||||
}>;
|
596
packages/common/src/Util.ts
Normal file
596
packages/common/src/Util.ts
Normal file
@ -0,0 +1,596 @@
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { ObjectToSnake, ObjectToLower } from './Types';
|
||||
|
||||
const isPlainObject = (value: any) => {
|
||||
return (
|
||||
(value !== null &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.constructor === 'function' &&
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
(value.constructor.prototype.hasOwnProperty('isPrototypeOf') || Object.getPrototypeOf(value.constructor.prototype) === null)) ||
|
||||
(value && Object.getPrototypeOf(value) === null)
|
||||
);
|
||||
};
|
||||
|
||||
const isObject = (o: any) => {
|
||||
return !!o && typeof o === 'object' && !Array.isArray(o);
|
||||
};
|
||||
|
||||
export const Options = <T = any>(defaults: any, ...options: any[]): T => {
|
||||
if (!options.length) {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
const source = options.shift();
|
||||
|
||||
if (isObject(defaults) && isPlainObject(source)) {
|
||||
Object.entries(source).forEach(([key, value]) => {
|
||||
if (typeof value === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPlainObject(value)) {
|
||||
if (!(key in defaults)) {
|
||||
Object.assign(defaults, { [key]: {} });
|
||||
}
|
||||
|
||||
Options(defaults[key], value);
|
||||
} else {
|
||||
Object.assign(defaults, { [key]: value });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Options(defaults, ...options);
|
||||
};
|
||||
/**
|
||||
* Convert a camelCase object to snake_case.
|
||||
* @param target The object to convert.
|
||||
* @returns The converted object.
|
||||
*/
|
||||
export function toSnakeCase<Obj extends { [k: string]: unknown }>(target: Obj): ObjectToSnake<Obj> {
|
||||
const result = {};
|
||||
for (const [key, value] of Object.entries(target)) {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
case 'bigint':
|
||||
case 'boolean':
|
||||
case 'function':
|
||||
case 'symbol':
|
||||
case 'undefined':
|
||||
result[ReplaceRegex.camel(key)] = value;
|
||||
break;
|
||||
case 'object':
|
||||
if (Array.isArray(value)) {
|
||||
result[ReplaceRegex.camel(key)] = Promise.all(value.map((prop) => toSnakeCase(prop)));
|
||||
break;
|
||||
}
|
||||
if (!Number.isNaN(value)) {
|
||||
result[ReplaceRegex.camel(key)] = null;
|
||||
break;
|
||||
}
|
||||
result[ReplaceRegex.camel(key)] = toSnakeCase({ ...value });
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result as ObjectToSnake<Obj>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a snake_case object to camelCase.
|
||||
* @param target The object to convert.
|
||||
* @returns The converted object.
|
||||
*/
|
||||
export function toCamelCase<Obj extends { [k: string]: unknown }>(target: Obj): ObjectToLower<Obj> {
|
||||
const result = {};
|
||||
for (const [key, value] of Object.entries(target)) {
|
||||
switch (typeof value) {
|
||||
case 'string':
|
||||
case 'bigint':
|
||||
case 'boolean':
|
||||
case 'function':
|
||||
case 'symbol':
|
||||
case 'undefined':
|
||||
result[ReplaceRegex.snake(key)] = value;
|
||||
break;
|
||||
case 'object':
|
||||
if (Array.isArray(value)) {
|
||||
result[ReplaceRegex.snake(key)] = Promise.all(value.map((prop) => toCamelCase(prop)));
|
||||
break;
|
||||
}
|
||||
if (!Number.isNaN(value)) {
|
||||
result[ReplaceRegex.snake(key)] = null;
|
||||
break;
|
||||
}
|
||||
result[ReplaceRegex.snake(key)] = toCamelCase({ ...value });
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result as ObjectToLower<Obj>;
|
||||
}
|
||||
|
||||
export const ReplaceRegex = {
|
||||
snake: (s: string) => {
|
||||
return s.replace(/(_\S)/gi, (a) => a[1].toUpperCase());
|
||||
},
|
||||
camel: (s: string) => {
|
||||
return s.replace(/[A-Z]/g, (a) => `_${a.toLowerCase()}`);
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/discordeno/discordeno/blob/main/packages/utils/src/colors.ts
|
||||
|
||||
export interface Code {
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
}
|
||||
|
||||
/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */
|
||||
export interface Rgb {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
let enabled = true;
|
||||
|
||||
/**
|
||||
* Set changing text color to enabled or disabled
|
||||
* @param value
|
||||
*/
|
||||
export function setColorEnabled(value: boolean) {
|
||||
enabled = value;
|
||||
}
|
||||
|
||||
/** Get whether text color change is enabled or disabled. */
|
||||
export function getColorEnabled(): boolean {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds color code
|
||||
* @param open
|
||||
* @param close
|
||||
*/
|
||||
function code(open: number[], close: number): Code {
|
||||
return {
|
||||
open: `\x1b[${open.join(';')}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, 'g')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies color and background based on color code and its associated text
|
||||
* @param str text to apply color settings to
|
||||
* @param code color code to apply
|
||||
*/
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the text modified
|
||||
* @param str text to reset
|
||||
*/
|
||||
export function reset(str: string): string {
|
||||
return run(str, code([0], 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text bold.
|
||||
* @param str text to make bold
|
||||
*/
|
||||
export function bold(str: string): string {
|
||||
return run(str, code([1], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
* The text emits only a small amount of light.
|
||||
* @param str text to dim
|
||||
*/
|
||||
export function dim(str: string): string {
|
||||
return run(str, code([2], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text italic.
|
||||
* @param str text to make italic
|
||||
*/
|
||||
export function italic(str: string): string {
|
||||
return run(str, code([3], 23));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text underline.
|
||||
* @param str text to underline
|
||||
*/
|
||||
export function underline(str: string): string {
|
||||
return run(str, code([4], 24));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert background color and text color.
|
||||
* @param str text to invert its color
|
||||
*/
|
||||
export function inverse(str: string): string {
|
||||
return run(str, code([7], 27));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the text hidden.
|
||||
* @param str text to hide
|
||||
*/
|
||||
export function hidden(str: string): string {
|
||||
return run(str, code([8], 28));
|
||||
}
|
||||
|
||||
/**
|
||||
* Put horizontal line through the center of the text.
|
||||
* @param str text to strike through
|
||||
*/
|
||||
export function strikethrough(str: string): string {
|
||||
return run(str, code([9], 29));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to black.
|
||||
* @param str text to make black
|
||||
*/
|
||||
export function black(str: string): string {
|
||||
return run(str, code([30], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to red.
|
||||
* @param str text to make red
|
||||
*/
|
||||
export function red(str: string): string {
|
||||
return run(str, code([31], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to green.
|
||||
* @param str text to make green
|
||||
*/
|
||||
export function green(str: string): string {
|
||||
return run(str, code([32], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to yellow.
|
||||
* @param str text to make yellow
|
||||
*/
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code([33], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to blue.
|
||||
* @param str text to make blue
|
||||
*/
|
||||
export function blue(str: string): string {
|
||||
return run(str, code([34], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to magenta.
|
||||
* @param str text to make magenta
|
||||
*/
|
||||
export function magenta(str: string): string {
|
||||
return run(str, code([35], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to cyan.
|
||||
* @param str text to make cyan
|
||||
*/
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code([36], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to white.
|
||||
* @param str text to make white
|
||||
*/
|
||||
export function white(str: string): string {
|
||||
return run(str, code([37], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to gray.
|
||||
* @param str text to make gray
|
||||
*/
|
||||
export function gray(str: string): string {
|
||||
return brightBlack(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright black.
|
||||
* @param str text to make bright-black
|
||||
*/
|
||||
export function brightBlack(str: string): string {
|
||||
return run(str, code([90], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright red.
|
||||
* @param str text to make bright-red
|
||||
*/
|
||||
export function brightRed(str: string): string {
|
||||
return run(str, code([91], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright green.
|
||||
* @param str text to make bright-green
|
||||
*/
|
||||
export function brightGreen(str: string): string {
|
||||
return run(str, code([92], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright yellow.
|
||||
* @param str text to make bright-yellow
|
||||
*/
|
||||
export function brightYellow(str: string): string {
|
||||
return run(str, code([93], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright blue.
|
||||
* @param str text to make bright-blue
|
||||
*/
|
||||
export function brightBlue(str: string): string {
|
||||
return run(str, code([94], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright magenta.
|
||||
* @param str text to make bright-magenta
|
||||
*/
|
||||
export function brightMagenta(str: string): string {
|
||||
return run(str, code([95], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright cyan.
|
||||
* @param str text to make bright-cyan
|
||||
*/
|
||||
export function brightCyan(str: string): string {
|
||||
return run(str, code([96], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color to bright white.
|
||||
* @param str text to make bright-white
|
||||
*/
|
||||
export function brightWhite(str: string): string {
|
||||
return run(str, code([97], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to black.
|
||||
* @param str text to make its background black
|
||||
*/
|
||||
export function bgBlack(str: string): string {
|
||||
return run(str, code([40], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to red.
|
||||
* @param str text to make its background red
|
||||
*/
|
||||
export function bgRed(str: string): string {
|
||||
return run(str, code([41], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to green.
|
||||
* @param str text to make its background green
|
||||
*/
|
||||
export function bgGreen(str: string): string {
|
||||
return run(str, code([42], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to yellow.
|
||||
* @param str text to make its background yellow
|
||||
*/
|
||||
export function bgYellow(str: string): string {
|
||||
return run(str, code([43], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to blue.
|
||||
* @param str text to make its background blue
|
||||
*/
|
||||
export function bgBlue(str: string): string {
|
||||
return run(str, code([44], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to magenta.
|
||||
* @param str text to make its background magenta
|
||||
*/
|
||||
export function bgMagenta(str: string): string {
|
||||
return run(str, code([45], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to cyan.
|
||||
* @param str text to make its background cyan
|
||||
*/
|
||||
export function bgCyan(str: string): string {
|
||||
return run(str, code([46], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to white.
|
||||
* @param str text to make its background white
|
||||
*/
|
||||
export function bgWhite(str: string): string {
|
||||
return run(str, code([47], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright black.
|
||||
* @param str text to make its background bright-black
|
||||
*/
|
||||
export function bgBrightBlack(str: string): string {
|
||||
return run(str, code([100], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright red.
|
||||
* @param str text to make its background bright-red
|
||||
*/
|
||||
export function bgBrightRed(str: string): string {
|
||||
return run(str, code([101], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright green.
|
||||
* @param str text to make its background bright-green
|
||||
*/
|
||||
export function bgBrightGreen(str: string): string {
|
||||
return run(str, code([102], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright yellow.
|
||||
* @param str text to make its background bright-yellow
|
||||
*/
|
||||
export function bgBrightYellow(str: string): string {
|
||||
return run(str, code([103], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright blue.
|
||||
* @param str text to make its background bright-blue
|
||||
*/
|
||||
export function bgBrightBlue(str: string): string {
|
||||
return run(str, code([104], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright magenta.
|
||||
* @param str text to make its background bright-magenta
|
||||
*/
|
||||
export function bgBrightMagenta(str: string): string {
|
||||
return run(str, code([105], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright cyan.
|
||||
* @param str text to make its background bright-cyan
|
||||
*/
|
||||
export function bgBrightCyan(str: string): string {
|
||||
return run(str, code([106], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color to bright white.
|
||||
* @param str text to make its background bright-white
|
||||
*/
|
||||
export function bgBrightWhite(str: string): string {
|
||||
return run(str, code([107], 49));
|
||||
}
|
||||
|
||||
/* Special Color Sequences */
|
||||
|
||||
/**
|
||||
* Clam and truncate color codes
|
||||
* @param n
|
||||
* @param max number to truncate to
|
||||
* @param min number to truncate from
|
||||
*/
|
||||
function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
return Math.trunc(Math.max(Math.min(n, max), min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color using paletted 8bit colors.
|
||||
* https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
|
||||
* @param str text color to apply paletted 8bit colors to
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb8(str: string, color: number): string {
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color using paletted 8bit colors.
|
||||
* https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
|
||||
* @param str text color to apply paletted 8bit background colors to
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb8(str: string, color: number): string {
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color using 24bit rgb.
|
||||
* `color` can be a number in range `0x000000` to `0xffffff` or
|
||||
* an `Rgb`.
|
||||
*
|
||||
* To produce the color magenta:
|
||||
*
|
||||
* ```ts
|
||||
* import { rgb24 } from "./colors.ts";
|
||||
* rgb24("foo", 0xff00ff);
|
||||
* rgb24("foo", {r: 255, g: 0, b: 255});
|
||||
* ```
|
||||
* @param str text color to apply 24bit rgb to
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 39));
|
||||
}
|
||||
return run(str, code([38, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set background color using 24bit rgb.
|
||||
* `color` can be a number in range `0x000000` to `0xffffff` or
|
||||
* an `Rgb`.
|
||||
*
|
||||
* To produce the color magenta:
|
||||
*
|
||||
* ```ts
|
||||
* import { bgRgb24 } from "./colors.ts";
|
||||
* bgRgb24("foo", 0xff00ff);
|
||||
* bgRgb24("foo", {r: 255, g: 0, b: 255});
|
||||
* ```
|
||||
* @param str text color to apply 24bit rgb to
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 49));
|
||||
}
|
||||
return run(str, code([48, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 49));
|
||||
}
|
||||
|
||||
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
||||
const ANSI_PATTERN = new RegExp(
|
||||
[
|
||||
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
|
||||
].join('|'),
|
||||
'g'
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove ANSI escape codes from the string.
|
||||
* @param string to remove ANSI escape codes from
|
||||
*/
|
||||
export function stripColor(string: string): string {
|
||||
return string.replace(ANSI_PATTERN, '');
|
||||
}
|
||||
|
||||
export function delay<T>(time: number, result?: T) {
|
||||
return setTimeout(time, result);
|
||||
}
|
29
packages/common/src/applyToClass.ts
Normal file
29
packages/common/src/applyToClass.ts
Normal file
@ -0,0 +1,29 @@
|
||||
export function applyToClass<
|
||||
T extends new (
|
||||
..._args: ConstructorParameters<T>
|
||||
) => InstanceType<T>,
|
||||
U extends new (
|
||||
..._args: ConstructorParameters<U>
|
||||
) => InstanceType<U>
|
||||
// @ts-expect-error
|
||||
>(structToApply: T, struct: U, ignore?: (keyof T['prototype'])[]) {
|
||||
const props = Object.getOwnPropertyNames(structToApply.prototype);
|
||||
for (const prop of props) {
|
||||
if (ignore?.includes(prop as keyof T) || prop === 'constructor') continue;
|
||||
Object.defineProperty(struct.prototype, prop, Object.getOwnPropertyDescriptor(structToApply.prototype, prop)!);
|
||||
}
|
||||
return struct as unknown as Struct<T, U>;
|
||||
}
|
||||
|
||||
// rome-ignore lint/nursery/noBannedTypes: fix applyToClass typing
|
||||
export type Struct<ToMix = {}, Final = {}> = Final extends new (
|
||||
..._args: never[]
|
||||
) => infer F
|
||||
? ToMix extends new (
|
||||
..._args: never[]
|
||||
) => infer TM
|
||||
? new (
|
||||
..._args: ConstructorParameters<Final>
|
||||
) => F & TM
|
||||
: never
|
||||
: never;
|
7
packages/common/src/index.ts
Normal file
7
packages/common/src/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './Constants';
|
||||
export * from './Util';
|
||||
export * from './Types';
|
||||
export * from 'discord-api-types/v10';
|
||||
export * from './applyToClass';
|
||||
export * from './Collection';
|
||||
export * from './Logger';
|
@ -8,5 +8,5 @@ export default defineConfig({
|
||||
entry: ['src/index.ts'],
|
||||
format: ['cjs', 'esm'],
|
||||
minify: isProduction,
|
||||
sourcemap: false,
|
||||
sourcemap: false
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
# @biscuitland/core
|
||||
Classes, functions and main structures to create an application with biscuit. Core contains the essentials to launch you to develop your own customized and scalable bot.
|
||||
Core contains the essentials to launch you to develop your own customized and scalable bot.
|
||||
|
||||
[<img src="https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white">](https://github.com/oasisjs/biscuit)
|
||||
[<img src="https://img.shields.io/badge/Discord-5865F2?style=for-the-badge&logo=discord&logoColor=white">](https://discord.gg/XNw2RZFzaP)
|
||||
@ -17,29 +17,12 @@ yarn add @biscuitland/core
|
||||
`project/index.js`:
|
||||
```js
|
||||
import { Session } from '@biscuitland/core';
|
||||
import { GatewayIntents } from '@biscuitland/api-types';
|
||||
import { GatewayIntentBits } from "discord-api-types/v10";
|
||||
|
||||
const session = new Session({ token: 'your token', intents: GatewayIntents.Guilds });
|
||||
const session = new Session({ token: 'your token', intents: GatewayIntentBits.Guilds });
|
||||
|
||||
const commands = [
|
||||
{
|
||||
name: 'ping',
|
||||
description: 'Replies with pong!'
|
||||
}
|
||||
];
|
||||
|
||||
session.events.on('ready', ({ user }) => {
|
||||
console.log('Logged in as:', user.tag);
|
||||
session.upsertApplicationCommands(commands, 'GUILD_ID');
|
||||
});
|
||||
|
||||
session.events.on('interactionCreate', (interaction) => {
|
||||
if (interaction.isCommand()) {
|
||||
// your commands go here
|
||||
if (interaction.commandName === 'ping') {
|
||||
interaction.respondWith({ content: 'pong!' });
|
||||
}
|
||||
}
|
||||
session.events.on('READY', (payload) => {
|
||||
console.log('Logged in as:', payload.user.username);
|
||||
});
|
||||
|
||||
session.start();
|
||||
@ -60,4 +43,4 @@ B:\project> node --experimental-fetch index.js
|
||||
* [Website](https://biscuitjs.com/)
|
||||
* [Documentation](https://docs.biscuitjs.com/)
|
||||
* [Discord](https://discord.gg/XNw2RZFzaP)
|
||||
* [api-types](https://www.npmjs.com/package/@biscuitland/api-types) | [cache](https://www.npmjs.com/package/@biscuitland/cache) | [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers)
|
||||
* [rest](https://www.npmjs.com/package/@biscuitland/rest) | [ws](https://www.npmjs.com/package/@biscuitland/ws) | [helpers](https://www.npmjs.com/package/@biscuitland/helpers)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@biscuitland/core",
|
||||
"version": "2.3.0",
|
||||
"version": "3.0.0",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.ts",
|
||||
@ -23,9 +23,10 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@biscuitland/api-types": "^2.3.0",
|
||||
"@biscuitland/rest": "^2.3.0",
|
||||
"@biscuitland/ws": "^2.3.0"
|
||||
"@biscuitland/common": "^0.0.1",
|
||||
"@biscuitland/rest": "^3.0.0",
|
||||
"@biscuitland/ws": "^3.0.0",
|
||||
"eventemitter2": "^6.4.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.14",
|
||||
@ -36,8 +37,7 @@
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Yuzuru",
|
||||
"url": "https://github.com/yuzudev",
|
||||
"author": true
|
||||
"url": "https://github.com/yuzudev"
|
||||
},
|
||||
{
|
||||
"name": "miia",
|
||||
@ -49,7 +49,8 @@
|
||||
},
|
||||
{
|
||||
"name": "socram03",
|
||||
"url": "https://github.com/socram03"
|
||||
"url": "https://github.com/socram03",
|
||||
"author": true
|
||||
},
|
||||
{
|
||||
"name": "Drylozu",
|
||||
|
@ -1,25 +0,0 @@
|
||||
import type { EventAdapter } from './event-adapter';
|
||||
import type { Events } from './events';
|
||||
import EventEmitter from 'node:events';
|
||||
|
||||
export class DefaultEventAdapter extends EventEmitter implements EventAdapter {
|
||||
override on<K extends keyof Events>(event: K, func: Events[K]): this;
|
||||
override on<K extends string>(event: K, func: (...args: unknown[]) => unknown): this {
|
||||
return super.on(event, func);
|
||||
}
|
||||
|
||||
override off<K extends keyof Events>(event: K, func: Events[K]): this;
|
||||
override off<K extends keyof Events>(event: K, func: (...args: unknown[]) => unknown): this {
|
||||
return super.off(event, func);
|
||||
}
|
||||
|
||||
override once<K extends keyof Events>(event: K, func: Events[K]): this;
|
||||
override once<K extends string>(event: K, func: (...args: unknown[]) => unknown): this {
|
||||
return super.once(event, func);
|
||||
}
|
||||
|
||||
override emit<K extends keyof Events>(event: K, ...params: Parameters<Events[K]>): boolean;
|
||||
override emit<K extends string>(event: K, ...params: unknown[]): boolean {
|
||||
return super.emit(event, ...params);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import type { Events } from './events';
|
||||
|
||||
export interface EventAdapter extends Omit<NodeJS.EventEmitter, 'emit' | 'on' | 'off' | 'once'> {
|
||||
options?: any;
|
||||
|
||||
emit<K extends keyof Events>(
|
||||
event: K,
|
||||
...params: Parameters<Events[K]>
|
||||
): boolean;
|
||||
|
||||
on<K extends keyof Events>(
|
||||
event: K,
|
||||
func: Events[K]
|
||||
): unknown;
|
||||
|
||||
off<K extends keyof Events>(
|
||||
event: K,
|
||||
func: Events[K]
|
||||
): unknown;
|
||||
|
||||
once<K extends keyof Events>(
|
||||
event: K,
|
||||
func: Events[K]
|
||||
): unknown;
|
||||
}
|
@ -1,837 +0,0 @@
|
||||
import type {
|
||||
DiscordAutoModerationActionExecution,
|
||||
DiscordAutoModerationRule,
|
||||
DiscordChannel,
|
||||
DiscordChannelPinsUpdate,
|
||||
DiscordEmoji,
|
||||
DiscordGuild,
|
||||
DiscordGuildBanAddRemove,
|
||||
DiscordGuildEmojisUpdate,
|
||||
DiscordGuildMemberAdd,
|
||||
DiscordGuildMemberRemove,
|
||||
DiscordGuildMemberUpdate,
|
||||
DiscordGuildMembersChunk,
|
||||
DiscordGuildRoleCreate,
|
||||
DiscordGuildRoleDelete,
|
||||
DiscordGuildRoleUpdate,
|
||||
DiscordIntegration,
|
||||
DiscordIntegrationDelete,
|
||||
DiscordInteraction,
|
||||
DiscordInviteCreate,
|
||||
DiscordInviteDelete,
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessage,
|
||||
DiscordMessageDelete,
|
||||
DiscordMessageReactionAdd,
|
||||
DiscordMessageReactionRemove,
|
||||
DiscordMessageReactionRemoveAll,
|
||||
DiscordMessageReactionRemoveEmoji,
|
||||
DiscordPresenceUpdate,
|
||||
DiscordReady,
|
||||
DiscordRole,
|
||||
DiscordScheduledEvent,
|
||||
DiscordScheduledEventUserAdd,
|
||||
DiscordScheduledEventUserRemove,
|
||||
DiscordThreadListSync,
|
||||
DiscordThreadMembersUpdate,
|
||||
DiscordThreadMemberUpdate,
|
||||
DiscordTypingStart,
|
||||
DiscordUser,
|
||||
DiscordWebhookUpdate,
|
||||
DiscordVoiceState,
|
||||
DiscordVoiceServerUpdate,
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Interaction } from '../structures/interactions';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
|
||||
import {
|
||||
AutoModerationRule,
|
||||
AutoModerationExecution,
|
||||
} from '../structures/automod';
|
||||
|
||||
import type { Channel } from '../structures/channels';
|
||||
import {
|
||||
ChannelFactory,
|
||||
GuildChannel,
|
||||
ThreadChannel,
|
||||
} from '../structures/channels';
|
||||
|
||||
import type { DiscordStageInstanceB } from '../structures/stage-instance';
|
||||
import { StageInstance } from '../structures/stage-instance';
|
||||
import { ScheduledEvent } from '../structures/scheduled-events';
|
||||
import { Presence } from '../structures/presence';
|
||||
|
||||
import { Member, ThreadMember } from '../structures/members';
|
||||
|
||||
import { Message } from '../structures/message';
|
||||
import { User } from '../structures/user';
|
||||
import { Integration } from '../structures/integration';
|
||||
|
||||
import { Guild } from '../structures/guilds';
|
||||
import { InteractionFactory } from '../structures/interactions';
|
||||
import type { InviteCreate } from '../structures/invite';
|
||||
import { NewInviteCreate } from '../structures/invite';
|
||||
|
||||
import type {
|
||||
MessageReactionAdd,
|
||||
MessageReactionRemove,
|
||||
MessageReactionRemoveAll,
|
||||
MessageReactionRemoveEmoji,
|
||||
} from '../structures/message-reaction';
|
||||
|
||||
import { NewMessageReactionAdd } from '../structures/message-reaction';
|
||||
import { Util, PartialMessage } from '../utils/util';
|
||||
|
||||
export type RawHandler<T> = (...args: [Session, number, T]) => void;
|
||||
export type Handler<T extends [obj?: unknown, ddy?: unknown]> = (
|
||||
...args: T
|
||||
) => unknown;
|
||||
|
||||
export const READY: RawHandler<DiscordReady> = (session, shardId, payload) => {
|
||||
session.applicationId = payload.application.id;
|
||||
session.botId = payload.user.id;
|
||||
session.events.emit(
|
||||
'ready',
|
||||
{ ...payload, user: new User(session, payload.user) },
|
||||
shardId
|
||||
);
|
||||
};
|
||||
|
||||
export const MESSAGE_CREATE: RawHandler<DiscordMessage> = (
|
||||
session,
|
||||
_shardId,
|
||||
message
|
||||
) => {
|
||||
session.events.emit('messageCreate', new Message(session, message));
|
||||
};
|
||||
|
||||
export const MESSAGE_UPDATE: RawHandler<PartialMessage> = (
|
||||
session,
|
||||
_shardId,
|
||||
new_message
|
||||
) => {
|
||||
// message is partial
|
||||
if (Util.isPartialMessage(new_message)) {
|
||||
const message = {
|
||||
session,
|
||||
id: new_message.id,
|
||||
guildId: new_message.guild_id,
|
||||
channelId: new_message.channel_id,
|
||||
fields: new_message,
|
||||
}; //satisfies Partial<Message>;
|
||||
|
||||
// all methods of Message can run on partial messages
|
||||
// we aknowledge people that their callback could be partial but giving them all functions of Message
|
||||
Object.setPrototypeOf(message, Message.prototype);
|
||||
|
||||
session.events.emit('messageUpdate', message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Util.isFullMessage(new_message)) {
|
||||
session.events.emit('messageUpdate', {
|
||||
...new Message(session, new_message),
|
||||
fields: {} as PartialMessage
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const MESSAGE_DELETE: RawHandler<DiscordMessageDelete> = (
|
||||
session,
|
||||
_shardId,
|
||||
{ id, channel_id, guild_id }
|
||||
) => {
|
||||
session.events.emit('messageDelete', {
|
||||
id,
|
||||
channelId: channel_id,
|
||||
guildId: guild_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_CREATE: RawHandler<DiscordGuild> = (
|
||||
session,
|
||||
_shardId,
|
||||
guild
|
||||
) => {
|
||||
session.events.emit('guildCreate', new Guild(session, guild));
|
||||
};
|
||||
|
||||
export const GUILD_DELETE: RawHandler<DiscordGuild> = (
|
||||
session,
|
||||
_shardId,
|
||||
guild
|
||||
) => {
|
||||
session.events.emit('guildDelete', { id: guild.id, unavailable: true });
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_ADD: RawHandler<DiscordGuildMemberAdd> = (
|
||||
session,
|
||||
_shardId,
|
||||
member
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildMemberAdd',
|
||||
new Member(session, member, member.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_UPDATE: RawHandler<DiscordGuildMemberUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
member
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildMemberUpdate',
|
||||
new Member(session, member, member.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_REMOVE: RawHandler<DiscordGuildMemberRemove> = (
|
||||
session,
|
||||
_shardId,
|
||||
member
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildMemberRemove',
|
||||
new User(session, member.user),
|
||||
member.guild_id
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_BAN_ADD: RawHandler<DiscordGuildBanAddRemove> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildBanAdd', {
|
||||
guildId: data.guild_id,
|
||||
user: data.user,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_BAN_REMOVE: RawHandler<DiscordGuildBanAddRemove> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildBanRemove', {
|
||||
guildId: data.guild_id,
|
||||
user: data.user,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_EMOJIS_UPDATE: RawHandler<DiscordGuildEmojisUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildEmojisUpdate', {
|
||||
guildId: data.guild_id,
|
||||
emojis: data.emojis,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_ROLE_CREATE: RawHandler<DiscordGuildRoleCreate> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildRoleCreate', {
|
||||
guildId: data.guild_id,
|
||||
role: data.role,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_ROLE_UPDATE: RawHandler<DiscordGuildRoleUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildRoleUpdate', {
|
||||
guildId: data.guild_id,
|
||||
role: data.role,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_ROLE_DELETE: RawHandler<DiscordGuildRoleDelete> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('guildRoleDelete', {
|
||||
guildId: data.guild_id,
|
||||
roleId: data.role_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const TYPING_START: RawHandler<DiscordTypingStart> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('typingStart', {
|
||||
channelId: payload.channel_id,
|
||||
guildId: payload.guild_id ? payload.guild_id : undefined,
|
||||
userId: payload.user_id,
|
||||
timestamp: payload.timestamp,
|
||||
member: payload.guild_id
|
||||
? new Member(
|
||||
session,
|
||||
payload.member as DiscordMemberWithUser,
|
||||
payload.guild_id
|
||||
)
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
export const INTERACTION_CREATE: RawHandler<DiscordInteraction> = (
|
||||
session,
|
||||
_shardId,
|
||||
interaction
|
||||
) => {
|
||||
session.events.emit(
|
||||
'interactionCreate',
|
||||
InteractionFactory.from(session, interaction)
|
||||
);
|
||||
};
|
||||
|
||||
export const CHANNEL_CREATE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
session.events.emit('channelCreate', ChannelFactory.from(session, channel));
|
||||
};
|
||||
|
||||
export const CHANNEL_UPDATE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
session.events.emit('channelUpdate', ChannelFactory.from(session, channel));
|
||||
};
|
||||
|
||||
export const CHANNEL_DELETE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
if (!channel.guild_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.events.emit(
|
||||
'channelDelete',
|
||||
new GuildChannel(session, channel, channel.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const THREAD_CREATE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
if (!channel.guild_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.events.emit(
|
||||
'threadCreate',
|
||||
new ThreadChannel(session, channel, channel.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const THREAD_UPDATE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
if (!channel.guild_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.events.emit(
|
||||
'threadUpdate',
|
||||
new ThreadChannel(session, channel, channel.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const THREAD_DELETE: RawHandler<DiscordChannel> = (
|
||||
session,
|
||||
_shardId,
|
||||
channel
|
||||
) => {
|
||||
if (!channel.guild_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.events.emit(
|
||||
'threadDelete',
|
||||
new ThreadChannel(session, channel, channel.guild_id)
|
||||
);
|
||||
};
|
||||
|
||||
export const THREAD_MEMBER_UPDATE: RawHandler<DiscordThreadMemberUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('threadMemberUpdate', {
|
||||
guildId: payload.guild_id,
|
||||
id: payload.id,
|
||||
userId: payload.user_id,
|
||||
joinedAt: payload.joined_at,
|
||||
flags: payload.flags,
|
||||
});
|
||||
};
|
||||
|
||||
export const THREAD_MEMBERS_UPDATE: RawHandler<DiscordThreadMembersUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('threadMembersUpdate', {
|
||||
memberCount: payload.member_count,
|
||||
addedMembers: payload.added_members
|
||||
? payload.added_members.map(tm => new ThreadMember(session, tm))
|
||||
: undefined,
|
||||
removedMemberIds: payload.removed_member_ids
|
||||
? payload.removed_member_ids
|
||||
: undefined,
|
||||
guildId: payload.guild_id,
|
||||
id: payload.id,
|
||||
});
|
||||
};
|
||||
|
||||
export const THREAD_LIST_SYNC: RawHandler<DiscordThreadListSync> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('threadListSync', {
|
||||
guildId: payload.guild_id,
|
||||
channelIds: payload.channel_ids ?? [],
|
||||
threads: payload.threads.map(
|
||||
channel => new ThreadChannel(session, channel, payload.guild_id)
|
||||
),
|
||||
members: payload.members.map(
|
||||
member => new ThreadMember(session, member)
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
export const CHANNEL_PINS_UPDATE: RawHandler<DiscordChannelPinsUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('channelPinsUpdate', {
|
||||
guildId: payload.guild_id,
|
||||
channelId: payload.channel_id,
|
||||
lastPinTimestamp: payload.last_pin_timestamp
|
||||
? Date.parse(payload.last_pin_timestamp)
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
export const USER_UPDATE: RawHandler<DiscordUser> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('userUpdate', new User(session, payload));
|
||||
};
|
||||
|
||||
export const PRESENCE_UPDATE: RawHandler<DiscordPresenceUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('presenceUpdate', new Presence(session, payload));
|
||||
};
|
||||
|
||||
export const WEBHOOKS_UPDATE: RawHandler<DiscordWebhookUpdate> = (
|
||||
session,
|
||||
_shardId,
|
||||
webhook
|
||||
) => {
|
||||
session.events.emit('webhooksUpdate', {
|
||||
guildId: webhook.guild_id,
|
||||
channelId: webhook.channel_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const INTEGRATION_CREATE: RawHandler<
|
||||
DiscordIntegration & { guildId?: Snowflake }
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit('integrationCreate', new Integration(session, payload));
|
||||
};
|
||||
|
||||
export const INTEGRATION_UPDATE: RawHandler<
|
||||
DiscordIntegration & { guildId?: Snowflake }
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit('integrationCreate', new Integration(session, payload));
|
||||
};
|
||||
|
||||
export const INTEGRATION_DELETE: RawHandler<DiscordIntegrationDelete> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit('integrationDelete', {
|
||||
id: payload.id,
|
||||
guildId: payload.guild_id,
|
||||
applicationId: payload.application_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_CREATE: RawHandler<
|
||||
DiscordAutoModerationRule
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit(
|
||||
'autoModerationRuleCreate',
|
||||
new AutoModerationRule(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_UPDATE: RawHandler<
|
||||
DiscordAutoModerationRule
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit(
|
||||
'autoModerationRuleUpdate',
|
||||
new AutoModerationRule(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_RULE_DELETE: RawHandler<
|
||||
DiscordAutoModerationRule
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit(
|
||||
'autoModerationRuleDelete',
|
||||
new AutoModerationRule(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const AUTO_MODERATION_ACTION_EXECUTE: RawHandler<
|
||||
DiscordAutoModerationActionExecution
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit(
|
||||
'autoModerationActionExecution',
|
||||
new AutoModerationExecution(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const MESSAGE_REACTION_ADD: RawHandler<DiscordMessageReactionAdd> = (
|
||||
session,
|
||||
_shardId,
|
||||
reaction
|
||||
) => {
|
||||
session.events.emit(
|
||||
'messageReactionAdd',
|
||||
NewMessageReactionAdd(session, reaction)
|
||||
);
|
||||
};
|
||||
|
||||
export const MESSAGE_REACTION_REMOVE: RawHandler<
|
||||
DiscordMessageReactionRemove
|
||||
> = (session, _shardId, reaction) => {
|
||||
session.events.emit(
|
||||
'messageReactionRemove',
|
||||
NewMessageReactionAdd(session, reaction)
|
||||
);
|
||||
};
|
||||
|
||||
export const MESSAGE_REACTION_REMOVE_ALL: RawHandler<
|
||||
DiscordMessageReactionRemoveAll
|
||||
> = (session, _shardId, reaction) => {
|
||||
session.events.emit(
|
||||
'messageReactionRemoveAll',
|
||||
NewMessageReactionAdd(session, reaction as DiscordMessageReactionAdd)
|
||||
);
|
||||
};
|
||||
|
||||
export const MESSAGE_REACTION_REMOVE_EMOJI: RawHandler<
|
||||
DiscordMessageReactionRemoveEmoji
|
||||
> = (session, _shardId, reaction) => {
|
||||
session.events.emit(
|
||||
'messageReactionRemoveEmoji',
|
||||
NewMessageReactionAdd(session, reaction as DiscordMessageReactionAdd)
|
||||
);
|
||||
};
|
||||
|
||||
export const INVITE_CREATE: RawHandler<DiscordInviteCreate> = (
|
||||
session,
|
||||
_shardId,
|
||||
invite
|
||||
) => {
|
||||
session.events.emit('inviteCreate', NewInviteCreate(session, invite));
|
||||
};
|
||||
|
||||
export const INVITE_DELETE: RawHandler<DiscordInviteDelete> = (
|
||||
session,
|
||||
_shardId,
|
||||
data
|
||||
) => {
|
||||
session.events.emit('inviteDelete', {
|
||||
channelId: data.channel_id,
|
||||
guildId: data.guild_id,
|
||||
code: data.code,
|
||||
});
|
||||
};
|
||||
|
||||
export const STAGE_INSTANCE_CREATE: RawHandler<DiscordStageInstanceB> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'stageInstanceCreate',
|
||||
new StageInstance(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const STAGE_INSTANCE_UPDATE: RawHandler<DiscordStageInstanceB> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'stageInstanceUpdate',
|
||||
new StageInstance(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const STAGE_INSTANCE_DELETE: RawHandler<DiscordStageInstanceB> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'stageInstanceDelete',
|
||||
new StageInstance(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_CREATE: RawHandler<DiscordScheduledEvent> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildScheduledEventCreate',
|
||||
new ScheduledEvent(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_UPDATE: RawHandler<DiscordScheduledEvent> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildScheduledEventUpdate',
|
||||
new ScheduledEvent(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_DELETE: RawHandler<DiscordScheduledEvent> = (
|
||||
session,
|
||||
_shardId,
|
||||
payload
|
||||
) => {
|
||||
session.events.emit(
|
||||
'guildScheduledEventDelete',
|
||||
new ScheduledEvent(session, payload)
|
||||
);
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_USER_ADD: RawHandler<
|
||||
DiscordScheduledEventUserAdd
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit('guildScheduledEventUserAdd', {
|
||||
scheduledEventId: payload.guild_scheduled_event_id,
|
||||
userId: payload.user_id,
|
||||
guildId: payload.guild_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_SCHEDULED_EVENT_USER_REMOVE: RawHandler<
|
||||
DiscordScheduledEventUserRemove
|
||||
> = (session, _shardId, payload) => {
|
||||
session.events.emit('guildScheduledEventUserRemove', {
|
||||
scheduledEventId: payload.guild_scheduled_event_id,
|
||||
userId: payload.user_id,
|
||||
guildId: payload.guild_id,
|
||||
});
|
||||
};
|
||||
|
||||
export const VOICE_STATE_UPDATE: RawHandler<DiscordVoiceState> = (session, _shardId, payload) => {
|
||||
if (!payload.guild_id) { return; }
|
||||
session.events.emit('voiceStateUpdate', payload);
|
||||
};
|
||||
|
||||
export const VOICE_SERVER_UPDATE: RawHandler<DiscordVoiceServerUpdate> = (session, _shardId, payload) => {
|
||||
session.events.emit('voiceServerUpdate', {
|
||||
token: payload.token,
|
||||
guildId: payload.guild_id,
|
||||
endpoint: payload.endpoint ? payload.endpoint : undefined
|
||||
});
|
||||
};
|
||||
|
||||
export const GUILD_MEMBERS_CHUNK: RawHandler<DiscordGuildMembersChunk> = (session, _shardId, payload) => {
|
||||
session.events.emit('guildMembersChunk', {
|
||||
guildId: payload.guild_id,
|
||||
members: new Map(payload.members.map(m => [m.user.id, new Member(session, m, payload.guild_id)])),
|
||||
chunkIndex: payload.chunk_index,
|
||||
chunkCount: payload.chunk_count,
|
||||
notFound: payload.not_found,
|
||||
presences: payload.presences?.map(p => new Presence(session, p)) ?? [],
|
||||
});
|
||||
};
|
||||
|
||||
export const raw: RawHandler<unknown> = (session, shardId, data) => {
|
||||
session.events.emit('raw', data as { t: string; d: unknown }, shardId);
|
||||
};
|
||||
|
||||
export interface Ready extends Omit<DiscordReady, 'user'> {
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface GuildMembersChunk {
|
||||
guildId: Snowflake;
|
||||
members: Map<Snowflake, Member>;
|
||||
chunkIndex: number;
|
||||
chunkCount: number;
|
||||
notFound?: string[];
|
||||
presences: Presence[];
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias of `keyof Events`. List of all events that can be emitted by the session.
|
||||
* Ex: ready, messageCreate, guildCreate, etc.
|
||||
* @see {@link Events}
|
||||
*/
|
||||
export type AllEvents = keyof Events;
|
||||
|
||||
export interface Events {
|
||||
ready: Handler<[Ready, number]>;
|
||||
messageCreate: Handler<[Message]>;
|
||||
messageUpdate: Handler<[Partial<Message> & { fields: PartialMessage }]>;
|
||||
messageDelete: Handler<
|
||||
[{ id: Snowflake; channelId: Snowflake; guildId?: Snowflake }]
|
||||
>;
|
||||
messageReactionAdd: Handler<[MessageReactionAdd]>;
|
||||
messageReactionRemove: Handler<[MessageReactionRemove]>;
|
||||
messageReactionRemoveAll: Handler<[MessageReactionRemoveAll]>;
|
||||
messageReactionRemoveEmoji: Handler<[MessageReactionRemoveEmoji]>;
|
||||
guildCreate: Handler<[Guild]>;
|
||||
guildDelete: Handler<[{ id: Snowflake; unavailable: boolean }]>;
|
||||
guildMemberAdd: Handler<[Member]>;
|
||||
guildMemberUpdate: Handler<[Member]>;
|
||||
guildMemberRemove: Handler<[User, Snowflake]>;
|
||||
guildMembersChunk: Handler<[GuildMembersChunk]>;
|
||||
guildBanAdd: Handler<[{ guildId: Snowflake; user: DiscordUser }]>;
|
||||
guildBanRemove: Handler<[{ guildId: Snowflake; user: DiscordUser }]>;
|
||||
guildEmojisUpdate: Handler<
|
||||
[{ guildId: Snowflake; emojis: DiscordEmoji[] }]
|
||||
>;
|
||||
guildRoleCreate: Handler<[{ guildId: Snowflake; role: DiscordRole }]>;
|
||||
guildRoleUpdate: Handler<[{ guildId: Snowflake; role: DiscordRole }]>;
|
||||
guildRoleDelete: Handler<[{ guildId: Snowflake; roleId: Snowflake }]>;
|
||||
typingStart: Handler<
|
||||
[
|
||||
{
|
||||
channelId: Snowflake;
|
||||
guildId?: Snowflake;
|
||||
userId: Snowflake;
|
||||
timestamp: number;
|
||||
member?: Member;
|
||||
}
|
||||
]
|
||||
>;
|
||||
channelCreate: Handler<[Channel]>;
|
||||
channelUpdate: Handler<[Channel]>;
|
||||
channelDelete: Handler<[GuildChannel]>;
|
||||
channelPinsUpdate: Handler<
|
||||
[
|
||||
{
|
||||
guildId?: Snowflake;
|
||||
channelId: Snowflake;
|
||||
lastPinTimestamp?: number;
|
||||
}
|
||||
]
|
||||
>;
|
||||
threadCreate: Handler<[ThreadChannel]>;
|
||||
threadUpdate: Handler<[ThreadChannel]>;
|
||||
threadDelete: Handler<[ThreadChannel]>;
|
||||
threadListSync: Handler<
|
||||
[
|
||||
{
|
||||
guildId: Snowflake;
|
||||
channelIds: Snowflake[];
|
||||
threads: ThreadChannel[];
|
||||
members: ThreadMember[];
|
||||
}
|
||||
]
|
||||
>;
|
||||
threadMemberUpdate: Handler<
|
||||
[
|
||||
{
|
||||
id: Snowflake;
|
||||
userId: Snowflake;
|
||||
guildId: Snowflake;
|
||||
joinedAt: string;
|
||||
flags: number;
|
||||
}
|
||||
]
|
||||
>;
|
||||
threadMembersUpdate: Handler<
|
||||
[
|
||||
{
|
||||
id: Snowflake;
|
||||
memberCount: number;
|
||||
addedMembers?: ThreadMember[];
|
||||
guildId: Snowflake;
|
||||
removedMemberIds?: Snowflake[];
|
||||
}
|
||||
]
|
||||
>;
|
||||
interactionCreate: Handler<[Interaction]>;
|
||||
integrationCreate: Handler<[Integration]>;
|
||||
integrationUpdate: Handler<[Integration]>;
|
||||
integrationDelete: Handler<
|
||||
[{ id: Snowflake; guildId?: Snowflake; applicationId?: Snowflake }]
|
||||
>;
|
||||
inviteCreate: Handler<[InviteCreate]>;
|
||||
inviteDelete: Handler<
|
||||
[{ channelId: string; guildId?: string; code: string }]
|
||||
>;
|
||||
autoModerationRuleCreate: Handler<[AutoModerationRule]>;
|
||||
autoModerationRuleUpdate: Handler<[AutoModerationRule]>;
|
||||
autoModerationRuleDelete: Handler<[AutoModerationRule]>;
|
||||
autoModerationActionExecution: Handler<[AutoModerationExecution]>;
|
||||
stageInstanceCreate: Handler<[StageInstance]>;
|
||||
stageInstanceUpdate: Handler<[StageInstance]>;
|
||||
stageInstanceDelete: Handler<[StageInstance]>;
|
||||
guildScheduledEventCreate: Handler<[ScheduledEvent]>;
|
||||
guildScheduledEventUpdate: Handler<[ScheduledEvent]>;
|
||||
guildScheduledEventDelete: Handler<[ScheduledEvent]>;
|
||||
guildScheduledEventUserAdd: Handler<
|
||||
[{ scheduledEventId: Snowflake; userId: Snowflake; guildId: Snowflake }]
|
||||
>;
|
||||
guildScheduledEventUserRemove: Handler<
|
||||
[{ scheduledEventId: Snowflake; userId: Snowflake; guildId: Snowflake }]
|
||||
>;
|
||||
raw: Handler<[{ t: string; d: unknown }, number]>;
|
||||
webhooksUpdate: Handler<[{ guildId: Snowflake; channelId: Snowflake }]>;
|
||||
userUpdate: Handler<[User]>;
|
||||
presenceUpdate: Handler<[Presence]>;
|
||||
debug: Handler<[string]>;
|
||||
voiceStateUpdate: Handler<[DiscordVoiceState]>;
|
||||
voiceServerUpdate: Handler<
|
||||
[{ token: string; guildId: Snowflake; endpoint?: string }]
|
||||
>;
|
||||
}
|
@ -1,496 +0,0 @@
|
||||
import type {
|
||||
AtLeastOne,
|
||||
ApplicationCommandPermissionTypes,
|
||||
DiscordApplicationCommand,
|
||||
// DiscordGatewayPayload,
|
||||
DiscordGuildApplicationCommandPermissions,
|
||||
DiscordUser,
|
||||
DiscordApplicationCommandOption,
|
||||
GatewayIntents,
|
||||
Localization,
|
||||
Snowflake,
|
||||
DiscordGetGatewayBot,
|
||||
DiscordGatewayPayload
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
import { ApplicationCommandTypes, GatewayOpcodes ,
|
||||
|
||||
APPLICATION_COMMANDS,
|
||||
GUILD_APPLICATION_COMMANDS,
|
||||
GUILD_APPLICATION_COMMANDS_PERMISSIONS,
|
||||
GUILD_APPLICATION_COMMANDS_LOCALIZATIONS,
|
||||
USER
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
// routes
|
||||
|
||||
import type { PermissionResolvable } from './structures/special/permissions';
|
||||
import type { Activities, StatusTypes } from './structures/presence';
|
||||
|
||||
// structs
|
||||
|
||||
import { User } from './structures/user';
|
||||
|
||||
// DiscordGetGatewayBot;
|
||||
|
||||
import type { RestAdapter } from '@biscuitland/rest';
|
||||
import { DefaultRestAdapter } from '@biscuitland/rest';
|
||||
|
||||
import type { Shard } from '@biscuitland/ws';
|
||||
import { ShardManager, Options as mergeOptions } from '@biscuitland/ws';
|
||||
|
||||
import type { EventAdapter } from './adapters/event-adapter';
|
||||
import { DefaultEventAdapter } from './adapters/default-event-adapter';
|
||||
|
||||
import { Util } from './utils/util';
|
||||
|
||||
// PRESENCE
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/topics/gateway#update-status
|
||||
*/
|
||||
export interface StatusUpdate {
|
||||
activities: Activities[];
|
||||
status: keyof typeof StatusTypes;
|
||||
}
|
||||
|
||||
// END PRESENCE
|
||||
|
||||
// INTERACTIONS
|
||||
|
||||
export type CreateApplicationCommands = CreateApplicationCommand | CreateContextApplicationCommand;
|
||||
export type UpsertDataApplicationCommands =
|
||||
| AtLeastOne<CreateApplicationCommand>
|
||||
| AtLeastOne<CreateContextApplicationCommand>;
|
||||
export type LastCreateApplicationCommands =
|
||||
| AtLeastOne<CreateContextApplicationCommand>
|
||||
| AtLeastOne<CreateApplicationCommand>;
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params
|
||||
*/
|
||||
export interface CreateApplicationCommand {
|
||||
name: string;
|
||||
name_localizations?: Localization;
|
||||
description: string;
|
||||
description_localizations?: Localization;
|
||||
type?: ApplicationCommandTypes;
|
||||
options?: DiscordApplicationCommandOption[];
|
||||
default_member_permissions?: PermissionResolvable;
|
||||
dm_permission?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/application-commands#endpoints-json-params
|
||||
*/
|
||||
export interface CreateContextApplicationCommand extends Omit<CreateApplicationCommand, 'options'> {
|
||||
type: ApplicationCommandTypes.Message | ApplicationCommandTypes.User;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/application-commands#endpoints-query-string-params
|
||||
*/
|
||||
export interface GetApplicationCommand {
|
||||
guildId?: Snowflake;
|
||||
withLocalizations?: boolean;
|
||||
}
|
||||
|
||||
export interface UpsertApplicationCommands extends CreateApplicationCommand {
|
||||
id?: Snowflake;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions
|
||||
*/
|
||||
export interface ApplicationCommandPermissions {
|
||||
id: Snowflake;
|
||||
type: ApplicationCommandPermissionTypes;
|
||||
permission: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/application-commands#edit-application-command-permissions
|
||||
*/
|
||||
export interface ApplicationCommandPermissions {
|
||||
id: Snowflake;
|
||||
type: ApplicationCommandPermissionTypes;
|
||||
permission: boolean;
|
||||
}
|
||||
|
||||
// END INTERACTIONS
|
||||
|
||||
export type DiscordRawEventHandler = (
|
||||
shard: Shard,
|
||||
data: MessageEvent<any>
|
||||
) => unknown;
|
||||
|
||||
export type PickOptions = Pick<
|
||||
BiscuitOptions,
|
||||
Exclude<keyof BiscuitOptions, keyof typeof Session.DEFAULTS>
|
||||
> &
|
||||
Partial<BiscuitOptions>;
|
||||
|
||||
export interface BiscuitOptions {
|
||||
intents?: GatewayIntents;
|
||||
token: string;
|
||||
precense?: {
|
||||
status: keyof typeof StatusTypes;
|
||||
afk: boolean;
|
||||
since: number | null;
|
||||
activities: Activities[];
|
||||
};
|
||||
events?: {
|
||||
adapter?: { new (...args: any[]): EventAdapter };
|
||||
options: any;
|
||||
};
|
||||
|
||||
rest: {
|
||||
adapter?: { new (...args: any[]): RestAdapter };
|
||||
options: any;
|
||||
};
|
||||
|
||||
ws: {
|
||||
adapter?: { new (...args: any[]): ShardManager };
|
||||
options: any;
|
||||
};
|
||||
}
|
||||
|
||||
import * as Actions from './adapters/events';
|
||||
|
||||
export class Session {
|
||||
private _applicationId?: Snowflake;
|
||||
private _botId?: Snowflake;
|
||||
token: string;
|
||||
|
||||
set botId(snowflake: Snowflake) {
|
||||
this._botId = snowflake;
|
||||
}
|
||||
|
||||
get botId(): Snowflake {
|
||||
return this._botId ?? Util.getBotIdFromToken(this.token);
|
||||
}
|
||||
|
||||
set applicationId(snowflake: Snowflake) {
|
||||
this._applicationId = snowflake;
|
||||
}
|
||||
|
||||
get applicationId(): Snowflake {
|
||||
return this._applicationId ?? this.botId;
|
||||
}
|
||||
|
||||
static readonly DEFAULTS = {
|
||||
intents: 0,
|
||||
rest: {
|
||||
adapter: DefaultRestAdapter,
|
||||
options: null,
|
||||
},
|
||||
ws: {
|
||||
adapter: ShardManager,
|
||||
options: null,
|
||||
}
|
||||
};
|
||||
|
||||
options: BiscuitOptions;
|
||||
|
||||
readonly events: EventAdapter;
|
||||
|
||||
readonly rest: RestAdapter;
|
||||
readonly ws: ShardManager;
|
||||
|
||||
private adapters = new Map<string, any>();
|
||||
|
||||
constructor(options: PickOptions) {
|
||||
this.options = mergeOptions(Session.DEFAULTS, options);
|
||||
|
||||
// makeRest
|
||||
|
||||
if (!this.options.rest.options) {
|
||||
this.options.rest.options = {
|
||||
intents: this.options.intents,
|
||||
token: this.options.token,
|
||||
};
|
||||
}
|
||||
|
||||
this.rest = this.getRest();
|
||||
|
||||
// makeWs
|
||||
|
||||
const defHandler = (shard: Shard, payload: DiscordGatewayPayload) => {
|
||||
Actions.raw(this, shard.options.id, payload);
|
||||
|
||||
if (!payload.t || !payload.d) {
|
||||
return;
|
||||
}
|
||||
|
||||
Actions[payload.t as keyof typeof Actions]?.(
|
||||
this,
|
||||
shard.options.id,
|
||||
payload.d as any
|
||||
);
|
||||
};
|
||||
|
||||
if (!this.options.ws.options) {
|
||||
this.options.ws.options = {
|
||||
handleDiscordPayload: defHandler,
|
||||
|
||||
gateway: {
|
||||
url: '',
|
||||
shards: '',
|
||||
|
||||
session_start_limit: {
|
||||
total: 1000,
|
||||
remaining: 1000,
|
||||
reset_after: 3600000,
|
||||
|
||||
max_concurrency: 1
|
||||
}
|
||||
},
|
||||
config: {
|
||||
token: this.options.token,
|
||||
intents: this.options.intents,
|
||||
},
|
||||
makePresence: this.options.precense
|
||||
};
|
||||
}
|
||||
|
||||
// makeEvents
|
||||
|
||||
this.events = this.options.events?.adapter ? new this.options.events.adapter() : new DefaultEventAdapter();
|
||||
|
||||
this.ws = this.getWs();
|
||||
this.token = options.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
private getAdapter<T extends { new (...args: any[]): InstanceType<T> }>(
|
||||
adapter: T,
|
||||
...args: ConstructorParameters<T>
|
||||
): InstanceType<T> {
|
||||
if (!this.adapters.has(adapter.name)) {
|
||||
const Class = adapter as { new (...args: any[]): T };
|
||||
this.adapters.set(adapter.name, new Class(...args));
|
||||
}
|
||||
|
||||
return this.adapters.get(adapter.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
private getRest(): RestAdapter {
|
||||
return this.getAdapter(
|
||||
this.options.rest.adapter!,
|
||||
this.options.rest.options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
private getWs(): ShardManager {
|
||||
return this.getAdapter(
|
||||
this.options.ws.adapter!,
|
||||
this.options.ws.options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
|
||||
async start(): Promise<void> {
|
||||
const gateway = await this.rest.get<DiscordGetGatewayBot>('/gateway/bot');
|
||||
this.ws.options.gateway = gateway;
|
||||
|
||||
this.ws.spawns();
|
||||
}
|
||||
|
||||
// USEFUL METHODS
|
||||
|
||||
async editProfile(nick?: string, avatar?: string): Promise<User> {
|
||||
const user = await this.rest.patch<DiscordUser>(USER(), {
|
||||
username: nick ?? null,
|
||||
avatar: avatar ?? null,
|
||||
});
|
||||
|
||||
return new User(this, user);
|
||||
}
|
||||
|
||||
// END USEFUL METHODS
|
||||
|
||||
// PRESENCE
|
||||
|
||||
|
||||
/**
|
||||
* Edit bot's status
|
||||
* tip: execute this on the ready event if possible
|
||||
* @example
|
||||
* for (const { id } of session.gateway.manager.shards) {
|
||||
* session.editStatus(id, data);
|
||||
* }
|
||||
*/
|
||||
editStatus(shardId: number, status: StatusUpdate, prio = true): void {
|
||||
const shard = this.ws.shards.get(shardId);
|
||||
|
||||
if (!shard) {
|
||||
throw new Error(`Unknown shard ${shardId}`);
|
||||
}
|
||||
|
||||
shard.send({
|
||||
op: GatewayOpcodes.PresenceUpdate,
|
||||
d: {
|
||||
status: status.status,
|
||||
since: null,
|
||||
afk: false,
|
||||
activities: this.makePresenceActivites(status.activities),
|
||||
},
|
||||
}, prio);
|
||||
}
|
||||
|
||||
private makePresenceActivites(act: Activities[]): Record<string, unknown>[] {
|
||||
return act.map(activity => {
|
||||
return {
|
||||
name: activity.name,
|
||||
type: activity.type,
|
||||
url: activity.url,
|
||||
created_at: activity.createdAt,
|
||||
timestamps: activity.timestamps,
|
||||
application_id: this.applicationId,
|
||||
details: activity.details,
|
||||
state: activity.state,
|
||||
emoji: activity.emoji && {
|
||||
name: activity.emoji.name,
|
||||
id: activity.emoji.id,
|
||||
animated: activity.emoji.animated,
|
||||
},
|
||||
party: activity.party,
|
||||
assets: activity.assets &&
|
||||
{
|
||||
large_image: activity.assets.largeImage,
|
||||
large_text: activity.assets.largeText,
|
||||
small_image: activity.assets.smallImage,
|
||||
small_text: activity.assets.smallText,
|
||||
},
|
||||
secrets: activity.secrets,
|
||||
instance: activity.instance,
|
||||
flags: activity.flags,
|
||||
buttons: activity.buttons,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// END PRESENCE
|
||||
|
||||
// INTERACTIONS
|
||||
|
||||
updateApplicationCommandPermissions(
|
||||
guildId: Snowflake,
|
||||
id: Snowflake,
|
||||
bearerToken: string,
|
||||
options: ApplicationCommandPermissions[],
|
||||
): Promise<DiscordGuildApplicationCommandPermissions> {
|
||||
return this.rest.post<DiscordGuildApplicationCommandPermissions>(
|
||||
GUILD_APPLICATION_COMMANDS_PERMISSIONS(this.applicationId, guildId, id),
|
||||
{
|
||||
permissions: options,
|
||||
},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${bearerToken}` },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fetchApplicationCommand(id: Snowflake, options?: GetApplicationCommand): Promise<DiscordApplicationCommand> {
|
||||
return this.rest.get<DiscordApplicationCommand>(
|
||||
options?.guildId
|
||||
? GUILD_APPLICATION_COMMANDS_LOCALIZATIONS(
|
||||
this.applicationId,
|
||||
options.guildId,
|
||||
id,
|
||||
options?.withLocalizations,
|
||||
)
|
||||
: APPLICATION_COMMANDS(this.applicationId, id),
|
||||
);
|
||||
}
|
||||
|
||||
fetchApplicationCommandPermissions(guildId: Snowflake): Promise<DiscordGuildApplicationCommandPermissions[]> {
|
||||
return this.rest.get<DiscordGuildApplicationCommandPermissions[]>(
|
||||
GUILD_APPLICATION_COMMANDS_PERMISSIONS(this.applicationId, guildId),
|
||||
);
|
||||
}
|
||||
|
||||
fetchApplicationCommandPermission(
|
||||
guildId: Snowflake,
|
||||
id: Snowflake,
|
||||
): Promise<DiscordGuildApplicationCommandPermissions> {
|
||||
return this.rest.get<DiscordGuildApplicationCommandPermissions>(
|
||||
GUILD_APPLICATION_COMMANDS_PERMISSIONS(this.applicationId, guildId, id),
|
||||
);
|
||||
}
|
||||
|
||||
upsertApplicationCommand(
|
||||
id: Snowflake,
|
||||
options: UpsertDataApplicationCommands,
|
||||
guildId?: Snowflake,
|
||||
): Promise<DiscordApplicationCommand> {
|
||||
return this.rest.patch<DiscordApplicationCommand>(
|
||||
guildId
|
||||
? GUILD_APPLICATION_COMMANDS(this.applicationId, guildId)
|
||||
: APPLICATION_COMMANDS(this.applicationId, id),
|
||||
this.isContextApplicationCommand(options)
|
||||
? {
|
||||
name: options.name,
|
||||
type: options.type,
|
||||
}
|
||||
: {
|
||||
name: options.name,
|
||||
description: options.description,
|
||||
type: options.type,
|
||||
options: options.options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
upsertApplicationCommands(
|
||||
options: UpsertDataApplicationCommands[],
|
||||
guildId?: Snowflake,
|
||||
): Promise<DiscordApplicationCommand[]> {
|
||||
return this.rest.put<DiscordApplicationCommand[]>(
|
||||
guildId
|
||||
? GUILD_APPLICATION_COMMANDS(this.applicationId, guildId)
|
||||
: APPLICATION_COMMANDS(this.applicationId),
|
||||
options.map(o =>
|
||||
this.isContextApplicationCommand(o)
|
||||
? {
|
||||
name: o.name,
|
||||
type: o.type,
|
||||
}
|
||||
: {
|
||||
name: o.name,
|
||||
description: o.description,
|
||||
type: o.type,
|
||||
options: o.options,
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
fetchCommands(guildId?: Snowflake): Promise<DiscordApplicationCommand[]> {
|
||||
return this.rest.get<DiscordApplicationCommand[]>(
|
||||
guildId
|
||||
? GUILD_APPLICATION_COMMANDS(this.applicationId, guildId)
|
||||
: APPLICATION_COMMANDS(this.applicationId),
|
||||
);
|
||||
}
|
||||
|
||||
isContextApplicationCommand(cmd: LastCreateApplicationCommands): cmd is AtLeastOne<CreateContextApplicationCommand> {
|
||||
return cmd.type === ApplicationCommandTypes.Message || cmd.type === ApplicationCommandTypes.User;
|
||||
}
|
||||
|
||||
// END INTERACTIONS
|
||||
}
|
15
packages/core/src/events/handler.ts
Normal file
15
packages/core/src/events/handler.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { Session } from '../index';
|
||||
import type { GatewayEvents } from '@biscuitland/ws';
|
||||
|
||||
export function actionHandler([session, payload, shardId]: Parameters<ActionHandler>) {
|
||||
// @ts-expect-error At this point, typescript sucks
|
||||
session.emit(payload.t, payload.d, shardId);
|
||||
}
|
||||
|
||||
export type ActionHandler<G extends keyof GatewayEvents = keyof GatewayEvents,> = (
|
||||
...args: [Session<true>, { t: G; d: GatewayEvents[G] }, number]
|
||||
) => unknown;
|
||||
|
||||
export type Handler = {
|
||||
[K in keyof GatewayEvents]: (...args: [GatewayEvents[K], number]) => unknown;
|
||||
};
|
@ -1,21 +1,6 @@
|
||||
// SESSION
|
||||
export * as Actions from './adapters/events';
|
||||
|
||||
export { Session as Biscuit } from './biscuit';
|
||||
export * from './biscuit';
|
||||
|
||||
// STRUCTURES
|
||||
export * from './structures';
|
||||
|
||||
// EVENTS
|
||||
export * from './adapters/events';
|
||||
export * from './adapters/event-adapter';
|
||||
export * from './adapters/default-event-adapter';
|
||||
|
||||
// ETC
|
||||
export * from './snowflakes';
|
||||
|
||||
// UTIL
|
||||
export * from './utils/calculate-shard';
|
||||
export * from './utils/url-to-base-64';
|
||||
export * from './utils/util';
|
||||
export * from './utils/types';
|
||||
export * from './utils/utils';
|
||||
export * from './session';
|
||||
export { Session as Biscuit } from './session';
|
||||
export * from './managers/MainManager';
|
||||
export * from './events/handler';
|
||||
|
121
packages/core/src/managers/ApplicationManager.ts
Normal file
121
packages/core/src/managers/ApplicationManager.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
APIApplicationCommand,
|
||||
Identify,
|
||||
MakeRequired,
|
||||
RESTGetAPIApplicationCommandsQuery,
|
||||
RESTGetAPIApplicationGuildCommandsQuery,
|
||||
RESTPatchAPIApplicationCommandJSONBody,
|
||||
RESTPatchAPIApplicationGuildCommandJSONBody,
|
||||
RESTPostAPIApplicationCommandsJSONBody,
|
||||
RESTPostAPIApplicationGuildCommandsJSONBody,
|
||||
RESTPutAPIApplicationCommandPermissionsJSONBody,
|
||||
RESTPutAPIApplicationCommandsJSONBody,
|
||||
RESTPutAPIApplicationGuildCommandsJSONBody,
|
||||
RESTPutAPIApplicationRoleConnectionMetadataJSONBody
|
||||
} from '@biscuitland/common';
|
||||
import { Session } from '..';
|
||||
|
||||
export class ApplicationManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
getBotGateway() {
|
||||
return this.session.api.gateway.bot.get();
|
||||
}
|
||||
|
||||
getGateway() {
|
||||
return this.session.api.gateway.get();
|
||||
}
|
||||
|
||||
getNitroStickerPacks() {
|
||||
return this.session.api['sticker-packs'].get();
|
||||
}
|
||||
|
||||
getRoleConnectionMetadata(applicationId: string) {
|
||||
return this.session.api.applications(applicationId)['role-connections'].metadata.get();
|
||||
}
|
||||
|
||||
editRoleConnectionMetadata(applicationId: string, body: RESTPutAPIApplicationRoleConnectionMetadataJSONBody) {
|
||||
return this.session.api.applications(applicationId)['role-connections'].metadata.put({ body });
|
||||
}
|
||||
|
||||
getCommands(
|
||||
applicationId: string,
|
||||
query: RESTGetAPIApplicationCommandsQuery
|
||||
): Promise<RESTGetAPIApplicationCommandsWithLocalizationsResult>;
|
||||
getCommands(applicationId: string, query?: RESTGetAPIApplicationCommandsQuery) {
|
||||
return this.session.api.applications(applicationId).commands.get({ query });
|
||||
}
|
||||
|
||||
createCommand(applicationId: string, body: RESTPostAPIApplicationCommandsJSONBody) {
|
||||
return this.session.api.applications(applicationId).commands.post({ body });
|
||||
}
|
||||
|
||||
getCommand(applicationId: string, commandId: string) {
|
||||
return this.session.api.applications(applicationId).commands(commandId).get();
|
||||
}
|
||||
|
||||
editCommand(applicationId: string, commandId: string, body: RESTPatchAPIApplicationCommandJSONBody) {
|
||||
return this.session.api.applications(applicationId).commands(commandId).patch({ body });
|
||||
}
|
||||
|
||||
deleteCommand(applicationId: string, commandId: string) {
|
||||
return this.session.api.applications(applicationId).commands(commandId).delete();
|
||||
}
|
||||
|
||||
bulkCommands(applicationId: string, body: RESTPutAPIApplicationCommandsJSONBody) {
|
||||
return this.session.api.applications(applicationId).commands.put({ body });
|
||||
}
|
||||
|
||||
getCommandPermissions(applicationId: string, guildId: string, commandId: string) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands(commandId).permissions.get();
|
||||
}
|
||||
|
||||
editCommandPermissions(applicationId: string, guildId: string, commandId: string, body: RESTPutAPIApplicationCommandPermissionsJSONBody) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands(commandId).permissions.put({ body });
|
||||
}
|
||||
|
||||
getGuildCommands(
|
||||
applicationId: string,
|
||||
guildId: string,
|
||||
query: RESTGetAPIApplicationGuildCommandsQuery
|
||||
): Promise<RESTGetAPIApplicationGuildCommandsWithLocalizationsResult>;
|
||||
getGuildCommands(applicationId: string, guildId: string, query?: RESTGetAPIApplicationGuildCommandsQuery) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands.get({ query });
|
||||
}
|
||||
|
||||
createGuildCommand(applicationId: string, guildId: string, body: RESTPostAPIApplicationGuildCommandsJSONBody) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands.post({ body });
|
||||
}
|
||||
|
||||
getGuildCommand(applicationId: string, guildId: string, commandId: string) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands(commandId).get();
|
||||
}
|
||||
|
||||
editGuildCommand(applicationId: string, guildId: string, commandId: string, body: RESTPatchAPIApplicationGuildCommandJSONBody) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands(commandId).patch({ body });
|
||||
}
|
||||
|
||||
deleteGuildCommand(applicationId: string, guildId: string, commandId: string) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands(commandId).delete();
|
||||
}
|
||||
|
||||
bulkGuildCommands(applicationId: string, guildId: string, body: RESTPutAPIApplicationGuildCommandsJSONBody) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands.put({ body });
|
||||
}
|
||||
|
||||
getGuildCommandPermissions(applicationId: string, guildId: string) {
|
||||
return this.session.api.applications(applicationId).guilds(guildId).commands.permissions.get();
|
||||
}
|
||||
}
|
||||
|
||||
export type RESTGetAPIApplicationCommandsWithLocalizationsResult = Identify<
|
||||
MakeRequired<APIApplicationCommand, 'name_localizations' | 'description_localizations'>
|
||||
>[];
|
||||
|
||||
export type RESTGetAPIApplicationGuildCommandsWithLocalizationsResult = RESTGetAPIApplicationCommandsWithLocalizationsResult;
|
214
packages/core/src/managers/ChannelManager.ts
Normal file
214
packages/core/src/managers/ChannelManager.ts
Normal file
@ -0,0 +1,214 @@
|
||||
import type {
|
||||
APIChannel,
|
||||
RESTPostAPIChannelMessageJSONBody,
|
||||
RESTPatchAPIChannelJSONBody,
|
||||
RESTGetAPIChannelThreadsArchivedQuery,
|
||||
RESTGetAPIChannelMessageReactionUsersQuery,
|
||||
RESTPatchAPIChannelMessageJSONBody,
|
||||
RESTPostAPIChannelMessagesBulkDeleteJSONBody,
|
||||
RESTPutAPIChannelPermissionJSONBody,
|
||||
RESTPostAPIChannelInviteJSONBody,
|
||||
RESTPostAPIChannelFollowersJSONBody,
|
||||
RESTPutAPIChannelRecipientJSONBody,
|
||||
RESTPostAPIChannelMessagesThreadsJSONBody,
|
||||
RESTPostAPIChannelThreadsJSONBody,
|
||||
RESTPostAPIChannelThreadsResult,
|
||||
RESTPostAPIGuildForumThreadsJSONBody,
|
||||
RESTGetAPIChannelThreadMembersQuery,
|
||||
RESTGetAPIChannelThreadMemberQuery,
|
||||
RESTPostAPIChannelWebhookJSONBody,
|
||||
RESTPatchAPIStageInstanceJSONBody
|
||||
} from '@biscuitland/common';
|
||||
import type { RawFile } from '@biscuitland/rest';
|
||||
|
||||
import type { Session } from '../session';
|
||||
|
||||
export class ChannelManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
get<T extends APIChannel = APIChannel>(id: string) {
|
||||
return this.session.api.channels(id).get() as Promise<T>;
|
||||
}
|
||||
|
||||
getWebhooks(channelId: string) {
|
||||
return this.session.api.channels(channelId).webhooks.get();
|
||||
}
|
||||
|
||||
createWebhook(channelId: string, body: RESTPostAPIChannelWebhookJSONBody) {
|
||||
return this.session.api.channels(channelId).webhooks.post({ body });
|
||||
}
|
||||
|
||||
edit(id: string, data: RESTPatchAPIChannelJSONBody) {
|
||||
return this.session.api.channels(id).patch({ body: data });
|
||||
}
|
||||
|
||||
delete(id: string) {
|
||||
return this.session.api.channels(id).delete();
|
||||
}
|
||||
|
||||
getMessages(id: string, limit = 50) {
|
||||
return this.session.api.channels(id).messages.get({
|
||||
query: { limit }
|
||||
});
|
||||
}
|
||||
|
||||
getMessage(id: string, messageId: string) {
|
||||
return this.session.api.channels(id).messages(messageId).get();
|
||||
}
|
||||
|
||||
createMessage(id: string, data: RESTPostAPIChannelMessageJSONBody) {
|
||||
return this.session.api.channels(id).messages.post({ body: data });
|
||||
}
|
||||
|
||||
sendTyping(id: string) {
|
||||
return this.session.api.channels(id).typing.post();
|
||||
}
|
||||
|
||||
getArchivedThreads(channelId: string, options: RESTGetAPIChannelThreadsArchivedOptions) {
|
||||
const { type, ...query } = options;
|
||||
if (type === 'private') {
|
||||
return this.session.api.channels(channelId).threads.archived.private.get({ query });
|
||||
}
|
||||
|
||||
return this.session.api.channels(channelId).threads.archived.public.get({ query });
|
||||
}
|
||||
|
||||
crosspostMessage(channelId: string, messageId: string) {
|
||||
return this.session.api.channels(channelId).messages(messageId).crosspost.post({});
|
||||
}
|
||||
|
||||
createReaction(channelId: string, messageId: string, emoji: string) {
|
||||
return this.session.api.channels(channelId).messages(messageId).reactions(emoji)('@me').put({});
|
||||
}
|
||||
|
||||
deleteReaction(channelId: string, messageId: string, emoji: string, user = '@me') {
|
||||
return this.session.api.channels(channelId).messages(messageId).reactions(emoji)(user).delete();
|
||||
}
|
||||
|
||||
getReactions(channelId: string, messageId: string, emoji: string, query?: RESTGetAPIChannelMessageReactionUsersQuery) {
|
||||
return this.session.api.channels(channelId).messages(messageId).reactions(emoji).get({ query });
|
||||
}
|
||||
|
||||
deleteAllReactions(channelId: string, messageId: string, emoji?: string) {
|
||||
if (emoji?.length) return this.session.api.channels(channelId).messages(messageId).reactions(emoji).delete();
|
||||
return this.session.api.channels(channelId).messages(messageId).reactions.delete();
|
||||
}
|
||||
|
||||
editMessage(channelId: string, messageId: string, body: RESTPatchAPIChannelMessageJSONBody, files?: RawFile[]) {
|
||||
return this.session.api.channels(channelId).messages(messageId).patch({
|
||||
body,
|
||||
files
|
||||
});
|
||||
}
|
||||
|
||||
deleteMessage(channelId: string, messageId: string, reason?: string) {
|
||||
return this.session.api.channels(channelId).messages(messageId).delete({ reason });
|
||||
}
|
||||
|
||||
bulkMessages(channelId: string, body: RESTPostAPIChannelMessagesBulkDeleteJSONBody, reason?: string) {
|
||||
return this.session.api.channels(channelId).messages['bulk-delete'].post({ body, reason });
|
||||
}
|
||||
|
||||
editPermissions(channelId: string, overwriteId: string, body: RESTPutAPIChannelPermissionJSONBody, reason?: string) {
|
||||
return this.session.api.channels(channelId).permissions(overwriteId).put({ body, reason });
|
||||
}
|
||||
|
||||
deletePermission(channelId: string, overwriteId: string, reason?: string) {
|
||||
return this.session.api.channels(channelId).permissions(overwriteId).delete({ reason });
|
||||
}
|
||||
|
||||
getInvites(channelId: string) {
|
||||
return this.session.api.channels(channelId).invites.get();
|
||||
}
|
||||
|
||||
createInvite(channelId: string, body: RESTPostAPIChannelInviteJSONBody) {
|
||||
return this.session.api.channels(channelId).invites.post({ body });
|
||||
}
|
||||
|
||||
followAnnoucement(channelId: string, body: RESTPostAPIChannelFollowersJSONBody) {
|
||||
return this.session.api.channels(channelId).followers.post({ body });
|
||||
}
|
||||
|
||||
getPinnedMessages(channelId: string) {
|
||||
return this.session.api.channels(channelId).pins.get();
|
||||
}
|
||||
|
||||
pinMessage(channelId: string, messageId: string, reason?: string) {
|
||||
return this.session.api.channels(channelId).pins(messageId).put({ reason });
|
||||
}
|
||||
|
||||
unpinMessage(channelId: string, messageId: string, reason?: string) {
|
||||
return this.session.api.channels(channelId).pins(messageId).delete({ reason });
|
||||
}
|
||||
|
||||
groupDMAddRecipient(channelId: string, userId: string, body: RESTPutAPIChannelRecipientJSONBody) {
|
||||
return this.session.api.channels(channelId).recipients(userId).put({ body });
|
||||
}
|
||||
|
||||
groupDMRemoveRecipient(channelId: string, userId: string) {
|
||||
return this.session.api.channels(channelId).recipients(userId).delete();
|
||||
}
|
||||
|
||||
startThreadFromMessage(channelId: string, messageId: string, body: RESTPostAPIChannelMessagesThreadsJSONBody, reason?: string) {
|
||||
return this.session.api.channels(channelId).messages(messageId).threads.post({ body, reason });
|
||||
}
|
||||
|
||||
startThread(channelId: string, body: RESTPostAPIChannelThreadsJSONBody, reason?: string): Promise<RESTPostAPIChannelThreadsResult>;
|
||||
startThread(channelId: string, body: RESTPostAPIGuildForumThreadsJSONBody, reason?: string) {
|
||||
return this.session.api.channels(channelId).threads.post({ body, reason });
|
||||
}
|
||||
|
||||
getListJoinedPrivateArchivedThreads(channelId: string, query?: RESTGetAPIChannelThreadsArchivedQuery) {
|
||||
return this.session.api.channels(channelId).users('@me').threads.archived.private.get({ query });
|
||||
}
|
||||
|
||||
getThreadMembers(channelId: string, query?: RESTGetAPIChannelThreadMembersQuery) {
|
||||
return this.session.api.channels(channelId)['thread-members'].get({ query });
|
||||
}
|
||||
|
||||
getThreadMember(channelId: string, userId: string, query?: RESTGetAPIChannelThreadMemberQuery) {
|
||||
return this.session.api.channels(channelId)['thread-members'](userId).get({ query });
|
||||
}
|
||||
|
||||
addThreadMember(channelId: string, userId: string) {
|
||||
return this.session.api.channels(channelId)['thread-members'](userId).put({});
|
||||
}
|
||||
|
||||
removeThreadMember(channelId: string, userId: string) {
|
||||
return this.session.api.channels(channelId)['thread-members'](userId).delete();
|
||||
}
|
||||
|
||||
leaveThread(channelId: string) {
|
||||
return this.session.api.channels(channelId)['thread-members']('@me').delete();
|
||||
}
|
||||
|
||||
joinThread(channelId: string) {
|
||||
return this.session.api.channels(channelId)['thread-members']('@me').put({});
|
||||
}
|
||||
|
||||
getVoiceRegions() {
|
||||
return this.session.api.voice.region.get();
|
||||
}
|
||||
|
||||
getStageInstance(channelId: string) {
|
||||
return this.session.api['stage-instances'](channelId).get();
|
||||
}
|
||||
|
||||
editStageInstance(channelId: string, body: RESTPatchAPIStageInstanceJSONBody, reason?: string) {
|
||||
return this.session.api['stage-instances'](channelId).patch({ body, reason });
|
||||
}
|
||||
|
||||
deleteStageInstance(channelId: string, reason?: string) {
|
||||
return this.session.api['stage-instances'](channelId).delete({ reason });
|
||||
}
|
||||
}
|
||||
|
||||
export type RESTGetAPIChannelThreadsArchivedOptions = {
|
||||
type: 'private' | 'public';
|
||||
} & RESTGetAPIChannelThreadsArchivedQuery;
|
348
packages/core/src/managers/GuildManager.ts
Normal file
348
packages/core/src/managers/GuildManager.ts
Normal file
@ -0,0 +1,348 @@
|
||||
import type { Session } from '../session';
|
||||
import type {
|
||||
GuildMFALevel,
|
||||
APIGuildChannel,
|
||||
GuildChannelType,
|
||||
RESTPostAPIGuildPruneJSONBody,
|
||||
RESTPostAPIGuildsJSONBody,
|
||||
RESTPatchAPIGuildJSONBody,
|
||||
RESTPostAPIGuildChannelJSONBody,
|
||||
RESTPatchAPIGuildChannelPositionsJSONBody,
|
||||
RESTGetAPIGuildBansQuery,
|
||||
RESTPutAPIGuildBanJSONBody,
|
||||
RESTPostAPIGuildRoleJSONBody,
|
||||
RESTPatchAPIGuildRolePositionsJSONBody,
|
||||
RESTPatchAPIGuildRoleJSONBody,
|
||||
RESTPatchAPIGuildWidgetSettingsJSONBody,
|
||||
RESTPatchAPIGuildWelcomeScreenJSONBody,
|
||||
RESTGetAPIGuildPruneCountQuery,
|
||||
RESTGetAPIAuditLogQuery,
|
||||
RESTPostAPIAutoModerationRuleJSONBody,
|
||||
RESTPatchAPIAutoModerationRuleJSONBody,
|
||||
RESTPostAPITemplateCreateGuildJSONBody,
|
||||
RESTGetAPIGuildMembersQuery,
|
||||
RESTGetAPIGuildMembersSearchQuery,
|
||||
RESTPatchAPICurrentGuildMemberJSONBody,
|
||||
RESTPutAPIGuildMemberJSONBody,
|
||||
RESTPatchAPIGuildMemberJSONBody,
|
||||
RESTGetAPIGuildWidgetImageQuery,
|
||||
RESTPatchAPIGuildEmojiJSONBody,
|
||||
RESTPostAPIGuildEmojiJSONBody,
|
||||
RESTPatchAPIGuildVoiceStateUserJSONBody,
|
||||
RESTPatchAPIGuildVoiceStateCurrentMemberJSONBody,
|
||||
RESTPatchAPIGuildStickerJSONBody,
|
||||
RESTPostAPIGuildStickerFormDataBody,
|
||||
RESTGetAPIGuildScheduledEventsQuery,
|
||||
RESTPatchAPIGuildScheduledEventJSONBody,
|
||||
RESTPostAPIGuildScheduledEventJSONBody,
|
||||
RESTGetAPIGuildScheduledEventQuery,
|
||||
RESTGetAPIGuildScheduledEventUsersQuery,
|
||||
RESTPatchAPIGuildTemplateJSONBody,
|
||||
RESTPostAPIGuildTemplatesJSONBody
|
||||
} from '@biscuitland/common';
|
||||
|
||||
export class GuildManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
get(guildId: string) {
|
||||
return this.session.api.guilds(guildId).get();
|
||||
}
|
||||
|
||||
create(options: RESTPostAPIGuildsJSONBody) {
|
||||
return this.session.api.guilds.post({ body: options });
|
||||
}
|
||||
|
||||
delete(guildId: string) {
|
||||
return this.session.api.guilds(guildId).delete();
|
||||
}
|
||||
|
||||
edit(guildId: string, options: RESTPatchAPIGuildJSONBody) {
|
||||
return this.session.api.guilds(guildId).patch({ body: options });
|
||||
}
|
||||
|
||||
getChannels(guildId: string) {
|
||||
return this.session.api.guilds(guildId).channels.get();
|
||||
}
|
||||
|
||||
createChannel<T extends APIGuildChannel<GuildChannelType>>(guildId: string, body: RESTPostAPIGuildChannelJSONBody) {
|
||||
return this.session.api.guilds(guildId).channels.post({ body }) as Promise<T>;
|
||||
}
|
||||
|
||||
editChannelPositions(guildId: string, body: RESTPatchAPIGuildChannelPositionsJSONBody): Promise<void> {
|
||||
return this.session.api.guilds(guildId).channels.patch({ body });
|
||||
}
|
||||
|
||||
getThreads(guildId: string) {
|
||||
return this.session.api.guilds(guildId).threads.active.get();
|
||||
}
|
||||
|
||||
getBans(guildId: string, query: RESTGetAPIGuildBansQuery = {}) {
|
||||
return this.session.api.guilds(guildId).bans.get({
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
getBan(guildId: string, userId: string) {
|
||||
return this.session.api.guilds(guildId).bans(userId).get();
|
||||
}
|
||||
|
||||
createBan(guildId: string, userId: string, body: RESTPutAPIGuildBanJSONBody) {
|
||||
return this.session.api.guilds(guildId).bans(userId).put({ body });
|
||||
}
|
||||
|
||||
removeBan(guildId: string, userId: string) {
|
||||
return this.session.api.guilds(guildId).bans(userId).delete();
|
||||
}
|
||||
|
||||
getRoles(guildId: string) {
|
||||
return this.session.api.guilds(guildId).roles.get();
|
||||
}
|
||||
|
||||
createRole(guildId: string, options: RESTPostAPIGuildRoleJSONBody) {
|
||||
return this.session.api.guilds(guildId).roles.post({ body: options });
|
||||
}
|
||||
|
||||
editRolePositions(guildId: string, options: RESTPatchAPIGuildRolePositionsJSONBody) {
|
||||
return this.session.api.guilds(guildId).roles.patch({ body: options });
|
||||
}
|
||||
|
||||
editRole(guildId: string, roleId: string, options: RESTPatchAPIGuildRoleJSONBody) {
|
||||
return this.session.api.guilds(guildId).roles(roleId).patch({ body: options });
|
||||
}
|
||||
|
||||
deleteRole(guildId: string, roleId: string) {
|
||||
return this.session.api.guilds(guildId).roles(roleId).delete();
|
||||
}
|
||||
|
||||
editGuildMFALevel(guildId: string, level: GuildMFALevel) {
|
||||
return this.session.api.guilds(guildId).mfa.post({ body: { level } });
|
||||
}
|
||||
|
||||
getPruneCount(guildId: string, query: RESTGetAPIGuildPruneCountQuery) {
|
||||
return this.session.api.guilds(guildId).prune.get({
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
beginGuildPrune(guildId: string, options: RESTPostAPIGuildPruneJSONBody) {
|
||||
return this.session.api.guilds(guildId).prune.post({ body: options });
|
||||
}
|
||||
|
||||
getVoiceRegions(guildId: string) {
|
||||
return this.session.api.guilds(guildId).regions.get();
|
||||
}
|
||||
|
||||
getInvites(guildId: string) {
|
||||
return this.session.api.guilds(guildId).invites.get();
|
||||
}
|
||||
|
||||
getIntegrations(guildId: string) {
|
||||
return this.session.api.guilds(guildId).integrations.get();
|
||||
}
|
||||
|
||||
deleteIntegration(guildId: string, integrationId: string) {
|
||||
return this.session.api.guilds(guildId).integrations(integrationId).delete();
|
||||
}
|
||||
|
||||
getWidget(guildId: string) {
|
||||
return this.session.api.guilds(guildId).widget.get();
|
||||
}
|
||||
|
||||
editWidget(guildId: string, options: RESTPatchAPIGuildWidgetSettingsJSONBody) {
|
||||
return this.session.api.guilds(guildId).widget.patch({ body: options });
|
||||
}
|
||||
|
||||
getVanityUrl(guildId: string) {
|
||||
return this.session.api.guilds(guildId)['vanity-url'].get();
|
||||
}
|
||||
|
||||
getWelcomeScreen(guildId: string) {
|
||||
return this.session.api.guilds(guildId)['welcome-screen'].get();
|
||||
}
|
||||
|
||||
editWelcomeScreen(guildId: string, options: RESTPatchAPIGuildWelcomeScreenJSONBody) {
|
||||
return this.session.api.guilds(guildId)['welcome-screen'].patch({ body: options });
|
||||
}
|
||||
|
||||
getAuditLog(guildId: string, query?: RESTGetAPIAuditLogQuery) {
|
||||
return this.session.api.guilds(guildId)['audit-logs'].get({ query });
|
||||
}
|
||||
|
||||
getAutoModerationRules(guildId: string) {
|
||||
return this.session.api.guilds(guildId)['auto-moderation'].rules.get();
|
||||
}
|
||||
|
||||
getAutoModerationRule(guildId: string, ruleId: string) {
|
||||
return this.session.api.guilds(guildId)['auto-moderation'].rules(ruleId).get();
|
||||
}
|
||||
|
||||
createAutoModerationRule(guildId: string, body: RESTPostAPIAutoModerationRuleJSONBody, reason?: string) {
|
||||
return this.session.api.guilds(guildId)['auto-moderation'].rules.post({ body, reason });
|
||||
}
|
||||
|
||||
editAutoModerationRule(guildId: string, body: RESTPatchAPIAutoModerationRuleJSONBody, reason?: string) {
|
||||
return this.session.api.guilds(guildId)['auto-moderation'].rules.patch({ body, reason });
|
||||
}
|
||||
|
||||
deleteAutoModerationRule(guildId: string, ruleId: string, reason?: string) {
|
||||
return this.session.api.guilds(guildId)['auto-moderation'].rules(ruleId).delete({ reason });
|
||||
}
|
||||
|
||||
getTemplate(code: string) {
|
||||
return this.session.api.guilds.templates(code).get();
|
||||
}
|
||||
|
||||
createTemplate(code: string, body: RESTPostAPITemplateCreateGuildJSONBody) {
|
||||
return this.session.api.guilds.templates(code).post({ body });
|
||||
}
|
||||
|
||||
getWebhooks(guildId: string) {
|
||||
return this.session.api.guilds(guildId).webhooks.get();
|
||||
}
|
||||
|
||||
getPreview(guildId: string) {
|
||||
return this.session.api.guilds(guildId).preview.get();
|
||||
}
|
||||
|
||||
getMembers(guildId: string, query?: RESTGetAPIGuildMembersQuery) {
|
||||
return this.session.api.guilds(guildId).members.get({ query });
|
||||
}
|
||||
|
||||
searchMembers(guildId: string, query?: RESTGetAPIGuildMembersSearchQuery) {
|
||||
return this.session.api.guilds(guildId).members.search.get({ query });
|
||||
}
|
||||
|
||||
editCurrentMember(guildId: string, body: RESTPatchAPICurrentGuildMemberJSONBody) {
|
||||
return this.session.api.guilds(guildId).members['@me'].patch({ body });
|
||||
}
|
||||
|
||||
getMember(guildId: string, memberId: string) {
|
||||
return this.session.api.guilds(guildId).members(memberId).get();
|
||||
}
|
||||
|
||||
addMember(guildId: string, memberId: string, body: RESTPutAPIGuildMemberJSONBody) {
|
||||
return this.session.api.guilds(guildId).members(memberId).put({ body });
|
||||
}
|
||||
|
||||
editMember(guildId: string, memberId: string, body: RESTPatchAPIGuildMemberJSONBody) {
|
||||
return this.session.api.guilds(guildId).members(memberId).patch({ body });
|
||||
}
|
||||
|
||||
removeMember(guildId: string, memberId: string) {
|
||||
return this.session.api.guilds(guildId).members(memberId).delete();
|
||||
}
|
||||
|
||||
addRoleMember(guildId: string, memberId: string, roleId: string) {
|
||||
return this.session.api.guilds(guildId).members(memberId).roles(roleId).put({});
|
||||
}
|
||||
|
||||
removeRoleMember(guildId: string, memberId: string, roleId: string) {
|
||||
return this.session.api.guilds(guildId).members(memberId).roles(roleId).delete({});
|
||||
}
|
||||
|
||||
getWidgetJson(guildId: string) {
|
||||
return this.session.api.guilds(guildId)['widget.json'].get();
|
||||
}
|
||||
|
||||
getWidgetPng(guildId: string, query?: RESTGetAPIGuildWidgetImageQuery) {
|
||||
return this.session.api.guilds(guildId)['widget.png'].get({ query });
|
||||
}
|
||||
|
||||
getEmojis(guildId: string) {
|
||||
return this.session.api.guilds(guildId).emojis.get();
|
||||
}
|
||||
|
||||
createEmoji(guildId: string, body: RESTPostAPIGuildEmojiJSONBody) {
|
||||
return this.session.api.guilds(guildId).emojis.post({ body });
|
||||
}
|
||||
|
||||
getEmoji(guildId: string, emojiId: string) {
|
||||
return this.session.api.guilds(guildId).emojis(emojiId).get();
|
||||
}
|
||||
|
||||
editEmoji(guildId: string, emojiId: string, body: RESTPatchAPIGuildEmojiJSONBody) {
|
||||
return this.session.api.guilds(guildId).emojis(emojiId).patch({ body });
|
||||
}
|
||||
|
||||
deleteEmoji(guildId: string, emojiId: string) {
|
||||
return this.session.api.guilds(guildId).emojis(emojiId).delete();
|
||||
}
|
||||
|
||||
editCurrentVoiceState(guildId: string, body: RESTPatchAPIGuildVoiceStateCurrentMemberJSONBody) {
|
||||
return this.session.api.guilds(guildId)['voice-states']['@me'].patch({ body });
|
||||
}
|
||||
|
||||
editMemberVoiceState(guildId: string, memberId: string, body: RESTPatchAPIGuildVoiceStateUserJSONBody) {
|
||||
return this.session.api.guilds(guildId)['voice-states'](memberId).patch({ body });
|
||||
}
|
||||
|
||||
getStickers(guildId: string) {
|
||||
return this.session.api.guilds(guildId).stickers.get();
|
||||
}
|
||||
|
||||
createSticker(guildId: string, body: RESTPostAPIGuildStickerFormDataBody) {
|
||||
return this.session.api.guilds(guildId).stickers.post({ body });
|
||||
}
|
||||
|
||||
getSticker(guildId: string, stickerId: string) {
|
||||
return this.session.api.guilds(guildId).stickers(stickerId).get();
|
||||
}
|
||||
|
||||
editSticker(guildId: string, stickerId: string, body: RESTPatchAPIGuildStickerJSONBody) {
|
||||
return this.session.api.guilds(guildId).stickers(stickerId).patch({ body });
|
||||
}
|
||||
|
||||
deleteSticker(guildId: string, stickerId: string) {
|
||||
return this.session.api.guilds(guildId).stickers(stickerId).delete();
|
||||
}
|
||||
|
||||
getScheduledEvents(guildId: string, query?: RESTGetAPIGuildScheduledEventsQuery) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'].get({ query });
|
||||
}
|
||||
|
||||
createScheduledEvent(guildId: string, body: RESTPostAPIGuildScheduledEventJSONBody) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'].post({ body });
|
||||
}
|
||||
|
||||
getScheduledEvent(guildId: string, scheduledEvent: string, query?: RESTGetAPIGuildScheduledEventQuery) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'](scheduledEvent).get({ query });
|
||||
}
|
||||
|
||||
editScheduledEvent(guildId: string, scheduledEvent: string, body: RESTPatchAPIGuildScheduledEventJSONBody) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'](scheduledEvent).patch({ body });
|
||||
}
|
||||
|
||||
deleteScheduledEvent(guildId: string, scheduledEvent: string) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'](scheduledEvent).delete();
|
||||
}
|
||||
|
||||
getUsersScheduledEvent(guildId: string, scheduledEvent: string, query: RESTGetAPIGuildScheduledEventUsersQuery) {
|
||||
return this.session.api.guilds(guildId)['scheduled-events'](scheduledEvent).users.get({ query });
|
||||
}
|
||||
|
||||
getGuildTemplates(guildId: string) {
|
||||
return this.session.api.guilds(guildId).templates.get();
|
||||
}
|
||||
|
||||
createGuildTemplate(guildId: string, body: RESTPostAPIGuildTemplatesJSONBody) {
|
||||
return this.session.api.guilds(guildId).templates.post({ body });
|
||||
}
|
||||
|
||||
syncGuildTemplate(guildId: string, code: string) {
|
||||
return this.session.api.guilds(guildId).templates(code).put({});
|
||||
}
|
||||
|
||||
modifyGuildTemaplte(guildId: string, code: string, body: RESTPatchAPIGuildTemplateJSONBody) {
|
||||
return this.session.api.guilds(guildId).templates(code).patch({ body });
|
||||
}
|
||||
|
||||
deleteCodeTemplate(guildId: string, code: string) {
|
||||
return this.session.api.guilds(guildId).templates(code).delete();
|
||||
}
|
||||
}
|
57
packages/core/src/managers/InteractionManager.ts
Normal file
57
packages/core/src/managers/InteractionManager.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import type {
|
||||
RESTPatchAPIWebhookWithTokenMessageJSONBody,
|
||||
RESTPostAPIInteractionCallbackJSONBody,
|
||||
RESTPostAPIInteractionFollowupJSONBody
|
||||
} from '@biscuitland/common';
|
||||
import type { Session } from '..';
|
||||
import type { RawFile } from '@biscuitland/rest';
|
||||
|
||||
export class InteractionManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
reply<T extends RESTPostAPIInteractionCallbackJSONBody = RESTPostAPIInteractionCallbackJSONBody>(
|
||||
interactionId: string,
|
||||
token: string,
|
||||
body: T,
|
||||
files?: RawFile[]
|
||||
) {
|
||||
return this.session.api.interactions(interactionId)(token).callback.post({
|
||||
body,
|
||||
files
|
||||
});
|
||||
}
|
||||
|
||||
getResponse(applicationId: string, token: string, messageId = '@original') {
|
||||
return this.session.api.webhooks(applicationId)(token).messages(messageId).get();
|
||||
}
|
||||
|
||||
editResponse(
|
||||
applicationId: string,
|
||||
token: string,
|
||||
messageId: string,
|
||||
body: RESTPatchAPIWebhookWithTokenMessageJSONBody,
|
||||
files?: RawFile[]
|
||||
) {
|
||||
return this.session.api.webhooks(applicationId)(token).messages(messageId).patch({
|
||||
body,
|
||||
files
|
||||
});
|
||||
}
|
||||
|
||||
deleteResponse(applicationId: string, token: string, messageId = '@original') {
|
||||
return this.session.api.webhooks(applicationId)(token).messages(messageId).delete();
|
||||
}
|
||||
|
||||
followUp(applicationId: string, token: string, body: RESTPostAPIInteractionFollowupJSONBody, files?: RawFile[]) {
|
||||
return this.session.api.webhooks(applicationId)(token).post({
|
||||
body,
|
||||
files
|
||||
});
|
||||
}
|
||||
}
|
19
packages/core/src/managers/MainManager.ts
Normal file
19
packages/core/src/managers/MainManager.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { Session } from '../session';
|
||||
import { ChannelManager } from './ChannelManager';
|
||||
import { GuildManager } from './GuildManager';
|
||||
import { MemberManager } from './MemberManager';
|
||||
import { UserManager } from './UserManager';
|
||||
|
||||
export class MainManager {
|
||||
constructor(private readonly session: Session) {
|
||||
this.users = new UserManager(this.session);
|
||||
this.guilds = new GuildManager(this.session);
|
||||
this.members = new MemberManager(this.session);
|
||||
this.channels = new ChannelManager(this.session);
|
||||
}
|
||||
|
||||
users: UserManager;
|
||||
guilds: GuildManager;
|
||||
members: MemberManager;
|
||||
channels: ChannelManager;
|
||||
}
|
19
packages/core/src/managers/MemberManager.ts
Normal file
19
packages/core/src/managers/MemberManager.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { APIGuildMember, MakeRequired } from '@biscuitland/common';
|
||||
import type { Session, ImageOptions } from '../index';
|
||||
import { formatImageURL } from '../index';
|
||||
|
||||
export class MemberManager {
|
||||
constructor(private readonly session: Session) {}
|
||||
|
||||
dynamicAvatarURL({ avatar, guild_id, user }: DynamicMember, { size, format }: ImageOptions): string {
|
||||
if (avatar?.length) {
|
||||
return formatImageURL(this.session.cdn.guilds(guild_id).users(user.id).avatars(avatar).get(), size, format);
|
||||
}
|
||||
|
||||
return this.session.managers.users.avatarURL(user, { size, format });
|
||||
}
|
||||
}
|
||||
|
||||
export type DynamicMember = MakeRequired<APIGuildMember, 'user'> & {
|
||||
guild_id: string;
|
||||
};
|
64
packages/core/src/managers/UserManager.ts
Normal file
64
packages/core/src/managers/UserManager.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import type {
|
||||
APIUser,
|
||||
RESTGetAPICurrentUserGuildsQuery,
|
||||
RESTPatchAPICurrentUserJSONBody,
|
||||
RESTPutAPICurrentUserApplicationRoleConnectionJSONBody
|
||||
} from '@biscuitland/common';
|
||||
import type { Session, ImageOptions } from '../index';
|
||||
import { formatImageURL } from '../index';
|
||||
|
||||
export class UserManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
get(userId = '@me') {
|
||||
return this.session.api.users(userId).get();
|
||||
}
|
||||
|
||||
avatarURL(user: APIUser, { size, format }: ImageOptions) {
|
||||
if (user.avatar?.length) {
|
||||
return formatImageURL(this.session.cdn.avatars(user.id).get(user.avatar), size, format);
|
||||
}
|
||||
|
||||
return formatImageURL(this.session.cdn.embed.avatars.get(Number(user.discriminator) % 5));
|
||||
}
|
||||
|
||||
createDM(userId: string) {
|
||||
return this.session.api.users('@me').channels.post({ body: { recipient_id: userId } });
|
||||
}
|
||||
|
||||
editCurrent(body: RESTPatchAPICurrentUserJSONBody) {
|
||||
return this.session.api.users('@me').patch({
|
||||
body
|
||||
});
|
||||
}
|
||||
|
||||
getGuilds(query?: RESTGetAPICurrentUserGuildsQuery) {
|
||||
return this.session.api.users('@me').guilds.get({ query });
|
||||
}
|
||||
|
||||
getGuildMember(guildId: string) {
|
||||
return this.session.api.users('@me').guilds(guildId).member.get();
|
||||
}
|
||||
|
||||
leaveGuild(guildId: string) {
|
||||
return this.session.api.users('@me').guilds(guildId).delete();
|
||||
}
|
||||
|
||||
getConnections() {
|
||||
return this.session.api.users('@me').connections.get();
|
||||
}
|
||||
|
||||
getRoleConnections(applicationId: string) {
|
||||
return this.session.api.users('@me').applications(applicationId)['role-connection'].get();
|
||||
}
|
||||
|
||||
updateRoleConnection(applicationId: string, body: RESTPutAPICurrentUserApplicationRoleConnectionJSONBody) {
|
||||
return this.session.api.users('@me').applications(applicationId)['role-connection'].put({ body });
|
||||
}
|
||||
}
|
124
packages/core/src/managers/WebhookManager.ts
Normal file
124
packages/core/src/managers/WebhookManager.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import {
|
||||
Identify,
|
||||
RESTPatchAPIWebhookJSONBody,
|
||||
RESTPatchAPIWebhookResult,
|
||||
RESTPatchAPIWebhookWithTokenJSONBody,
|
||||
RESTPatchAPIWebhookWithTokenMessageJSONBody,
|
||||
RESTPatchAPIWebhookWithTokenResult,
|
||||
RESTPostAPIChannelWebhookJSONBody,
|
||||
RESTPostAPIWebhookWithTokenGitHubQuery,
|
||||
RESTPostAPIWebhookWithTokenGitHubWaitResult,
|
||||
RESTPostAPIWebhookWithTokenJSONBody,
|
||||
RESTPostAPIWebhookWithTokenQuery,
|
||||
RESTPostAPIWebhookWithTokenSlackQuery,
|
||||
RESTPostAPIWebhookWithTokenSlackWaitResult,
|
||||
RESTPostAPIWebhookWithTokenWaitResult
|
||||
} from '@biscuitland/common';
|
||||
import type { Session } from '..';
|
||||
|
||||
export class WebhookManager {
|
||||
readonly session!: Session;
|
||||
constructor(session: Session) {
|
||||
Object.defineProperty(this, 'session', {
|
||||
value: session,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
create(channelId: string, body: RESTPostAPIChannelWebhookJSONBody) {
|
||||
return this.session.api.channels(channelId).webhooks.post({ body });
|
||||
}
|
||||
|
||||
getChannelWebhooks(channelId: string) {
|
||||
return this.session.api.channels(channelId).webhooks.get();
|
||||
}
|
||||
|
||||
getGuildWebhooks(guildId: string) {
|
||||
return this.session.api.guilds(guildId).webhooks.get();
|
||||
}
|
||||
|
||||
get(webhookdId: string, token?: string) {
|
||||
if (!token?.length) return this.session.api.webhooks(webhookdId).get();
|
||||
return this.session.api.webhooks(webhookdId)(token).get();
|
||||
}
|
||||
|
||||
edit(webhookId: string, body: RESTPatchAPIWebhookJSONBody): Promise<RESTPatchAPIWebhookResult>;
|
||||
edit(webhookId: string, body: RESTPatchAPIWebhookWithTokenJSONBody, token: string): Promise<RESTPatchAPIWebhookWithTokenResult>;
|
||||
edit(webhookId: string, body: RESTPatchAPIWebhookJSONBody, token?: string) {
|
||||
if (!token?.length) {
|
||||
return this.session.api.webhooks(webhookId).patch({ body });
|
||||
}
|
||||
return this.session.api.webhooks(webhookId)(token).patch({ body });
|
||||
}
|
||||
|
||||
delete(webhookdId: string, token?: string) {
|
||||
if (!token?.length) return this.session.api.webhooks(webhookdId).delete();
|
||||
return this.session.api.webhooks(webhookdId)(token).delete();
|
||||
}
|
||||
|
||||
execute(
|
||||
webhookId: string,
|
||||
token: string,
|
||||
body: RESTPostAPIWebhookWithTokenJSONBody,
|
||||
query: RESTPostAPIWebhookWithTokenWaitQuery
|
||||
): Promise<RESTPostAPIWebhookWithTokenWaitResult>;
|
||||
execute(webhookId: string, token: string, body: RESTPostAPIWebhookWithTokenJSONBody, query?: RESTPostAPIWebhookWithTokenQuery) {
|
||||
return this.session.api.webhooks(webhookId)(token).post({
|
||||
body,
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
executeGithub(
|
||||
webhookId: string,
|
||||
token: string,
|
||||
body: RESTPostAPIWebhookWithTokenJSONBody,
|
||||
query: Identify<RESTPostAPIWebhookWithTokenGitHubQuery & { wait: true }>
|
||||
): Promise<RESTPostAPIWebhookWithTokenGitHubWaitResult>;
|
||||
executeGithub(
|
||||
webhookId: string,
|
||||
token: string,
|
||||
body: RESTPostAPIWebhookWithTokenJSONBody,
|
||||
query?: RESTPostAPIWebhookWithTokenGitHubQuery
|
||||
) {
|
||||
return this.session.api.webhooks(webhookId)(token).github.post({
|
||||
body,
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
executeSlack(
|
||||
webhookId: string,
|
||||
token: string,
|
||||
body: RESTPostAPIWebhookWithTokenJSONBody,
|
||||
query: Identify<RESTPostAPIWebhookWithTokenSlackQuery & { wait: true }>
|
||||
): Promise<RESTPostAPIWebhookWithTokenSlackWaitResult>;
|
||||
executeSlack(webhookId: string, token: string, body: RESTPostAPIWebhookWithTokenJSONBody, query?: RESTPostAPIWebhookWithTokenSlackQuery) {
|
||||
return this.session.api.webhooks(webhookId)(token).slack.post({
|
||||
body,
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
getMessage(webhookId: string, token: string, messageId: string, query?: { thread_id?: string }) {
|
||||
return this.session.api.webhooks(webhookId)(token).messages(messageId).get({
|
||||
query
|
||||
});
|
||||
}
|
||||
|
||||
editMessage(
|
||||
webhookId: string,
|
||||
token: string,
|
||||
messageId: string,
|
||||
body: RESTPatchAPIWebhookWithTokenMessageJSONBody,
|
||||
query?: { thread_id?: string }
|
||||
) {
|
||||
return this.session.api.webhooks(webhookId)(token).messages(messageId).patch({ query, body });
|
||||
}
|
||||
|
||||
deleteMessage(webhookId: string, token: string, messageId: string, query?: { thread_id?: string }) {
|
||||
return this.session.api.webhooks(webhookId)(token).messages(messageId).delete({ query });
|
||||
}
|
||||
}
|
||||
|
||||
export type RESTPostAPIWebhookWithTokenWaitQuery = Identify<RESTPostAPIWebhookWithTokenQuery & { wait: true }>;
|
110
packages/core/src/session.ts
Normal file
110
packages/core/src/session.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import type { BiscuitRESTOptions, CDNRoutes, Routes } from '@biscuitland/rest';
|
||||
import { CDN, BiscuitREST, Router } from '@biscuitland/rest';
|
||||
import { When } from '@biscuitland/common';
|
||||
import { EventEmitter2 } from 'eventemitter2';
|
||||
import { MainManager, getBotIdFromToken } from '.';
|
||||
import { GatewayManager, CreateGatewayManagerOptions, GatewayEvents } from '@biscuitland/ws';
|
||||
import { GatewayIntentBits } from '@biscuitland/common';
|
||||
import { actionHandler, Handler } from './events/handler';
|
||||
|
||||
export class Session<On extends boolean = boolean> extends EventEmitter2 {
|
||||
constructor(public options: BiscuitOptions) {
|
||||
super();
|
||||
this.rest = this.createRest(this.options.rest);
|
||||
this.api = new Router(this.rest).createProxy();
|
||||
this.cdn = CDN.createProxy();
|
||||
this.managers = new MainManager(this);
|
||||
}
|
||||
rest: BiscuitREST;
|
||||
api: Routes;
|
||||
cdn: CDNRoutes;
|
||||
managers: MainManager;
|
||||
gateway!: When<On, GatewayManager>;
|
||||
private _applicationId?: string;
|
||||
private _botId?: string;
|
||||
|
||||
override on<K extends keyof GatewayEvents>(event: `${K}`, func: Handler[K]): this;
|
||||
override on<K extends string>(event: `${K}`, func: (...args: unknown[]) => unknown): this {
|
||||
const ev = super.on(event, func);
|
||||
|
||||
// @ts-expect-error Eventemitter can sometimes return a listener
|
||||
return ev.emitter ? ev.emitter : ev;
|
||||
}
|
||||
override off<K extends keyof GatewayEvents>(event: `${K}`, func: Handler[K]): this;
|
||||
override off<K extends keyof GatewayEvents>(event: `${K}`, func: (...args: unknown[]) => unknown): this {
|
||||
return super.off(event, func);
|
||||
}
|
||||
|
||||
override once<K extends keyof GatewayEvents>(event: `${K}`, func: Handler[K]): this;
|
||||
override once<K extends string>(event: `${K}`, func: (...args: unknown[]) => unknown): this {
|
||||
const ev = super.on(event, func);
|
||||
|
||||
// @ts-expect-error Eventemitter can sometimes return a listener
|
||||
return ev.emitter ? ev.emitter : ev;
|
||||
}
|
||||
|
||||
override emit<K extends keyof GatewayEvents>(event: `${K}`, ...params: Parameters<Handler[K]>): boolean;
|
||||
override emit<K extends string>(event: `${K}`, ...params: unknown[]): boolean {
|
||||
return super.emit(event, ...params);
|
||||
}
|
||||
|
||||
set botId(id: string) {
|
||||
this._botId = id;
|
||||
}
|
||||
|
||||
set applicationId(id: string) {
|
||||
this._applicationId = id;
|
||||
}
|
||||
|
||||
get botId() {
|
||||
return this._botId ?? getBotIdFromToken(this.options.token);
|
||||
}
|
||||
|
||||
get applicationId() {
|
||||
return this._applicationId ?? this.botId;
|
||||
}
|
||||
|
||||
private createRest(rest: any) {
|
||||
if (!rest) {
|
||||
return new BiscuitREST({
|
||||
...this.options.defaultRestOptions,
|
||||
token: this.options.token
|
||||
});
|
||||
}
|
||||
|
||||
if (rest instanceof BiscuitREST || rest.cRest) {
|
||||
return rest;
|
||||
}
|
||||
|
||||
throw new Error('[CORE] REST not found');
|
||||
}
|
||||
|
||||
async start() {
|
||||
// alias fixed `this` on handlePayload
|
||||
const ctx = this as Session<true>;
|
||||
|
||||
ctx.gateway = new GatewayManager({
|
||||
token: this.options.token,
|
||||
intents: this.options.intents ?? 0,
|
||||
connection: this.options.defaultGatewayOptions?.connection ?? (await this.rest.get('/gateway/bot')),
|
||||
async handlePayload(shard, data) {
|
||||
const { t, d } = data;
|
||||
if (!(t && d)) return;
|
||||
actionHandler([ctx, { t, d }, shard]);
|
||||
},
|
||||
...this.options.defaultGatewayOptions
|
||||
});
|
||||
|
||||
await ctx.gateway.spawnShards();
|
||||
}
|
||||
}
|
||||
|
||||
export type HandlePayload = Pick<CreateGatewayManagerOptions, 'handlePayload'>['handlePayload'];
|
||||
|
||||
export interface BiscuitOptions {
|
||||
token: string;
|
||||
intents: number | GatewayIntentBits;
|
||||
rest?: BiscuitREST;
|
||||
defaultRestOptions?: Partial<BiscuitRESTOptions>;
|
||||
defaultGatewayOptions?: Omit<CreateGatewayManagerOptions, 'token' | 'intents'>;
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
/** snowflake type */
|
||||
export type Snowflake = string;
|
||||
|
||||
/** Discord epoch */
|
||||
export const DiscordEpoch = 14200704e5;
|
||||
|
||||
/** utilities for Snowflakes */
|
||||
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||
export const Snowflake = {
|
||||
snowflakeToTimestamp(id: Snowflake): number {
|
||||
return (Number(id) >> 22) + DiscordEpoch;
|
||||
},
|
||||
};
|
@ -1,34 +0,0 @@
|
||||
// STRUCTURES
|
||||
export * from './structures/application';
|
||||
export * from './structures/attachment';
|
||||
export * from './structures/automod';
|
||||
export * from './structures/base';
|
||||
export * from './structures/embed';
|
||||
export * from './structures/emojis';
|
||||
export * from './structures/scheduled-events';
|
||||
export * from './structures/integration';
|
||||
export * from './structures/invite';
|
||||
export * from './structures/members';
|
||||
export * from './structures/message';
|
||||
export * from './structures/message-reaction';
|
||||
export * from './structures/special/interaction-options';
|
||||
export * from './structures/special/permissions';
|
||||
export * from './structures/presence';
|
||||
export * from './structures/role';
|
||||
export * from './structures/stage-instance';
|
||||
export * from './structures/sticker';
|
||||
export * from './structures/user';
|
||||
export * from './structures/webhook';
|
||||
export * from './structures/welcome';
|
||||
|
||||
// INTERACTIONS
|
||||
export * from './structures/interactions';
|
||||
|
||||
// CHANNELS
|
||||
export * from './structures/channels';
|
||||
|
||||
// COMPONENTS
|
||||
export * from './structures/components';
|
||||
|
||||
// GUILDS
|
||||
export * from './structures/guilds';
|
@ -1,112 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordApplication,
|
||||
DiscordInstallParams,
|
||||
DiscordTeam,
|
||||
DiscordUser,
|
||||
TeamMembershipStates,
|
||||
} from '@biscuitland/api-types';
|
||||
import { User } from './user';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type SummaryDeprecated = '';
|
||||
|
||||
/**
|
||||
* Discord team that holds members
|
||||
*/
|
||||
export interface Team {
|
||||
/** a hash of the image of the team's icon */
|
||||
icon?: string;
|
||||
/** the unique id of the team */
|
||||
id: string;
|
||||
/** the members of the team */
|
||||
members: TeamMember[];
|
||||
/** user id of the current team owner */
|
||||
ownerUserId: string;
|
||||
/** team name */
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface TeamMember {
|
||||
/** the user's membership state on the team */
|
||||
membershipState: TeamMembershipStates;
|
||||
permissions: '*'[];
|
||||
teamId: string;
|
||||
user: Partial<User> &
|
||||
Pick<User, 'avatarHash' | 'discriminator' | 'id' | 'username'>;
|
||||
}
|
||||
|
||||
// NewTeam create a new Team object for discord applications
|
||||
export function NewTeam(session: Session, data: DiscordTeam): Team {
|
||||
return {
|
||||
icon: data.icon ? data.icon : undefined,
|
||||
id: data.id,
|
||||
members: data.members.map(member => {
|
||||
return {
|
||||
membershipState: member.membership_state,
|
||||
permissions: member.permissions,
|
||||
teamId: member.team_id,
|
||||
user: new User(session, member.user),
|
||||
};
|
||||
}),
|
||||
ownerUserId: data.owner_user_id,
|
||||
name: data.name,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/application#application-object
|
||||
*/
|
||||
export class Application implements Model {
|
||||
constructor(session: Session, data: DiscordApplication) {
|
||||
this.id = data.id;
|
||||
this.session = session;
|
||||
|
||||
this.name = data.name;
|
||||
this.icon = data.icon ?? undefined;
|
||||
this.description = data.description;
|
||||
this.rpcOrigins = data.rpc_origins;
|
||||
this.botPublic = data.bot_public;
|
||||
this.botRequireCodeGrant = data.bot_require_code_grant;
|
||||
this.termsOfServiceURL = data.terms_of_service_url;
|
||||
this.privacyPolicyURL = data.privacy_policy_url;
|
||||
this.owner = data.owner
|
||||
? new User(session, data.owner as DiscordUser)
|
||||
: undefined;
|
||||
this.summary = '';
|
||||
this.verifyKey = data.verify_key;
|
||||
this.team = data.team ? NewTeam(session, data.team) : undefined;
|
||||
this.guildId = data.guild_id;
|
||||
this.coverImage = data.cover_image;
|
||||
this.tags = data.tags;
|
||||
this.installParams = data.install_params;
|
||||
this.customInstallURL = data.custom_install_url;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
icon?: string;
|
||||
description: string;
|
||||
rpcOrigins?: string[];
|
||||
botPublic: boolean;
|
||||
botRequireCodeGrant: boolean;
|
||||
termsOfServiceURL?: string;
|
||||
privacyPolicyURL?: string;
|
||||
owner?: Partial<User>;
|
||||
summary: SummaryDeprecated;
|
||||
verifyKey: string;
|
||||
team?: Team;
|
||||
guildId?: Snowflake;
|
||||
primarySkuId?: Snowflake;
|
||||
slug?: string;
|
||||
coverImage?: string;
|
||||
flags?: number;
|
||||
tags?: string[];
|
||||
installParams?: DiscordInstallParams;
|
||||
customInstallURL?: string;
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { DiscordAttachment } from '@biscuitland/api-types';
|
||||
|
||||
/**
|
||||
* Represents an attachment
|
||||
* @link https://discord.com/developers/docs/resources/channel#attachment-object
|
||||
*/
|
||||
export class Attachment implements Model {
|
||||
constructor(session: Session, data: DiscordAttachment) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
|
||||
this.contentType = data.content_type ? data.content_type : undefined;
|
||||
this.attachment = data.url;
|
||||
this.proxyUrl = data.proxy_url;
|
||||
this.name = data.filename;
|
||||
this.size = data.size;
|
||||
this.height = data.height ? data.height : undefined;
|
||||
this.width = data.width ? data.width : undefined;
|
||||
this.ephemeral = !!data.ephemeral;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
|
||||
contentType?: string;
|
||||
attachment: string;
|
||||
proxyUrl: string;
|
||||
name: string;
|
||||
size: number;
|
||||
height?: number;
|
||||
width?: number;
|
||||
ephemeral: boolean;
|
||||
}
|
@ -1,215 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type {
|
||||
AutoModerationActionType,
|
||||
AutoModerationEventTypes,
|
||||
AutoModerationTriggerTypes,
|
||||
DiscordAutoModerationRule,
|
||||
DiscordAutoModerationRuleTriggerMetadataPresets,
|
||||
DiscordAutoModerationActionExecution } from '@biscuitland/api-types';
|
||||
import {
|
||||
AUTO_MODERATION_RULES
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
export interface AutoModerationRuleTriggerMetadata {
|
||||
keywordFilter?: string[];
|
||||
presets?: DiscordAutoModerationRuleTriggerMetadataPresets[];
|
||||
allowList?: string[];
|
||||
}
|
||||
|
||||
export interface ActionMetadata {
|
||||
channelId: Snowflake;
|
||||
durationSeconds: number;
|
||||
}
|
||||
|
||||
export interface AutoModerationAction {
|
||||
type: AutoModerationActionType;
|
||||
metadata: ActionMetadata;
|
||||
}
|
||||
|
||||
/** @link https://discord.com/developers/docs/resources/auto-moderation#create-auto-moderation-rule-json-params */
|
||||
export interface CreateAutoModerationRule {
|
||||
name: string;
|
||||
eventType: 1;
|
||||
triggerType: AutoModerationTriggerTypes;
|
||||
triggerMetadata?: AutoModerationRuleTriggerMetadata;
|
||||
actions: AutoModerationAction[];
|
||||
enabled?: boolean;
|
||||
exemptRoles?: Snowflake[];
|
||||
exemptChannels?: Snowflake[];
|
||||
}
|
||||
|
||||
export class AutoModerationRule implements Model {
|
||||
constructor(session: Session, data: DiscordAutoModerationRule) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.guildId = data.guild_id;
|
||||
this.name = data.name;
|
||||
this.creatorId = data.creator_id;
|
||||
this.eventType = data.event_type;
|
||||
this.triggerType = data.trigger_type;
|
||||
this.triggerMetadata = {
|
||||
keywordFilter: data.trigger_metadata.keyword_filter,
|
||||
presets: data.trigger_metadata.presets,
|
||||
allowList: data.trigger_metadata.allow_list
|
||||
};
|
||||
this.actions = data.actions.map(action =>
|
||||
Object.create({
|
||||
type: action.type,
|
||||
metadata: {
|
||||
channelId: action.metadata.channel_id,
|
||||
durationSeconds: action.metadata.duration_seconds
|
||||
}
|
||||
})
|
||||
);
|
||||
this.enabled = !!data.enabled;
|
||||
this.exemptRoles = data.exempt_roles;
|
||||
this.exemptChannels = data.exempt_channels;
|
||||
}
|
||||
|
||||
session: Session;
|
||||
id: Snowflake;
|
||||
guildId: Snowflake;
|
||||
name: string;
|
||||
creatorId: Snowflake;
|
||||
eventType: AutoModerationEventTypes;
|
||||
triggerType: AutoModerationTriggerTypes;
|
||||
triggerMetadata: AutoModerationRuleTriggerMetadata;
|
||||
actions: AutoModerationAction[];
|
||||
enabled: boolean;
|
||||
exemptRoles: Snowflake[];
|
||||
exemptChannels: Snowflake[];
|
||||
|
||||
async getRules(
|
||||
ruleId?: Snowflake
|
||||
): Promise<AutoModerationRule | AutoModerationRule[]> {
|
||||
const request = await this.session.rest.get<
|
||||
DiscordAutoModerationRule | DiscordAutoModerationRule[]
|
||||
>(AUTO_MODERATION_RULES(this.guildId, ruleId));
|
||||
if (Array.isArray(request)) {
|
||||
return request.map(
|
||||
amr => new AutoModerationRule(this.session, amr)
|
||||
);
|
||||
}
|
||||
return new AutoModerationRule(this.session, request);
|
||||
}
|
||||
|
||||
async createRule(options: CreateAutoModerationRule) {
|
||||
const request = await this.session.rest.post<DiscordAutoModerationRule>(
|
||||
AUTO_MODERATION_RULES(this.guildId),
|
||||
{
|
||||
name: options.name,
|
||||
event_type: options.eventType,
|
||||
trigger_type: options.triggerType,
|
||||
trigger_metadata: options.triggerMetadata,
|
||||
actions: options.actions
|
||||
? options.actions.map(x =>
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
type: x.type,
|
||||
metadata: {
|
||||
channel_id: x.metadata.channelId,
|
||||
duration_seconds:
|
||||
x.metadata.durationSeconds
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
enabled: !!options.enabled,
|
||||
exempt_roles: options.exemptRoles,
|
||||
exempt_channels: options.exemptChannels
|
||||
}
|
||||
);
|
||||
return new AutoModerationRule(this.session, request);
|
||||
}
|
||||
|
||||
async editRule(
|
||||
ruleId = this.id,
|
||||
options: Partial<CreateAutoModerationRule>
|
||||
) {
|
||||
const request = await this.session.rest.patch<
|
||||
DiscordAutoModerationRule
|
||||
>(AUTO_MODERATION_RULES(this.guildId, ruleId), {
|
||||
name: options.name,
|
||||
event_type: options.eventType,
|
||||
trigger_type: options.triggerType,
|
||||
trigger_metadata: options.triggerMetadata,
|
||||
actions: options.actions
|
||||
? options.actions.map(x =>
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
type: x.type,
|
||||
metadata: {
|
||||
channel_id: x.metadata.channelId,
|
||||
duration_seconds: x.metadata.durationSeconds
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
enabled: !!options.enabled,
|
||||
exempt_roles: options.exemptRoles,
|
||||
exempt_channels: options.exemptChannels
|
||||
});
|
||||
return new AutoModerationRule(this.session, request);
|
||||
}
|
||||
|
||||
async deleteRule(ruleId = this.id): Promise<void> {
|
||||
await this.session.rest.delete(
|
||||
AUTO_MODERATION_RULES(this.guildId, ruleId)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export class AutoModerationExecution {
|
||||
constructor(session: Session, data: DiscordAutoModerationActionExecution) {
|
||||
this.session = session;
|
||||
this.guildId = data.guild_id;
|
||||
this.action = {
|
||||
type: data.action.type,
|
||||
metadata: {
|
||||
channelId: data.action.metadata.channel_id as string,
|
||||
durationSeconds: data.action.metadata.duration_seconds as number
|
||||
}
|
||||
};
|
||||
this.ruleId = data.rule_id;
|
||||
this.ruleTriggerType = data.rule_trigger_type;
|
||||
this.userId = data.user_id;
|
||||
this.content = data.content;
|
||||
if (data.channel_id) {
|
||||
this.channelId = data.channel_id;
|
||||
}
|
||||
if (data.message_id) {
|
||||
this.messageId = data.message_id;
|
||||
}
|
||||
if (data.alert_system_message_id) {
|
||||
this.alertSystemMessageId = data.alert_system_message_id;
|
||||
}
|
||||
|
||||
if (data.matched_keyword) {
|
||||
this.matchedKeyword = data.matched_keyword;
|
||||
}
|
||||
|
||||
if (data.matched_content) {
|
||||
this.matched_content = data.matched_content;
|
||||
}
|
||||
}
|
||||
|
||||
session: Session;
|
||||
guildId: Snowflake;
|
||||
action: AutoModerationAction;
|
||||
ruleId: Snowflake;
|
||||
ruleTriggerType: AutoModerationTriggerTypes;
|
||||
userId: Snowflake;
|
||||
channelId?: Snowflake;
|
||||
messageId?: Snowflake;
|
||||
alertSystemMessageId?: Snowflake;
|
||||
content?: string;
|
||||
matchedKeyword?: string;
|
||||
matched_content?: string;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
|
||||
/**
|
||||
* Represents a Discord data model
|
||||
*/
|
||||
export interface Model {
|
||||
/** id of the model */
|
||||
id: Snowflake;
|
||||
/** reference to the client that instantiated the model */
|
||||
session: Session;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,280 +0,0 @@
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordComponent,
|
||||
DiscordInputTextComponent,
|
||||
TextStyles,
|
||||
} from '@biscuitland/api-types';
|
||||
import { Emoji } from './emojis';
|
||||
import { ButtonStyles, MessageComponentTypes } from '@biscuitland/api-types';
|
||||
|
||||
export class BaseComponent {
|
||||
constructor(type: MessageComponentTypes) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
type: MessageComponentTypes;
|
||||
|
||||
isActionRow(): this is ActionRowComponent {
|
||||
return this.type === MessageComponentTypes.ActionRow;
|
||||
}
|
||||
|
||||
isButton(): this is ButtonComponent {
|
||||
return this.type === MessageComponentTypes.Button;
|
||||
}
|
||||
|
||||
isSelectMenu(): this is SelectMenuComponent {
|
||||
return this.type === MessageComponentTypes.SelectMenu;
|
||||
}
|
||||
|
||||
isTextInput(): this is TextInputComponent {
|
||||
return this.type === MessageComponentTypes.InputText;
|
||||
}
|
||||
}
|
||||
|
||||
/** Action Row Component */
|
||||
export interface ActionRowComponent {
|
||||
type: MessageComponentTypes.ActionRow;
|
||||
components: Exclude<Component, ActionRowComponent>[];
|
||||
}
|
||||
|
||||
/** All Components */
|
||||
export type Component =
|
||||
| ActionRowComponent
|
||||
| ButtonComponent
|
||||
| LinkButtonComponent
|
||||
| SelectMenuComponent
|
||||
| TextInputComponent;
|
||||
|
||||
/** Button Component */
|
||||
export type ClassicButton = Exclude<ButtonStyles, ButtonStyles.Link>;
|
||||
|
||||
export type ComponentsWithoutRow = Exclude<Component, ActionRowComponent>;
|
||||
|
||||
export interface ButtonComponent {
|
||||
type: MessageComponentTypes.Button;
|
||||
style: ClassicButton;
|
||||
label?: string;
|
||||
emoji?: Emoji;
|
||||
customId?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/** Link Button Component */
|
||||
export interface LinkButtonComponent {
|
||||
type: MessageComponentTypes.Button;
|
||||
style: ButtonStyles.Link;
|
||||
label?: string;
|
||||
url: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/** Select Menu Component */
|
||||
export interface SelectMenuComponent {
|
||||
type: MessageComponentTypes.SelectMenu;
|
||||
customId: string;
|
||||
options?: SelectMenuOption[];
|
||||
placeholder?: string;
|
||||
minValue?: number;
|
||||
maxValue?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/** Text Input Component */
|
||||
export interface TextInputComponent {
|
||||
type: MessageComponentTypes.InputText;
|
||||
customId: string;
|
||||
style: TextStyles;
|
||||
label: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
required?: boolean;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export interface SelectMenuOption {
|
||||
label: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
emoji?: Emoji;
|
||||
default?: boolean;
|
||||
}
|
||||
|
||||
export class Button extends BaseComponent implements ButtonComponent {
|
||||
constructor(session: Session, data: DiscordComponent) {
|
||||
super(data.type);
|
||||
|
||||
this.session = session;
|
||||
this.type = data.type as MessageComponentTypes.Button;
|
||||
this.customId = data.custom_id;
|
||||
this.label = data.label;
|
||||
this.style = data.style as ClassicButton;
|
||||
this.disabled = data.disabled;
|
||||
|
||||
if (data.emoji) {
|
||||
this.emoji = new Emoji(session, data.emoji);
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
override type: MessageComponentTypes.Button;
|
||||
customId?: string;
|
||||
label?: string;
|
||||
style: ClassicButton;
|
||||
disabled?: boolean;
|
||||
emoji?: Emoji;
|
||||
}
|
||||
|
||||
export class LinkButton extends BaseComponent implements LinkButtonComponent {
|
||||
constructor(session: Session, data: DiscordComponent) {
|
||||
super(data.type);
|
||||
|
||||
this.session = session;
|
||||
this.type = data.type as MessageComponentTypes.Button;
|
||||
this.url = data.url!;
|
||||
this.label = data.label;
|
||||
this.style = data.style as number;
|
||||
this.disabled = data.disabled;
|
||||
|
||||
if (data.emoji) {
|
||||
this.emoji = new Emoji(session, data.emoji);
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
override type: MessageComponentTypes.Button;
|
||||
url: string;
|
||||
label?: string;
|
||||
style: ButtonStyles.Link;
|
||||
disabled?: boolean;
|
||||
emoji?: Emoji;
|
||||
}
|
||||
|
||||
export class SelectMenu extends BaseComponent implements SelectMenuComponent {
|
||||
constructor(session: Session, data: DiscordComponent) {
|
||||
super(data.type);
|
||||
|
||||
this.session = session;
|
||||
this.type = data.type as MessageComponentTypes.SelectMenu;
|
||||
this.customId = data.custom_id!;
|
||||
|
||||
if ('options' in data) {
|
||||
this.options = data.options?.map(option => {
|
||||
return {
|
||||
label: option.label,
|
||||
description: option.description,
|
||||
emoji: option.emoji ? new Emoji(session, option.emoji) : undefined,
|
||||
value: option.value,
|
||||
} as SelectMenuOption;
|
||||
});
|
||||
}
|
||||
|
||||
this.placeholder = data.placeholder;
|
||||
this.minValues = data.min_values;
|
||||
this.maxValues = data.max_values;
|
||||
this.disabled = data.disabled;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
override type: MessageComponentTypes.SelectMenu;
|
||||
customId: string;
|
||||
options?: SelectMenuOption[];
|
||||
placeholder?: string;
|
||||
minValues?: number;
|
||||
maxValues?: number;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export class TextInput extends BaseComponent implements TextInputComponent {
|
||||
constructor(session: Session, data: DiscordInputTextComponent) {
|
||||
super(data.type);
|
||||
|
||||
this.session = session;
|
||||
this.type = data.type as MessageComponentTypes.InputText;
|
||||
this.customId = data.custom_id!;
|
||||
this.label = data.label!;
|
||||
this.style = data.style as TextStyles;
|
||||
|
||||
this.placeholder = data.placeholder;
|
||||
this.value = data.value;
|
||||
|
||||
this.minLength = data.min_length;
|
||||
this.maxLength = data.max_length;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
override type: MessageComponentTypes.InputText;
|
||||
style: TextStyles;
|
||||
customId: string;
|
||||
label: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
export class ActionRow extends BaseComponent implements ActionRowComponent {
|
||||
constructor(session: Session, data: DiscordComponent) {
|
||||
super(data.type);
|
||||
|
||||
this.session = session;
|
||||
this.type = data.type as MessageComponentTypes.ActionRow;
|
||||
this.components = data.components!.map(component => {
|
||||
switch (component.type) {
|
||||
case MessageComponentTypes.Button:
|
||||
if (component.style === ButtonStyles.Link) {
|
||||
return new LinkButton(session, component);
|
||||
}
|
||||
return new Button(session, component);
|
||||
case MessageComponentTypes.SelectMenu:
|
||||
case MessageComponentTypes.RoleSelect:
|
||||
case MessageComponentTypes.UserSelect:
|
||||
case MessageComponentTypes.MentionableSelect:
|
||||
case MessageComponentTypes.ChannelSelect:
|
||||
return new SelectMenu(session, component);
|
||||
case MessageComponentTypes.InputText:
|
||||
return new TextInput(
|
||||
session,
|
||||
component as DiscordInputTextComponent
|
||||
);
|
||||
case MessageComponentTypes.ActionRow:
|
||||
throw new Error(
|
||||
'Cannot have an action row inside an action row'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
override type: MessageComponentTypes.ActionRow;
|
||||
components: ComponentsWithoutRow[];
|
||||
}
|
||||
|
||||
export class ComponentFactory {
|
||||
/**
|
||||
* Component factory
|
||||
* @internal
|
||||
*/
|
||||
static from(session: Session, component: DiscordComponent): Component {
|
||||
switch (component.type) {
|
||||
case MessageComponentTypes.ActionRow:
|
||||
return new ActionRow(session, component);
|
||||
case MessageComponentTypes.Button:
|
||||
if (component.style === ButtonStyles.Link) {
|
||||
return new LinkButton(session, component);
|
||||
}
|
||||
return new Button(session, component);
|
||||
case MessageComponentTypes.SelectMenu:
|
||||
case MessageComponentTypes.RoleSelect:
|
||||
case MessageComponentTypes.UserSelect:
|
||||
case MessageComponentTypes.MentionableSelect:
|
||||
case MessageComponentTypes.ChannelSelect:
|
||||
return new SelectMenu(session, component);
|
||||
case MessageComponentTypes.InputText:
|
||||
return new TextInput(
|
||||
session,
|
||||
component as DiscordInputTextComponent
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
import type { DiscordEmbed, EmbedTypes } from '@biscuitland/api-types';
|
||||
|
||||
export interface Embed {
|
||||
title?: string;
|
||||
timestamp?: string;
|
||||
type?: EmbedTypes;
|
||||
url?: string;
|
||||
color?: number;
|
||||
description?: string;
|
||||
author?: {
|
||||
name: string;
|
||||
iconURL?: string;
|
||||
proxyIconURL?: string;
|
||||
url?: string;
|
||||
};
|
||||
footer?: {
|
||||
text: string;
|
||||
iconURL?: string;
|
||||
proxyIconURL?: string;
|
||||
};
|
||||
fields?: {
|
||||
name: string;
|
||||
value: string;
|
||||
inline?: boolean;
|
||||
}[];
|
||||
thumbnail?: {
|
||||
url: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
video?: {
|
||||
url?: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
image?: {
|
||||
url: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
provider?: {
|
||||
url?: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function NewEmbed(data: Embed): DiscordEmbed {
|
||||
return {
|
||||
title: data.title,
|
||||
timestamp: data.timestamp,
|
||||
type: data.type,
|
||||
url: data.url,
|
||||
color: data.color,
|
||||
description: data.description,
|
||||
author: data.author && {
|
||||
name: data.author.name,
|
||||
url: data.author.url,
|
||||
icon_url: data.author.iconURL,
|
||||
proxy_icon_url: data.author.proxyIconURL,
|
||||
},
|
||||
footer: data.footer && {
|
||||
text: data.footer.text,
|
||||
icon_url: data.footer.iconURL,
|
||||
proxy_icon_url: data.footer.proxyIconURL,
|
||||
},
|
||||
fields: data.fields?.map(f => {
|
||||
return {
|
||||
name: f.name,
|
||||
value: f.value,
|
||||
inline: f.inline,
|
||||
};
|
||||
}),
|
||||
thumbnail: data.thumbnail && {
|
||||
url: data.thumbnail.url,
|
||||
proxy_url: data.thumbnail.proxyURL,
|
||||
width: data.thumbnail.width,
|
||||
height: data.thumbnail.height,
|
||||
},
|
||||
video: {
|
||||
url: data.video?.url,
|
||||
proxy_url: data.video?.proxyURL,
|
||||
width: data.video?.width,
|
||||
height: data.video?.height,
|
||||
},
|
||||
image: data.image && {
|
||||
url: data.image.url,
|
||||
proxy_url: data.image.proxyURL,
|
||||
width: data.image.width,
|
||||
height: data.image.height,
|
||||
},
|
||||
provider: {
|
||||
url: data.provider?.url,
|
||||
name: data.provider?.name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const embed = NewEmbed;
|
||||
|
||||
export function NewEmbedR(data: DiscordEmbed): Embed {
|
||||
return {
|
||||
title: data.title,
|
||||
timestamp: data.timestamp,
|
||||
type: data.type,
|
||||
url: data.url,
|
||||
color: data.color,
|
||||
description: data.description,
|
||||
author: data.author && {
|
||||
name: data.author.name,
|
||||
url: data.author.url,
|
||||
iconURL: data.author.icon_url,
|
||||
proxyIconURL: data.author.proxy_icon_url,
|
||||
},
|
||||
footer: data.footer && {
|
||||
text: data.footer.text,
|
||||
iconURL: data.footer.icon_url,
|
||||
proxyIconURL: data.footer.proxy_icon_url,
|
||||
},
|
||||
fields: data.fields?.map(f => {
|
||||
return {
|
||||
name: f.name,
|
||||
value: f.value,
|
||||
inline: f.inline,
|
||||
};
|
||||
}),
|
||||
thumbnail: data.thumbnail && {
|
||||
url: data.thumbnail.url,
|
||||
proxyURL: data.thumbnail.proxy_url,
|
||||
width: data.thumbnail.width,
|
||||
height: data.thumbnail.height,
|
||||
},
|
||||
video: {
|
||||
url: data.video?.url,
|
||||
proxyURL: data.video?.proxy_url,
|
||||
width: data.video?.width,
|
||||
height: data.video?.height,
|
||||
},
|
||||
image: data.image && {
|
||||
url: data.image.url,
|
||||
proxyURL: data.image.proxy_url,
|
||||
width: data.image.width,
|
||||
height: data.image.height,
|
||||
},
|
||||
provider: {
|
||||
url: data.provider?.url,
|
||||
name: data.provider?.name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
export const embed_ = NewEmbedR;
|
@ -1,85 +0,0 @@
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { ModifyGuildEmoji } from './guilds';
|
||||
import { Guild } from './guilds';
|
||||
import { User } from './user';
|
||||
import type { DiscordEmoji } from '@biscuitland/api-types';
|
||||
import { EMOJI_URL, GUILD_EMOJIS } from '@biscuitland/api-types';
|
||||
|
||||
export class Emoji implements Partial<Model> {
|
||||
constructor(session: Session, data: DiscordEmoji) {
|
||||
this.id = data.id;
|
||||
this.name = data.name;
|
||||
this.animated = !!data.animated;
|
||||
this.available = !!data.available;
|
||||
this.requireColons = !!data.require_colons;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
readonly id?: Snowflake;
|
||||
readonly session: Session;
|
||||
|
||||
name?: string;
|
||||
animated: boolean;
|
||||
available: boolean;
|
||||
requireColons: boolean;
|
||||
}
|
||||
|
||||
export class GuildEmoji extends Emoji implements Model {
|
||||
constructor(session: Session, data: DiscordEmoji, guildId: Snowflake) {
|
||||
super(session, data);
|
||||
this.guildId = guildId;
|
||||
this.roles = data.roles;
|
||||
this.user = data.user ? new User(this.session, data.user) : undefined;
|
||||
this.managed = !!data.managed;
|
||||
this.id = super.id!;
|
||||
}
|
||||
|
||||
guildId: Snowflake;
|
||||
roles?: Snowflake[];
|
||||
user?: User;
|
||||
managed?: boolean;
|
||||
|
||||
// id cannot be null in a GuildEmoji
|
||||
override id: Snowflake;
|
||||
|
||||
async edit(options: ModifyGuildEmoji): Promise<GuildEmoji> {
|
||||
const emoji = await Guild.prototype.editEmoji.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.id,
|
||||
options
|
||||
);
|
||||
|
||||
return emoji;
|
||||
}
|
||||
|
||||
async delete(reason?: string): Promise<GuildEmoji> {
|
||||
await Guild.prototype.deleteEmoji.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.id,
|
||||
reason
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async fetchAuthor(): Promise<User | null> {
|
||||
const emoji = await this.session.rest.get<DiscordEmoji>(GUILD_EMOJIS(this.guildId, this.id));
|
||||
|
||||
if (emoji.user) { return new User(this.session, emoji.user); }
|
||||
return null;
|
||||
}
|
||||
|
||||
setName(name: string): Promise<GuildEmoji> {
|
||||
return this.edit({ name });
|
||||
}
|
||||
|
||||
get url(): string {
|
||||
return EMOJI_URL(this.id, this.animated);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<${this.animated ? 'a' : ''}:${this.name}:${this.id}>`;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,87 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordIntegration,
|
||||
IntegrationExpireBehaviors,
|
||||
} from '@biscuitland/api-types';
|
||||
import { User } from './user';
|
||||
|
||||
export type IntegrationTypes = 'twitch' | 'youtube' | 'discord';
|
||||
|
||||
export interface IntegrationAccount {
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IntegrationApplication {
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
icon?: string;
|
||||
description: string;
|
||||
bot?: User;
|
||||
}
|
||||
|
||||
export class Integration implements Model {
|
||||
constructor(
|
||||
session: Session,
|
||||
data: DiscordIntegration & { guild_id?: Snowflake }
|
||||
) {
|
||||
this.id = data.id;
|
||||
this.session = session;
|
||||
|
||||
data.guild_id ? (this.guildId = data.guild_id) : null;
|
||||
|
||||
this.name = data.name;
|
||||
this.type = data.type;
|
||||
this.enabled = !!data.enabled;
|
||||
this.syncing = !!data.syncing;
|
||||
this.roleId = data.role_id;
|
||||
this.enableEmoticons = !!data.enable_emoticons;
|
||||
this.expireBehavior = data.expire_behavior;
|
||||
this.expireGracePeriod = data.expire_grace_period;
|
||||
this.syncedAt = data.synced_at;
|
||||
this.subscriberCount = data.subscriber_count;
|
||||
this.revoked = !!data.revoked;
|
||||
|
||||
this.user = data.user ? new User(session, data.user) : undefined;
|
||||
this.account = {
|
||||
id: data.account.id,
|
||||
name: data.account.name,
|
||||
};
|
||||
|
||||
if (data.application) {
|
||||
this.application = {
|
||||
id: data.application.id,
|
||||
name: data.application.name,
|
||||
icon: data.application.icon ? data.application.icon : undefined,
|
||||
description: data.application.description,
|
||||
bot: data.application.bot
|
||||
? new User(session, data.application.bot)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
id: Snowflake;
|
||||
guildId?: Snowflake;
|
||||
|
||||
name: string;
|
||||
type: IntegrationTypes;
|
||||
enabled?: boolean;
|
||||
syncing?: boolean;
|
||||
roleId?: string;
|
||||
enableEmoticons?: boolean;
|
||||
expireBehavior?: IntegrationExpireBehaviors;
|
||||
expireGracePeriod?: number;
|
||||
syncedAt?: string;
|
||||
subscriberCount?: number;
|
||||
revoked?: boolean;
|
||||
|
||||
user?: User;
|
||||
account: IntegrationAccount;
|
||||
application?: IntegrationApplication;
|
||||
}
|
||||
|
||||
export default Integration;
|
@ -1,669 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
ApplicationCommandTypes,
|
||||
DiscordInteraction,
|
||||
DiscordMessage,
|
||||
DiscordMessageComponents,
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessageInteraction,
|
||||
Locales
|
||||
} from '@biscuitland/api-types';
|
||||
import type { CreateMessage } from './message';
|
||||
import type { MessageFlags } from '../utils/util';
|
||||
import type { EditWebhookMessage } from './webhook';
|
||||
import {
|
||||
InteractionResponseTypes,
|
||||
InteractionTypes,
|
||||
MessageComponentTypes,
|
||||
INTERACTION_ID_TOKEN,
|
||||
WEBHOOK_MESSAGE,
|
||||
WEBHOOK_MESSAGE_ORIGINAL
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
import { Role } from './role';
|
||||
import { Attachment } from './attachment';
|
||||
import { Snowflake } from '../snowflakes';
|
||||
import { User } from './user';
|
||||
import { Member } from './members';
|
||||
import { Message } from './message';
|
||||
import { Permissions } from './special/permissions';
|
||||
import { Webhook } from './webhook';
|
||||
import { InteractionOptions } from './special/interaction-options';
|
||||
import { NewEmbed } from './embed';
|
||||
|
||||
export type InteractionResponseWith = {
|
||||
with: InteractionApplicationCommandCallbackData;
|
||||
};
|
||||
export type InteractionResponseWithData =
|
||||
| InteractionResponse
|
||||
| InteractionResponseWith;
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/slash-commands#interaction-response
|
||||
*/
|
||||
export interface InteractionResponse {
|
||||
type: InteractionResponseTypes;
|
||||
data?: InteractionApplicationCommandCallbackData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/slash-commands#interaction-response-interactionapplicationcommandcallbackdata
|
||||
*/
|
||||
export interface InteractionApplicationCommandCallbackData
|
||||
extends Pick<
|
||||
CreateMessage,
|
||||
'allowedMentions' | 'content' | 'embeds' | 'files'
|
||||
> {
|
||||
customId?: string;
|
||||
title?: string;
|
||||
components?: DiscordMessageComponents;
|
||||
flags?: MessageFlags;
|
||||
choices?: ApplicationCommandOptionChoice[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice
|
||||
*/
|
||||
export interface ApplicationCommandOptionChoice {
|
||||
name: string;
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export abstract class BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.token = data.token;
|
||||
this.type = data.type;
|
||||
this.guildId = data.guild_id;
|
||||
this.channelId = data.channel_id;
|
||||
this.applicationId = data.application_id;
|
||||
this.version = data.version;
|
||||
|
||||
this.locale = data.locale as Locales;
|
||||
this.guildLocale = data.guild_locale as Locales;
|
||||
|
||||
const perms = data.app_permissions;
|
||||
|
||||
if (perms) {
|
||||
this.appPermissions = new Permissions(BigInt(perms));
|
||||
}
|
||||
|
||||
if (!data.guild_id) {
|
||||
this.user = new User(session, data.user!);
|
||||
} else {
|
||||
this.member = new Member(session, data.member!, data.guild_id);
|
||||
|
||||
// dangerous black magic be careful!
|
||||
Object.defineProperty(this, 'user', {
|
||||
get() {
|
||||
return this.member.user;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
readonly token: string;
|
||||
|
||||
type: InteractionTypes;
|
||||
guildId?: Snowflake;
|
||||
channelId?: Snowflake;
|
||||
applicationId?: Snowflake;
|
||||
user!: User;
|
||||
member?: Member;
|
||||
appPermissions?: Permissions;
|
||||
|
||||
// must be implemented
|
||||
locale: Locales;
|
||||
guildLocale: Locales;
|
||||
|
||||
// readonly property according to docs
|
||||
readonly version: 1;
|
||||
|
||||
responded = false;
|
||||
|
||||
get createdTimestamp(): number {
|
||||
return Snowflake.snowflakeToTimestamp(this.id);
|
||||
}
|
||||
|
||||
get createdAt(): Date {
|
||||
return new Date(this.createdTimestamp);
|
||||
}
|
||||
|
||||
isCommand(): this is CommandInteraction {
|
||||
return this.type === InteractionTypes.ApplicationCommand;
|
||||
}
|
||||
|
||||
isAutoComplete(): this is AutoCompleteInteraction {
|
||||
return this.type === InteractionTypes.ApplicationCommandAutocomplete;
|
||||
}
|
||||
|
||||
isComponent(): this is ComponentInteraction {
|
||||
return this.type === InteractionTypes.MessageComponent;
|
||||
}
|
||||
|
||||
isPing(): this is PingInteraction {
|
||||
return this.type === InteractionTypes.Ping;
|
||||
}
|
||||
|
||||
isModalSubmit(): this is ModalSubmitInteraction {
|
||||
return this.type === InteractionTypes.ModalSubmit;
|
||||
}
|
||||
|
||||
inGuild(): this is this & { guildId: Snowflake } {
|
||||
return !!this.guildId;
|
||||
}
|
||||
|
||||
// webhooks methods:
|
||||
|
||||
async editReply(
|
||||
options: EditWebhookMessage & { messageId?: Snowflake }
|
||||
): Promise<Message | undefined> {
|
||||
const message = await this.session.rest.patch<
|
||||
DiscordMessage | undefined
|
||||
>(
|
||||
options.messageId
|
||||
? WEBHOOK_MESSAGE(this.session.applicationId, this.token, options.messageId)
|
||||
: WEBHOOK_MESSAGE_ORIGINAL(this.session.applicationId, this.token),
|
||||
{
|
||||
content: options.content,
|
||||
embeds: options.embeds?.map(NewEmbed),
|
||||
file: options.files,
|
||||
components: options.components,
|
||||
allowed_mentions: options.allowedMentions && {
|
||||
parse: options.allowedMentions.parse,
|
||||
replied_user: options.allowedMentions.repliedUser,
|
||||
users: options.allowedMentions.users,
|
||||
roles: options.allowedMentions.roles,
|
||||
},
|
||||
attachments: options.attachments?.map(attachment => {
|
||||
return {
|
||||
id: attachment.id,
|
||||
filename: attachment.name,
|
||||
content_type: attachment.contentType,
|
||||
size: attachment.size,
|
||||
url: attachment.attachment,
|
||||
proxy_url: attachment.proxyUrl,
|
||||
height: attachment.height,
|
||||
width: attachment.width,
|
||||
};
|
||||
}),
|
||||
message_id: options.messageId,
|
||||
}
|
||||
);
|
||||
|
||||
if (!message || !options.messageId) {
|
||||
return message as undefined;
|
||||
}
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
async sendFollowUp(
|
||||
options: InteractionApplicationCommandCallbackData
|
||||
): Promise<Message> {
|
||||
const message = await Webhook.prototype.execute.call(
|
||||
{
|
||||
id: this.applicationId!,
|
||||
token: this.token,
|
||||
session: this.session,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
return message!;
|
||||
}
|
||||
|
||||
async editFollowUp(
|
||||
messageId: Snowflake,
|
||||
options?: { threadId: Snowflake }
|
||||
): Promise<Message> {
|
||||
const message = await Webhook.prototype.editMessage.call(
|
||||
{
|
||||
id: this.session.applicationId,
|
||||
session: this.session,
|
||||
token: this.token,
|
||||
},
|
||||
messageId,
|
||||
options
|
||||
);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
async deleteEphemeral(messageId?: Snowflake): Promise<void> {
|
||||
await Webhook.prototype.deleteFollowUp.call(
|
||||
{
|
||||
id: this.session.applicationId,
|
||||
session: this.session,
|
||||
token: this.token,
|
||||
},
|
||||
messageId
|
||||
);
|
||||
}
|
||||
|
||||
async deleteFollowUp(
|
||||
messageId: Snowflake,
|
||||
threadId?: Snowflake
|
||||
): Promise<void> {
|
||||
await Webhook.prototype.deleteMessage.call(
|
||||
{
|
||||
id: this.session.applicationId,
|
||||
session: this.session,
|
||||
token: this.token,
|
||||
},
|
||||
messageId,
|
||||
threadId
|
||||
);
|
||||
}
|
||||
|
||||
async fetchFollowUp(
|
||||
messageId: Snowflake,
|
||||
threadId?: Snowflake
|
||||
): Promise<Message | undefined> {
|
||||
const message = await Webhook.prototype.fetchMessage.call(
|
||||
{
|
||||
id: this.session.applicationId,
|
||||
session: this.session,
|
||||
token: this.token,
|
||||
},
|
||||
messageId,
|
||||
threadId
|
||||
);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
// end webhook methods
|
||||
|
||||
async respond(resp: InteractionResponse): Promise<Message | undefined>;
|
||||
async respond(resp: InteractionResponseWith): Promise<Message | undefined>;
|
||||
async respond(
|
||||
resp: InteractionResponseWithData
|
||||
): Promise<Message | undefined> {
|
||||
const options = 'with' in resp ? resp.with : resp.data;
|
||||
const type =
|
||||
'type' in resp
|
||||
? resp.type
|
||||
: InteractionResponseTypes.ChannelMessageWithSource;
|
||||
|
||||
const data = {
|
||||
content: options?.content,
|
||||
custom_id: options?.customId,
|
||||
file: options?.files,
|
||||
allowed_mentions: options?.allowedMentions,
|
||||
flags: options?.flags,
|
||||
chocies: options?.choices,
|
||||
embeds: options?.embeds?.map(NewEmbed),
|
||||
title: options?.title,
|
||||
components: options?.components,
|
||||
};
|
||||
|
||||
if (!this.responded) {
|
||||
await this.session.rest.post<undefined>(
|
||||
INTERACTION_ID_TOKEN(this.id, this.token),
|
||||
{
|
||||
file: options?.files,
|
||||
type,
|
||||
data,
|
||||
}
|
||||
);
|
||||
|
||||
this.responded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
return this.sendFollowUp(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* internal usage only, same as respond but doesn't tries to follow up
|
||||
* */
|
||||
async respond_(resp: InteractionResponse): Promise<void> {
|
||||
if (!this.responded) return this.respond(resp) as Promise<undefined>;
|
||||
}
|
||||
|
||||
// start custom methods
|
||||
|
||||
async respondWith(
|
||||
resp: InteractionApplicationCommandCallbackData
|
||||
): Promise<Message | undefined> {
|
||||
const m = await this.respond({ with: resp });
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
async defer() {
|
||||
await this.respond({
|
||||
type: InteractionResponseTypes.DeferredChannelMessageWithSource,
|
||||
});
|
||||
}
|
||||
|
||||
async autocomplete() {
|
||||
await this.respond({
|
||||
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* taken from Detritus
|
||||
* try respond, try edit, try follow up
|
||||
* */
|
||||
async editOrReply(resp: InteractionResponseWithData & EditWebhookMessage) {
|
||||
if (this.responded) {
|
||||
return this.editReply(resp);
|
||||
}
|
||||
|
||||
let type: InteractionResponseTypes = InteractionResponseTypes.ChannelMessageWithSource;
|
||||
|
||||
switch (this.type) {
|
||||
case InteractionTypes.ApplicationCommand:
|
||||
type = InteractionResponseTypes.ChannelMessageWithSource;
|
||||
break;
|
||||
case InteractionTypes.MessageComponent:
|
||||
type = InteractionResponseTypes.UpdateMessage;
|
||||
break;
|
||||
}
|
||||
|
||||
const result = await this.respond({ type, data: resp });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// end custom methods
|
||||
}
|
||||
|
||||
export class AutoCompleteInteraction extends BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
super(session, data);
|
||||
this.type = data.type as number;
|
||||
this.commandId = data.data!.id;
|
||||
this.commandName = data.data!.name;
|
||||
this.commandType = data.data!.type;
|
||||
this.commandGuildId = data.data!.guild_id;
|
||||
this.options = new InteractionOptions(
|
||||
data.data!.options ?? []
|
||||
);
|
||||
}
|
||||
|
||||
override type: InteractionTypes.ApplicationCommandAutocomplete;
|
||||
commandId: Snowflake;
|
||||
commandName: string;
|
||||
commandType: ApplicationCommandTypes;
|
||||
commandGuildId?: Snowflake;
|
||||
options: InteractionOptions;
|
||||
|
||||
async respondWithChoices(
|
||||
choices: ApplicationCommandOptionChoice[]
|
||||
): Promise<void> {
|
||||
await this.session.rest.post<undefined>(
|
||||
INTERACTION_ID_TOKEN(this.id, this.token),
|
||||
{
|
||||
data: { choices },
|
||||
type: InteractionResponseTypes.ApplicationCommandAutocompleteResult,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface CommandInteractionDataResolved {
|
||||
users: Map<Snowflake, User>;
|
||||
members: Map<Snowflake, Member>;
|
||||
roles: Map<Snowflake, Role>;
|
||||
messages: Map<Snowflake, Message>;
|
||||
attachments: Map<Snowflake, Attachment>;
|
||||
}
|
||||
|
||||
export class CommandInteraction extends BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
super(session, data);
|
||||
this.type = data.type as number;
|
||||
this.commandId = data.data!.id;
|
||||
this.commandName = data.data!.name;
|
||||
this.commandType = data.data!.type;
|
||||
this.commandGuildId = data.data!.guild_id;
|
||||
this.options = new InteractionOptions(
|
||||
data.data!.options ?? []
|
||||
);
|
||||
|
||||
this.resolved = {
|
||||
users: new Map(),
|
||||
members: new Map(),
|
||||
roles: new Map(),
|
||||
attachments: new Map(),
|
||||
messages: new Map(),
|
||||
};
|
||||
|
||||
if (data.data!.resolved?.users) {
|
||||
for (const [id, u] of Object.entries(data.data!.resolved.users)) {
|
||||
this.resolved.users.set(id, new User(session, u));
|
||||
}
|
||||
}
|
||||
|
||||
if (data.data!.resolved?.members && !!super.guildId) {
|
||||
for (const [id, m] of Object.entries(data.data!.resolved.members)) {
|
||||
this.resolved.members.set(
|
||||
id,
|
||||
new Member(
|
||||
session,
|
||||
m as DiscordMemberWithUser,
|
||||
super.guildId!
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.data!.resolved?.roles && !!super.guildId) {
|
||||
for (const [id, r] of Object.entries(data.data!.resolved.roles)) {
|
||||
this.resolved.roles.set(
|
||||
id,
|
||||
new Role(session, r, super.guildId!)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.data!.resolved?.attachments) {
|
||||
for (const [id, a] of Object.entries(
|
||||
data.data!.resolved.attachments
|
||||
)) {
|
||||
this.resolved.attachments.set(id, new Attachment(session, a));
|
||||
}
|
||||
}
|
||||
|
||||
if (data.data!.resolved?.messages) {
|
||||
for (const [id, m] of Object.entries(
|
||||
data.data!.resolved.messages
|
||||
)) {
|
||||
this.resolved.messages.set(id, new Message(session, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override type: InteractionTypes.ApplicationCommand;
|
||||
commandId: Snowflake;
|
||||
commandName: string;
|
||||
commandType: ApplicationCommandTypes;
|
||||
commandGuildId?: Snowflake;
|
||||
resolved: CommandInteractionDataResolved;
|
||||
options: InteractionOptions;
|
||||
}
|
||||
|
||||
export type ModalInMessage = ModalSubmitInteraction & {
|
||||
message: Message;
|
||||
};
|
||||
|
||||
export class ModalSubmitInteraction extends BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
super(session, data);
|
||||
this.type = data.type as number;
|
||||
this.componentType = data.data!.component_type!;
|
||||
this.customId = data.data!.custom_id;
|
||||
this.targetId = data.data!.target_id;
|
||||
this.values = data.data!.values;
|
||||
|
||||
this.components = data.data?.components?.map(
|
||||
ModalSubmitInteraction.transformComponent
|
||||
);
|
||||
|
||||
if (data.message) {
|
||||
this.message = new Message(session, data.message);
|
||||
}
|
||||
}
|
||||
|
||||
override type: InteractionTypes.MessageComponent;
|
||||
componentType: MessageComponentTypes;
|
||||
customId?: string;
|
||||
targetId?: Snowflake;
|
||||
values?: string[];
|
||||
message?: Message;
|
||||
components;
|
||||
|
||||
static transformComponent(component: DiscordMessageComponents[number]) {
|
||||
return {
|
||||
type: component.type,
|
||||
components: component.components.map(component => {
|
||||
return {
|
||||
customId: component.custom_id,
|
||||
value: (component as typeof component & { value: string })
|
||||
.value,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
inMessage(): this is ModalInMessage {
|
||||
return !!this.message;
|
||||
}
|
||||
}
|
||||
|
||||
export class PingInteraction extends BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
super(session, data);
|
||||
this.type = data.type as number;
|
||||
this.commandId = data.data!.id;
|
||||
this.commandName = data.data!.name;
|
||||
this.commandType = data.data!.type;
|
||||
this.commandGuildId = data.data!.guild_id;
|
||||
}
|
||||
|
||||
override type: InteractionTypes.Ping;
|
||||
commandId: Snowflake;
|
||||
commandName: string;
|
||||
commandType: ApplicationCommandTypes;
|
||||
commandGuildId?: Snowflake;
|
||||
override locale = undefined as never;
|
||||
override guildLocale = undefined as never;
|
||||
|
||||
async pong(): Promise<void> {
|
||||
await this.session.rest.post<undefined>(
|
||||
INTERACTION_ID_TOKEN(this.id, this.token),
|
||||
{
|
||||
type: InteractionResponseTypes.Pong,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ComponentInteraction extends BaseInteraction implements Model {
|
||||
constructor(session: Session, data: DiscordInteraction) {
|
||||
super(session, data);
|
||||
this.type = data.type as number;
|
||||
this.componentType = data.data!.component_type!;
|
||||
this.customId = data.data!.custom_id;
|
||||
this.targetId = data.data!.target_id;
|
||||
this.values = data.data!.values;
|
||||
this.message = new Message(session, data.message!);
|
||||
}
|
||||
|
||||
override type: InteractionTypes.MessageComponent;
|
||||
componentType: MessageComponentTypes;
|
||||
customId?: string;
|
||||
targetId?: Snowflake;
|
||||
values?: string[];
|
||||
message: Message;
|
||||
|
||||
isButton(): boolean {
|
||||
return this.componentType === MessageComponentTypes.Button;
|
||||
}
|
||||
|
||||
isActionRow(): boolean {
|
||||
return this.componentType === MessageComponentTypes.ActionRow;
|
||||
}
|
||||
|
||||
isTextInput(): boolean {
|
||||
return this.componentType === MessageComponentTypes.InputText;
|
||||
}
|
||||
|
||||
isSelectMenu(): boolean {
|
||||
return this.componentType === MessageComponentTypes.SelectMenu;
|
||||
}
|
||||
|
||||
async deferUpdate() {
|
||||
await this.respond({
|
||||
type: InteractionResponseTypes.DeferredUpdateMessage,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/interactions/receiving-and-responding#message-interaction-object-message-interaction-structure
|
||||
*/
|
||||
export interface MessageInteraction {
|
||||
/** id of the interaction */
|
||||
id: Snowflake;
|
||||
/** type of interaction */
|
||||
type: InteractionTypes;
|
||||
/** name of the application command, including subcommands and subcommand groups */
|
||||
name: string;
|
||||
/** user who invoked the interaction */
|
||||
user: User;
|
||||
/** member who invoked the interaction in the guild */
|
||||
member?: Partial<Member>;
|
||||
}
|
||||
|
||||
export type Interaction =
|
||||
| CommandInteraction
|
||||
| ComponentInteraction
|
||||
| PingInteraction
|
||||
| AutoCompleteInteraction
|
||||
| ModalSubmitInteraction;
|
||||
|
||||
export class InteractionFactory {
|
||||
static from(
|
||||
session: Session,
|
||||
interaction: DiscordInteraction
|
||||
): Interaction {
|
||||
switch (interaction.type) {
|
||||
case InteractionTypes.Ping:
|
||||
return new PingInteraction(session, interaction);
|
||||
case InteractionTypes.ApplicationCommand:
|
||||
return new CommandInteraction(session, interaction);
|
||||
case InteractionTypes.MessageComponent:
|
||||
return new ComponentInteraction(session, interaction);
|
||||
case InteractionTypes.ApplicationCommandAutocomplete:
|
||||
return new AutoCompleteInteraction(session, interaction);
|
||||
case InteractionTypes.ModalSubmit:
|
||||
return new ModalSubmitInteraction(session, interaction);
|
||||
}
|
||||
}
|
||||
|
||||
static fromMessage(
|
||||
session: Session,
|
||||
interaction: DiscordMessageInteraction,
|
||||
_guildId?: Snowflake
|
||||
): MessageInteraction {
|
||||
const obj = {
|
||||
id: interaction.id,
|
||||
type: interaction.type,
|
||||
name: interaction.name,
|
||||
user: new User(session, interaction.user),
|
||||
// TODO: Parse member somehow with the guild id passed in message
|
||||
};
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
@ -1,206 +0,0 @@
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type {
|
||||
DiscordApplication,
|
||||
DiscordChannel,
|
||||
DiscordInvite,
|
||||
DiscordInviteCreate,
|
||||
DiscordMemberWithUser,
|
||||
DiscordScheduledEventEntityMetadata,
|
||||
ScheduledEventEntityType,
|
||||
ScheduledEventPrivacyLevel,
|
||||
ScheduledEventStatus,
|
||||
TargetTypes,
|
||||
} from '@biscuitland/api-types';
|
||||
import { GuildChannel } from './channels';
|
||||
import { Member } from './members';
|
||||
import { Guild, InviteGuild } from './guilds';
|
||||
import { User } from './user';
|
||||
import { Application } from './application';
|
||||
|
||||
export interface InviteStageInstance {
|
||||
/** The members speaking in the Stage */
|
||||
members: Partial<Member>[];
|
||||
/** The number of users in the Stage */
|
||||
participantCount: number;
|
||||
/** The number of users speaking in the Stage */
|
||||
speakerCount: number;
|
||||
/** The topic of the Stage instance (1-120 characters) */
|
||||
topic: string;
|
||||
}
|
||||
|
||||
export interface InviteScheduledEvent {
|
||||
id: Snowflake;
|
||||
guildId: string;
|
||||
channelId?: string;
|
||||
creatorId?: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
scheduledStartTime: string;
|
||||
scheduledEndTime?: string;
|
||||
privacyLevel: ScheduledEventPrivacyLevel;
|
||||
status: ScheduledEventStatus;
|
||||
entityType: ScheduledEventEntityType;
|
||||
entityId?: string;
|
||||
entityMetadata?: DiscordScheduledEventEntityMetadata;
|
||||
creator?: User;
|
||||
userCount?: number;
|
||||
image?: string;
|
||||
}
|
||||
|
||||
export interface InviteCreate {
|
||||
channelId: string;
|
||||
code: string;
|
||||
createdAt: string;
|
||||
guildId?: string;
|
||||
inviter?: User;
|
||||
maxAge: number;
|
||||
maxUses: number;
|
||||
targetType: TargetTypes;
|
||||
targetUser?: User;
|
||||
targetApplication?: Partial<Application>;
|
||||
temporary: boolean;
|
||||
uses: number;
|
||||
}
|
||||
|
||||
export function NewInviteCreate(
|
||||
session: Session,
|
||||
invite: DiscordInviteCreate
|
||||
): InviteCreate {
|
||||
return {
|
||||
channelId: invite.channel_id,
|
||||
code: invite.code,
|
||||
createdAt: invite.created_at,
|
||||
guildId: invite.guild_id,
|
||||
inviter: invite.inviter ? new User(session, invite.inviter) : undefined,
|
||||
maxAge: invite.max_age,
|
||||
maxUses: invite.max_uses,
|
||||
targetType: invite.target_type,
|
||||
targetUser: invite.target_user
|
||||
? new User(session, invite.target_user)
|
||||
: undefined,
|
||||
targetApplication:
|
||||
invite.target_application &&
|
||||
new Application(
|
||||
session,
|
||||
invite.target_application as DiscordApplication
|
||||
),
|
||||
temporary: invite.temporary,
|
||||
uses: invite.uses,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/invite#invite-object
|
||||
*/
|
||||
export class Invite {
|
||||
constructor(session: Session, data: DiscordInvite) {
|
||||
this.session = session;
|
||||
|
||||
this.guild = data.guild
|
||||
? new InviteGuild(session, data.guild)
|
||||
: undefined;
|
||||
this.approximateMemberCount = data.approximate_member_count
|
||||
? data.approximate_member_count
|
||||
: undefined;
|
||||
this.approximatePresenceCount = data.approximate_presence_count
|
||||
? data.approximate_presence_count
|
||||
: undefined;
|
||||
this.code = data.code;
|
||||
this.expiresAt = data.expires_at
|
||||
? Date.parse(data.expires_at)
|
||||
: undefined;
|
||||
this.inviter = data.inviter
|
||||
? new User(session, data.inviter)
|
||||
: undefined;
|
||||
this.targetUser = data.target_user
|
||||
? new User(session, data.target_user)
|
||||
: undefined;
|
||||
this.targetApplication = data.target_application
|
||||
? new Application(
|
||||
session,
|
||||
data.target_application as DiscordApplication
|
||||
)
|
||||
: undefined;
|
||||
this.targetType = data.target_type;
|
||||
|
||||
if (data.channel) {
|
||||
const guildId = data.guild?.id ? data.guild.id : '';
|
||||
this.channel = new GuildChannel(
|
||||
session,
|
||||
data.channel as DiscordChannel,
|
||||
guildId
|
||||
);
|
||||
}
|
||||
|
||||
if (data.guild_scheduled_event) {
|
||||
this.guildScheduledEvent = {
|
||||
id: data.guild_scheduled_event.id,
|
||||
guildId: data.guild_scheduled_event.guild_id,
|
||||
channelId: data.guild_scheduled_event.channel_id
|
||||
? data.guild_scheduled_event.channel_id
|
||||
: undefined,
|
||||
creatorId: data.guild_scheduled_event.creator_id
|
||||
? data.guild_scheduled_event.creator_id
|
||||
: undefined,
|
||||
name: data.guild_scheduled_event.name,
|
||||
description: data.guild_scheduled_event.description
|
||||
? data.guild_scheduled_event.description
|
||||
: undefined,
|
||||
scheduledStartTime:
|
||||
data.guild_scheduled_event.scheduled_start_time,
|
||||
scheduledEndTime: data.guild_scheduled_event.scheduled_end_time
|
||||
? data.guild_scheduled_event.scheduled_end_time
|
||||
: undefined,
|
||||
privacyLevel: data.guild_scheduled_event.privacy_level,
|
||||
status: data.guild_scheduled_event.status,
|
||||
entityType: data.guild_scheduled_event.entity_type,
|
||||
entityId: data.guild ? data.guild.id : undefined,
|
||||
entityMetadata: data.guild_scheduled_event.entity_metadata
|
||||
? data.guild_scheduled_event.entity_metadata
|
||||
: undefined,
|
||||
creator: data.guild_scheduled_event.creator
|
||||
? new User(session, data.guild_scheduled_event.creator)
|
||||
: undefined,
|
||||
userCount: data.guild_scheduled_event.user_count
|
||||
? data.guild_scheduled_event.user_count
|
||||
: undefined,
|
||||
image: data.guild_scheduled_event.image
|
||||
? data.guild_scheduled_event.image
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (data.stage_instance) {
|
||||
const guildId = data.guild?.id ? data.guild.id : '';
|
||||
this.stageInstance = {
|
||||
members: data.stage_instance.members.map(
|
||||
m =>
|
||||
new Member(session, m as DiscordMemberWithUser, guildId)
|
||||
),
|
||||
participantCount: data.stage_instance.participant_count,
|
||||
speakerCount: data.stage_instance.speaker_count,
|
||||
topic: data.stage_instance.topic,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
guild?: InviteGuild;
|
||||
approximateMemberCount?: number;
|
||||
approximatePresenceCount?: number;
|
||||
code: string;
|
||||
expiresAt?: number;
|
||||
inviter?: User;
|
||||
targetUser?: User;
|
||||
targetType?: TargetTypes;
|
||||
channel?: Partial<GuildChannel>;
|
||||
stageInstance?: InviteStageInstance;
|
||||
guildScheduledEvent?: InviteScheduledEvent;
|
||||
targetApplication?: Partial<Application>;
|
||||
|
||||
async delete(): Promise<Invite> {
|
||||
await Guild.prototype.deleteInvite.call(this.guild, this.code);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -1,239 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordMemberWithUser,
|
||||
DiscordThreadMember,
|
||||
} from '@biscuitland/api-types';
|
||||
import type { CreateGuildBan, ModifyGuildMember } from './guilds';
|
||||
import type { AvatarOptions } from './user';
|
||||
import { User } from './user';
|
||||
import { Guild } from './guilds';
|
||||
import { Util } from '../utils/util';
|
||||
import { USER_AVATAR, THREAD_USER } from '@biscuitland/api-types';
|
||||
import { Permissions } from './special/permissions';
|
||||
|
||||
/**
|
||||
* Represents a guild member
|
||||
* @link https://discord.com/developers/docs/resources/guild#guild-member-object
|
||||
*/
|
||||
export class Member implements Model {
|
||||
constructor(
|
||||
session: Session,
|
||||
data: DiscordMemberWithUser,
|
||||
guildId: Snowflake
|
||||
) {
|
||||
this.session = session;
|
||||
this.user = new User(session, data.user);
|
||||
this.guildId = guildId;
|
||||
this.avatarHash = data.avatar
|
||||
? data.avatar
|
||||
: undefined;
|
||||
|
||||
this.nickname = data.nick ? data.nick : undefined;
|
||||
this.premiumSince = data.premium_since
|
||||
? Date.parse(data.premium_since)
|
||||
: undefined;
|
||||
|
||||
this.channelPermissions = data.permissions ? new Permissions(BigInt(data.permissions)) : undefined;
|
||||
this.joinedTimestamp = Date.parse(data.joined_at);
|
||||
this.roles = data.roles;
|
||||
this.deaf = !!data.deaf;
|
||||
this.mute = !!data.mute;
|
||||
this.pending = !!data.pending;
|
||||
this.communicationDisabledUntilTimestamp =
|
||||
data.communication_disabled_until
|
||||
? Date.parse(data.communication_disabled_until)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/** the session that instantiated this member */
|
||||
readonly session: Session;
|
||||
|
||||
/** the user this guild member represents */
|
||||
user: User;
|
||||
|
||||
/** the choosen guild id */
|
||||
guildId: Snowflake;
|
||||
|
||||
/** the member's guild avatar hash optimized as a bigint */
|
||||
avatarHash?: string;
|
||||
|
||||
/** this user's guild nickname */
|
||||
nickname?: string;
|
||||
|
||||
/** when the user started boosting the guild */
|
||||
premiumSince?: number;
|
||||
|
||||
/** total permissions of the member in the channel, including overwrites, returned when in the interaction object */
|
||||
channelPermissions?: Permissions;
|
||||
|
||||
/** when the user joined the guild */
|
||||
joinedTimestamp: number;
|
||||
|
||||
/** array of role object ids */
|
||||
roles: Snowflake[];
|
||||
|
||||
/** whether the user is deafened in voice channels */
|
||||
deaf: boolean;
|
||||
|
||||
/** whether the user is muted in voice channels */
|
||||
mute: boolean;
|
||||
|
||||
/** whether the user has not yet passed the guild's Membership Screening requirements */
|
||||
pending: boolean;
|
||||
|
||||
/** when the user's timeout will expire and the user will be able to communicate in the guild again, null or a time in the past if the user is not timed out */
|
||||
communicationDisabledUntilTimestamp?: number;
|
||||
|
||||
/** shorthand to User.id */
|
||||
get id(): Snowflake {
|
||||
return this.user.id;
|
||||
}
|
||||
|
||||
/** gets the nickname or the username */
|
||||
get nicknameOrUsername(): string {
|
||||
return this.nickname ?? this.user.username;
|
||||
}
|
||||
|
||||
/** gets the joinedAt timestamp as a Date */
|
||||
get joinedAt(): Date {
|
||||
return new Date(this.joinedTimestamp);
|
||||
}
|
||||
|
||||
/** bans a member from this guild and delete previous messages sent by the member */
|
||||
async ban(options: CreateGuildBan): Promise<Member> {
|
||||
await Guild.prototype.banMember.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
options
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** kicks a member from this guild */
|
||||
async kick(options: { reason?: string }): Promise<Member> {
|
||||
await Guild.prototype.kickMember.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
options.reason
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** unbans a member from this guild */
|
||||
async unban(): Promise<void> {
|
||||
await Guild.prototype.unbanMember.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id
|
||||
);
|
||||
}
|
||||
|
||||
/** edits member's nickname, roles, etc */
|
||||
async edit(options: ModifyGuildMember): Promise<Member> {
|
||||
const member = await Guild.prototype.editMember.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
options
|
||||
);
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
/** calls {@link Member#edit} which calls {@link Guild#editMember} under the hood */
|
||||
async timeout(time: number | null) {
|
||||
await this.edit({ communicationDisabledUntil: time });
|
||||
}
|
||||
|
||||
/** adds a role to this member */
|
||||
async addRole(roleId: Snowflake, reason?: string): Promise<void> {
|
||||
await Guild.prototype.addRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
roleId,
|
||||
reason
|
||||
);
|
||||
}
|
||||
|
||||
/** removes a role from this member */
|
||||
async removeRole(
|
||||
roleId: Snowflake,
|
||||
options: { reason?: string } = {}
|
||||
): Promise<void> {
|
||||
await Guild.prototype.removeRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
roleId,
|
||||
options.reason
|
||||
);
|
||||
}
|
||||
|
||||
async fetch(): Promise<Member> {
|
||||
const member = await Guild.prototype.fetchMember.call({ session: this.session, id: this.guildId }, this.id);
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
/** gets the members's guild avatar if the user has one, gets the user's avatar instead */
|
||||
avatarURL(options: AvatarOptions): string {
|
||||
if (!this.avatarHash) {
|
||||
return this.user.avatarURL(options);
|
||||
}
|
||||
|
||||
return Util.formatImageURL(USER_AVATAR(
|
||||
this.user.id,
|
||||
this.avatarHash
|
||||
), options.size ?? 128, options.format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new nickname for this member. Same as Member.edit({ nick: ... })
|
||||
* @param nick - The new nickname for the member.
|
||||
*/
|
||||
async setNickname(nick: string): Promise<Member> {
|
||||
return this.edit({ nick });
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<@!${this.user.id}>`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member that comes from a thread
|
||||
* @link https://discord.com/developers/docs/resources/channel#thread-member-object
|
||||
* **/
|
||||
export class ThreadMember implements Model {
|
||||
constructor(session: Session, data: DiscordThreadMember) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.flags = data.flags;
|
||||
this.timestamp = Date.parse(data.join_timestamp);
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
flags: number;
|
||||
timestamp: number;
|
||||
|
||||
get threadId(): Snowflake {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
async quitThread(memberId?: Snowflake): Promise<void> {
|
||||
await this.session.rest.delete<undefined>(
|
||||
THREAD_USER(this.id, memberId ?? this.session.botId),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
async fetchMember(memberId?: Snowflake): Promise<ThreadMember> {
|
||||
const member = await this.session.rest.get<DiscordThreadMember>(
|
||||
THREAD_USER(this.id, memberId ?? this.session.botId)
|
||||
);
|
||||
|
||||
return new ThreadMember(this.session, member);
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordMemberWithUser,
|
||||
DiscordMessageReactionAdd,
|
||||
DiscordReaction,
|
||||
} from '@biscuitland/api-types';
|
||||
import { Emoji } from './emojis';
|
||||
import { Member } from './members';
|
||||
|
||||
/**
|
||||
* Represents when a new reaction was added to a message.
|
||||
* @link https://discord.com/developers/docs/topics/gateway#message-reaction-add
|
||||
*/
|
||||
export interface MessageReactionAdd {
|
||||
userId: string;
|
||||
channelId: string;
|
||||
messageId: string;
|
||||
guildId?: string;
|
||||
member?: Member;
|
||||
emoji: Partial<Emoji>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents when a reaction was removed from a message.
|
||||
* Equal to MessageReactionAdd but without 'member' property.
|
||||
* @see {@link MessageReactionAdd}
|
||||
* @link https://discord.com/developers/docs/topics/gateway#message-reaction-remove-message-reaction-remove-event-fields
|
||||
*/
|
||||
export type MessageReactionRemove = Omit<MessageReactionAdd, 'member'>;
|
||||
|
||||
/**
|
||||
* Represents when all reactions were removed from a message.
|
||||
* Equals to MessageReactionAdd but with 'channelId', 'messageId' and 'guildId' properties guaranteed.
|
||||
* @see {@link MessageReactionAdd}
|
||||
* @link https://discord.com/developers/docs/topics/gateway#message-reaction-remove-all
|
||||
*/
|
||||
export type MessageReactionRemoveAll = Pick<
|
||||
MessageReactionAdd,
|
||||
'channelId' | 'messageId' | 'guildId'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Represents when a reaction-emoji was removed from a message.
|
||||
* Equals to MessageReactionAdd but with 'channelId', 'messageId', 'emoji' and 'guildId' properties guaranteed.
|
||||
* @see {@link MessageReactionRemove}
|
||||
* @see {@link Emoji}
|
||||
* @link https://discord.com/developers/docs/topics/gateway#message-reaction-remove-emoji
|
||||
*/
|
||||
export type MessageReactionRemoveEmoji = Pick<
|
||||
MessageReactionAdd,
|
||||
'channelId' | 'guildId' | 'messageId' | 'emoji'
|
||||
>;
|
||||
|
||||
/**
|
||||
* Creates a new MessageReactionAdd object.
|
||||
* @param session - Current application session.
|
||||
* @param data - Discord message reaction to parse.
|
||||
*/
|
||||
export function NewMessageReactionAdd(
|
||||
session: Session,
|
||||
data: DiscordMessageReactionAdd
|
||||
): MessageReactionAdd {
|
||||
return {
|
||||
userId: data.user_id,
|
||||
channelId: data.channel_id,
|
||||
messageId: data.message_id,
|
||||
guildId: data.guild_id,
|
||||
member: data.member
|
||||
? new Member(
|
||||
session,
|
||||
data.member as DiscordMemberWithUser,
|
||||
data.guild_id ?? ''
|
||||
)
|
||||
: undefined,
|
||||
emoji: new Emoji(session, data.emoji),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a reaction
|
||||
* @link https://discord.com/developers/docs/resources/channel#reaction-object
|
||||
*/
|
||||
export class MessageReaction {
|
||||
constructor(session: Session, data: DiscordReaction) {
|
||||
this.session = session;
|
||||
this.me = data.me;
|
||||
this.count = data.count;
|
||||
this.emoji = new Emoji(session, data.emoji);
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
me: boolean;
|
||||
count: number;
|
||||
emoji: Emoji;
|
||||
}
|
||||
|
||||
export default MessageReaction;
|
@ -1,644 +0,0 @@
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
AllowedMentionsTypes,
|
||||
DiscordMessage,
|
||||
DiscordMessageComponents,
|
||||
DiscordUser,
|
||||
FileContent,
|
||||
MessageActivityTypes,
|
||||
MessageTypes,
|
||||
GetReactions,
|
||||
} from '@biscuitland/api-types';
|
||||
import type { Channel } from './channels';
|
||||
import type { Component } from './components';
|
||||
import type { MessageInteraction } from './interactions';
|
||||
import type { StickerItem } from './sticker';
|
||||
import type { Embed } from './embed';
|
||||
import { NewEmbed, NewEmbedR } from './embed';
|
||||
import { MessageFlags } from '../utils/util';
|
||||
import { Snowflake } from '../snowflakes';
|
||||
import { ChannelFactory, ThreadChannel } from './channels';
|
||||
import { User } from './user';
|
||||
import { Member } from './members';
|
||||
import { Attachment } from './attachment';
|
||||
import { ComponentFactory } from './components';
|
||||
import { MessageReaction } from './message-reaction';
|
||||
import { Application, NewTeam } from './application';
|
||||
import { InteractionFactory } from './interactions';
|
||||
|
||||
import {
|
||||
CHANNEL_PIN,
|
||||
CHANNEL_MESSAGE,
|
||||
CHANNEL_MESSAGES,
|
||||
CHANNEL_MESSAGE_REACTION_ME,
|
||||
CHANNEL_MESSAGE_REACTION_USER,
|
||||
CHANNEL_MESSAGE_REACTION,
|
||||
CHANNEL_MESSAGE_REACTIONS,
|
||||
CHANNEL_MESSAGE_CROSSPOST,
|
||||
} from '@biscuitland/api-types';
|
||||
|
||||
export interface GuildMessage extends Message {
|
||||
guildId: Snowflake;
|
||||
}
|
||||
|
||||
export type WebhookMessage = Message & {
|
||||
author: Partial<User>;
|
||||
webhook: WebhookAuthor;
|
||||
member: undefined;
|
||||
};
|
||||
|
||||
export interface MessageActivity {
|
||||
partyId?: Snowflake;
|
||||
type: MessageActivityTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#allowed-mentions-object
|
||||
*/
|
||||
export interface AllowedMentions {
|
||||
parse?: AllowedMentionsTypes[];
|
||||
repliedUser?: boolean;
|
||||
roles?: Snowflake[];
|
||||
users?: Snowflake[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#message-reference-object-message-reference-structure
|
||||
* channelId is optional when creating a reply, but will always be present when receiving an event/response that includes this data model.
|
||||
*/
|
||||
export interface CreateMessageReference {
|
||||
messageId: Snowflake;
|
||||
channelId?: Snowflake;
|
||||
guildId?: Snowflake;
|
||||
failIfNotExists?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#create-message-json-params
|
||||
* Posts a message to a guild text or DM channel. Returns a message object. Fires a Message Create Gateway event.
|
||||
*/
|
||||
export interface CreateMessage {
|
||||
embeds?: Embed[];
|
||||
content?: string;
|
||||
allowedMentions?: AllowedMentions;
|
||||
files?: FileContent[];
|
||||
messageReference?: CreateMessageReference;
|
||||
tts?: boolean;
|
||||
components?: DiscordMessageComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#edit-message-json-params
|
||||
* Edit a previously sent message.
|
||||
* Returns a {@link Message} object. Fires a Message Update Gateway event.
|
||||
*/
|
||||
export interface EditMessage extends Partial<CreateMessage> {
|
||||
flags?: MessageFlags;
|
||||
attachments?: Attachment[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a guild or unicode {@link Emoji}
|
||||
*/
|
||||
export type EmojiResolvable =
|
||||
| string
|
||||
| {
|
||||
name: string;
|
||||
id: Snowflake;
|
||||
};
|
||||
|
||||
/**
|
||||
* A partial {@link User} to represent the author of a message sent by a webhook
|
||||
*/
|
||||
export interface WebhookAuthor {
|
||||
id: string;
|
||||
username: string;
|
||||
discriminator: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#message-object
|
||||
* Represents a message
|
||||
*/
|
||||
export class Message implements Model {
|
||||
constructor(session: Session, data: DiscordMessage) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
|
||||
this.type = data.type;
|
||||
this.channelId = data.channel_id;
|
||||
this.guildId = data.guild_id;
|
||||
this.applicationId = data.application_id;
|
||||
|
||||
this.mentions = {
|
||||
users: data.mentions?.map(user => new User(session, user)) ?? [],
|
||||
roleIds: data.mention_roles ?? [],
|
||||
channels:
|
||||
data.mention_channels?.map(channel =>
|
||||
ChannelFactory.from(session, channel)
|
||||
) ?? [],
|
||||
};
|
||||
|
||||
if (!data.webhook_id) {
|
||||
this.author = new User(session, data.author);
|
||||
}
|
||||
|
||||
this.flags = data.flags;
|
||||
this.pinned = !!data.pinned;
|
||||
this.tts = !!data.tts;
|
||||
this.content = data.content!;
|
||||
this.nonce = data.nonce;
|
||||
this.mentionEveryone = data.mention_everyone;
|
||||
|
||||
this.timestamp = Date.parse(data.timestamp);
|
||||
this.editedTimestamp = data.edited_timestamp
|
||||
? Date.parse(data.edited_timestamp)
|
||||
: undefined;
|
||||
|
||||
this.reactions =
|
||||
data.reactions?.map(react => new MessageReaction(session, react)) ??
|
||||
[];
|
||||
this.attachments = data.attachments.map(
|
||||
attachment => new Attachment(session, attachment)
|
||||
);
|
||||
this.embeds = data.embeds.map(NewEmbedR);
|
||||
|
||||
if (data.position) { this.position = data.position; }
|
||||
|
||||
if (data.interaction) {
|
||||
this.interaction = InteractionFactory.fromMessage(
|
||||
session,
|
||||
data.interaction,
|
||||
data.guild_id
|
||||
);
|
||||
}
|
||||
|
||||
if (data.thread && data.guild_id) {
|
||||
this.thread = new ThreadChannel(
|
||||
session,
|
||||
data.thread,
|
||||
data.guild_id
|
||||
);
|
||||
}
|
||||
|
||||
// webhook handling
|
||||
if (data.webhook_id && data.author.discriminator === '0000') {
|
||||
this.webhook = {
|
||||
id: data.webhook_id!,
|
||||
username: data.author.username,
|
||||
discriminator: data.author.discriminator,
|
||||
avatar: data.author.avatar ? data.author.avatar : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// user is always null on MessageCreate and its replaced with author
|
||||
if (data.guild_id && data.member && !this.isWebhookMessage()) {
|
||||
this.member = new Member(
|
||||
session,
|
||||
{ ...data.member, user: data.author },
|
||||
data.guild_id
|
||||
);
|
||||
}
|
||||
|
||||
this.components =
|
||||
data.components?.map(component =>
|
||||
ComponentFactory.from(session, component)
|
||||
) ?? [];
|
||||
|
||||
if (data.activity) {
|
||||
this.activity = {
|
||||
partyId: data.activity.party_id,
|
||||
type: data.activity.type,
|
||||
};
|
||||
}
|
||||
|
||||
if (data.sticker_items) {
|
||||
this.stickers = data.sticker_items.map(si => {
|
||||
return {
|
||||
id: si.id,
|
||||
name: si.name,
|
||||
formatType: si.format_type,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (data.application) {
|
||||
const application: Partial<Application> = {
|
||||
id: data.application.id,
|
||||
icon: data.application.icon ? data.application.icon : undefined,
|
||||
name: data.application.name,
|
||||
guildId: data.application.guild_id,
|
||||
flags: data.application.flags,
|
||||
botPublic: data.application.bot_public,
|
||||
owner: data.application.owner
|
||||
? new User(session, data.application.owner as DiscordUser)
|
||||
: undefined,
|
||||
botRequireCodeGrant: data.application.bot_require_code_grant,
|
||||
coverImage: data.application.cover_image,
|
||||
customInstallURL: data.application.custom_install_url,
|
||||
description: data.application.description,
|
||||
installParams: data.application.install_params,
|
||||
tags: data.application.tags,
|
||||
verifyKey: data.application.verify_key,
|
||||
team: data.application.team
|
||||
? NewTeam(session, data.application.team)
|
||||
: undefined,
|
||||
primarySkuId: data.application.primary_sku_id,
|
||||
privacyPolicyURL: data.application.privacy_policy_url,
|
||||
rpcOrigins: data.application.rpc_origins,
|
||||
slug: data.application.slug,
|
||||
};
|
||||
|
||||
Object.setPrototypeOf(application, Application.prototype);
|
||||
|
||||
this.application = application;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reference to the client that instantiated this Message */
|
||||
readonly session: Session;
|
||||
|
||||
/** id of the message */
|
||||
readonly id: Snowflake;
|
||||
|
||||
/** type of message */
|
||||
type: MessageTypes;
|
||||
|
||||
/** id of the channel the message was sent in */
|
||||
channelId: Snowflake;
|
||||
|
||||
/** id of the guild the message was sent in, this should exist on MESSAGE_CREATE and MESSAGE_UPDATE events */
|
||||
guildId?: Snowflake;
|
||||
|
||||
/** if the message is an Interaction or application-owned webhook, this is the id of the application */
|
||||
applicationId?: Snowflake;
|
||||
|
||||
/** mentions if any */
|
||||
mentions: {
|
||||
/** users specifically mentioned in the message */
|
||||
users: User[];
|
||||
|
||||
/** roles specifically mentioned in this message */
|
||||
roleIds: Snowflake[];
|
||||
|
||||
/** channels specifically mentioned in the message */
|
||||
channels: Channel[];
|
||||
};
|
||||
|
||||
/** sent if the message is a response to an Interaction */
|
||||
interaction?: MessageInteraction;
|
||||
|
||||
/** the author of this message, this field is **not** sent on webhook messages */
|
||||
author!: User;
|
||||
|
||||
/** message flags combined as a bitfield */
|
||||
flags?: MessageFlags;
|
||||
|
||||
/** whether this message is pinned */
|
||||
pinned: boolean;
|
||||
|
||||
/** whether this was a TTS message */
|
||||
tts: boolean;
|
||||
|
||||
/** contents of the message */
|
||||
content: string;
|
||||
|
||||
/** used for validating a message was sent */
|
||||
nonce?: string | number;
|
||||
|
||||
/** whether this message mentions everyone */
|
||||
mentionEveryone: boolean;
|
||||
|
||||
/** when this message was sent */
|
||||
timestamp: number;
|
||||
|
||||
/** when this message was edited */
|
||||
editedTimestamp?: number;
|
||||
|
||||
/**
|
||||
* sent if the message contains stickers
|
||||
* **this contains sticker items not stickers**
|
||||
*/
|
||||
stickers?: StickerItem[];
|
||||
|
||||
/** reactions to the message */
|
||||
reactions: MessageReaction[];
|
||||
|
||||
/** any attached files */
|
||||
attachments: Attachment[];
|
||||
|
||||
/** any embedded content */
|
||||
embeds: Embed[];
|
||||
|
||||
/** member properties for this message's author */
|
||||
member?: Member;
|
||||
|
||||
/** the thread that was started from this message, includes {@link ThreadMember} */
|
||||
thread?: ThreadChannel;
|
||||
|
||||
/** sent if the message contains components like buttons, action rows, or other interactive components */
|
||||
components: Component[];
|
||||
|
||||
/** if the message is generated by a webhook, this is the webhook's author data */
|
||||
webhook?: WebhookAuthor;
|
||||
|
||||
/** sent with Rich Presence-related chat embeds */
|
||||
application?: Partial<Application>;
|
||||
|
||||
/** sent with Rich Presence-related chat embeds */
|
||||
activity?: MessageActivity;
|
||||
|
||||
/** Represents the approximate position of the message in a thread */
|
||||
position?: number;
|
||||
|
||||
/** gets the timestamp of this message, this does not requires the timestamp field */
|
||||
get createdTimestamp(): number {
|
||||
return Snowflake.snowflakeToTimestamp(this.id);
|
||||
}
|
||||
|
||||
/** gets the timestamp of this message as a Date */
|
||||
get createdAt(): Date {
|
||||
return new Date(this.createdTimestamp);
|
||||
}
|
||||
|
||||
/** gets the timestamp of this message (sent by the API) */
|
||||
get sentAt(): Date {
|
||||
return new Date(this.timestamp);
|
||||
}
|
||||
|
||||
/** gets the edited timestamp as a Date */
|
||||
get editedAt(): Date | undefined {
|
||||
return this.editedTimestamp
|
||||
? new Date(this.editedTimestamp)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/** whether this message was edited */
|
||||
get edited(): number | undefined {
|
||||
return this.editedTimestamp;
|
||||
}
|
||||
|
||||
/** gets the url of the message that points to the message */
|
||||
get url(): string {
|
||||
return `https://discord.com/channels/${this.guildId ?? '@me'}/${
|
||||
this.channelId
|
||||
}/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the author is bot.
|
||||
* same as Message.author.bot
|
||||
*/
|
||||
get isBot(): boolean {
|
||||
return this.author.bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins this message
|
||||
*/
|
||||
async pin(): Promise<void> {
|
||||
await this.session.rest.put<undefined>(
|
||||
CHANNEL_PIN(this.channelId, this.id),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpins this message
|
||||
*/
|
||||
async unpin(): Promise<void> {
|
||||
await this.session.rest.delete<undefined>(
|
||||
CHANNEL_PIN(this.channelId, this.id),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/** Edits the current message */
|
||||
async edit(options: EditMessage): Promise<Message> {
|
||||
const message = await this.session.rest.patch<DiscordMessage>(
|
||||
CHANNEL_MESSAGE(this.channelId, this.id),
|
||||
{
|
||||
content: options.content,
|
||||
allowed_mentions: {
|
||||
parse: options.allowedMentions?.parse,
|
||||
roles: options.allowedMentions?.roles,
|
||||
users: options.allowedMentions?.users,
|
||||
replied_user: options.allowedMentions?.repliedUser,
|
||||
},
|
||||
flags: options.flags,
|
||||
embeds: options.embeds?.map(NewEmbed),
|
||||
components: options.components,
|
||||
files: options.files,
|
||||
attachments: options.attachments
|
||||
}
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/** edits the current message flags to supress its embeds */
|
||||
async suppressEmbeds(suppress: true): Promise<Message>;
|
||||
async suppressEmbeds(suppress: false): Promise<Message | undefined>;
|
||||
async suppressEmbeds(suppress = true) {
|
||||
if (this.flags === MessageFlags.SupressEmbeds && suppress === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = await this.edit({ flags: MessageFlags.SupressEmbeds });
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/** deletes this message */
|
||||
async delete(): Promise<Message> {
|
||||
await this.session.rest.delete<void>(
|
||||
CHANNEL_MESSAGE(this.channelId, this.id)
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Replies directly in the channel where the message was sent */
|
||||
async reply(options: CreateMessage | string | Embed[]): Promise<Message> {
|
||||
|
||||
// Options is plain content
|
||||
if (typeof options === 'string') {
|
||||
const message = await this.session.rest.post<DiscordMessage>(
|
||||
CHANNEL_MESSAGES(this.channelId),
|
||||
{ content: options }
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
// Opptions are multiple embeds
|
||||
if (Array.isArray(options)) {
|
||||
const message = await this.session.rest.post<DiscordMessage>(
|
||||
CHANNEL_MESSAGES(this.channelId),
|
||||
{ embeds: options.map(NewEmbed) }
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
// Options is of type CreateMessage
|
||||
const message = await this.session.rest.post<DiscordMessage>(
|
||||
CHANNEL_MESSAGES(this.channelId),
|
||||
{
|
||||
content: options.content,
|
||||
file: options.files,
|
||||
allowed_mentions: {
|
||||
parse: options.allowedMentions?.parse,
|
||||
roles: options.allowedMentions?.roles,
|
||||
users: options.allowedMentions?.users,
|
||||
replied_user: options.allowedMentions?.repliedUser,
|
||||
},
|
||||
message_reference: options.messageReference
|
||||
? {
|
||||
message_id: options.messageReference.messageId,
|
||||
channel_id: options.messageReference.channelId,
|
||||
guild_id: options.messageReference.guildId,
|
||||
fail_if_not_exists:
|
||||
options.messageReference.failIfNotExists ??
|
||||
true,
|
||||
}
|
||||
: undefined,
|
||||
embeds: options.embeds?.map(NewEmbed),
|
||||
tts: options.tts,
|
||||
components: options.components,
|
||||
}
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/** alias for Message.addReaction */
|
||||
get react() {
|
||||
return this.addReaction;
|
||||
}
|
||||
|
||||
/** adds a Reaction */
|
||||
async addReaction(reaction: EmojiResolvable): Promise<void> {
|
||||
const r =
|
||||
typeof reaction === 'string'
|
||||
? reaction
|
||||
: `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.put<undefined>(
|
||||
CHANNEL_MESSAGE_REACTION_ME(this.channelId, this.id, r),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/** removes a reaction from someone */
|
||||
async removeReaction(
|
||||
reaction: EmojiResolvable,
|
||||
options?: { userId: Snowflake }
|
||||
): Promise<void> {
|
||||
const r =
|
||||
typeof reaction === 'string'
|
||||
? reaction
|
||||
: `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.delete<undefined>(
|
||||
options?.userId
|
||||
? CHANNEL_MESSAGE_REACTION_USER(
|
||||
this.channelId,
|
||||
this.id,
|
||||
r,
|
||||
options.userId
|
||||
)
|
||||
: CHANNEL_MESSAGE_REACTION_ME(this.channelId, this.id, r),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users who reacted with this emoji
|
||||
* not recommended since the cache handles reactions already
|
||||
*/
|
||||
async fetchReactions(
|
||||
reaction: EmojiResolvable,
|
||||
options?: GetReactions
|
||||
): Promise<User[]> {
|
||||
const r =
|
||||
typeof reaction === 'string'
|
||||
? reaction
|
||||
: `${reaction.name}:${reaction.id}`;
|
||||
|
||||
const users = await this.session.rest.get<DiscordUser[]>(
|
||||
CHANNEL_MESSAGE_REACTION(
|
||||
this.channelId,
|
||||
this.id,
|
||||
encodeURIComponent(r),
|
||||
options
|
||||
)
|
||||
);
|
||||
|
||||
return users.map(user => new User(this.session, user));
|
||||
}
|
||||
|
||||
/**
|
||||
* same as Message.removeReaction but removes using a unicode emoji
|
||||
*/
|
||||
async removeReactionEmoji(reaction: EmojiResolvable): Promise<void> {
|
||||
const r =
|
||||
typeof reaction === 'string'
|
||||
? reaction
|
||||
: `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.delete<undefined>(
|
||||
CHANNEL_MESSAGE_REACTION(this.channelId, this.id, r),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/** nukes every reaction on the message */
|
||||
async nukeReactions(): Promise<void> {
|
||||
await this.session.rest.delete<undefined>(
|
||||
CHANNEL_MESSAGE_REACTIONS(this.channelId, this.id),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/** publishes/crossposts a message to all followers */
|
||||
async crosspost(): Promise<Message> {
|
||||
const message = await this.session.rest.post<DiscordMessage>(
|
||||
CHANNEL_MESSAGE_CROSSPOST(this.channelId, this.id),
|
||||
{}
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/** fetches this message, meant to be used with Function.call since its redundant */
|
||||
async fetch(): Promise<Message | undefined> {
|
||||
const message = await this.session.rest.get<DiscordMessage>(
|
||||
CHANNEL_MESSAGE(this.channelId, this.id)
|
||||
);
|
||||
|
||||
if (!message?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/** alias of Message.crosspost */
|
||||
get publish() {
|
||||
return this.crosspost;
|
||||
}
|
||||
|
||||
/** wheter the message comes from a guild **/
|
||||
inGuild(): this is GuildMessage {
|
||||
return !!this.guildId;
|
||||
}
|
||||
|
||||
/** wheter the messages comes from a Webhook */
|
||||
isWebhookMessage(): this is WebhookMessage {
|
||||
return !!this.webhook;
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
import type {
|
||||
ActivityTypes,
|
||||
DiscordActivityButton,
|
||||
DiscordActivitySecrets,
|
||||
DiscordClientStatus,
|
||||
DiscordPresenceUpdate,
|
||||
} from '@biscuitland/api-types';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { ComponentEmoji } from '../utils/util';
|
||||
import { User } from './user';
|
||||
|
||||
export interface ActivityAssets {
|
||||
largeImage?: string;
|
||||
largeText?: string;
|
||||
smallImage?: string;
|
||||
smallText?: string;
|
||||
}
|
||||
|
||||
export interface Activities {
|
||||
name: string;
|
||||
type: ActivityTypes;
|
||||
url?: string;
|
||||
createdAt: number;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
applicationId?: Snowflake;
|
||||
details?: string;
|
||||
state?: string;
|
||||
emoji?: ComponentEmoji;
|
||||
party?: {
|
||||
id?: string;
|
||||
size?: number[];
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
secrets?: DiscordActivitySecrets;
|
||||
instance?: boolean;
|
||||
flags?: number;
|
||||
buttons?: DiscordActivityButton;
|
||||
}
|
||||
|
||||
export enum StatusTypes {
|
||||
online = 0,
|
||||
dnd = 1,
|
||||
idle = 2,
|
||||
invisible = 3,
|
||||
offline = 4,
|
||||
}
|
||||
|
||||
export class Presence {
|
||||
constructor(session: Session, data: DiscordPresenceUpdate) {
|
||||
this.session = session;
|
||||
this.user = new User(this.session, data.user);
|
||||
this.guildId = data.guild_id;
|
||||
this.status = StatusTypes[data.status];
|
||||
this.activities = data.activities.map<Activities>(activity =>
|
||||
Object({
|
||||
name: activity.name,
|
||||
type: activity.type,
|
||||
url: activity.url ? activity.url : undefined,
|
||||
createdAt: activity.created_at,
|
||||
timestamps: activity.timestamps,
|
||||
applicationId: activity.application_id,
|
||||
details: activity.details ? activity.details : undefined,
|
||||
state: activity.state,
|
||||
emoji: activity.emoji ? activity.emoji : undefined,
|
||||
party: activity.party ? activity.party : undefined,
|
||||
assets: activity.assets
|
||||
? {
|
||||
largeImage: activity.assets.large_image,
|
||||
largeText: activity.assets.large_text,
|
||||
smallImage: activity.assets.small_image,
|
||||
smallText: activity.assets.small_text,
|
||||
}
|
||||
: null,
|
||||
secrets: activity.secrets ? activity.secrets : undefined,
|
||||
instance: !!activity.instance,
|
||||
flags: activity.flags,
|
||||
buttons: activity.buttons,
|
||||
})
|
||||
);
|
||||
this.clientStatus = data.client_status;
|
||||
}
|
||||
|
||||
session: Session;
|
||||
user: User;
|
||||
guildId: Snowflake;
|
||||
status: StatusTypes;
|
||||
activities: Activities[];
|
||||
clientStatus: DiscordClientStatus;
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { DiscordRole } from '@biscuitland/api-types';
|
||||
import type { ModifyGuildRole } from './guilds';
|
||||
import { Snowflake } from '../snowflakes';
|
||||
import { Guild } from './guilds';
|
||||
import { Util } from '../utils/util';
|
||||
import { Permissions } from './special/permissions';
|
||||
|
||||
export class Role implements Model {
|
||||
constructor(session: Session, data: DiscordRole, guildId: Snowflake) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.guildId = guildId;
|
||||
this.hoist = data.hoist;
|
||||
this.iconHash = data.icon
|
||||
? Util.iconHashToBigInt(data.icon)
|
||||
: undefined;
|
||||
this.color = data.color;
|
||||
this.name = data.name;
|
||||
this.unicodeEmoji = data.unicode_emoji;
|
||||
this.mentionable = data.mentionable;
|
||||
this.managed = data.managed;
|
||||
this.permissions = new Permissions(BigInt(data.permissions));
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
|
||||
readonly guildId: Snowflake;
|
||||
|
||||
hoist: boolean;
|
||||
iconHash?: bigint;
|
||||
color: number;
|
||||
name: string;
|
||||
unicodeEmoji?: string;
|
||||
mentionable: boolean;
|
||||
managed: boolean;
|
||||
permissions: Permissions;
|
||||
|
||||
get createdTimestamp(): number {
|
||||
return Snowflake.snowflakeToTimestamp(this.id);
|
||||
}
|
||||
|
||||
get createdAt(): Date {
|
||||
return new Date(this.createdTimestamp);
|
||||
}
|
||||
|
||||
get hexColor(): string {
|
||||
return `#${this.color.toString(16).padStart(6, '0')}`;
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
await Guild.prototype.deleteRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.id
|
||||
);
|
||||
}
|
||||
|
||||
async edit(options: ModifyGuildRole): Promise<Role> {
|
||||
const role = await Guild.prototype.editRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.id,
|
||||
options
|
||||
);
|
||||
return role;
|
||||
}
|
||||
|
||||
async add(memberId: Snowflake, reason?: string): Promise<void> {
|
||||
await Guild.prototype.addRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
memberId,
|
||||
this.id,
|
||||
reason
|
||||
);
|
||||
}
|
||||
|
||||
async remove(memberId: Snowflake, reason?: string): Promise<void> {
|
||||
await Guild.prototype.removeRole.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
memberId,
|
||||
this.id,
|
||||
reason
|
||||
);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
switch (this.id) {
|
||||
case this.guildId:
|
||||
return '@everyone';
|
||||
default:
|
||||
return `<@&${this.id}>`;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type {
|
||||
DiscordScheduledEvent,
|
||||
DiscordScheduledEventEntityMetadata,
|
||||
ScheduledEventEntityType,
|
||||
ScheduledEventStatus,
|
||||
} from '@biscuitland/api-types';
|
||||
import { PrivacyLevels } from './stage-instance';
|
||||
import { User } from './user';
|
||||
|
||||
export class ScheduledEvent implements Model {
|
||||
constructor(session: Session, data: DiscordScheduledEvent) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.guildId = data.guild_id;
|
||||
this.channelId = data.channel_id;
|
||||
this.creatorId = data.creator_id ? data.creator_id : undefined;
|
||||
this.name = data.name;
|
||||
this.description = data.description;
|
||||
this.scheduledStartTime = data.scheduled_start_time;
|
||||
this.scheduledEndTime = data.scheduled_end_time;
|
||||
this.privacyLevel = PrivacyLevels.GuildOnly;
|
||||
this.status = data.status;
|
||||
this.entityType = data.entity_type;
|
||||
this.entityMetadata = data.entity_metadata
|
||||
? data.entity_metadata
|
||||
: undefined;
|
||||
this.creator = data.creator
|
||||
? new User(session, data.creator)
|
||||
: undefined;
|
||||
this.userCount = data.user_count;
|
||||
this.image = data.image ? data.image : undefined;
|
||||
}
|
||||
|
||||
session: Session;
|
||||
id: Snowflake;
|
||||
guildId: Snowflake;
|
||||
channelId: Snowflake | null;
|
||||
creatorId?: Snowflake;
|
||||
name: string;
|
||||
description?: string;
|
||||
scheduledStartTime: string;
|
||||
scheduledEndTime: string | null;
|
||||
privacyLevel: PrivacyLevels;
|
||||
status: ScheduledEventStatus;
|
||||
entityType: ScheduledEventEntityType;
|
||||
entityMetadata?: DiscordScheduledEventEntityMetadata;
|
||||
creator?: User;
|
||||
userCount?: number;
|
||||
image?: string;
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
import type {
|
||||
DiscordInteractionDataOption,
|
||||
DiscordInteractionDataResolved,
|
||||
Snowflake,
|
||||
} from '@biscuitland/api-types';
|
||||
import { ApplicationCommandOptionTypes } from '@biscuitland/api-types';
|
||||
|
||||
/**
|
||||
* Utility class to get the resolved options for a command
|
||||
* @example const option = ctx.options.getStringOption("name");
|
||||
*/
|
||||
export class InteractionOptions {
|
||||
private _subcommand?: string;
|
||||
private _group?: string;
|
||||
|
||||
hoistedOptions: DiscordInteractionDataOption[];
|
||||
resolved?: DiscordInteractionDataResolved;
|
||||
|
||||
constructor(
|
||||
options?: DiscordInteractionDataOption[],
|
||||
resolved?: DiscordInteractionDataResolved
|
||||
) {
|
||||
this.hoistedOptions = options ?? [];
|
||||
|
||||
// warning: black magic do not edit and thank djs authors
|
||||
|
||||
if (
|
||||
this.hoistedOptions[0]?.type ===
|
||||
ApplicationCommandOptionTypes.SubCommandGroup
|
||||
) {
|
||||
this._group = this.hoistedOptions[0].name;
|
||||
this.hoistedOptions = this.hoistedOptions[0].options ?? [];
|
||||
}
|
||||
|
||||
if (
|
||||
this.hoistedOptions[0]?.type ===
|
||||
ApplicationCommandOptionTypes.SubCommand
|
||||
) {
|
||||
this._subcommand = this.hoistedOptions[0].name;
|
||||
this.hoistedOptions = this.hoistedOptions[0].options ?? [];
|
||||
}
|
||||
|
||||
this.resolved = resolved;
|
||||
}
|
||||
|
||||
private getTypedOption(
|
||||
name: string | number,
|
||||
type: ApplicationCommandOptionTypes,
|
||||
properties: (keyof DiscordInteractionDataOption)[],
|
||||
required: boolean
|
||||
): DiscordInteractionDataOption | void {
|
||||
const option: DiscordInteractionDataOption | undefined = this.get(
|
||||
name,
|
||||
required
|
||||
);
|
||||
|
||||
if (!option) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (option.type !== type) {
|
||||
// pass
|
||||
}
|
||||
|
||||
if (
|
||||
required === true &&
|
||||
properties.every(prop => typeof option[prop] === 'undefined')
|
||||
) {
|
||||
throw new TypeError(
|
||||
`Properties ${properties.join(
|
||||
', '
|
||||
)} are missing in option ${name}`
|
||||
);
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
get(name: string | number, required: true): DiscordInteractionDataOption;
|
||||
get(
|
||||
name: string | number,
|
||||
required: boolean
|
||||
): DiscordInteractionDataOption | undefined;
|
||||
|
||||
get(name: string | number, required?: boolean) {
|
||||
const option: DiscordInteractionDataOption | undefined =
|
||||
this.hoistedOptions.find(o =>
|
||||
typeof name === 'number'
|
||||
? o.name === name.toString()
|
||||
: o.name === name
|
||||
);
|
||||
|
||||
if (!option) {
|
||||
if (required && name in this.hoistedOptions.map(o => o.name)) {
|
||||
throw new TypeError('Option marked as required was undefined');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
/** searches for a string option */
|
||||
getString(name: string | number, required: true): string;
|
||||
getString(name: string | number, required?: boolean): string | undefined;
|
||||
getString(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.String,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a number option */
|
||||
getNumber(name: string | number, required: true): number;
|
||||
getNumber(name: string | number, required?: boolean): number | undefined;
|
||||
getNumber(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Number,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searhces for an integer option */
|
||||
getInteger(name: string | number, required: true): number;
|
||||
getInteger(name: string | number, required?: boolean): number | undefined;
|
||||
getInteger(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Integer,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a boolean option */
|
||||
getBoolean(name: string | number, required: true): boolean;
|
||||
getBoolean(name: string | number, required?: boolean): boolean | undefined;
|
||||
getBoolean(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Boolean,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a user option */
|
||||
getUser(name: string | number, required: true): Snowflake;
|
||||
getUser(name: string | number, required?: boolean): Snowflake | undefined;
|
||||
getUser(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.User,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a channel option */
|
||||
getChannel(name: string | number, required: true): Snowflake;
|
||||
getChannel(name: string | number, required?: boolean): Snowflake | undefined;
|
||||
getChannel(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Channel,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a mentionable-based option */
|
||||
getMentionable(name: string | number, required: true): string;
|
||||
getMentionable(
|
||||
name: string | number,
|
||||
required?: boolean
|
||||
): string | undefined;
|
||||
|
||||
getMentionable(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Mentionable,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for a mentionable-based option */
|
||||
getRole(name: string | number, required: true): Snowflake;
|
||||
getRole(name: string | number, required?: boolean): Snowflake | undefined;
|
||||
getRole(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Role,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for an attachment option */
|
||||
getAttachment(name: string | number, required: true): Snowflake;
|
||||
getAttachment(name: string | number, required?: boolean): Snowflake | undefined;
|
||||
getAttachment(name: string | number, required = false) {
|
||||
const option: DiscordInteractionDataOption | void = this.getTypedOption(
|
||||
name,
|
||||
ApplicationCommandOptionTypes.Attachment,
|
||||
['value'],
|
||||
required
|
||||
);
|
||||
|
||||
return option?.value ?? undefined;
|
||||
}
|
||||
|
||||
/** searches for the focused option */
|
||||
getFocused(full: true): DiscordInteractionDataOption;
|
||||
getFocused(full: false): DiscordInteractionDataOption['value'];
|
||||
getFocused(full = false) {
|
||||
const focusedOption: DiscordInteractionDataOption | void =
|
||||
this.hoistedOptions.find(option => option.focused);
|
||||
|
||||
if (!focusedOption) {
|
||||
throw new TypeError('No option found');
|
||||
}
|
||||
|
||||
return full ? focusedOption : focusedOption.value;
|
||||
}
|
||||
|
||||
getSubCommand(
|
||||
required = true
|
||||
): [string | undefined, DiscordInteractionDataOption[]] {
|
||||
if (required && !this._subcommand) {
|
||||
throw new TypeError('Option marked as required was undefined');
|
||||
}
|
||||
|
||||
return [this._subcommand, this.hoistedOptions];
|
||||
}
|
||||
|
||||
getSubCommandGroup(
|
||||
required = false
|
||||
): [string | undefined, DiscordInteractionDataOption[]] {
|
||||
if (required && !this._group) {
|
||||
throw new TypeError('Option marked as required was undefined');
|
||||
}
|
||||
|
||||
return [this._group, this.hoistedOptions];
|
||||
}
|
||||
}
|
@ -1,147 +0,0 @@
|
||||
import { BitwisePermissionFlags } from '@biscuitland/api-types';
|
||||
|
||||
export interface BitField<T extends number | bigint> {
|
||||
add(...bits: T[]): this;
|
||||
remove(...bits: T[]): this;
|
||||
has(bit: T): boolean;
|
||||
any(bit: T): boolean;
|
||||
equals(bit: T): boolean;
|
||||
}
|
||||
|
||||
export type PermissionString = keyof typeof BitwisePermissionFlags;
|
||||
export type PermissionResolvable =
|
||||
| bigint
|
||||
| PermissionString
|
||||
| PermissionString[]
|
||||
| BitwisePermissionFlags
|
||||
| BitwisePermissionFlags[];
|
||||
|
||||
export class Permissions implements BitField<bigint> {
|
||||
/** Stores a reference to BitwisePermissionFlags */
|
||||
static Flags = BitwisePermissionFlags;
|
||||
|
||||
/** Falsy; Stores the lack of permissions*/
|
||||
static None = 0n;
|
||||
|
||||
/** Stores all entity permissions */
|
||||
bitfield: bigint;
|
||||
|
||||
/**
|
||||
* Wheter to grant all other permissions to the administrator
|
||||
* **Not to get confused with Permissions#admin**
|
||||
*/
|
||||
__admin__ = true;
|
||||
|
||||
constructor(bitfield: PermissionResolvable) {
|
||||
this.bitfield = Permissions.resolve(bitfield);
|
||||
}
|
||||
|
||||
/** Wheter the bitfield has the administrator flag */
|
||||
get admin(): boolean {
|
||||
return this.has(Permissions.Flags.ADMINISTRATOR);
|
||||
}
|
||||
|
||||
get array(): PermissionString[] {
|
||||
// unsafe cast, do not edit
|
||||
const permissions = Object.keys(Permissions.Flags) as PermissionString[];
|
||||
return permissions.filter(bit => this.has(bit));
|
||||
}
|
||||
|
||||
add(...bits: PermissionResolvable[]): this {
|
||||
let reduced = 0n;
|
||||
for (const bit of bits) {
|
||||
reduced |= Permissions.resolve(bit);
|
||||
}
|
||||
this.bitfield |= reduced;
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(...bits: PermissionResolvable[]): this {
|
||||
let reduced = 0n;
|
||||
for (const bit of bits) {
|
||||
reduced |= Permissions.resolve(bit);
|
||||
}
|
||||
this.bitfield &= ~reduced;
|
||||
return this;
|
||||
}
|
||||
|
||||
has(bit: PermissionResolvable): boolean {
|
||||
const bbit = Permissions.resolve(bit);
|
||||
|
||||
if (this.__admin__ && this.bitfield & BigInt(Permissions.Flags.ADMINISTRATOR)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (this.bitfield & bbit) === bbit;
|
||||
}
|
||||
|
||||
any(bit: PermissionResolvable): boolean {
|
||||
const bbit = Permissions.resolve(bit);
|
||||
|
||||
if (this.__admin__ && this.bitfield & BigInt(Permissions.Flags.ADMINISTRATOR)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (this.bitfield & bbit) !== Permissions.None;
|
||||
}
|
||||
|
||||
equals(bit: PermissionResolvable): boolean {
|
||||
return !!(this.bitfield & Permissions.resolve(bit));
|
||||
}
|
||||
|
||||
/** Gets all permissions */
|
||||
static get All(): bigint {
|
||||
let reduced = 0n;
|
||||
for (const key in BitwisePermissionFlags) {
|
||||
const perm = BitwisePermissionFlags[key];
|
||||
|
||||
if (typeof perm === 'number') {
|
||||
reduced += perm;
|
||||
}
|
||||
}
|
||||
return reduced;
|
||||
}
|
||||
|
||||
static resolve(bit: PermissionResolvable): bigint {
|
||||
switch (typeof bit) {
|
||||
case 'bigint':
|
||||
return bit;
|
||||
case 'number':
|
||||
return BigInt(bit);
|
||||
case 'string':
|
||||
return BigInt(Permissions.Flags[bit]);
|
||||
case 'object':
|
||||
return Permissions.resolve(
|
||||
bit
|
||||
.map(p => typeof p === 'string' ? BigInt(Permissions.Flags[p]) : BigInt(p))
|
||||
.reduce((acc, cur) => acc | cur, Permissions.None)
|
||||
);
|
||||
default:
|
||||
throw new TypeError(`Cannot resolve permission: ${bit}`);
|
||||
}
|
||||
}
|
||||
|
||||
static sum(permissions: (bigint | number)[]) {
|
||||
return permissions.reduce((y, x) => BigInt(y) | BigInt(x), Permissions.None);
|
||||
}
|
||||
|
||||
static reduce(permissions: PermissionResolvable[]): Permissions {
|
||||
const solved = permissions.map(Permissions.resolve);
|
||||
|
||||
return new Permissions(solved.reduce((y, x) => y | x, Permissions.None));
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
yield* this.array;
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.bitfield;
|
||||
}
|
||||
|
||||
toJSON(): { fields: string[] } {
|
||||
const fields = Object.keys(Permissions.Flags).filter(bit => typeof bit === 'number' && this.has(bit));
|
||||
|
||||
return { fields };
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { DiscordStageInstance as DiscordAutoClosingStageInstance } from '@biscuitland/api-types';
|
||||
import { STAGE_INSTANCE } from '@biscuitland/api-types';
|
||||
|
||||
export interface DiscordStageInstanceB extends DiscordAutoClosingStageInstance {
|
||||
privacy_level: PrivacyLevels;
|
||||
discoverable_disabled: boolean;
|
||||
guild_scheduled_event_id: Snowflake;
|
||||
}
|
||||
|
||||
export enum PrivacyLevels {
|
||||
Public = 1,
|
||||
GuildOnly = 2,
|
||||
}
|
||||
|
||||
export type StageEditOptions = {
|
||||
topic?: string;
|
||||
privacy?: PrivacyLevels;
|
||||
};
|
||||
|
||||
export class StageInstance implements Model {
|
||||
constructor(session: Session, data: DiscordStageInstanceB) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.channelId = data.channel_id;
|
||||
this.guildId = data.guild_id;
|
||||
this.topic = data.topic;
|
||||
this.privacyLevel = data.privacy_level;
|
||||
this.discoverableDisabled = data.discoverable_disabled;
|
||||
this.guildScheduledEventId = data.guild_scheduled_event_id;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
|
||||
channelId: Snowflake;
|
||||
guildId: Snowflake;
|
||||
topic: string;
|
||||
|
||||
// TODO: see if this works
|
||||
privacyLevel: PrivacyLevels;
|
||||
discoverableDisabled: boolean;
|
||||
guildScheduledEventId: Snowflake;
|
||||
|
||||
async edit(options: StageEditOptions): Promise<StageInstance> {
|
||||
const stageInstance =
|
||||
await this.session.rest.patch<DiscordStageInstanceB>(
|
||||
STAGE_INSTANCE(this.id),
|
||||
{
|
||||
topic: options.topic,
|
||||
privacy_level: options.privacy,
|
||||
}
|
||||
);
|
||||
|
||||
return new StageInstance(this.session, stageInstance);
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
await this.session.rest.delete<undefined>(STAGE_INSTANCE(this.id), {});
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
import type {
|
||||
DiscordSticker,
|
||||
DiscordStickerPack,
|
||||
StickerFormatTypes,
|
||||
StickerTypes,
|
||||
} from '@biscuitland/api-types';
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import { User } from './user';
|
||||
import { STICKER_PACKS } from '@biscuitland/api-types';
|
||||
|
||||
export interface StickerItem {
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
formatType: StickerFormatTypes;
|
||||
}
|
||||
|
||||
export interface StickerPack {
|
||||
id: Snowflake;
|
||||
stickers: Sticker[];
|
||||
name: string;
|
||||
skuId: Snowflake;
|
||||
coverStickerId?: Snowflake;
|
||||
description: string;
|
||||
bannerAssetId?: Snowflake;
|
||||
}
|
||||
|
||||
export class Sticker implements Model {
|
||||
constructor(session: Session, data: DiscordSticker) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.packId = data.pack_id;
|
||||
this.name = data.name;
|
||||
this.description = data.description;
|
||||
this.tags = data.tags.split(',');
|
||||
this.type = data.type;
|
||||
this.formatType = data.format_type;
|
||||
this.available = !!data.available;
|
||||
this.guildId = data.guild_id;
|
||||
this.user = data.user ? new User(this.session, data.user) : undefined;
|
||||
this.sortValue = data.sort_value;
|
||||
}
|
||||
|
||||
session: Session;
|
||||
id: Snowflake;
|
||||
packId?: Snowflake;
|
||||
name: string;
|
||||
description?: string;
|
||||
tags: string[];
|
||||
type: StickerTypes;
|
||||
formatType: StickerFormatTypes;
|
||||
available?: boolean;
|
||||
guildId?: Snowflake;
|
||||
user?: User;
|
||||
sortValue?: number;
|
||||
|
||||
async fetchPremiumPack(): Promise<StickerPack> {
|
||||
const data = await this.session.rest.get<DiscordStickerPack>(
|
||||
STICKER_PACKS()
|
||||
);
|
||||
|
||||
return {
|
||||
id: data.id,
|
||||
stickers: data.stickers.map(st => new Sticker(this.session, st)),
|
||||
name: data.name,
|
||||
skuId: data.sku_id,
|
||||
coverStickerId: data.cover_sticker_id,
|
||||
description: data.description,
|
||||
bannerAssetId: data.banner_asset_id,
|
||||
};
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { DiscordUser, PremiumTypes, UserFlags } from '@biscuitland/api-types';
|
||||
import type { ImageFormat, ImageSize } from '../utils/util';
|
||||
import { USER, USER_AVATAR, USER_DEFAULT_AVATAR } from '@biscuitland/api-types';
|
||||
import { Util } from '../utils/util';
|
||||
import { DMChannel } from './channels';
|
||||
|
||||
export type AvatarOptions = {
|
||||
format?: ImageFormat;
|
||||
size?: ImageSize;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a user
|
||||
* @link https://discord.com/developers/docs/resources/user#user-object
|
||||
*/
|
||||
export class User implements Model {
|
||||
constructor(session: Session, data: DiscordUser) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
|
||||
this.username = data.username;
|
||||
this.discriminator = data.discriminator;
|
||||
this.avatarHash = data.avatar
|
||||
? data.avatar
|
||||
: undefined;
|
||||
|
||||
this.accentColor = data.accent_color;
|
||||
this.bot = !!data.bot;
|
||||
this.system = !!data.system;
|
||||
this.banner = data.banner
|
||||
? data.banner
|
||||
: undefined;
|
||||
|
||||
this.mfaEnabled = !!data.mfa_enabled;
|
||||
this.locale = data.locale;
|
||||
this.email = data.email ? data.email : undefined;
|
||||
this.verified = !!data.verified;
|
||||
this.flags = data.flags;
|
||||
}
|
||||
|
||||
/** the session that instantiated this User */
|
||||
readonly session: Session;
|
||||
|
||||
/** the user's id */
|
||||
readonly id: Snowflake;
|
||||
|
||||
/** the user's username, not unique across the platform */
|
||||
username: string;
|
||||
|
||||
/** the user's 4-digit discord-tag */
|
||||
discriminator: string;
|
||||
|
||||
/** the user's avatar hash */
|
||||
avatarHash?: string;
|
||||
|
||||
/** the user's banner color encoded as an integer representation of hexadecimal color code */
|
||||
accentColor?: number;
|
||||
|
||||
/** whether the user belongs to an OAuth2 application */
|
||||
bot: boolean;
|
||||
|
||||
/** whether the user is an Official Discord System user (part of the urgent message system) */
|
||||
system: boolean;
|
||||
|
||||
/** the user's banner hash */
|
||||
banner?: string;
|
||||
|
||||
/** whether the user has two factor enabled on their account */
|
||||
mfaEnabled: boolean;
|
||||
|
||||
/** the user's chosen language option */
|
||||
locale?: string;
|
||||
|
||||
/** the user's email */
|
||||
email?: string;
|
||||
|
||||
/** the flags on a user's account */
|
||||
flags?: UserFlags;
|
||||
|
||||
/** whether the email on this account has been verified */
|
||||
verified: boolean;
|
||||
|
||||
/** the type of Nitro subscription on a user's account */
|
||||
premiumType?: PremiumTypes;
|
||||
|
||||
/** the public flags on a user's account */
|
||||
publicFlags?: UserFlags;
|
||||
|
||||
/** gets the user's username#discriminator */
|
||||
get tag(): string {
|
||||
return `${this.username}#${this.discriminator}`;
|
||||
}
|
||||
|
||||
/** fetches this user */
|
||||
async fetch(): Promise<User> {
|
||||
const user = await this.session.rest.get<DiscordUser>(USER(this.id));
|
||||
|
||||
return new User(this.session, user);
|
||||
}
|
||||
|
||||
/** gets the user's avatar */
|
||||
avatarURL(options: AvatarOptions): string {
|
||||
if (!this.avatarHash) {
|
||||
return USER_DEFAULT_AVATAR(Number(this.discriminator) % 5);
|
||||
}
|
||||
|
||||
return Util.formatImageURL(USER_AVATAR(
|
||||
this.id,
|
||||
this.avatarHash
|
||||
), options.size ?? 128, options.format);
|
||||
}
|
||||
|
||||
openDM(): Promise<DMChannel> {
|
||||
return DMChannel.prototype.open.call({ session: this.session }, this.id);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<@${this.id}>`;
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type {
|
||||
DiscordEmbed,
|
||||
DiscordMessage,
|
||||
DiscordMessageComponents,
|
||||
DiscordWebhook,
|
||||
FileContent,
|
||||
WebhookTypes,
|
||||
WebhookOptions,
|
||||
} from '@biscuitland/api-types';
|
||||
import type { Attachment } from './attachment';
|
||||
import type { AllowedMentions, CreateMessage } from './message';
|
||||
import { User } from './user';
|
||||
import { Message } from './message';
|
||||
import { Util } from '../utils/util';
|
||||
import {
|
||||
WEBHOOK,
|
||||
WEBHOOK_TOKEN,
|
||||
WEBHOOK_MESSAGE,
|
||||
WEBHOOK_MESSAGE_ORIGINAL,
|
||||
} from '@biscuitland/api-types';
|
||||
import { NewEmbed } from './embed';
|
||||
|
||||
export type ExecuteWebhookOptions = WebhookOptions &
|
||||
CreateMessage & { avatarUrl?: string; username?: string };
|
||||
export type EditMessageWithThread = EditWebhookMessage & {
|
||||
threadId?: Snowflake;
|
||||
};
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/webhook#edit-webhook-message-jsonform-params
|
||||
*/
|
||||
export interface EditWebhookMessage {
|
||||
content?: string;
|
||||
embeds?: DiscordEmbed[];
|
||||
files?: FileContent[];
|
||||
allowedMentions?: AllowedMentions;
|
||||
attachments?: Attachment[];
|
||||
components?: DiscordMessageComponents;
|
||||
}
|
||||
|
||||
export class Webhook implements Model {
|
||||
constructor(session: Session, data: DiscordWebhook) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.type = data.type;
|
||||
this.token = data.token;
|
||||
|
||||
if (data.avatar) {
|
||||
this.avatar = Util.iconHashToBigInt(data.avatar);
|
||||
}
|
||||
|
||||
if (data.user) {
|
||||
this.user = new User(session, data.user);
|
||||
}
|
||||
|
||||
if (data.guild_id) {
|
||||
this.guildId = data.guild_id;
|
||||
}
|
||||
|
||||
if (data.channel_id) {
|
||||
this.channelId = data.channel_id;
|
||||
}
|
||||
|
||||
if (data.application_id) {
|
||||
this.applicationId = data.application_id;
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
readonly id: Snowflake;
|
||||
type: WebhookTypes;
|
||||
token?: string;
|
||||
avatar?: bigint;
|
||||
applicationId?: Snowflake;
|
||||
channelId?: Snowflake;
|
||||
guildId?: Snowflake;
|
||||
user?: User;
|
||||
|
||||
async execute(
|
||||
options?: ExecuteWebhookOptions
|
||||
): Promise<Message | undefined> {
|
||||
if (!this.token) {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
content: options?.content,
|
||||
embeds: options?.embeds?.map(NewEmbed),
|
||||
tts: options?.tts,
|
||||
allowed_mentions: options?.allowedMentions,
|
||||
components: options?.components,
|
||||
file: options?.files,
|
||||
};
|
||||
|
||||
const message = await this.session.rest.post<DiscordMessage>(
|
||||
WEBHOOK(this.id, this.token, {
|
||||
wait: options?.wait,
|
||||
threadId: options?.threadId,
|
||||
}),
|
||||
data
|
||||
);
|
||||
|
||||
return options?.wait ?? true
|
||||
? new Message(this.session, message)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
async fetch(): Promise<Webhook> {
|
||||
const message = await this.session.rest.get<DiscordWebhook>(
|
||||
WEBHOOK_TOKEN(this.id, this.token)
|
||||
);
|
||||
|
||||
return new Webhook(this.session, message);
|
||||
}
|
||||
|
||||
async fetchMessage(
|
||||
messageId: Snowflake,
|
||||
threadId?: Snowflake
|
||||
): Promise<Message | undefined> {
|
||||
if (!this.token) {
|
||||
return;
|
||||
}
|
||||
|
||||
const message = await this.session.rest.get<DiscordMessage>(
|
||||
WEBHOOK_MESSAGE(this.id, this.token, messageId, { threadId })
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated you might want to delete an ephemeral message (deleteFollowUp)
|
||||
* */
|
||||
deleteMessage(
|
||||
messageId: Snowflake,
|
||||
threadId?: Snowflake
|
||||
): Promise<void> {
|
||||
return this.deleteThreadMessage(messageId, threadId);
|
||||
}
|
||||
|
||||
async deleteThreadMessage(
|
||||
messageId: Snowflake,
|
||||
threadId?: Snowflake
|
||||
): Promise<void> {
|
||||
if (!this.token) {
|
||||
throw new Error('No token found');
|
||||
}
|
||||
|
||||
await this.session.rest.delete<undefined>(
|
||||
WEBHOOK_MESSAGE(this.id, this.token, messageId, { threadId }),
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
async deleteFollowUp(messageId?: Snowflake): Promise<void> {
|
||||
if (!this.token) {
|
||||
throw new Error('No token found');
|
||||
}
|
||||
|
||||
await this.session.rest.delete(
|
||||
messageId
|
||||
? WEBHOOK_MESSAGE(this.id, this.token, messageId)
|
||||
: WEBHOOK_MESSAGE_ORIGINAL(this.id, this.token)
|
||||
);
|
||||
}
|
||||
|
||||
async editMessage(
|
||||
messageId?: Snowflake,
|
||||
options?: EditMessageWithThread
|
||||
): Promise<Message> {
|
||||
if (!this.token) {
|
||||
throw new Error('No token found');
|
||||
}
|
||||
|
||||
const message = await this.session.rest.patch<DiscordMessage>(
|
||||
messageId
|
||||
? WEBHOOK_MESSAGE(this.id, this.token, messageId)
|
||||
: WEBHOOK_MESSAGE_ORIGINAL(this.id, this.token),
|
||||
{
|
||||
content: options?.content,
|
||||
embeds: options?.embeds?.map(NewEmbed),
|
||||
file: options?.files,
|
||||
components: options?.components,
|
||||
allowed_mentions: options?.allowedMentions && {
|
||||
parse: options?.allowedMentions.parse,
|
||||
replied_user: options?.allowedMentions.repliedUser,
|
||||
users: options?.allowedMentions.users,
|
||||
roles: options?.allowedMentions.roles,
|
||||
},
|
||||
attachments: options?.attachments?.map(attachment => {
|
||||
return {
|
||||
id: attachment.id,
|
||||
filename: attachment.name,
|
||||
content_type: attachment.contentType,
|
||||
size: attachment.size,
|
||||
url: attachment.attachment,
|
||||
proxy_url: attachment.proxyUrl,
|
||||
height: attachment.height,
|
||||
width: attachment.width,
|
||||
ephemeral: attachment.ephemeral,
|
||||
};
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Model } from './base';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type {
|
||||
DiscordWelcomeScreen,
|
||||
DiscordWelcomeScreenChannel,
|
||||
} from '@biscuitland/api-types';
|
||||
import { Emoji } from './emojis';
|
||||
|
||||
/**
|
||||
* Not a channel
|
||||
* @link https://discord.com/developers/docs/resources/guild#welcome-screen-object-welcome-screen-channel-structure
|
||||
*/
|
||||
export class WelcomeChannel implements Model {
|
||||
constructor(session: Session, data: DiscordWelcomeScreenChannel) {
|
||||
this.session = session;
|
||||
this.channelId = data.channel_id;
|
||||
this.description = data.description;
|
||||
this.emoji = new Emoji(session, {
|
||||
name: data.emoji_name ? data.emoji_name : undefined,
|
||||
id: data.emoji_id ? data.emoji_id : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
session: Session;
|
||||
channelId: Snowflake;
|
||||
description: string;
|
||||
emoji: Emoji;
|
||||
|
||||
/** alias for WelcomeScreenChannel.channelId */
|
||||
get id(): Snowflake {
|
||||
return this.channelId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#welcome-screen-object
|
||||
*/
|
||||
export class WelcomeScreen {
|
||||
constructor(session: Session, data: DiscordWelcomeScreen) {
|
||||
this.session = session;
|
||||
this.welcomeChannels = data.welcome_channels.map(
|
||||
welcomeChannel => new WelcomeChannel(session, welcomeChannel)
|
||||
);
|
||||
|
||||
if (data.description) {
|
||||
this.description = data.description;
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
|
||||
description?: string;
|
||||
welcomeChannels: WelcomeChannel[];
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import type { Model } from './base';
|
||||
import type { Session } from '../biscuit';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { DiscordGuildWidget } from '@biscuitland/api-types';
|
||||
import type { PartialChannel } from './channels';
|
||||
|
||||
export interface WidgetMember {
|
||||
id?: string;
|
||||
username: string;
|
||||
avatar?: string | null;
|
||||
status: string;
|
||||
avatarURL: string;
|
||||
}
|
||||
|
||||
export class Widget implements Model {
|
||||
constructor(session: Session, data: DiscordGuildWidget) {
|
||||
this.session = session;
|
||||
this.id = data.id;
|
||||
this.name = data.name;
|
||||
this.instantInvite = data.instant_invite;
|
||||
this.channels = data.channels;
|
||||
this.members = data.members.map(x => {
|
||||
return {
|
||||
id: x.id,
|
||||
username: x.username,
|
||||
avatar: x.avatar,
|
||||
status: x.status,
|
||||
avatarURL: x.avatar_url,
|
||||
};
|
||||
});
|
||||
this.presenceCount = data.presence_count;
|
||||
}
|
||||
|
||||
session: Session;
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
instantInvite?: string;
|
||||
channels: PartialChannel[];
|
||||
members: WidgetMember[];
|
||||
presenceCount: number;
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
export function calculateShardId(totalShards: number, guildId: bigint) {
|
||||
if (totalShards === 1) { return 0; }
|
||||
|
||||
return Number((guildId >> 22n) % BigInt(totalShards - 1));
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/** Converts a url to base 64. Useful for example, uploading/creating server emojis. */
|
||||
export async function urlToBase64(url: string): Promise<string> {
|
||||
const buffer = await fetch(url).then(res => res.arrayBuffer());
|
||||
const imageStr = encode(buffer);
|
||||
const type = url.substring(url.lastIndexOf('.') + 1);
|
||||
return `data:image/${type};base64,${imageStr}`;
|
||||
}
|
||||
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
const base64abc: string[] = [
|
||||
'A', 'B', 'C',
|
||||
'D', 'E', 'F',
|
||||
'G', 'H', 'I',
|
||||
'J', 'K', 'L',
|
||||
'M', 'N', 'O',
|
||||
'P', 'Q', 'R',
|
||||
'S', 'T', 'U',
|
||||
'V', 'W', 'X',
|
||||
'Y', 'Z', 'a',
|
||||
'b', 'c', 'd',
|
||||
'e', 'f', 'g',
|
||||
'h', 'i', 'j',
|
||||
'k', 'l', 'm',
|
||||
'n', 'o', 'p',
|
||||
'q', 'r', 's',
|
||||
't', 'u', 'v',
|
||||
'w', 'x', 'y',
|
||||
'z', '0', '1',
|
||||
'2', '3', '4',
|
||||
'5', '6', '7',
|
||||
'8', '9', '+', '/',
|
||||
];
|
||||
|
||||
/**
|
||||
* CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
|
||||
* Encodes a given Uint8Array, ArrayBuffer or string into RFC4648 base64 representation
|
||||
* @param data
|
||||
*/
|
||||
export function encode(data: ArrayBuffer | string): string {
|
||||
const uint8: Uint8Array = typeof data === 'string'
|
||||
? new TextEncoder().encode(data)
|
||||
: data instanceof Uint8Array
|
||||
? data
|
||||
: new Uint8Array(data);
|
||||
let result = '',
|
||||
i;
|
||||
const l: number = uint8.length;
|
||||
for (i = 2; i < l; i += 3) {
|
||||
result += base64abc[uint8[i - 2] >> 2];
|
||||
result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];
|
||||
result += base64abc[((uint8[i - 1] & 0x0f) << 2) | (uint8[i] >> 6)];
|
||||
result += base64abc[uint8[i] & 0x3f];
|
||||
}
|
||||
if (i === l + 1) {
|
||||
// 1 octet yet to write
|
||||
result += base64abc[uint8[i - 2] >> 2];
|
||||
result += base64abc[(uint8[i - 2] & 0x03) << 4];
|
||||
result += '==';
|
||||
}
|
||||
if (i === l) {
|
||||
// 2 octets yet to write
|
||||
result += base64abc[uint8[i - 2] >> 2];
|
||||
result += base64abc[((uint8[i - 2] & 0x03) << 4) | (uint8[i - 1] >> 4)];
|
||||
result += base64abc[(uint8[i - 1] & 0x0f) << 2];
|
||||
result += '=';
|
||||
}
|
||||
return result;
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
import type { SelectMenuBuilder, InputTextBuilder, ButtonBuilder } from '@biscuitland/helpers';
|
||||
import type { Permissions } from '../structures/special/permissions';
|
||||
import type { Snowflake } from '../snowflakes';
|
||||
import type { DiscordMessage, MakeRequired } from '@biscuitland/api-types';
|
||||
|
||||
/*
|
||||
* @link https://discord.com/developers/docs/resources/channel#message-object-message-flags
|
||||
*/
|
||||
export enum MessageFlags {
|
||||
/** this message has been published to subscribed channels (via Channel Following) */
|
||||
CrossPosted = 1 << 0,
|
||||
/** this message originated from a message in another channel (via Channel Following) */
|
||||
IsCrosspost = 1 << 1,
|
||||
/** do not include any embeds when serializing this message */
|
||||
SupressEmbeds = 1 << 2,
|
||||
/** the source message for this crosspost has been deleted (via Channel Following) */
|
||||
SourceMessageDeleted = 1 << 3,
|
||||
/** this message came from the urgent message system */
|
||||
Urgent = 1 << 4,
|
||||
/** this message has an associated thread, with the same id as the message */
|
||||
HasThread = 1 << 5,
|
||||
/** this message is only visible to the user who invoked the Interaction */
|
||||
Ephemeral = 1 << 6,
|
||||
/** this message is an Interaction Response and the bot is "thinking" */
|
||||
Loading = 1 << 7,
|
||||
/** this message failed to mention some roles and add their members to the thread */
|
||||
FailedToMentionSomeRolesInThread = 1 << 8,
|
||||
}
|
||||
|
||||
export type ComponentBuilder =
|
||||
| InputTextBuilder
|
||||
| SelectMenuBuilder
|
||||
| ButtonBuilder;
|
||||
|
||||
/** *
|
||||
* Utility type
|
||||
*/
|
||||
export type ComponentEmoji = {
|
||||
id: Snowflake;
|
||||
name: string;
|
||||
animated?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility type
|
||||
*/
|
||||
export interface PermissionsOverwrites {
|
||||
id: Snowflake;
|
||||
type: 0 | 1;
|
||||
allow: Permissions;
|
||||
deny: Permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/reference#image-formatting
|
||||
*/
|
||||
export type ImageFormat = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' | 'json';
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/reference#image-formatting
|
||||
*/
|
||||
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;
|
||||
|
||||
/**
|
||||
* Utility functions
|
||||
*/
|
||||
export abstract class Util {
|
||||
static formatImageURL(url: string, size: ImageSize = 128, format?: ImageFormat): string {
|
||||
return `${url}.${format ?? (url.includes('/a_') ? 'gif' : 'jpg')}?size=${size}`;
|
||||
}
|
||||
|
||||
static iconHashToBigInt(hash: string): bigint {
|
||||
return BigInt('0x' + (hash.startsWith('a_') ? `a${hash.substring(2)}` : `b${hash}`));
|
||||
}
|
||||
|
||||
static iconBigintToHash(icon: bigint): string {
|
||||
const hash: string = icon.toString(16);
|
||||
|
||||
return hash.startsWith('a') ? `a_${hash.substring(1)}` : hash.substring(1);
|
||||
}
|
||||
|
||||
/** Removes the Bot before the token. */
|
||||
static removeTokenPrefix(token?: string, type: 'GATEWAY' | 'REST' = 'REST'): string {
|
||||
// If no token is provided, throw an error
|
||||
if (!token) { throw new Error(`The ${type} was not given a token. Please provide a token and try again.`); }
|
||||
|
||||
// If the token does not have a prefix just return token
|
||||
if (!token.startsWith('Bot ')) { return token; }
|
||||
|
||||
// Remove the prefix and return only the token.
|
||||
return token.substring(token.indexOf(' ') + 1);
|
||||
}
|
||||
|
||||
/** Get the bot id from the bot token. WARNING: Discord staff has mentioned this may not be stable forever. Use at your own risk. However, note for over 5 years this has never broken. */
|
||||
static getBotIdFromToken(token: string): string {
|
||||
return atob(token.split('.')[0]);
|
||||
}
|
||||
|
||||
static isFullMessage(m: Partial<DiscordMessage> | DiscordMessage): m is DiscordMessage {
|
||||
return !!m.edited_timestamp;
|
||||
}
|
||||
|
||||
static isPartialMessage(m: Partial<DiscordMessage> | DiscordMessage): m is PartialMessage {
|
||||
return !m.edited_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
export type PartialMessage = MakeRequired<DiscordMessage, "id" | "channel_id">;
|
@ -3,5 +3,5 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": ["src/**/*", "../../bot/Components", "../common/src/applyToClass.ts"]
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user