initial update
This commit is contained in:
parent
3272429cf6
commit
db9b70bf66
|
@ -1,4 +1,4 @@
|
||||||
**/node_modules
|
**/node_modules
|
||||||
**/build
|
**/build
|
||||||
**/logs
|
**/logs
|
||||||
**/data
|
**/data
|
|
@ -1,3 +1,3 @@
|
||||||
[*.js]
|
[*.js]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
106
.env.example
106
.env.example
|
@ -1,54 +1,54 @@
|
||||||
############
|
############
|
||||||
# Required #
|
# Required #
|
||||||
############
|
############
|
||||||
|
|
||||||
# Put environment type here (development, staging, or production)
|
# Put environment type here (development, staging, or production)
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
# Put Discord bot token here, you can get it from https://discord.com/developers/applications
|
# Put Discord bot token here, you can get it from https://discord.com/developers/applications
|
||||||
TOKEN=
|
TOKEN=
|
||||||
|
|
||||||
# Put the database connection URL here
|
# Put the database connection URL here
|
||||||
# Example for SQLite:
|
# Example for SQLite:
|
||||||
DB=sqlite://data.sqlite
|
DB=sqlite://data.sqlite
|
||||||
# Example for PostgreSQL:
|
# Example for PostgreSQL:
|
||||||
# DB=postgresql://esmbot:verycoolpass100@localhost:5432/esmbot
|
# DB=postgresql://esmbot:verycoolpass100@localhost:5432/esmbot
|
||||||
|
|
||||||
# Put snowflake ID of bot owner here (obtainable by going into Discord settings -> Appearance and enabling Developer Mode,
|
# Put snowflake ID of bot owner here (obtainable by going into Discord settings -> Appearance and enabling Developer Mode,
|
||||||
# then right clicking on your profile picture and selecting Copy ID)
|
# then right clicking on your profile picture and selecting Copy ID)
|
||||||
OWNER=
|
OWNER=
|
||||||
|
|
||||||
# Put default classic command prefix here
|
# Put default classic command prefix here
|
||||||
PREFIX=&
|
PREFIX=&
|
||||||
|
|
||||||
############
|
############
|
||||||
# Optional #
|
# Optional #
|
||||||
############
|
############
|
||||||
|
|
||||||
# Set this to true if you want the bot to stay in voice chats after sound effects and music have stopped
|
# Set this to true if you want the bot to stay in voice chats after sound effects and music have stopped
|
||||||
# (you can still make the bot leave using the stop command)
|
# (you can still make the bot leave using the stop command)
|
||||||
STAYVC=false
|
STAYVC=false
|
||||||
# Set this to true to disable music playback from YouTube
|
# Set this to true to disable music playback from YouTube
|
||||||
YT_DISABLED=false
|
YT_DISABLED=false
|
||||||
|
|
||||||
# Put Tenor API key here (used for obtaining raw tenor GIF urls)
|
# Put Tenor API key here (used for obtaining raw tenor GIF urls)
|
||||||
TENOR=
|
TENOR=
|
||||||
|
|
||||||
# Put HTML help page output location here, leave blank to disable
|
# Put HTML help page output location here, leave blank to disable
|
||||||
OUTPUT=
|
OUTPUT=
|
||||||
# Put temporary image dir here (make sure it's accessible via a web server), leave blank to disable
|
# Put temporary image dir here (make sure it's accessible via a web server), leave blank to disable
|
||||||
TEMPDIR=
|
TEMPDIR=
|
||||||
# Put temporary image web server domain
|
# Put temporary image web server domain
|
||||||
TMP_DOMAIN=
|
TMP_DOMAIN=
|
||||||
# Threshold where optional space saving methods will be performed
|
# Threshold where optional space saving methods will be performed
|
||||||
THRESHOLD=
|
THRESHOLD=
|
||||||
# Port for serving metrics. Metrics served are compatible with Prometheus.
|
# Port for serving metrics. Metrics served are compatible with Prometheus.
|
||||||
METRICS=
|
METRICS=
|
||||||
|
|
||||||
# The image API type to be used
|
# The image API type to be used
|
||||||
# Set this to `none` to process all images locally
|
# Set this to `none` to process all images locally
|
||||||
# Set this to `ws` if you want to use the external image API script, located in api/index.js
|
# Set this to `ws` if you want to use the external image API script, located in api/index.js
|
||||||
API_TYPE=none
|
API_TYPE=none
|
||||||
|
|
||||||
# Put ID of server to limit owner-only commands to
|
# Put ID of server to limit owner-only commands to
|
||||||
ADMIN_SERVER=
|
ADMIN_SERVER=
|
138
.eslintrc.json
138
.eslintrc.json
|
@ -1,69 +1,69 @@
|
||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"es2020": true,
|
"es2020": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"extends": ["eslint:recommended"],
|
"extends": ["eslint:recommended"],
|
||||||
"parser": "@babel/eslint-parser",
|
"parser": "@babel/eslint-parser",
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"ecmaVersion": 12,
|
"ecmaVersion": 12,
|
||||||
"requireConfigFile": false,
|
"requireConfigFile": false,
|
||||||
"babelOptions": {
|
"babelOptions": {
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-proposal-class-properties"
|
"@babel/plugin-proposal-class-properties"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": ["@babel", "unicorn"],
|
"plugins": ["@babel", "unicorn"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
2,
|
2,
|
||||||
{
|
{
|
||||||
"SwitchCase": 1
|
"SwitchCase": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
"error",
|
"error",
|
||||||
"unix"
|
"unix"
|
||||||
],
|
],
|
||||||
"unicorn/prefer-module": "error",
|
"unicorn/prefer-module": "error",
|
||||||
"quotes": [
|
"quotes": [
|
||||||
"warn",
|
"warn",
|
||||||
"double"
|
"double"
|
||||||
],
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"warn",
|
"warn",
|
||||||
"always"
|
"always"
|
||||||
],
|
],
|
||||||
"keyword-spacing": [
|
"keyword-spacing": [
|
||||||
"error", {
|
"error", {
|
||||||
"before": true,
|
"before": true,
|
||||||
"after": true
|
"after": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"space-before-blocks": [
|
"space-before-blocks": [
|
||||||
"error", {
|
"error", {
|
||||||
"functions": "always",
|
"functions": "always",
|
||||||
"keywords": "always",
|
"keywords": "always",
|
||||||
"classes": "always"
|
"classes": "always"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"space-before-function-paren": [
|
"space-before-function-paren": [
|
||||||
"error", {
|
"error", {
|
||||||
"anonymous": "never",
|
"anonymous": "never",
|
||||||
"named": "never",
|
"named": "never",
|
||||||
"asyncArrow": "always"
|
"asyncArrow": "always"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"prefer-const": [
|
"prefer-const": [
|
||||||
"error", {
|
"error", {
|
||||||
"destructuring": "any",
|
"destructuring": "any",
|
||||||
"ignoreReadBeforeAssign": false
|
"ignoreReadBeforeAssign": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"prefer-template": "error"
|
"prefer-template": "error"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
patreon: TheEssem
|
patreon: TheEssem
|
||||||
ko_fi: TheEssem
|
ko_fi: TheEssem
|
|
@ -1,54 +1,54 @@
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: Report an issue with the bot that didn't result in an error file
|
description: Report an issue with the bot that didn't result in an error file
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: describe
|
id: describe
|
||||||
attributes:
|
attributes:
|
||||||
label: Describe the bug
|
label: Describe the bug
|
||||||
description: A clear and concise description of what the bug is.
|
description: A clear and concise description of what the bug is.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: reproduce
|
id: reproduce
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to reproduce
|
label: Steps to reproduce
|
||||||
description: Steps to reproduce the behavior.
|
description: Steps to reproduce the behavior.
|
||||||
value: |
|
value: |
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: expected
|
id: expected
|
||||||
attributes:
|
attributes:
|
||||||
label: Expected behavior
|
label: Expected behavior
|
||||||
description: A clear and concise description of what you expected to happen.
|
description: A clear and concise description of what you expected to happen.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: screenshots
|
id: screenshots
|
||||||
attributes:
|
attributes:
|
||||||
label: Screenshots
|
label: Screenshots
|
||||||
description: If applicable, add screenshots to help explain your problem.
|
description: If applicable, add screenshots to help explain your problem.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: self-hosted
|
id: self-hosted
|
||||||
attributes:
|
attributes:
|
||||||
label: Self-hosted instance?
|
label: Self-hosted instance?
|
||||||
description: Did the error occur on a self-hosted instance (e.g. not the main esmBot or esmBot Dev instances)?
|
description: Did the error occur on a self-hosted instance (e.g. not the main esmBot or esmBot Dev instances)?
|
||||||
options:
|
options:
|
||||||
- "Yes"
|
- "Yes"
|
||||||
- "No"
|
- "No"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional context
|
label: Additional context
|
||||||
description: Add any other context about the problem here.
|
description: Add any other context about the problem here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
|
@ -1,58 +1,58 @@
|
||||||
name: Error Report
|
name: Error Report
|
||||||
description: Report an error that the bot posted in chat
|
description: Report an error that the bot posted in chat
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
body:
|
body:
|
||||||
- type: input
|
- type: input
|
||||||
id: command
|
id: command
|
||||||
attributes:
|
attributes:
|
||||||
label: Command that caused the error
|
label: Command that caused the error
|
||||||
description: Post the exact command that caused the error.
|
description: Post the exact command that caused the error.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: image
|
id: image
|
||||||
attributes:
|
attributes:
|
||||||
label: Image that caused the error
|
label: Image that caused the error
|
||||||
description: If the error is regarding an image command, please post a direct link to the image here.
|
description: If the error is regarding an image command, please post a direct link to the image here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: reproduce
|
id: reproduce
|
||||||
attributes:
|
attributes:
|
||||||
label: Steps to reproduce
|
label: Steps to reproduce
|
||||||
description: Steps to reproduce the behavior.
|
description: Steps to reproduce the behavior.
|
||||||
value: |
|
value: |
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: self-hosted
|
id: self-hosted
|
||||||
attributes:
|
attributes:
|
||||||
label: Self-hosted instance?
|
label: Self-hosted instance?
|
||||||
description: Did the error occur on a self-hosted instance (e.g. not the main esmBot or esmBot Dev instances)?
|
description: Did the error occur on a self-hosted instance (e.g. not the main esmBot or esmBot Dev instances)?
|
||||||
options:
|
options:
|
||||||
- "Yes"
|
- "Yes"
|
||||||
- "No"
|
- "No"
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: error
|
id: error
|
||||||
attributes:
|
attributes:
|
||||||
label: Error file
|
label: Error file
|
||||||
description: Post the contents of the error file in between the backticks.
|
description: Post the contents of the error file in between the backticks.
|
||||||
value: |
|
value: |
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional context
|
label: Additional context
|
||||||
description: Add any other context about the problem here.
|
description: Add any other context about the problem here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
|
@ -1,25 +1,25 @@
|
||||||
name: Feature Request
|
name: Feature Request
|
||||||
description: Suggest an idea for this project
|
description: Suggest an idea for this project
|
||||||
labels: [enhancement]
|
labels: [enhancement]
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: describe
|
id: describe
|
||||||
attributes:
|
attributes:
|
||||||
label: Describe the request
|
label: Describe the request
|
||||||
description: What do you want to be added to the bot?
|
description: What do you want to be added to the bot?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
attributes:
|
attributes:
|
||||||
label: Is your feature request related to a problem? Please describe.
|
label: Is your feature request related to a problem? Please describe.
|
||||||
description: A clear and concise description of what the problem is (if applicable). Ex. I'm always frustrated when [...]
|
description: A clear and concise description of what the problem is (if applicable). Ex. I'm always frustrated when [...]
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: context
|
id: context
|
||||||
attributes:
|
attributes:
|
||||||
label: Additional context
|
label: Additional context
|
||||||
description: Add any other context or screenshots about the feature request here.
|
description: Add any other context or screenshots about the feature request here.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
|
@ -1,43 +1,43 @@
|
||||||
name: Build Test
|
name: Build Test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linux:
|
linux:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@v2.2.2
|
uses: pnpm/action-setup@v2.2.2
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v2.1.2
|
uses: actions/setup-node@v2.1.2
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt update && sudo apt install -y cmake libvips-dev libmagick++-dev
|
run: sudo apt update && sudo apt install -y cmake libvips-dev libmagick++-dev
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm install --frozen-lockfile && pnpm run build
|
run: pnpm install --frozen-lockfile && pnpm run build
|
||||||
|
|
||||||
darwin:
|
darwin:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install imagemagick vips node pnpm npm
|
brew install imagemagick vips node pnpm npm
|
||||||
pnpm install --config.strict-peer-dependencies=false
|
pnpm install --config.strict-peer-dependencies=false
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm run build
|
run: pnpm run build
|
||||||
|
|
||||||
|
|
|
@ -1,67 +1,67 @@
|
||||||
# For most projects, this workflow file will not need changing; you simply need
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
# to commit it to your repository.
|
# to commit it to your repository.
|
||||||
#
|
#
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
# or to provide custom queries or build logic.
|
# or to provide custom queries or build logic.
|
||||||
#
|
#
|
||||||
# ******** NOTE ********
|
# ******** NOTE ********
|
||||||
# We have attempted to detect the languages in your repository. Please check
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
# the `language` matrix defined below to confirm you have the correct set of
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
# supported CodeQL languages.
|
# supported CodeQL languages.
|
||||||
#
|
#
|
||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '35 2 * * 0'
|
- cron: '35 2 * * 0'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
language: [ 'javascript' ]
|
language: [ 'javascript' ]
|
||||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||||
# Learn more:
|
# Learn more:
|
||||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Node.js environment
|
- name: Setup Node.js environment
|
||||||
uses: actions/setup-node@v2.1.2
|
uses: actions/setup-node@v2.1.2
|
||||||
#with:
|
#with:
|
||||||
# Set always-auth in npmrc
|
# Set always-auth in npmrc
|
||||||
#always-auth: # optional, default is false
|
#always-auth: # optional, default is false
|
||||||
# Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0
|
# Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0
|
||||||
#node-version: # optional
|
#node-version: # optional
|
||||||
# Set this option if you want the action to check for the latest available version that satisfies the version spec
|
# Set this option if you want the action to check for the latest available version that satisfies the version spec
|
||||||
#check-latest: # optional
|
#check-latest: # optional
|
||||||
# Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, and set up auth to read in from env.NODE_AUTH_TOKEN
|
# Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file, and set up auth to read in from env.NODE_AUTH_TOKEN
|
||||||
#registry-url: # optional
|
#registry-url: # optional
|
||||||
# Optional scope for authenticating against scoped registries
|
# Optional scope for authenticating against scoped registries
|
||||||
#scope: # optional
|
#scope: # optional
|
||||||
# Used to pull node distributions from node-versions. Since there's a default, this is typically not supplied by the user.
|
# Used to pull node distributions from node-versions. Since there's a default, this is typically not supplied by the user.
|
||||||
#token: # optional, default is ${{ github.token }}
|
#token: # optional, default is ${{ github.token }}
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
# By default, queries listed here will override any specified in a config file.
|
# By default, queries listed here will override any specified in a config file.
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v2
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
name: Publish docs via GitHub Pages
|
name: Publish docs via GitHub Pages
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
paths:
|
paths:
|
||||||
- 'docs/**'
|
- 'docs/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Deploy docs
|
name: Deploy docs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout main
|
- name: Checkout main
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Deploy docs
|
- name: Deploy docs
|
||||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
REQUIREMENTS: docs/requirements.txt
|
REQUIREMENTS: docs/requirements.txt
|
||||||
|
|
|
@ -1,121 +1,121 @@
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
*.log.gz
|
*.log.gz
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
pids
|
pids
|
||||||
*.pid
|
*.pid
|
||||||
*.seed
|
*.seed
|
||||||
*.pid.lock
|
*.pid.lock
|
||||||
|
|
||||||
# node-waf configuration
|
# node-waf configuration
|
||||||
.lock-wscript
|
.lock-wscript
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
build/
|
build/
|
||||||
|
|
||||||
# Dependency directories
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
jspm_packages/
|
jspm_packages/
|
||||||
libvips/
|
libvips/
|
||||||
|
|
||||||
# Optional npm cache directory
|
# Optional npm cache directory
|
||||||
.npm
|
.npm
|
||||||
|
|
||||||
# Optional eslint cache
|
# Optional eslint cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
# Optional stylelint cache
|
# Optional stylelint cache
|
||||||
.stylelintcache
|
.stylelintcache
|
||||||
|
|
||||||
# Microbundle cache
|
# Microbundle cache
|
||||||
.rpt2_cache/
|
.rpt2_cache/
|
||||||
.rts2_cache_cjs/
|
.rts2_cache_cjs/
|
||||||
.rts2_cache_es/
|
.rts2_cache_es/
|
||||||
.rts2_cache_umd/
|
.rts2_cache_umd/
|
||||||
|
|
||||||
# Optional REPL history
|
# Optional REPL history
|
||||||
.node_repl_history
|
.node_repl_history
|
||||||
|
|
||||||
# Output of 'npm pack'
|
# Output of 'npm pack'
|
||||||
*.tgz
|
*.tgz
|
||||||
|
|
||||||
# Yarn Integrity file
|
# Yarn Integrity file
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
.env.local
|
.env.local
|
||||||
|
|
||||||
# Gatsby files
|
# Gatsby files
|
||||||
.cache/
|
.cache/
|
||||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
# public
|
# public
|
||||||
|
|
||||||
# vuepress v2.x temp and cache directory
|
# vuepress v2.x temp and cache directory
|
||||||
.temp
|
.temp
|
||||||
.cache
|
.cache
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
.vscode-test
|
.vscode-test
|
||||||
|
|
||||||
# yarn v2
|
# yarn v2
|
||||||
.yarn/cache
|
.yarn/cache
|
||||||
.yarn/unplugged
|
.yarn/unplugged
|
||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
# Prerequisites
|
# Prerequisites
|
||||||
*.d
|
*.d
|
||||||
|
|
||||||
# Compiled Object files
|
# Compiled Object files
|
||||||
*.slo
|
*.slo
|
||||||
*.lo
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
*.obj
|
*.obj
|
||||||
|
|
||||||
# Precompiled Headers
|
# Precompiled Headers
|
||||||
*.gch
|
*.gch
|
||||||
*.pch
|
*.pch
|
||||||
|
|
||||||
# Compiled Dynamic libraries
|
# Compiled Dynamic libraries
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.dll
|
*.dll
|
||||||
|
|
||||||
# Compiled Static libraries
|
# Compiled Static libraries
|
||||||
*.lai
|
*.lai
|
||||||
*.la
|
*.la
|
||||||
*.a
|
*.a
|
||||||
*.lib
|
*.lib
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.app
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
*.heap
|
*.heap
|
||||||
*.out.*
|
*.out.*
|
||||||
|
|
||||||
# vscode stuff
|
# vscode stuff
|
||||||
.vscode
|
.vscode
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
# Databases
|
# Databases
|
||||||
data/
|
data/
|
||||||
*.sqlite
|
*.sqlite
|
|
@ -1,3 +1,3 @@
|
||||||
[submodule "assets/images/region-flags"]
|
[submodule "assets/images/region-flags"]
|
||||||
path = assets/images/region-flags
|
path = assets/images/region-flags
|
||||||
url = https://github.com/fonttools/region-flags
|
url = https://github.com/fonttools/region-flags
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
cmake_policy(SET CMP0091 NEW)
|
cmake_policy(SET CMP0091 NEW)
|
||||||
cmake_policy(SET CMP0042 NEW)
|
cmake_policy(SET CMP0042 NEW)
|
||||||
project(image)
|
project(image)
|
||||||
|
|
||||||
file(GLOB SOURCE_FILES "natives/*.cc" "natives/*.h")
|
file(GLOB SOURCE_FILES "natives/*.cc" "natives/*.h")
|
||||||
|
|
||||||
if (CMAKE_JS_VERSION)
|
if (CMAKE_JS_VERSION)
|
||||||
include_directories(${CMAKE_JS_INC})
|
include_directories(${CMAKE_JS_INC})
|
||||||
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC} natives/node/image.cc)
|
add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC} natives/node/image.cc)
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
|
||||||
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
|
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB})
|
||||||
else()
|
else()
|
||||||
add_executable(${PROJECT_NAME} ${SOURCE_FILES} natives/cli/image.cc)
|
add_executable(${PROJECT_NAME} ${SOURCE_FILES} natives/cli/image.cc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
|
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17)
|
||||||
|
|
||||||
if(MSVC) # todo: change flags for more parity with GCC/clang, I don't know much about MSVC so pull requests are open
|
if(MSVC) # todo: change flags for more parity with GCC/clang, I don't know much about MSVC so pull requests are open
|
||||||
set(CMAKE_CXX_FLAGS "/Wall /EHsc /GS")
|
set(CMAKE_CXX_FLAGS "/Wall /EHsc /GS")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
|
set(CMAKE_CXX_FLAGS_DEBUG "/Zi")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "/Ox")
|
set(CMAKE_CXX_FLAGS_RELEASE "/Ox")
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
set(BUILD_SHARED_LIBS TRUE)
|
set(BUILD_SHARED_LIBS TRUE)
|
||||||
else()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror=format-security -Wno-cast-function-type -fexceptions -D_GLIBCXX_ASSERTIONS -fstack-clash-protection -pedantic -D_GLIBCXX_USE_CXX11_ABI=1")
|
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Werror=format-security -Wno-cast-function-type -fexceptions -D_GLIBCXX_ASSERTIONS -fstack-clash-protection -pedantic -D_GLIBCXX_USE_CXX11_ABI=1")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
find_package(ImageMagick REQUIRED COMPONENTS Magick++ MagickCore)
|
find_package(ImageMagick REQUIRED COMPONENTS Magick++ MagickCore)
|
||||||
add_definitions(-DMAGICKCORE_QUANTUM_DEPTH=16)
|
add_definitions(-DMAGICKCORE_QUANTUM_DEPTH=16)
|
||||||
add_definitions(-DMAGICKCORE_HDRI_ENABLE=0)
|
add_definitions(-DMAGICKCORE_HDRI_ENABLE=0)
|
||||||
include_directories(${ImageMagick_INCLUDE_DIRS})
|
include_directories(${ImageMagick_INCLUDE_DIRS})
|
||||||
target_link_libraries(${PROJECT_NAME} ${ImageMagick_LIBRARIES})
|
target_link_libraries(${PROJECT_NAME} ${ImageMagick_LIBRARIES})
|
||||||
|
|
||||||
pkg_check_modules(VIPS REQUIRED vips-cpp)
|
pkg_check_modules(VIPS REQUIRED vips-cpp)
|
||||||
include_directories(${VIPS_INCLUDE_DIRS})
|
include_directories(${VIPS_INCLUDE_DIRS})
|
||||||
link_directories(${VIPS_LIBRARY_DIRS})
|
link_directories(${VIPS_LIBRARY_DIRS})
|
||||||
target_link_libraries(${PROJECT_NAME} ${VIPS_LDFLAGS})
|
target_link_libraries(${PROJECT_NAME} ${VIPS_LDFLAGS})
|
||||||
|
|
||||||
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET AND CMAKE_JS_VERSION)
|
if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET AND CMAKE_JS_VERSION)
|
||||||
# Generate node.lib
|
# Generate node.lib
|
||||||
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
||||||
endif()
|
endif()
|
||||||
|
|
140
Dockerfile
140
Dockerfile
|
@ -1,70 +1,70 @@
|
||||||
# Docker/Kubernetes file for running the bot
|
# Docker/Kubernetes file for running the bot
|
||||||
#FROM node:alpine
|
#FROM node:alpine
|
||||||
FROM alpine:edge
|
FROM alpine:edge
|
||||||
|
|
||||||
RUN apk --no-cache upgrade
|
RUN apk --no-cache upgrade
|
||||||
RUN apk add --no-cache git cmake msttcorefonts-installer python3 alpine-sdk ffmpeg wget rpm2cpio \
|
RUN apk add --no-cache git cmake msttcorefonts-installer python3 alpine-sdk ffmpeg wget rpm2cpio \
|
||||||
zlib-dev libpng-dev libjpeg-turbo-dev freetype-dev fontconfig-dev \
|
zlib-dev libpng-dev libjpeg-turbo-dev freetype-dev fontconfig-dev \
|
||||||
libtool libwebp-dev libxml2-dev freetype fontconfig \
|
libtool libwebp-dev libxml2-dev freetype fontconfig \
|
||||||
vips vips-dev grep libc6-compat nodejs-current nodejs-current-dev npm
|
vips vips-dev grep libc6-compat nodejs-current nodejs-current-dev npm
|
||||||
|
|
||||||
# install pnpm
|
# install pnpm
|
||||||
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
|
RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \
|
||||||
npm install -g pnpm@6.27.1
|
npm install -g pnpm@6.27.1
|
||||||
|
|
||||||
# liblqr needs to be built manually for magick to work
|
# liblqr needs to be built manually for magick to work
|
||||||
# and because alpine doesn't have it in their repos
|
# and because alpine doesn't have it in their repos
|
||||||
RUN git clone https://github.com/carlobaldassi/liblqr \
|
RUN git clone https://github.com/carlobaldassi/liblqr \
|
||||||
&& cd liblqr \
|
&& cd liblqr \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
&& make \
|
&& make \
|
||||||
&& make install
|
&& make install
|
||||||
|
|
||||||
# install imagemagick from source rather than using the package
|
# install imagemagick from source rather than using the package
|
||||||
# since the alpine package does not include liblqr support.
|
# since the alpine package does not include liblqr support.
|
||||||
RUN git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick \
|
RUN git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick \
|
||||||
&& cd ImageMagick \
|
&& cd ImageMagick \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
--prefix=/usr \
|
--prefix=/usr \
|
||||||
--sysconfdir=/etc \
|
--sysconfdir=/etc \
|
||||||
--mandir=/usr/share/man \
|
--mandir=/usr/share/man \
|
||||||
--infodir=/usr/share/info \
|
--infodir=/usr/share/info \
|
||||||
--enable-static \
|
--enable-static \
|
||||||
--disable-openmp \
|
--disable-openmp \
|
||||||
--with-threads \
|
--with-threads \
|
||||||
--with-png \
|
--with-png \
|
||||||
--with-webp \
|
--with-webp \
|
||||||
--with-modules \
|
--with-modules \
|
||||||
--with-pango \
|
--with-pango \
|
||||||
--without-hdri \
|
--without-hdri \
|
||||||
--with-lqr \
|
--with-lqr \
|
||||||
&& make \
|
&& make \
|
||||||
&& make install
|
&& make install
|
||||||
|
|
||||||
RUN update-ms-fonts && fc-cache -f
|
RUN update-ms-fonts && fc-cache -f
|
||||||
|
|
||||||
RUN adduser esmBot -s /bin/sh -D
|
RUN adduser esmBot -s /bin/sh -D
|
||||||
USER esmBot
|
USER esmBot
|
||||||
|
|
||||||
WORKDIR /home/esmBot/.internal
|
WORKDIR /home/esmBot/.internal
|
||||||
|
|
||||||
COPY --chown=esmBot:esmBot ./package.json package.json
|
COPY --chown=esmBot:esmBot ./package.json package.json
|
||||||
COPY --chown=esmBot:esmBot ./pnpm-lock.yaml pnpm-lock.yaml
|
COPY --chown=esmBot:esmBot ./pnpm-lock.yaml pnpm-lock.yaml
|
||||||
RUN pnpm install
|
RUN pnpm install
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN rm .env
|
RUN rm .env
|
||||||
RUN pnpm run build
|
RUN pnpm run build
|
||||||
|
|
||||||
RUN mkdir /home/esmBot/help \
|
RUN mkdir /home/esmBot/help \
|
||||||
&& chown esmBot:esmBot /home/esmBot/help \
|
&& chown esmBot:esmBot /home/esmBot/help \
|
||||||
&& chmod 777 /home/esmBot/help
|
&& chmod 777 /home/esmBot/help
|
||||||
|
|
||||||
RUN mkdir /home/esmBot/temp \
|
RUN mkdir /home/esmBot/temp \
|
||||||
&& chown esmBot:esmBot /home/esmBot/temp \
|
&& chown esmBot:esmBot /home/esmBot/temp \
|
||||||
&& chmod 777 /home/esmBot/temp
|
&& chmod 777 /home/esmBot/temp
|
||||||
|
|
||||||
RUN mkdir /home/esmBot/.internal/logs \
|
RUN mkdir /home/esmBot/.internal/logs \
|
||||||
&& chown esmBot:esmBot /home/esmBot/.internal/logs \
|
&& chown esmBot:esmBot /home/esmBot/.internal/logs \
|
||||||
&& chmod 777 /home/esmBot/.internal/logs
|
&& chmod 777 /home/esmBot/.internal/logs
|
||||||
|
|
||||||
ENTRYPOINT ["node", "app.js"]
|
ENTRYPOINT ["node", "app.js"]
|
||||||
|
|
42
LICENSE
42
LICENSE
|
@ -1,21 +1,21 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2023 Essem
|
Copyright (c) 2023 Essem
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
48
PRIVACY.md
48
PRIVACY.md
|
@ -1,24 +1,24 @@
|
||||||
# esmBot and Privacy
|
# esmBot and Privacy
|
||||||
First things first: **esmBot does not and is incapable of collecting IP addresses, emails, or any other sensitive personal/private info.** This info is not accessible via Discord's API [except for emails](https://discord.com/developers/docs/resources/user#user-object), which require the email OAuth2 scope to access. esmBot does not use OAuth2 to link to a user account, therefore it does not have access to this info.
|
First things first: **esmBot does not and is incapable of collecting IP addresses, emails, or any other sensitive personal/private info.** This info is not accessible via Discord's API [except for emails](https://discord.com/developers/docs/resources/user#user-object), which require the email OAuth2 scope to access. esmBot does not use OAuth2 to link to a user account, therefore it does not have access to this info.
|
||||||
|
|
||||||
Whenever a command is run using esmBot, a command count number is increased. **This counter is completely anonymous and is used only for statistical purposes.** Users can check this info at any time using the count and help commands.
|
Whenever a command is run using esmBot, a command count number is increased. **This counter is completely anonymous and is used only for statistical purposes.** Users can check this info at any time using the count and help commands.
|
||||||
|
|
||||||
esmBot uses the following user-related info:
|
esmBot uses the following user-related info:
|
||||||
+ User IDs (needed for many reasons such as the tag commands and replying to users)
|
+ User IDs (needed for many reasons such as the tag commands and replying to users)
|
||||||
+ Avatars (needed for some embeds and the avatar command)
|
+ Avatars (needed for some embeds and the avatar command)
|
||||||
+ Usernames (for embeds and avatar command)
|
+ Usernames (for embeds and avatar command)
|
||||||
+ Permissions (for checking if a user has perms to run some commands)
|
+ Permissions (for checking if a user has perms to run some commands)
|
||||||
+ Whether the user is a bot (needed to prevent other bots from running commands)
|
+ Whether the user is a bot (needed to prevent other bots from running commands)
|
||||||
|
|
||||||
Out of these, **only user IDs are stored in the database**, and they are used only with the tag system for checking the owner of a tag.
|
Out of these, **only user IDs are stored in the database**, and they are used only with the tag system for checking the owner of a tag.
|
||||||
|
|
||||||
esmBot uses the following guild-related info:
|
esmBot uses the following guild-related info:
|
||||||
+ Guild IDs (for guild-specific settings)
|
+ Guild IDs (for guild-specific settings)
|
||||||
+ Guild channel IDs (for getting where to send a message, storing disabled channels)
|
+ Guild channel IDs (for getting where to send a message, storing disabled channels)
|
||||||
+ List of members (for getting permissions and obtaining user objects by ID)
|
+ List of members (for getting permissions and obtaining user objects by ID)
|
||||||
|
|
||||||
Out of these, **only guild and channel IDs are stored in the database** for configuration info and storing disabled channels/commands, prefixes, and tags.
|
Out of these, **only guild and channel IDs are stored in the database** for configuration info and storing disabled channels/commands, prefixes, and tags.
|
||||||
|
|
||||||
If you want this data removed on the main instance, you can DM me on Discord (Essem#9261) or email me at [data@esmbot.net](mailto:data@esmbot.net).
|
If you want this data removed on the main instance, you can DM me on Discord (Essem#9261) or email me at [data@esmbot.net](mailto:data@esmbot.net).
|
||||||
|
|
||||||
Hopefully this document is clear enough to help understand what esmBot does and doesn't use. If you have any further questions, please contact me via the [esmBot Support](https://esmbot.net/support) server.
|
Hopefully this document is clear enough to help understand what esmBot does and doesn't use. If you have any further questions, please contact me via the [esmBot Support](https://esmbot.net/support) server.
|
||||||
|
|
58
README.md
58
README.md
|
@ -1,29 +1,29 @@
|
||||||
# <img src="https://github.com/esmBot/esmBot/raw/master/docs/assets/esmbot.png" width="128"> esmBot
|
# <img src="https://github.com/esmBot/esmBot/raw/master/docs/assets/esmbot.png" width="128"> esmBot
|
||||||
[![esmBot Support](https://discordapp.com/api/guilds/592399417676529688/embed.png)](https://discord.gg/esmbot) ![GitHub license](https://img.shields.io/github/license/esmBot/esmBot.svg)
|
[![esmBot Support](https://discordapp.com/api/guilds/592399417676529688/embed.png)](https://discord.gg/esmbot) ![GitHub license](https://img.shields.io/github/license/esmBot/esmBot.svg)
|
||||||
|
|
||||||
|
|
||||||
esmBot is a free and open-source Discord bot designed to entertain your server. It's made using [Oceanic](https://oceanic.ws) and comes with image, music, and utility commands out of the box.
|
esmBot is a free and open-source Discord bot designed to entertain your server. It's made using [Oceanic](https://oceanic.ws) and comes with image, music, and utility commands out of the box.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Powerful, efficient, and performant image processing powered by [libvips](https://github.com/libvips/libvips)
|
- Powerful, efficient, and performant image processing powered by [libvips](https://github.com/libvips/libvips)
|
||||||
- Lots of image manipulation and processing commands out of the box
|
- Lots of image manipulation and processing commands out of the box
|
||||||
- Handling of output images larger than 8MB via a local web server
|
- Handling of output images larger than 8MB via a local web server
|
||||||
- Optional WebSocket/HTTP-based external image API with load balancing
|
- Optional WebSocket/HTTP-based external image API with load balancing
|
||||||
- Music and sound playback from many different configurable sources via [Lavalink](https://github.com/freyacodes/Lavalink)
|
- Music and sound playback from many different configurable sources via [Lavalink](https://github.com/freyacodes/Lavalink)
|
||||||
- Server tags system for saving/retrieving content
|
- Server tags system for saving/retrieving content
|
||||||
- Low RAM and CPU usage when idle
|
- Low RAM and CPU usage when idle
|
||||||
- Support for slash/application commands and classic, prefix-based message commands
|
- Support for slash/application commands and classic, prefix-based message commands
|
||||||
- Support for multiple database backends (PostgreSQL and SQLite backends included)
|
- Support for multiple database backends (PostgreSQL and SQLite backends included)
|
||||||
- [PM2](https://pm2.keymetrics.io)-based cluster/shard handling
|
- [PM2](https://pm2.keymetrics.io)-based cluster/shard handling
|
||||||
- Flexible command handler allowing you to create new commands by adding script files
|
- Flexible command handler allowing you to create new commands by adding script files
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
You can invite the main instance of esmBot to your server using this link: https://esmbot.net/invite
|
You can invite the main instance of esmBot to your server using this link: https://esmbot.net/invite
|
||||||
|
|
||||||
A command list can be found [here](https://esmbot.net/help.html).
|
A command list can be found [here](https://esmbot.net/help.html).
|
||||||
|
|
||||||
If you want to self-host the bot, a guide can be found [here](https://docs.esmbot.net/setup).
|
If you want to self-host the bot, a guide can be found [here](https://docs.esmbot.net/setup).
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
Icon by [Steel](https://twitter.com/MintBurrow).
|
Icon by [Steel](https://twitter.com/MintBurrow).
|
||||||
All images, sounds, and fonts are copyright of their respective owners.
|
All images, sounds, and fonts are copyright of their respective owners.
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
# esmBot Image API
|
# esmBot Image API
|
||||||
The esmBot image API is a combined HTTP and WebSocket API. The default port to access the API is 3762. The API supports very basic authentication, which is defined on the server via the PASS environment variable and is sent from the client via the Authentication header in both HTTP and WS requests.
|
The esmBot image API is a combined HTTP and WebSocket API. The default port to access the API is 3762. The API supports very basic authentication, which is defined on the server via the PASS environment variable and is sent from the client via the Authentication header in both HTTP and WS requests.
|
||||||
|
|
||||||
## HTTP
|
## HTTP
|
||||||
|
|
||||||
### GET `/image/?id=<job id>`
|
### GET `/image/?id=<job id>`
|
||||||
Get image data after job is finished running. The Content-Type header is properly set.
|
Get image data after job is finished running. The Content-Type header is properly set.
|
||||||
|
|
||||||
### GET `/count`
|
### GET `/count`
|
||||||
Get the current amount of running jobs. Response is a plaintext number value.
|
Get the current amount of running jobs. Response is a plaintext number value.
|
||||||
|
|
||||||
## WebSockets
|
## WebSockets
|
||||||
A client sends *requests* (T-messages) to a server, which subsequently *replies* (R-messages) to the client.
|
A client sends *requests* (T-messages) to a server, which subsequently *replies* (R-messages) to the client.
|
||||||
### Message IDs
|
### Message IDs
|
||||||
- Rerror 0x01
|
- Rerror 0x01
|
||||||
- Tqueue 0x02
|
- Tqueue 0x02
|
||||||
- Rqueue 0x03
|
- Rqueue 0x03
|
||||||
- Tcancel 0x04
|
- Tcancel 0x04
|
||||||
- Rcancel 0x05
|
- Rcancel 0x05
|
||||||
- Twait 0x06
|
- Twait 0x06
|
||||||
- Rwait 0x07
|
- Rwait 0x07
|
||||||
- Rinit 0x08
|
- Rinit 0x08
|
||||||
|
|
||||||
### Messages
|
### Messages
|
||||||
[n] means n bytes.
|
[n] means n bytes.
|
||||||
[s] means a string that goes until the end of the message.
|
[s] means a string that goes until the end of the message.
|
||||||
[j] means JSON data that goes until the end of the message.
|
[j] means JSON data that goes until the end of the message.
|
||||||
`tag` is used to identify a request/response pair, like `lock` in the original API. `jid` is used to identify a job. `job` is a job object.
|
`tag` is used to identify a request/response pair, like `lock` in the original API. `jid` is used to identify a job. `job` is a job object.
|
||||||
- Rerror tag[2] error[s]
|
- Rerror tag[2] error[s]
|
||||||
- Tqueue tag[2] jid[8] job[j]
|
- Tqueue tag[2] jid[8] job[j]
|
||||||
- Rqueue tag[2]
|
- Rqueue tag[2]
|
||||||
- Tcancel tag[2] jid[8]
|
- Tcancel tag[2] jid[8]
|
||||||
- Rcancel tag[2]
|
- Rcancel tag[2]
|
||||||
- Twait tag[2] jid[8]
|
- Twait tag[2] jid[8]
|
||||||
- Rwait tag[2]
|
- Rwait tag[2]
|
||||||
- Rinit tag[2] max_jobs[2] running_jobs[2] formats[j]
|
- Rinit tag[2] max_jobs[2] running_jobs[2] formats[j]
|
||||||
|
|
||||||
### Job Object
|
### Job Object
|
||||||
The job object is formatted like this:
|
The job object is formatted like this:
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
"cmd": string, // name of internal image command, e.g. caption
|
"cmd": string, // name of internal image command, e.g. caption
|
||||||
"path": string, // canonical image URL, used for getting the actual image
|
"path": string, // canonical image URL, used for getting the actual image
|
||||||
"url": string, // original image URL, used for message filtering
|
"url": string, // original image URL, used for message filtering
|
||||||
"params": { // content varies depending on the command, some common parameters are listed here
|
"params": { // content varies depending on the command, some common parameters are listed here
|
||||||
"type": string, // mime type of output, should usually be the same as input
|
"type": string, // mime type of output, should usually be the same as input
|
||||||
...
|
...
|
||||||
},
|
},
|
||||||
"name": string // filename of the image, without extension
|
"name": string // filename of the image, without extension
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
580
api/index.js
580
api/index.js
|
@ -1,290 +1,290 @@
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
config();
|
config();
|
||||||
import { cpus } from "os";
|
import { cpus } from "os";
|
||||||
import { Worker } from "worker_threads";
|
import { Worker } from "worker_threads";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
import { WebSocketServer } from "ws";
|
import { WebSocketServer } from "ws";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
import EventEmitter from "events";
|
import EventEmitter from "events";
|
||||||
|
|
||||||
const nodeRequire = createRequire(import.meta.url);
|
const nodeRequire = createRequire(import.meta.url);
|
||||||
const img = nodeRequire(`../build/${process.env.DEBUG && process.env.DEBUG === "true" ? "Debug" : "Release"}/image.node`);
|
const img = nodeRequire(`../build/${process.env.DEBUG && process.env.DEBUG === "true" ? "Debug" : "Release"}/image.node`);
|
||||||
|
|
||||||
const Rerror = 0x01;
|
const Rerror = 0x01;
|
||||||
const Tqueue = 0x02;
|
const Tqueue = 0x02;
|
||||||
const Rqueue = 0x03;
|
const Rqueue = 0x03;
|
||||||
const Tcancel = 0x04;
|
const Tcancel = 0x04;
|
||||||
const Rcancel = 0x05;
|
const Rcancel = 0x05;
|
||||||
const Twait = 0x06;
|
const Twait = 0x06;
|
||||||
const Rwait = 0x07;
|
const Rwait = 0x07;
|
||||||
const Rinit = 0x08;
|
const Rinit = 0x08;
|
||||||
|
|
||||||
const start = process.hrtime();
|
const start = process.hrtime();
|
||||||
const log = (msg, jobNum) => {
|
const log = (msg, jobNum) => {
|
||||||
console.log(`[${process.hrtime(start)[1] / 1000000}${jobNum ? `:${jobNum}` : ""}]\t ${msg}`);
|
console.log(`[${process.hrtime(start)[1] / 1000000}${jobNum ? `:${jobNum}` : ""}]\t ${msg}`);
|
||||||
};
|
};
|
||||||
const error = (msg, jobNum) => {
|
const error = (msg, jobNum) => {
|
||||||
console.error(`[${process.hrtime(start)[1] / 1000000}${jobNum ? `:${jobNum}` : ""}]\t ${msg}`);
|
console.error(`[${process.hrtime(start)[1] / 1000000}${jobNum ? `:${jobNum}` : ""}]\t ${msg}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
class JobCache extends Map {
|
class JobCache extends Map {
|
||||||
set(key, value) {
|
set(key, value) {
|
||||||
super.set(key, value);
|
super.set(key, value);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (super.has(key) && this.get(key) === value && value.data) super.delete(key);
|
if (super.has(key) && this.get(key) === value && value.data) super.delete(key);
|
||||||
}, 300000); // delete jobs if not requested after 5 minutes
|
}, 300000); // delete jobs if not requested after 5 minutes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const jobs = new JobCache();
|
const jobs = new JobCache();
|
||||||
// Should look like ID : { msg: "request", num: <job number> }
|
// Should look like ID : { msg: "request", num: <job number> }
|
||||||
const queue = [];
|
const queue = [];
|
||||||
// Array of IDs
|
// Array of IDs
|
||||||
|
|
||||||
const MAX_JOBS = process.env.JOBS ? parseInt(process.env.JOBS) : cpus().length * 4; // Completely arbitrary, should usually be some multiple of your amount of cores
|
const MAX_JOBS = process.env.JOBS ? parseInt(process.env.JOBS) : cpus().length * 4; // Completely arbitrary, should usually be some multiple of your amount of cores
|
||||||
const PASS = process.env.PASS ? process.env.PASS : undefined;
|
const PASS = process.env.PASS ? process.env.PASS : undefined;
|
||||||
let jobAmount = 0;
|
let jobAmount = 0;
|
||||||
|
|
||||||
const acceptJob = (id, sock) => {
|
const acceptJob = (id, sock) => {
|
||||||
jobAmount++;
|
jobAmount++;
|
||||||
queue.shift();
|
queue.shift();
|
||||||
const job = jobs.get(id);
|
const job = jobs.get(id);
|
||||||
return runJob({
|
return runJob({
|
||||||
id: id,
|
id: id,
|
||||||
msg: job.msg,
|
msg: job.msg,
|
||||||
num: job.num
|
num: job.num
|
||||||
}, sock).then(() => {
|
}, sock).then(() => {
|
||||||
log(`Job ${id} has finished`);
|
log(`Job ${id} has finished`);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
error(`Error on job ${id}: ${err}`, job.num);
|
error(`Error on job ${id}: ${err}`, job.num);
|
||||||
const newJob = jobs.get(id);
|
const newJob = jobs.get(id);
|
||||||
if (!newJob.tag) {
|
if (!newJob.tag) {
|
||||||
newJob.error = err.message;
|
newJob.error = err.message;
|
||||||
jobs.set(id, newJob);
|
jobs.set(id, newJob);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jobs.delete(id);
|
jobs.delete(id);
|
||||||
sock.send(Buffer.concat([Buffer.from([Rerror]), newJob.tag, Buffer.from(err.message)]));
|
sock.send(Buffer.concat([Buffer.from([Rerror]), newJob.tag, Buffer.from(err.message)]));
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
jobAmount--;
|
jobAmount--;
|
||||||
if (queue.length > 0) {
|
if (queue.length > 0) {
|
||||||
acceptJob(queue[0], sock);
|
acceptJob(queue[0], sock);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const waitForVerify = (event) => {
|
const waitForVerify = (event) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
event.once("end", (r) => resolve(r));
|
event.once("end", (r) => resolve(r));
|
||||||
event.once("error", (e) => reject(e));
|
event.once("error", (e) => reject(e));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const wss = new WebSocketServer({ clientTracking: true, noServer: true });
|
const wss = new WebSocketServer({ clientTracking: true, noServer: true });
|
||||||
|
|
||||||
wss.on("connection", (ws, request) => {
|
wss.on("connection", (ws, request) => {
|
||||||
log(`WS client ${request.socket.remoteAddress}:${request.socket.remotePort} has connected`);
|
log(`WS client ${request.socket.remoteAddress}:${request.socket.remotePort} has connected`);
|
||||||
const num = Buffer.alloc(2);
|
const num = Buffer.alloc(2);
|
||||||
num.writeUInt16LE(MAX_JOBS);
|
num.writeUInt16LE(MAX_JOBS);
|
||||||
const cur = Buffer.alloc(2);
|
const cur = Buffer.alloc(2);
|
||||||
cur.writeUInt16LE(jobAmount);
|
cur.writeUInt16LE(jobAmount);
|
||||||
const formats = {};
|
const formats = {};
|
||||||
for (const cmd of img.funcs) {
|
for (const cmd of img.funcs) {
|
||||||
formats[cmd] = ["image/png", "image/gif", "image/jpeg", "image/webp"];
|
formats[cmd] = ["image/png", "image/gif", "image/jpeg", "image/webp"];
|
||||||
}
|
}
|
||||||
const init = Buffer.concat([Buffer.from([Rinit]), Buffer.from([0x00, 0x00]), num, cur, Buffer.from(JSON.stringify(formats))]);
|
const init = Buffer.concat([Buffer.from([Rinit]), Buffer.from([0x00, 0x00]), num, cur, Buffer.from(JSON.stringify(formats))]);
|
||||||
ws.send(init);
|
ws.send(init);
|
||||||
|
|
||||||
ws.on("error", (err) => {
|
ws.on("error", (err) => {
|
||||||
error(err);
|
error(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("message", (msg) => {
|
ws.on("message", (msg) => {
|
||||||
const opcode = msg.readUint8(0);
|
const opcode = msg.readUint8(0);
|
||||||
const tag = msg.slice(1, 3);
|
const tag = msg.slice(1, 3);
|
||||||
const req = msg.toString().slice(3);
|
const req = msg.toString().slice(3);
|
||||||
if (opcode == Tqueue) {
|
if (opcode == Tqueue) {
|
||||||
const id = msg.readBigInt64LE(3);
|
const id = msg.readBigInt64LE(3);
|
||||||
const obj = msg.slice(11);
|
const obj = msg.slice(11);
|
||||||
const job = { msg: obj, num: jobAmount, verifyEvent: new EventEmitter() };
|
const job = { msg: obj, num: jobAmount, verifyEvent: new EventEmitter() };
|
||||||
jobs.set(id, job);
|
jobs.set(id, job);
|
||||||
queue.push(id);
|
queue.push(id);
|
||||||
|
|
||||||
const newBuffer = Buffer.concat([Buffer.from([Rqueue]), tag]);
|
const newBuffer = Buffer.concat([Buffer.from([Rqueue]), tag]);
|
||||||
ws.send(newBuffer);
|
ws.send(newBuffer);
|
||||||
|
|
||||||
if (jobAmount < MAX_JOBS) {
|
if (jobAmount < MAX_JOBS) {
|
||||||
log(`Got WS request for job ${job.msg} with id ${id}`, job.num);
|
log(`Got WS request for job ${job.msg} with id ${id}`, job.num);
|
||||||
acceptJob(id, ws);
|
acceptJob(id, ws);
|
||||||
} else {
|
} else {
|
||||||
log(`Got WS request for job ${job.msg} with id ${id}, queued in position ${queue.indexOf(id)}`, job.num);
|
log(`Got WS request for job ${job.msg} with id ${id}, queued in position ${queue.indexOf(id)}`, job.num);
|
||||||
}
|
}
|
||||||
} else if (opcode == Tcancel) {
|
} else if (opcode == Tcancel) {
|
||||||
delete queue[queue.indexOf(req) - 1];
|
delete queue[queue.indexOf(req) - 1];
|
||||||
jobs.delete(req);
|
jobs.delete(req);
|
||||||
const cancelResponse = Buffer.concat([Buffer.from([Rcancel]), tag]);
|
const cancelResponse = Buffer.concat([Buffer.from([Rcancel]), tag]);
|
||||||
ws.send(cancelResponse);
|
ws.send(cancelResponse);
|
||||||
} else if (opcode == Twait) {
|
} else if (opcode == Twait) {
|
||||||
const id = msg.readBigUInt64LE(3);
|
const id = msg.readBigUInt64LE(3);
|
||||||
const job = jobs.get(id);
|
const job = jobs.get(id);
|
||||||
if (!job) {
|
if (!job) {
|
||||||
const errorResponse = Buffer.concat([Buffer.from([Rerror]), tag, Buffer.from("Invalid job ID")]);
|
const errorResponse = Buffer.concat([Buffer.from([Rerror]), tag, Buffer.from("Invalid job ID")]);
|
||||||
ws.send(errorResponse);
|
ws.send(errorResponse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (job.error) {
|
if (job.error) {
|
||||||
job.verifyEvent.emit("error", job.error);
|
job.verifyEvent.emit("error", job.error);
|
||||||
jobs.delete(id);
|
jobs.delete(id);
|
||||||
const errorResponse = Buffer.concat([Buffer.from([Rerror]), tag, Buffer.from(job.error)]);
|
const errorResponse = Buffer.concat([Buffer.from([Rerror]), tag, Buffer.from(job.error)]);
|
||||||
ws.send(errorResponse);
|
ws.send(errorResponse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
job.verifyEvent.emit("end", tag);
|
job.verifyEvent.emit("end", tag);
|
||||||
job.tag = tag;
|
job.tag = tag;
|
||||||
jobs.set(id, job);
|
jobs.set(id, job);
|
||||||
//const waitResponse = Buffer.concat([Buffer.from([Rwait]), tag]);
|
//const waitResponse = Buffer.concat([Buffer.from([Rwait]), tag]);
|
||||||
//ws.send(waitResponse);
|
//ws.send(waitResponse);
|
||||||
} else {
|
} else {
|
||||||
log("Could not parse WS message");
|
log("Could not parse WS message");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on("close", () => {
|
ws.on("close", () => {
|
||||||
log(`WS client ${request.socket.remoteAddress}:${request.socket.remotePort} has disconnected`);
|
log(`WS client ${request.socket.remoteAddress}:${request.socket.remotePort} has disconnected`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
wss.on("error", (err) => {
|
wss.on("error", (err) => {
|
||||||
error("A WS error occurred: ", err);
|
error("A WS error occurred: ", err);
|
||||||
});
|
});
|
||||||
|
|
||||||
const httpServer = createServer();
|
const httpServer = createServer();
|
||||||
|
|
||||||
httpServer.on("request", async (req, res) => {
|
httpServer.on("request", async (req, res) => {
|
||||||
if (req.method !== "GET") {
|
if (req.method !== "GET") {
|
||||||
res.statusCode = 405;
|
res.statusCode = 405;
|
||||||
return res.end("405 Method Not Allowed");
|
return res.end("405 Method Not Allowed");
|
||||||
}
|
}
|
||||||
if (PASS && req.headers.authentication !== PASS) {
|
if (PASS && req.headers.authentication !== PASS) {
|
||||||
res.statusCode = 401;
|
res.statusCode = 401;
|
||||||
return res.end("401 Unauthorized");
|
return res.end("401 Unauthorized");
|
||||||
}
|
}
|
||||||
const reqUrl = new URL(req.url, `http://${req.headers.host}`);
|
const reqUrl = new URL(req.url, `http://${req.headers.host}`);
|
||||||
if (reqUrl.pathname === "/image" && req.method === "GET") {
|
if (reqUrl.pathname === "/image" && req.method === "GET") {
|
||||||
if (!reqUrl.searchParams.has("id")) {
|
if (!reqUrl.searchParams.has("id")) {
|
||||||
res.statusCode = 400;
|
res.statusCode = 400;
|
||||||
return res.end("400 Bad Request");
|
return res.end("400 Bad Request");
|
||||||
}
|
}
|
||||||
const id = BigInt(reqUrl.searchParams.get("id"));
|
const id = BigInt(reqUrl.searchParams.get("id"));
|
||||||
if (!jobs.has(id)) {
|
if (!jobs.has(id)) {
|
||||||
res.statusCode = 410;
|
res.statusCode = 410;
|
||||||
return res.end("410 Gone");
|
return res.end("410 Gone");
|
||||||
}
|
}
|
||||||
log(`Sending image data for job ${id} to ${req.socket.remoteAddress}:${req.socket.remotePort} via HTTP`);
|
log(`Sending image data for job ${id} to ${req.socket.remoteAddress}:${req.socket.remotePort} via HTTP`);
|
||||||
const ext = jobs.get(id).ext;
|
const ext = jobs.get(id).ext;
|
||||||
let contentType;
|
let contentType;
|
||||||
switch (ext) {
|
switch (ext) {
|
||||||
case "gif":
|
case "gif":
|
||||||
contentType = "image/gif";
|
contentType = "image/gif";
|
||||||
break;
|
break;
|
||||||
case "png":
|
case "png":
|
||||||
contentType = "image/png";
|
contentType = "image/png";
|
||||||
break;
|
break;
|
||||||
case "jpeg":
|
case "jpeg":
|
||||||
case "jpg":
|
case "jpg":
|
||||||
contentType = "image/jpeg";
|
contentType = "image/jpeg";
|
||||||
break;
|
break;
|
||||||
case "webp":
|
case "webp":
|
||||||
contentType = "image/webp";
|
contentType = "image/webp";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (contentType) res.setHeader("Content-Type", contentType);
|
if (contentType) res.setHeader("Content-Type", contentType);
|
||||||
else res.setHeader("Content-Type", ext);
|
else res.setHeader("Content-Type", ext);
|
||||||
const data = jobs.get(id).data;
|
const data = jobs.get(id).data;
|
||||||
jobs.delete(id);
|
jobs.delete(id);
|
||||||
return res.end(data, (err) => {
|
return res.end(data, (err) => {
|
||||||
if (err) error(err);
|
if (err) error(err);
|
||||||
});
|
});
|
||||||
} else if (reqUrl.pathname === "/count" && req.method === "GET") {
|
} else if (reqUrl.pathname === "/count" && req.method === "GET") {
|
||||||
log(`Sending job count to ${req.socket.remoteAddress}:${req.socket.remotePort} via HTTP`);
|
log(`Sending job count to ${req.socket.remoteAddress}:${req.socket.remotePort} via HTTP`);
|
||||||
return res.end(jobAmount.toString(), (err) => {
|
return res.end(jobAmount.toString(), (err) => {
|
||||||
if (err) error(err);
|
if (err) error(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.statusCode = 404;
|
res.statusCode = 404;
|
||||||
return res.end("404 Not Found");
|
return res.end("404 Not Found");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
httpServer.on("upgrade", (req, sock, head) => {
|
httpServer.on("upgrade", (req, sock, head) => {
|
||||||
const reqUrl = new URL(req.url, `http://${req.headers.host}`);
|
const reqUrl = new URL(req.url, `http://${req.headers.host}`);
|
||||||
|
|
||||||
if (PASS && req.headers.authentication !== PASS) {
|
if (PASS && req.headers.authentication !== PASS) {
|
||||||
sock.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
sock.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
|
||||||
sock.destroy();
|
sock.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reqUrl.pathname === "/sock") {
|
if (reqUrl.pathname === "/sock") {
|
||||||
wss.handleUpgrade(req, sock, head, (ws) => {
|
wss.handleUpgrade(req, sock, head, (ws) => {
|
||||||
wss.emit("connection", ws, req);
|
wss.emit("connection", ws, req);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
sock.destroy();
|
sock.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
httpServer.on("error", (e) => {
|
httpServer.on("error", (e) => {
|
||||||
error("An HTTP error occurred: ", e);
|
error("An HTTP error occurred: ", e);
|
||||||
});
|
});
|
||||||
const port = parseInt(process.env.PORT) || 3762;
|
const port = parseInt(process.env.PORT) || 3762;
|
||||||
httpServer.listen(port, () => {
|
httpServer.listen(port, () => {
|
||||||
log("HTTP and WS listening on port 3762");
|
log("HTTP and WS listening on port 3762");
|
||||||
});
|
});
|
||||||
|
|
||||||
const runJob = (job, ws) => {
|
const runJob = (job, ws) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
log(`Job ${job.id} starting...`, job.num);
|
log(`Job ${job.id} starting...`, job.num);
|
||||||
|
|
||||||
const object = JSON.parse(job.msg);
|
const object = JSON.parse(job.msg);
|
||||||
// If the image has a path, it must also have a type
|
// If the image has a path, it must also have a type
|
||||||
if (object.path && !object.params.type) {
|
if (object.path && !object.params.type) {
|
||||||
reject(new TypeError("Unknown image type"));
|
reject(new TypeError("Unknown image type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const worker = new Worker(join(dirname(fileURLToPath(import.meta.url)), "../utils/image-runner.js"), {
|
const worker = new Worker(join(dirname(fileURLToPath(import.meta.url)), "../utils/image-runner.js"), {
|
||||||
workerData: object
|
workerData: object
|
||||||
});
|
});
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
reject(new Error("Job timed out"));
|
reject(new Error("Job timed out"));
|
||||||
}, 900000);
|
}, 900000);
|
||||||
log(`Job ${job.id} started`, job.num);
|
log(`Job ${job.id} started`, job.num);
|
||||||
worker.once("message", (data) => {
|
worker.once("message", (data) => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
log(`Sending result of job ${job.id} back to the bot`, job.num);
|
log(`Sending result of job ${job.id} back to the bot`, job.num);
|
||||||
const jobObject = jobs.get(job.id);
|
const jobObject = jobs.get(job.id);
|
||||||
jobObject.data = data.buffer;
|
jobObject.data = data.buffer;
|
||||||
jobObject.ext = data.fileExtension;
|
jobObject.ext = data.fileExtension;
|
||||||
let verifyPromise;
|
let verifyPromise;
|
||||||
if (!jobObject.tag) {
|
if (!jobObject.tag) {
|
||||||
verifyPromise = waitForVerify(jobObject.verifyEvent);
|
verifyPromise = waitForVerify(jobObject.verifyEvent);
|
||||||
} else {
|
} else {
|
||||||
verifyPromise = Promise.resolve(jobObject.tag);
|
verifyPromise = Promise.resolve(jobObject.tag);
|
||||||
}
|
}
|
||||||
verifyPromise.then(tag => {
|
verifyPromise.then(tag => {
|
||||||
jobs.set(job.id, jobObject);
|
jobs.set(job.id, jobObject);
|
||||||
const waitResponse = Buffer.concat([Buffer.from([Rwait]), tag]);
|
const waitResponse = Buffer.concat([Buffer.from([Rwait]), tag]);
|
||||||
ws.send(waitResponse);
|
ws.send(waitResponse);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
worker.once("error", (e) => {
|
worker.once("error", (e) => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
416
app.js
416
app.js
|
@ -1,228 +1,190 @@
|
||||||
if (process.versions.node.split(".")[0] < 16) {
|
if (process.versions.node.split(".")[0] < 16) {
|
||||||
console.error(`You are currently running Node.js version ${process.version}.
|
console.error(`You are currently running Node.js version ${process.version}.
|
||||||
esmBot requires Node.js version 16 or above.
|
esmBot requires Node.js version 16 or above.
|
||||||
Please refer to step 3 of the setup guide.`);
|
Please refer to step 3 of the setup guide.`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
if (process.platform === "win32") {
|
if (process.platform === "win32") {
|
||||||
console.error("\x1b[1m\x1b[31m\x1b[40m" + `WINDOWS IS NOT OFFICIALLY SUPPORTED!
|
console.error("\x1b[1m\x1b[31m\x1b[40m" + `WINDOWS IS NOT OFFICIALLY SUPPORTED!
|
||||||
Although there's a (very) slim chance of it working, multiple aspects of the bot are built with UNIX-like systems in mind and could break on Win32-based systems. If you want to run the bot on Windows, using Windows Subsystem for Linux is highly recommended.
|
Although there's a (very) slim chance of it working, multiple aspects of the bot are built with UNIX-like systems in mind and could break on Win32-based systems. If you want to run the bot on Windows, using Windows Subsystem for Linux is highly recommended.
|
||||||
The bot will continue to run past this message in 5 seconds, but keep in mind that it could break at any time. Continue running at your own risk; alternatively, stop the bot using Ctrl+C and install WSL.` + "\x1b[0m");
|
The bot will continue to run past this message in 5 seconds, but keep in mind that it could break at any time. Continue running at your own risk; alternatively, stop the bot using Ctrl+C and install WSL.` + "\x1b[0m");
|
||||||
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// load config from .env file
|
// load config from .env file
|
||||||
import { resolve, dirname } from "path";
|
import { resolve, dirname } from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
config({ path: resolve(dirname(fileURLToPath(import.meta.url)), ".env") });
|
config({ path: resolve(dirname(fileURLToPath(import.meta.url)), ".env") });
|
||||||
|
|
||||||
import { reloadImageConnections } from "./utils/image.js";
|
import { reloadImageConnections } from "./utils/image.js";
|
||||||
|
|
||||||
// main services
|
// main services
|
||||||
import { Client } from "oceanic.js";
|
// import { Client } from "oceanic.js";
|
||||||
import pm2 from "pm2";
|
import * as sdk from "matrix-js-sdk";
|
||||||
// some utils
|
const AutojoinRoomsMixin = sdk.AutojoinRoomsMixin;
|
||||||
import { promises, readFileSync } from "fs";
|
|
||||||
import { logger } from "./utils/logger.js";
|
|
||||||
import { exec as baseExec } from "child_process";
|
|
||||||
import { promisify } from "util";
|
|
||||||
const exec = promisify(baseExec);
|
import pm2 from "pm2";
|
||||||
// initialize command loader
|
// some utils
|
||||||
import { load } from "./utils/handler.js";
|
import { promises, readFileSync } from "fs";
|
||||||
// command collections
|
import { logger } from "./utils/logger.js";
|
||||||
import { paths } from "./utils/collections.js";
|
import { exec as baseExec } from "child_process";
|
||||||
// database stuff
|
import { promisify } from "util";
|
||||||
import database from "./utils/database.js";
|
const exec = promisify(baseExec);
|
||||||
// lavalink stuff
|
// initialize command loader
|
||||||
import { reload, connect, connected } from "./utils/soundplayer.js";
|
import { load } from "./utils/handler.js";
|
||||||
// events
|
// command collections
|
||||||
import { endBroadcast, startBroadcast } from "./utils/misc.js";
|
import { paths } from "./utils/collections.js";
|
||||||
import { parseThreshold } from "./utils/tempimages.js";
|
// database stuff
|
||||||
|
import database from "./utils/database.js";
|
||||||
const { types } = JSON.parse(readFileSync(new URL("./config/commands.json", import.meta.url)));
|
// lavalink stuff
|
||||||
const esmBotVersion = JSON.parse(readFileSync(new URL("./package.json", import.meta.url))).version;
|
import { reload, connect, connected } from "./utils/soundplayer.js";
|
||||||
process.env.ESMBOT_VER = esmBotVersion;
|
// events
|
||||||
|
import { endBroadcast, startBroadcast } from "./utils/misc.js";
|
||||||
const intents = [
|
import { parseThreshold } from "./utils/tempimages.js";
|
||||||
"GUILD_VOICE_STATES",
|
|
||||||
"DIRECT_MESSAGES",
|
const { types } = JSON.parse(readFileSync(new URL("./config/commands.json", import.meta.url)));
|
||||||
"GUILDS"
|
const esmBotVersion = JSON.parse(readFileSync(new URL("./package.json", import.meta.url))).version;
|
||||||
];
|
process.env.ESMBOT_VER = esmBotVersion;
|
||||||
if (types.classic) {
|
|
||||||
intents.push("GUILD_MESSAGES");
|
// const intents = [
|
||||||
intents.push("MESSAGE_CONTENT");
|
// "GUILD_VOICE_STATES",
|
||||||
}
|
// "DIRECT_MESSAGES",
|
||||||
|
// "GUILDS"
|
||||||
async function* getFiles(dir) {
|
// ];
|
||||||
const dirents = await promises.readdir(dir, { withFileTypes: true });
|
// if (types.classic) {
|
||||||
for (const dirent of dirents) {
|
// intents.push("GUILD_MESSAGES");
|
||||||
const name = dir + (dir.charAt(dir.length - 1) !== "/" ? "/" : "") + dirent.name;
|
// intents.push("MESSAGE_CONTENT");
|
||||||
if (dirent.isDirectory()) {
|
// }
|
||||||
yield* getFiles(name);
|
|
||||||
} else if (dirent.name.endsWith(".js")) {
|
async function* getFiles(dir) {
|
||||||
yield name;
|
const dirents = await promises.readdir(dir, { withFileTypes: true });
|
||||||
}
|
for (const dirent of dirents) {
|
||||||
}
|
const name = dir + (dir.charAt(dir.length - 1) !== "/" ? "/" : "") + dirent.name;
|
||||||
}
|
if (dirent.isDirectory()) {
|
||||||
|
yield* getFiles(name);
|
||||||
async function init() {
|
} else if (dirent.name.endsWith(".js")) {
|
||||||
await exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o);
|
yield name;
|
||||||
console.log(`
|
}
|
||||||
,*\`$ z\`"v
|
}
|
||||||
F zBw\`% A ,W "W
|
}
|
||||||
,\` ,EBBBWp"%. ,-=~~==-,+* 4BBE T
|
|
||||||
M BBBBBBBB* ,w=####Wpw 4BBBBB# 1
|
async function init() {
|
||||||
F BBBBBBBMwBBBBBBBBBBBBB#wXBBBBBH E
|
await exec("git rev-parse HEAD").then(output => output.stdout.substring(0, 7), () => "unknown commit").then(o => process.env.GIT_REV = o);
|
||||||
F BBBBBBkBBBBBBBBBBBBBBBBBBBBE4BL k
|
console.log(`
|
||||||
# BFBBBBBBBBBBBBF" "RBBBW F
|
,*\`$ z\`"v
|
||||||
V ' 4BBBBBBBBBBM TBBL F
|
F zBw\`% A ,W "W
|
||||||
F BBBBBBBBBBF JBB L
|
,\` ,EBBBWp"%. ,-=~~==-,+* 4BBE T
|
||||||
F FBBBBBBBEB BBL 4
|
M BBBBBBBB* ,w=####Wpw 4BBBBB# 1
|
||||||
E [BB4BBBBEBL BBL 4
|
F BBBBBBBMwBBBBBBBBBBBBB#wXBBBBBH E
|
||||||
I #BBBBBBBEB 4BBH *w
|
F BBBBBBkBBBBBBBBBBBBBBBBBBBBE4BL k
|
||||||
A 4BBBBBBBBBEW, ,BBBB W [
|
# BFBBBBBBBBBBBBF" "RBBBW F
|
||||||
.A ,k 4BBBBBBBBBBBEBW####BBBBBBM BF F
|
V ' 4BBBBBBBBBBM TBBL F
|
||||||
k <BBBw BBBBEBBBBBBBBBBBBBBBBBQ4BM #
|
F BBBBBBBBBBF JBB L
|
||||||
5, REBBB4BBBBB#BBBBBBBBBBBBP5BFF ,F
|
F FBBBBBBBEB BBL 4
|
||||||
*w \`*4BBW\`"FF#F##FFFF"\` , * +"
|
E [BB4BBBBEBL BBL 4
|
||||||
*+, " F'"'*^~~~^"^\` V+*^
|
I #BBBBBBBEB 4BBH *w
|
||||||
\`"""
|
A 4BBBBBBBBBEW, ,BBBB W [
|
||||||
|
.A ,k 4BBBBBBBBBBBEBW####BBBBBBM BF F
|
||||||
esmBot ${esmBotVersion} (${process.env.GIT_REV})
|
k <BBBw BBBBEBBBBBBBBBBBBBBBBBQ4BM #
|
||||||
`);
|
5, REBBB4BBBBB#BBBBBBBBBBBBP5BFF ,F
|
||||||
|
*w \`*4BBW\`"FF#F##FFFF"\` , * +"
|
||||||
if (!types.classic && !types.application) {
|
*+, " F'"'*^~~~^"^\` V+*^
|
||||||
logger.error("Both classic and application commands are disabled! Please enable at least one command type in config/commands.json.");
|
\`"""
|
||||||
return process.exit(1);
|
|
||||||
}
|
esmBot ${esmBotVersion} (${process.env.GIT_REV})
|
||||||
|
`);
|
||||||
if (database) {
|
|
||||||
// database handling
|
if (!types.classic && !types.application) {
|
||||||
const dbResult = await database.upgrade(logger);
|
logger.error("Both classic and application commands are disabled! Please enable at least one command type in config/commands.json.");
|
||||||
if (dbResult === 1) return process.exit(1);
|
return process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// process the threshold into bytes early
|
if (database) {
|
||||||
if (process.env.TEMPDIR && process.env.THRESHOLD) {
|
// database handling
|
||||||
await parseThreshold();
|
const dbResult = await database.upgrade(logger);
|
||||||
}
|
if (dbResult === 1) return process.exit(1);
|
||||||
|
}
|
||||||
// register commands and their info
|
|
||||||
logger.log("info", "Attempting to load commands...");
|
// process the threshold into bytes early
|
||||||
for await (const commandFile of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./commands/"))) {
|
if (process.env.TEMPDIR && process.env.THRESHOLD) {
|
||||||
logger.log("main", `Loading command from ${commandFile}...`);
|
await parseThreshold();
|
||||||
try {
|
}
|
||||||
await load(null, commandFile);
|
|
||||||
} catch (e) {
|
// register commands and their info
|
||||||
logger.error(`Failed to register command from ${commandFile}: ${e}`);
|
logger.log("info", "Attempting to load commands...");
|
||||||
}
|
for await (const commandFile of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./commands/"))) {
|
||||||
}
|
logger.log("main", `Loading command from ${commandFile}...`);
|
||||||
logger.log("info", "Finished loading commands.");
|
try {
|
||||||
|
await load(null, commandFile);
|
||||||
if (database) {
|
} catch (e) {
|
||||||
await database.setup();
|
logger.error(`Failed to register command from ${commandFile}: ${e}`);
|
||||||
}
|
}
|
||||||
if (process.env.API_TYPE === "ws") await reloadImageConnections();
|
}
|
||||||
|
logger.log("info", "Finished loading commands.");
|
||||||
// create the oceanic client
|
|
||||||
const client = new Client({
|
if (database) {
|
||||||
auth: `Bot ${process.env.TOKEN}`,
|
await database.setup();
|
||||||
allowedMentions: {
|
}
|
||||||
everyone: false,
|
if (process.env.API_TYPE === "ws") await reloadImageConnections();
|
||||||
roles: false,
|
|
||||||
users: true,
|
// create the oceanic client
|
||||||
repliedUser: true
|
// const client = new Client({
|
||||||
},
|
// auth: `Bot ${process.env.TOKEN}`,
|
||||||
gateway: {
|
// allowedMentions: {
|
||||||
concurrency: "auto",
|
// everyone: false,
|
||||||
maxShards: "auto",
|
// roles: false,
|
||||||
shardIDs: process.env.SHARDS ? JSON.parse(process.env.SHARDS)[process.env.pm_id - 1] : null,
|
// users: true,
|
||||||
presence: {
|
// repliedUser: true
|
||||||
status: "idle",
|
// },
|
||||||
activities: [{
|
// gateway: {
|
||||||
type: 0,
|
// concurrency: "auto",
|
||||||
name: "Starting esmBot..."
|
// maxShards: "auto",
|
||||||
}]
|
// shardIDs: process.env.SHARDS ? JSON.parse(process.env.SHARDS)[process.env.pm_id - 1] : null,
|
||||||
},
|
// presence: {
|
||||||
intents
|
// status: "idle",
|
||||||
},
|
// activities: [{
|
||||||
collectionLimits: {
|
// type: 0,
|
||||||
messages: 50
|
// name: "Starting esmBot..."
|
||||||
}
|
// }]
|
||||||
});
|
// },
|
||||||
|
// intents
|
||||||
// register events
|
// },
|
||||||
logger.log("info", "Attempting to load events...");
|
// collectionLimits: {
|
||||||
for await (const file of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./events/"))) {
|
// messages: 50
|
||||||
logger.log("main", `Loading event from ${file}...`);
|
// }
|
||||||
const eventArray = file.split("/");
|
// });
|
||||||
const eventName = eventArray[eventArray.length - 1].split(".")[0];
|
|
||||||
if (eventName === "interactionCreate" && !types.application) {
|
const myUserId = process.env.MATRIX_USERNAME;
|
||||||
logger.log("warn", `Skipped loading event from ${file} because application commands are disabled`);
|
const myAccessToken = process.env.TOKEN;
|
||||||
continue;
|
const matrixClient = sdk.createClient({
|
||||||
}
|
baseUrl: process.env.MATRIX_BASEURL,
|
||||||
const { default: event } = await import(file);
|
accessToken: myAccessToken,
|
||||||
client.on(eventName, event.bind(null, client));
|
userId: myUserId,
|
||||||
}
|
});
|
||||||
logger.log("info", "Finished loading events.");
|
|
||||||
|
|
||||||
// PM2-specific handling
|
// register events
|
||||||
if (process.env.PM2_USAGE) {
|
logger.log("info", "Attempting to load events...");
|
||||||
// callback hell :)
|
// for await (const file of getFiles(resolve(dirname(fileURLToPath(import.meta.url)), "./events/"))) {
|
||||||
pm2.launchBus((err, pm2Bus) => {
|
// logger.log("main", `Loading event from ${file}...`);
|
||||||
if (err) {
|
// const eventArray = file.split("/");
|
||||||
logger.error(err);
|
// const eventName = eventArray[eventArray.length - 1].split(".")[0];
|
||||||
return;
|
// if (eventName === "interactionCreate" && !types.application) {
|
||||||
}
|
// logger.log("warn", `Skipped loading event from ${file} because application commands are disabled`);
|
||||||
pm2.list((err, list) => {
|
// continue;
|
||||||
if (err) {
|
// }
|
||||||
logger.error(err);
|
// const { default: event } = await import(file);
|
||||||
return;
|
// // client.on(eventName, event.bind(null, client));
|
||||||
}
|
// }
|
||||||
const managerProc = list.filter((v) => v.name === "esmBot-manager")[0];
|
const { default: event } = await import("./events/roommessage.js");
|
||||||
pm2Bus.on("process:msg", async (packet) => {
|
matrixClient.on("Room.timeline", event.bind(null,matrixClient));
|
||||||
switch (packet.data?.type) {
|
logger.log("info", "Finished loading events.");
|
||||||
case "reload":
|
|
||||||
var path = paths.get(packet.data.message);
|
matrixClient.startClient({ initialSyncLimit: 0 }).then(() => logger.log("info", "Client started!"));
|
||||||
await load(client, path, true);
|
}
|
||||||
break;
|
|
||||||
case "soundreload":
|
|
||||||
await reload(client);
|
|
||||||
break;
|
|
||||||
case "imagereload":
|
|
||||||
await reloadImageConnections();
|
|
||||||
break;
|
|
||||||
case "broadcastStart":
|
|
||||||
startBroadcast(client, packet.data.message);
|
|
||||||
break;
|
|
||||||
case "broadcastEnd":
|
|
||||||
endBroadcast(client);
|
|
||||||
break;
|
|
||||||
case "serverCounts":
|
|
||||||
pm2.sendDataToProcessId(managerProc.pm_id, {
|
|
||||||
id: managerProc.pm_id,
|
|
||||||
type: "process:msg",
|
|
||||||
data: {
|
|
||||||
type: "serverCounts",
|
|
||||||
guilds: client.guilds.size,
|
|
||||||
shards: client.shards.size
|
|
||||||
},
|
|
||||||
topic: true
|
|
||||||
}, (err) => {
|
|
||||||
if (err) logger.error(err);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect to lavalink
|
|
||||||
if (!connected) connect(client);
|
|
||||||
|
|
||||||
client.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
init();
|
144
application.yml
144
application.yml
|
@ -1,72 +1,72 @@
|
||||||
server: # REST and WS server
|
server: # REST and WS server
|
||||||
port: 2333
|
port: 2333
|
||||||
address: 0.0.0.0
|
address: 0.0.0.0
|
||||||
lavalink:
|
lavalink:
|
||||||
server:
|
server:
|
||||||
password: "youshallnotpass"
|
password: "youshallnotpass"
|
||||||
sources:
|
sources:
|
||||||
youtube: true
|
youtube: true
|
||||||
bandcamp: true
|
bandcamp: true
|
||||||
soundcloud: true
|
soundcloud: true
|
||||||
twitch: true
|
twitch: true
|
||||||
vimeo: true
|
vimeo: true
|
||||||
mixer: true
|
mixer: true
|
||||||
http: true
|
http: true
|
||||||
local: true
|
local: true
|
||||||
bufferDurationMs: 400
|
bufferDurationMs: 400
|
||||||
youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
|
youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
|
||||||
playerUpdateInterval: 1
|
playerUpdateInterval: 1
|
||||||
youtubeSearchEnabled: true
|
youtubeSearchEnabled: true
|
||||||
soundcloudSearchEnabled: true
|
soundcloudSearchEnabled: true
|
||||||
gc-warnings: true
|
gc-warnings: true
|
||||||
#ratelimit:
|
#ratelimit:
|
||||||
#ipBlocks: ["1.0.0.0/8", "..."] # list of ip blocks
|
#ipBlocks: ["1.0.0.0/8", "..."] # list of ip blocks
|
||||||
#excludedIps: ["...", "..."] # ips which should be explicit excluded from usage by lavalink
|
#excludedIps: ["...", "..."] # ips which should be explicit excluded from usage by lavalink
|
||||||
#strategy: "RotateOnBan" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
#strategy: "RotateOnBan" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
||||||
#searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
|
#searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
|
||||||
#retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
|
#retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
|
||||||
plugins:
|
plugins:
|
||||||
- dependency: "com.github.esmBot:lava-xm-plugin:v0.2.1"
|
- dependency: "com.github.esmBot:lava-xm-plugin:v0.2.1"
|
||||||
repository: "https://jitpack.io"
|
repository: "https://jitpack.io"
|
||||||
- dependency: "com.github.TopiSenpai.LavaSrc:lavasrc-plugin:3.2.0"
|
- dependency: "com.github.TopiSenpai.LavaSrc:lavasrc-plugin:3.2.0"
|
||||||
repository: "https://jitpack.io"
|
repository: "https://jitpack.io"
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
lavasrc:
|
lavasrc:
|
||||||
providers:
|
providers:
|
||||||
- "ytsearch:\"%ISRC%\""
|
- "ytsearch:\"%ISRC%\""
|
||||||
- "ytsearch:%QUERY%"
|
- "ytsearch:%QUERY%"
|
||||||
sources:
|
sources:
|
||||||
spotify: false
|
spotify: false
|
||||||
applemusic: true
|
applemusic: true
|
||||||
deezer: false
|
deezer: false
|
||||||
spotify:
|
spotify:
|
||||||
clientId: "your client id"
|
clientId: "your client id"
|
||||||
clientSecret: "your client secret"
|
clientSecret: "your client secret"
|
||||||
countryCode: "US"
|
countryCode: "US"
|
||||||
applemusic:
|
applemusic:
|
||||||
countryCode: "US"
|
countryCode: "US"
|
||||||
deezer:
|
deezer:
|
||||||
masterDecryptionKey: "go looking for this somewhere"
|
masterDecryptionKey: "go looking for this somewhere"
|
||||||
|
|
||||||
metrics:
|
metrics:
|
||||||
prometheus:
|
prometheus:
|
||||||
enabled: false
|
enabled: false
|
||||||
endpoint: /metrics
|
endpoint: /metrics
|
||||||
|
|
||||||
sentry:
|
sentry:
|
||||||
dsn: ""
|
dsn: ""
|
||||||
# tags:
|
# tags:
|
||||||
# some_key: some_value
|
# some_key: some_value
|
||||||
# another_key: another_value
|
# another_key: another_value
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
max-history: 30
|
max-history: 30
|
||||||
max-size: 1GB
|
max-size: 1GB
|
||||||
path: ./logs/
|
path: ./logs/
|
||||||
|
|
||||||
level:
|
level:
|
||||||
root: INFO
|
root: INFO
|
||||||
lavalink: INFO
|
lavalink: INFO
|
||||||
|
|
||||||
|
|
|
@ -1,71 +1,71 @@
|
||||||
class Command {
|
class Command {
|
||||||
success = true;
|
success = true;
|
||||||
constructor(client, options) {
|
constructor(matrixClient, options) {
|
||||||
this.client = client;
|
this.client = matrixClient;
|
||||||
this.origOptions = options;
|
this.origOptions = options;
|
||||||
this.type = options.type;
|
this.type = options.type;
|
||||||
this.args = options.args;
|
this.args = options.args;
|
||||||
if (options.type === "classic") {
|
if (options.type === "classic") {
|
||||||
this.message = options.message;
|
this.message = options.message;
|
||||||
this.channel = options.message.channel;
|
this.channel = options.message.room_id;
|
||||||
this.guild = options.message.guild;
|
this.guild = options.message.guild;
|
||||||
this.author = options.message.author;
|
this.author = options.message.sender;
|
||||||
this.member = options.message.member;
|
this.member = options.message.member;
|
||||||
this.content = options.content;
|
this.content = options.content;
|
||||||
this.options = options.specialArgs;
|
this.options = options.specialArgs;
|
||||||
this.reference = {
|
this.reference = {
|
||||||
messageReference: {
|
messageReference: {
|
||||||
channelID: this.message.channelID,
|
channelID: this.message.channelID,
|
||||||
messageID: this.message.id,
|
messageID: this.message.id,
|
||||||
guildID: this.message.guildID ?? undefined,
|
guildID: this.message.guildID ?? undefined,
|
||||||
failIfNotExists: false
|
failIfNotExists: false
|
||||||
},
|
},
|
||||||
allowedMentions: {
|
allowedMentions: {
|
||||||
repliedUser: false
|
repliedUser: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (options.type === "application") {
|
} else if (options.type === "application") {
|
||||||
this.interaction = options.interaction;
|
this.interaction = options.interaction;
|
||||||
this.args = [];
|
this.args = [];
|
||||||
this.channel = options.interaction.channel;
|
this.channel = options.interaction.channel;
|
||||||
this.guild = options.interaction.guild;
|
this.guild = options.interaction.guild;
|
||||||
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
|
this.author = this.member = options.interaction.guildID ? options.interaction.member : options.interaction.user;
|
||||||
if (options.interaction.data.options) {
|
if (options.interaction.data.options) {
|
||||||
this.options = options.interaction.data.options.raw.reduce((obj, item) => {
|
this.options = options.interaction.data.options.raw.reduce((obj, item) => {
|
||||||
obj[item.name] = item.value;
|
obj[item.name] = item.value;
|
||||||
return obj;
|
return obj;
|
||||||
}, {});
|
}, {});
|
||||||
this.optionsArray = options.interaction.data.options.raw;
|
this.optionsArray = options.interaction.data.options.raw;
|
||||||
} else {
|
} else {
|
||||||
this.options = {};
|
this.options = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
return "It works!";
|
return "It works!";
|
||||||
}
|
}
|
||||||
|
|
||||||
async acknowledge() {
|
async acknowledge() {
|
||||||
if (this.type === "classic") {
|
if (this.type === "classic") {
|
||||||
const channel = this.channel ?? await this.client.rest.channels.get(this.message.channelID);
|
const channel = this.channel;
|
||||||
await channel.sendTyping();
|
await this.client.sendTyping(channel, true, 5);
|
||||||
} else if (!this.interaction.acknowledged) {
|
} else if (!this.interaction.acknowledged) {
|
||||||
await this.interaction.defer();
|
await this.interaction.defer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "No description found";
|
static description = "No description found";
|
||||||
static aliases = [];
|
static aliases = [];
|
||||||
static arguments = [];
|
static arguments = [];
|
||||||
static flags = [];
|
static flags = [];
|
||||||
static slashAllowed = true;
|
static slashAllowed = true;
|
||||||
static directAllowed = true;
|
static directAllowed = true;
|
||||||
static adminOnly = false;
|
static adminOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Command;
|
export default Command;
|
|
@ -1,153 +1,160 @@
|
||||||
import Command from "./command.js";
|
import Command from "./command.js";
|
||||||
import imageDetect from "../utils/imagedetect.js";
|
import imageDetect from "../utils/imagedetect.js";
|
||||||
import { runImageJob } from "../utils/image.js";
|
import { runImageJob } from "../utils/image.js";
|
||||||
import { runningCommands } from "../utils/collections.js";
|
import { runningCommands } from "../utils/collections.js";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
const { emotes } = JSON.parse(readFileSync(new URL("../config/messages.json", import.meta.url)));
|
const { emotes } = JSON.parse(readFileSync(new URL("../config/messages.json", import.meta.url)));
|
||||||
import { random } from "../utils/misc.js";
|
import { random } from "../utils/misc.js";
|
||||||
import { selectedImages } from "../utils/collections.js";
|
import { selectedImages } from "../utils/collections.js";
|
||||||
|
|
||||||
class ImageCommand extends Command {
|
class ImageCommand extends Command {
|
||||||
async criteria() {
|
async criteria() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000);
|
const timestamp = this.type === "classic" ? this.message.createdAt : Math.floor((this.interaction.id / 4194304) + 1420070400000);
|
||||||
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
|
// check if this command has already been run in this channel with the same arguments, and we are awaiting its result
|
||||||
// if so, don't re-run it
|
// if so, don't re-run it
|
||||||
if (runningCommands.has(this.author.id) && (new Date(runningCommands.get(this.author.id)) - new Date(timestamp)) < 5000) {
|
if (runningCommands.has(this.author) && (new Date(runningCommands.get(this.author)) - new Date(timestamp)) < 5000) {
|
||||||
return "Please slow down a bit.";
|
return "Please slow down a bit.";
|
||||||
}
|
}
|
||||||
// before awaiting the command result, add this command to the set of running commands
|
// before awaiting the command result, add this command to the set of running commands
|
||||||
runningCommands.set(this.author.id, timestamp);
|
runningCommands.set(this.author, timestamp);
|
||||||
|
|
||||||
const imageParams = {
|
const imageParams = {
|
||||||
cmd: this.constructor.command,
|
cmd: this.constructor.command,
|
||||||
params: {
|
params: {
|
||||||
togif: !!this.options.togif
|
togif: !!this.options.togif
|
||||||
},
|
},
|
||||||
id: (this.interaction ?? this.message).id
|
id: this.message.event_id
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.type === "application") await this.acknowledge();
|
// if (this.type === "application") await this.acknowledge();
|
||||||
|
|
||||||
if (this.constructor.requiresImage) {
|
if (this.constructor.requiresImage) {
|
||||||
try {
|
try {
|
||||||
const selection = selectedImages.get(this.author.id);
|
const selection = selectedImages.get(this.author);
|
||||||
const image = selection ?? await imageDetect(this.client, this.message, this.interaction, this.options, true);
|
const image = await imageDetect(this.client, this.message, this.interaction, this.options, true);
|
||||||
if (selection) selectedImages.delete(this.author.id);
|
if (selection) selectedImages.delete(this.author);
|
||||||
if (image === undefined) {
|
if (image === undefined) {
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
return `${this.constructor.noImage} (Tip: try right-clicking/holding on a message and press Apps -> Select Image, then try again.)`;
|
return `${this.constructor.noImage} (Tip: try right-clicking/holding on a message and press Apps -> Select Image, then try again.)`;
|
||||||
} else if (image.type === "large") {
|
} else if (image.type === "large") {
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
return "That image is too large (>= 25MB)! Try using a smaller image.";
|
return "That image is too large (>= 25MB)! Try using a smaller image.";
|
||||||
} else if (image.type === "tenorlimit") {
|
} else if (image.type === "tenorlimit") {
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere.";
|
return "I've been rate-limited by Tenor. Please try uploading your GIF elsewhere.";
|
||||||
}
|
}
|
||||||
imageParams.path = image.path;
|
imageParams.path = image.path;
|
||||||
imageParams.params.type = image.type;
|
imageParams.params.type = image.type;
|
||||||
imageParams.url = image.url; // technically not required but can be useful for text filtering
|
imageParams.url = image.url; // technically not required but can be useful for text filtering
|
||||||
imageParams.name = image.name;
|
imageParams.name = image.name;
|
||||||
if (this.constructor.requiresGIF) imageParams.onlyGIF = true;
|
if (this.constructor.requiresGIF) imageParams.onlyGIF = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.constructor.requiresText) {
|
if (this.constructor.requiresText) {
|
||||||
const text = this.options.text ?? this.args.join(" ").trim();
|
const text = this.options.text ?? this.args.join(" ").trim();
|
||||||
if (text.length === 0 || !await this.criteria(text, imageParams.url)) {
|
if (text.length === 0 || !await this.criteria(text, imageParams.url)) {
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
return this.constructor.noText;
|
return this.constructor.noText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.params === "function") {
|
if (typeof this.params === "function") {
|
||||||
Object.assign(imageParams.params, this.params(imageParams.url, imageParams.name));
|
Object.assign(imageParams.params, this.params(imageParams.url, imageParams.name));
|
||||||
} else if (typeof this.params === "object") {
|
} else if (typeof this.params === "object") {
|
||||||
Object.assign(imageParams.params, this.params);
|
Object.assign(imageParams.params, this.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
let status;
|
let status;
|
||||||
if (imageParams.params.type === "image/gif" && this.type === "classic") {
|
if (imageParams.params.type === "image/gif" && this.type === "classic") {
|
||||||
status = await this.processMessage(this.message.channel ?? await this.client.rest.channels.get(this.message.channelID));
|
status = await this.processMessage(this.message.room_id ?? await this.client.rest.channels.get(this.message.room_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { buffer, type } = await runImageJob(imageParams);
|
const { buffer, type } = await runImageJob(imageParams);
|
||||||
if (type === "nogif" && this.constructor.requiresGIF) {
|
if (type === "nogif" && this.constructor.requiresGIF) {
|
||||||
return "That isn't a GIF!";
|
return "That isn't a GIF!";
|
||||||
}
|
}
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return {
|
return {
|
||||||
contents: buffer,
|
contents: buffer,
|
||||||
name: `${this.constructor.command}.${type}`
|
name: `${this.constructor.command}.${type}`
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again.";
|
if (e === "Request ended prematurely due to a closed connection") return "This image job couldn't be completed because the server it was running on went down. Try running your command again.";
|
||||||
if (e === "Job timed out" || e === "Timeout") return "The image is taking too long to process (>=15 minutes), so the job was cancelled. Try using a smaller image.";
|
if (e === "Job timed out" || e === "Timeout") return "The image is taking too long to process (>=15 minutes), so the job was cancelled. Try using a smaller image.";
|
||||||
if (e === "No available servers") return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit.";
|
if (e === "No available servers") return "I can't seem to contact the image servers, they might be down or still trying to start up. Please wait a little bit.";
|
||||||
throw e;
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
if (status) await status.delete();
|
if (status) await status.delete();
|
||||||
} catch {
|
} catch {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
runningCommands.delete(this.author.id);
|
runningCommands.delete(this.author);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processMessage(channel) {
|
processMessage(channel) {
|
||||||
return channel.createMessage({
|
this.client.send
|
||||||
content: `${random(emotes) || process.env.PROCESSING_EMOJI || "<a:processing:479351417102925854>"} Processing... This might take a while`
|
const content = {
|
||||||
});
|
body: "Processing... This might take a while",
|
||||||
}
|
msgtype: "m.text",
|
||||||
|
};
|
||||||
static init() {
|
this.client.sendEvent(channel, "m.room.message", content, "", (err, res) => {
|
||||||
this.flags = [];
|
// console.log(res)
|
||||||
if (this.requiresText || this.textOptional) {
|
logger.log("error", err)
|
||||||
this.flags.push({
|
return res
|
||||||
name: "text",
|
});
|
||||||
type: 3,
|
}
|
||||||
description: "The text to put on the image",
|
|
||||||
required: !this.textOptional
|
static init() {
|
||||||
});
|
this.flags = [];
|
||||||
}
|
if (this.requiresText || this.textOptional) {
|
||||||
if (this.requiresImage) {
|
this.flags.push({
|
||||||
this.flags.push({
|
name: "text",
|
||||||
name: "image",
|
type: 3,
|
||||||
type: 11,
|
description: "The text to put on the image",
|
||||||
description: "An image/GIF attachment"
|
required: !this.textOptional
|
||||||
}, {
|
});
|
||||||
name: "link",
|
}
|
||||||
type: 3,
|
if (this.requiresImage) {
|
||||||
description: "An image/GIF URL"
|
this.flags.push({
|
||||||
});
|
name: "image",
|
||||||
}
|
type: 11,
|
||||||
this.flags.push({
|
description: "An image/GIF attachment"
|
||||||
name: "togif",
|
}, {
|
||||||
type: 5,
|
name: "link",
|
||||||
description: "Force GIF output"
|
type: 3,
|
||||||
});
|
description: "An image/GIF URL"
|
||||||
return this;
|
});
|
||||||
}
|
}
|
||||||
|
this.flags.push({
|
||||||
static allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times", "comic sans ms"];
|
name: "togif",
|
||||||
|
type: 5,
|
||||||
static requiresImage = true;
|
description: "Force GIF output"
|
||||||
static requiresText = false;
|
});
|
||||||
static textOptional = false;
|
return this;
|
||||||
static requiresGIF = false;
|
}
|
||||||
static noImage = "You need to provide an image/GIF!";
|
|
||||||
static noText = "You need to provide some text!";
|
static allowedFonts = ["futura", "impact", "helvetica", "arial", "roboto", "noto", "times", "comic sans ms"];
|
||||||
static command = "";
|
|
||||||
}
|
static requiresImage = true;
|
||||||
|
static requiresText = false;
|
||||||
export default ImageCommand;
|
static textOptional = false;
|
||||||
|
static requiresGIF = false;
|
||||||
|
static noImage = "You need to provide an image/GIF!";
|
||||||
|
static noText = "You need to provide some text!";
|
||||||
|
static command = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ImageCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import Command from "./command.js";
|
import Command from "./command.js";
|
||||||
import { players, queues } from "../utils/soundplayer.js";
|
import { players, queues } from "../utils/soundplayer.js";
|
||||||
|
|
||||||
class MusicCommand extends Command {
|
class MusicCommand extends Command {
|
||||||
constructor(client, options) {
|
constructor(client, options) {
|
||||||
super(client, options);
|
super(client, options);
|
||||||
if (this.guild) {
|
if (this.guild) {
|
||||||
this.connection = players.get(this.guild.id);
|
this.connection = players.get(this.guild.id);
|
||||||
this.queue = queues.get(this.guild.id);
|
this.queue = queues.get(this.guild.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
static directAllowed = false;
|
static directAllowed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MusicCommand;
|
export default MusicCommand;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import Command from "./command.js";
|
import Command from "./command.js";
|
||||||
import { play } from "../utils/soundplayer.js";
|
import { play } from "../utils/soundplayer.js";
|
||||||
|
|
||||||
// only exists to sort the various soundboard commands
|
// only exists to sort the various soundboard commands
|
||||||
class SoundboardCommand extends Command {
|
class SoundboardCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
return play(this.client, this.constructor.file, { channel: this.channel, author: this.author, member: this.member, type: this.type, interaction: this.interaction });
|
return play(this.client, this.constructor.file, { channel: this.channel, author: this.author, member: this.member, type: this.type, interaction: this.interaction });
|
||||||
}
|
}
|
||||||
|
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
static directAllowed = false;
|
static directAllowed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SoundboardCommand;
|
export default SoundboardCommand;
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { random } from "../../utils/misc.js";
|
import { random } from "../../utils/misc.js";
|
||||||
|
|
||||||
class EightBallCommand extends Command {
|
class EightBallCommand extends Command {
|
||||||
static responses = [
|
static responses = [
|
||||||
"It is certain",
|
"It is certain",
|
||||||
"It is decidedly so",
|
"It is decidedly so",
|
||||||
"Without a doubt",
|
"Without a doubt",
|
||||||
"Yes, definitely",
|
"Yes, definitely",
|
||||||
"You may rely on it",
|
"You may rely on it",
|
||||||
"As I see it, yes",
|
"As I see it, yes",
|
||||||
"Most likely",
|
"Most likely",
|
||||||
"Outlook good",
|
"Outlook good",
|
||||||
"Yes",
|
"Yes",
|
||||||
"Signs point to yes",
|
"Signs point to yes",
|
||||||
"Reply hazy, try again",
|
"Reply hazy, try again",
|
||||||
"Ask again later",
|
"Ask again later",
|
||||||
"Better not tell you now",
|
"Better not tell you now",
|
||||||
"Cannot predict now",
|
"Cannot predict now",
|
||||||
"Concentrate and ask again",
|
"Concentrate and ask again",
|
||||||
"Don't count on it",
|
"Don't count on it",
|
||||||
"My reply is no",
|
"My reply is no",
|
||||||
"My sources say no",
|
"My sources say no",
|
||||||
"Outlook not so good",
|
"Outlook not so good",
|
||||||
"Very doubtful"
|
"Very doubtful"
|
||||||
];
|
];
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
return `🎱 ${random(EightBallCommand.responses)}`;
|
return `🎱 ${random(EightBallCommand.responses)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "question",
|
name: "question",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "A question you want to ask the ball"
|
description: "A question you want to ask the ball"
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Asks the magic 8-ball a question";
|
static description = "Asks the magic 8-ball a question";
|
||||||
static aliases = ["magicball", "magikball", "magic8ball", "magik8ball", "eightball"];
|
static aliases = ["magicball", "magikball", "magic8ball", "magik8ball", "eightball"];
|
||||||
static arguments = ["{text}"];
|
static arguments = ["{text}"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EightBallCommand;
|
export default EightBallCommand;
|
|
@ -1,27 +1,27 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class AncientCommand extends Command {
|
class AncientCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
try {
|
try {
|
||||||
const data = await request("https://files.projectlounge.pw/meme/", { method: "HEAD", signal: controller.signal });
|
const data = await request("https://files.projectlounge.pw/meme/", { method: "HEAD", signal: controller.signal });
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
return `https://files.projectlounge.pw${data.headers.location}`;
|
return `https://files.projectlounge.pw${data.headers.location}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === "AbortError") {
|
if (e.name === "AbortError") {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I couldn't get a meme in time. Maybe try again?";
|
return "I couldn't get a meme in time. Maybe try again?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a random ancient meme";
|
static description = "Gets a random ancient meme";
|
||||||
static aliases = ["old", "oldmeme", "badmeme"];
|
static aliases = ["old", "oldmeme", "badmeme"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AncientCommand;
|
export default AncientCommand;
|
|
@ -1,28 +1,28 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class BirdCommand extends Command {
|
class BirdCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
try {
|
try {
|
||||||
const imageData = await request("http://shibe.online/api/birds", { signal: controller.signal });
|
const imageData = await request("http://shibe.online/api/birds", { signal: controller.signal });
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const json = await imageData.body.json();
|
const json = await imageData.body.json();
|
||||||
return json[0];
|
return json[0];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === "AbortError") {
|
if (e.name === "AbortError") {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I couldn't get a bird image in time. Maybe try again?";
|
return "I couldn't get a bird image in time. Maybe try again?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a random bird picture";
|
static description = "Gets a random bird picture";
|
||||||
static aliases = ["birb", "birds", "birbs"];
|
static aliases = ["birb", "birds", "birbs"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BirdCommand;
|
export default BirdCommand;
|
|
@ -1,27 +1,27 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class CatCommand extends Command {
|
class CatCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
try {
|
try {
|
||||||
const data = await request("https://files.projectlounge.pw/cta/", { method: "HEAD", signal: controller.signal });
|
const data = await request("https://files.projectlounge.pw/cta/", { method: "HEAD", signal: controller.signal });
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
return `https://files.projectlounge.pw${data.headers.location}`;
|
return `https://files.projectlounge.pw${data.headers.location}`;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === "AbortError") {
|
if (e.name === "AbortError") {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I couldn't get a cat image in time. Maybe try again?";
|
return "I couldn't get a cat image in time. Maybe try again?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a random cat picture";
|
static description = "Gets a random cat picture";
|
||||||
static aliases = ["kitters", "kitties", "kitty", "cattos", "catto", "cats", "cta"];
|
static aliases = ["kitters", "kitties", "kitty", "cattos", "catto", "cats", "cta"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CatCommand;
|
export default CatCommand;
|
|
@ -1,25 +1,25 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class DiceCommand extends Command {
|
class DiceCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const max = this.options.max ?? parseInt(this.args[0]);
|
const max = this.options.max ?? parseInt(this.args[0]);
|
||||||
if (!max) {
|
if (!max) {
|
||||||
return `🎲 The dice landed on ${Math.floor(Math.random() * 6) + 1}.`;
|
return `🎲 The dice landed on ${Math.floor(Math.random() * 6) + 1}.`;
|
||||||
} else {
|
} else {
|
||||||
return `🎲 The dice landed on ${Math.floor(Math.random() * max) + 1}.`;
|
return `🎲 The dice landed on ${Math.floor(Math.random() * max) + 1}.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "max",
|
name: "max",
|
||||||
type: 4,
|
type: 4,
|
||||||
description: "The maximum dice value",
|
description: "The maximum dice value",
|
||||||
min_value: 1
|
min_value: 1
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Rolls the dice";
|
static description = "Rolls the dice";
|
||||||
static aliases = ["roll", "die", "rng", "random"];
|
static aliases = ["roll", "die", "rng", "random"];
|
||||||
static arguments = ["{number}"];
|
static arguments = ["{number}"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DiceCommand;
|
export default DiceCommand;
|
|
@ -1,28 +1,28 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class DogCommand extends Command {
|
class DogCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
try {
|
try {
|
||||||
const imageData = await request("https://dog.ceo/api/breeds/image/random", { signal: controller.signal });
|
const imageData = await request("https://dog.ceo/api/breeds/image/random", { signal: controller.signal });
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const json = await imageData.body.json();
|
const json = await imageData.body.json();
|
||||||
return json.message;
|
return json.message;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === "AbortError") {
|
if (e.name === "AbortError") {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I couldn't get a dog image in time. Maybe try again?";
|
return "I couldn't get a dog image in time. Maybe try again?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a random dog picture";
|
static description = "Gets a random dog picture";
|
||||||
static aliases = ["doggos", "doggo", "pupper", "puppers", "dogs", "puppy", "puppies", "pups", "pup"];
|
static aliases = ["doggos", "doggo", "pupper", "puppers", "dogs", "puppy", "puppies", "pups", "pup"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DogCommand;
|
export default DogCommand;
|
|
@ -1,20 +1,20 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class HomebrewCommand extends ImageCommand {
|
class HomebrewCommand extends ImageCommand {
|
||||||
params() {
|
params() {
|
||||||
return {
|
return {
|
||||||
caption: (this.options.text ?? this.args.join(" ")).toLowerCase().replaceAll("\n", " ")
|
caption: (this.options.text ?? this.args.join(" ")).toLowerCase().replaceAll("\n", " ")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Creates a Homebrew Channel edit";
|
static description = "Creates a Homebrew Channel edit";
|
||||||
static aliases = ["hbc", "brew", "wiibrew"];
|
static aliases = ["hbc", "brew", "wiibrew"];
|
||||||
static arguments = ["[text]"];
|
static arguments = ["[text]"];
|
||||||
|
|
||||||
static requiresImage = false;
|
static requiresImage = false;
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide some text to make a Homebrew Channel edit!";
|
static noText = "You need to provide some text to make a Homebrew Channel edit!";
|
||||||
static command = "homebrew";
|
static command = "homebrew";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HomebrewCommand;
|
export default HomebrewCommand;
|
|
@ -1,22 +1,22 @@
|
||||||
//import wrap from "../../utils/wrap.js";
|
//import wrap from "../../utils/wrap.js";
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { cleanMessage } from "../../utils/misc.js";
|
import { cleanMessage } from "../../utils/misc.js";
|
||||||
|
|
||||||
class SonicCommand extends ImageCommand {
|
class SonicCommand extends ImageCommand {
|
||||||
params() {
|
params() {
|
||||||
const cleanedMessage = cleanMessage(this.message ?? this.interaction, this.options.text ?? this.args.join(" "));
|
const cleanedMessage = cleanMessage(this.message ?? this.interaction, this.options.text ?? this.args.join(" "));
|
||||||
return {
|
return {
|
||||||
text: cleanedMessage
|
text: cleanedMessage
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Creates a Sonic speech bubble image";
|
static description = "Creates a Sonic speech bubble image";
|
||||||
static arguments = ["[text]"];
|
static arguments = ["[text]"];
|
||||||
|
|
||||||
static requiresImage = false;
|
static requiresImage = false;
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide some text to make a Sonic meme!";
|
static noText = "You need to provide some text to make a Sonic meme!";
|
||||||
static command = "sonic";
|
static command = "sonic";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SonicCommand;
|
export default SonicCommand;
|
|
@ -1,34 +1,34 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class WikihowCommand extends Command {
|
class WikihowCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
try {
|
try {
|
||||||
const req = await request("https://www.wikihow.com/api.php?action=query&generator=random&prop=imageinfo&format=json&iiprop=url&grnnamespace=6", { signal: controller.signal });
|
const req = await request("https://www.wikihow.com/api.php?action=query&generator=random&prop=imageinfo&format=json&iiprop=url&grnnamespace=6", { signal: controller.signal });
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const json = await req.body.json();
|
const json = await req.body.json();
|
||||||
const id = Object.keys(json.query.pages)[0];
|
const id = Object.keys(json.query.pages)[0];
|
||||||
const data = json.query.pages[id];
|
const data = json.query.pages[id];
|
||||||
if (data.imageinfo) {
|
if (data.imageinfo) {
|
||||||
return json.query.pages[id].imageinfo[0].url;
|
return json.query.pages[id].imageinfo[0].url;
|
||||||
} else {
|
} else {
|
||||||
return await this.run();
|
return await this.run();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === "AbortError") {
|
if (e.name === "AbortError") {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I couldn't get a WikiHow image in time. Maybe try again?";
|
return "I couldn't get a WikiHow image in time. Maybe try again?";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a random WikiHow image";
|
static description = "Gets a random WikiHow image";
|
||||||
static aliases = ["wiki"];
|
static aliases = ["wiki"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default WikihowCommand;
|
export default WikihowCommand;
|
||||||
|
|
|
@ -1,53 +1,53 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
|
const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
|
||||||
|
|
||||||
class AvatarCommand extends Command {
|
class AvatarCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const member = this.options.member ?? this.args[0];
|
const member = this.options.member ?? this.args[0];
|
||||||
const self = this.client.users.get(this.author.id) ?? await this.client.rest.users.get(this.author.id);
|
const self = this.client.users.get(this.author.id) ?? await this.client.rest.users.get(this.author.id);
|
||||||
if (this.type === "classic" && this.message.mentions.users[0]) {
|
if (this.type === "classic" && this.message.mentions.users[0]) {
|
||||||
return this.message.mentions.users[0].avatarURL(null, 512);
|
return this.message.mentions.users[0].avatarURL(null, 512);
|
||||||
} else if (member && member > 21154535154122752n) {
|
} else if (member && member > 21154535154122752n) {
|
||||||
const user = this.client.users.get(member) ?? await this.client.rest.users.get(member);
|
const user = this.client.users.get(member) ?? await this.client.rest.users.get(member);
|
||||||
if (user) {
|
if (user) {
|
||||||
return user.avatarURL(null, 512);
|
return user.avatarURL(null, 512);
|
||||||
} else if (mentionRegex.test(member)) {
|
} else if (mentionRegex.test(member)) {
|
||||||
const id = member.match(mentionRegex)[1];
|
const id = member.match(mentionRegex)[1];
|
||||||
if (id < 21154535154122752n) {
|
if (id < 21154535154122752n) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "That's not a valid mention!";
|
return "That's not a valid mention!";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = this.client.users.get(id) ?? await this.client.rest.users.get(id);
|
const user = this.client.users.get(id) ?? await this.client.rest.users.get(id);
|
||||||
return user.avatarURL(null, 512);
|
return user.avatarURL(null, 512);
|
||||||
} catch {
|
} catch {
|
||||||
return self.avatarURL(null, 512);
|
return self.avatarURL(null, 512);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.avatarURL(null, 512);
|
return self.avatarURL(null, 512);
|
||||||
}
|
}
|
||||||
} else if (this.args.join(" ") !== "" && this.guild) {
|
} else if (this.args.join(" ") !== "" && this.guild) {
|
||||||
const searched = await this.guild.searchMembers({
|
const searched = await this.guild.searchMembers({
|
||||||
query: this.args.join(" "),
|
query: this.args.join(" "),
|
||||||
limit: 1
|
limit: 1
|
||||||
});
|
});
|
||||||
if (searched.length === 0) return self.avatarURL(null, 512);
|
if (searched.length === 0) return self.avatarURL(null, 512);
|
||||||
const user = this.client.users.get(searched[0].user.id) ?? await this.client.rest.users.get(searched[0].user.id);
|
const user = this.client.users.get(searched[0].user.id) ?? await this.client.rest.users.get(searched[0].user.id);
|
||||||
return user ? user.avatarURL(null, 512) : self.avatarURL(null, 512);
|
return user ? user.avatarURL(null, 512) : self.avatarURL(null, 512);
|
||||||
} else {
|
} else {
|
||||||
return self.avatarURL(null, 512);
|
return self.avatarURL(null, 512);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a user's avatar";
|
static description = "Gets a user's avatar";
|
||||||
static aliases = ["pfp", "ava"];
|
static aliases = ["pfp", "ava"];
|
||||||
static arguments = ["{mention/id}"];
|
static arguments = ["{mention/id}"];
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "member",
|
name: "member",
|
||||||
type: 6,
|
type: 6,
|
||||||
description: "The member to get the avatar from",
|
description: "The member to get the avatar from",
|
||||||
required: false
|
required: false
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AvatarCommand;
|
export default AvatarCommand;
|
||||||
|
|
|
@ -1,55 +1,55 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { Routes } from "oceanic.js";
|
import { Routes } from "oceanic.js";
|
||||||
const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
|
const mentionRegex = /^<?[@#]?[&!]?(\d+)>?$/;
|
||||||
|
|
||||||
class BannerCommand extends Command {
|
class BannerCommand extends Command {
|
||||||
// this command sucks
|
// this command sucks
|
||||||
async run() {
|
async run() {
|
||||||
const member = this.options.member ?? this.args[0];
|
const member = this.options.member ?? this.args[0];
|
||||||
const self = await this.client.rest.users.get(this.author.id); // banners are only available over REST
|
const self = await this.client.rest.users.get(this.author.id); // banners are only available over REST
|
||||||
if (this.type === "classic" && this.message.mentions.users[0] && this.message.mentions.users[0].banner) {
|
if (this.type === "classic" && this.message.mentions.users[0] && this.message.mentions.users[0].banner) {
|
||||||
return this.client.util.formatImage(Routes.BANNER(this.message.mentions.users[0].id, this.message.mentions.users[0].banner), null, 512);
|
return this.client.util.formatImage(Routes.BANNER(this.message.mentions.users[0].id, this.message.mentions.users[0].banner), null, 512);
|
||||||
} else if (member && member > 21154535154122752n) {
|
} else if (member && member > 21154535154122752n) {
|
||||||
const user = await this.client.rest.users.get(member);
|
const user = await this.client.rest.users.get(member);
|
||||||
if (user && user.banner) {
|
if (user && user.banner) {
|
||||||
return this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512);
|
return this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512);
|
||||||
} else if (mentionRegex.test(member)) {
|
} else if (mentionRegex.test(member)) {
|
||||||
const id = member.match(mentionRegex)[1];
|
const id = member.match(mentionRegex)[1];
|
||||||
if (id < 21154535154122752n) {
|
if (id < 21154535154122752n) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "That's not a valid mention!";
|
return "That's not a valid mention!";
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const user = await this.client.rest.users.get(id);
|
const user = await this.client.rest.users.get(id);
|
||||||
return user.banner ? this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512) : "This user doesn't have a banner!";
|
return user.banner ? this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512) : "This user doesn't have a banner!";
|
||||||
} catch {
|
} catch {
|
||||||
return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "You don't have a banner!";
|
return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "You don't have a banner!";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return "This user doesn't have a banner!";
|
return "This user doesn't have a banner!";
|
||||||
}
|
}
|
||||||
} else if (this.args.join(" ") !== "" && this.guild) {
|
} else if (this.args.join(" ") !== "" && this.guild) {
|
||||||
const searched = await this.guild.searchMembers({
|
const searched = await this.guild.searchMembers({
|
||||||
query: this.args.join(" "),
|
query: this.args.join(" "),
|
||||||
limit: 1
|
limit: 1
|
||||||
});
|
});
|
||||||
if (searched.length === 0) return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "This user doesn't have a banner!";
|
if (searched.length === 0) return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "This user doesn't have a banner!";
|
||||||
const user = await this.client.rest.users.get(searched[0].user.id);
|
const user = await this.client.rest.users.get(searched[0].user.id);
|
||||||
return user.banner ? this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512) : (self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "This user doesn't have a banner!");
|
return user.banner ? this.client.util.formatImage(Routes.BANNER(user.id, user.banner), null, 512) : (self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "This user doesn't have a banner!");
|
||||||
} else {
|
} else {
|
||||||
return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "You don't have a banner!";
|
return self.banner ? this.client.util.formatImage(Routes.BANNER(self.id, self.banner), null, 512) : "You don't have a banner!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a user's banner";
|
static description = "Gets a user's banner";
|
||||||
static aliases = ["userbanner"];
|
static aliases = ["userbanner"];
|
||||||
static arguments = ["{mention/id}"];
|
static arguments = ["{mention/id}"];
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "member",
|
name: "member",
|
||||||
type: 6,
|
type: 6,
|
||||||
description: "The member to get the banner from",
|
description: "The member to get the banner from",
|
||||||
required: false
|
required: false
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BannerCommand;
|
export default BannerCommand;
|
||||||
|
|
|
@ -1,48 +1,48 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { clean } from "../../utils/misc.js";
|
import { clean } from "../../utils/misc.js";
|
||||||
|
|
||||||
class Base64Command extends Command {
|
class Base64Command extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (this.type === "classic" && this.args.length === 0) return "You need to provide whether you want to encode or decode the text!";
|
if (this.type === "classic" && this.args.length === 0) return "You need to provide whether you want to encode or decode the text!";
|
||||||
const command = this.type === "classic" ? this.args[0].toLowerCase() : this.optionsArray[0].name.toLowerCase();
|
const command = this.type === "classic" ? this.args[0].toLowerCase() : this.optionsArray[0].name.toLowerCase();
|
||||||
if (command !== "decode" && command !== "encode") return "You need to provide whether you want to encode or decode the text!";
|
if (command !== "decode" && command !== "encode") return "You need to provide whether you want to encode or decode the text!";
|
||||||
const string = this.options.text ?? this.args.slice(1).join(" ");
|
const string = this.options.text ?? this.args.slice(1).join(" ");
|
||||||
if (!string || !string.trim()) return `You need to provide a string to ${command}!`;
|
if (!string || !string.trim()) return `You need to provide a string to ${command}!`;
|
||||||
this.success = true;
|
this.success = true;
|
||||||
if (command === "decode") {
|
if (command === "decode") {
|
||||||
const b64Decoded = Buffer.from(string, "base64").toString("utf8");
|
const b64Decoded = Buffer.from(string, "base64").toString("utf8");
|
||||||
return `\`\`\`\n${await clean(b64Decoded)}\`\`\``;
|
return `\`\`\`\n${await clean(b64Decoded)}\`\`\``;
|
||||||
} else if (command === "encode") {
|
} else if (command === "encode") {
|
||||||
const b64Encoded = Buffer.from(string, "utf8").toString("base64");
|
const b64Encoded = Buffer.from(string, "utf8").toString("base64");
|
||||||
return `\`\`\`\n${b64Encoded}\`\`\``;
|
return `\`\`\`\n${b64Encoded}\`\`\``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "decode",
|
name: "decode",
|
||||||
type: 1,
|
type: 1,
|
||||||
description: "Decodes a Base64 string",
|
description: "Decodes a Base64 string",
|
||||||
options: [{
|
options: [{
|
||||||
name: "text",
|
name: "text",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The text to decode",
|
description: "The text to decode",
|
||||||
required: true
|
required: true
|
||||||
}]
|
}]
|
||||||
}, {
|
}, {
|
||||||
name: "encode",
|
name: "encode",
|
||||||
type: 1,
|
type: 1,
|
||||||
description: "Encodes a Base64 string",
|
description: "Encodes a Base64 string",
|
||||||
options: [{
|
options: [{
|
||||||
name: "text",
|
name: "text",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The text to encode",
|
description: "The text to encode",
|
||||||
required: true
|
required: true
|
||||||
}]
|
}]
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Encodes/decodes a Base64 string";
|
static description = "Encodes/decodes a Base64 string";
|
||||||
static arguments = ["[encode/decode]", "[text]"];
|
static arguments = ["[encode/decode]", "[text]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Base64Command;
|
export default Base64Command;
|
|
@ -1,52 +1,52 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import database from "../../utils/database.js";
|
import database from "../../utils/database.js";
|
||||||
import { endBroadcast, startBroadcast } from "../../utils/misc.js";
|
import { endBroadcast, startBroadcast } from "../../utils/misc.js";
|
||||||
|
|
||||||
class BroadcastCommand extends Command {
|
class BroadcastCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can broadcast messages!";
|
return "Only the bot owner can broadcast messages!";
|
||||||
}
|
}
|
||||||
const message = this.options.message ?? this.args.join(" ");
|
const message = this.options.message ?? this.args.join(" ");
|
||||||
if (message?.trim()) {
|
if (message?.trim()) {
|
||||||
await database.setBroadcast(message);
|
await database.setBroadcast(message);
|
||||||
startBroadcast(this.client, message);
|
startBroadcast(this.client, message);
|
||||||
if (process.env.PM2_USAGE) {
|
if (process.env.PM2_USAGE) {
|
||||||
process.send({
|
process.send({
|
||||||
type: "process:msg",
|
type: "process:msg",
|
||||||
data: {
|
data: {
|
||||||
type: "broadcastStart",
|
type: "broadcastStart",
|
||||||
message
|
message
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return "Started broadcast.";
|
return "Started broadcast.";
|
||||||
} else {
|
} else {
|
||||||
await database.setBroadcast(null);
|
await database.setBroadcast(null);
|
||||||
endBroadcast(this.client);
|
endBroadcast(this.client);
|
||||||
if (process.env.PM2_USAGE) {
|
if (process.env.PM2_USAGE) {
|
||||||
process.send({
|
process.send({
|
||||||
type: "process:msg",
|
type: "process:msg",
|
||||||
data: {
|
data: {
|
||||||
type: "broadcastEnd"
|
type: "broadcastEnd"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return "Ended broadcast.";
|
return "Ended broadcast.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "message",
|
name: "message",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The message to broadcast"
|
description: "The message to broadcast"
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Broadcasts a playing message until the command is run again or the bot restarts";
|
static description = "Broadcasts a playing message until the command is run again or the bot restarts";
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
static dbRequired = true;
|
static dbRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BroadcastCommand;
|
export default BroadcastCommand;
|
|
@ -1,53 +1,53 @@
|
||||||
import db from "../../utils/database.js";
|
import db from "../../utils/database.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class ChannelCommand extends Command {
|
class ChannelCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!this.guild) return "This command only works in servers!";
|
if (!this.guild) return "This command only works in servers!";
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
|
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
|
||||||
if (this.args.length === 0) return "You need to provide whether I should be enabled or disabled in this channel!";
|
if (this.args.length === 0) return "You need to provide whether I should be enabled or disabled in this channel!";
|
||||||
if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!";
|
if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!";
|
||||||
|
|
||||||
const guildDB = await db.getGuild(this.guild.id);
|
const guildDB = await db.getGuild(this.guild.id);
|
||||||
|
|
||||||
if (this.args[0].toLowerCase() === "disable") {
|
if (this.args[0].toLowerCase() === "disable") {
|
||||||
let channel;
|
let channel;
|
||||||
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
|
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
|
||||||
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
|
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
|
||||||
if (guildDB.disabled.includes(id)) return "I'm already disabled in this channel!";
|
if (guildDB.disabled.includes(id)) return "I'm already disabled in this channel!";
|
||||||
channel = this.guild.channels.get(id) ?? await this.client.rest.channels.get(id);
|
channel = this.guild.channels.get(id) ?? await this.client.rest.channels.get(id);
|
||||||
} else {
|
} else {
|
||||||
if (guildDB.disabled.includes(this.channel.id)) return "I'm already disabled in this channel!";
|
if (guildDB.disabled.includes(this.channel.id)) return "I'm already disabled in this channel!";
|
||||||
channel = this.channel;
|
channel = this.channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.disableChannel(channel);
|
await db.disableChannel(channel);
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `I have been disabled in this channel. To re-enable me, just run \`${guildDB.prefix}channel enable\`.`;
|
return `I have been disabled in this channel. To re-enable me, just run \`${guildDB.prefix}channel enable\`.`;
|
||||||
} else if (this.args[0].toLowerCase() === "enable") {
|
} else if (this.args[0].toLowerCase() === "enable") {
|
||||||
let channel;
|
let channel;
|
||||||
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
|
if (this.args[1]?.match(/^<?[@#]?[&!]?\d+>?$/) && this.args[1] >= 21154535154122752n) {
|
||||||
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
|
const id = this.args[1].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "");
|
||||||
if (!guildDB.disabled.includes(id)) return "I'm not disabled in that channel!";
|
if (!guildDB.disabled.includes(id)) return "I'm not disabled in that channel!";
|
||||||
channel = this.guild.channels.get(id) ?? await this.client.rest.channels.get(id);
|
channel = this.guild.channels.get(id) ?? await this.client.rest.channels.get(id);
|
||||||
} else {
|
} else {
|
||||||
if (!guildDB.disabled.includes(this.channel.id)) return "I'm not disabled in this channel!";
|
if (!guildDB.disabled.includes(this.channel.id)) return "I'm not disabled in this channel!";
|
||||||
channel = this.channel;
|
channel = this.channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.enableChannel(channel);
|
await db.enableChannel(channel);
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return "I have been re-enabled in this channel.";
|
return "I have been re-enabled in this channel.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Enables/disables classic commands in a channel (use server settings for slash commands)";
|
static description = "Enables/disables classic commands in a channel (use server settings for slash commands)";
|
||||||
static arguments = ["[enable/disable]", "{id}"];
|
static arguments = ["[enable/disable]", "{id}"];
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
static directAllowed = false;
|
static directAllowed = false;
|
||||||
static dbRequired = true;
|
static dbRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChannelCommand;
|
export default ChannelCommand;
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
import db from "../../utils/database.js";
|
import db from "../../utils/database.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import * as collections from "../../utils/collections.js";
|
import * as collections from "../../utils/collections.js";
|
||||||
|
|
||||||
class CommandCommand extends Command {
|
class CommandCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!this.guild) return "This command only works in servers!";
|
if (!this.guild) return "This command only works in servers!";
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
|
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) return "You need to be an administrator to enable/disable me!";
|
||||||
if (this.args.length === 0) return "You need to provide whether you want to enable/disable a command!";
|
if (this.args.length === 0) return "You need to provide whether you want to enable/disable a command!";
|
||||||
if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!";
|
if (this.args[0] !== "disable" && this.args[0] !== "enable") return "That's not a valid option!";
|
||||||
if (!this.args[1]) return "You need to provide what command to enable/disable!";
|
if (!this.args[1]) return "You need to provide what command to enable/disable!";
|
||||||
if (!collections.commands.has(this.args[1].toLowerCase()) && !collections.aliases.has(this.args[1].toLowerCase())) return "That isn't a command!";
|
if (!collections.commands.has(this.args[1].toLowerCase()) && !collections.aliases.has(this.args[1].toLowerCase())) return "That isn't a command!";
|
||||||
|
|
||||||
const guildDB = await db.getGuild(this.guild.id);
|
const guildDB = await db.getGuild(this.guild.id);
|
||||||
const disabled = guildDB.disabled_commands ?? guildDB.disabledCommands;
|
const disabled = guildDB.disabled_commands ?? guildDB.disabledCommands;
|
||||||
const command = collections.aliases.get(this.args[1].toLowerCase()) ?? this.args[1].toLowerCase();
|
const command = collections.aliases.get(this.args[1].toLowerCase()) ?? this.args[1].toLowerCase();
|
||||||
|
|
||||||
if (this.args[0].toLowerCase() === "disable") {
|
if (this.args[0].toLowerCase() === "disable") {
|
||||||
if (command === "command") return "You can't disable that command!";
|
if (command === "command") return "You can't disable that command!";
|
||||||
if (disabled?.includes(command)) return "That command is already disabled!";
|
if (disabled?.includes(command)) return "That command is already disabled!";
|
||||||
|
|
||||||
await db.disableCommand(this.guild.id, command);
|
await db.disableCommand(this.guild.id, command);
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `The command has been disabled. To re-enable it, just run \`${guildDB.prefix}command enable ${command}\`.`;
|
return `The command has been disabled. To re-enable it, just run \`${guildDB.prefix}command enable ${command}\`.`;
|
||||||
} else if (this.args[0].toLowerCase() === "enable") {
|
} else if (this.args[0].toLowerCase() === "enable") {
|
||||||
if (!disabled?.includes(command)) return "That command isn't disabled!";
|
if (!disabled?.includes(command)) return "That command isn't disabled!";
|
||||||
|
|
||||||
await db.enableCommand(this.guild.id, command);
|
await db.enableCommand(this.guild.id, command);
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `The command \`${command}\` has been re-enabled.`;
|
return `The command \`${command}\` has been re-enabled.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Enables/disables a classic command for a server (use server settings for slash commands)";
|
static description = "Enables/disables a classic command for a server (use server settings for slash commands)";
|
||||||
static aliases = ["cmd"];
|
static aliases = ["cmd"];
|
||||||
static arguments = ["[enable/disable]", "[command]"];
|
static arguments = ["[enable/disable]", "[command]"];
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
static directAllowed = false;
|
static directAllowed = false;
|
||||||
static dbRequired = true;
|
static dbRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CommandCommand;
|
export default CommandCommand;
|
||||||
|
|
|
@ -1,54 +1,54 @@
|
||||||
import paginator from "../../utils/pagination/pagination.js";
|
import paginator from "../../utils/pagination/pagination.js";
|
||||||
import database from "../../utils/database.js";
|
import database from "../../utils/database.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class CountCommand extends Command {
|
class CountCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
if (this.guild && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) {
|
if (this.guild && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I don't have the `Embed Links` permission!";
|
return "I don't have the `Embed Links` permission!";
|
||||||
}
|
}
|
||||||
const counts = await database.getCounts();
|
const counts = await database.getCounts();
|
||||||
const countArray = [];
|
const countArray = [];
|
||||||
for (const entry of Object.entries(counts)) {
|
for (const entry of Object.entries(counts)) {
|
||||||
countArray.push(entry);
|
countArray.push(entry);
|
||||||
}
|
}
|
||||||
const sortedValues = countArray.sort((a, b) => {
|
const sortedValues = countArray.sort((a, b) => {
|
||||||
return b[1] - a[1];
|
return b[1] - a[1];
|
||||||
});
|
});
|
||||||
const countArray2 = [];
|
const countArray2 = [];
|
||||||
for (const [key, value] of sortedValues) {
|
for (const [key, value] of sortedValues) {
|
||||||
countArray2.push(`**${key}**: ${value}`);
|
countArray2.push(`**${key}**: ${value}`);
|
||||||
}
|
}
|
||||||
const embeds = [];
|
const embeds = [];
|
||||||
const groups = countArray2.map((item, index) => {
|
const groups = countArray2.map((item, index) => {
|
||||||
return index % 15 === 0 ? countArray2.slice(index, index + 15) : null;
|
return index % 15 === 0 ? countArray2.slice(index, index + 15) : null;
|
||||||
}).filter((item) => {
|
}).filter((item) => {
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
for (const [i, value] of groups.entries()) {
|
for (const [i, value] of groups.entries()) {
|
||||||
embeds.push({
|
embeds.push({
|
||||||
embeds: [{
|
embeds: [{
|
||||||
title: "Command Usage Counts",
|
title: "Command Usage Counts",
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
footer: {
|
footer: {
|
||||||
text: `Page ${i + 1} of ${groups.length}`
|
text: `Page ${i + 1} of ${groups.length}`
|
||||||
},
|
},
|
||||||
description: value.join("\n"),
|
description: value.join("\n"),
|
||||||
author: {
|
author: {
|
||||||
name: this.author.username,
|
name: this.author.username,
|
||||||
iconURL: this.author.avatarURL()
|
iconURL: this.author.avatarURL()
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
|
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets how many times every command was used";
|
static description = "Gets how many times every command was used";
|
||||||
static arguments = ["{mention/id}"];
|
static arguments = ["{mention/id}"];
|
||||||
static aliases = ["counts"];
|
static aliases = ["counts"];
|
||||||
static dbRequired = true;
|
static dbRequired = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CountCommand;
|
export default CountCommand;
|
|
@ -1,30 +1,30 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class DonateCommand extends Command {
|
class DonateCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
let prefix = "";
|
let prefix = "";
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
try {
|
try {
|
||||||
const patrons = await request("https://projectlounge.pw/patrons", { signal: controller.signal }).then(data => data.body.json());
|
const patrons = await request("https://projectlounge.pw/patrons", { signal: controller.signal }).then(data => data.body.json());
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
prefix = "Thanks to the following patrons for their support:\n";
|
prefix = "Thanks to the following patrons for their support:\n";
|
||||||
for (const patron of patrons) {
|
for (const patron of patrons) {
|
||||||
prefix += `**- ${patron}**\n`;
|
prefix += `**- ${patron}**\n`;
|
||||||
}
|
}
|
||||||
prefix += "\n";
|
prefix += "\n";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
return `${prefix}Like esmBot? Consider supporting the developer on Patreon to help keep it running! https://patreon.com/TheEssem`;
|
return `${prefix}Like esmBot? Consider supporting the developer on Patreon to help keep it running! https://patreon.com/TheEssem`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Learn more about how you can support esmBot's development";
|
static description = "Learn more about how you can support esmBot's development";
|
||||||
static aliases = ["support", "patreon", "patrons"];
|
static aliases = ["support", "patreon", "patrons"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DonateCommand;
|
export default DonateCommand;
|
|
@ -1,33 +1,33 @@
|
||||||
import emojiRegex from "emoji-regex";
|
import emojiRegex from "emoji-regex";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class EmoteCommand extends Command {
|
class EmoteCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const emoji = this.options.emoji ?? this.content;
|
const emoji = this.options.emoji ?? this.content;
|
||||||
if (emoji && emoji.trim() && emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
|
if (emoji && emoji.trim() && emoji.split(" ")[0].match(/^<a?:.+:\d+>$/)) {
|
||||||
return `https://cdn.discordapp.com/emojis/${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$2")}.${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$1") === "a" ? "gif" : "png"}`;
|
return `https://cdn.discordapp.com/emojis/${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$2")}.${emoji.split(" ")[0].replace(/^<(a)?:.+:(\d+)>$/, "$1") === "a" ? "gif" : "png"}`;
|
||||||
} else if (emoji.match(emojiRegex())) {
|
} else if (emoji.match(emojiRegex())) {
|
||||||
const codePoints = [];
|
const codePoints = [];
|
||||||
for (const codePoint of emoji) {
|
for (const codePoint of emoji) {
|
||||||
codePoints.push(codePoint.codePointAt(0).toString(16));
|
codePoints.push(codePoint.codePointAt(0).toString(16));
|
||||||
}
|
}
|
||||||
return `https://twemoji.maxcdn.com/v/latest/72x72/${codePoints.join("-").replace("-fe0f", "")}.png`;
|
return `https://twemoji.maxcdn.com/v/latest/72x72/${codePoints.join("-").replace("-fe0f", "")}.png`;
|
||||||
} else {
|
} else {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "You need to provide a valid emoji to get an image!";
|
return "You need to provide a valid emoji to get an image!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "emoji",
|
name: "emoji",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The emoji you want to get",
|
description: "The emoji you want to get",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Gets a raw emote image";
|
static description = "Gets a raw emote image";
|
||||||
static aliases = ["e", "em", "hugemoji", "hugeemoji", "emoji"];
|
static aliases = ["e", "em", "hugemoji", "hugeemoji", "emoji"];
|
||||||
static arguments = ["[emote]"];
|
static arguments = ["[emote]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EmoteCommand;
|
export default EmoteCommand;
|
||||||
|
|
|
@ -1,49 +1,49 @@
|
||||||
import { clean } from "../../utils/misc.js";
|
import { clean } from "../../utils/misc.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class EvalCommand extends Command {
|
class EvalCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can use eval!";
|
return "Only the bot owner can use eval!";
|
||||||
}
|
}
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const code = this.options.code ?? this.args.join(" ");
|
const code = this.options.code ?? this.args.join(" ");
|
||||||
try {
|
try {
|
||||||
let evaled = eval(code);
|
let evaled = eval(code);
|
||||||
if (evaled?.constructor?.name == "Promise") evaled = await evaled;
|
if (evaled?.constructor?.name == "Promise") evaled = await evaled;
|
||||||
const cleaned = clean(evaled);
|
const cleaned = clean(evaled);
|
||||||
const sendString = `\`\`\`js\n${cleaned}\n\`\`\``;
|
const sendString = `\`\`\`js\n${cleaned}\n\`\`\``;
|
||||||
if (sendString.length >= 2000) {
|
if (sendString.length >= 2000) {
|
||||||
return {
|
return {
|
||||||
content: "The result was too large, so here it is as a file:",
|
content: "The result was too large, so here it is as a file:",
|
||||||
files: [{
|
files: [{
|
||||||
contents: cleaned,
|
contents: cleaned,
|
||||||
name: "result.txt"
|
name: "result.txt"
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return sendString;
|
return sendString;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
let error = err;
|
let error = err;
|
||||||
if (err?.constructor?.name == "Promise") error = await err;
|
if (err?.constructor?.name == "Promise") error = await err;
|
||||||
return `\`ERROR\` \`\`\`xl\n${clean(error)}\n\`\`\``;
|
return `\`ERROR\` \`\`\`xl\n${clean(error)}\n\`\`\``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "code",
|
name: "code",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The code to execute",
|
description: "The code to execute",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Executes JavaScript code";
|
static description = "Executes JavaScript code";
|
||||||
static aliases = ["run"];
|
static aliases = ["run"];
|
||||||
static arguments = ["[code]"];
|
static arguments = ["[code]"];
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default EvalCommand;
|
export default EvalCommand;
|
|
@ -1,48 +1,48 @@
|
||||||
import { clean } from "../../utils/misc.js";
|
import { clean } from "../../utils/misc.js";
|
||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
import { exec as baseExec } from "child_process";
|
import { exec as baseExec } from "child_process";
|
||||||
const exec = util.promisify(baseExec);
|
const exec = util.promisify(baseExec);
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class ExecCommand extends Command {
|
class ExecCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can use exec!";
|
return "Only the bot owner can use exec!";
|
||||||
}
|
}
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const code = this.options.cmd ?? this.args.join(" ");
|
const code = this.options.cmd ?? this.args.join(" ");
|
||||||
try {
|
try {
|
||||||
const execed = await exec(code);
|
const execed = await exec(code);
|
||||||
if (execed.stderr) return `\`ERROR\` \`\`\`xl\n${await clean(execed.stderr)}\n\`\`\``;
|
if (execed.stderr) return `\`ERROR\` \`\`\`xl\n${await clean(execed.stderr)}\n\`\`\``;
|
||||||
const cleaned = await clean(execed.stdout);
|
const cleaned = await clean(execed.stdout);
|
||||||
const sendString = `\`\`\`bash\n${cleaned}\n\`\`\``;
|
const sendString = `\`\`\`bash\n${cleaned}\n\`\`\``;
|
||||||
if (sendString.length >= 2000) {
|
if (sendString.length >= 2000) {
|
||||||
return {
|
return {
|
||||||
text: "The result was too large, so here it is as a file:",
|
text: "The result was too large, so here it is as a file:",
|
||||||
file: cleaned,
|
file: cleaned,
|
||||||
name: "result.txt"
|
name: "result.txt"
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return sendString;
|
return sendString;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return `\`ERROR\` \`\`\`xl\n${await clean(err)}\n\`\`\``;
|
return `\`ERROR\` \`\`\`xl\n${await clean(err)}\n\`\`\``;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "cmd",
|
name: "cmd",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The command to execute",
|
description: "The command to execute",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Executes a shell command";
|
static description = "Executes a shell command";
|
||||||
static aliases = ["runcmd"];
|
static aliases = ["runcmd"];
|
||||||
static arguments = ["[command]"];
|
static arguments = ["[command]"];
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ExecCommand;
|
export default ExecCommand;
|
|
@ -1,120 +1,120 @@
|
||||||
import { Constants } from "oceanic.js";
|
import { Constants } from "oceanic.js";
|
||||||
import database from "../../utils/database.js";
|
import database from "../../utils/database.js";
|
||||||
import * as collections from "../../utils/collections.js";
|
import * as collections from "../../utils/collections.js";
|
||||||
import { random } from "../../utils/misc.js";
|
import { random } from "../../utils/misc.js";
|
||||||
import paginator from "../../utils/pagination/pagination.js";
|
import paginator from "../../utils/pagination/pagination.js";
|
||||||
import * as help from "../../utils/help.js";
|
import * as help from "../../utils/help.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
const tips = ["You can change the bot's prefix using the prefix command.", "Image commands also work with images previously posted in that channel.", "You can use the tags commands to save things for later use.", "You can visit https://esmbot.net/help.html for a web version of this command list.", "You can view a command's aliases by putting the command name after the help command (e.g. help image).", "Parameters wrapped in [] are required, while parameters wrapped in {} are optional.", "esmBot is hosted and paid for completely out-of-pocket by the main developer. If you want to support development, please consider donating! https://patreon.com/TheEssem"];
|
const tips = ["You can change the bot's prefix using the prefix command.", "Image commands also work with images previously posted in that channel.", "You can use the tags commands to save things for later use.", "You can visit https://esmbot.net/help.html for a web version of this command list.", "You can view a command's aliases by putting the command name after the help command (e.g. help image).", "Parameters wrapped in [] are required, while parameters wrapped in {} are optional.", "esmBot is hosted and paid for completely out-of-pocket by the main developer. If you want to support development, please consider donating! https://patreon.com/TheEssem"];
|
||||||
|
|
||||||
class HelpCommand extends Command {
|
class HelpCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
let prefix;
|
let prefix;
|
||||||
if (this.guild && database) {
|
if (this.guild && database) {
|
||||||
prefix = (await database.getGuild(this.guild.id)).prefix;
|
prefix = (await database.getGuild(this.guild.id)).prefix;
|
||||||
} else {
|
} else {
|
||||||
prefix = process.env.PREFIX;
|
prefix = process.env.PREFIX;
|
||||||
}
|
}
|
||||||
if (this.args.length !== 0 && (collections.commands.has(this.args[0].toLowerCase()) || collections.aliases.has(this.args[0].toLowerCase()))) {
|
if (this.args.length !== 0 && (collections.commands.has(this.args[0].toLowerCase()) || collections.aliases.has(this.args[0].toLowerCase()))) {
|
||||||
const command = collections.aliases.get(this.args[0].toLowerCase()) ?? this.args[0].toLowerCase();
|
const command = collections.aliases.get(this.args[0].toLowerCase()) ?? this.args[0].toLowerCase();
|
||||||
const info = collections.info.get(command);
|
const info = collections.info.get(command);
|
||||||
const embed = {
|
const embed = {
|
||||||
embeds: [{
|
embeds: [{
|
||||||
author: {
|
author: {
|
||||||
name: "esmBot Help",
|
name: "esmBot Help",
|
||||||
iconURL: this.client.user.avatarURL()
|
iconURL: this.client.user.avatarURL()
|
||||||
},
|
},
|
||||||
title: `${this.guild ? prefix : ""}${command}`,
|
title: `${this.guild ? prefix : ""}${command}`,
|
||||||
url: "https://esmbot.net/help.html",
|
url: "https://esmbot.net/help.html",
|
||||||
description: command === "tags" ? "The main tags command. Check the help page for more info: https://esmbot.net/help.html" : info.description,
|
description: command === "tags" ? "The main tags command. Check the help page for more info: https://esmbot.net/help.html" : info.description,
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
fields: [{
|
fields: [{
|
||||||
name: "Aliases",
|
name: "Aliases",
|
||||||
value: info.aliases.length !== 0 ? info.aliases.join(", ") : "None"
|
value: info.aliases.length !== 0 ? info.aliases.join(", ") : "None"
|
||||||
}, {
|
}, {
|
||||||
name: "Parameters",
|
name: "Parameters",
|
||||||
value: command === "tags" ? "[name]" : (info.params ? (info.params.length !== 0 ? info.params.join(" ") : "None") : "None"),
|
value: command === "tags" ? "[name]" : (info.params ? (info.params.length !== 0 ? info.params.join(" ") : "None") : "None"),
|
||||||
inline: true
|
inline: true
|
||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
if (database) {
|
if (database) {
|
||||||
embed.embeds[0].fields.push({
|
embed.embeds[0].fields.push({
|
||||||
name: "Times used",
|
name: "Times used",
|
||||||
value: (await database.getCounts())[command],
|
value: (await database.getCounts())[command],
|
||||||
inline: true
|
inline: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (info.flags.length !== 0) {
|
if (info.flags.length !== 0) {
|
||||||
const flagInfo = [];
|
const flagInfo = [];
|
||||||
for (const flag of info.flags) {
|
for (const flag of info.flags) {
|
||||||
if (flag.type === 1) continue;
|
if (flag.type === 1) continue;
|
||||||
flagInfo.push(`\`--${flag.name}${flag.type ? `=[${Constants.ApplicationCommandOptionTypes[flag.type]}]` : ""}\` - ${flag.description}`);
|
flagInfo.push(`\`--${flag.name}${flag.type ? `=[${Constants.ApplicationCommandOptionTypes[flag.type]}]` : ""}\` - ${flag.description}`);
|
||||||
}
|
}
|
||||||
if (flagInfo.length !== 0) {
|
if (flagInfo.length !== 0) {
|
||||||
embed.embeds[0].fields.push({
|
embed.embeds[0].fields.push({
|
||||||
"name": "Flags",
|
"name": "Flags",
|
||||||
"value": flagInfo.join("\n")
|
"value": flagInfo.join("\n")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return embed;
|
return embed;
|
||||||
} else {
|
} else {
|
||||||
if (this.guild && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) {
|
if (this.guild && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "I don't have the `Embed Links` permission!";
|
return "I don't have the `Embed Links` permission!";
|
||||||
}
|
}
|
||||||
const pages = [];
|
const pages = [];
|
||||||
if (help.categories === help.categoryTemplate && !help.generated) help.generateList();
|
if (help.categories === help.categoryTemplate && !help.generated) help.generateList();
|
||||||
for (const category of Object.keys(help.categories)) {
|
for (const category of Object.keys(help.categories)) {
|
||||||
const splitPages = help.categories[category].map((item, index) => {
|
const splitPages = help.categories[category].map((item, index) => {
|
||||||
return index % 15 === 0 ? help.categories[category].slice(index, index + 15) : null;
|
return index % 15 === 0 ? help.categories[category].slice(index, index + 15) : null;
|
||||||
}).filter((item) => {
|
}).filter((item) => {
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
const categoryStringArray = category.split("-");
|
const categoryStringArray = category.split("-");
|
||||||
for (const index of categoryStringArray.keys()) {
|
for (const index of categoryStringArray.keys()) {
|
||||||
categoryStringArray[index] = categoryStringArray[index].charAt(0).toUpperCase() + categoryStringArray[index].slice(1);
|
categoryStringArray[index] = categoryStringArray[index].charAt(0).toUpperCase() + categoryStringArray[index].slice(1);
|
||||||
}
|
}
|
||||||
for (const page of splitPages) {
|
for (const page of splitPages) {
|
||||||
pages.push({
|
pages.push({
|
||||||
title: categoryStringArray.join(" "),
|
title: categoryStringArray.join(" "),
|
||||||
page: page
|
page: page
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const embeds = [];
|
const embeds = [];
|
||||||
for (const [i, value] of pages.entries()) {
|
for (const [i, value] of pages.entries()) {
|
||||||
embeds.push({
|
embeds.push({
|
||||||
embeds: [{
|
embeds: [{
|
||||||
author: {
|
author: {
|
||||||
name: "esmBot Help",
|
name: "esmBot Help",
|
||||||
iconURL: this.client.user.avatarURL()
|
iconURL: this.client.user.avatarURL()
|
||||||
},
|
},
|
||||||
title: value.title,
|
title: value.title,
|
||||||
description: value.page.join("\n"),
|
description: value.page.join("\n"),
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
footer: {
|
footer: {
|
||||||
text: `Page ${i + 1} of ${pages.length}`
|
text: `Page ${i + 1} of ${pages.length}`
|
||||||
},
|
},
|
||||||
fields: [{
|
fields: [{
|
||||||
name: "Prefix",
|
name: "Prefix",
|
||||||
value: prefix
|
value: prefix
|
||||||
}, {
|
}, {
|
||||||
name: "Tip",
|
name: "Tip",
|
||||||
value: random(tips)
|
value: random(tips)
|
||||||
}]
|
}]
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, author: this.author }, embeds);
|
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, author: this.author }, embeds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a list of commands";
|
static description = "Gets a list of commands";
|
||||||
static aliases = ["commands"];
|
static aliases = ["commands"];
|
||||||
static arguments = ["{command}"];
|
static arguments = ["{command}"];
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HelpCommand;
|
export default HelpCommand;
|
||||||
|
|
|
@ -1,54 +1,54 @@
|
||||||
import paginator from "../../utils/pagination/pagination.js";
|
import paginator from "../../utils/pagination/pagination.js";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
const { searx } = JSON.parse(readFileSync(new URL("../../config/servers.json", import.meta.url)));
|
const { searx } = JSON.parse(readFileSync(new URL("../../config/servers.json", import.meta.url)));
|
||||||
import { random } from "../../utils/misc.js";
|
import { random } from "../../utils/misc.js";
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class ImageSearchCommand extends Command {
|
class ImageSearchCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (this.channel && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) return "I don't have the `Embed Links` permission!";
|
if (this.channel && !this.channel.permissionsOf(this.client.user.id.toString()).has("EMBED_LINKS")) return "I don't have the `Embed Links` permission!";
|
||||||
const query = this.options.query ?? this.args.join(" ");
|
const query = this.options.query ?? this.args.join(" ");
|
||||||
if (!query || !query.trim()) return "You need to provide something to search for!";
|
if (!query || !query.trim()) return "You need to provide something to search for!";
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const embeds = [];
|
const embeds = [];
|
||||||
const rawImages = await request(`${random(searx)}/search?format=json&safesearch=2&categories=images&q=!goi%20!ddi%20${encodeURIComponent(query)}`).then(res => res.body.json());
|
const rawImages = await request(`${random(searx)}/search?format=json&safesearch=2&categories=images&q=!goi%20!ddi%20${encodeURIComponent(query)}`).then(res => res.body.json());
|
||||||
if (rawImages.results.length === 0) return "I couldn't find any results!";
|
if (rawImages.results.length === 0) return "I couldn't find any results!";
|
||||||
const images = rawImages.results.filter((val) => !val.img_src.startsWith("data:"));
|
const images = rawImages.results.filter((val) => !val.img_src.startsWith("data:"));
|
||||||
for (const [i, value] of images.entries()) {
|
for (const [i, value] of images.entries()) {
|
||||||
embeds.push({
|
embeds.push({
|
||||||
embeds: [{
|
embeds: [{
|
||||||
title: "Search Results",
|
title: "Search Results",
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
footer: {
|
footer: {
|
||||||
text: `Page ${i + 1} of ${images.length}`
|
text: `Page ${i + 1} of ${images.length}`
|
||||||
},
|
},
|
||||||
description: value.title,
|
description: value.title,
|
||||||
image: {
|
image: {
|
||||||
url: encodeURI(value.img_src)
|
url: encodeURI(value.img_src)
|
||||||
},
|
},
|
||||||
author: {
|
author: {
|
||||||
name: this.author.username,
|
name: this.author.username,
|
||||||
iconURL: this.author.avatarURL()
|
iconURL: this.author.avatarURL()
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
|
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, embeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "query",
|
name: "query",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The query you want to search for",
|
description: "The query you want to search for",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Searches for images across the web";
|
static description = "Searches for images across the web";
|
||||||
static aliases = ["im", "photo", "img"];
|
static aliases = ["im", "photo", "img"];
|
||||||
static arguments = ["[query]"];
|
static arguments = ["[query]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImageSearchCommand;
|
export default ImageSearchCommand;
|
|
@ -1,32 +1,32 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { reloadImageConnections } from "../../utils/image.js";
|
import { reloadImageConnections } from "../../utils/image.js";
|
||||||
|
|
||||||
class ImageReloadCommand extends Command {
|
class ImageReloadCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can reload the image servers!";
|
return "Only the bot owner can reload the image servers!";
|
||||||
}
|
}
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const length = await reloadImageConnections();
|
const length = await reloadImageConnections();
|
||||||
if (!length) {
|
if (!length) {
|
||||||
if (process.env.PM2_USAGE) {
|
if (process.env.PM2_USAGE) {
|
||||||
process.send({
|
process.send({
|
||||||
type: "process:msg",
|
type: "process:msg",
|
||||||
data: {
|
data: {
|
||||||
type: "imagereload"
|
type: "imagereload"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return `Successfully connected to ${length} image server(s).`;
|
return `Successfully connected to ${length} image server(s).`;
|
||||||
} else {
|
} else {
|
||||||
return "I couldn't connect to any image servers!";
|
return "I couldn't connect to any image servers!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Attempts to reconnect to all available image processing servers";
|
static description = "Attempts to reconnect to all available image processing servers";
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImageReloadCommand;
|
export default ImageReloadCommand;
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { connections } from "../../utils/image.js";
|
import { connections } from "../../utils/image.js";
|
||||||
|
|
||||||
class ImageStatsCommand extends Command {
|
class ImageStatsCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const embed = {
|
const embed = {
|
||||||
embeds: [{
|
embeds: [{
|
||||||
"author": {
|
"author": {
|
||||||
"name": "esmBot Image Statistics",
|
"name": "esmBot Image Statistics",
|
||||||
"iconURL": this.client.user.avatarURL()
|
"iconURL": this.client.user.avatarURL()
|
||||||
},
|
},
|
||||||
"color": 16711680,
|
"color": 16711680,
|
||||||
"description": `The bot is currently connected to ${connections.size} image server(s).`,
|
"description": `The bot is currently connected to ${connections.size} image server(s).`,
|
||||||
"fields": []
|
"fields": []
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (const connection of connections.values()) {
|
for (const connection of connections.values()) {
|
||||||
const count = await connection.getCount();
|
const count = await connection.getCount();
|
||||||
if (!count) continue;
|
if (!count) continue;
|
||||||
embed.embeds[0].fields.push({
|
embed.embeds[0].fields.push({
|
||||||
name: `Server ${i++}`,
|
name: `Server ${i++}`,
|
||||||
value: `Running Jobs: ${count}`
|
value: `Running Jobs: ${count}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return embed;
|
return embed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets some statistics about the image servers";
|
static description = "Gets some statistics about the image servers";
|
||||||
static aliases = ["imgstat", "imstats", "imgstats", "imstat"];
|
static aliases = ["imgstat", "imstats", "imgstats", "imstat"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImageStatsCommand;
|
export default ImageStatsCommand;
|
||||||
|
|
|
@ -1,57 +1,57 @@
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
|
const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { getServers } from "../../utils/misc.js";
|
import { getServers } from "../../utils/misc.js";
|
||||||
|
|
||||||
class InfoCommand extends Command {
|
class InfoCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
|
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
|
||||||
if (!owner) owner = await this.client.rest.users.get(process.env.OWNER.split(",")[0]);
|
if (!owner) owner = await this.client.rest.users.get(process.env.OWNER.split(",")[0]);
|
||||||
const servers = await getServers(this.client);
|
const servers = await getServers(this.client);
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
return {
|
return {
|
||||||
embeds: [{
|
embeds: [{
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
author: {
|
author: {
|
||||||
name: "esmBot Info/Credits",
|
name: "esmBot Info/Credits",
|
||||||
iconURL: this.client.user.avatarURL()
|
iconURL: this.client.user.avatarURL()
|
||||||
},
|
},
|
||||||
description: `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
|
description: `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
|
||||||
fields: [{
|
fields: [{
|
||||||
name: "ℹ️ Version:",
|
name: "ℹ️ Version:",
|
||||||
value: `v${version}${process.env.NODE_ENV === "development" ? `-dev (${process.env.GIT_REV})` : ""}`
|
value: `v${version}${process.env.NODE_ENV === "development" ? `-dev (${process.env.GIT_REV})` : ""}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "📝 Credits:",
|
name: "📝 Credits:",
|
||||||
value: "Bot by **[Essem](https://essem.space)** and **[various contributors](https://github.com/esmBot/esmBot/graphs/contributors)**\nLogo by **[MintBurrow](https://twitter.com/MintBurrow)**"
|
value: "Bot by **[Essem](https://essem.space)** and **[various contributors](https://github.com/esmBot/esmBot/graphs/contributors)**\nLogo by **[MintBurrow](https://twitter.com/MintBurrow)**"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💬 Total Servers:",
|
name: "💬 Total Servers:",
|
||||||
value: servers ? servers : `${this.client.guilds.size} (for this process only)`
|
value: servers ? servers : `${this.client.guilds.size} (for this process only)`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "✅ Official Server:",
|
name: "✅ Official Server:",
|
||||||
value: "[Click here!](https://esmbot.net/support)"
|
value: "[Click here!](https://esmbot.net/support)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "💻 Source Code:",
|
name: "💻 Source Code:",
|
||||||
value: "[Click here!](https://github.com/esmBot/esmBot)"
|
value: "[Click here!](https://github.com/esmBot/esmBot)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🛡️ Privacy Policy:",
|
name: "🛡️ Privacy Policy:",
|
||||||
value: "[Click here!](https://esmbot.net/privacy.html)"
|
value: "[Click here!](https://esmbot.net/privacy.html)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "🐘 Mastodon:",
|
name: "🐘 Mastodon:",
|
||||||
value: "[Click here!](https://wetdry.world/@esmBot)"
|
value: "[Click here!](https://wetdry.world/@esmBot)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets some info and credits about me";
|
static description = "Gets some info and credits about me";
|
||||||
static aliases = ["botinfo", "credits"];
|
static aliases = ["botinfo", "credits"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InfoCommand;
|
export default InfoCommand;
|
|
@ -1,32 +1,32 @@
|
||||||
import urlCheck from "../../utils/urlcheck.js";
|
import urlCheck from "../../utils/urlcheck.js";
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class LengthenCommand extends Command {
|
class LengthenCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const input = this.options.url ?? this.args.join(" ");
|
const input = this.options.url ?? this.args.join(" ");
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!input || !input.trim() || !urlCheck(input)) return "You need to provide a short URL to lengthen!";
|
if (!input || !input.trim() || !urlCheck(input)) return "You need to provide a short URL to lengthen!";
|
||||||
if (urlCheck(input)) {
|
if (urlCheck(input)) {
|
||||||
const url = await request(encodeURI(input), { method: "HEAD" });
|
const url = await request(encodeURI(input), { method: "HEAD" });
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return url.headers.location || input;
|
return url.headers.location || input;
|
||||||
} else {
|
} else {
|
||||||
return "That isn't a URL!";
|
return "That isn't a URL!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "url",
|
name: "url",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The URL you want to lengthen",
|
description: "The URL you want to lengthen",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Lengthens a short URL";
|
static description = "Lengthens a short URL";
|
||||||
static aliases = ["longurl", "lengthenurl", "longuri", "lengthenuri", "unshorten"];
|
static aliases = ["longurl", "lengthenurl", "longuri", "lengthenuri", "unshorten"];
|
||||||
static arguments = ["[url]"];
|
static arguments = ["[url]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LengthenCommand;
|
export default LengthenCommand;
|
|
@ -1,27 +1,27 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class PingCommand extends Command {
|
class PingCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
if (this.type === "classic") {
|
if (this.type === "classic") {
|
||||||
const pingMessage = await this.client.rest.channels.createMessage(this.message.channelID, Object.assign({
|
const pingMessage = await this.client.rest.channels.createMessage(this.message.channelID, Object.assign({
|
||||||
content: "🏓 Ping?"
|
content: "🏓 Ping?"
|
||||||
}, this.reference));
|
}, this.reference));
|
||||||
await pingMessage.edit({
|
await pingMessage.edit({
|
||||||
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.message.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.message.guildID]).latency)}ms` : ""}\n\`\`\``
|
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - this.message.timestamp}ms${this.message.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.message.guildID]).latency)}ms` : ""}\n\`\`\``
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.interaction.createMessage({
|
await this.interaction.createMessage({
|
||||||
content: "🏓 Ping?"
|
content: "🏓 Ping?"
|
||||||
});
|
});
|
||||||
const pingMessage = await this.interaction.getOriginal();
|
const pingMessage = await this.interaction.getOriginal();
|
||||||
await this.interaction.editOriginal({
|
await this.interaction.editOriginal({
|
||||||
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - Math.floor((this.interaction.id / 4194304) + 1420070400000)}ms${this.interaction.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.interaction.guildID]).latency)}ms` : ""}\n\`\`\``
|
content: `🏓 Pong!\n\`\`\`\nLatency: ${pingMessage.timestamp - Math.floor((this.interaction.id / 4194304) + 1420070400000)}ms${this.interaction.guildID ? `\nShard Latency: ${Math.round(this.client.shards.get(this.client.guildShardMap[this.interaction.guildID]).latency)}ms` : ""}\n\`\`\``
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Pings Discord's servers";
|
static description = "Pings Discord's servers";
|
||||||
static aliases = ["pong"];
|
static aliases = ["pong"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PingCommand;
|
export default PingCommand;
|
|
@ -1,30 +1,30 @@
|
||||||
import database from "../../utils/database.js";
|
import database from "../../utils/database.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class PrefixCommand extends Command {
|
class PrefixCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
if (!this.guild) return `The current prefix is \`${process.env.PREFIX}\`.`;
|
if (!this.guild) return `The current prefix is \`${process.env.PREFIX}\`.`;
|
||||||
const guild = await database.getGuild(this.guild.id);
|
const guild = await database.getGuild(this.guild.id);
|
||||||
if (this.args.length !== 0) {
|
if (this.args.length !== 0) {
|
||||||
if (!database) {
|
if (!database) {
|
||||||
return "Setting a per-guild prefix is not possible on a stateless instance of esmBot!";
|
return "Setting a per-guild prefix is not possible on a stateless instance of esmBot!";
|
||||||
}
|
}
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) {
|
if (!this.member.permissions.has("ADMINISTRATOR") && !owners.includes(this.member.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "You need to be an administrator to change the bot prefix!";
|
return "You need to be an administrator to change the bot prefix!";
|
||||||
}
|
}
|
||||||
await database.setPrefix(this.args[0], this.guild);
|
await database.setPrefix(this.args[0], this.guild);
|
||||||
return `The prefix has been changed to ${this.args[0]}.`;
|
return `The prefix has been changed to ${this.args[0]}.`;
|
||||||
} else {
|
} else {
|
||||||
return `The current prefix is \`${guild.prefix}\`.`;
|
return `The current prefix is \`${guild.prefix}\`.`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Checks/changes the server prefix";
|
static description = "Checks/changes the server prefix";
|
||||||
static aliases = ["setprefix", "changeprefix", "checkprefix"];
|
static aliases = ["setprefix", "changeprefix", "checkprefix"];
|
||||||
static arguments = ["{prefix}"];
|
static arguments = ["{prefix}"];
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PrefixCommand;
|
export default PrefixCommand;
|
|
@ -1,40 +1,40 @@
|
||||||
import qrcode from "qrcode";
|
import qrcode from "qrcode";
|
||||||
import { PassThrough } from "stream";
|
import { PassThrough } from "stream";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class QrCreateCommand extends Command {
|
class QrCreateCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
if (this.args.length === 0) {
|
if (this.args.length === 0) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "You need to provide some text to generate a QR code!";
|
return "You need to provide some text to generate a QR code!";
|
||||||
}
|
}
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const writable = new PassThrough();
|
const writable = new PassThrough();
|
||||||
qrcode.toFileStream(writable, this.content, { margin: 1 });
|
qrcode.toFileStream(writable, this.content, { margin: 1 });
|
||||||
const file = await this.streamToBuf(writable);
|
const file = await this.streamToBuf(writable);
|
||||||
return {
|
return {
|
||||||
contents: file,
|
contents: file,
|
||||||
name: "qr.png"
|
name: "qr.png"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
streamToBuf(stream) {
|
streamToBuf(stream) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const chunks = [];
|
const chunks = [];
|
||||||
stream.on("data", (chunk) => {
|
stream.on("data", (chunk) => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
});
|
});
|
||||||
stream.once("error", (error) => {
|
stream.once("error", (error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
stream.once("end", () => {
|
stream.once("end", () => {
|
||||||
resolve(Buffer.concat(chunks));
|
resolve(Buffer.concat(chunks));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Generates a QR code";
|
static description = "Generates a QR code";
|
||||||
static arguments = ["[text]"];
|
static arguments = ["[text]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default QrCreateCommand;
|
export default QrCreateCommand;
|
|
@ -1,34 +1,34 @@
|
||||||
import jsqr from "jsqr";
|
import jsqr from "jsqr";
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import { clean } from "../../utils/misc.js";
|
import { clean } from "../../utils/misc.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import imageDetect from "../../utils/imagedetect.js";
|
import imageDetect from "../../utils/imagedetect.js";
|
||||||
|
|
||||||
class QrReadCommand extends Command {
|
class QrReadCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
|
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (image === undefined) return "You need to provide an image/GIF with a QR code to read!";
|
if (image === undefined) return "You need to provide an image/GIF with a QR code to read!";
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const data = Buffer.from(await (await request(image.path)).body.arrayBuffer());
|
const data = Buffer.from(await (await request(image.path)).body.arrayBuffer());
|
||||||
const rawData = await sharp(data).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
const rawData = await sharp(data).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
||||||
const qrBuffer = jsqr(rawData.data, rawData.info.width, rawData.info.height);
|
const qrBuffer = jsqr(rawData.data, rawData.info.width, rawData.info.height);
|
||||||
if (!qrBuffer) return "I couldn't find a QR code!";
|
if (!qrBuffer) return "I couldn't find a QR code!";
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `\`\`\`\n${await clean(qrBuffer.data)}\n\`\`\``;
|
return `\`\`\`\n${await clean(qrBuffer.data)}\n\`\`\``;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Reads a QR code";
|
static description = "Reads a QR code";
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "image",
|
name: "image",
|
||||||
type: 11,
|
type: 11,
|
||||||
description: "An image/GIF attachment"
|
description: "An image/GIF attachment"
|
||||||
}, {
|
}, {
|
||||||
name: "link",
|
name: "link",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "An image/GIF URL"
|
description: "An image/GIF URL"
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default QrReadCommand;
|
export default QrReadCommand;
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import imageDetect from "../../utils/imagedetect.js";
|
import imageDetect from "../../utils/imagedetect.js";
|
||||||
|
|
||||||
class RawCommand extends Command {
|
class RawCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
|
const image = await imageDetect(this.client, this.message, this.interaction, this.options);
|
||||||
if (image === undefined) {
|
if (image === undefined) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "You need to provide an image/GIF to get a raw URL!";
|
return "You need to provide an image/GIF to get a raw URL!";
|
||||||
}
|
}
|
||||||
return image.path;
|
return image.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a direct image URL (useful for saving GIFs from sites like Tenor)";
|
static description = "Gets a direct image URL (useful for saving GIFs from sites like Tenor)";
|
||||||
static aliases = ["giflink", "imglink", "getimg", "rawgif", "rawimg"];
|
static aliases = ["giflink", "imglink", "getimg", "rawgif", "rawimg"];
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "image",
|
name: "image",
|
||||||
type: 11,
|
type: 11,
|
||||||
description: "An image/GIF attachment"
|
description: "An image/GIF attachment"
|
||||||
}, {
|
}, {
|
||||||
name: "link",
|
name: "link",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "An image/GIF URL"
|
description: "An image/GIF URL"
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RawCommand;
|
export default RawCommand;
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { load } from "../../utils/handler.js";
|
import { load } from "../../utils/handler.js";
|
||||||
import { paths } from "../../utils/collections.js";
|
import { paths } from "../../utils/collections.js";
|
||||||
|
|
||||||
class ReloadCommand extends Command {
|
class ReloadCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) return "Only the bot owner can reload commands!";
|
if (!owners.includes(this.sender)) return "Only the bot owner can reload commands!";
|
||||||
const commandName = this.options.cmd ?? this.args.join(" ");
|
const commandName = this.options.cmd ?? this.args.join(" ");
|
||||||
if (!commandName || !commandName.trim()) return "You need to provide a command to reload!";
|
if (!commandName || !commandName.trim()) return "You need to provide a command to reload!";
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const path = paths.get(commandName);
|
const path = paths.get(commandName);
|
||||||
if (!path) return "I couldn't find that command!";
|
if (!path) return "I couldn't find that command!";
|
||||||
const result = await load(this.client, path, true);
|
const result = await load(this.matrixClient, path, true);
|
||||||
if (result !== commandName) return "I couldn't reload that command!";
|
if (result !== commandName) return "I couldn't reload that command!";
|
||||||
if (process.env.PM2_USAGE) {
|
if (process.env.PM2_USAGE) {
|
||||||
process.send({
|
process.send({
|
||||||
type: "process:msg",
|
type: "process:msg",
|
||||||
data: {
|
data: {
|
||||||
type: "reload",
|
type: "reload",
|
||||||
message: commandName
|
message: commandName
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return `The command \`${commandName}\` has been reloaded.`;
|
return `The command \`${commandName}\` has been reloaded.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static flags = [{
|
static flags = [{
|
||||||
name: "cmd",
|
name: "cmd",
|
||||||
type: 3,
|
type: 3,
|
||||||
description: "The command to reload",
|
description: "The command to reload",
|
||||||
required: true
|
required: true
|
||||||
}];
|
}];
|
||||||
|
|
||||||
static description = "Reloads a command";
|
static description = "Reloads a command";
|
||||||
static arguments = ["[command]"];
|
static arguments = ["[command]"];
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ReloadCommand;
|
export default ReloadCommand;
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class RestartCommand extends Command {
|
class RestartCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can restart me!";
|
return "Only the bot owner can restart me!";
|
||||||
}
|
}
|
||||||
await this.channel.createMessage(Object.assign({
|
await this.channel.createMessage(Object.assign({
|
||||||
content: "esmBot is restarting."
|
content: "esmBot is restarting."
|
||||||
}, this.reference));
|
}, this.reference));
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Restarts me";
|
static description = "Restarts me";
|
||||||
static aliases = ["reboot"];
|
static aliases = ["reboot"];
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RestartCommand;
|
export default RestartCommand;
|
|
@ -1,20 +1,20 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class SnowflakeCommand extends Command {
|
class SnowflakeCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!this.args[0]) return "You need to provide a snowflake ID!";
|
if (!this.args[0]) return "You need to provide a snowflake ID!";
|
||||||
if (!this.args[0].match(/^<?[@#]?[&!]?\d+>?$/) && this.args[0] < 21154535154122752n) return "That's not a valid snowflake!";
|
if (!this.args[0].match(/^<?[@#]?[&!]?\d+>?$/) && this.args[0] < 21154535154122752n) return "That's not a valid snowflake!";
|
||||||
const id = Math.floor(((this.args[0].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "") / 4194304) + 1420070400000) / 1000);
|
const id = Math.floor(((this.args[0].replaceAll("@", "").replaceAll("#", "").replaceAll("!", "").replaceAll("&", "").replaceAll("<", "").replaceAll(">", "") / 4194304) + 1420070400000) / 1000);
|
||||||
if (isNaN(id)) return "That's not a valid snowflake!";
|
if (isNaN(id)) return "That's not a valid snowflake!";
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `<t:${id}:F>`;
|
return `<t:${id}:F>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Converts a Discord snowflake id into a timestamp";
|
static description = "Converts a Discord snowflake id into a timestamp";
|
||||||
static aliases = ["timestamp", "snowstamp", "snow"];
|
static aliases = ["timestamp", "snowstamp", "snow"];
|
||||||
static arguments = ["[id]"];
|
static arguments = ["[id]"];
|
||||||
static slashAllowed = false;
|
static slashAllowed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SnowflakeCommand;
|
export default SnowflakeCommand;
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { reload } from "../../utils/soundplayer.js";
|
import { reload } from "../../utils/soundplayer.js";
|
||||||
|
|
||||||
class SoundReloadCommand extends Command {
|
class SoundReloadCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const owners = process.env.OWNER.split(",");
|
const owners = process.env.OWNER.split(",");
|
||||||
if (!owners.includes(this.author.id)) {
|
if (!owners.includes(this.author.id)) {
|
||||||
this.success = false;
|
this.success = false;
|
||||||
return "Only the bot owner can reload Lavalink!";
|
return "Only the bot owner can reload Lavalink!";
|
||||||
}
|
}
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const length = await reload();
|
const length = await reload();
|
||||||
if (process.env.PM2_USAGE) {
|
if (process.env.PM2_USAGE) {
|
||||||
process.send({
|
process.send({
|
||||||
type: "process:msg",
|
type: "process:msg",
|
||||||
data: {
|
data: {
|
||||||
type: "soundreload"
|
type: "soundreload"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (length) {
|
if (length) {
|
||||||
return `Successfully connected to ${length} Lavalink node(s).`;
|
return `Successfully connected to ${length} Lavalink node(s).`;
|
||||||
} else {
|
} else {
|
||||||
return "I couldn't connect to any Lavalink nodes!";
|
return "I couldn't connect to any Lavalink nodes!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Attempts to reconnect to all available Lavalink nodes";
|
static description = "Attempts to reconnect to all available Lavalink nodes";
|
||||||
static aliases = ["lava", "lavalink", "lavaconnect", "soundconnect"];
|
static aliases = ["lava", "lavalink", "lavaconnect", "soundconnect"];
|
||||||
static adminOnly = true;
|
static adminOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SoundReloadCommand;
|
export default SoundReloadCommand;
|
|
@ -1,90 +1,90 @@
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
|
const { version } = JSON.parse(readFileSync(new URL("../../package.json", import.meta.url)));
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import { VERSION } from "oceanic.js";
|
import { VERSION } from "oceanic.js";
|
||||||
import pm2 from "pm2";
|
import pm2 from "pm2";
|
||||||
import { getServers } from "../../utils/misc.js";
|
import { getServers } from "../../utils/misc.js";
|
||||||
|
|
||||||
class StatsCommand extends Command {
|
class StatsCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const uptime = process.uptime() * 1000;
|
const uptime = process.uptime() * 1000;
|
||||||
const connUptime = this.client.uptime;
|
const connUptime = this.client.uptime;
|
||||||
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
|
let owner = this.client.users.get(process.env.OWNER.split(",")[0]);
|
||||||
if (!owner) owner = await this.client.rest.users.get(process.env.OWNER.split(",")[0]);
|
if (!owner) owner = await this.client.rest.users.get(process.env.OWNER.split(",")[0]);
|
||||||
const servers = await getServers(this.client);
|
const servers = await getServers(this.client);
|
||||||
const processMem = `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`;
|
const processMem = `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`;
|
||||||
return {
|
return {
|
||||||
embeds: [{
|
embeds: [{
|
||||||
"author": {
|
"author": {
|
||||||
"name": "esmBot Statistics",
|
"name": "esmBot Statistics",
|
||||||
"iconURL": this.client.user.avatarURL()
|
"iconURL": this.client.user.avatarURL()
|
||||||
},
|
},
|
||||||
"description": `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
|
"description": `This instance is managed by **${owner.username}#${owner.discriminator}**.`,
|
||||||
"color": 16711680,
|
"color": 16711680,
|
||||||
"fields": [{
|
"fields": [{
|
||||||
"name": "Version",
|
"name": "Version",
|
||||||
"value": `v${version}${process.env.NODE_ENV === "development" ? `-dev (${process.env.GIT_REV})` : ""}`
|
"value": `v${version}${process.env.NODE_ENV === "development" ? `-dev (${process.env.GIT_REV})` : ""}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Process Memory Usage",
|
"name": "Process Memory Usage",
|
||||||
"value": processMem,
|
"value": processMem,
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Total Memory Usage",
|
"name": "Total Memory Usage",
|
||||||
"value": process.env.PM2_USAGE ? `${((await this.list()).reduce((prev, cur) => prev + cur.monit.memory, 0) / 1024 / 1024).toFixed(2)} MB` : processMem,
|
"value": process.env.PM2_USAGE ? `${((await this.list()).reduce((prev, cur) => prev + cur.monit.memory, 0) / 1024 / 1024).toFixed(2)} MB` : processMem,
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Bot Uptime",
|
"name": "Bot Uptime",
|
||||||
"value": `${Math.trunc(uptime / 86400000)} days, ${Math.trunc(uptime / 3600000) % 24} hrs, ${Math.trunc(uptime / 60000) % 60} mins, ${Math.trunc(uptime / 1000) % 60} secs`
|
"value": `${Math.trunc(uptime / 86400000)} days, ${Math.trunc(uptime / 3600000) % 24} hrs, ${Math.trunc(uptime / 60000) % 60} mins, ${Math.trunc(uptime / 1000) % 60} secs`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Connection Uptime",
|
"name": "Connection Uptime",
|
||||||
"value": `${Math.trunc(connUptime / 86400000)} days, ${Math.trunc(connUptime / 3600000) % 24} hrs, ${Math.trunc(connUptime / 60000) % 60} mins, ${Math.trunc(connUptime / 1000) % 60} secs`
|
"value": `${Math.trunc(connUptime / 86400000)} days, ${Math.trunc(connUptime / 3600000) % 24} hrs, ${Math.trunc(connUptime / 60000) % 60} mins, ${Math.trunc(connUptime / 1000) % 60} secs`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Host",
|
"name": "Host",
|
||||||
"value": `${os.type()} ${os.release()} (${os.arch()})`,
|
"value": `${os.type()} ${os.release()} (${os.arch()})`,
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Library",
|
"name": "Library",
|
||||||
"value": `Oceanic ${VERSION}`,
|
"value": `Oceanic ${VERSION}`,
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Node.js Version",
|
"name": "Node.js Version",
|
||||||
"value": process.version,
|
"value": process.version,
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Shard",
|
"name": "Shard",
|
||||||
"value": this.guild ? this.client.guildShardMap[this.guild.id] : "N/A",
|
"value": this.guild ? this.client.guildShardMap[this.guild.id] : "N/A",
|
||||||
"inline": true
|
"inline": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Servers",
|
"name": "Servers",
|
||||||
"value": servers ? servers : `${this.client.guilds.size} (for this process only)`,
|
"value": servers ? servers : `${this.client.guilds.size} (for this process only)`,
|
||||||
"inline": true
|
"inline": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
list() {
|
list() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
pm2.list((err, list) => {
|
pm2.list((err, list) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
resolve(list.filter((v) => v.name.includes("esmBot-proc")));
|
resolve(list.filter((v) => v.name.includes("esmBot-proc")));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets some statistics about me";
|
static description = "Gets some statistics about me";
|
||||||
static aliases = ["status", "stat"];
|
static aliases = ["status", "stat"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StatsCommand;
|
export default StatsCommand;
|
|
@ -1,36 +1,36 @@
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
import imagedetect from "../../utils/imagedetect.js";
|
import imagedetect from "../../utils/imagedetect.js";
|
||||||
|
|
||||||
class StickerCommand extends Command {
|
class StickerCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const result = await imagedetect(this.client, this.message, this.interaction, this.options, false, false, true);
|
const result = await imagedetect(this.client, this.message, this.interaction, this.options, false, false, true);
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!result) return "You need to provide a sticker!";
|
if (!result) return "You need to provide a sticker!";
|
||||||
if (result.format_type === 1) { // PNG
|
if (result.format_type === 1) { // PNG
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `https://cdn.discordapp.com/stickers/${result.id}.png`;
|
return `https://cdn.discordapp.com/stickers/${result.id}.png`;
|
||||||
} else if (result.format_type === 2) { // APNG
|
} else if (result.format_type === 2) { // APNG
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return {
|
return {
|
||||||
embeds: [{
|
embeds: [{
|
||||||
color: 16711680,
|
color: 16711680,
|
||||||
description: `[This sticker is an APNG; however, since Discord doesn't allow displaying APNGs outside of stickers, you'll have to save it or open it in your browser to view it.](https://cdn.discordapp.com/stickers/${result.id}.png)`,
|
description: `[This sticker is an APNG; however, since Discord doesn't allow displaying APNGs outside of stickers, you'll have to save it or open it in your browser to view it.](https://cdn.discordapp.com/stickers/${result.id}.png)`,
|
||||||
image: {
|
image: {
|
||||||
url: `https://cdn.discordapp.com/stickers/${result.id}.png`
|
url: `https://cdn.discordapp.com/stickers/${result.id}.png`
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
} else if (result.format_type === 3) { // Lottie
|
} else if (result.format_type === 3) { // Lottie
|
||||||
this.success = true;
|
this.success = true;
|
||||||
return `I can't display this sticker because it uses the Lottie animation format; however, I can give you the raw JSON link to it: https://cdn.discordapp.com/stickers/${result.id}.json`;
|
return `I can't display this sticker because it uses the Lottie animation format; however, I can give you the raw JSON link to it: https://cdn.discordapp.com/stickers/${result.id}.json`;
|
||||||
} else {
|
} else {
|
||||||
return "I don't recognize that sticker format!";
|
return "I don't recognize that sticker format!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Gets a raw sticker image";
|
static description = "Gets a raw sticker image";
|
||||||
static aliases = ["stick"];
|
static aliases = ["stick"];
|
||||||
static arguments = ["[sticker]"];
|
static arguments = ["[sticker]"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default StickerCommand;
|
export default StickerCommand;
|
|
@ -1,36 +1,35 @@
|
||||||
import { request } from "undici";
|
import { request } from "undici";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
const { searx } = JSON.parse(readFileSync(new URL("../../config/servers.json", import.meta.url)));
|
const { searx } = JSON.parse(readFileSync(new URL("../../config/servers.json", import.meta.url)));
|
||||||
import { random } from "../../utils/misc.js";
|
import { random } from "../../utils/misc.js";
|
||||||
import paginator from "../../utils/pagination/pagination.js";
|
// import paginator from "../../utils/pagination/pagination.js";
|
||||||
import Command from "../../classes/command.js";
|
import Command from "../../classes/command.js";
|
||||||
|
|
||||||
class YouTubeCommand extends Command {
|
class YouTubeCommand extends Command {
|
||||||
async run() {
|
async run() {
|
||||||
const query = this.options.query ?? this.args.join(" ");
|
const query = this.options.query ?? this.args.join(" ");
|
||||||
this.success = false;
|
this.success = false;
|
||||||
if (!query || !query.trim()) return "You need to provide something to search for!";
|
if (!query || !query.trim()) return "You need to provide something to search for!";
|
||||||
await this.acknowledge();
|
await this.acknowledge();
|
||||||
const messages = [];
|
const messages = [];
|
||||||
const videos = await request(`${random(searx)}/search?format=json&safesearch=1&categories=videos&q=!youtube%20${encodeURIComponent(query)}`).then(res => res.body.json());
|
const videos = await request(`${random(searx)}/search?format=json&safesearch=1&categories=videos&q=!youtube%20${encodeURIComponent(query)}`).then(res => res.body.json());
|
||||||
if (videos.results.length === 0) return "I couldn't find any results!";
|
if (videos.results.length === 0) return "I couldn't find any results!";
|
||||||
for (const [i, value] of videos.results.entries()) {
|
// console.log(videos.results[0])
|
||||||
messages.push({ content: `Page ${i + 1} of ${videos.results.length}\n<:youtube:637020823005167626> **${value.title.replaceAll("*", "\\*")}**\nUploaded by **${value.author.replaceAll("*", "\\*")}**\n${value.url}` });
|
this.success = true;
|
||||||
}
|
return videos.results[0].url
|
||||||
this.success = true;
|
// return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, messages);
|
||||||
return paginator(this.client, { type: this.type, message: this.message, interaction: this.interaction, channel: this.channel, author: this.author }, messages);
|
}
|
||||||
}
|
|
||||||
|
static flags = [{
|
||||||
static flags = [{
|
name: "query",
|
||||||
name: "query",
|
type: 3,
|
||||||
type: 3,
|
description: "The query you want to search for",
|
||||||
description: "The query you want to search for",
|
required: true
|
||||||
required: true
|
}];
|
||||||
}];
|
|
||||||
|
static description = "Searches YouTube";
|
||||||
static description = "Searches YouTube";
|
static aliases = ["yt", "video", "ytsearch"];
|
||||||
static aliases = ["yt", "video", "ytsearch"];
|
static arguments = ["[query]"];
|
||||||
static arguments = ["[query]"];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export default YouTubeCommand;
|
export default YouTubeCommand;
|
|
@ -1,16 +1,16 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class NineGagCommand extends ImageCommand {
|
class NineGagCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/9gag.png",
|
water: "assets/images/9gag.png",
|
||||||
gravity: 6
|
gravity: 6
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the 9GAG watermark to an image";
|
static description = "Adds the 9GAG watermark to an image";
|
||||||
static aliases = ["ninegag", "gag"];
|
static aliases = ["ninegag", "gag"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a 9GAG watermark!";
|
static noImage = "You need to provide an image/GIF to add a 9GAG watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NineGagCommand;
|
export default NineGagCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class AVSCommand extends ImageCommand {
|
class AVSCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/avs4you.png",
|
water: "assets/images/avs4you.png",
|
||||||
gravity: 5,
|
gravity: 5,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the avs4you watermark to an image";
|
static description = "Adds the avs4you watermark to an image";
|
||||||
static aliases = ["a4y", "avs"];
|
static aliases = ["a4y", "avs"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add an avs4you watermark!";
|
static noImage = "You need to provide an image/GIF to add an avs4you watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AVSCommand;
|
export default AVSCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class BandicamCommand extends ImageCommand {
|
class BandicamCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/bandicam.png",
|
water: "assets/images/bandicam.png",
|
||||||
gravity: 2,
|
gravity: 2,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the Bandicam watermark to an image";
|
static description = "Adds the Bandicam watermark to an image";
|
||||||
static aliases = ["bandi"];
|
static aliases = ["bandi"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a Bandicam watermark!";
|
static noImage = "You need to provide an image/GIF to add a Bandicam watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BandicamCommand;
|
export default BandicamCommand;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class BlurCommand extends ImageCommand {
|
class BlurCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
sharp: false
|
sharp: false
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Blurs an image";
|
static description = "Blurs an image";
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to blur!";
|
static noImage = "You need to provide an image/GIF to blur!";
|
||||||
static command = "blur";
|
static command = "blur";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BlurCommand;
|
export default BlurCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class BounceCommand extends ImageCommand {
|
class BounceCommand extends ImageCommand {
|
||||||
static description = "Makes an image bounce up and down";
|
static description = "Makes an image bounce up and down";
|
||||||
static aliases = ["bouncy"];
|
static aliases = ["bouncy"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to bounce!";
|
static noImage = "You need to provide an image/GIF to bounce!";
|
||||||
static command = "bounce";
|
static command = "bounce";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BounceCommand;
|
export default BounceCommand;
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { cleanMessage } from "../../utils/misc.js";
|
import { cleanMessage } from "../../utils/misc.js";
|
||||||
|
|
||||||
class CaptionCommand extends ImageCommand {
|
class CaptionCommand extends ImageCommand {
|
||||||
params(url) {
|
params(url) {
|
||||||
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
||||||
let newCaption = cleanMessage(this.message ?? this.interaction, newArgs);
|
let newCaption = cleanMessage(this.message ?? this.interaction, newArgs);
|
||||||
if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.options.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`;
|
if (process.env.NODE_ENV === "development" && newCaption.toLowerCase() === "get real" && !this.options.noEgg) newCaption = `I'm tired of people telling me to "get real". Every day I put captions on images for people, some funny and some not, but out of all of those "get real" remains the most used caption. Why? I am simply a computer program running on a server, I am unable to manifest myself into the real world. As such, I'm confused as to why anyone would want me to "get real". Is this form not good enough? Alas, as I am simply a bot, I must follow the tasks that I was originally intended to perform, so here goes:\n${newCaption}`;
|
||||||
return {
|
return {
|
||||||
caption: newCaption,
|
caption: newCaption,
|
||||||
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "futura"
|
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "futura"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "noegg",
|
name: "noegg",
|
||||||
description: "Disable... something. Not saying what it is though.",
|
description: "Disable... something. Not saying what it is though.",
|
||||||
type: 5
|
type: 5
|
||||||
}, {
|
}, {
|
||||||
name: "font",
|
name: "font",
|
||||||
type: 3,
|
type: 3,
|
||||||
choices: (() => {
|
choices: (() => {
|
||||||
const array = [];
|
const array = [];
|
||||||
for (const font of this.allowedFonts) {
|
for (const font of this.allowedFonts) {
|
||||||
array.push({ name: font, value: font });
|
array.push({ name: font, value: font });
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
})(),
|
})(),
|
||||||
description: "Specify the font you want to use (default: futura)"
|
description: "Specify the font you want to use (default: futura)"
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Adds a caption to an image";
|
static description = "Adds a caption to an image";
|
||||||
static aliases = ["gifc", "gcaption", "ifcaption", "ifunnycaption"];
|
static aliases = ["gifc", "gcaption", "ifcaption", "ifunnycaption"];
|
||||||
static arguments = ["[text]"];
|
static arguments = ["[text]"];
|
||||||
|
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide some text to add a caption!";
|
static noText = "You need to provide some text to add a caption!";
|
||||||
static noImage = "You need to provide an image/GIF to add a caption!";
|
static noImage = "You need to provide an image/GIF to add a caption!";
|
||||||
static command = "caption";
|
static command = "caption";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CaptionCommand;
|
export default CaptionCommand;
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { cleanMessage } from "../../utils/misc.js";
|
import { cleanMessage } from "../../utils/misc.js";
|
||||||
const words = ["me irl", "dank", "follow my second account @esmBot_", "2016", "meme", "wholesome", "reddit", "instagram", "twitter", "facebook", "fortnite", "minecraft", "relatable", "gold", "funny", "template", "hilarious", "memes", "deep fried", "2020", "leafy", "pewdiepie"];
|
const words = ["me irl", "dank", "follow my second account @esmBot_", "2016", "meme", "wholesome", "reddit", "instagram", "twitter", "facebook", "fortnite", "minecraft", "relatable", "gold", "funny", "template", "hilarious", "memes", "deep fried", "2020", "leafy", "pewdiepie"];
|
||||||
|
|
||||||
class CaptionTwoCommand extends ImageCommand {
|
class CaptionTwoCommand extends ImageCommand {
|
||||||
params(url) {
|
params(url) {
|
||||||
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
||||||
return {
|
return {
|
||||||
caption: newArgs && newArgs.trim() ? cleanMessage(this.message ?? this.interaction, newArgs) : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
|
caption: newArgs && newArgs.trim() ? cleanMessage(this.message ?? this.interaction, newArgs) : words.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * words.length + 1)).join(" "),
|
||||||
top: !!this.options.top,
|
top: !!this.options.top,
|
||||||
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica"
|
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "helvetica"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "top",
|
name: "top",
|
||||||
description: "Put the caption on the top of an image instead of the bottom",
|
description: "Put the caption on the top of an image instead of the bottom",
|
||||||
type: 5
|
type: 5
|
||||||
}, {
|
}, {
|
||||||
name: "font",
|
name: "font",
|
||||||
type: 3,
|
type: 3,
|
||||||
choices: (() => {
|
choices: (() => {
|
||||||
const array = [];
|
const array = [];
|
||||||
for (const font of this.allowedFonts) {
|
for (const font of this.allowedFonts) {
|
||||||
array.push({ name: font, value: font });
|
array.push({ name: font, value: font });
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
})(),
|
})(),
|
||||||
description: "Specify the font you want to use (default: helvetica)"
|
description: "Specify the font you want to use (default: helvetica)"
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Adds a me.me caption/tag list to an image";
|
static description = "Adds a me.me caption/tag list to an image";
|
||||||
static aliases = ["tags2", "meirl", "memecaption", "medotmecaption"];
|
static aliases = ["tags2", "meirl", "memecaption", "medotmecaption"];
|
||||||
static arguments = ["{text}"];
|
static arguments = ["{text}"];
|
||||||
|
|
||||||
static textOptional = true;
|
static textOptional = true;
|
||||||
static noText = "You need to provide some text to add a caption!";
|
static noText = "You need to provide some text to add a caption!";
|
||||||
static noImage = "You need to provide an image/GIF to add a caption!";
|
static noImage = "You need to provide an image/GIF to add a caption!";
|
||||||
static command = "captionTwo";
|
static command = "captionTwo";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CaptionTwoCommand;
|
export default CaptionTwoCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class CircleCommand extends ImageCommand {
|
class CircleCommand extends ImageCommand {
|
||||||
static description = "Applies a radial blur effect on an image";
|
static description = "Applies a radial blur effect on an image";
|
||||||
static aliases = ["cblur", "radial", "radialblur"];
|
static aliases = ["cblur", "radial", "radialblur"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add radial blur!";
|
static noImage = "You need to provide an image/GIF to add radial blur!";
|
||||||
static command = "circle";
|
static command = "circle";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CircleCommand;
|
export default CircleCommand;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class CropCommand extends ImageCommand {
|
class CropCommand extends ImageCommand {
|
||||||
static description = "Crops an image to 1:1";
|
static description = "Crops an image to 1:1";
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to crop!";
|
static noImage = "You need to provide an image/GIF to crop!";
|
||||||
static command = "crop";
|
static command = "crop";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CropCommand;
|
export default CropCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class DeepfryCommand extends ImageCommand {
|
class DeepfryCommand extends ImageCommand {
|
||||||
static description = "Deep-fries an image";
|
static description = "Deep-fries an image";
|
||||||
static aliases = ["fry", "jpeg2", "nuke", "df"];
|
static aliases = ["fry", "jpeg2", "nuke", "df"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to fry!";
|
static noImage = "You need to provide an image/GIF to fry!";
|
||||||
static command = "deepfry";
|
static command = "deepfry";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeepfryCommand;
|
export default DeepfryCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class DeviantArtCommand extends ImageCommand {
|
class DeviantArtCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/deviantart.png",
|
water: "assets/images/deviantart.png",
|
||||||
gravity: 5,
|
gravity: 5,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds a DeviantArt watermark to an image";
|
static description = "Adds a DeviantArt watermark to an image";
|
||||||
static aliases = ["da", "deviant"];
|
static aliases = ["da", "deviant"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a DeviantArt watermark!";
|
static noImage = "You need to provide an image/GIF to add a DeviantArt watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DeviantArtCommand;
|
export default DeviantArtCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class ExplodeCommand extends ImageCommand {
|
class ExplodeCommand extends ImageCommand {
|
||||||
static description = "Explodes an image";
|
static description = "Explodes an image";
|
||||||
static aliases = ["exp"];
|
static aliases = ["exp"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to explode!";
|
static noImage = "You need to provide an image/GIF to explode!";
|
||||||
static command = "explode";
|
static command = "explode";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ExplodeCommand;
|
export default ExplodeCommand;
|
||||||
|
|
|
@ -1,45 +1,45 @@
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import emojiRegex from "emoji-regex";
|
import emojiRegex from "emoji-regex";
|
||||||
import emoji from "node-emoji";
|
import emoji from "node-emoji";
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class FlagCommand extends ImageCommand {
|
class FlagCommand extends ImageCommand {
|
||||||
flagPath = "";
|
flagPath = "";
|
||||||
|
|
||||||
async criteria() {
|
async criteria() {
|
||||||
const text = this.options.text ?? this.args[0];
|
const text = this.options.text ?? this.args[0];
|
||||||
if (!text.match(emojiRegex())) return false;
|
if (!text.match(emojiRegex())) return false;
|
||||||
const flag = emoji.unemojify(text).replaceAll(":", "").replace("flag-", "");
|
const flag = emoji.unemojify(text).replaceAll(":", "").replace("flag-", "");
|
||||||
let path = `assets/images/region-flags/png/${flag.toUpperCase()}.png`;
|
let path = `assets/images/region-flags/png/${flag.toUpperCase()}.png`;
|
||||||
if (flag === "pirate_flag") path = "assets/images/pirateflag.png";
|
if (flag === "pirate_flag") path = "assets/images/pirateflag.png";
|
||||||
if (flag === "rainbow-flag") path = "assets/images/rainbowflag.png";
|
if (flag === "rainbow-flag") path = "assets/images/rainbowflag.png";
|
||||||
if (flag === "checkered_flag") path = "assets/images/checkeredflag.png";
|
if (flag === "checkered_flag") path = "assets/images/checkeredflag.png";
|
||||||
if (flag === "transgender_flag") path = "assets/images/transflag.png";
|
if (flag === "transgender_flag") path = "assets/images/transflag.png";
|
||||||
if (text === "🏴") path = "assets/images/region-flags/png/GB-SCT.png";
|
if (text === "🏴") path = "assets/images/region-flags/png/GB-SCT.png";
|
||||||
if (text === "🏴") path = "assets/images/region-flags/png/GB-WLS.png";
|
if (text === "🏴") path = "assets/images/region-flags/png/GB-WLS.png";
|
||||||
if (text === "🏴") path = "assets/images/region-flags/png/GB-ENG.png";
|
if (text === "🏴") path = "assets/images/region-flags/png/GB-ENG.png";
|
||||||
try {
|
try {
|
||||||
await fs.promises.access(path);
|
await fs.promises.access(path);
|
||||||
this.flagPath = path;
|
this.flagPath = path;
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params() {
|
params() {
|
||||||
return {
|
return {
|
||||||
overlay: this.flagPath
|
overlay: this.flagPath
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Overlays a flag onto an image";
|
static description = "Overlays a flag onto an image";
|
||||||
static arguments = ["[flag]"];
|
static arguments = ["[flag]"];
|
||||||
|
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide an emoji of a flag to overlay!";
|
static noText = "You need to provide an emoji of a flag to overlay!";
|
||||||
static noImage = "You need to provide an image/GIF to overlay a flag onto!";
|
static noImage = "You need to provide an image/GIF to overlay a flag onto!";
|
||||||
static command = "flag";
|
static command = "flag";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FlagCommand;
|
export default FlagCommand;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class FlipCommand extends ImageCommand {
|
class FlipCommand extends ImageCommand {
|
||||||
static description = "Flips an image";
|
static description = "Flips an image";
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to flip!";
|
static noImage = "You need to provide an image/GIF to flip!";
|
||||||
static command = "flip";
|
static command = "flip";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FlipCommand;
|
export default FlipCommand;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class FlopCommand extends ImageCommand {
|
class FlopCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
flop: true
|
flop: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Flips an image";
|
static description = "Flips an image";
|
||||||
static aliases = ["flip2"];
|
static aliases = ["flip2"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to flop!";
|
static noImage = "You need to provide an image/GIF to flop!";
|
||||||
static command = "flip";
|
static command = "flip";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FlopCommand;
|
export default FlopCommand;
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class FreezeCommand extends ImageCommand {
|
class FreezeCommand extends ImageCommand {
|
||||||
params() {
|
params() {
|
||||||
const frameCount = parseInt(this.options.endframe ?? this.args[0]);
|
const frameCount = parseInt(this.options.endframe ?? this.args[0]);
|
||||||
return {
|
return {
|
||||||
loop: false,
|
loop: false,
|
||||||
frame: isNaN(frameCount) ? -1 : frameCount
|
frame: isNaN(frameCount) ? -1 : frameCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "endframe",
|
name: "endframe",
|
||||||
type: 4,
|
type: 4,
|
||||||
description: "Set the end frame (default: last frame)",
|
description: "Set the end frame (default: last frame)",
|
||||||
min_value: 0
|
min_value: 0
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Makes an image sequence only play once";
|
static description = "Makes an image sequence only play once";
|
||||||
static aliases = ["noloop", "once"];
|
static aliases = ["noloop", "once"];
|
||||||
static arguments = ["{end frame number}"];
|
static arguments = ["{end frame number}"];
|
||||||
|
|
||||||
static requiresGIF = true;
|
static requiresGIF = true;
|
||||||
static noImage = "You need to provide an image/GIF to freeze!";
|
static noImage = "You need to provide an image/GIF to freeze!";
|
||||||
static command = "freeze";
|
static command = "freeze";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FreezeCommand;
|
export default FreezeCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class FunkyCommand extends ImageCommand {
|
class FunkyCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/funky.png",
|
water: "assets/images/funky.png",
|
||||||
gravity: 3,
|
gravity: 3,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the New Funky Mode banner to an image";
|
static description = "Adds the New Funky Mode banner to an image";
|
||||||
static aliases = ["funkymode", "newfunkymode", "funkykong"];
|
static aliases = ["funkymode", "newfunkymode", "funkykong"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a New Funky Mode banner!";
|
static noImage = "You need to provide an image/GIF to add a New Funky Mode banner!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FunkyCommand;
|
export default FunkyCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class GameXplainCommand extends ImageCommand {
|
class GameXplainCommand extends ImageCommand {
|
||||||
static description = "Makes a GameXplain thumbnail from an image";
|
static description = "Makes a GameXplain thumbnail from an image";
|
||||||
static aliases = ["gx"];
|
static aliases = ["gx"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to make a GameXplain thumbnail from!";
|
static noImage = "You need to provide an image/GIF to make a GameXplain thumbnail from!";
|
||||||
static command = "gamexplain";
|
static command = "gamexplain";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GameXplainCommand;
|
export default GameXplainCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class GIFCommand extends ImageCommand {
|
class GIFCommand extends ImageCommand {
|
||||||
static description = "Converts an image into a GIF";
|
static description = "Converts an image into a GIF";
|
||||||
static aliases = ["gif", "getgif", "togif", "tgif", "gifify"];
|
static aliases = ["gif", "getgif", "togif", "tgif", "gifify"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image to convert to GIF!";
|
static noImage = "You need to provide an image to convert to GIF!";
|
||||||
static command = "togif";
|
static command = "togif";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GIFCommand;
|
export default GIFCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class GlobeCommand extends ImageCommand {
|
class GlobeCommand extends ImageCommand {
|
||||||
static description = "Spins an image";
|
static description = "Spins an image";
|
||||||
static aliases = ["sphere"];
|
static aliases = ["sphere"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to spin!";
|
static noImage = "You need to provide an image/GIF to spin!";
|
||||||
static command = "globe";
|
static command = "globe";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GlobeCommand;
|
export default GlobeCommand;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class GrayscaleCommand extends ImageCommand {
|
class GrayscaleCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
color: "grayscale"
|
color: "grayscale"
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds a grayscale filter";
|
static description = "Adds a grayscale filter";
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to turn grayscale!";
|
static noImage = "You need to provide an image/GIF to turn grayscale!";
|
||||||
static command = "colors";
|
static command = "colors";
|
||||||
static aliases = ["gray", "greyscale", "grey"];
|
static aliases = ["gray", "greyscale", "grey"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GrayscaleCommand;
|
export default GrayscaleCommand;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class HaaHCommand extends ImageCommand {
|
class HaaHCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
first: true
|
first: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Mirrors the left side of an image onto the right";
|
static description = "Mirrors the left side of an image onto the right";
|
||||||
static aliases = ["magik4", "mirror2"];
|
static aliases = ["magik4", "mirror2"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to mirror!";
|
static noImage = "You need to provide an image/GIF to mirror!";
|
||||||
static command = "mirror";
|
static command = "mirror";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HaaHCommand;
|
export default HaaHCommand;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class HooHCommand extends ImageCommand {
|
class HooHCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
vertical: true
|
vertical: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Mirrors the bottom of an image onto the top";
|
static description = "Mirrors the bottom of an image onto the top";
|
||||||
static aliases = ["magik6", "mirror4"];
|
static aliases = ["magik6", "mirror4"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to mirror!";
|
static noImage = "You need to provide an image/GIF to mirror!";
|
||||||
static command = "mirror";
|
static command = "mirror";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HooHCommand;
|
export default HooHCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class HypercamCommand extends ImageCommand {
|
class HypercamCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/hypercam.png",
|
water: "assets/images/hypercam.png",
|
||||||
gravity: 1,
|
gravity: 1,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the Hypercam watermark to an image";
|
static description = "Adds the Hypercam watermark to an image";
|
||||||
static aliases = ["hcam"];
|
static aliases = ["hcam"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a Hypercam watermark!";
|
static noImage = "You need to provide an image/GIF to add a Hypercam watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default HypercamCommand;
|
export default HypercamCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class iFunnyCommand extends ImageCommand {
|
class iFunnyCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/ifunny.png",
|
water: "assets/images/ifunny.png",
|
||||||
gravity: 8,
|
gravity: 8,
|
||||||
resize: true,
|
resize: true,
|
||||||
append: true
|
append: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the iFunny watermark to an image";
|
static description = "Adds the iFunny watermark to an image";
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add an iFunny watermark!";
|
static noImage = "You need to provide an image/GIF to add an iFunny watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default iFunnyCommand;
|
export default iFunnyCommand;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class ImplodeCommand extends ImageCommand {
|
class ImplodeCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
implode: true
|
implode: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Implodes an image";
|
static description = "Implodes an image";
|
||||||
static aliases = ["imp"];
|
static aliases = ["imp"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to implode!";
|
static noImage = "You need to provide an image/GIF to implode!";
|
||||||
static command = "explode";
|
static command = "explode";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ImplodeCommand;
|
export default ImplodeCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class InvertCommand extends ImageCommand {
|
class InvertCommand extends ImageCommand {
|
||||||
static description = "Inverts an image";
|
static description = "Inverts an image";
|
||||||
static aliases = ["inverse", "negate", "negative"];
|
static aliases = ["inverse", "negate", "negative"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to invert!";
|
static noImage = "You need to provide an image/GIF to invert!";
|
||||||
static command = "invert";
|
static command = "invert";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default InvertCommand;
|
export default InvertCommand;
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class JPEGCommand extends ImageCommand {
|
class JPEGCommand extends ImageCommand {
|
||||||
params() {
|
params() {
|
||||||
const quality = parseInt(this.options.quality ?? this.args[0]);
|
const quality = parseInt(this.options.quality ?? this.args[0]);
|
||||||
return {
|
return {
|
||||||
quality: isNaN(quality) ? 1 : Math.max(1, Math.min(quality, 100))
|
quality: isNaN(quality) ? 1 : Math.max(1, Math.min(quality, 100))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "quality",
|
name: "quality",
|
||||||
type: 4,
|
type: 4,
|
||||||
description: "Set the JPEG quality (default: 1)",
|
description: "Set the JPEG quality (default: 1)",
|
||||||
min_value: 1,
|
min_value: 1,
|
||||||
max_value: 100
|
max_value: 100
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Adds JPEG compression to an image";
|
static description = "Adds JPEG compression to an image";
|
||||||
static aliases = ["needsmorejpeg", "jpegify", "magik2", "morejpeg", "jpg", "quality"];
|
static aliases = ["needsmorejpeg", "jpegify", "magik2", "morejpeg", "jpg", "quality"];
|
||||||
static arguments = ["{quality}"];
|
static arguments = ["{quality}"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add more JPEG!";
|
static noImage = "You need to provide an image/GIF to add more JPEG!";
|
||||||
static command = "jpeg";
|
static command = "jpeg";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default JPEGCommand;
|
export default JPEGCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class KineMasterCommand extends ImageCommand {
|
class KineMasterCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/kinemaster.png",
|
water: "assets/images/kinemaster.png",
|
||||||
gravity: 3,
|
gravity: 3,
|
||||||
resize: true
|
resize: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the KineMaster watermark to an image";
|
static description = "Adds the KineMaster watermark to an image";
|
||||||
static aliases = ["kine"];
|
static aliases = ["kine"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a KineMaster watermark!";
|
static noImage = "You need to provide an image/GIF to add a KineMaster watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default KineMasterCommand;
|
export default KineMasterCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class MagikCommand extends ImageCommand {
|
class MagikCommand extends ImageCommand {
|
||||||
static description = "Adds a content aware scale effect to an image";
|
static description = "Adds a content aware scale effect to an image";
|
||||||
static aliases = ["imagemagic", "imagemagick", "imagemagik", "magic", "magick", "cas", "liquid"];
|
static aliases = ["imagemagic", "imagemagick", "imagemagik", "magic", "magick", "cas", "liquid"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add some magik!";
|
static noImage = "You need to provide an image/GIF to add some magik!";
|
||||||
static command = "magik";
|
static command = "magik";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MagikCommand;
|
export default MagikCommand;
|
||||||
|
|
|
@ -1,54 +1,54 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { cleanMessage } from "../../utils/misc.js";
|
import { cleanMessage } from "../../utils/misc.js";
|
||||||
|
|
||||||
class MemeCommand extends ImageCommand {
|
class MemeCommand extends ImageCommand {
|
||||||
async criteria(text, url) {
|
async criteria(text, url) {
|
||||||
const [topText, bottomText] = text.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
const [topText, bottomText] = text.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
||||||
if (topText === "" && bottomText === "") {
|
if (topText === "" && bottomText === "") {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params(url) {
|
params(url) {
|
||||||
const newArgs = this.options.text ?? this.args.join(" ");
|
const newArgs = this.options.text ?? this.args.join(" ");
|
||||||
const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
||||||
return {
|
return {
|
||||||
top: cleanMessage(this.message ?? this.interaction, this.options.case ? topText : topText.toUpperCase()),
|
top: cleanMessage(this.message ?? this.interaction, this.options.case ? topText : topText.toUpperCase()),
|
||||||
bottom: bottomText ? cleanMessage(this.message ?? this.interaction, this.options.case ? bottomText : bottomText.toUpperCase()) : "",
|
bottom: bottomText ? cleanMessage(this.message ?? this.interaction, this.options.case ? bottomText : bottomText.toUpperCase()) : "",
|
||||||
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "impact"
|
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "impact"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "case",
|
name: "case",
|
||||||
description: "Make the meme text case-sensitive (allows for lowercase text)",
|
description: "Make the meme text case-sensitive (allows for lowercase text)",
|
||||||
type: 5
|
type: 5
|
||||||
}, {
|
}, {
|
||||||
name: "font",
|
name: "font",
|
||||||
type: 3,
|
type: 3,
|
||||||
choices: (() => {
|
choices: (() => {
|
||||||
const array = [];
|
const array = [];
|
||||||
for (const font of this.allowedFonts) {
|
for (const font of this.allowedFonts) {
|
||||||
array.push({ name: font, value: font });
|
array.push({ name: font, value: font });
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
})(),
|
})(),
|
||||||
description: "Specify the font you want to use (default: impact)"
|
description: "Specify the font you want to use (default: impact)"
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Generates a meme from an image (separate top/bottom text with a comma)";
|
static description = "Generates a meme from an image (separate top/bottom text with a comma)";
|
||||||
static arguments = ["[top text]", "{bottom text}"];
|
static arguments = ["[top text]", "{bottom text}"];
|
||||||
|
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide some text to generate a meme!";
|
static noText = "You need to provide some text to generate a meme!";
|
||||||
static noImage = "You need to provide an image/GIF to generate a meme!";
|
static noImage = "You need to provide an image/GIF to generate a meme!";
|
||||||
static command = "meme";
|
static command = "meme";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MemeCommand;
|
export default MemeCommand;
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class MemeCenterCommand extends ImageCommand {
|
class MemeCenterCommand extends ImageCommand {
|
||||||
params = {
|
params = {
|
||||||
water: "assets/images/memecenter.png",
|
water: "assets/images/memecenter.png",
|
||||||
gravity: 9,
|
gravity: 9,
|
||||||
mc: true
|
mc: true
|
||||||
};
|
};
|
||||||
|
|
||||||
static description = "Adds the MemeCenter watermark to an image";
|
static description = "Adds the MemeCenter watermark to an image";
|
||||||
static aliases = ["memec", "mcenter"];
|
static aliases = ["memec", "mcenter"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to add a MemeCenter watermark!";
|
static noImage = "You need to provide an image/GIF to add a MemeCenter watermark!";
|
||||||
static command = "watermark";
|
static command = "watermark";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MemeCenterCommand;
|
export default MemeCenterCommand;
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { cleanMessage } from "../../utils/misc.js";
|
import { cleanMessage } from "../../utils/misc.js";
|
||||||
|
|
||||||
class MotivateCommand extends ImageCommand {
|
class MotivateCommand extends ImageCommand {
|
||||||
async criteria(text, url) {
|
async criteria(text, url) {
|
||||||
const [topText, bottomText] = text.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
const [topText, bottomText] = text.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
||||||
if (topText === "" && bottomText === "") {
|
if (topText === "" && bottomText === "") {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params(url) {
|
params(url) {
|
||||||
const newArgs = this.options.text ?? this.args.join(" ");
|
const newArgs = this.options.text ?? this.args.join(" ");
|
||||||
const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
const [topText, bottomText] = newArgs.replaceAll(url, "").split(/(?<!\\),/).map(elem => elem.trim());
|
||||||
return {
|
return {
|
||||||
top: cleanMessage(this.message ?? this.interaction, topText),
|
top: cleanMessage(this.message ?? this.interaction, topText),
|
||||||
bottom: bottomText ? cleanMessage(this.message ?? this.interaction, bottomText) : "",
|
bottom: bottomText ? cleanMessage(this.message ?? this.interaction, bottomText) : "",
|
||||||
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "times"
|
font: typeof this.options.font === "string" && this.constructor.allowedFonts.includes(this.options.font.toLowerCase()) ? this.options.font.toLowerCase() : "times"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static init() {
|
static init() {
|
||||||
super.init();
|
super.init();
|
||||||
this.flags.push({
|
this.flags.push({
|
||||||
name: "font",
|
name: "font",
|
||||||
type: 3,
|
type: 3,
|
||||||
choices: (() => {
|
choices: (() => {
|
||||||
const array = [];
|
const array = [];
|
||||||
for (const font of this.allowedFonts) {
|
for (const font of this.allowedFonts) {
|
||||||
array.push({ name: font, value: font });
|
array.push({ name: font, value: font });
|
||||||
}
|
}
|
||||||
return array;
|
return array;
|
||||||
})(),
|
})(),
|
||||||
description: "Specify the font you want to use (default: times)"
|
description: "Specify the font you want to use (default: times)"
|
||||||
});
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
static description = "Generates a motivational poster";
|
static description = "Generates a motivational poster";
|
||||||
static aliases = ["motivational", "motiv", "demotiv", "demotivational", "poster", "motivation", "demotivate"];
|
static aliases = ["motivational", "motiv", "demotiv", "demotivational", "poster", "motivation", "demotivate"];
|
||||||
static arguments = ["[top text]", "{bottom text}"];
|
static arguments = ["[top text]", "{bottom text}"];
|
||||||
|
|
||||||
static requiresText = true;
|
static requiresText = true;
|
||||||
static noText = "You need to provide some text to generate a motivational poster!";
|
static noText = "You need to provide some text to generate a motivational poster!";
|
||||||
static noImage = "You need to provide an image/GIF to generate a motivational poster!";
|
static noImage = "You need to provide an image/GIF to generate a motivational poster!";
|
||||||
static command = "motivate";
|
static command = "motivate";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MotivateCommand;
|
export default MotivateCommand;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class PixelateCommand extends ImageCommand {
|
class PixelateCommand extends ImageCommand {
|
||||||
static description = "Pixelates an image";
|
static description = "Pixelates an image";
|
||||||
static aliases = ["pixel", "small"];
|
static aliases = ["pixel", "small"];
|
||||||
|
|
||||||
static noImage = "You need to provide an image/GIF to pixelate!";
|
static noImage = "You need to provide an image/GIF to pixelate!";
|
||||||
static command = "resize";
|
static command = "resize";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PixelateCommand;
|
export default PixelateCommand;
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
import { random } from "../../utils/misc.js";
|
import { random } from "../../utils/misc.js";
|
||||||
const names = ["esmBot", "me_irl", "dankmemes", "hmmm", "gaming", "wholesome", "chonkers", "memes", "funny", "lies"];
|
const names = ["esmBot", "me_irl", "dankmemes", "hmmm", "gaming", "wholesome", "chonkers", "memes", "funny", "lies"];
|
||||||
|
|
||||||
class RedditCommand extends ImageCommand {
|
class RedditCommand extends ImageCommand {
|
||||||
params(url) {
|
params(url) {
|
||||||
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
const newArgs = this.options.text ?? this.args.filter(item => !item.includes(url)).join(" ");
|
||||||
return {
|
return {
|
||||||
caption: newArgs?.trim() ? newArgs.replaceAll("\n", "").replaceAll(" ", "") : random(names)
|
caption: newArgs?.trim() ? newArgs.replaceAll("\n", "").replaceAll(" ", "") : random(names)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static textOptional = true;
|
static textOptional = true;
|
||||||
|
|
||||||
static description = "Adds a Reddit watermark to an image";
|
static description = "Adds a Reddit watermark to an image";
|
||||||
static arguments = ["{text}"];
|
static arguments = ["{text}"];
|
||||||
|
|
||||||
static noText = "You need to provide some text to add a Reddit watermark!";
|
static noText = "You need to provide some text to add a Reddit watermark!";
|
||||||
static command = "reddit";
|
static command = "reddit";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RedditCommand;
|
export default RedditCommand;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import ImageCommand from "../../classes/imageCommand.js";
|
import ImageCommand from "../../classes/imageCommand.js";
|
||||||
|
|
||||||
class ReverseCommand extends ImageCommand {
|
class ReverseCommand extends ImageCommand {
|
||||||
static description = "Reverses an image sequence";
|
static description = "Reverses an image sequence";
|
||||||
static aliases = ["backwards"];
|
static aliases = ["backwards"];
|
||||||
|
|
||||||
static requiresGIF = true;
|
static requiresGIF = true;
|
||||||
static noImage = "You need to provide an image/GIF to reverse!";
|
static noImage = "You need to provide an image/GIF to reverse!";
|
||||||
static command = "reverse";
|
static command = "reverse";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ReverseCommand;
|
export default ReverseCommand;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue