Initial Commit

This commit is contained in:
Chase 2023-04-03 09:38:01 -05:00
commit a0326ce5f0
Signed by: chase
GPG Key ID: 9EC29E797878008C
266 changed files with 25606 additions and 0 deletions

4
.dockerignore Normal file
View File

@ -0,0 +1,4 @@
# Prevent items from being copied into container.
node_modules
npm-debug.log
.DS_Store

18
.editorconfig Normal file
View File

@ -0,0 +1,18 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.json]
insert_final_newline = false
[*.md]
indent_size = unset
tab_width = unset

33
.eslintrc.json Normal file
View File

@ -0,0 +1,33 @@
{
"env": {
"es2021": true,
"node": true
},
"extends": [
"chase"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"no-console": "off",
"no-restricted-syntax": [
"warn",
{
"selector": "CallExpression[callee.object.name='console'][callee.property.name=/^(log)$/]",
"message": "Do not console.log"
}
]
},
"globals": {
"reactionMessageIds": "writable",
"getInfoById": "readonly",
"getConfiguration": "readonly",
"setInfo": "writable",
"setConfiguration": "writable",
"elevatedServers": "readonly",
"APIUrl": "readonly",
"gLocale": "writable"
}
}

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

6
.github/COMMIT_OPTIONS.md vendored Normal file
View File

@ -0,0 +1,6 @@
# Commit Options
* `[skip ci]` - Skip all CI
* `[skip test]` - Skips testing if the bot can start, this also skips updating the bot
* `[skip update]` - Skip updating production bot
* `[skip lint]` - Skips linting

28
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View File

@ -0,0 +1,28 @@
---
name: Bug Report
about: Report incorrect or unexpected behavior
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
<!--
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files into it.
-->
**To Reproduce**
Steps to reproduce the behavior:
1. Do '...'
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: true

View File

@ -0,0 +1,14 @@
---
name: Feature Request
about: Suggest an idea for the project
title: ''
labels: enhancement
assignees: ''
---
**Describe the feature request you'd like**
A clear and concise description of what you want to have added.
**Additional context**
Add any other context or screenshots about the feature request here.

105
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,105 @@
**Please describe the changes this PR makes and why it should be merged:**
<!--
Please move lines that apply to you out of the comment:
- Code changes have been fully tested, or there are no code changes
- This PR includes breaking changes (methods removed or renamed, parameters moved or removed)
- This PR **only** includes non-code changes, like changes to documentation, README, etc.
- I have linted this PR and it passes all tests
-->
<!--
Testing Checklist Template
If you are doing a big change, uncomment this.
- [ ] Ensure submitting Ban Appeals works.
- [ ] Ensure rejecting Ban Appeals works.
- [ ] Ensure accepting Ban Appeals works.
- [ ] Ensure webfront editing settings works as intended.
- [ ] `!bl` / `/info banlist`
- [ ] `!epoch`
- [ ] `!gl` / `/info guildlist`
- [ ] `!help`
- [ ] `!invite`
- [ ] `!membercount` / `/info membercount`
- [ ] `!ml` / `/info mutelist`
- [ ] `!ping` / `/info ping`
- [ ] `!sinfo` / `/info server`
- [ ] `!version` / `/botowner version`
- [ ] Ensure `/alloweddiscords list` returns a list of allowed discord servers, or a message saying there is none.
- [ ] Ensure `/alloweddiscords add` adds the discord server to the whitelist.
- [ ] Ensure `/alloweddiscords remove` removes the discord server from the whitelist.
- [ ] Ensure `/links list` returns a list of allowed links, or a message saying there is none.
- [ ] Ensure `/links add` adds the link to the whitelist.
- [ ] Ensure `/links remove` removes the link from the whitelist.
- [ ] Ensure `/autoban list` returns a list of auto banned phrases, or a message saying there is none.
- [ ] Ensure `/autoban add` adds the phrase to the blacklist.
- [ ] Ensure `/autoban remove` removes the phrase from the blacklist.
- [ ] Ensure `/autoreply list` returns a list of auto replied phrases, or a message saying there is none.
- [ ] Ensure `/autoreply add` adds the trigger and response to the appropriate list.
- [ ] Ensure `/autoreply remove` removes the trigger and response from the appropriate list.
- [ ] Ensure `/badword list` returns a list of bad words, or a message saying there is none.
- [ ] Ensure `/badword add` adds the word to the blacklist.
- [ ] Ensure `/badword remove` removes the word from the blacklist.
- [ ] Ensure `/ignoredm list` returns a list of ignored users, or a message saying there is none.
- [ ] Ensure `/ignoredm add` adds the word to the ignored users.
- [ ] Ensure `/ignoredm remove` removes the word from the ignored users.
- [ ] Ensure `/ban` (In Guild) works as intended, and that it sends them a DM if their DMs are open.
- [ ] Ensure `/ban` (Not In Guild) works as intended.
- [ ] Ensure `/clearhistory` removes every punishment from said user. Use `/lookup` to confirm.
- [ ] Ensure `/clearwarns` removes every warning from said user. Use `/lookup` to confirm.
- [ ] Ensure `/duration` changes a users punishments. Use `/lookup` to confirm.
- [ ] Ensure `/editcase` edits the case, and has the correct spacing.
- [ ] Ensure `/kick` works as intended.
- [ ] Ensure `/lockdown start` works as intended.
- [ ] Ensure `/lockdown end` works as intended.
- [ ] Ensure `/lookup` shows punishment information.
- [ ] Ensure `/lookup` (Hide punishments) works as intended.
- [ ] Ensure `/mute` (Perma) (In Guild) works as intended.
- [ ] Ensure `/mute` (Perma) (Not In Guild) works as intended.
- [ ] Ensure `/mute` (Temp) (In Guild) works as intended.
- [ ] Ensure `/mute` (Temp) (Not In Guild) works as intended.
- [ ] Ensure `/nick` works as intended.
- [ ] Ensure `/note add` works as intended.
- [ ] Ensure `/note remove` works as intended.
- [ ] Ensure `/prune amount` works as intended.
- [ ] Ensure `/prune user` works as intended.
- [ ] Ensure `/prune after` works as intended.
- [ ] Ensure `/slowmode start` works as intended.
- [ ] Ensure `/slowmode end` works as intended.
- [ ] Ensure `/softban` works as intended.
- [ ] Ensure `/unban` works as intended.
- [ ] Ensure `/unmute` works as intended.
- [ ] Ensure `/warn` works as intended.
- [ ] Ensure `/broadcast` works as intended.
- [ ] Ensure `/debug` treats you as a normal user. (Test with auto replies?)
- [ ] Ensure `/say` works (normal text, just image, multiple images, normal text + image, normal text + multiple images) as intended.
- [ ] Ensure `/edit` works as intended.
- [ ] Ensure `/embed` works as intended.
- [ ] Ensure `Apps > Translate` works as intended.
- [ ] Ensure `/botowner generateinvite` works as intended.
- [ ] Ensure `/respond` works as intended.
- [ ] Ensure `/pfp` works as intended. (Self & Other User)
- [ ] Ensure `/remindme` works as intended.
- [ ] Ensure `/sale` works as intended.
- [ ] Ensure `/schedule` works as intended.
- [ ] Ensure `/botowner senddm` works as intended.
- [ ] Ensure `/suggest` works as intended.
- [ ] Ensure `Apps > Report Message` works as intended.
- [ ] Ensure `Apps > User Information` works as intended.
- [ ] Ensure `/botowner banserver` works as intended.
- [ ] Ensure `/botowner unbanserver` works as intended.
- [ ] Ensure `/botowner version` works as intended.
- [ ] Ensure `/botowner purgename` works as intended.
- [ ] Ensure `/botowner printroles` works as intended.
- [ ] Ensure `/botowner printchannels` works as intended.
- [ ] Ensure `/botowner badnames` works as intended.
- [ ] Ensure `/rolepersist` works as intended.
- [ ] Ensure `/giveaway` works as intended.
- [ ] Ensure `/bootstrap` works as intended.
- [ ] Ensure `/selectroles` works as intended.
- [ ] Ensure `/ticket` works as intended.
- [ ] Ensure `/serverstats` works as intended.
-->

6
.github/SECRETS.md vendored Normal file
View File

@ -0,0 +1,6 @@
# Secrets
* `GH_PAT` - A GitHub [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) with `repo` permissions.
* `HOST` - A hostname of a SSH server to use for the `update.yml` workflow.
* `PRIVATE_KEY` - A private SSH key to use for the `update.yml` workflow.
* `VALID_CONFIG_TESTING` - An entire valid `config.json` file with `isWorkflow` set to `true` for the `test.yml` workflow. (Creating a dummy bot in one server is recommended.)

15
.github/SECURITY.md vendored Normal file
View File

@ -0,0 +1,15 @@
# Security Policy
## Supported Versions
The only supported version of the bot is the latest commit. Nothing else is supported by us.
## Testing
If you believe you have found a vulnerability, be sure you test it in the latest commit.
## Reporting a Vulnerability
If you believe you have discovered a vulnerability or exploit and ensured it happens on the latest commit, please reach out to `c@chse.dev`
**Do not make a GitHub Issue for a security vulnerability.**

12
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,12 @@
---
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 10

30
.github/labeler.yml vendored Normal file
View File

@ -0,0 +1,30 @@
---
bot:
- commands/**/*
- events/**/*
- utils/**/*
- config.json.example
- index.js
epoch:
- webfront/epoch/**/*
webfront:
- any: ["webfront/**/*", "!webfront/epoch/**/*"]
localization:
- locale/**/*
dependencies:
- package.json
- package-lock.json
- .github/**/*
misc:
- README.md
- LICENSE.md
- .gitignore
- .gitattributes
- .eslintrc.json
- .github/**/*
- .vscode/**/*

52
.github/labels.yml vendored Normal file
View File

@ -0,0 +1,52 @@
---
- name: "bot"
color: "e7e7e7"
description: "Changes related to the main bot"
- name: "bug"
color: "d73a4a"
description: "Something isn't working"
- name: "dependencies"
color: "0052cc"
description: "Pull requests that update a dependency file"
- name: "documentation"
color: "0075ca"
description: "Improvements or additions to documentation"
- name: "duplicate"
color: "cfd8d7"
description: "This issue or pull request already exists"
- name: "enhancement"
color: "a22eef"
description: "New feature or request"
- name: "epoch"
color: "f8f8f2"
description: "Changes related to epoch site"
- name: "github_actions"
color: "f8f8f2"
description: "Pull requests that update GitHub Actions code"
- name: "good first issue"
color: "7057ff"
description: "Good for newcomers"
- name: "held up"
color: "f8f8f2"
description: "This issue or pull request is held up"
- name: "help wanted"
color: "008672"
description: "Extra attention is needed"
- name: "invalid"
color: "e6e6fa"
description: "This doesn't seem right"
- name: "localization"
color: "0052cc"
description: "Pull requests that update localization files"
- name: "misc"
color: "ffffff"
description: "Changes that aren't directly related to any source"
- name: "question"
color: "cc317c"
description: "Further information is needed"
- name: "webfront"
color: "f8f8f2"
description: "Changes related to the webfront"
- name: "wontfix"
color: "ffffff"
description: "This will not be worked on"

5
.github/linters/.hadolint.yaml vendored Normal file
View File

@ -0,0 +1,5 @@
---
##########################
## Hadolint config file ##
##########################
ignored: [DL3018]

25
.github/linters/.htmlhintrc vendored Normal file
View File

@ -0,0 +1,25 @@
{
"tagname-lowercase": true,
"attr-lowercase": true,
"attr-value-double-quotes": true,
"attr-value-not-empty": false,
"attr-no-duplication": true,
"doctype-first": true,
"tag-pair": true,
"tag-self-close": false,
"spec-char-escape": false,
"id-unique": true,
"src-not-empty": true,
"title-require": false,
"alt-require": true,
"doctype-html5": true,
"id-class-value": false,
"style-disabled": false,
"inline-style-disabled": false,
"inline-script-disabled": false,
"space-tab-mixed-disabled": "space",
"id-class-ad-disabled": false,
"href-abs-or-rel": false,
"attr-unsafe-chars": true,
"head-script-disabled": false
}

7
.github/linters/.stylelintrc.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"extends": "stylelint-config-standard",
"rules": {
"selector-class-pattern": null,
"no-missing-end-of-source-newline": null
}
}

15
.github/workflows/auto-merge.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: "📦️ Auto Merge Dependency Updates"
on:
- pull_request_target
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: tjenkinson/gh-action-auto-merge-dependency-updates@v1
with:
allowed-actors: dependabot[bot]
allowed-update-types: devDependencies:minor, devDependencies:patch, devDependencies:major
merge-method: squash
repo-token: ${{ secrets.GITHUB_TOKEN }}

13
.github/workflows/labeler.yml vendored Normal file
View File

@ -0,0 +1,13 @@
---
name: "🏷️ Pull Request Labeler"
on:
- pull_request_target
jobs:
labeler:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true

20
.github/workflows/labelsync.yml vendored Normal file
View File

@ -0,0 +1,20 @@
---
name: "🏷️ Label Sync"
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
push:
branches:
- main
paths:
- ".github/labels.yml"
jobs:
labelsync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "🏷️ Label Sync"
uses: crazy-max/ghaction-github-labeler@v4
with:
github-token: ${{ secrets.GH_PAT }}

40
.github/workflows/linter.yml vendored Normal file
View File

@ -0,0 +1,40 @@
---
name: "🎨 Lint"
on:
push:
branches: [master, main]
pull_request:
branches: [master, main]
jobs:
lint:
runs-on: ubuntu-latest
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip lint]')
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: "📦️ Install Dependencies"
run: |
npm install
- name: "🎨 ESLint"
run: npx eslint . --ext .js,.jsx,.ts,.tsx
- name: "🎨 Super-Linter"
uses: github/super-linter/slim@v4
env:
VALIDATE_ALL_CODEBASE: false
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_JAVASCRIPT_ES: false
VALIDATE_JAVASCRIPT_STANDARD: false
VALIDATE_JSX: false
VALIDATE_TSX: false
VALIDATE_TYPESCRIPT_ES: false
VALIDATE_TYPESCRIPT_STANDARD: false
VALIDATE_JSCPD: false
VALIDATE_MARKDOWN: false

22
.github/workflows/lock.yml vendored Normal file
View File

@ -0,0 +1,22 @@
---
name: "🔒 Lock Resolved Issues/PRs"
on:
pull_request:
types:
- closed
issues:
types:
- closed
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: chxseh/action-lock-unlock@v1.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number || github.event.issue.number }}
lock: true
lock-reason: resolved

21
.github/workflows/unlock.yml vendored Normal file
View File

@ -0,0 +1,21 @@
---
name: "🔓 Unlock Reopened Issues/PRs"
on:
pull_request:
types:
- reopened
issues:
types:
- reopened
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: chxseh/action-lock-unlock@v1.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
number: ${{ github.event.pull_request.number || github.event.issue.number }}
lock: false

View File

@ -0,0 +1,50 @@
name: Sync Docs to Wiki
on:
push:
branches:
- main
paths:
- "docs/**"
repository_dispatch:
types: [docs]
gollum:
env:
GIT_AUTHOR_NAME: Actionbot
GIT_AUTHOR_EMAIL: actions@github.com
jobs:
job-sync-docs-to-wiki:
runs-on: ubuntu-latest
if: github.event_name != 'gollum'
steps:
- name: Checkout Repo
uses: actions/checkout@v2
- name: Sync docs to wiki
uses: newrelic/wiki-sync-action@master
with:
source: docs
destination: wiki
token: ${{ secrets.GH_PAT }}
gitAuthorName: ${{ env.GIT_AUTHOR_NAME }}
gitAuthorEmail: ${{ env.GIT_AUTHOR_EMAIL }}
job-sync-wiki-to-docs:
runs-on: ubuntu-latest
if: github.event_name == 'gollum'
steps:
- name: Checkout Repo
uses: actions/checkout@v2
with:
token: ${{ secrets.GH_PAT }} # allows us to push back to repo
ref: main
- name: Sync Wiki to Docs
uses: newrelic/wiki-sync-action@master
with:
source: wiki
destination: docs
token: ${{ secrets.GH_PAT }}
gitAuthorName: ${{ env.GIT_AUTHOR_NAME }}
gitAuthorEmail: ${{ env.GIT_AUTHOR_EMAIL }}
branch: main

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# Packages
node_modules/
# Private Data
config.json
db.*
## Ignore all guild import/export data:
*port.json
# Misc
*.bak
.DS_Store

9
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,9 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"github.vscode-pull-request-github",
"eamodio.gitlens",
"aaron-bond.better-comments",
"EditorConfig.EditorConfig"
]
}

14
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": true,
"javascript.format.placeOpenBraceOnNewLineForControlBlocks": true,
"javascript.format.placeOpenBraceOnNewLineForFunctions": true,
"editor.formatOnPaste": true,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.detectIndentation": false,
"editor.tabSize": 4,
"files.eol": "\n",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM node:16-alpine
RUN apk add --no-cache git
USER daemon
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# EXPOSE 5000
# HEALTHCHECK --interval=5m --timeout=3s \
# CMD curl -f http://localhost:5000/debug || exit 1
# CMD [ "node", "." ]

21
LICENSE.md Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Chase
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
SOFTWARE.

52
README.md Normal file
View File

@ -0,0 +1,52 @@
<div align="center">
<h1>Discord Bot<br>
<a href="https://github.com/chxseh/discord-bot/actions/workflows/linter.yml"><img alt="GitHub Actions Status" src="https://github.com/chxseh/discord-bot/actions/workflows/linter.yml/badge.svg"></a>
<a href="https://github.com/chxseh/discord-bot/stargazers"><img alt="GitHub stars" src="https://badges.chse.dev:/github/stars/chxseh/discord-bot"></a>
<a href="https://github.com/chxseh/discord-bot/issues"><img alt="GitHub issues" src="https://badges.chse.dev:/github/issues/chxseh/discord-bot"></a>
<a href="https://github.com/chxseh/discord-bot/pulls"><img alt="GitHub Pull Requests" src="https://badges.chse.dev:/github/issues-pr/chxseh/discord-bot"></a>
<a href="https://github.com/chxseh/discord-bot/network"><img alt="GitHub forks" src="https://badges.chse.dev:/github/forks/chxseh/discord-bot"></a>
<a href="https://github.com/chxseh/discord-bot/blob/main/LICENSE.md"><img alt="GitHub license" src="https://badges.chse.dev:/github/license/chxseh/discord-bot"></a>
</h1></div>
# Table of Contents <!-- omit in toc -->
- [Installation](#installation)
- [Setup](#setup)
- [Normal Installation](#normal-installation)
- [Requirements](#requirements)
- [Installation](#installation-1)
- [Webfront Setup](#webfront-setup)
## Installation
### Setup
1. Create a new [Discord Application](https://discord.com/developers/applications).
2. Make it a Bot account.
3. Enable `Server Members Intent`, and `Message Content Intent` under Privileged Gateway Intents.
4. Proceed with the [installation](#installation).
5. Setup the [Webfront Configuration](#webfront-configuration)
### Normal Installation
#### Requirements
- [Git](https://git-scm.com/)
- [Node.js](https://nodejs.org/) >= v16.14.0
- [pm2](https://www.npmjs.com/package/pm2) (`npm i -g pm2`)
#### Installation
```bash
git clone https://github.com/chxseh/discord-bot.git
cd bot
npm ci
```
> For the initial setup, use `npm start`. After initial setup is completed you can use `pm2 start npm --name "bot" -- start` to keep the bot running.
### Webfront Setup
For the webfront to work, you must set a OAuth2 Redirect URL in your Discord Application.
The redirect should be `https://yourDomain.com/discord/callback`
![img](https://i.imgur.com/f6KsPBs.png)
<!-- ### For more information, please see [the wiki](https://github.com/chxseh/discord-bot/wiki). --> <!-- omit in toc -->

10
docker-compose.yml Normal file
View File

@ -0,0 +1,10 @@
version: '2'
services:
bot:
build: .
command: node .
volumes:
- .:/app/
- /app/node_modules
ports:
- "5000:5000"

8391
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
package.json Normal file
View File

@ -0,0 +1,49 @@
{
"name": "bot",
"version": "1.0.0",
"description": "A powerful Discord Bot for large servers.",
"main": "./src/app.js",
"engines": {
"node": ">=16.14.0"
},
"type": "module",
"scripts": {
"start": "node .",
"lint": "eslint --fix --ext .js .",
"sync-i18n": "sync-i18n --files ./locale/*.json --primary en --space 4"
},
"author": "Chase <c@chse.dev> (https://chse.dev)",
"contributors": [
"mikzy <> (https://github.com/mjkzy)"
],
"license": "MIT",
"dependencies": {
"better-sqlite3": "8.0.1",
"chalk": "5.2.0",
"dbd-dark-dashboard": "1.6.592",
"discord-dashboard": "2.3.492",
"discord.js": "14.7.1",
"node-fetch": "3.3.0",
"readline-sync": "1.4.10",
"string-table": "0.1.5",
"unidecode-plus": "1.0.4"
},
"devDependencies": {
"eslint": "8.33.0",
"eslint-config-chase": "1.0.5",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jsdoc": "39.7.5",
"eslint-plugin-unicorn": "45.0.2",
"i18next-json-sync": "3.1.2"
},
"homepage": "https://github.com/chxseh/discord-bot",
"keywords": [
"discord",
"discord.js",
"discordjs"
],
"repository": {
"type": "git",
"url": "https://github.com/chxseh/discord-bot.git"
}
}

372
src/app.js Normal file
View File

@ -0,0 +1,372 @@
import fs from "node:fs";
import { createRequire } from "node:module";
import Discord from "discord.js";
import SQLite from "better-sqlite3";
import bootWeb from "./webfront/index.js";
import logging from "./utils/logging.js";
import common from "./utils/common.js";
const require = createRequire(import.meta.url);
const sql = new SQLite(`./src/db.sqlite`);
const client = new Discord.Client({
partials: [Discord.Partials.Channel],
intents: [
Discord.GatewayIntentBits.Guilds,
Discord.GatewayIntentBits.GuildMembers,
Discord.GatewayIntentBits.GuildBans,
Discord.GatewayIntentBits.GuildMessages,
Discord.GatewayIntentBits.MessageContent,
Discord.GatewayIntentBits.GuildMessageReactions,
Discord.GatewayIntentBits.DirectMessages,
Discord.GatewayIntentBits.GuildVoiceStates,
],
});
/**
* @name loadLocalization
* @description Loads the localization files.
*/
async function loadLocalization()
{
global.locale = {};
// Load all JSON files in ./locale and JSON parse them, then add them to the global.locale object
const locales = fs.readdirSync(`./src/locale`).filter((file) => file.endsWith(`.json`));
for (const locale of locales)
{
const data = JSON.parse(fs.readFileSync(`./src/locale/${ locale }`));
const localeName = locale.split(`.`)[0];
global.locale[localeName] = data;
}
// Get the defined locale from the config file
const { locale } = require(`./config.json`);
global.gLocale = locale;
}
await initSetup();
await loadLocalization(); // load all localizations first, so we can translate everything below.
const { token, isWorkflow, debugMode } = require(`./config.json`);
if (token.length < 50)
logging.error(`${ global.locale[global.gLocale].invalidToken } `, true);
global.reactionMessageIds = []; // React to Delete Bot Message 1
global.elevatedServers = [`808962821697306654`, `872330479565680640`]; // Prod & testing
await setupDB();
await setupCommands();
client.login(token);
bootWeb(client, Discord);
if (!isWorkflow && !debugMode)
{ // catch everything if not workflow AND not dev env
process.on(`unhandledRejection`, (error) =>
{
const d8 = new Date(Date.now());
logging.error(`${ global.locale[global.gLocale].unhandledRejection } [${ d8.toDateString() } ${ d8.toTimeString() }]: ${ error }`, false, true);
});
process.on(`uncaughtException`, (error) =>
{
const d8 = new Date(Date.now());
logging.error(`${ global.locale[global.gLocale].uncaughtException } [${ d8.toDateString() } ${ d8.toTimeString() }]: ${ error }`, false, true);
});
}
/**
* @name initSetup
* @description Setup config.json if it doesn't exist.
*/
async function initSetup()
{
if (fs.existsSync(`./src/config.json`))
{
const rawConfig = fs.readFileSync(`./src/config.json`);
const config = JSON.parse(rawConfig);
const rawDefaultConfig = fs.readFileSync(`./src/config.json.example`);
const defaultConfig = JSON.parse(rawDefaultConfig);
for (const key in defaultConfig)
{ // If we already have a config, check and see if everything from defaultConfig is set.
if (!(key in config))
config[key] = defaultConfig[key];
}
for (const key in config)
{ // Clean Legacy Config Options. (If something in config is not in defaultConfig, remove it from config).
if (!(key in defaultConfig))
delete config[key];
}
fs.writeFileSync(`./src/config.json`, JSON.stringify(config, undefined, 4));
return;
}
fs.copyFileSync(`./src/config.json.example`, `./src/config.json`);
var readlineSync = require(`readline-sync`);
let localeList = fs.readdirSync(`./src/locale`).filter((file) => file.endsWith(`.json`));
localeList = localeList.map((file) => file.split(`.`)[0]);
localeList = localeList.join(`, `);
let locale = readlineSync.question(`Please enter your locale (${ localeList }): `).trim().toLowerCase();
const locales = fs.readdirSync(`./src/locale`).filter((file) => file.endsWith(`.json`));
if (!locales.includes(`${ locale }.json`))
locale = `en`; // Set to en if the locale is not found.
// Load locales
await loadLocalization();
let token = ``;
do
{
token = readlineSync.question(`${ global.locale[locale].setupBotToken } `, {
hideEchoBack: true,
});
}
while (token.length < 50);
let clientSecret = ``;
do
{
clientSecret = readlineSync.question(`${ global.locale[locale].setupClientSecret } `, {
hideEchoBack: true,
});
}
while (clientSecret.length < 20);
let botOwners = readlineSync.question(`${ global.locale[locale].setupBotOwners } `);
if (botOwners.length > 1)
{
botOwners = botOwners.split(`,`);
for (let index = 0; index < botOwners.length; index++)
botOwners[index] = botOwners[index].trim(); // this also handles "1, 2, 3"
}
else
botOwners = [];
const playingStatus = readlineSync.question(`${ global.locale[locale].setupPlayingStatus } `);
let debugMode = readlineSync.question(`${ global.locale[locale].setupDebugMode } `);
debugMode = !(debugMode.toLowerCase() !== `true` && debugMode.toLowerCase() !== `y` && debugMode.toLowerCase !== `yes`);
let forwardDMs = readlineSync.question(`${ global.locale[locale].setupForwardDM } `);
forwardDMs = !(forwardDMs.toLowerCase() !== `true` && forwardDMs.toLowerCase() !== `y` && forwardDMs.toLowerCase !== `yes`);
// ask what port to use
let webfrontPort = readlineSync.question(`${ global.locale[locale].setupWebfrontPort } `);
webfrontPort = webfrontPort.trim();
webfrontPort = webfrontPort.length === 0 ? 5000 : Number.parseInt(webfrontPort, 10);
if (Number.isNaN(webfrontPort) || webfrontPort < 1000 || webfrontPort > 65_534)
{
webfrontPort = 5000;
logging.log(`${ global.locale[locale].setupInvalidPort }`);
}
// ask what url to use (default: http://localhost:port)
var url = readlineSync.question(`${ global.locale[locale].setupWebfrontURL.replaceAll(`%webfrontPort%`, `${ webfrontPort }`) } `);
if (url.length === 0 || !url.startsWith(`http`))
url = `http://localhost:${ webfrontPort }`;
// remove trailing slash if found
if (url.endsWith(`/`))
url = url.slice(0, Math.max(0, url.length - 1));
// ask for their webfront api key
var webfrontAPIKey = ``;
do
webfrontAPIKey = readlineSync.question(`${ global.locale[locale].setupWebfrontAPIKey } `);
while (webfrontAPIKey.length < 20);
readlineSync.question(`\n${ global.locale[locale].setupOauthRedirect.replaceAll(`%url%`, `${ url }`) }\n`);
const config = require(`./config.json`);
config.token = token.trim();
config.locale = locale;
config.botOwnerID = botOwners;
config.playingStatus = playingStatus.trim();
config.clientSecret = clientSecret.trim();
config.debugMode = debugMode;
config.forwardDMs = forwardDMs;
config.webfrontPort = webfrontPort;
config.webfrontURL = url.trim();
config.webfrontAPIKey = webfrontAPIKey.trim();
fs.writeFileSync(`./src/config.json`, JSON.stringify(config, undefined, 4)); // save settings to config
}
/**
* @name setupCommands
* @description Loads and prepares Chat/Slash Commands & Events.
*/
async function setupCommands()
{
// slash commands
client.commands = new Discord.Collection();
client.chatcommands = new Discord.Collection();
const commandFolders = fs.readdirSync(`./src/commands/slash_commands`);
const chatcommandFolders = fs.readdirSync(`./src/commands/chat_commands`);
let numberFailed = 0;
for (const folder of commandFolders)
{
const commandFiles = fs.readdirSync(`./src/commands/slash_commands/${ folder }`).filter((file) => file.endsWith(`.js`));
for (const file of commandFiles)
{
try
{
let command = await import(`./commands/slash_commands/${ folder }/${ file }`);
command = command.default || command;
command = await injectLocale(command);
client.commands.set(command.name, command);
if (command.elevatedOnly)
common.elevatedCommands.push(command);
else
common.commands.push(command);
}
catch (error)
{
logging.error(`${ global.locale[global.gLocale].failedToLoadCommand.replaceAll(`%file%`, file).replaceAll(`%error%`, error) }`);
numberFailed += 1;
}
}
}
for (const folder of chatcommandFolders)
{
const commandFiles = fs.readdirSync(`./src/commands/chat_commands/${ folder }`).filter((file) => file.endsWith(`.js`));
for (const file of commandFiles)
{
try
{
let command = await import(`./commands/chat_commands/${ folder }/${ file }`);
command = command.default || command;
client.chatcommands.set(command.name, command);
}
catch (error)
{
logging.error(`${ global.locale[global.gLocale].failedToLoadCommand.replaceAll(`%file%`, file).replaceAll(`%error%`, error) }`);
numberFailed += 1;
}
}
}
logging.log(`${ global.locale[global.gLocale].loadedCommands.replaceAll(`%numberFailed%`, numberFailed) }`);
// events
const eventFiles = fs.readdirSync(`./src/events`).filter((file) => file.endsWith(`.js`));
for (const file of eventFiles)
{
let event = await import(`./events/${ file }`);
event = event.default || event;
if (event.once)
client.once(event.name, (...ourArguments) => event.execute(...ourArguments, Discord, client));
else
client.on(event.name, (...ourArguments) => event.execute(...ourArguments, Discord, client));
}
}
/**
* @name setupDB
* @description Create the database if it doesn't exist.
*/
async function setupDB()
{
sql.prepare(`CREATE TABLE IF NOT EXISTS punishments (guildAndUserId TEXT, guildId TEXT, id TEXT, warns TEXT, mutes TEXT, kicks TEXT, bans TEXT, punishmentreason TEXT, punishmentinfo TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_punishments ON punishments (guildAndUserId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS mutes (guildAndUserId TEXT, guildId TEXT, userId TEXT, time TEXT, moderatorId TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_mutes ON mutes (guildAndUserId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS allowedLinks (guildId TEXT, link TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS autoBannedPhrases (guildId TEXT, phrase TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS badWords (guildId TEXT, word TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS badNames (guildId TEXT, name TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS allowedDiscords (guildId TEXT, id TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS keywords (guildId TEXT, phrase TEXT, response TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS keywordsRegional (guildId TEXT, phrase TEXT, response TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS scheduledMessages (guildId TEXT, channelId TEXT, time TEXT, content TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS memberCounts (guildId TEXT, count TEXT, time TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_mc_gid ON memberCounts (guildId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS banAppealTokens (guildId TEXT, userId TEXT, token TEXT, threadId TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS configuration (guildId TEXT PRIMARY KEY, config TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_configuration_guildId ON configuration (guildId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS keywordSpam (guildId TEXT, userId TEXT, count TEXT, time TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS doNotForwardDMsFrom (userId TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS userNotes (guildAndUserId TEXT, userId TEXT, guildId TEXT, note TEXT, meta TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_userNotes_guildAndUserId ON userNotes (guildAndUserId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS bannedServers (guildId TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS tempbanUsers (guildAndUserId TEXT, guildId TEXT, userId TEXT, time TEXT, moderatorId TEXT);`).run();
sql.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tempbans ON tempbanUsers (guildAndUserId);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS rolePersists (guildId TEXT, userId TEXT, roleArray TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS giveaways (guildId TEXT, messageId TEXT, time TEXT, amountOfWinners TEXT, channelId TEXT);`).run();
sql.prepare(`CREATE TABLE IF NOT EXISTS giveawayEntries (guildId TEXT, messageId TEXT, userId TEXT);`).run();
sql.pragma(`synchronous = 1`);
sql.pragma(`journal_mode = wal`);
process.on(`exit`, () => sql.close());
process.on(`SIGHUP`, () => process.exit(128 + 1));
process.on(`SIGINT`, () => process.exit(128 + 2));
process.on(`SIGTERM`, () => process.exit(128 + 15));
// punishment variables (quick access)
global.getInfoById = sql.prepare(`SELECT * FROM punishments WHERE guildId = ? AND id = ?;`);
global.setInfo = sql.prepare(`INSERT OR REPLACE INTO punishments (guildAndUserId, guildId, id, warns, mutes, kicks, bans, punishmentreason, punishmentinfo) VALUES (@guildAndUserId, @guildId, @id, @warns, @mutes, @kicks, @bans, @punishmentreason, @punishmentinfo);`);
// guild configuration variables
global.getConfiguration = sql.prepare(`SELECT * FROM configuration WHERE guildId = ?`);
global.setConfiguration = sql.prepare(`INSERT OR REPLACE INTO configuration (guildId, config) VALUES (@guildId, @config);`);
}
/**
* @name injectLocale
* @description Inject Localization into Slash Commands (https://discord.com/developers/docs/interactions/application-commands#localization)
* @param {object} command The command object to inject the locale into.
*/
async function injectLocale(command)
{
// https://discord.com/developers/docs/reference#locales
const discordLocales = [`da`, `de`, `en-GB`, `en-US`, `es-ES`, `fr`, `hr`, `it`, `lt`, `hu`, `nl`, `no`, `pl`, `pt-BR`, `ro`, `fi`, `sv-SE`, `vi`, `tr`, `cs`, `el`, `bg`, `ru`, `uk`, `hi`, `th`, `zh-CN`, `ja`, `zh-TW`, `ko`];
command.nameLocalizations = {};
command.descriptionLocalizations = {};
for (const locale of discordLocales)
{
let splitLocale = locale;
if (splitLocale.includes(`-`))
splitLocale = splitLocale.split(`-`)[0];
// Inject nameLocalizations & descriptionLocalizations into the command
let commandName = `${ command.name }CommandName`;
commandName = commandName.replaceAll(` `, `_`);
const commandDescription = `${ command.name }CommandDescription`;
const workingLocale = global.findLocale(undefined, splitLocale);
if (workingLocale !== `en`)
{
// Add commandName to the findLocale
command.nameLocalizations = { ...command.nameLocalizations, [locale]: global.findLocaleString(workingLocale, commandName) };
if (command.description)
// eslint-disable-next-line max-len
command.descriptionLocalizations = { ...command.descriptionLocalizations, [locale]: global.findLocaleString(workingLocale, commandDescription) };
// For each option in the command, inject the locale into the option
if (command.options)
{
for (let index = 0; index < command.options.length; index++)
{
const option = command.options[index];
let optionName = `${ command.name }OptionName${ index }`;
optionName = optionName.replaceAll(` `, `_`);
const optionDescription = `${ command.name }OptionDescription${ index }`;
if (workingLocale !== `en`)
{
// Add optionName to the findLocale
option.nameLocalizations = { ...option.nameLocalizations, [locale]: global.findLocaleString(workingLocale, optionName) };
if (option.description)
// eslint-disable-next-line max-len
option.descriptionLocalizations = { ...option.descriptionLocalizations, [locale]: global.findLocaleString(workingLocale, optionDescription) };
}
}
}
}
}
return command;
}

View File

@ -0,0 +1,14 @@
import { banlist } from "../../commands/informational/banlist.js";
import common from "../../../utils/common.js";
export default {
name: `banlist`,
aliases: [`bl`],
async execute(message)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
banlist(message, false);
}
};

View File

@ -0,0 +1,23 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { webfrontURL } = require(`../../../config.json`);
export default {
name: `epoch`,
async execute(message)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].seeWebfrontEpoch.replaceAll(`%webfrontURL%`, `${ webfrontURL }`) }`)
.then((message) =>
{
setTimeout(() =>
{
message.delete();
}, 15_000);
});
}
};

View File

@ -0,0 +1,17 @@
import { createRequire } from "node:module";
import { guildlist } from "../../commands/informational/guildlist.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `guildlist`,
aliases: [`gl`],
async execute(message, ourArguments, Discord, client)
{
if (!botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
guildlist(message, false, Discord, client, (ourArguments[0] === `true`));
}
};

View File

@ -0,0 +1,24 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { webfrontURL } = require(`../../../config.json`);
export default {
name: `help`,
aliases: [`h`, `commands`, `cmd`, `cmds`, `command`],
async execute(message)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].helpText.replaceAll(`%webfrontURL%`, `${ webfrontURL }`) }`)
.then((message) =>
{
setTimeout(() =>
{
message.delete();
}, 15_000);
});
}
};

View File

@ -0,0 +1,14 @@
export default {
name: `invite`,
async execute(message, ourArguments, Discord, client)
{
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].botInviteText }\nhttps://discord.com/api/oauth2/authorize?client_id=${ client.user.id }&permissions=8&scope=bot%20applications.commands`)
.then((message) =>
{
setTimeout(() =>
{
message.delete();
}, 10_000);
});
}
};

View File

@ -0,0 +1,14 @@
import { membercount } from "../../commands/informational/membercount.js";
export default {
name: `membercount`,
aliases: [`mc`, `members`, `membercounts`],
async execute(message)
{
// If you do not have a role, you cannot use this command.
if (message.member.roles.cache.size <= 1)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
membercount(message, false);
}
};

View File

@ -0,0 +1,14 @@
import { mutelist } from "../../commands/informational/mutelist.js";
import common from "../../../utils/common.js";
export default {
name: `mutelist`,
aliases: [`ml`],
async execute(message)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
mutelist(message, false);
}
};

View File

@ -0,0 +1,13 @@
import { ping } from "../../commands/informational/ping.js";
import common from "../../../utils/common.js";
export default {
name: `ping`,
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
ping(message, false, client);
}
};

View File

@ -0,0 +1,19 @@
import { serverinfo } from "../../commands/informational/serverinfo.js";
import common from "../../../utils/common.js";
export default {
name: `serverinfo`,
aliases: [`sinfo`, `guildinfo`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (ourArguments[0])
var guild = client.guilds.cache.get(ourArguments[0]);
else
var guild = client.guilds.cache.get(message.guild.id);
serverinfo(message, false, guild, Discord);
}
};

View File

@ -0,0 +1,18 @@
import { createRequire } from "node:module";
import version from "../../commands/informational/version.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `version`,
aliases: [`ver`],
async execute(message)
{
if (!botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
await version.getCommitInfo(message, message.channel, false);
}
};

View File

@ -0,0 +1,40 @@
import { list, add, remove } from "../../commands/manipulation/allowed-discords.js";
import common from "../../../utils/common.js";
export default {
name: `alloweddiscords`,
aliases: [`ad`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no ourArguments
if (ourArguments[0] !== `list` && !ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyDiscord }`);
switch (ourArguments[0])
{
case `list`: {
list(message, false, message.guild.id, Discord);
break;
}
case `add`: {
const invite = ourArguments[1];
add(message, false, message.guild.id, invite, Discord, client);
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
const invite = ourArguments[1];
remove(message, false, message.guild.id, invite, Discord, client);
break;
}
// No default
}
}
};

View File

@ -0,0 +1,39 @@
import { list, add, remove } from "../../commands/manipulation/allowed-links.js";
import common from "../../../utils/common.js";
export default {
name: `links`,
aliases: [`link`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no argument is given
else if (ourArguments[0] !== `list` && !ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyLink }`);
switch (ourArguments[0])
{
case `list`: {
list(message, false, Discord);
break;
}
case `add`: {
const newLink = ourArguments[1].toLowerCase();
add(message, false, message.guild.id, newLink);
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
const linkToRemove = ourArguments[1].toLowerCase();
remove(message, false, message.guild.id, linkToRemove);
break;
}
// No default
}
}
};

View File

@ -0,0 +1,40 @@
import { list, add, remove } from "../../commands/manipulation/auto-banned-phrases.js";
import common from "../../../utils/common.js";
export default {
name: `autoban`,
aliases: [`ab`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no ourArguments
if (ourArguments[0] !== `list` && !ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyPhrases }`);
switch (ourArguments[0])
{
case `list`: {
list(message, false, Discord);
break;
}
case `add`: {
const newWord = ourArguments.slice(1).join(` `).toLowerCase();
add(message, false, newWord);
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
const searchKeyword = ourArguments.slice(1).join(` `).toLowerCase();
remove(message, false, message.guild.id, searchKeyword);
break;
}
// No default
}
}
};

View File

@ -0,0 +1,67 @@
import { list, add, remove } from "../../commands/manipulation/auto-replies.js";
import common from "../../../utils/common.js";
export default {
name: `autoreply`,
aliases: [`ar`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no ourArguments
switch (ourArguments[0])
{
case `list`: {
list(message, false, message.guild.id, Discord);
break;
}
case `add`: {
if (!ourArguments[1] || !ourArguments[2] || !ourArguments[3])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyResponse }`);
if (ourArguments[1] === `true`)
{
var trigger = message.content.split(`"`)[1];
var response = message.content.split(`"`)[3];
if (trigger.length > 1900 || response.length > 1900)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].shortenTrig }`);
add(message, false, message.guild.id, trigger, response, true);
}
else if (ourArguments[1] === `false`)
{
var trigger = message.content.split(`"`)[1];
var response = message.content.split(`"`)[3];
if (trigger.length > 1900 || response.length > 1900)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].shortenTrig }`);
add(message, false, message.guild.id, trigger, response, false);
}
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
if (!ourArguments[1] || !ourArguments[2])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyResponse }`);
if (ourArguments[1] === `true`)
{
var itemToSearchFor = message.content.split(`"`)[1];
remove(message, false, message.guild.id, itemToSearchFor, true);
}
else if (ourArguments[1] === `false`)
{
var itemToSearchFor = message.content.split(`"`)[1];
remove(message, false, message.guild.id, itemToSearchFor, false);
}
break;
}
// No default
}
}
};

View File

@ -0,0 +1,40 @@
import { list, add, remove } from "../../commands/manipulation/bad-words.js";
import common from "../../../utils/common.js";
export default {
name: `badword`,
aliases: [`badwords`, `banwords`, `banword`, `bannedword`, `bannedwords`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no ourArguments
if (ourArguments[0] !== `list` && !ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyWord }`);
switch (ourArguments[0])
{
case `list`: {
list(message, false, Discord);
break;
}
case `add`: {
const newWord = ourArguments.slice(1).join(` `).toLowerCase();
add(message, false, message.guild.id, newWord);
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
const searchKeyword = ourArguments.slice(1).join(` `).toLowerCase();
remove(message, false, message.guild.id, searchKeyword);
break;
}
// No default
}
}
};

View File

@ -0,0 +1,43 @@
import { createRequire } from "node:module";
import { list, add, remove } from "../../commands/manipulation/ignore-dms.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `ignoredms`,
aliases: [`ignoredm`, `blockdm`, `blockdms`, `ignore`],
async execute(message, ourArguments, Discord)
{
if (!botOwnerID.includes(message.author.id)) // dev only cuz we're cool
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = `list`; // default to list if no ourArguments
if (ourArguments[0] !== `list` && !ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyUserID }`);
switch (ourArguments[0])
{
case `list`: {
list(message, false, Discord);
break;
}
case `add`: {
const uid = ourArguments[1];
add(message, false, uid);
break;
}
case `remove`:
case `delete`:
case `del`:
case `rm`: {
const uid = ourArguments[1];
remove(message, false, uid);
break;
}
// No default
}
}
};

View File

@ -0,0 +1,128 @@
import SQLite from "better-sqlite3";
import sqlite from "../../../utils/sqlite.js";
import mentions from "../../../utils/mentions.js";
import logging from "../../../utils/logging.js";
import common from "../../../utils/common.js";
import ban from "../../commands/moderation/ban.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `ban`,
aliases: [`b`],
async execute(message, ourArguments, Discord, client)
{
const { sendDmOnBan, banAppealsLink } = common.getGuildConfig(message.guild.id);
if (!common.canRunCommand(message, `BanMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const row = sql.prepare(`SELECT * FROM tempbanUsers WHERE guildId = ? AND userId = ?`).get(message.guild.id, id);
if (ourArguments[1])
{ // args[1] can either be a duration or a reason
const Time = ourArguments[1];
var time = await common.getEpoch(Time, false);
if (time === -1337)
{
time = -1337; // perma ban
var reason = ourArguments.slice(1).join(` `);
if (!reason || !ourArguments[1])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
}
else
{
var reason = ourArguments.slice(2).join(` `);
if (!reason || !ourArguments[2])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
if (row && row.time > 1)
{ // If they are already muted, and have a punishment, take their current mute time and add the new time to it.
time = Number.parseInt(row.time, 10) + time;
sql.prepare(`UPDATE tempbanUsers SET time = ?, moderatorId = ? WHERE guildId = ? AND userId = ?`).run(time, message.author.id, message.guild.id, id);
}
else if (row && row.time < 1)
return message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].permBanned }`);
else
{ // fresh temp ban
time += Date.now();
sql.prepare(`INSERT INTO tempbanUsers VALUES (?, ?, ?, ?, ?)`).run(`${ message.guild.id }_${ id }`, message.guild.id, id, time, message.author.id);
}
}
}
const authorTag = message.author.tag;
var isSilent = false;
if (reason.includes(`--silent`))
{
isSilent = true; // Do not send DM upon ban.
reason = reason.replace(`--silent`, ``);
}
if (time > 0)
var timeForMuteMeta = `${ global.locale[global.findLocale(message.guild.id)].for } ${ ourArguments[1] }`;
else
var timeForMuteMeta = `${ global.locale[global.findLocale(message.guild.id)].permanently }`;
const guild = client.guilds.cache.get(message.guild.id);
const member = await common.getGuildMember(client, message.guild.id, id);
if (!member)
{
// prevent double banning
let isBanned = true;
await guild.bans.fetch(id).catch(() =>
{
isBanned = false;
});
if (isBanned && time < 0)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].alreadyBanned }`);
if (isBanned && time > 0)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].banTimeUpdate.replaceAll(`%ourArguments%`, `${ ourArguments[1] }`) }`);
// They aren't in the server, try anyways
await message.guild.members.ban(id, { deleteMessageSeconds: 7 * 24 * 60 * 60, reason: `${ authorTag } | ${ reason }` }).then(async () =>
{
ban.sendBanEmbed(Discord, client, id, id, message.author.id, reason, message.guild.id, time);
var newReason = `🔨 ${ reason }`;
await sqlite.modifyPunishmentsProfile(client, id, message.author.id, message.guild.id, `bans`, 1, newReason, timeForMuteMeta);
}).catch((error) =>
{
logging.error(error);
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].mentionUser }`).then((message) =>
{
setTimeout(() =>
{
message.delete();
}, 10_000);
});
});
return; // we banned someone not in the server, so we're done here
}
if ((member.roles.highest.position >= message.member.roles.highest.position && guild.ownerId !== message.author.id) || !member.bannable)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].couldntBanUser }`);
if (sendDmOnBan && banAppealsLink && banAppealsLink.length > 1 && isSilent === false)
{
const nitroSafety = `${ global.locale[global.findLocale(message.guild.id)].nitroSafety.replaceAll(`%guildName%`, `${ guild.name }`).replaceAll(`%banAppealsLink%`, `${ banAppealsLink }`) }`;
const standardMessage = `${ global.locale[global.findLocale(message.guild.id)].standardBanDM.replaceAll(`%guildName%`, `${ guild.name }`).replaceAll(`%banAppealsLink%`, `${ banAppealsLink }`) }`;
await (reason.toLowerCase().includes(`nitro`) || reason.toLowerCase().includes(`scam`) ? member.send(nitroSafety).catch(() => logging.log(`Could not DM, ignoring`)) : member.send(standardMessage).catch(() => logging.warn(`Could not DM, ignoring`)));
}
member.ban({ deleteMessageSeconds: 7 * 24 * 60 * 60, reason: `${ authorTag } | ${ reason }` });
ban.sendBanEmbed(Discord, client, member.id, member.user.username, message.author.id, reason, message.guild.id, time);
var newReason = `🔨 ${ reason }`;
await sqlite.modifyPunishmentsProfile(client, id, message.author.id, message.guild.id, `bans`, 1, newReason, timeForMuteMeta);
}
};

View File

@ -0,0 +1,29 @@
import SQLite from "better-sqlite3";
import { createRequire } from "node:module";
import mentions from "../../../utils/mentions.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `clearhistory`,
aliases: [`ch`],
async execute(message, ourArguments)
{
if (!botOwnerID.includes(message.author.id)) // dev only
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
sql.prepare(`DELETE FROM punishments WHERE guildId = ${ message.guild.id } AND id = ${ id };`).run();
sql.prepare(`DELETE FROM mutes WHERE guildId = ${ message.guild.id } AND userId = ${ id };`).run();
sql.prepare(`DELETE FROM userNotes WHERE guildAndUserId = ?;`).run(`${ message.guild.id }_${ id }`);
}
};

View File

@ -0,0 +1,31 @@
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
import { clearWarns } from "../../commands/moderation/clearwarns.js";
export default {
name: `clearwarn`,
aliases: [`cw`, `clearwarns`, `warnclear`, `wc`, `warnsclear`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
// we don't need GuildMember for this, only their ID and to confirm they are a valid account
const member = await client.users.fetch(id)
.catch(() => message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noId }`));
let reason = ourArguments.slice(1).join(` `);
if (!reason || !ourArguments[1])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
await clearWarns(client, Discord, message.guild.id, id, member, message.author.id, reason);
}
};

View File

@ -0,0 +1,29 @@
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
import { duration } from "../../commands/moderation/duration.js";
export default {
name: `duration`,
async execute(message, ourArguments)
{
if (!common.canRunCommand(message, `ManageRoles`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyDuration }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const newDuration = ourArguments[1];
let reason = ourArguments.slice(2).join(` `);
if (!reason)
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
const time = await common.getEpoch(newDuration, true);
if (time !== -1337)
return duration(message, false, message.guild.id, id, message.author.id, time, newDuration, reason);
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badTimeUnit }`);
}
};

View File

@ -0,0 +1,39 @@
import mentions from "../../../utils/mentions.js";
import { editcase } from "../../commands/moderation/editcase.js";
import common from "../../../utils/common.js";
export default {
name: `editcase`,
aliases: [`ecase`],
async execute(message, ourArguments)
{
if (!common.canRunCommand(message, `ManageRoles`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
if (!ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyCaseNumber }`);
if (!ourArguments[2])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyReason }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
// if case id contains a period, strip it
if (ourArguments[1].includes(`.`))
ourArguments[1] = ourArguments[1].replace(`.`, ``);
if (Number.isNaN(ourArguments[1]))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyCaseNumber }`);
const caseId = ourArguments[1];
const reason = ourArguments.slice(2).join(` `);
await editcase(message, false, id, caseId, reason);
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].caseInfo.replaceAll(`%caseID%`, `${ caseId }`).replaceAll(`%id%`, `${ id }`).replaceAll(`%reason%`, `${ reason }`) }`);
}
};

View File

@ -0,0 +1,23 @@
import mentions from "../../../utils/mentions.js";
import { kick } from "../../commands/moderation/kick.js";
import common from "../../../utils/common.js";
export default {
name: `kick`,
aliases: [`k`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const reason = ourArguments.slice(1).join(` `);
kick(message, false, id, message.author.id, reason, Discord, client);
}
};

View File

@ -0,0 +1,48 @@
import { lockdown } from "../../commands/moderation/lockdown.js";
import common from "../../../utils/common.js";
export default {
name: `lockdown`,
aliases: [`lock`],
async execute(message, ourArguments, Discord, client)
{
const { lockdownChannelIDs } = common.getGuildConfig(message.guild.id);
if (!common.canRunCommand(message, `Administrator`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
let singleChannel = false;
if (ourArguments[0].startsWith(`<#`) || (ourArguments.length > 1 && ourArguments[1].startsWith(`<#`)))
singleChannel = true;
if (!singleChannel && (!lockdownChannelIDs
|| lockdownChannelIDs.length === 0))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badLockDownConfig }`);
if (ourArguments[0] !== `end`)
{
if (ourArguments[0].startsWith(`<#`))
{
var channelId = ourArguments[0].slice(2, -1);
const reason = ourArguments.slice(1).join(` `);
return lockdown(true, message, false, reason, Discord, client, channelId);
}
let reason = [...ourArguments].join(` `);
if (reason.startsWith(`start`))
reason = reason.replace(`start`, ``);
return lockdown(true, message, false, reason, Discord, client);
}
if (ourArguments[0] === `end`)
{
if (ourArguments[1].startsWith(`<#`))
{
var channelId = ourArguments[1].slice(2, -1);
const reason = ourArguments.slice(2).join(` `);
return lockdown(false, message, false, reason, Discord, client, channelId);
}
const reason = ourArguments.slice(1).join(` `);
return lockdown(false, message, false, reason, Discord, client);
}
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].validSubCommand }`);
}
};

View File

@ -0,0 +1,27 @@
import lookup from "../../commands/moderation/lookup.js";
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
export default {
name: `lookup`,
aliases: [`l`, `info`, `uinfo`, `userinfo`, `history`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
if (ourArguments[1] === `--public`)
var isPublic = true;
else
var isPublic = false;
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
return lookup.lookup(Discord, client, id, message.guild.id, isPublic, false, message);
}
};

View File

@ -0,0 +1,103 @@
import SQLite from "better-sqlite3";
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
import mute from "../../commands/moderation/mute.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `mute`,
aliases: [`m`],
async execute(message, ourArguments, Discord, client)
{
const { mutedRoleID } = common.getGuildConfig(message.guild.id);
if (!common.canRunCommand(message, `ManageRoles`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if ((!mutedRoleID)
|| mutedRoleID.length < 10)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noMuteRole }`);
const role = message.guild.roles.cache.find((r) => r.id === mutedRoleID);
if (!role)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badMuteRole }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const guild = client.guilds.cache.get(message.guild.id);
const member = await common.getGuildMember(client, message.guild.id, id);
if (member && member.roles.highest.position >= message.member.roles.highest.position && guild.ownerId !== message.author.id)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].cantMute }`);
var reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
let time = 0;
var isExtending = false;
const row = sql.prepare(`SELECT * FROM mutes WHERE guildId = ? AND userId = ?`).get(message.guild.id, id);
if (ourArguments[1])
{ // args[1] can either be a duration or a reason
const Time = ourArguments[1];
time = await common.getEpoch(Time, false);
if (time !== -1337)
{
reason = ourArguments.slice(2).join(` `);
if (!reason || !ourArguments[2])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
if (row && row.time > 1)
{ // If they are already muted, and have a punishment, take their current mute time and add the new time to it.
isExtending = true;
time = Number.parseInt(row.time, 10) + time;
await mute.sendMuteToDB(client, message.guild.id, message.author.id, id, time, reason, Time);
}
else if (row && row.time < 1)
return message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].permMute }`);
else if (member && member.roles.cache.some((r) => r.id === mutedRoleID)) // If they are muted, but do not have a punishment (i.e. a manual mute), assume the manual mute is what we want.
return message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].manualMute }`);
else
{ // fresh temp mute
await mute.sendMuteToDB(client, message.guild.id, message.author.id, id, Date.now() + time, reason, Time);
}
}
else if (row && row.time > 1)
{ // args[1] is a reason, so we perma mute
reason = ourArguments.slice(1).join(` `);
if (!reason || !ourArguments[1])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
time = -1;
await mute.sendMuteToDB(client, message.guild.id, message.author.id, id, time, reason);
}
else
{ // fresh perma mute
reason = ourArguments.slice(1).join(` `); // We don't need to sanity check this since we know args[1] to be a reason.
reason = common.truncateString(reason, 256);
time = -1;
await mute.sendMuteToDB(client, message.guild.id, message.author.id, id, time, reason);
}
}
else
{ // we did not provide args[1], so assume we want to perma mute without a reason.
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
time = -1;
await mute.sendMuteToDB(client, message.guild.id, message.author.id, id, time, reason);
}
const authorTag = message.author.tag;
// Boot them from VC
if (member && member.voice)
member.voice.disconnect();
mute.handleMute(Discord, client, member, message.author.id, role, authorTag, reason, time, isExtending, message.guild.id, id); // handles rest of mute process
}
};

View File

@ -0,0 +1,24 @@
import mentions from "../../../utils/mentions.js";
import { nick } from "../../commands/moderation/nick.js";
import common from "../../../utils/common.js";
export default {
name: `nick`,
aliases: [`n`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `ManageNicknames`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].enterNick }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const newName = ourArguments.slice(1).join(` `);
nick(message, false, id, newName, message.author.id, Discord, client);
}
};

View File

@ -0,0 +1,45 @@
import { add, remove } from "../../commands/moderation/note.js";
import common from "../../../utils/common.js";
import mentions from "../../../utils/mentions.js";
export default {
name: `note`,
aliases: [`notes`],
async execute(message, ourArguments)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noteSpecifyArgs }`);
if (!ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyUser }`);
var id = ourArguments[1];
if (id[0] === `<`)
id = mentions.getUserFromMention(id);
if (ourArguments[0] === `add`)
{
if (!ourArguments[2])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyNote }`);
const noteText = ourArguments.slice(2).join(` `);
add(message, false, message.guild.id, id, message.author.id, noteText);
}
else if ((ourArguments[0] === `remove`) || ourArguments[0] === `delete` || ourArguments[0] === `del` || ourArguments[0] === `rm`)
{
if (!ourArguments[2])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyNoteID }`);
// if case id contains a period, strip it
if (ourArguments[2].includes(`.`))
ourArguments[2] = ourArguments[2].replace(`.`, ``);
if (Number.isNaN(ourArguments[2]))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badNoteID }`);
const noteId = ourArguments[2];
remove(message, false, message.guild.id, id, message.author.id, noteId);
}
}
};

View File

@ -0,0 +1,37 @@
import mentions from "../../../utils/mentions.js";
import { user, amount, after } from "../../commands/moderation/prune.js";
import common from "../../../utils/common.js";
export default {
name: `prune`,
aliases: [`purge`, `p`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `ManageMessages`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (ourArguments[0] && /^\d+$/.test(ourArguments[0]))
{ // Prune X amount of messages
const amountInt = Number.parseInt(ourArguments[0], 10);
const reason = ourArguments.slice(1).join(` `);
amount(message, false, amountInt, reason, message.author.id, client, Discord);
}
else if (ourArguments[0] && ourArguments[0][0] === `<`)
{ // Prune X amount of messages from a specific user
var id = ourArguments[0];
id = mentions.getUserFromMention(ourArguments[0]);
const amount = ourArguments[1];
const reason = ourArguments.slice(2).join(` `);
user(message, false, id, amount, reason, message.author.id, client, Discord);
}
else if (ourArguments[0] && ourArguments[0] === `after`)
{
// Prune messages after a specific message
var messageId = ourArguments[1];
var reason = `No reason provided`;
if (ourArguments[2])
reason = ourArguments.slice(2).join(` `);
after(message, false, messageId, reason, message.author.id, client, Discord);
}
}
};

View File

@ -0,0 +1,27 @@
import { start, end } from "../../commands/moderation/slowmode.js";
import common from "../../../utils/common.js";
export default {
name: `slowmode`,
aliases: [`slow`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `ManageChannels`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifySlowTime }`);
const friendlyTime = ourArguments[0]; // "1h" or whatever
if (/^\d+[hms]$/.test(friendlyTime)) // we know its in the right format, and not a reason.
{
const reason = ourArguments.slice(1).join(` `);
start(message, false, friendlyTime, message.author.id, message.channel.id, reason, Discord, client);
}
else
{ // we assume this to be a reason to remove slowmode?
const reason = [...ourArguments].join(` `);
end(message, false, message.author.id, message.channel.id, reason, Discord, client);
}
}
};

View File

@ -0,0 +1,23 @@
import mentions from "../../../utils/mentions.js";
import { softban } from "../../commands/moderation/softban.js";
import common from "../../../utils/common.js";
export default {
name: `softban`,
aliases: [`sb`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `BanMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const reason = ourArguments.slice(1).join(` `);
softban(message, false, id, message.author.id, reason, Discord, client);
}
};

View File

@ -0,0 +1,28 @@
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
import unban from "../../commands/moderation/unban.js";
export default {
name: `unban`,
aliases: [`ub`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `BanMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
let reason = ourArguments.slice(1).join(` `);
if (!reason || !ourArguments[1])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
const authorTag = message.author.tag;
await unban.unbanUser(message, id, message.author.id, authorTag, reason, Discord, client);
},
};

View File

@ -0,0 +1,33 @@
import mentions from "../../../utils/mentions.js";
import unmute from "../../commands/moderation/unmute.js";
import common from "../../../utils/common.js";
export default {
name: `unmute`,
aliases: [`um`],
async execute(message, ourArguments, Discord, client)
{
const { mutedRoleID } = common.getGuildConfig(message.guild.id);
if (!common.canRunCommand(message, `ManageRoles`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if ((!mutedRoleID)
|| mutedRoleID.length < 10)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noMuteRole }`);
const mutedRole = message.guild.roles.cache.get(mutedRoleID);
if (!mutedRole)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badMuteRole }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const reason = ourArguments.slice(1).join(` `);
unmute.unmute(message, false, id, message.author.id, reason, client, Discord);
}
};

View File

@ -0,0 +1,29 @@
import mentions from "../../../utils/mentions.js";
import common from "../../../utils/common.js";
import warn from "../../commands/moderation/warn.js";
export default {
name: `warn`,
aliases: [`w`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].mentionUser }`);
let reason = ourArguments.slice(1).join(` `);
if (!reason
|| !ourArguments[1])
reason = `${ global.locale[global.findLocale(message.guild.id)].noReasonSpecified }`;
reason = common.truncateString(reason, 256);
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(id);
await warn.warnUser(message, false, false, id, message.author.id, reason, undefined, Discord, client);
}
};

View File

@ -0,0 +1,22 @@
import SQLite from "better-sqlite3";
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `bat`,
async execute(message, ourArguments, Discord, client)
{
if (!message.channel.isThread())
return;
if (message.author.id !== client.user.id)
return; // only the bot can use this command
const guildId = message.guild.id;
const userId = ourArguments[0];
const token = ourArguments[1];
const threadId = ourArguments[2];
sql.prepare(`INSERT INTO banAppealTokens (guildId, userId, token, threadId) VALUES (?, ?, ?, ?)`).run(guildId, userId, token, threadId);
}
};

View File

@ -0,0 +1,35 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `broadcast`,
async execute(message, ourArguments, Discord, client)
{
if (!botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noMessage }`);
const messageToSend = ourArguments.join(` `);
if (messageToSend.length > 2000)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].messageTooLong }`);
const guilds = client.guilds.cache.map((guild) => guild.id);
for (const guildId of guilds)
{
const guild = client.guilds.cache.get(guildId);
const { auditChannelID } = common.getGuildConfig(guildId);
if (auditChannelID)
{
const auditChannel = guild.channels.cache.get(auditChannelID);
if (auditChannel)
auditChannel.send(messageToSend);
}
}
}
};

View File

@ -0,0 +1,46 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
const debugUsers = [];
export default {
name: `debug`,
async execute(message, ourArguments, Discord, client)
{
if (!botOwnerID.includes(message.author.id)) // dev only cuz we're cool
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
const { auditChannelID } = common.getGuildConfig(message.guild.id);
if (!auditChannelID || auditChannelID.length < 10)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].debugBadSetup }`);
const auditChannel = client.channels.cache.get(auditChannelID);
// temp table for debugging
if (!debugUsers.includes(message.author.id))
{
debugUsers.push(message.author.id);
return auditChannel.send(`<@${ message.author.id }> ${ global.locale[global.findLocale(message.guild.id)].enabledDebug }`);
}
debugUsers.splice(debugUsers.indexOf(message.author.id), 1);
auditChannel.send(`<@${ message.author.id }> ${ global.locale[global.findLocale(message.guild.id)].disabledDebug }`);
},
/**
* @name isDebugUser
* @description Checks if the user is in debug mode.
* @param {string} id The ID of the user.
* @returns {boolean} Whether or not the user is in debug mode.
*/
isDebugUser(id)
{
if (debugUsers.includes(id))
return true;
return false;
},
debugUsers
};

View File

@ -0,0 +1,31 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `edit`,
aliases: [`e`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `Administrator`)
&& !botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].messageToEdit }`);
if (!ourArguments[1])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badEdit }`);
const messageToEdit = ourArguments[0];
const content = ourArguments.slice(1).join(` `);
var message = await client.channels.cache.get(message.channel.id).messages.fetch(messageToEdit);
if (!message)
return;
if (message.author.id === client.user.id && content.length <= 2000)
message.edit(content);
}
};

View File

@ -0,0 +1,22 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
import embed from "../../commands/utility/embed.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `embed`,
async execute(message, ourArguments, Discord, client)
{
if ((!common.canRunCommand(message, `ADMINISTRATOR`)
&& !botOwnerID.includes(message.author.id) && message.author.id !== client.user.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
ourArguments[0] = message.channel.id;
else if (ourArguments[0].startsWith(`<#`))
ourArguments[0] = ourArguments[0].slice(2, -1);
embed.embedGenerator(message, false, ourArguments[0], Discord);
}
};

View File

@ -0,0 +1,22 @@
import { en } from "../../commands/utility/en.js";
import common from "../../../utils/common.js";
export default {
name: `en`,
async execute(message, ourArguments)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].translate }`);
const { DeepLAPIKey } = common.getGuildConfig(message.guild.id);
if (!DeepLAPIKey)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].deeplNoKey }`);
const sourceText = ourArguments.join(` `);
await en(message, false, sourceText);
}
};

View File

@ -0,0 +1,23 @@
export default {
name: `eval`,
// Here we actually want to pass Discord & client in so we can use them in the eval code.
// eslint-disable-next-line no-unused-vars
async execute(message, ourArguments, Discord, client)
{
if (message.author.id !== `666843518802591764`)
return;
let evalResult;
try
{
// Yes yes I know how dangerous eval is.
// eslint-disable-next-line no-eval
evalResult = eval(ourArguments.join(` `));
message.channel.send(`\`\`\`js\n${ evalResult }\n\`\`\``);
}
catch
{
message.channel.send(`\`ERROR\` \`\`\`xl\n${ evalResult }\n\`\`\``);
}
}
};

View File

@ -0,0 +1,93 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
import logging from "../../../utils/logging.js";
import unban from "../../commands/moderation/unban.js";
const require = createRequire(import.meta.url);
const { clientSecret, token } = require(`../../../config.json`);
export default {
name: `forcejoin`,
async execute(message, ourArguments, Discord, client)
{
if (message.author.id !== client.user.id)
return; // only the bot can use this command
const userId = ourArguments[0];
const token = ourArguments[1];
const successForceJoin = await this.forceJoinUser(message, userId, token, undefined, Discord, client);
if (!successForceJoin.success)
message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].somethingWentWrong } ${ successForceJoin.error }`);
},
async forceJoinUser(interactionOrMessage, userId, refreshToken, reason, Discord, client)
{
if (!refreshToken || !userId)
return { success: false, error: `Missing token or user ID.` };
const { banAppealsLink } = common.getGuildConfig(interactionOrMessage.guild.id);
const clientId = client.user.id;
// get fresh & new access token
/* eslint-disable camelcase */
const oauthData = await common.fetchJsonResponse(
`${ common.DISCORD_API_URL }/oauth2/token`,
{
method: `POST`,
body: new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
refresh_token: refreshToken,
grant_type: `refresh_token`
}),
headers: {
'Content-Type': `application/x-www-form-urlencoded`,
}
}
);
/* eslint-enable camelcase */
if (!oauthData)
return { success: false, error: `Failed to regenerate oauth token for ${ userId }.` };
if (!reason)
reason = `Auto Mod: Solved captcha for account age.`;
const accessToken = oauthData.access_token;
await unban.unbanUser(interactionOrMessage, userId, client.user.id, client.user.tag, reason, Discord, client).catch(() =>
{
logging.warn(`Could not unban or user is not banned.`);
});
await common.sleep(1500); // give discord time to process
// before sending the PUT request, add the user to a whitelist list where they can bypass future alt checks
common.whitelistedAltUsers.push(userId);
await common.sleep(1500); // give some time to process
/* eslint-disable camelcase */
const unbanRequest = await common.fetchJsonResponse(
`${ common.DISCORD_API_URL }/guilds/${ interactionOrMessage.guild.id }/members/${ userId }`,
{
method: `PUT`,
body: JSON.stringify({
access_token: accessToken
}),
headers: {
Authorization: `Bot ${ token }`,
'Content-Type': `application/json`
}
}
);
/* eslint-enable camelcase */
if (!unbanRequest)
return { success: false, error: `Could not PUT user ${ userId } into guild.` };
const member = await common.getGuildMember(client, interactionOrMessage.guild.id, userId, true);
if (member && banAppealsLink && banAppealsLink.length > 1)
await member.send(`:partying_face: ${ global.locale[global.findLocale(interactionOrMessage.guild.id)].unbanDM } <${ banAppealsLink }/revoke-permissions>`).catch(() => logging.warn(`Could not DM, ignoring`));
return { success: true };
}
};

View File

@ -0,0 +1,38 @@
import { createRequire } from "node:module";
import logging from "../../../utils/logging.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `generateinvite`,
aliases: [`geninv`, `getinv`, `getinvite`, `geninvite`],
async execute(message, ourArguments, Discord, client)
{
if (!botOwnerID.includes(message.author.id)) // dev only cuz we're cool
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].specifyGuild }`);
const guild = client.guilds.cache.get(ourArguments[0]);
if (!guild)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noGuild.replaceAll(`%ourArgs%`, `${ ourArguments[0] }`) } `);
// Find the first channel & make an invite link
const channel = guild.channels.cache.find((c) => c.type === `GUILD_TEXT`);
if (!channel)
return message.channel.send(`<@!${ message.author.id }>,${ global.locale[global.findLocale(message.guild.id)].noTextChan.replaceAll(`%guildName%`, `${ guild.name }`) } `);
const invite = await channel.createInvite({
maxAge: 0,
maxUses: 0
}).catch((error) =>
{
logging.error(error);
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noInvite.replaceAll(`%guildName%`, `${ guild.name }`) }`);
});
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission.replaceAll(`%guildName%`, `${ guild.name }`).replaceAll(`%invite%`, `${ invite.url }`) }`);
}
};

View File

@ -0,0 +1,21 @@
import { respond } from "../../commands/utility/manually-respond.js";
import common from "../../../utils/common.js";
export default {
name: `respond`,
aliases: [`r`, `trigger`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (ourArguments.length === 0)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noTrigger }`);
const trigger = ourArguments.join(` `);
if (message.type === `REPLY`) // reply w image + caption
respond(message, false, trigger, message.author.id, message.reference.messageId, Discord);
else
respond(message, false, trigger, message.author.id, undefined, Discord);
}
};

View File

@ -0,0 +1,26 @@
import mentions from "../../../utils/mentions.js";
import { getAVI } from "../../commands/utility/pfp.js";
import common from "../../../utils/common.js";
export default {
name: `pfp`,
aliases: [`avi`, `avatar`],
async execute(message, ourArguments, Discord, client)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
{ // If no user is mentioned, use the author.
const self = await client.users.fetch(message.author.id, true);
return getAVI(message, false, self);
}
var id = ourArguments[0];
if (id[0] === `<`)
id = mentions.getUserFromMention(ourArguments[0]);
const member = await client.users.fetch(id, true);
return getAVI(message, false, member);
},
};

View File

@ -0,0 +1,32 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `remindme`,
aliases: [`remind`],
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
const Time = ourArguments[0];
const messageToSend = ourArguments.slice(1).join(` `);
if (messageToSend.length > 2000)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].messageTooLong }`);
const time = await common.getEpoch(Time, true);
if (time === -1337)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].badTimeUnit }`);
// insert into db
sql.prepare(`INSERT INTO scheduledmessages (guildId, channelId, time, content) VALUES (?, ?, ?, ?)`).run(message.guild.id, message.channel.id, time, `<@!${ message.author.id }>, ${ messageToSend }`);
const embed = new Discord.EmbedBuilder()
.setColor(`#00ff00`)
.setTimestamp()
.setAuthor({ name: `${ global.locale[global.findLocale(message.guild.id)].reminderScheduled }` })
.setDescription(`${ global.locale[global.findLocale(message.guild.id)].reminderDescription.replaceAll(`%messageAuthorUsername%`, `${ message.author.username }`) } <t:${ Math.round(time / 1000) }:R> ${ global.locale[global.findLocale(message.guild.id)].reminderDescription2.replaceAll(`%messageToSend%`, `${ messageToSend }`) }`);
message.channel.send({ embeds: [embed] });
}
};

View File

@ -0,0 +1,16 @@
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `restart`,
async execute(message)
{
if (!botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
await message.delete();
process.exit(); // requires pm2 to be installed, and the bot to be running in pm2.
}
};

View File

@ -0,0 +1,9 @@
import sale from "../../commands/utility/sale.js";
export default {
name: `sale`,
async execute(message, ourArguments, Discord, client)
{
sale.checkSteamMW2Sale(message.channel.id, client, false);
}
};

View File

@ -0,0 +1,26 @@
import { createRequire } from "node:module";
import { say } from "../../commands/utility/say.js";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `say`,
aliases: [`s`],
async execute(message, ourArguments)
{
if (!common.canRunCommand(message, `Administrator`)
&& !botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (ourArguments.length === 0)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].sayError }`);
const messageToSay = ourArguments.join(` `);
if (message.type === `REPLY`) // reply w image + caption
say(message, false, messageToSay, message.reference.messageId);
else
say(message, false, messageToSay);
}
};

View File

@ -0,0 +1,43 @@
import SQLite from "better-sqlite3";
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
const require = createRequire(import.meta.url);
const { botOwnerID, webfrontURL } = require(`../../../config.json`);
const sql = new SQLite(`./src/db.sqlite`);
export default {
name: `schedule`,
async execute(message, ourArguments, Discord)
{
if (!common.canRunCommand(message, `Administrator`)
&& !botOwnerID.includes(message.author.id))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[2]) return message.channel.send(`${ global.locale[global.findLocale(message.guild.id)].seeWebfrontEpoch.replaceAll(`%webfrontURL%`, `${ webfrontURL }`) }`);
const time = ourArguments[0];
let channel = ourArguments[1];
const messageToSend = ourArguments.slice(2).join(` `);
if (channel.includes(`<#`))
channel = channel.replace(/<#/g, ``).replace(/>/g, ``);
if (messageToSend.length > 2000)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].messageTooLong }`);
// if time is not just numbers
if (!/^\d+$/.test(time))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].epochFormat.replaceAll(`%webfrontURL%`, `${ webfrontURL }`) }`);
// insert into db
sql.prepare(`INSERT INTO scheduledmessages (guildId, channelId, time, content) VALUES (?, ?, ?, ?)`).run(message.guild.id, channel, time, messageToSend);
const embed = new Discord.EmbedBuilder()
.setColor(`#00ff00`)
.setTimestamp()
.setAuthor({ name: `${ global.locale[global.findLocale(message.guild.id)].messageScheduled }` })
.addFields([{ name: `${ global.locale[global.findLocale(message.guild.id)].channel }`, value: `<#${ channel }>`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(message.guild.id)].time }`, value: `<t:${ time / 1000 }> (<t:${ time / 1000 }:R>)`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(message.guild.id)].message }`, value: messageToSend, inline: true }]);
message.channel.send({ embeds: [embed] });
}
};

View File

@ -0,0 +1,24 @@
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
import { sendDm } from "../../commands/utility/send-dm.js";
const require = createRequire(import.meta.url);
const { botOwnerID } = require(`../../../config.json`);
export default {
name: `dm`,
async execute(message, ourArguments, Discord, client)
{
if (!botOwnerID.includes(message.author.id)) // dev only cuz we're cool
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (!ourArguments[0])
return;
let messageToDm = ourArguments.slice(1).join(` `);
messageToDm = common.truncateString(messageToDm, 1024);
const user = await client.users.fetch(ourArguments[0]);
await sendDm(message, false, messageToDm, user, Discord, client);
}
};

View File

@ -0,0 +1,31 @@
import common from "../../../utils/common.js";
export default {
name: `suggest`,
aliases: [`sg`],
async execute(message, ourArguments, Discord, client)
{
const { suggestionChannelID } = common.getGuildConfig(message.guild.id);
if (!common.canRunCommand(message, `KickMembers`))
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noPermission }`);
if (ourArguments.length === 0)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].noSuggestion }`);
if (!suggestionChannelID
|| suggestionChannelID.length < 10)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].suggestionSetup }`);
const votingChannel = client.channels.cache.get(suggestionChannelID);
if (!votingChannel)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].invalidSugChan }`);
const suggestion = ourArguments.join(` `);
if (suggestion.length > 2000)
return message.channel.send(`<@!${ message.author.id }>, ${ global.locale[global.findLocale(message.guild.id)].sugTooLong }`);
votingChannel.send(ourArguments.join(` `)).then((message) =>
{
message.react(`👍`);
message.react(`👎`);
});
}
};

View File

@ -0,0 +1,16 @@
import common from "../../../utils/common.js";
export default {
banlist
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
*/
export async function banlist(interactionOrMessage, isSlashCommand)
{
const banArray = await common.getAllGuildBans(interactionOrMessage.guild);
const message = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].amountOfBans.replaceAll(`%count%`, `${ banArray.length }`) }`;
common.sendInteractionOrMessage(interactionOrMessage, { content: message }, isSlashCommand);
}

View File

@ -0,0 +1,49 @@
import st from "string-table";
import common from "../../../utils/common.js";
export default {
guildlist
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {Discord} Discord The Discord client.
* @param {Discord.client} client The client.
* @param {boolean} onlyCount Should we only return the number of guilds it is in?
*/
export async function guildlist(interactionOrMessage, isSlashCommand, Discord, client, onlyCount = false)
{
const guilds = client.guilds.cache.map((guild) => guild.id);
if (onlyCount)
{
const lengthMessage = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].amountOfGuilds.replaceAll(`%count%`, `${ guilds.length }`) }`;
return common.sendInteractionOrMessage(interactionOrMessage, { content: lengthMessage }, isSlashCommand);
}
const data = [];
for (const guildId of guilds)
{
data.push({
guildName: client.guilds.cache.get(guildId).name,
guildId,
guildOwner: await client.guilds.cache.get(guildId).fetchOwner().then((owner) => owner.user.tag),
memberCount: await client.guilds.cache.get(guildId).memberCount,
channelCount: await client.guilds.cache.get(guildId).channels.cache.size,
roleCount: await client.guilds.cache.get(guildId).roles.cache.size,
});
}
const testString = `\`\`\`${ st.create(data) }\n\`\`\``;
if (testString.length < 2000)
{
const string = `\`\`\`${ st.create(data) }\n\`\`\``;
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
}
else
{
const string = `${ st.create(data) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `guildlist.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
}

View File

@ -0,0 +1,41 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
membercount
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
*/
export async function membercount(interactionOrMessage, isSlashCommand)
{
const guildId = interactionOrMessage.guild.id;
const guild = interactionOrMessage.guild;
const memberCount = guild.memberCount;
const memberCounts = await sql.prepare(`SELECT * FROM memberCounts WHERE guildId = ?`).get(guildId);
const time = Date.now();
if (memberCounts)
{
const oldCount = Math.round(memberCounts.count);
const oldTime = memberCounts.time;
let string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].memberCount1.replaceAll(`%guildName%`, `${ guild.name }`).replaceAll(`%memberCount%`, `${ memberCount }`) }\n`;
if (oldCount > memberCount)
string += `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].memberCount2.replaceAll(`%dec%`, `${ oldCount - memberCount }`) }`;
else if (oldCount < memberCount)
string += `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].memberCount3.replaceAll(`%inc%`, `${ memberCount - oldCount }`) }`;
if (oldCount !== memberCount)
string += ` from <t:${ Math.round(oldTime / 1000) }:R>`;
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
await sql.prepare(`INSERT OR REPLACE INTO memberCounts (guildId, count, time) VALUES (?, ?, ?);`).run(guildId, memberCount, time);
}
else
{
const string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].memberCount1.replaceAll(`%guildName%`, `${ guild.name }`).replaceAll(`%memberCount%`, `${ memberCount }`) }`;
await sql.prepare(`INSERT INTO memberCounts (guildId, count, time) VALUES (?, ?, ?);`).run(guildId, memberCount, time);
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
}
}

View File

@ -0,0 +1,28 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
mutelist
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
*/
export async function mutelist(interactionOrMessage, isSlashCommand)
{
const guildId = interactionOrMessage.guild.id;
const totalMutes = sql.prepare(`SELECT COUNT(*) count FROM mutes WHERE guildId = ?`).get(guildId);
const totalMutesCount = totalMutes.count;
const temporaryMutes = sql.prepare(`SELECT COUNT(*) count FROM mutes WHERE guildId = ? AND time > 1`).get(guildId);
const temporaryMutesCount = temporaryMutes.count;
const permMutes = sql.prepare(`SELECT COUNT(*) count FROM mutes WHERE guildId = ? AND time < 0`).get(guildId);
const permMutesCount = permMutes.count;
const message = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].amountOfMutes.replaceAll(`%totalMutesCount%`, `${ totalMutesCount }`).replaceAll(`%temporaryMutesCount%`, `${ temporaryMutesCount }`).replaceAll(`%permMutesCount%`, `${ permMutesCount }`) }`;
common.sendInteractionOrMessage(interactionOrMessage, { content: message }, isSlashCommand);
}

View File

@ -0,0 +1,18 @@
import common from "../../../utils/common.js";
export default {
ping
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {*} client The client.
*/
export async function ping(interactionOrMessage, isSlashCommand, client)
{
const ping = `${ Date.now() - interactionOrMessage.createdTimestamp }ms.`;
const apiPing = `${ Math.round(client.ws.ping) }ms.`;
const message = `🏓 ${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].latencyIs } ${ ping } ${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].api } ${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].latencyIs } ${ apiPing }`;
common.sendInteractionOrMessage(interactionOrMessage, { content: message }, isSlashCommand);
}

View File

@ -0,0 +1,29 @@
import common from "../../../utils/common.js";
export default {
serverinfo
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {Discord.Guild} guild The guild.
* @param {Discord} Discord The Discord object.
*/
export async function serverinfo(interactionOrMessage, isSlashCommand, guild, Discord)
{
var owner = await guild.fetchOwner();
const embed = new Discord.EmbedBuilder()
.setColor(`#0e9af9`)
.setTimestamp()
.setAuthor({ name: `🔎 (${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].serverInfo })` })
.setThumbnail(guild.iconURL())
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].serverName }`, value: `${ guild.name } (${ guild.id })`, inline: false }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].owner }`, value: `${ owner.user.tag } (${ owner.user.id })`, inline: false }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].memberCount }`, value: `${ guild.memberCount }`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].channelCount }`, value: `${ guild.channels.cache.size }`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].roleCount }`, value: `${ guild.roles.cache.size }`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].emojiCount }`, value: `${ guild.emojis.cache.size }`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].createdOn }`, value: `<t:${ Math.round(guild.createdTimestamp / 1000) }>`, inline: true }]);
common.sendInteractionOrMessage(interactionOrMessage, { embeds: [embed] }, isSlashCommand);
}

View File

@ -0,0 +1,63 @@
import { execSync } from "node:child_process";
import { createRequire } from "node:module";
import common from "../../../utils/common.js";
import logging from "../../../utils/logging.js";
const require = createRequire(import.meta.url);
const { clientSecret, debugMode } = require(`../../../config.json`);
export default {
/**
* @name getCommitInfo
* @description Gets the commit info for boot messages / version command.
* @param {*} interactionOrMessage The interaction or message that called this function.
* @param {*} auditChannel The audit channel to send the message to.
* @param {boolean} isSlashCommand Whether or not this function was called from a slash command.
* @param {boolean} botOnline Was this function called on the bot's startup?
*/
async getCommitInfo(interactionOrMessage, auditChannel, isSlashCommand, botOnline = false)
{
const guildId = botOnline ? auditChannel.guild.id : interactionOrMessage.guild.id;
const { banAppealsChannelID, banAppealsLink } = common.getGuildConfig(guildId);
var gitMessage = execSync(`git show -s --format="*%s* (**%an**)"`).toString();
// Don't @everyone/@here
if (gitMessage.includes(`@everyone`) || gitMessage.includes(`@here`))
gitMessage = gitMessage.replace(/@/g, `@${ String.fromCodePoint(8203) }`);
if (gitMessage.length > 1024)
gitMessage = common.truncateString(gitMessage, 1024);
// bot online message
if (botOnline)
{
// don't send anything if the bot is in debug mode
if (debugMode)
return;
auditChannel.send(`${ global.locale[global.findLocale(guildId)].botIsOnline } ${ gitMessage }`);
return;
}
const versionString = `**BOT:** ${ gitMessage }`;
if (!banAppealsChannelID || !clientSecret)
return common.sendInteractionOrMessage(interactionOrMessage, { content: versionString }, isSlashCommand);
let banAppealsInfo = await common.fetchJsonResponse(
`${ banAppealsLink }/commit-info?authKey=${ clientSecret }`,
{
methods: `GET`
}
).catch(() => { logging.warn(`Ban Appeals API: Failed to get commit info.`); });
if (!banAppealsInfo)
{
banAppealsInfo = {
message: `Failed to reach ban appeals API.`
};
}
common.sendInteractionOrMessage(interactionOrMessage, { content: `${ versionString }\n**APPEALS:** ${ banAppealsInfo.message }` }, isSlashCommand);
}
};

View File

@ -0,0 +1,141 @@
import SQLite from "better-sqlite3";
import st from "string-table";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
list,
add,
remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The guild ID.
* @param {*} Discord The Discord object.
*/
export async function list(interactionOrMessage, isSlashCommand, guildId, Discord)
{
const allowedDiscords = sql.prepare(`SELECT * FROM allowedDiscords WHERE guildId = ${ guildId }`).raw().all();
if (allowedDiscords.length === 0)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noDiscordsInList
}, isSlashCommand);
}
const servers = [];
for (const allowedDiscord of allowedDiscords)
{
servers.push({
id: allowedDiscord[1]
});
}
let string = `\`\`\`${ st.create(servers) }\`\`\``;
if (string.length < 2000)
return common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
string = `${ st.create(servers) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `allowed-discords.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The guild ID.
* @param {string} inviteString The invite string.
* @param {Discord} Discord The Discord object.
* @param {Discord.client} client The client.
*/
export async function add(interactionOrMessage, isSlashCommand, guildId, inviteString, Discord, client)
{
const discordInvite = await client.fetchInvite(inviteString);
if (!discordInvite)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].invalidDiscordInvite
}, isSlashCommand);
}
// If discord invite is already in db, return
const allowedDiscords = sql.prepare(`SELECT * FROM allowedDiscords WHERE guildId = ${ guildId }`).raw().all();
for (const allowedDiscord of allowedDiscords)
{
if (guildId === allowedDiscord[1])
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].serverAlreadyInList
}, isSlashCommand);
}
}
const data = {
guildId,
id: discordInvite.guild.id
};
try
{
sql.prepare(`INSERT INTO allowedDiscords (guildId, id) VALUES (@guildId, @id)`).run(data);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].addedToList.replaceAll(`%item%`, `${ discordInvite.guild.name }`) }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The guild ID.
* @param {string} inviteString The invite string.
* @param {Discord} Discord The Discord object.
* @param {Discord.client} client The client.
*/
export async function remove(interactionOrMessage, isSlashCommand, guildId, inviteString, Discord, client)
{
const regex = /^\d*$/;
let discordInvite = {
guild: {
guildId,
id: undefined
}
};
if (inviteString.includes(`.`))
{ // Is a discord invite link
discordInvite = await client.fetchInvite(inviteString);
if (!discordInvite)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].invalidDiscordInvite
}, isSlashCommand);
}
var guildIdToRm = discordInvite.guild.id;
}
else if (regex.test(inviteString))
{ // Is a guildId
var guildIdToRm = inviteString;
}
try
{
sql.prepare(`DELETE FROM allowedDiscords WHERE id = ? AND guildId = ?`).run(guildIdToRm, guildId);
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].removedFromList.replaceAll(`%item%`, `${ guildIdToRm }`) }, isSlashCommand);
}
catch (error)
{
console.error(error);
}
}

View File

@ -0,0 +1,84 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
list, add, remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {Discord} Discord The Discord object.
*/
export async function list(interactionOrMessage, isSlashCommand, Discord)
{
const approvedLinksH = sql.prepare(`SELECT link FROM allowedLinks WHERE guildId = ${ interactionOrMessage.guild.id }`).raw().all();
const approvedLinks = approvedLinksH.flat();
if (approvedLinks.length === 0)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noLinksInList
}, isSlashCommand);
}
let string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].allowedLinks } \`${ approvedLinks.join(`\`, \``) }\``;
if (string.length < 2000)
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
else
{
string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].allowedLinks } \n${ approvedLinks.join(`\n`) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `allowed-links.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The guild ID.
* @param {string} link The link to add.
*/
export async function add(interactionOrMessage, isSlashCommand, guildId, link)
{
if (link.length > 1900)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].itemTooLong
}, isSlashCommand);
}
try
{
sql.prepare(`INSERT INTO allowedLinks (link, guildId) VALUES (?, ?)`).run(link, guildId);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].addedToList.replaceAll(`%item%`, `${ link }`) }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The guild ID.
* @param {string} link The link to remove.
*/
export async function remove(interactionOrMessage, isSlashCommand, guildId, link)
{
try
{
sql.prepare(`DELETE FROM allowedLinks WHERE link = ? AND guildId = ?`).run(link, guildId);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].removedFromList.replaceAll(`%item%`, `${ link }`) }, isSlashCommand);
}

View File

@ -0,0 +1,84 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
list, add, remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {*} Discord The Discord Object.
*/
export async function list(interactionOrMessage, isSlashCommand, Discord)
{
const autoBanWordsH = sql.prepare(`SELECT phrase FROM autoBannedPhrases WHERE guildId = ${ interactionOrMessage.guild.id }`).raw().all();
const autoBanWords = autoBanWordsH.flat();
if (autoBanWords.length === 0)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noAutoBannedPhrases
}, isSlashCommand);
}
let string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoBannedPhrases } \`${ autoBanWords.join(`\`, \``) }\``;
if (string.length < 2000)
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
else
{
string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoBannedPhrases } \n${ autoBanWords.join(`\n`) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `auto-banned-phrases.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} phrase The auto banned phrase to add.
*/
export async function add(interactionOrMessage, isSlashCommand, phrase)
{
if (phrase.length > 1900)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].itemTooLong
}, isSlashCommand);
}
try
{
sql.prepare(`INSERT INTO autoBannedPhrases (guildId, phrase) VALUES (?, ?)`).run(interactionOrMessage.guild.id, phrase);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].addedToList.replaceAll(`%item%`, `${ phrase }`) }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {string} phrase The phrase to remove.
*/
export async function remove(interactionOrMessage, isSlashCommand, guildId, phrase)
{
try
{
sql.prepare(`DELETE FROM autoBannedPhrases WHERE guildId = ? AND phrase = ?`).run(guildId, phrase);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].removedFromList.replaceAll(`%item%`, `${ phrase }`) }, isSlashCommand);
}

View File

@ -0,0 +1,155 @@
import SQLite from "better-sqlite3";
import st from "string-table";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
clear, list, add, remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
*/
export async function clear(interactionOrMessage, isSlashCommand, guildId)
{
sql.prepare(`DELETE FROM keywords WHERE guildId = ${ guildId }`).run();
sql.prepare(`DELETE FROM keywordsRegional WHERE guildId = ${ guildId }`).run();
common.sendInteractionOrMessage(interactionOrMessage, {
content: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].clearedKeywords }`
}, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {*} Discord The Discord Object.
*/
export async function list(interactionOrMessage, isSlashCommand, guildId, Discord)
{
const responseObject = sql.prepare(`SELECT * FROM keywords WHERE guildId = ${ guildId }`).raw().all();
const responseObjectRegional = sql.prepare(`SELECT * FROM keywordsRegional WHERE guildId = ${ guildId }`).raw().all();
const data = [];
for (const element of responseObject)
{
data.push({
trigger: element[1],
response: element[2]
});
}
const dataRegional = [];
for (const element of responseObjectRegional)
{
dataRegional.push({
trigger: element[1],
response: element[2]
});
}
let string = ``;
if (data.length > 0)
string += `**${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].keywords }**\n\`\`\`${ st.create(data) }\n\`\`\``;
if (dataRegional.length > 0)
string += `\n**${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].keywordsRegional }**\n\`\`\`${ st.create(dataRegional) }\n\`\`\``;
if (string === ``)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noKeywords
}, isSlashCommand);
}
if (string.length < 2000)
return common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].keywords }\n${ st.create(data) }\n\n${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].keywordsRegional }\n${ st.create(dataRegional) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `auto-replies.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {string} trigger The trigger to add.
* @param {string} response The response to add.
* @param {boolean} isRegional Whether or not the keyword is regional.
*/
export async function add(interactionOrMessage, isSlashCommand, guildId, trigger, response, isRegional)
{
const responseObject = sql.prepare(`SELECT * FROM keywords WHERE guildId = ${ guildId }`).raw().all();
const responseObjectRegional = sql.prepare(`SELECT * FROM keywordsRegional WHERE guildId = ${ guildId }`).raw().all();
if (isRegional)
{
if (responseObjectRegional.some((x) => x[1] === trigger))
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].triggerAlreadyExists
}, isSlashCommand);
}
sql.prepare(`INSERT INTO keywordsRegional (guildId, phrase, response) VALUES (?, ?, ?)`).run(guildId, trigger, response);
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoReplyAdded
}, isSlashCommand);
}
if (responseObject.some((x) => x[1] === trigger))
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].triggerAlreadyExists
}, isSlashCommand);
}
sql.prepare(`INSERT INTO keywords (guildId, phrase, response) VALUES (?, ?, ?)`).run(guildId, trigger, response);
common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoReplyAdded
}, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {string} trigger The trigger to remove.
* @param {boolean} isRegional Whether or not the keyword is regional.
*/
export async function remove(interactionOrMessage, isSlashCommand, guildId, trigger, isRegional)
{
if (isRegional)
{
const responseObject = sql.prepare(`SELECT * FROM keywordsRegional WHERE guildId = ${ guildId }`).raw().all();
if (!responseObject.some((x) => x[1] === trigger))
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].triggerDoesNotExist
}, isSlashCommand);
}
sql.prepare(`DELETE FROM keywordsRegional WHERE guildId = ${ guildId } AND phrase = ?`).run(trigger);
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoReplyRemoved
}, isSlashCommand);
}
const responseObject = sql.prepare(`SELECT * FROM keywords WHERE guildId = ${ guildId }`).raw().all();
if (!responseObject.some((x) => x[1] === trigger))
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].triggerDoesNotExist
}, isSlashCommand);
}
sql.prepare(`DELETE FROM keywords WHERE guildId = ${ guildId } AND phrase = ?`).run(trigger);
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].autoReplyRemoved
}, isSlashCommand);
}

View File

@ -0,0 +1,87 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
list, add, remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {*} Discord The Discord Object.
*/
export async function list(interactionOrMessage, isSlashCommand, Discord)
{
const badWordsH = sql.prepare(`SELECT word FROM badWords WHERE guildId = ${ interactionOrMessage.guild.id }`).raw().all();
const badWords = badWordsH.flat();
if (badWords.length === 0)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noBadWords
}, isSlashCommand);
}
let string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].badWords } \`${ badWords.join(`\`, \``) }\``;
if (string.length < 2000)
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
else
{
string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].badWords } \n${ badWords.join(`\n`) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `bad-words.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {string} phrase The phrase to add.
*/
export async function add(interactionOrMessage, isSlashCommand, guildId, phrase)
{
if (phrase.length > 1900)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].itemTooLong
}, isSlashCommand);
}
try
{
sql.prepare(`INSERT INTO badWords (guildId, word) VALUES (?, ?)`).run(guildId, phrase);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, { content: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].badWordAdded } \`${ phrase }\`` }, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {string} guildId The Guild ID.
* @param {string} phrase The phrase to remove.
*/
export async function remove(interactionOrMessage, isSlashCommand, guildId, phrase)
{
try
{
sql.prepare(`DELETE FROM badWords WHERE guildId = ? AND word = ?`).run(guildId, phrase);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, {
content: `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].badWordRemoved } \`${ phrase }\``
}, isSlashCommand);
}

View File

@ -0,0 +1,85 @@
import SQLite from "better-sqlite3";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
list, add, remove
};
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {*} Discord The Discord Object.
*/
export async function list(interactionOrMessage, isSlashCommand, Discord)
{
const dmListH = sql.prepare(`SELECT userId FROM doNotForwardDMsFrom`).raw().all();
const dmList = dmListH.flat();
if (dmList.length === 0)
{
return common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].noIgnoredDMs
}, isSlashCommand);
}
let string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].ignoredDMs } \`${ dmList.join(`\`, \``) }\``;
if (string.length < 2000)
common.sendInteractionOrMessage(interactionOrMessage, { content: string }, isSlashCommand);
else
{
string = `${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].ignoredDMs } \n${ dmList.join(`\n`) }`;
// send message as attachment
const attachment = new Discord.AttachmentBuilder(Buffer.from(string), { name: `ignore-dms.txt` });
common.sendInteractionOrMessage(interactionOrMessage, { files: [attachment] }, isSlashCommand);
}
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {number} uid The user ID.
*/
export async function add(interactionOrMessage, isSlashCommand, uid)
{
const dmListH = sql.prepare(`SELECT userId FROM doNotForwardDMsFrom`).raw().all();
const dmList = dmListH.flat();
if (dmList.includes(uid))
return common.sendInteractionOrMessage(interactionOrMessage, { content: `<@!${ uid }> ${ global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].isAlreadyIgnored }` }, isSlashCommand);
try
{
sql.prepare(`INSERT INTO doNotForwardDMsFrom (userId) VALUES (?)`).run(uid);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].userIgnored
}, isSlashCommand);
}
/**
* @param {*} interactionOrMessage The interaction or message object.
* @param {boolean} isSlashCommand Whether or not the interaction is a slash command.
* @param {number} uid The user ID.
*/
export async function remove(interactionOrMessage, isSlashCommand, uid)
{
try
{
sql.prepare(`DELETE FROM doNotForwardDMsFrom WHERE userId = ?`).run(uid);
}
catch (error)
{
return console.error(error);
}
common.sendInteractionOrMessage(interactionOrMessage, {
content: global.locale[global.findLocale(interactionOrMessage.guild.id, interactionOrMessage.locale)].userUnignored
}, isSlashCommand);
}

View File

@ -0,0 +1,100 @@
import { AuditLogEvent } from "discord.js";
import sqlite from "../../../utils/sqlite.js";
import common from "../../../utils/common.js";
export default {
guildBanAddEvent,
sendBanEmbed,
banAndSendEmbed,
};
/**
* @name guildBanAddEvent
* @description Event that fires when a user is banned from a guild.
* @param {*} Discord The Discord Object.
* @param {*} client The Discord Client Object.
* @param {*} data The data object.
*/
async function guildBanAddEvent(Discord, client, data)
{
const guildId = data.guild.id;
const guild = client.guilds.cache.get(guildId);
const user = data.user;
const fetchedLogs = await guild.fetchAuditLogs({
limit: 1,
type: AuditLogEvent.MemberBanAdd
});
const banLog = fetchedLogs.entries.first();
if (!banLog)
return; // no ban log, something went wrong?
const { executor, target, action } = banLog;
if (executor.id === client.user.id)
return; // dont send embed if bot banned
if (target.id !== user.id)
return; // dont send embed if user wasn't banned
if (action !== AuditLogEvent.MemberBanAdd)
return; // dont send embed if user wasn't banned
var reason = banLog.reason;
if (!reason)
reason = `${ global.locale[global.findLocale(guildId)].noReasonSpecified }`;
var newReason = `🔨 ${ reason }`;
await sqlite.modifyPunishmentsProfile(client, target.id, executor.id, guild.id, `bans`, 1, newReason, `permanently`);
sendBanEmbed(Discord, client, target.id, target.username, executor.id, reason, guildId);
}
/**
* @param {*} Discord The Discord Object.
* @param {*} client The Discord Client Object.
* @param {string} userId The user ID.
* @param {string} displayName The user display name.
* @param {string} moderatorId The moderator ID.
* @param {string} reason The reason for the ban.
* @param {Discord.GuildMember} member The guild member object.
* @name banAndSendEmbed
* @description Ban the user and send an embed.
*/
function banAndSendEmbed(Discord, client, userId, displayName, moderatorId, reason, member)
{
const authorTag = client.users.cache.get(moderatorId).tag;
member.ban({ deleteMessageSeconds: 7 * 24 * 60 * 60, reason: `${ authorTag } | ${ reason }` });
sendBanEmbed(Discord, client, userId, displayName, moderatorId, reason, member.guild.id);
}
/**
* @name sendBanEmbed
* @description Send an embed to the audit channel when a user is banned.
* @param {*} Discord The Discord Object.
* @param {*} client The Discord Client Object.
* @param {string} userId The user's id.
* @param {string} displayName The user's display name.
* @param {string} moderatorId The moderator's id.
* @param {string} reason The reason for the ban.
* @param {string} guildId The guild's id.
* @param {number} time Time in Epoch format.
*/
function sendBanEmbed(Discord, client, userId, displayName, moderatorId, reason, guildId, time)
{
const { auditChannelID } = common.getGuildConfig(guildId);
if (!auditChannelID || auditChannelID.length < 10) return; // no audit channel set, dont try and send embed
const auditChannel = client.channels.cache.get(auditChannelID);
if (!auditChannel) // auditChannelID is invalid.
return;
const embed = new Discord.EmbedBuilder()
.setColor(`#ff1e00`)
.setTimestamp()
.setAuthor({ name: `🔨 (${ global.locale[global.findLocale(guildId)].ban })` })
.addFields([{ name: `${ global.locale[global.findLocale(guildId)].user }`, value: `<@!${ userId }> (${ displayName })`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(guildId)].moderator }`, value: `<@!${ moderatorId }>`, inline: true }]);
if (time && time > 0)
embed.addFields([{ name: `${ global.locale[global.findLocale(guildId)].expires }`, value: `<t:${ Math.round(time / 1000) }:R>`, inline: true }]);
embed.addFields([{ name: `${ global.locale[global.findLocale(guildId)].reason }`, value: (reason || `${ global.locale[global.findLocale(guildId)].noReasonSpecified }`), inline: true }]);
auditChannel.send({ embeds: [embed] });
}

View File

@ -0,0 +1,65 @@
import SQLite from "better-sqlite3";
import sqlite from "../../../utils/sqlite.js";
import common from "../../../utils/common.js";
const sql = new SQLite(`./src/db.sqlite`);
export default {
clearWarns,
};
/**
* @param {*} client The Discord Client Object.
* @param {*} Discord The Discord Object.
* @param {string} guildId The Guild ID.
* @param {string} id The ID of the user to clear warns for.
* @param {Discord.GuildMember} member The GuildMember object.
* @param {string} authorId The ID of the user who issued the command.
* @param {string} reason The reason for the clear.
*/
export async function clearWarns(client, Discord, guildId, id, member, authorId, reason)
{
var warnings = sqlite.getPunishmentData(guildId, id, `warns`);
if (warnings >= 1)
{
const data = global.getInfoById.get(guildId, id);
var punishmentinfo = JSON.parse(data.punishmentinfo);
var punishmentreason = JSON.parse(data.punishmentreason);
for (let index = 0; index < punishmentreason.length;)
{
if (punishmentreason[index].includes(``))
{
punishmentreason.splice(index, 1);
punishmentinfo.splice(index, 1);
// apply data
data.punishmentinfo = JSON.stringify(punishmentinfo);
data.punishmentreason = JSON.stringify(punishmentreason);
data.warns -= 1;
global.setInfo.run(data);
}
else
index += 1; // only up 1 index if no warning reason was found. this solves the issue of back-to-back warnings and we skip over 1
}
}
// If the user has 0 history now, delete their record.
if (!sqlite.hasRecord(guildId, id))
sql.prepare(`DELETE FROM punishments WHERE guildId = ? AND id = ?`).run(guildId, id);
const { auditChannelID } = common.getGuildConfig(guildId);
if (!auditChannelID || auditChannelID.length < 10) return; // no audit channel ID, dont try and send an embed
const auditChannel = client.channels.cache.get(auditChannelID);
if (!auditChannel) // auditChannelID is invalid.
return;
const embed = new Discord.EmbedBuilder()
.setColor(`#00FF00`)
.setTimestamp()
.setAuthor({ name: `⚠️ (${ global.locale[global.findLocale(guildId)].clearWarns })` })
.addFields([{ name: `${ global.locale[global.findLocale(guildId)].user }`, value: `<@!${ member.id }> (${ member.username })`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(guildId)].moderator }`, value: `<@!${ authorId }>`, inline: true }])
.addFields([{ name: `${ global.locale[global.findLocale(guildId)].reason }`, value: reason, inline: true }]);
auditChannel.send({ embeds: [embed] });
}

Some files were not shown because too many files have changed in this diff Show More