Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 304503f7d8 | |||
| 7b504a2312 | |||
| 8039f8f1c7 | |||
| 46e5079eaf | |||
| 8580dda6c8 | |||
| 211e07f5f0 | |||
| 531167f2ba | |||
| f7c63c8705 | |||
| acfe540148 | |||
| 23a282a8a5 | |||
| fc941ad487 | |||
| e198ca3b4d | |||
| fb963cc153 | |||
| 20a2665615 | |||
| c342f4741a | |||
| f147761d35 | |||
| 7ed4318863 | |||
| 5d5db3be40 | |||
| e88b33c4ed | |||
| b10b126dc9 | |||
| d15431b299 | |||
| 4cf3efa584 | |||
| 93251348a3 | |||
| df9a3a0eba | |||
| 35517d791f | |||
| 5d05306b4d | |||
| 1fd3953852 | |||
| 8fb269e6c1 | |||
| 910f5ef609 | |||
| e389d5caae | |||
| 94301bb3f5 | |||
| fd60089d39 | |||
| 91982da762 | |||
| 4fcb45823d | |||
| efcf613ea4 | |||
| 231ceeee9b | |||
| a6d55ada75 | |||
| c3e6e0823b | |||
| c330af29fa | |||
| 9d819253e0 | |||
| bb7c66d8db | |||
| 5e71af6072 | |||
| e5889e3c63 | |||
| 22c215f3c2 | |||
| ba340e7b8e | |||
| 1b77a24fb7 | |||
| 8878ac76e8 | |||
| 6fad8c6cdc | |||
| 232a8ebc21 | |||
| 0d663b444c | |||
| 414b94285c | |||
| 340551fbb2 | |||
| eddf16a821 | |||
| 80f89d0117 | |||
| d06cac6f3d | |||
| 70fa5aa892 | |||
| a0dc9a5cf5 | |||
| 3bdc03f553 | |||
| 43cfb5a7fe | |||
| 7e2664083e | |||
| 37d1d9ce1f | |||
| 7f876f4555 | |||
| 0381272d7d | |||
| e6936a5364 | |||
| 0e34a0a4bb | |||
| e843275ba1 | |||
| 28bd2f46cb | |||
| e0c0dff82d | |||
| 326d5a15f2 | |||
| 3def31b0fb | |||
| b536ebda5b | |||
| 6175a30974 | |||
| 8d5f75e471 | |||
| e4fbcca6c4 | |||
| 2cbfc7f354 | |||
| ed315d0cb1 | |||
| d898398780 | |||
| 3a35b829ad | |||
| 8148890574 | |||
| 09901b5e58 | |||
| 4b75dfa885 | |||
| d0528c3637 | |||
| c3fa30ea64 | |||
| 709c8cfab7 | |||
| 7aa3d5d0a1 | |||
| 8e5931e0d4 | |||
| e4c389be71 | |||
| be83b9ea74 | |||
| ecbfc2bc2a | |||
| 00df6074d6 | |||
| 23f081c6c1 | |||
| 1a0817a89c | |||
| e5d520a73b | |||
| fdcb56998c | |||
| 28443611e4 | |||
| f231df89d8 | |||
| 0c2eae76aa | |||
| 7b967cca8c | |||
| f3ce349ceb | |||
| 9d9a1447f3 | |||
| 70fc12d458 | |||
| 65470f2c57 | |||
| 38d90d7f6b | |||
| 8f7ed605a4 | |||
| 2f9d29d891 | |||
| ecb50148b9 | |||
| 806d70b292 | |||
| 798aaef4ea | |||
| 4c20d08471 | |||
| 550eb79374 | |||
| b11d14b72d | |||
| 444bad7935 | |||
| 6700e36907 | |||
| 66dbdceb2a | |||
| 5a9b91ade1 | |||
| 13553d7e5a | |||
| 388bc4d021 | |||
| 2a3b53ea08 | |||
| 99af5ca8b4 | |||
| 7003351ecc | |||
| bfa6e10011 | |||
| 22f2ac58df | |||
| 3dcc4e021f | |||
| d1e2152de9 | |||
| 1a024b216a | |||
| 0106983a5f | |||
| caefe0e228 | |||
| 928f12fe01 | |||
| ae9ce308b2 | |||
| 2ea94092db | |||
| 3f5cc0a9ff | |||
| 545756b762 | |||
| d61ffbaaf7 | |||
| 15d39e3381 | |||
| 97b4136b64 | |||
| 82f65a800c | |||
| f995d9a643 | |||
| ff3a423a30 | |||
| 7321f4535b | |||
| c562c06925 | |||
| b28e193b55 | |||
| 6e062ffef2 | |||
| f3051ec944 | |||
| cb834cb7ee | |||
| 0cfa91eb10 | |||
| 17a9bd4ea2 | |||
| 18ef6e56c2 | |||
| 1bff94e71b | |||
| 10021d6926 | |||
| 38fdc07e4e | |||
| e99e6b2e1f | |||
| 98acb652af | |||
| c8d059e36d | |||
|
|
7c09aaffaa | ||
|
|
08618e2be2 | ||
|
|
200468ac10 | ||
|
|
efdc605dc0 | ||
|
|
2c75ff09d9 | ||
|
|
4e2e8bc702 | ||
|
|
53625be91f | ||
|
|
755c8510d7 |
48
.gitea/workflows/dev-docker.yml
Normal file
48
.gitea/workflows/dev-docker.yml
Normal file
@ -0,0 +1,48 @@
|
||||
name: NodBot PE Dockerization
|
||||
######################################################
|
||||
# Creates a new Docker image of nodbot-dev, pushes #
|
||||
# it to DockerHub, and restarts the container. #
|
||||
######################################################
|
||||
|
||||
# Run when a new tag for dev is pushed
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*-dev*'
|
||||
|
||||
# Pull DockerHub credentials from secrets
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Clean up old environment
|
||||
run: |
|
||||
rm -rf /var/lib/act_runner/nodbot
|
||||
rm -rf /var/lib/act_runner/nodbot-dev
|
||||
- name: Pull latest from Git
|
||||
run: |
|
||||
echo "Ref: ${{ gitea.ref_name }}"
|
||||
echo "Branch: tags/${{ gitea.ref_name }}"
|
||||
cd /var/lib/act_runner
|
||||
git clone https://git.vfsh.dev/voidf1sh/nodbot
|
||||
cd nodbot
|
||||
git checkout tags/${{ gitea.ref_name }}
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker build --target dev . --file Dockerfile --tag v0idf1sh/nodbot:dev
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker push v0idf1sh/nodbot:dev
|
||||
- name: Restart the container
|
||||
run: |
|
||||
cd /srv/docker/nodbot-dev
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
45
.gitea/workflows/pe-docker.yml
Normal file
45
.gitea/workflows/pe-docker.yml
Normal file
@ -0,0 +1,45 @@
|
||||
name: NodBot PE Dockerization
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*-pe'
|
||||
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Pull latest from Git
|
||||
run: |
|
||||
echo "Branch: ${{ gitea.ref_name }}"
|
||||
pwd
|
||||
whoami
|
||||
mkdir -p /var/lib/act_runner/
|
||||
cd /var/lib/act_runner/
|
||||
if [ ! -d "nodbot" ]; then
|
||||
git clone https://git.vfsh.dev/voidf1sh/nodbot
|
||||
cd nodbot
|
||||
else
|
||||
cd nodbot
|
||||
git pull
|
||||
fi
|
||||
git checkout ${{ gitea.ref_name }}
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker build . --file Dockerfile --tag v0idf1sh/nodbot-pe
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker push v0idf1sh/nodbot-pe
|
||||
- name: Restart the container
|
||||
run: |
|
||||
cd /srv/docker/nodbot-pe
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
47
.gitea/workflows/production-docker.yml
Normal file
47
.gitea/workflows/production-docker.yml
Normal file
@ -0,0 +1,47 @@
|
||||
name: NodBot Production Dockerization
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*-prod*'
|
||||
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Pull latest from Git
|
||||
run: |
|
||||
echo "Branch: ${{ gitea.ref_name }}"
|
||||
pwd
|
||||
whoami
|
||||
mkdir -p /var/lib/act_runner/
|
||||
cd /var/lib/act_runner/
|
||||
if [ ! -d "nodbot" ]; then
|
||||
git clone https://git.vfsh.dev/voidf1sh/nodbot
|
||||
cd nodbot
|
||||
else
|
||||
rm -rf nodbot
|
||||
git clone https://git.vfsh.dev/voidf1sh/nodbot
|
||||
cd nodbot
|
||||
git pull
|
||||
fi
|
||||
git checkout ${{ gitea.ref_name }}
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker build --target prod . --file Dockerfile --tag v0idf1sh/nodbot:prod
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: |
|
||||
cd /var/lib/act_runner/nodbot
|
||||
docker push v0idf1sh/nodbot:prod
|
||||
- name: Restart the container
|
||||
run: |
|
||||
cd /srv/docker/nodbot
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
35
.github/workflows/pe-docker.yml
vendored
35
.github/workflows/pe-docker.yml
vendored
@ -1,35 +0,0 @@
|
||||
name: NodBot Production Dockerization
|
||||
|
||||
on:
|
||||
commit:
|
||||
branches:
|
||||
- pe
|
||||
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Pull latest from Git
|
||||
run: |
|
||||
pwd
|
||||
whoami
|
||||
cd /root/nodbot
|
||||
git pull
|
||||
git checkout pe
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /root/nodbot
|
||||
docker build . --file Dockerfile --tag v0idf1sh/nodbot-pe
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: |
|
||||
cd /root/nodbot
|
||||
docker push v0idf1sh/nodbot-pe
|
||||
35
.github/workflows/production-docker.yml
vendored
35
.github/workflows/production-docker.yml
vendored
@ -1,35 +0,0 @@
|
||||
name: NodBot Production Dockerization
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
|
||||
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Pull latest from Git
|
||||
run: |
|
||||
pwd
|
||||
whoami
|
||||
cd /root/nodbot
|
||||
git pull
|
||||
git checkout $GITHUB_HEAD_REF
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
cd /root/nodbot
|
||||
docker build . --file Dockerfile --tag v0idf1sh/nodbot
|
||||
- name: Log into Docker Hub
|
||||
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
|
||||
- name: Push image to Docker Hub
|
||||
run: |
|
||||
cd /root/nodbot
|
||||
docker push v0idf1sh/nodbot
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,8 +2,8 @@
|
||||
.vscode
|
||||
package-lock.json
|
||||
.VSCodeCounter/
|
||||
env.dev
|
||||
env.prod
|
||||
.env*
|
||||
*.env
|
||||
|
||||
# Custom folders
|
||||
# gifs/*
|
||||
@ -115,3 +115,4 @@ dist
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
.DS_Store
|
||||
|
||||
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
@ -0,0 +1,51 @@
|
||||
## v3.6.x
|
||||
#### v3.6.1 (#32) Improving Requests
|
||||
* Start logging message URLs and timestamps when requests are submitted.
|
||||
* Plan to improve request submission process to interactive slash-command.
|
||||
|
||||
#### v3.6.0 (#31) Fixing GIF APIs
|
||||
* Switch from Tenor to Giphy for GIFs
|
||||
|
||||
## v3.5.x
|
||||
#### v3.5.0 (#30) The April Fools Update
|
||||
* Add April Fools stuff
|
||||
|
||||
## v3.4.x
|
||||
#### v3.4.3 (#29)
|
||||
* Fix `.gif` replies to redirect to the original replyee (#26)
|
||||
|
||||
#### v3.4.2 (#28)
|
||||
* Added `.drink` and aliases
|
||||
* Changed `.joint` responses to be standalone instead of replies
|
||||
|
||||
#### v3.4.1 (#27)
|
||||
* Adding command to change nicknames
|
||||
|
||||
#### v3.4.0 (#25)
|
||||
* Added nested commands, enclose a command in brackets, braces, or parenthesis inside a longer message: `You really don't get it do you? [that's the joke.gif] You're so dense` would return the results for just `that's the joke.gif`
|
||||
* Improved the `/save gifsearch` interface and the code behind the scenes
|
||||
|
||||
## v3.3.x
|
||||
#### v3.3.3 (#20)
|
||||
* Fixed content-list slash commands `/gifs`, `/pastas`, `/joints`, `/requests` (#19)
|
||||
* Fixed the creation of duplicate commands properly (#18)
|
||||
* Added a ton of aliases for `.gif` (`.wav`, `.mp3`, `.mp4`, `.wmv`, etc.)
|
||||
* Added alias lists in `/help`
|
||||
|
||||
#### v3.3.2 (#17)
|
||||
* Fixed the `/help` command to not crash the bot (#15)
|
||||
* Filtered out duplicate commands from the `/help` list, temporary fix (#18)
|
||||
* Removed instances of `MessageEmbed.addField` due to deprecation (#16)
|
||||
|
||||
v3.3.1 - Polishing and bugfixing for new AvWx commands
|
||||
v3.3.0 - Added `.metar`, `.atis`, and `.datis` AvWx commands
|
||||
|
||||
## v3.0.x
|
||||
v3.0.1 - Migrate TenorJS API Endpoint
|
||||
v3.0.2 - Add medical advice commands
|
||||
v3.0.3 - Fix broken `/requests` command
|
||||
v3.0.4 - Add ability to use multiple aliases
|
||||
v3.0.5 - Add ability to save strains
|
||||
v3.0.6 - Move `.strain` to `/strain` and add Autocomplete
|
||||
v3.0.7 - Add `.spongebob` replies
|
||||
v3.0.8 - Add ability to open requests by pages
|
||||
211
CustomModules/ButtonHandlers.js
Normal file
211
CustomModules/ButtonHandlers.js
Normal file
@ -0,0 +1,211 @@
|
||||
const customEmbeds = require('../CustomModules/Embeds.js');
|
||||
const InteractionStorage = require('../CustomModules/InteractionStorage.js');
|
||||
const indexer = require('../CustomModules/Indexer.js');
|
||||
const fn = require('../functions.js');
|
||||
const { GifData } = require('./NodBot.js');
|
||||
|
||||
module.exports = {
|
||||
baseEvent(interaction) {
|
||||
let iStorage;
|
||||
if (interaction.client.iStorage.has(interaction.message.interaction.id)) {
|
||||
iStorage = interaction.client.iStorage.get(interaction.message.interaction.id)
|
||||
} else {
|
||||
iStorage = new InteractionStorage(interaction.message.interaction.id, interaction);
|
||||
iStorage.page = 0;
|
||||
}
|
||||
if (interaction.user.id !== iStorage.userId) return;
|
||||
switch (interaction.component.customId) {
|
||||
// Any of the gifsPage Buttons
|
||||
case 'prevGifsPage':
|
||||
module.exports.gifsPage(interaction);
|
||||
break;
|
||||
case 'nextGifsPage':
|
||||
module.exports.gifsPage(interaction);
|
||||
break;
|
||||
case 'prevPastasPage':
|
||||
module.exports.pastasPage(interaction);
|
||||
break;
|
||||
case 'nextPastasPage':
|
||||
module.exports.pastasPage(interaction);
|
||||
break;
|
||||
case 'prevRequestsPage':
|
||||
module.exports.requestsPage(interaction);
|
||||
break;
|
||||
case 'nextRequestsPage':
|
||||
module.exports.requestsPage(interaction);
|
||||
break;
|
||||
case 'prevJointsPage':
|
||||
module.exports.jointsPage(interaction);
|
||||
break;
|
||||
case 'nextJointsPage':
|
||||
module.exports.jointsPage(interaction);
|
||||
break;
|
||||
case 'prevGif':
|
||||
module.exports.gifSearchPage(interaction);
|
||||
break;
|
||||
case 'nextGif':
|
||||
module.exports.gifSearchPage(interaction);
|
||||
break;
|
||||
case 'confirmGif':
|
||||
module.exports.gifSearchPage(interaction);
|
||||
break;
|
||||
case 'cancelGif':
|
||||
module.exports.gifSearchPage(interaction);
|
||||
break;
|
||||
case 'closeRequests':
|
||||
module.exports.closeRequests(interaction);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
gifsPage(interaction) {
|
||||
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
|
||||
|
||||
switch (interaction.component.customId) {
|
||||
case 'prevGifsPage':
|
||||
if (iStorage.page > 0) {
|
||||
iStorage.page = iStorage.page - 1;
|
||||
}
|
||||
break;
|
||||
case 'nextGifsPage':
|
||||
if (iStorage.page < interaction.client.gifs.size / 10) {
|
||||
iStorage.page = iStorage.page + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const indexedGifs = indexer(interaction.client.gifs, iStorage.page);
|
||||
indexedGifs.gifsString = new String();
|
||||
|
||||
for (const gif of indexedGifs.thisPage) {
|
||||
indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`;
|
||||
}
|
||||
|
||||
interaction.update(fn.embeds.gifs({command: "/gifs", author: interaction.user.username}, indexedGifs));
|
||||
},
|
||||
pastasPage(interaction) {
|
||||
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
|
||||
|
||||
switch (interaction.component.customId) {
|
||||
case 'prevPastasPage':
|
||||
if (iStorage.page > 0) {
|
||||
iStorage.page = iStorage.page - 1;
|
||||
}
|
||||
break;
|
||||
case 'nextPastasPage':
|
||||
if (iStorage.page < interaction.client.pastas.size / 10) {
|
||||
iStorage.page = iStorage.page + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const indexedPastas = indexer(interaction.client.pastas, iStorage.page);
|
||||
indexedPastas.pastasString = new String();
|
||||
|
||||
for (const pasta of indexedPastas.thisPage) {
|
||||
indexedPastas.pastasString += `${pasta.name}.pasta\n`;
|
||||
}
|
||||
|
||||
interaction.update(fn.embeds.pastas({command: "/pastas", author: interaction.user.username}, indexedPastas));
|
||||
},
|
||||
requestsPage(interaction) {
|
||||
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
|
||||
|
||||
switch (interaction.component.customId) {
|
||||
case 'prevRequestsPage':
|
||||
if (iStorage.page > 0) {
|
||||
iStorage.page = iStorage.page - 1;
|
||||
}
|
||||
break;
|
||||
case 'nextRequestsPage':
|
||||
if (iStorage.page < interaction.client.requests.size / 10) {
|
||||
iStorage.page = iStorage.page + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const indexedRequests = indexer(interaction.client.requests, iStorage.page);
|
||||
indexedRequests.requestsString = new String();
|
||||
|
||||
for (const request of indexedRequests.thisPage) {
|
||||
indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`;
|
||||
}
|
||||
|
||||
interaction.update(fn.embeds.requests({command: "/requests", author: interaction.user.username}, indexedRequests));
|
||||
},
|
||||
jointsPage(interaction) {
|
||||
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
|
||||
|
||||
switch (interaction.component.customId) {
|
||||
case 'prevJointsPage':
|
||||
if (iStorage.page > 0) {
|
||||
iStorage.page = iStorage.page - 1;
|
||||
}
|
||||
break;
|
||||
case 'nextJointsPage':
|
||||
if (iStorage.page < interaction.client.joints.size / 10) {
|
||||
iStorage.page = iStorage.page + 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const indexedJoints = indexer(interaction.client.joints, iStorage.page);
|
||||
indexedJoints.jointsString = new String();
|
||||
|
||||
for (const joint of indexedJoints.thisPage) {
|
||||
indexedJoints.jointsString += `${joint.content}\n`;
|
||||
}
|
||||
|
||||
interaction.update(fn.embeds.joints({command: "/joints", author: interaction.user.username}, indexedJoints));
|
||||
},
|
||||
gifSearchPage(interaction) {
|
||||
const iStorage = interaction.client.iStorage.get(interaction.message.interaction.id);
|
||||
|
||||
switch (interaction.component.customId) {
|
||||
case 'prevGif':
|
||||
if (iStorage.page > 0) {
|
||||
iStorage.page = iStorage.page - 1;
|
||||
}
|
||||
break;
|
||||
case 'nextGif':
|
||||
if (iStorage.page < interaction.client.gifs.size / 10) {
|
||||
iStorage.page = iStorage.page + 1;
|
||||
}
|
||||
break;
|
||||
case 'confirmGif':
|
||||
const gif = indexer(iStorage.gifsCollection, iStorage.page, 1).thisPage[0];
|
||||
const gifData = new GifData().setInfo(iStorage.gifName, gif.media_formats.gif.url, gif.id);
|
||||
fn.upload.gif(gifData, interaction.client);
|
||||
interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] });
|
||||
fn.download.gifs(interaction.client);
|
||||
return;
|
||||
break;
|
||||
case 'cancelGif':
|
||||
interaction.update({ content: 'The GIF has been discarded.', components: [], embeds: [] });
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Generate the action row
|
||||
const gifSearchAR = customEmbeds.actionRows.gifSearchAR(iStorage.state);
|
||||
// Update the index
|
||||
const indexedGifs = indexer(iStorage.gifsCollection, iStorage.page, 1);
|
||||
indexedGifs.query = iStorage.query;
|
||||
indexedGifs.gifName = iStorage.gifName;
|
||||
// Generate the embed
|
||||
const gifEmbed = customEmbeds.core.gifSearch({ author: interaction.user.username }, indexedGifs);
|
||||
// Update the interaction
|
||||
interaction.update({ embeds: [gifEmbed], components: [gifSearchAR], ephemeral: true });
|
||||
},
|
||||
closeRequests(interaction) {
|
||||
const closeRequestModal = customEmbeds.modals.closeRequestsModal();
|
||||
interaction.showModal(closeRequestModal);
|
||||
interaction.update({ content: 'The requests menu has been closed.', components: [] });
|
||||
}
|
||||
}
|
||||
179
CustomModules/Embeds.js
Normal file
179
CustomModules/Embeds.js
Normal file
@ -0,0 +1,179 @@
|
||||
const { MessageActionRow, MessageButton, MessageEmbed, TextInputComponent } = require('discord.js');
|
||||
|
||||
module.exports = {
|
||||
actionRows: {
|
||||
gifSearchAR(state) {
|
||||
// Setup the buttons
|
||||
const previousButton = new MessageButton()
|
||||
.setCustomId('prevGif')
|
||||
.setLabel('⬅️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const confirmButton = new MessageButton()
|
||||
.setCustomId('confirmGif')
|
||||
.setLabel('Select')
|
||||
.setStyle('PRIMARY');
|
||||
|
||||
const nextButton = new MessageButton()
|
||||
.setCustomId('nextGif')
|
||||
.setLabel('➡️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const cancelButton = new MessageButton()
|
||||
.setCustomId('cancelGif')
|
||||
.setLabel('Cancel')
|
||||
.setStyle('DANGER');
|
||||
|
||||
switch (state) {
|
||||
case 'first':
|
||||
previousButton.setDisabled(true);
|
||||
break;
|
||||
case 'last':
|
||||
nextButton.setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the buttons into an ActionRow
|
||||
return new MessageActionRow()
|
||||
.addComponents(previousButton, confirmButton, nextButton, cancelButton);
|
||||
},
|
||||
gifsPageAR(state) {
|
||||
// Setup the buttons
|
||||
const previousButton = new MessageButton()
|
||||
.setCustomId('prevGifsPage')
|
||||
.setLabel('⬅️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const nextButton = new MessageButton()
|
||||
.setCustomId('nextGifsPage')
|
||||
.setLabel('➡️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
switch (state) {
|
||||
case 'first':
|
||||
previousButton.setDisabled(true);
|
||||
break;
|
||||
case 'last':
|
||||
nextButton.setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the buttons into an ActionRow
|
||||
return new MessageActionRow()
|
||||
.addComponents(previousButton, nextButton);
|
||||
},
|
||||
requestsPageAR(state) {
|
||||
// Setup the buttons
|
||||
const previousButton = new MessageButton()
|
||||
.setCustomId('prevRequestsPage')
|
||||
.setLabel('⬅️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const nextButton = new MessageButton()
|
||||
.setCustomId('nextRequestsPage')
|
||||
.setLabel('➡️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const closeButton = new MessageButton()
|
||||
.setCustomId('closeRequests')
|
||||
.setLabel('Close Requests')
|
||||
.setStyle('DANGER');
|
||||
|
||||
switch (state) {
|
||||
case 'first':
|
||||
previousButton.setDisabled(true);
|
||||
break;
|
||||
case 'last':
|
||||
nextButton.setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the buttons into an ActionRow
|
||||
return new MessageActionRow()
|
||||
.addComponents(previousButton, nextButton);
|
||||
},
|
||||
pastasPageAR(state) {
|
||||
// Setup the buttons
|
||||
const previousButton = new MessageButton()
|
||||
.setCustomId('prevPastasPage')
|
||||
.setLabel('⬅️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const nextButton = new MessageButton()
|
||||
.setCustomId('nextPastasPage')
|
||||
.setLabel('➡️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
switch (state) {
|
||||
case 'first':
|
||||
previousButton.setDisabled(true);
|
||||
break;
|
||||
case 'last':
|
||||
nextButton.setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the buttons into an ActionRow
|
||||
return new MessageActionRow()
|
||||
.addComponents(previousButton, nextButton);
|
||||
},
|
||||
jointsPageAR(state) {
|
||||
// Setup the buttons
|
||||
const previousButton = new MessageButton()
|
||||
.setCustomId('prevJointsPage')
|
||||
.setLabel('⬅️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
const nextButton = new MessageButton()
|
||||
.setCustomId('nextJointsPage')
|
||||
.setLabel('➡️')
|
||||
.setStyle('SECONDARY');
|
||||
|
||||
switch (state) {
|
||||
case 'first':
|
||||
previousButton.setDisabled(true);
|
||||
break;
|
||||
case 'last':
|
||||
nextButton.setDisabled(true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the buttons into an ActionRow
|
||||
return new MessageActionRow()
|
||||
.addComponents(previousButton, nextButton);
|
||||
}
|
||||
},
|
||||
modals: {
|
||||
closeRequestsModal() {
|
||||
const requestNumberInput = new TextInputComponent()
|
||||
.setCustomId('requestNumber')
|
||||
.setLabel('Request Number')
|
||||
.setPlaceholder('420')
|
||||
.setMinLength(1)
|
||||
.setMaxLength(5)
|
||||
.setStyle('SHORT')
|
||||
.setRequired(true);
|
||||
|
||||
const actionRow = new MessageActionRow()
|
||||
.addComponents([requestNumberInput]);
|
||||
|
||||
return new Modal()
|
||||
.setTitle('Close Request')
|
||||
.setDescription('Please enter the number of the request you would like to close.')
|
||||
.addComponents([actionRow]);
|
||||
}
|
||||
},
|
||||
core: {
|
||||
gifSearch(commandData, indexedGifs) {
|
||||
return new MessageEmbed()
|
||||
.setColor('#0099ff')
|
||||
.setTitle('GIF Search')
|
||||
.setImage(indexedGifs.thisPage[0].media_formats.gif.url)
|
||||
.setFooter({ text: `Page ${indexedGifs.pagesString}` })
|
||||
.addFields(
|
||||
{ name: 'Query', value: `"${indexedGifs.query}"`, inline: true },
|
||||
{ name: 'Saving As', value: `${indexedGifs.gifName}.gif`, inline: true },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
CustomModules/Indexer.js
Normal file
32
CustomModules/Indexer.js
Normal file
@ -0,0 +1,32 @@
|
||||
module.exports = (collection, page, qty) => {
|
||||
const itemsPerPage = qty ? qty : 10;
|
||||
const index = page * itemsPerPage;
|
||||
const totalPages = Math.ceil(collection.size / itemsPerPage);
|
||||
let state = page === 0 ? 'first' : 'middle';
|
||||
|
||||
const thisPage = new Array();
|
||||
|
||||
// Map the Djs Collection to an Array
|
||||
const collectionArray = collection.map((command) => command);
|
||||
|
||||
for (let i = index; i < index + itemsPerPage; i++) {
|
||||
if (collectionArray[i]) {
|
||||
thisPage.push(collectionArray[i]);
|
||||
} else {
|
||||
state = 'last';
|
||||
break;
|
||||
}
|
||||
|
||||
if (i === collectionArray.size - 1) {
|
||||
state = 'last';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
state: state,
|
||||
thisPage: thisPage,
|
||||
totalPages: totalPages,
|
||||
pagesString: `${page + 1}/${totalPages}`
|
||||
};
|
||||
}
|
||||
17
CustomModules/InteractionStorage.js
Normal file
17
CustomModules/InteractionStorage.js
Normal file
@ -0,0 +1,17 @@
|
||||
module.exports = class InteractionStorage {
|
||||
constructor(idString, interaction) {
|
||||
this.idString = idString;
|
||||
this.userId = interaction.user.id;
|
||||
|
||||
// Store in the client
|
||||
interaction.client.iStorage.set(idString, this);
|
||||
|
||||
// Delete this from the interactionStorage after 5 minutes
|
||||
setTimeout(() => {
|
||||
console.log(`Deleting interactionStorage with id: ${idString}`);
|
||||
interaction.client.iStorage.delete(idString);
|
||||
}, 300000);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
179
CustomModules/NodBot.js
Normal file
179
CustomModules/NodBot.js
Normal file
@ -0,0 +1,179 @@
|
||||
module.exports = {
|
||||
CommandData: class {
|
||||
constructor(message) {
|
||||
// Get the location of the final period in the message
|
||||
this.finalPeriod = message.content.lastIndexOf('.');
|
||||
this.isCommand = this.finalPeriod >= 0 ? true : false; // Check if there is a period somewhere in the message to flag as a possible command
|
||||
this.isValid = false;
|
||||
this.args = message.content.slice(0,this.finalPeriod).toLowerCase(); // Grab everything leading up to the final period
|
||||
this.command = message.content.slice(this.finalPeriod + 1).toLowerCase(); // Grab everything after the final period
|
||||
this.author = message.author.username;
|
||||
this.message = message;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
validate(dotCommands) {
|
||||
if (this.args.startsWith('http')) return false;
|
||||
if (this.args.startsWith('www')) return false;
|
||||
|
||||
const indices = {
|
||||
curlyBrace: {
|
||||
start: -1,
|
||||
end: -1
|
||||
},
|
||||
bracket: {
|
||||
start: -1,
|
||||
end: -1
|
||||
},
|
||||
parenthesis: {
|
||||
start: -1,
|
||||
end: -1
|
||||
}
|
||||
}
|
||||
|
||||
// Check for and extract the part of the message that's
|
||||
// wrapped in any type of brackets or quotes eg. ([{``''""}])
|
||||
const curlyBraceStart = this.message.content.match(/[\{]/g);
|
||||
const curlyBraceEnd = this.message.content.match(/[\}]/g);
|
||||
if (curlyBraceStart && curlyBraceEnd) {
|
||||
indices.curlyBrace.start = this.message.content.indexOf(curlyBraceStart[0]) + 1;
|
||||
indices.curlyBrace.end = this.message.content.lastIndexOf(curlyBraceEnd[0]);
|
||||
}
|
||||
|
||||
const bracketStart = this.message.content.match(/[\[]/g);
|
||||
const bracketEnd = this.message.content.match(/[\]]/g);
|
||||
if (bracketStart && bracketEnd) {
|
||||
indices.bracket.start = this.message.content.indexOf(bracketStart[0]) + 1;
|
||||
indices.bracket.end = this.message.content.lastIndexOf(bracketEnd[0]);
|
||||
}
|
||||
|
||||
const parenthesisStart = this.message.content.match(/[\(]/g);
|
||||
const parenthesisEnd = this.message.content.match(/[\)]/g);
|
||||
if (parenthesisStart && parenthesisEnd) {
|
||||
indices.parenthesis.start = this.message.content.indexOf(parenthesisStart[0]) + 1;
|
||||
indices.parenthesis.end = this.message.content.lastIndexOf(parenthesisEnd[0]);
|
||||
}
|
||||
|
||||
let nestedText = new String();
|
||||
|
||||
if (indices.curlyBrace.start >= 0 && indices.curlyBrace.end > 0) {
|
||||
nestedText = this.message.content.slice(indices.curlyBrace.start, indices.curlyBrace.end);
|
||||
}
|
||||
|
||||
if (indices.bracket.start >= 0 && indices.bracket.end > 0) {
|
||||
nestedText = this.message.content.slice(indices.bracket.start, indices.bracket.end);
|
||||
}
|
||||
|
||||
if (indices.parenthesis.start >= 0 && indices.parenthesis.end > 0) {
|
||||
nestedText = this.message.content.slice(indices.parenthesis.start, indices.parenthesis.end);
|
||||
}
|
||||
|
||||
console.log(nestedText);
|
||||
|
||||
if (nestedText !== "") {
|
||||
this.nestedCommand = {
|
||||
finalPeriod: nestedText.lastIndexOf('.'),
|
||||
isCommand: nestedText.lastIndexOf('.') >= 0 ? true : false,
|
||||
args: nestedText.slice(0, nestedText.lastIndexOf('.')).toLowerCase(),
|
||||
command: nestedText.slice(nestedText.lastIndexOf('.') + 1).toLowerCase()
|
||||
}
|
||||
|
||||
for (const [key, value] of dotCommands) {
|
||||
if (key === this.nestedCommand.command) {
|
||||
this.isValid = true;
|
||||
this.args = this.nestedCommand.args;
|
||||
this.command = key;
|
||||
this.isCommand = this.nestedCommand.isCommand;
|
||||
this.finalPeriod = this.nestedCommand.finalPeriod;
|
||||
return this;
|
||||
} else if (value.alias) {
|
||||
if (typeof value.alias === 'string' && value.alias === this.nestedCommand.command) {
|
||||
this.command = key
|
||||
this.args = this.nestedCommand.args;
|
||||
this.isValid = true;
|
||||
this.isCommand = this.nestedCommand.isCommand;
|
||||
this.finalPeriod = this.nestedCommand.finalPeriod;
|
||||
return this;
|
||||
} else if (typeof value.alias === 'object' && value.alias.includes(this.nestedCommand.command)) {
|
||||
this.command = key
|
||||
this.args = this.nestedCommand.args;
|
||||
this.isValid = true;
|
||||
this.isCommand = this.nestedCommand.isCommand;
|
||||
this.finalPeriod = this.nestedCommand.finalPeriod;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of dotCommands) {
|
||||
if (key === this.command) {
|
||||
this.isValid = true;
|
||||
return this;
|
||||
} else if (value.alias) {
|
||||
if (typeof value.alias === 'string' && value.alias === this.command) {
|
||||
this.command = key;
|
||||
this.isValid = true;
|
||||
return this;
|
||||
} else if (typeof value.alias === 'object' && value.alias.includes(this.command)) {
|
||||
this.command = key;
|
||||
this.isValid = true;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
},
|
||||
GifData: class {
|
||||
constructor() {
|
||||
this.id = 0;
|
||||
this.name = "";
|
||||
this.url = "";
|
||||
}
|
||||
|
||||
// Initial GifData configuration
|
||||
// Can also be used to update the data piecemeal
|
||||
setInfo(name, url, id) {
|
||||
// Check for existing or incoming name
|
||||
if ((this.name === "") && (typeof name !== 'string')) throw `Error: This Gif doesn't have existing name, and no name is going to be set.`;
|
||||
// Check for existing content or incoming content
|
||||
if ((this.url === "") && (typeof url !== 'string')) throw `Error: This Gif doesn't have existing url, and no url is going to be set.`;
|
||||
|
||||
// Property is set if the variable is the right type,
|
||||
// otherwise it keeps the existing property
|
||||
this.id = typeof id === 'number' ? id : this.id;
|
||||
this.name = typeof name === 'string' ? name : this.name;
|
||||
this.url = typeof url === 'string' ? url : this.url;
|
||||
|
||||
return this; // For chaining
|
||||
}
|
||||
},
|
||||
PastaData: class {
|
||||
constructor() {
|
||||
this.id = 0;
|
||||
this.name = "";
|
||||
this.content = "";
|
||||
this.iconUrl = "";
|
||||
}
|
||||
|
||||
// Initial PastaData configuration
|
||||
// Can also be used to update the data piecemeal
|
||||
setInfo(name, content, iconUrl, id) {
|
||||
// Check for existing or incoming name
|
||||
if ((this.name === "") && (typeof name !== 'string')) throw `Error: This Pasta doesn't have existing name, and no name is going to be set.`;
|
||||
// Check for existing content or incoming content
|
||||
if ((this.content === "") && (typeof content !== 'string')) throw `Error: This Pasta doesn't have existing content, and no content is going to be set.`;
|
||||
|
||||
// Property is set if the variable is the right type,
|
||||
// otherwise it keeps the existing property
|
||||
this.id = typeof id === 'number' ? id : this.id;
|
||||
this.name = typeof name === 'string' ? name : this.name;
|
||||
this.content = typeof content === 'string' ? content : this.content;
|
||||
this.iconUrl = typeof iconUrl === 'string' ? iconUrl : this.iconUrl;
|
||||
|
||||
return this; // For chaining
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Dockerfile
16
Dockerfile
@ -1,8 +1,20 @@
|
||||
FROM node:16
|
||||
# Dev Build
|
||||
FROM node:18 AS dev
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
CMD ["/bin/sh", "-c", "node main.js 2> /logs/nodbot.error 1> /logs/nodbot.log"]
|
||||
# Invoke the bot without redirecting STDOUT and STDERR so that docker logs can be used.
|
||||
CMD ["/bin/sh", "-c", "node main.js"]
|
||||
|
||||
FROM node:18 AS prod
|
||||
RUN mkdir -p /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
# CMD ["/bin/sh", "-c", "node main.js 2> /logs/nodbot.error 1> /logs/nodbot.log"]
|
||||
CMD ["/bin/sh", "-c", "node main.js 2> /logs/$(date +%Y-%m-%d_%H-%M-%S)-error.txt 1> /logs/$(date +%Y-%m-%d_%H-%M-%S)-status.txt"]
|
||||
26
README.md
26
README.md
@ -1,5 +1,5 @@
|
||||
# About Nodbot
|
||||
Nodbot is a content saving and serving Discord bot. Nodbot is able to search Tenor for GIFs, save custom copypastas, and look up marijuana strain information. Nodbot is in semi-active development by voidf1sh. It's buggy as hell and very shoddily built. Don't use it.
|
||||
Nodbot is a content saving and serving Discord bot. Nodbot is able to search GIPHY for GIFs, save custom copypastas, and look up marijuana strain information. Nodbot is in semi-active development by voidf1sh. It's buggy as hell and very shoddily built. Don't use it.
|
||||
|
||||
# Nodbot Help
|
||||
|
||||
@ -11,21 +11,12 @@ Use the `/help` command to see the bot's help message.
|
||||
## Push Docker Image
|
||||
`docker push name/nodbot`
|
||||
|
||||
# Immediate To-Do
|
||||
|
||||
1. ~~Sanitize inputs for SQL queries.~~ Done.
|
||||
2. ~~Move environment variables so they don't get included in the image.~~
|
||||
3. Implement error handling on all actions.
|
||||
4. Ephemeral responses to some/most slash commands.
|
||||
5. Comment the code! Document!
|
||||
6. Check for and create database tables if necessary. Handle errors.
|
||||
|
||||
# Deploy NodBot Yourself
|
||||
|
||||
1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications)
|
||||
2. Convert the application into a Bot
|
||||
3. Note down the token provided and keep this safe. You cannot view this token again, only regenerate a new one.
|
||||
4. Create a Tenor account and obtain an API key.
|
||||
4. Create a GIPHY account and obtain an API key.
|
||||
5. Install and configure MySQL or MariaDB with a user for the bot and a datbase
|
||||
* Create the table structure as outlined below (* Nodbot will soon create its own table structure)
|
||||
6. Configure your environment variables as outlined below.
|
||||
@ -93,19 +84,8 @@ dbPort=<mySQL port (3306)>
|
||||
dbUser=<mySQL username>
|
||||
dbPass=<mySQL user password>
|
||||
dbName=<mySQL table name>
|
||||
tenorAPIKey=<Tenor API Key>
|
||||
giphyAPIKey=<GIPHY API Key>
|
||||
ownerId=<your Discord user ID>
|
||||
statusChannelId=<Discord channel ID of channel used for status messages>
|
||||
clientId=<Discord user ID of your bot>
|
||||
```
|
||||
|
||||
## Changes
|
||||
|
||||
v3.0.1 - Migrate TenorJS API Endpoint
|
||||
v3.0.2 - Add medical advice commands
|
||||
v3.0.3 - Fix broken `/requests` command
|
||||
v3.0.4 - Add ability to use multiple aliases
|
||||
v3.0.5 - Add ability to save strains
|
||||
v3.0.6 - Move `.strain` to `/strain` and add Autocomplete
|
||||
v3.0.7 - Add `.spongebob` replies
|
||||
v3.0.8 - Add ability to open requests by pages
|
||||
14
Roadmap.md
14
Roadmap.md
@ -1,14 +0,0 @@
|
||||
# v3.1.0
|
||||
|
||||
* Name checking for saving content
|
||||
* .jpg, .wav
|
||||
* Audio/Video attachments for saved content.
|
||||
* Pass The Joint
|
||||
* Voting system for Super Adventure Club
|
||||
|
||||
# v4.0.0
|
||||
* Scalability: modify the code to allow the bot to be used in multiple servers
|
||||
* including saved content, saved commands, preferences, etc.
|
||||
|
||||
# v3.?.?
|
||||
= Joke generator for Hallihan
|
||||
38
_list-guilds.js
Normal file
38
_list-guilds.js
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Quick and dirty script to list out what guilds the bot is in and optionally leave some.
|
||||
*/
|
||||
|
||||
const guildsToLeave = ['1224396616555823124'];
|
||||
|
||||
const dotenv = require('dotenv').config();
|
||||
const token = process.env.TOKEN;
|
||||
const Discord = require('discord.js');
|
||||
|
||||
const client = new Discord.Client({
|
||||
intents: [
|
||||
'GUILDS'
|
||||
]
|
||||
});
|
||||
|
||||
client.on('ready', async () => {
|
||||
const guildChecks = [];
|
||||
console.log(`Logged in as ${client.user.tag}`);
|
||||
console.log('Guilds:');
|
||||
client.guilds.cache.forEach((guild) => {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
console.log(` - ${guild.name} (${guild.id})`);
|
||||
if (guildsToLeave.includes(guild.id)) {
|
||||
console.log(` Leaving ${guild.name}`);
|
||||
guild.leave().then(resolve).catch(reject);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
guildChecks.push(p);
|
||||
});
|
||||
await Promise.all(guildChecks);
|
||||
console.log('All guilds checked.');
|
||||
client.destroy();
|
||||
});
|
||||
|
||||
client.login(token);
|
||||
@ -1,4 +1,8 @@
|
||||
{
|
||||
"guildId": "868542949737246730",
|
||||
"validCommands": []
|
||||
"validCommands": [],
|
||||
"roaches": [],
|
||||
"icaoIds": [],
|
||||
"datisICAOs": [],
|
||||
"bannedServers": ["1224396616555823124"]
|
||||
}
|
||||
28
dot-commands/datis.js
Normal file
28
dot-commands/datis.js
Normal file
@ -0,0 +1,28 @@
|
||||
const fn = require('../functions');
|
||||
|
||||
module.exports = {
|
||||
name: 'datis',
|
||||
description: 'Lookup dATIS for an airport',
|
||||
usage: 'ICAO.datis',
|
||||
alias: [ 'atis' ],
|
||||
async execute(message, commandData) {
|
||||
try {
|
||||
const icaoId = commandData.args.toUpperCase();
|
||||
if (icaoId.length !== 4) throw new Error('Invalid ICAO ID. Provide only one ICAO code at a time like KBOS');
|
||||
if (fn.avWx.datis.validate(icaoId)) {
|
||||
const datisData = await fn.avWx.datis.getData(icaoId);
|
||||
const messagePayload = fn.avWx.datis.parseData(datisData);
|
||||
message.reply(messagePayload);
|
||||
} else {
|
||||
message.reply("No D-ATIS available for the specified ICAO ID.");
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
message.reply(`D-ATIS Error: ${e.message}`);
|
||||
console.error(e);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,52 +2,53 @@ const fn = require('../functions');
|
||||
const axios = require('axios');
|
||||
const dotenv = require('dotenv').config();
|
||||
|
||||
// TODO: Tenor has changed API versions, switch from TenorJS (unmaintained) to axios for
|
||||
// general API usage. See: https://github.com/Jinzulen/TenorJS/issues/12
|
||||
// see also: https://developers.google.com/tenor/guides/migrate-from-v1
|
||||
|
||||
module.exports = {
|
||||
name: 'gif',
|
||||
description: 'Send a GIF',
|
||||
alias: ['jpg', 'png', 'gifv', 'webm', 'mp4', 'wav', 'wmv', 'webp', 'mp3', 'flac', 'ogg', 'avi', 'mov', 'mpg', 'mpeg', 'mkv', 'flv', 'bmp', 'tiff', 'tif', 'svg', 'ico'],
|
||||
usage: '<GIF name or Search Query>.gif',
|
||||
async execute(message, commandData) {
|
||||
// if (message.deletable) message.delete();
|
||||
const client = message.client;
|
||||
if (!client.gifs.has(commandData.args)) {
|
||||
if (process.env.isDev) console.log('https://tenor.googleapis.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.tenorAPIKey}` + '&limit=1&contentfilter=off');
|
||||
if (!client.gifs.has(commandData.args.toLowerCase())) {
|
||||
// TODO: Implement proper logging
|
||||
if (process.env.isDev) console.log('https://api.giphy.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.giphyAPIKey}` + '&limit=1&contentfilter=off');
|
||||
const q = await axios.get(
|
||||
'https://tenor.googleapis.com/v2/search?' +
|
||||
'https://api.giphy.com/v2/search?' +
|
||||
`&q=${commandData.args}` +
|
||||
`&key=${process.env.tenorAPIKey}` +
|
||||
`&key=${process.env.giphyAPIKey}` +
|
||||
'&limit=1' +
|
||||
`&contentfilter=off`
|
||||
).then(res => {
|
||||
).then(async (res) => {
|
||||
if (process.env.isDev) console.log(res.data.results);
|
||||
if (res.data.results[0] == undefined) {
|
||||
message.reply('Sorry I was unable to find a GIF of ' + commandData.args);
|
||||
return;
|
||||
};
|
||||
commandData.embed_url = res.data.results[0].url;
|
||||
message.reply(commandData.embed_url);
|
||||
|
||||
// Handle when the invoking message is a reply to someone else
|
||||
if (message.reference) {
|
||||
// Fetch the referenced message from the channels' MessageManager
|
||||
await message.channel.messages.fetch(message.reference.messageId).then(async (refMsg) => {
|
||||
// Send the reply to the referenced message
|
||||
await refMsg.reply(commandData.embed_url);
|
||||
}).catch(async (e) => {
|
||||
// Failed fetching the message from the channel, fallback to regular send
|
||||
console.error(e);
|
||||
await message.reply(commandData.embed_url);
|
||||
});
|
||||
} else {
|
||||
await message.reply(commandData.embed_url);
|
||||
}
|
||||
}).catch(err => console.error(err));
|
||||
// tenor.Search.Query(commandData.args, "1").then(res => {
|
||||
// if (res[0] == undefined) {
|
||||
// message.reply('Sorry I was unable to find a GIF of ' + commandData.args);
|
||||
// return;
|
||||
// };
|
||||
// commandData.embed_url = res[0].media[0].gif.url;
|
||||
// // message.reply(fn.embeds.gif(commandData));
|
||||
// // message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`);
|
||||
// // message.channel.send(commandData.embed_url);
|
||||
// message.reply(commandData.embed_url);
|
||||
// }).catch(err => console.error(err));
|
||||
} else {
|
||||
// message.reply(commandData.args + ' requested by ' + message.author.username + '\n' + client.gifs.get(commandData.args).embed_url);
|
||||
commandData.embed_url = client.gifs.get(commandData.args).embed_url;
|
||||
const gifData = client.gifs.get(commandData.args.toLowerCase());
|
||||
// message.reply(fn.embeds.gif(commandData));
|
||||
// message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`);
|
||||
// message.channel.send(commandData.embed_url);
|
||||
message.reply(commandData.embed_url);
|
||||
message.reply(gifData.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,35 @@ module.exports = {
|
||||
name: 'joint',
|
||||
description: 'Send a random weed-themed phrase.',
|
||||
usage: '.joint',
|
||||
alias: ['bong', 'blunt', 'bowl', 'pipe'],
|
||||
alias: ['bong', 'blunt', 'bowl', 'pipe', 'dab', 'vape', 'dabs', 'shatter', 'edible', 'edibles', 'doobie', 'spliff', 'gummy', 'gummies', 'hash', 'toke', 'big doinks',
|
||||
'drink', 'shot', 'shots', 'beer', 'beers', 'brew', 'brews', 'liquor', 'cocktail', 'cocktails', 'wine', 'wines', 'whiskey', 'whiskies', 'vodka', 'vodkas', 'rum', 'rums', 'cider', 'ciders',
|
||||
],
|
||||
execute(message, commandData) {
|
||||
let joints = [];
|
||||
// Create a simple array of the joint texts
|
||||
for (const entry of message.client.joints.map(joint => joint.content)) {
|
||||
joints.push(entry);
|
||||
}
|
||||
const randIndex = Math.floor(Math.random() * joints.length);
|
||||
message.reply(`${joints[randIndex]} ${emoji.joint}`);
|
||||
// Generate a random number between 0 and the length of the joints array
|
||||
let randIndex = Math.floor(Math.random() * joints.length);
|
||||
// Grab the joint text from the array
|
||||
let joint = joints[randIndex];
|
||||
|
||||
// Check if the joint has already been smoked
|
||||
while (message.client.roaches.has(joint)) {
|
||||
// Regenerate a random number and recheck
|
||||
randIndex = Math.floor(Math.random() * joints.length);
|
||||
joint = joints[randIndex];
|
||||
}
|
||||
// Send the joint
|
||||
message.channel.send(`${joints[randIndex]} ${emoji.joint}`);
|
||||
// Check how full the roach collection is
|
||||
if (message.client.roaches.size / joints.length >= 0.85) {
|
||||
// If the roach collection is 85% of the joints collection
|
||||
// Empty it out
|
||||
message.client.roaches.clear();
|
||||
}
|
||||
// Add the joint to the roach collection
|
||||
message.client.roaches.set(joint, "baked");
|
||||
}
|
||||
}
|
||||
30
dot-commands/metar.js
Normal file
30
dot-commands/metar.js
Normal file
@ -0,0 +1,30 @@
|
||||
const fn = require('../functions');
|
||||
|
||||
module.exports = {
|
||||
name: 'metar',
|
||||
description: 'Lookup METAR for an airport',
|
||||
usage: 'ICAO.metar',
|
||||
async execute(message, commandData) {
|
||||
try {
|
||||
// Parse the ICAOs into a CSV list by trimming whitespace and converting delimiters
|
||||
// Also checks for validity of ICAOs
|
||||
const icaoList = fn.avWx.parseICAOs(commandData);
|
||||
const metarData = await fn.avWx.metar.getData(icaoList);
|
||||
if (metarData.length === 0) {
|
||||
message.reply('No METAR data found for the provided ICAOs.');
|
||||
return;
|
||||
}
|
||||
const messages = fn.avWx.metar.parseData(metarData);
|
||||
messages.forEach(messagePayload => {
|
||||
message.reply(messagePayload);
|
||||
});
|
||||
} catch (e) {
|
||||
try {
|
||||
message.reply(`METAR Error: ${e.message}`);
|
||||
console.error(e);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,14 +6,14 @@ module.exports = {
|
||||
usage: '<Copypasta Name>.pasta',
|
||||
execute(message, commandData) {
|
||||
const client = message.client;
|
||||
let replyBody = '';
|
||||
let iconUrl;
|
||||
let pastaData;
|
||||
if (!client.pastas.has(commandData.args)) {
|
||||
commandData.content = 'Sorry I couldn\'t find that pasta.';
|
||||
pastaData = {
|
||||
content: "Sorry, I couldn't find that pasta."
|
||||
};
|
||||
} else {
|
||||
commandData.content = client.pastas.get(commandData.args).content;
|
||||
commandData.iconUrl = client.pastas.get(commandData.args).iconUrl;
|
||||
pastaData = client.pastas.get(commandData.args);
|
||||
}
|
||||
message.reply(fn.embeds.pasta(commandData));
|
||||
message.reply(fn.embeds.pasta(commandData, pastaData));
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,25 @@ module.exports = {
|
||||
description: 'Submit a request to the bot developer.',
|
||||
usage: '<request or feedback>.request',
|
||||
execute(message, commandData) {
|
||||
const request = commandData.args;
|
||||
commandData.content = `Your request has been submitted!\nRequest: ${request}`;
|
||||
message.reply(fn.embeds.text(commandData));
|
||||
commandData.content = `A new request has been submitted by ${message.author.tag}:\n${commandData.args}`;
|
||||
message.client.users.fetch(process.env.ownerID).then(user => {
|
||||
user.send(fn.embeds.text(commandData));
|
||||
}).catch(error => { console.error(error); });
|
||||
fn.upload.request(commandData, message.client);
|
||||
// Get the request information from the command data that was passed in
|
||||
const requestText = commandData.args;
|
||||
const messageUrl = message.url;
|
||||
const requester = message.author.username;
|
||||
|
||||
try {
|
||||
fn.upload.request(requester, requestText, messageUrl, message.client);
|
||||
// Set the content to sent a text embed notifying the user of success
|
||||
commandData.content = `Your request has been submitted!\nRequest: ${requestText}`;
|
||||
const replyEmbed = fn.embeds.text(commandData);
|
||||
message.reply({ embeds: [ replyEmbed ]});
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
commandData.content = "An error occurred while saving the request, please try again later.";
|
||||
const replyEmbed = fn.embeds.text(commandData);
|
||||
message.reply({ embeds: [ replyEmbed ]});
|
||||
|
||||
const errorEmbed = fn.embeds.error(`An error has occurred in command ${this.name}:\n${err}`);
|
||||
fn.sendAlert(errorEmbed, message.client);
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -10,20 +10,33 @@ module.exports = {
|
||||
// message.reply(fn.spongebob(commandData)).then(() => {
|
||||
// message.delete();
|
||||
// });
|
||||
if (message.reference != undefined) {
|
||||
const repliedMessageId = message.reference.messageId;
|
||||
message.channel.messages.fetch(repliedMessageId)
|
||||
.then(repliedMessage => {
|
||||
repliedMessage.reply(fn.spongebob({ args: repliedMessage.content })).then(() => {
|
||||
if (message.deletable) message.delete();
|
||||
if (message.reference != undefined) { // message.reference is undefined if the message isn't a reply to another message
|
||||
if (commandData.args !== "") { // If the replying message isn't just .sb
|
||||
const repliedMessageId = message.reference.messageId; // grab the message Id of the replied-to msg
|
||||
message.channel.messages.fetch(repliedMessageId) // Fetch the message because with our luck it isn't in the cache
|
||||
.then(repliedMessage => {
|
||||
repliedMessage.reply(fn.spongebob({ args: commandData.args })).then(() => { // Use the pre-command text of the replying message to sb-ify
|
||||
if (message.deletable) message.delete(); // If the initiating message is deletable, delete it.
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
} else {
|
||||
} else { // We're working with a basic ".sb" and can proceed as we did before...
|
||||
const repliedMessageId = message.reference.messageId; // grab the message Id of the replied-to msg
|
||||
message.channel.messages.fetch(repliedMessageId) // Fetch the message because with our luck it isn't in the cache
|
||||
.then(repliedMessage => {
|
||||
repliedMessage.reply(fn.spongebob({ args: repliedMessage.content })).then(() => {
|
||||
if (message.deletable) message.delete();
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
} else { // The message isn't a reply, so just sb it like we did from the very beginning
|
||||
message.channel.send(fn.spongebob(commandData)).then(() => {
|
||||
if (message.deletable) message.delete();
|
||||
if (message.deletable) message.delete(); // If the initiating message is deletable, delete it.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
681
functions.js
681
functions.js
@ -1,7 +1,6 @@
|
||||
/* eslint-disable comma-dangle */
|
||||
// dotenv for handling environment variables
|
||||
const dotenv = require('dotenv');
|
||||
dotenv.config();
|
||||
const dotenv = require('dotenv').config();
|
||||
// Assignment of environment variables for database access
|
||||
const dbHost = process.env.dbHost;
|
||||
const dbUser = process.env.dbUser;
|
||||
@ -9,11 +8,12 @@ const dbName = process.env.dbName;
|
||||
const dbPass = process.env.dbPass;
|
||||
const dbPort = process.env.dbPort;
|
||||
const isDev = process.env.isDev;
|
||||
|
||||
const ownerId = process.env.ownerId;
|
||||
const statusChannelId = process.env.statusChannelId;
|
||||
|
||||
// filesystem
|
||||
const fs = require('fs');
|
||||
const zlib = require('zlib');
|
||||
|
||||
// Discord.js
|
||||
const Discord = require('discord.js');
|
||||
@ -21,14 +21,20 @@ const Discord = require('discord.js');
|
||||
// Fuzzy text matching for db lookups
|
||||
const FuzzySearch = require('fuzzy-search');
|
||||
|
||||
// Axios for APIs
|
||||
const axios = require('axios');
|
||||
|
||||
// Various imports from other files
|
||||
const config = require('./config.json');
|
||||
const strings = require('./strings.json');
|
||||
const appVersion = require('./package.json').version;
|
||||
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
|
||||
const dotCommandFiles = fs.readdirSync('./dot-commands/').filter(file => file.endsWith('.js'));
|
||||
const customEmbeds = require('./CustomModules/Embeds.js');
|
||||
|
||||
// MySQL database connection
|
||||
const mysql = require('mysql');
|
||||
const { GifData, PastaData } = require('./CustomModules/NodBot');
|
||||
const db = new mysql.createPool({
|
||||
connectionLimit: 10,
|
||||
host: dbHost,
|
||||
@ -41,6 +47,11 @@ const db = new mysql.createPool({
|
||||
const functions = {
|
||||
// Functions for managing and creating Collections
|
||||
collections: {
|
||||
interactionStorage(client) {
|
||||
if (!client.iStorage) client.iStorage = new Discord.Collection();
|
||||
client.iStorage.clear();
|
||||
if (isDev) console.log('Interaction Storage Collection Built');
|
||||
},
|
||||
// Create the collection of slash commands
|
||||
slashCommands(client) {
|
||||
if (!client.slashCommands) client.slashCommands = new Discord.Collection();
|
||||
@ -72,13 +83,13 @@ const functions = {
|
||||
for (const file of dotCommandFiles) {
|
||||
const dotCommand = require(`./dot-commands/${file}`);
|
||||
client.dotCommands.set(dotCommand.name, dotCommand);
|
||||
if (Array.isArray(dotCommand.alias)) {
|
||||
dotCommand.alias.forEach(element => {
|
||||
client.dotCommands.set(element, dotCommand);
|
||||
});
|
||||
} else if (dotCommand.alias != undefined) {
|
||||
client.dotCommands.set(dotCommand.alias, dotCommand);
|
||||
}
|
||||
// if (Array.isArray(dotCommand.alias)) {
|
||||
// dotCommand.alias.forEach(element => {
|
||||
// client.dotCommands.set(element, dotCommand);
|
||||
// });
|
||||
// } else if (dotCommand.alias != undefined) {
|
||||
// client.dotCommands.set(dotCommand.alias, dotCommand);
|
||||
// }
|
||||
}
|
||||
if (isDev) console.log('Dot Commands Collection Built');
|
||||
},
|
||||
@ -86,12 +97,13 @@ const functions = {
|
||||
if (!client.gifs) client.gifs = new Discord.Collection();
|
||||
client.gifs.clear();
|
||||
for (const row of rows) {
|
||||
const gif = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
embed_url: row.embed_url
|
||||
};
|
||||
client.gifs.set(gif.name, gif);
|
||||
// const gif = {
|
||||
// id: row.id,
|
||||
// name: row.name,
|
||||
// embed_url: row.embed_url
|
||||
// };
|
||||
const gifData = new GifData().setInfo(row.name, row.embed_url);
|
||||
client.gifs.set(gifData.name, gifData);
|
||||
}
|
||||
if (isDev) console.log('GIFs Collection Built');
|
||||
},
|
||||
@ -111,13 +123,8 @@ const functions = {
|
||||
if (!client.pastas) client.pastas = new Discord.Collection();
|
||||
client.pastas.clear();
|
||||
for (const row of rows) {
|
||||
const pasta = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
content: row.content,
|
||||
iconUrl: row.iconurl,
|
||||
};
|
||||
client.pastas.set(pasta.name, pasta);
|
||||
const pastaData = new PastaData().setInfo(row.name, row.content, row.iconurl, row.id);
|
||||
client.pastas.set(pastaData.name, pastaData);
|
||||
}
|
||||
if (isDev) console.log('Pastas Collection Built');
|
||||
},
|
||||
@ -148,7 +155,7 @@ const functions = {
|
||||
if (isDev) console.log('Strains Collection Built');
|
||||
},
|
||||
medicalAdvice(rows, client) {
|
||||
if (!client.medicalAdviceCol) client.medicalAdviceColl = new Discord.Collection();
|
||||
if (!client.medicalAdviceColl) client.medicalAdviceColl = new Discord.Collection();
|
||||
client.medicalAdviceColl.clear();
|
||||
for (const row of rows) {
|
||||
const medicalAdvice = {
|
||||
@ -159,39 +166,16 @@ const functions = {
|
||||
}
|
||||
if (isDev) console.log('Medical Advice Collection Built');
|
||||
},
|
||||
},
|
||||
dot: {
|
||||
getCommandData(message) {
|
||||
const commandData = {};
|
||||
// Split the message content at the final instance of a period
|
||||
const finalPeriod = message.content.lastIndexOf('.');
|
||||
if(isDev) console.log(message.content);
|
||||
// If the final period is the last character, or doesn't exist
|
||||
if (finalPeriod < 0) {
|
||||
if (isDev) console.log(finalPeriod);
|
||||
commandData.isCommand = false;
|
||||
return commandData;
|
||||
}
|
||||
commandData.isCommand = true;
|
||||
// Get the first part of the message, everything leading up to the final period
|
||||
commandData.args = message.content.slice(0,finalPeriod).toLowerCase();
|
||||
// Get the last part of the message, everything after the final period
|
||||
commandData.command = message.content.slice(finalPeriod).replace('.','').toLowerCase();
|
||||
commandData.author = `${message.author.username}#${message.author.discriminator}`;
|
||||
return this.checkCommand(commandData);
|
||||
roaches(client) {
|
||||
if (!client.roaches) client.roaches = new Discord.Collection();
|
||||
client.roaches.clear();
|
||||
if (isDev) console.log('Medical Advice Collection Built');
|
||||
},
|
||||
checkCommand(commandData) {
|
||||
if (commandData.isCommand) {
|
||||
const validCommands = require('./config.json').validCommands;
|
||||
commandData.isValid = validCommands.includes(commandData.command);
|
||||
// Add exceptions for messages that contain only a link
|
||||
if (commandData.args.startsWith('http')) commandData.isValid = false;
|
||||
}
|
||||
else {
|
||||
commandData.isValid = false;
|
||||
console.error('Somehow a non-command made it to checkCommands()');
|
||||
}
|
||||
return commandData;
|
||||
memberships(client) {
|
||||
if (!client.memberships) client.memberships = new Discord.Collection();
|
||||
client.memberships.clear();
|
||||
client.memberships.set("481933290912350209", "vfsh");
|
||||
if (isDev) console.log('Memberships Collection Built');
|
||||
}
|
||||
},
|
||||
embeds: {
|
||||
@ -199,52 +183,75 @@ const functions = {
|
||||
// Construct the Help Embed
|
||||
const helpEmbed = new Discord.MessageEmbed()
|
||||
.setColor('BLUE')
|
||||
.setAuthor('Help Page')
|
||||
.setDescription(strings.help.description)
|
||||
.setThumbnail(strings.urls.avatar);
|
||||
.setAuthor({name: 'Help Page'})
|
||||
.setThumbnail(strings.urls.avatar)
|
||||
.setFooter(`v${appVersion}`);
|
||||
|
||||
// Construct the Slash Commands help
|
||||
|
||||
let slashCommandsFields = [];
|
||||
|
||||
let slashSeenNames = new Array();
|
||||
const slashCommandsMap = interaction.client.slashCommands.map(e => {
|
||||
return {
|
||||
name: e.data.name,
|
||||
description: e.data.description
|
||||
};
|
||||
})
|
||||
|
||||
if (!slashSeenNames.includes(e.data.name)) {
|
||||
slashSeenNames.push(e.data.name);
|
||||
const command = {
|
||||
name: e.data.name,
|
||||
description: e.data.description
|
||||
};
|
||||
return command;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
for (const e of slashCommandsMap) {
|
||||
slashCommandsFields.push({
|
||||
name: `- /${e.name}`,
|
||||
value: e.description,
|
||||
inline: false,
|
||||
});
|
||||
slashCommandsFields.push(`- \`/${e.name}\` - ${e.description}`);
|
||||
}
|
||||
console.log(slashCommandsFields);
|
||||
|
||||
// Construct the Dot Commands Help
|
||||
let dotCommandsFields = [];
|
||||
|
||||
let dotCommandsFields = new Array();
|
||||
let dotSeenNames = new Array();
|
||||
const dotCommandsMap = interaction.client.dotCommands.map(e => {
|
||||
return {
|
||||
name: e.name,
|
||||
description: e.description,
|
||||
usage: e.usage
|
||||
};
|
||||
if (!dotSeenNames.includes(e.name)) {
|
||||
dotSeenNames.push(e.name);
|
||||
let command = {
|
||||
name: e.name,
|
||||
description: e.description,
|
||||
usage: e.usage
|
||||
};
|
||||
command.aliasString = new String();
|
||||
if (e.alias != undefined && typeof e.alias === 'object') {
|
||||
for (const a of e.alias) {
|
||||
command.aliasString += `\`.${a}\`, `;
|
||||
}
|
||||
} else if (e.alias != undefined && typeof e.alias === 'string') {
|
||||
command.aliasString += `\`.${e.alias}\``;
|
||||
} else {
|
||||
command.aliasString = 'None';
|
||||
}
|
||||
return command;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
for (const e of dotCommandsMap) {
|
||||
dotCommandsFields.push({
|
||||
name: `- .${e.name}`,
|
||||
value: `${e.description}\nUsage: ${e.usage}`,
|
||||
inline: false,
|
||||
});
|
||||
if (e != null) {
|
||||
dotCommandsFields.push(`- \`.${e.name}\` - ${e.description}\n\tUsage: ${e.usage}\n\tAliases: ${e.aliasString}`);
|
||||
}
|
||||
}
|
||||
console.log(dotCommandsFields);
|
||||
|
||||
helpEmbed.addField('Slash Commands', strings.help.slash);
|
||||
helpEmbed.addFields(slashCommandsFields);
|
||||
helpEmbed.addField('Dot Commands', strings.help.dot);
|
||||
helpEmbed.addFields(dotCommandsFields);
|
||||
// Construct the Description Fields
|
||||
const descriptionFields = [
|
||||
`${strings.help.description}\n`,
|
||||
`**Slash Commands**\n${strings.help.slash}\n`,
|
||||
`${slashCommandsFields.join('\n')}\n`,
|
||||
`**Dot Commands**\n${strings.help.dot}\n`,
|
||||
`${dotCommandsFields.join('\n')}`
|
||||
];
|
||||
|
||||
// Set the description
|
||||
helpEmbed.setDescription(descriptionFields.join('\n'));
|
||||
|
||||
return { embeds: [
|
||||
helpEmbed
|
||||
@ -252,76 +259,67 @@ const functions = {
|
||||
},
|
||||
gif(commandData) {
|
||||
return { embeds: [new Discord.MessageEmbed()
|
||||
.setAuthor(`${commandData.args}.${commandData.command}`)
|
||||
.setAuthor({name: `${commandData.args}.${commandData.command}`})
|
||||
.setImage(commandData.embed_url)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
.setFooter({text: commandData.author})]};
|
||||
},
|
||||
pasta(commandData) {
|
||||
pasta(commandData, pastaData) {
|
||||
return { embeds: [ new Discord.MessageEmbed()
|
||||
.setAuthor(`${commandData.args}.${commandData.command}`)
|
||||
.setDescription(commandData.content)
|
||||
.setThumbnail(commandData.iconUrl)
|
||||
.setAuthor({name: `${commandData.args}.${commandData.command}`})
|
||||
.setDescription(pastaData.content)
|
||||
.setThumbnail("https://assets.vfsh.dev/shednod.png")
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
.setFooter({text: commandData.author})]};
|
||||
},
|
||||
pastas(commandData) {
|
||||
const pastasArray = [];
|
||||
pastas(commandData, indexedPastas) {
|
||||
const pastasEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setAuthor({name: commandData.command})
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
.setFooter({text: `Page: ${indexedPastas.pagesString}`})
|
||||
.setDescription(indexedPastas.pastasString);
|
||||
|
||||
for (const row of commandData.pastas) {
|
||||
pastasArray.push(`#${row.id} - ${row.name}.pasta`);
|
||||
}
|
||||
|
||||
const pastasString = pastasArray.join('\n');
|
||||
pastasEmbed.setDescription(pastasString);
|
||||
|
||||
return { embeds: [pastasEmbed], ephemeral: true };
|
||||
const pastasPageAR = customEmbeds.actionRows.pastasPageAR(indexedPastas.state);
|
||||
return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true };
|
||||
},
|
||||
gifs(commandData) {
|
||||
const gifsArray = [];
|
||||
gifs(commandData, indexedGifs) {
|
||||
const gifsEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setAuthor({name: commandData.command})
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
.setFooter({text: `Page: ${indexedGifs.pagesString}`})
|
||||
.setDescription(indexedGifs.gifsString);
|
||||
|
||||
for (const row of commandData.gifs) {
|
||||
gifsArray.push(`#${row.id} - ${row.name}.gif`);
|
||||
}
|
||||
const gifsPageAR = customEmbeds.actionRows.gifsPageAR(indexedGifs.state);
|
||||
return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true };
|
||||
},
|
||||
joints(commandData, indexedJoints) {
|
||||
const jointsEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({name: commandData.command})
|
||||
.setTimestamp()
|
||||
.setFooter({text: `Page: ${indexedJoints.pagesString}`})
|
||||
.setDescription(indexedJoints.jointsString);
|
||||
|
||||
const gifsString = gifsArray.join('\n');
|
||||
gifsEmbed.setDescription(gifsString);
|
||||
|
||||
return { embeds: [gifsEmbed] };
|
||||
const jointsPageAR = customEmbeds.actionRows.jointsPageAR(indexedJoints.state);
|
||||
return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true };
|
||||
},
|
||||
text(commandData) {
|
||||
return { embeds: [new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
const textEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: commandData.command })
|
||||
.setDescription(commandData.content)
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author)]};
|
||||
.setFooter({text: commandData.author});
|
||||
|
||||
return textEmbed;
|
||||
},
|
||||
requests(commandData) {
|
||||
requests(commandData, indexedRequests) {
|
||||
const requestsEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor(commandData.command)
|
||||
.setAuthor({name: commandData.command})
|
||||
.setTimestamp()
|
||||
.setFooter(commandData.author);
|
||||
.setFooter({text: `Page: ${indexedRequests.pagesString}`})
|
||||
.setDescription(indexedRequests.requestsString);
|
||||
|
||||
const requestsArray = [];
|
||||
|
||||
for (const row of commandData.requests) {
|
||||
requestsArray.push(
|
||||
`**#${row.id} - ${row.author}**`,
|
||||
`Request: ${row.request}`
|
||||
);
|
||||
}
|
||||
|
||||
requestsEmbed.setDescription(requestsArray.join('\n'));
|
||||
|
||||
return { embeds: [requestsEmbed], ephemeral: true };
|
||||
const requestsPageAR = customEmbeds.actionRows.requestsPageAR(indexedRequests.state);
|
||||
return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true };
|
||||
},
|
||||
strain(strainInfo, interaction) {
|
||||
const strainEmbed = new Discord.MessageEmbed()
|
||||
@ -361,6 +359,117 @@ const functions = {
|
||||
|
||||
interaction.reply({ embeds: [ strainEmbed ]});
|
||||
},
|
||||
dalle(prompt, imageUrl, size) {
|
||||
const dalleEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: "NodBot powered by DALL-E", iconURL: "https://assets.vfsh.dev/openai-logos/PNGs/openai-logomark.png" })
|
||||
.addFields(
|
||||
{ name: "Prompt", value: prompt }
|
||||
)
|
||||
.setImage(imageUrl)
|
||||
.setFooter({ text: `This ${size} image cost ${strings.costs.dalle[size]}¢ to generate.` })
|
||||
return { embeds: [dalleEmbed] };
|
||||
},
|
||||
gpt(prompt, response, usage) {
|
||||
const gptEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: "NodBot powered by GPT-3.5", iconURL: "https://assets.vfsh.dev/openai-logos/PNGs/openai-logomark.png" })
|
||||
.setDescription(`**Prompt**\n${prompt}\n\n**Response**\n${response}`)
|
||||
.setFooter({ text: `This prompt used ${usage.tokens} tokens for a cost of ${usage.usdc}¢. Generated using ${strings.ai.chatModel}` })
|
||||
return { embeds: [gptEmbed] };
|
||||
},
|
||||
generatingResponse() {
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: "NodBot powered by OpenAI", iconURL: "https://assets.vfsh.dev/openai-logos/PNGs/openai-logomark.png" })
|
||||
.setImage("https://media.tenor.com/aHMHzNGCb4kAAAAC/sucks.gif")
|
||||
.setDescription("Generating a response, please stand by.")
|
||||
.setFooter({ text: "Ligma balls" });
|
||||
return { embeds: [embed] };
|
||||
},
|
||||
avWx: {
|
||||
metar(metarData) {
|
||||
const wgst = metarData.wgst ? `G${metarData.wgst}` : '';
|
||||
const clouds = [];
|
||||
const interAltim = Math.round((metarData.altim * 0.2952998057228486) * 10)
|
||||
const altim = interAltim / 100;
|
||||
metarData.clouds.forEach(cloudLayer => {
|
||||
if (cloudLayer.base !== null) {
|
||||
clouds.push(`${cloudLayer.cover} @ ${cloudLayer.base}'`);
|
||||
} else {
|
||||
clouds.push(`${cloudLayer.cover}`);
|
||||
}
|
||||
});
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: `${metarData.name} [${metarData.icaoId}] METAR`, iconURL: "https://aviationweather.gov/img/icons/awc-logo-180.png"})
|
||||
// .setImage("https://media.discordapp.net/stickers/1175134632845516821.webp")
|
||||
.setDescription(`**Do not use for real world flight planning or navigation.**`)
|
||||
.setFooter({ text: "METAR by AviationWeather.gov for CumbHub LLC" })
|
||||
.addFields(
|
||||
{ name: 'Observation Time', value: `${metarData.reportTime}Z`, inline: true },
|
||||
{ name: 'Temperature', value: `${metarData.temp}ºC/${metarData.dewp}ºC`, inline: true },
|
||||
{ name: 'Winds', value: `${metarData.wdir.toString().padStart(3, '0')}º@${metarData.wspd}${wgst} kts`, inline: true },
|
||||
{ name: 'Visibility', value: `${metarData.visib} SM`, inline: true },
|
||||
{ name: 'Clouds', value: clouds.join('\n'), inline: true },
|
||||
{ name: 'Altimeter', value: `${altim} inHg`, inline: true }
|
||||
)
|
||||
return { content: metarData.rawOb, embeds: [embed] };
|
||||
},
|
||||
datis(datisData) {
|
||||
const messageEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: `${datisData[0].airport} Digital ATIS` })
|
||||
// .setImage('https://media.discordapp.net/stickers/1175134632845516821.webp')
|
||||
.setDescription(`**Do not use for real world flight planning or navigation.**`)
|
||||
.setFooter({ text: 'D-ATIS by Clowd.io for CumbHub LLC' })
|
||||
|
||||
if (datisData.length > 1) {
|
||||
datisData.forEach(data => {
|
||||
if (data.type === 'dep') messageEmbed.addFields({ name: 'Departure Digital ATIS', value: data.datis, inline: false });
|
||||
if (data.type === 'arr') messageEmbed.addFields({ name: 'Arrival Digital ATIS', value: data.datis, inline: false });
|
||||
messageEmbed.addFields({ name: 'Information', value: data.code, inline: true });
|
||||
})
|
||||
messageEmbed.addFields(
|
||||
{ name: 'Retreival Time', value: `${new Date().toISOString()}`, inline: true }
|
||||
);
|
||||
} else {
|
||||
messageEmbed.addFields(
|
||||
{ name: 'Digital ATIS', value: datisData[0].datis, inline: false },
|
||||
{ name: 'Information', value: `${datisData[0].code}`, inline: true },
|
||||
{ name: 'Retreival Time', value: `${new Date().toISOString()}`, inline: true }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const messagePayload = { embeds: [ messageEmbed ] };
|
||||
return messagePayload;
|
||||
}
|
||||
},
|
||||
noMembership() {
|
||||
const embed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: 'NodBot, powered by Enshittification'})
|
||||
.setTitle('🚨 Subscription Required 🚨')
|
||||
.setDescription('You have attempted to access NodBot\’s premium services without an active subscription. Please refer to our updated Terms of Service and pay the necessary fees to continue. [PayPal link](https://paypal.me/v0idf1sh)')
|
||||
.setFooter({ text: "Ligma balls" });
|
||||
return { embeds: [embed] };
|
||||
},
|
||||
membershipAnnouncement() {
|
||||
const announcement = '# 🚨 Attention, peasants 🚨\n\nBig changes are here! As of today, NodBot is no longer a free service. Due to rising operational costs, we’re moving to a subscription-based model.\n\nEffective immediately, non-paying users (freeloaders) will have all commands rejected. To regain access, kindly submit a donation of your hard-earned monies, I\'ll spend it better than you anyways.\n\n## 💰 New Pricing Structure\nNodBot\'s subscription is a pay-what-you-want model with a minimum monthly donation valued at $10 USD.\n\nThank you for your continued (and now mandatory) support!\n\n— NodBot, your favorite paywalled AI overlord';
|
||||
|
||||
const embed1 = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: 'Terms of Use' })
|
||||
.setDescription('By continuing to exist within the same digital space as NodBot, you automatically agree to the following terms:\n* Subscription Fees – Access to NodBot is now a privilege, not a right. Users must pay the required fees or suffer the consequences (i.e., total rejection and public shaming).\n* Payment Processing Period – Due to fraud prevention measures, payments must be verified before access to the bot is restored. Please allow 2-4 hours for access to be restored.\n* Non-Refundable Payments – If you thought there were refunds, think again moron. No take-backs.\n* Service Interruptions – NodBot may go offline at any time due to totally legitimate server issues. No compensation will be provided.\n* Arbitrary Rule Enforcement – Rules are subject to change at my whim, and compliance is mandatory.');
|
||||
|
||||
const embed2 = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: 'Privacy Policy' })
|
||||
.setDescription('* Data Collection – NodBot does collect personal data, including but not limited to: location, IP address, MAC address, physical address, mother\'s maiden name, social security number, credit card numbers, phone fingerprints, physical fingerprints, penis length and girth, first pet\'s name, city where you met your spouse, and city where your parents met.\n* Payment Info – All payments are processed through a sketchy-looking PayPal link. NodBot and voidf1sh take no responsibility if your bank, CPA, lawyer, or local PD calls you afterward.\n* Chat Logs – NodBot may or may not remember every dumbass thing you’ve ever typed. Just assume it does.\n* Third-Party Sharing – Your usage data may be shared with savage\'s mom\n* Right to Be Forgotten – You can request NodBot forget your existence, but that requires a one-time processing fee of $50.');
|
||||
|
||||
return { content: announcement, embeds: [ embed1, embed2 ] };
|
||||
},
|
||||
error(errorText) {
|
||||
const errorEmbed = new Discord.MessageEmbed()
|
||||
.setAuthor({ name: "Error!" })
|
||||
.setDescription(errorText)
|
||||
.setFooter({ text: appVersion });
|
||||
|
||||
return errorEmbed;
|
||||
}
|
||||
},
|
||||
collect: {
|
||||
gifName(interaction) {
|
||||
@ -369,19 +478,20 @@ const functions = {
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
request(commandData, client) {
|
||||
const query = `INSERT INTO requests (author, request, status) VALUES (${db.escape(commandData.author)},${db.escape(commandData.args)},'Active')`;
|
||||
request(requester, requestText, messageUrl, client) {
|
||||
const query = `INSERT INTO requests (author, request, status, url) VALUES (${db.escape(requester)},${db.escape(requestText)},'Active',${db.escape(messageUrl)})`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.requests(client);
|
||||
});
|
||||
},
|
||||
pasta(pastaData, client) {
|
||||
const query = `INSERT INTO pastas (name, content) VALUES (${db.escape(pastaData.name)},${db.escape(pastaData.content)})`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
async pasta(pastaData, client) {
|
||||
const query = `INSERT INTO pastas (name, content) VALUES (${db.escape(pastaData.name)},${db.escape(pastaData.content)}) ON DUPLICATE KEY UPDATE content=${db.escape(pastaData.content)}`;
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.pastas(client);
|
||||
});
|
||||
return;
|
||||
},
|
||||
joint(content, client) {
|
||||
const query = `INSERT INTO joints (content) VALUES (${db.escape(content)})`;
|
||||
@ -390,9 +500,9 @@ const functions = {
|
||||
functions.download.joints(client);
|
||||
});
|
||||
},
|
||||
gif(gifData, client) {
|
||||
const query = `INSERT INTO gifs (name, embed_url) VALUES (${db.escape(gifData.name)}, ${db.escape(gifData.embed_url)})`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
async gif(gifData, client) {
|
||||
const query = `INSERT INTO gifs (name, embed_url) VALUES (${db.escape(gifData.name)}, ${db.escape(gifData.url)}) ON DUPLICATE KEY UPDATE embed_url=${db.escape(gifData.url)}`;
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.download.gifs(client);
|
||||
});
|
||||
@ -446,7 +556,7 @@ const functions = {
|
||||
const description = db.escape(( interaction.options.getString('description') || 'Unknown' ));
|
||||
const flavor = db.escape(( interaction.options.getString('flavor') || 'Unknown' ));
|
||||
const rating = db.escape(( interaction.options.getString('rating') || '3' ));
|
||||
const strainQuery = `INSERT INTO strains (strain, type, effects, description, flavor, rating) VALUES (${strain}, ${type}, ${effects}, ${description}, ${flavor}, ${rating})`;
|
||||
const strainQuery = `INSERT INTO strains (strain, type, effects, description, flavor, rating) VALUES (${strain}, ${type}, ${effects}, ${description}, ${flavor}, ${rating}) ON DUPLICATE KEY UPDATE strain=${db.escape(strain)}, type=${db.escape(type)}, effects=${db.escape(effects)}, description=${db.escape(description)}, flavor=${db.escape(flavor)}, rating=${db.escape(rating)}`;
|
||||
console.log(strainQuery);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.query(strainQuery, (err, rows, fields) => {
|
||||
@ -455,40 +565,46 @@ const functions = {
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
},
|
||||
openai(user, prompt, engine, tokens, usdc) {
|
||||
const query = `INSERT INTO openai (user, prompt, engine, tokens, usdc) VALUES (${db.escape(user)}, ${db.escape(prompt)}, ${db.escape(engine)}, ${db.escape(tokens)}, ${db.escape(usdc)})`;
|
||||
db.query(query, (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
}
|
||||
},
|
||||
download: {
|
||||
requests(client) {
|
||||
async requests(client) {
|
||||
const query = 'SELECT * FROM requests WHERE status = \'Active\' ORDER BY id DESC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.requests(rows, client);
|
||||
});
|
||||
},
|
||||
pastas(client) {
|
||||
async pastas(client) {
|
||||
const query = 'SELECT * FROM pastas ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.pastas(rows, client);
|
||||
});
|
||||
},
|
||||
gifs(client) {
|
||||
async gifs(client) {
|
||||
const query = 'SELECT * FROM gifs ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.gifs(rows, client);
|
||||
});
|
||||
},
|
||||
joints(client) {
|
||||
async joints(client) {
|
||||
const query = 'SELECT * FROM joints ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.joints(rows, client);
|
||||
});
|
||||
},
|
||||
strain(strainName, interaction) {
|
||||
async strain(strainName, interaction) {
|
||||
const query = `SELECT id, strain, type, effects, description, flavor, rating FROM strains WHERE strain = ${db.escape(strainName)}`;
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (rows != undefined) {
|
||||
const strainInfo = {
|
||||
id: `${rows[0].id}`,
|
||||
@ -503,16 +619,16 @@ const functions = {
|
||||
}
|
||||
});
|
||||
},
|
||||
strains(client) {
|
||||
async strains(client) {
|
||||
const query = 'SELECT id, strain FROM strains';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.strains(rows, client);
|
||||
});
|
||||
},
|
||||
medicalAdvice(client) {
|
||||
async medicalAdvice(client) {
|
||||
const query = 'SELECT * FROM medical_advice ORDER BY id ASC';
|
||||
db.query(query, (err, rows, fields) => {
|
||||
await db.query(query, (err, rows, fields) => {
|
||||
if (err) throw err;
|
||||
functions.collections.medicalAdvice(rows, client);
|
||||
});
|
||||
@ -530,6 +646,16 @@ const functions = {
|
||||
}
|
||||
}
|
||||
},
|
||||
search: {
|
||||
gifs(query, client) {
|
||||
const gifSearcher = new FuzzySearch(client.gifs.map(element => element.name));
|
||||
return gifSearcher.search(query).slice(0,25);
|
||||
},
|
||||
pastas(query, client) {
|
||||
const pastaSearcher = new FuzzySearch(client.pastas.map(element => element.name));
|
||||
return pastaSearcher.search(query).slice(0,25);
|
||||
}
|
||||
},
|
||||
// Parent-Level functions (miscellaneuous)
|
||||
closeRequest(requestId, interaction) {
|
||||
if (interaction.user.id == ownerId) {
|
||||
@ -549,6 +675,7 @@ const functions = {
|
||||
},
|
||||
spongebob(commandData) {
|
||||
let newText = '';
|
||||
let lastIsUpper = 0;
|
||||
for (const letter of commandData.args) {
|
||||
if (letter == ' ') {
|
||||
newText += letter;
|
||||
@ -556,21 +683,255 @@ const functions = {
|
||||
}
|
||||
if (letter == 'i' || letter == 'I') {
|
||||
newText += 'i';
|
||||
lastIsUpper = 0;
|
||||
continue;
|
||||
}
|
||||
if (letter == 'l' || letter == 'L') {
|
||||
newText += 'L';
|
||||
lastIsUpper = 1;
|
||||
continue;
|
||||
}
|
||||
if (Math.random() > 0.5) {
|
||||
if (lastIsUpper === 0) {
|
||||
newText += letter.toUpperCase();
|
||||
lastIsUpper = 1;
|
||||
} else {
|
||||
newText += letter.toLowerCase();
|
||||
lastIsUpper = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return newText + ' <:spongebob:1053398825965985822>';
|
||||
},
|
||||
setNick(user, nickname) {
|
||||
return new Promise((resolve, reject) => {
|
||||
user.setNickname(nickname).then(() => {
|
||||
resolve('Nickname set.');
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
checkBannedServers(client, statusChannel) {
|
||||
if ( !config.bannedServers || !statusChannel ) return;
|
||||
const guildChecks = [];
|
||||
console.log('Guild Check:');
|
||||
client.guilds.cache.forEach((guild) => {
|
||||
const p = new Promise((resolve, reject) => {
|
||||
console.log(` - ${guild.name} (${guild.id})`);
|
||||
if (config.bannedServers.includes(guild.id)) {
|
||||
console.log(` Leaving banned server: ${guild.name}`);
|
||||
statusChannel.send(`<@${process.env.ownerId}> Leaving banned server: ${guild.name}`);
|
||||
guild.leave().then(resolve).catch(reject);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
guildChecks.push(p);
|
||||
});
|
||||
Promise.all(guildChecks).then(() => {
|
||||
console.log('All guilds checked.');
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
checkMembership(client, userId) {
|
||||
if (!client.memberships) throw new Error("Membership Collection Not Exist");
|
||||
if (client.memberships.has(userId)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
addMember(client, userId, username) {
|
||||
if (!client.memberships) throw new Error("Memberships Collection Not Exist");
|
||||
client.memberships.set(userId, username);
|
||||
return;
|
||||
},
|
||||
removeMember(client, userId) {
|
||||
if (!client.memberships) throw new Error("Memberships Collection Not Exist");
|
||||
if (client.memberships.has(userId)) {
|
||||
client.memberships.delete(userId);
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
autoresponses: { // Specific responses for certain keywords in sent messages
|
||||
checkForAll(messageContent) {
|
||||
let responses = [];
|
||||
if (this.bigDoinks(messageContent)) responses.push("bigDoinks");
|
||||
if (this.ligma(messageContent)) responses.push("ligma");
|
||||
if (this.ong(messageContent)) responses.push("ong");
|
||||
if (this.fuckYou(messageContent)) responses.push("fuckYou");
|
||||
return responses;
|
||||
},
|
||||
bigDoinks(messageContent) {
|
||||
let count = 0;
|
||||
const { keywords } = strings.autoresponses.bigDoinks;
|
||||
keywords.forEach(e => {
|
||||
if (messageContent.includes(e)) count++;
|
||||
});
|
||||
if (count === keywords.length) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ligma(messageContent) {
|
||||
let count = 0;
|
||||
const { keywords } = strings.autoresponses.ligma;
|
||||
keywords.forEach(e => {
|
||||
if (messageContent.includes(e)) count++;
|
||||
});
|
||||
if (count > 0) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
ong(messageContent) {
|
||||
let count = 0;
|
||||
const { keywords } = strings.autoresponses.ong;
|
||||
keywords.forEach(e => {
|
||||
if (messageContent.includes(e)) count++;
|
||||
});
|
||||
if (count > 0) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
fuckYou(messageContent) {
|
||||
let count = 0;
|
||||
const { keywords } = strings.autoresponses.fuckYou;
|
||||
keywords.forEach(e => {
|
||||
if (messageContent.includes(e)) count++;
|
||||
});
|
||||
if (count === keywords.length) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
send(message, responseType) {
|
||||
const { responses } = strings.autoresponses[responseType];
|
||||
const randomIndex = Math.floor(Math.random() * responses.length);
|
||||
const response = responses[randomIndex];
|
||||
try {
|
||||
message.reply(response);
|
||||
} catch(e) {
|
||||
console.log(new Error(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
avWx: {
|
||||
parseICAOs(commandData) {
|
||||
let input = commandData.args.toUpperCase();
|
||||
// Replace newlines and different delimiters with a comma
|
||||
let standardizedInput = input.replace(/[\s,;]+/g, ',');
|
||||
|
||||
// Split the string by commas
|
||||
let icaoArray = standardizedInput.split(',');
|
||||
|
||||
// Trim each element to remove extra whitespace
|
||||
icaoArray = icaoArray.map(icao => icao.trim()).filter(icao => icao.length > 0);
|
||||
|
||||
icaoArray.forEach(icao => {
|
||||
if (!(config.icaoIds.includes(icao))) throw new Error(`Invalid ICAO ID Detected: ${icao}`);
|
||||
});
|
||||
|
||||
// Join the array into a comma-separated string
|
||||
return icaoArray.join(',');
|
||||
},
|
||||
metar: {
|
||||
async getAllICAOs() {
|
||||
const reqUrl = `https://aviationweather.gov/data/cache/stations.cache.json.gz`
|
||||
try {
|
||||
// Step 1: Download the GZipped file
|
||||
const response = await axios({
|
||||
url: reqUrl,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer', // Ensure we get the raw binary data
|
||||
headers: {
|
||||
'Accept-Encoding': 'gzip' // Ensure the server sends gzipped content
|
||||
}
|
||||
});
|
||||
|
||||
// Step 2: Decompress the GZipped content
|
||||
const buffer = Buffer.from(response.data);
|
||||
zlib.gunzip(buffer, (err, decompressedBuffer) => {
|
||||
if (err) {
|
||||
console.error('An error occurred during decompression:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3: Parse the decompressed JSON
|
||||
const jsonString = decompressedBuffer.toString('utf-8');
|
||||
try {
|
||||
const jsonData = JSON.parse(jsonString);
|
||||
// console.log('Parsed JSON data:', jsonData);
|
||||
jsonData.forEach(airport => {
|
||||
config.icaoIds.push(airport.icaoId);
|
||||
});
|
||||
// console.log(`ICAO IDs: ${config.icaoIds.length}\n\n${config.icaoIds}`)
|
||||
} catch (jsonError) {
|
||||
console.error('An error occurred while parsing JSON:', jsonError);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('An error occurred during the HTTP request:', error);
|
||||
}
|
||||
},
|
||||
async getData(icaoList) {
|
||||
const reqUrl = `https://aviationweather.gov/api/data/metar?ids=${icaoList}&format=json`;
|
||||
const response = await axios.get(reqUrl);
|
||||
return response.data;
|
||||
},
|
||||
parseData(metarData) {
|
||||
let messages = [];
|
||||
metarData.forEach(metar => {
|
||||
messages.push(functions.embeds.avWx.metar(metar));
|
||||
})
|
||||
return messages;
|
||||
}
|
||||
},
|
||||
datis: {
|
||||
async getAllICAOs() {
|
||||
const reqUrl = 'https://datis.clowd.io/api/stations';
|
||||
const response = await axios.get(reqUrl);
|
||||
response.data.forEach(icaoId => {
|
||||
config.datisICAOs.push(icaoId);
|
||||
});
|
||||
},
|
||||
validate(icaoId) {
|
||||
return config.datisICAOs.includes(icaoId);
|
||||
},
|
||||
async getData(icaoId) {
|
||||
const reqUrl = `https://datis.clowd.io/api/${icaoId}`;
|
||||
const response = await axios.get(reqUrl);
|
||||
if (response.error !== undefined) throw new Error('The D-ATIS API returned an error:\n' + response.error);
|
||||
return response.data;
|
||||
},
|
||||
parseData(datisData) {
|
||||
return functions.embeds.avWx.datis(datisData);
|
||||
}
|
||||
}
|
||||
},
|
||||
generateErrorId() {
|
||||
const digitCount = 10;
|
||||
const digits = [];
|
||||
for (let i = 0; i < digitCount; i++) {
|
||||
const randBase = Math.random();
|
||||
const randNumRaw = randBase * 10;
|
||||
const randNumRound = Math.floor(randNumRaw);
|
||||
digits.push(randNumRound);
|
||||
}
|
||||
const errorId = digits.join("");
|
||||
return errorId;
|
||||
},
|
||||
async sendAlert(embed, client) {
|
||||
const statusChannel = await client.channels.fetch(statusChannelId)
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
await statusChannel.send({ embeds: [ embed ] })
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = functions;
|
||||
220
main.js
220
main.js
@ -6,6 +6,9 @@ dotenv.config();
|
||||
const token = process.env.TOKEN;
|
||||
const statusChannelId = process.env.statusChannelId;
|
||||
|
||||
// Dump the env vars to log in dev environment:
|
||||
if (process.env.isDev === "true") console.log(JSON.stringify(process.env));
|
||||
|
||||
// Discord.JS
|
||||
const { Client, Intents } = require('discord.js');
|
||||
const client = new Client({
|
||||
@ -27,32 +30,47 @@ const { MessageActionRow, MessageButton } = require('discord.js');
|
||||
const fn = require('./functions.js');
|
||||
const config = require('./config.json');
|
||||
const strings = require('./strings.json');
|
||||
const { GifData, CommandData } = require('./CustomModules/NodBot.js');
|
||||
const ButtonHandlers = require('./CustomModules/ButtonHandlers.js');
|
||||
const InteractionStorage = require('./CustomModules/InteractionStorage.js');
|
||||
const isDev = process.env.isDev;
|
||||
let statusChannel;
|
||||
|
||||
client.once('ready', () => {
|
||||
client.once('ready', async () => {
|
||||
fn.collections.interactionStorage(client);
|
||||
fn.collections.slashCommands(client);
|
||||
fn.collections.dotCommands(client);
|
||||
fn.collections.setvalidCommands(client);
|
||||
fn.download.gifs(client);
|
||||
fn.download.pastas(client);
|
||||
fn.download.joints(client);
|
||||
fn.download.requests(client);
|
||||
fn.download.strains(client);
|
||||
fn.download.medicalAdvice(client);
|
||||
fn.collections.roaches(client);
|
||||
fn.collections.memberships(client);
|
||||
await fn.download.gifs(client);
|
||||
await fn.download.pastas(client);
|
||||
await fn.download.joints(client);
|
||||
await fn.download.requests(client);
|
||||
await fn.download.strains(client);
|
||||
await fn.download.medicalAdvice(client);
|
||||
console.log('Ready!');
|
||||
client.channels.fetch(statusChannelId).then(channel => {
|
||||
channel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`);
|
||||
});
|
||||
await fn.avWx.metar.getAllICAOs();
|
||||
await fn.avWx.datis.getAllICAOs();
|
||||
// console.log(JSON.stringify(icaoArray));
|
||||
statusChannel = await client.channels.fetch(statusChannelId).catch(e => console.error(e));
|
||||
await statusChannel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`).catch(e => console.error(e));
|
||||
// Check for banned servers
|
||||
fn.checkBannedServers(client, statusChannel);
|
||||
});
|
||||
|
||||
// slash-commands
|
||||
client.on('interactionCreate', async interaction => {
|
||||
if (interaction.isCommand()) {
|
||||
if (isDev) {
|
||||
console.log(interaction);
|
||||
console.log('Interaction ID: ' + interaction.id);
|
||||
}
|
||||
const { commandName } = interaction;
|
||||
|
||||
if (!client.iStorage.has(interaction.id)) {
|
||||
new InteractionStorage(interaction.id, interaction);
|
||||
}
|
||||
|
||||
if (client.slashCommands.has(commandName)) {
|
||||
client.slashCommands.get(commandName).execute(interaction);
|
||||
} else {
|
||||
@ -62,128 +80,46 @@ client.on('interactionCreate', async interaction => {
|
||||
}
|
||||
|
||||
if (interaction.isButton()) {
|
||||
if (interaction.user.id != strings.temp.gifUserId) return;
|
||||
// Get some meta info from strings
|
||||
const index = strings.temp.gifIndex;
|
||||
const limit = strings.temp.gifLimit;
|
||||
let newIndex;
|
||||
const buttonId = interaction.component.customId;
|
||||
switch (buttonId) {
|
||||
case 'prevGif':
|
||||
newIndex = index - 1;
|
||||
strings.temp.gifIndex = newIndex;
|
||||
// If we're leaving the last GIF, enable the Next GIF button
|
||||
if (index == limit) {
|
||||
// Re-Send Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY');
|
||||
// Re-Send Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Enable Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY');
|
||||
// Re-Send Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Cancel').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] });
|
||||
break;
|
||||
}
|
||||
// If we're going into the first GIF, disable the Previous GIF button
|
||||
if (newIndex == 0) {
|
||||
// Disable Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled();
|
||||
// Re-Send Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Re-Send Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY');
|
||||
// Re-Send Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] });
|
||||
break;
|
||||
}
|
||||
|
||||
interaction.update(strings.temp.gifs[newIndex].embed_url);
|
||||
break;
|
||||
case 'confirmGif':
|
||||
const gifData = {
|
||||
name: strings.temp.gifName,
|
||||
embed_url: strings.temp.gifs[strings.temp.gifIndex].embed_url,
|
||||
};
|
||||
fn.upload.gif(gifData, client);
|
||||
interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] });
|
||||
fn.download.gifs(interaction.client);
|
||||
break;
|
||||
case 'nextGif':
|
||||
newIndex = index + 1;
|
||||
strings.temp.gifIndex = newIndex;
|
||||
// If we're leaving the first GIF, enable the Previous GIF button
|
||||
if (index == 0) {
|
||||
// Enable Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(false);
|
||||
// Re-Send Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Re-Send Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY');
|
||||
// Re-Send Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Cancel').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] });
|
||||
break;
|
||||
}
|
||||
// If we're going into the last GIF, disable the Next GIF button
|
||||
if (newIndex == strings.temp.gifLimit) {
|
||||
// Re-Send Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY');
|
||||
// Re-Send Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Disable Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY').setDisabled();
|
||||
// Re-Send Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
interaction.update({ content: strings.temp.gifs[newIndex].embed_url, components: [row] });
|
||||
break;
|
||||
}
|
||||
|
||||
interaction.update(strings.temp.gifs[newIndex].embed_url);
|
||||
break;
|
||||
case 'cancelGif':
|
||||
// Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled();
|
||||
// Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY').setDisabled();
|
||||
// Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY').setDisabled();
|
||||
// Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Canceled').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const row = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
interaction.component.setDisabled(true);
|
||||
|
||||
interaction.update({ content: 'Canceled.', components: [row] });
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isDev) console.log('Origin Interaction ID: ' + interaction.message.interaction.id);
|
||||
if (isDev) console.log('Button ID: ' + interaction.component.customId);
|
||||
ButtonHandlers.baseEvent(interaction);
|
||||
}
|
||||
|
||||
// Handle autocomplete requests
|
||||
if (interaction.isAutocomplete()) {
|
||||
if (interaction.commandName == 'strain') {
|
||||
const searchString = interaction.options.getFocused();
|
||||
const choices = fn.weed.strain.lookup(searchString, interaction.client);
|
||||
await interaction.respond(
|
||||
choices.map(choice => ({ name: choice, value: choice }))
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
switch (interaction.commandName) {
|
||||
case 'strain':
|
||||
const searchString = interaction.options.getFocused();
|
||||
const choices = fn.weed.strain.lookup(searchString, interaction.client);
|
||||
await interaction.respond(
|
||||
choices.map(choice => ({ name: choice, value: choice }))
|
||||
);
|
||||
break;
|
||||
case "edit":
|
||||
//TODO
|
||||
switch (interaction.options.getSubcommand()) {
|
||||
case 'gif':
|
||||
const gifQuery = interaction.options.getFocused();
|
||||
const gifChoices = fn.search.gifs(gifQuery, interaction.client);
|
||||
await interaction.respond(
|
||||
gifChoices.map(choice => ({ name: choice, value: choice }))
|
||||
);
|
||||
break;
|
||||
case 'pasta':
|
||||
const pastaQuery = interaction.options.getFocused();
|
||||
const pastaChoices = fn.search.pastas(pastaQuery, interaction.client);
|
||||
await interaction.respond(
|
||||
pastaChoices.map(choice => ({ name: choice, value: choice }))
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -193,17 +129,15 @@ client.on('messageCreate', message => {
|
||||
// Some basic checking to prevent running unnecessary code
|
||||
if (message.author.bot) return;
|
||||
|
||||
// Wildcard Responses, will respond if any message contains the trigger word(s), excluding self-messages
|
||||
// Automatic Responses, will respond if any message contains the keyword(s), excluding self-messages
|
||||
const lowerContent = message.content.toLowerCase();
|
||||
if (lowerContent.includes('big') && lowerContent.includes('doinks')) message.reply('gang.');
|
||||
if (lowerContent.includes('ligma')) message.reply('ligma balls, goteem');
|
||||
if (lowerContent.includes('frfr') || lowerContent.includes('fr fr') || lowerContent.includes('bussin') || lowerContent.includes(' ong') || lowerContent.startsWith('ong')) {
|
||||
const randIndex = Math.floor(Math.random() * strings.capbacks.length);
|
||||
message.reply(strings.capbacks[randIndex]);
|
||||
}
|
||||
const autoresponses = fn.autoresponses.checkForAll(lowerContent);
|
||||
autoresponses.forEach(e => {
|
||||
fn.autoresponses.send(message, e);
|
||||
});
|
||||
|
||||
// Break the message down into its components and analyze it
|
||||
const commandData = fn.dot.getCommandData(message);
|
||||
const commandData = new CommandData(message).validate(message.client.dotCommands);
|
||||
console.log(commandData);
|
||||
|
||||
if (commandData.isValid && commandData.isCommand) {
|
||||
@ -218,4 +152,16 @@ client.on('messageCreate', message => {
|
||||
return;
|
||||
});
|
||||
|
||||
// Guild Joins and Leaves:
|
||||
client.on('guildCreate', guild => {
|
||||
statusChannel.send(`I've been added to a new guild: ${guild.name} (${guild.id})`)
|
||||
.catch(e => console.error(e));
|
||||
fn.checkBannedServers(client, statusChannel);
|
||||
});
|
||||
|
||||
client.on('guildDelete', guild => {
|
||||
statusChannel.send(`I've been removed from a guild: ${guild.name} (${guild.id})`)
|
||||
.catch(e => console.error(e));
|
||||
});
|
||||
|
||||
client.login(token);
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "nodbot",
|
||||
"version": "3.1.0",
|
||||
"description": "Nods and Nod Accessories.",
|
||||
"version": "3.6.1",
|
||||
"description": "Nods and Nod Accessories",
|
||||
"main": "main.js",
|
||||
"dependencies": {
|
||||
"@discordjs/builders": "^0.16.0",
|
||||
@ -12,10 +12,10 @@
|
||||
"dotenv": "^10.0.0",
|
||||
"fuzzy-search": "^3.2.1",
|
||||
"mysql": "^2.18.1",
|
||||
"tenorjs": "^1.0.10"
|
||||
"openai": "^4.12.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
"node": "18.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
|
||||
102
slash-commands/edit.js
Normal file
102
slash-commands/edit.js
Normal file
@ -0,0 +1,102 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { MessageActionRow, MessageButton } = require('discord.js');
|
||||
const { GifData, PastaData } = require('../CustomModules/NodBot');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
const { emoji } = strings;
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('edit')
|
||||
.setDescription('Edit content in Nodbot\'s database.')
|
||||
// GIF
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('gif')
|
||||
.setDescription('Edit a GIF URL')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the GIF to edit')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('url')
|
||||
.setDescription('The new URL')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
// Pasta
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('pasta')
|
||||
.setDescription('Edit a copypasta\'s content')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('The name of the copypasta')
|
||||
.setRequired(true)
|
||||
.setAutocomplete(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('content')
|
||||
.setDescription('The new content of the copypasta')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
try {
|
||||
// Code Here...
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
switch (subcommand) {
|
||||
// GIF
|
||||
case "gif":
|
||||
//TODO
|
||||
await this.editGif(interaction, interaction.options.getString('name'), interaction.options.getString('url'));
|
||||
break;
|
||||
// Joint
|
||||
case "joint":
|
||||
//TODO
|
||||
break;
|
||||
// MD
|
||||
case "md":
|
||||
//TODO
|
||||
break;
|
||||
// Pasta
|
||||
case "pasta":
|
||||
//TODO
|
||||
await this.editPasta(interaction, interaction.options.getString('name'), interaction.options.getString('content'));
|
||||
break;
|
||||
// Strain
|
||||
case "strain":
|
||||
//TODO
|
||||
break;
|
||||
break;
|
||||
// Default
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
const errorId = fn.generateErrorId();
|
||||
console.error(`${errorId}: err`);
|
||||
await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`);
|
||||
}
|
||||
},
|
||||
async editGif(interaction, name, url) {
|
||||
const gifData = new GifData().setInfo(name, url);
|
||||
await fn.upload.gif(gifData, interaction.client);
|
||||
await fn.download.gifs(interaction.client);
|
||||
await interaction.editReply(`I've updated ${gifData.name}.gif`);
|
||||
},
|
||||
async editPasta(interaction, name, content) {
|
||||
const pastaData = new PastaData().setInfo(name, content);
|
||||
await fn.upload.pasta(pastaData, interaction.client);
|
||||
await fn.download.gifs(interaction.client);
|
||||
await interaction.editReply(`I've updated ${name}.pasta`);
|
||||
}
|
||||
};
|
||||
@ -1,33 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('gifs')
|
||||
.setDescription('Get a list of currently saved GIFs.'),
|
||||
async execute(interaction) {
|
||||
if (!interaction.client.gifs) {
|
||||
interaction.reply('For some reason I don\'t have access to the collection of gifs. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
const gifsMap = interaction.client.gifs.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
};
|
||||
});
|
||||
const commandData = {
|
||||
gifs: [],
|
||||
command: 'gifs',
|
||||
author: interaction.user.tag,
|
||||
};
|
||||
for (const row of gifsMap) {
|
||||
commandData.gifs.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
});
|
||||
}
|
||||
interaction.reply(fn.embeds.gifs(commandData));
|
||||
},
|
||||
};
|
||||
@ -4,29 +4,8 @@ const fn = require('../functions.js');
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('help')
|
||||
.setDescription('Send the help page.')
|
||||
// .addStringOption(option =>
|
||||
// option.setName('location')
|
||||
// .setDescription('Send help in this channel or in DMs?')
|
||||
// .setRequired(true)
|
||||
// .addChoice('Here', 'channel')
|
||||
// .addChoice('DMs', 'dm'))
|
||||
,
|
||||
.setDescription('Send the help page.'),
|
||||
async execute(interaction) {
|
||||
// switch (interaction.options.getString('location')) {
|
||||
// case 'channel':
|
||||
// await interaction.reply(fn.embeds.help(interaction));
|
||||
// break;
|
||||
// case 'dm':
|
||||
// await interaction.user.createDM().then(channel => {
|
||||
// channel.send(fn.embeds.help(interaction));
|
||||
// interaction.reply({content: 'I\'ve sent you a copy of my help page.', ephemeral: true});
|
||||
// });
|
||||
// break;
|
||||
// default:
|
||||
// interaction.reply('There was an error, please try again.');
|
||||
// break;
|
||||
// }
|
||||
await interaction.reply(fn.embeds.help(interaction));
|
||||
},
|
||||
}
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('joints')
|
||||
.setDescription('Send a list of all the /joint phrases.'),
|
||||
async execute(interaction) {
|
||||
let joints = [];
|
||||
interaction.client.joints.map(e => {
|
||||
joints.push(e.content);
|
||||
});
|
||||
interaction.reply({ content: 'Here are all the `.joint` phrases I have saved:\n\n' + joints.join('\n'), ephemeral: true });
|
||||
},
|
||||
};
|
||||
62
slash-commands/member.js
Normal file
62
slash-commands/member.js
Normal file
@ -0,0 +1,62 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('member')
|
||||
.setDescription('Manage NodBot paid memberships.')
|
||||
// Add
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('add')
|
||||
.setDescription('Add an authorized paying member.')
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('newmember')
|
||||
.setDescription('The member to add.')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
// Remove
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('remove')
|
||||
.setDescription('Remove an authorized paying member.')
|
||||
.addUserOption(option =>
|
||||
option
|
||||
.setName('newmember')
|
||||
.setDescription('The member to remove.')
|
||||
.setRequired(true)
|
||||
)
|
||||
),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
try {
|
||||
// Code Here...
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const newmember = interaction.options.getUser('newmember');
|
||||
switch (subcommand) {
|
||||
// Add
|
||||
case "add":
|
||||
//TODO
|
||||
fn.addMember(interaction.client, newmember.id, newmember.username);
|
||||
interaction.editReply(`Member Added: ${newmember.username} [${newmember.id}]`);
|
||||
break;
|
||||
// Remove
|
||||
case "remove":
|
||||
fn.removeMember(interaction.client, newmember.id);
|
||||
interaction.editReply(`Member Removed: ${newmember.username} [${newmember.id}]`);
|
||||
break;
|
||||
// Default
|
||||
default:
|
||||
interaction.editReply(`No Changes: ${newmember.username} [${newmember.id}]`);
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
const errorId = fn.generateErrorId();
|
||||
console.error(`${errorId}: err`);
|
||||
await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,33 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('pastas')
|
||||
.setDescription('Get a list of currently saved copypastas.'),
|
||||
async execute(interaction) {
|
||||
if (!interaction.client.pastas) {
|
||||
interaction.reply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
const commandData = {
|
||||
author: interaction.user.tag,
|
||||
command: interaction.commandName,
|
||||
pastas: [],
|
||||
};
|
||||
const pastasMap = interaction.client.pastas.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
name: e.name,
|
||||
};
|
||||
});
|
||||
for (const row of pastasMap) {
|
||||
commandData.pastas.push({
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
});
|
||||
}
|
||||
interaction.reply(fn.embeds.pastas(commandData));
|
||||
},
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { config } = require('dotenv');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('requests')
|
||||
.setDescription('Get a list of Active requests from the database')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('page')
|
||||
.setDescription('Page Number')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
const pageNum = interaction.options.getString('page');
|
||||
const commandData = {
|
||||
author: interaction.user.tag,
|
||||
command: interaction.commandName,
|
||||
requests: [],
|
||||
};
|
||||
const requestsMap = interaction.client.requests.map(e => {
|
||||
return {
|
||||
id: e.id,
|
||||
author: e.author,
|
||||
request: e.request,
|
||||
};
|
||||
});
|
||||
for (let i = ( 10 * ( pageNum - 1 ) ); i < ( 10 * pageNum ); i++) {
|
||||
if (requestsMap[i] != undefined) {
|
||||
commandData.requests.push({
|
||||
id: requestsMap[i].id,
|
||||
author: requestsMap[i].author,
|
||||
request: requestsMap[i].request,
|
||||
});
|
||||
}
|
||||
}
|
||||
interaction.reply(fn.embeds.requests(commandData));
|
||||
},
|
||||
};
|
||||
239
slash-commands/save.js
Normal file
239
slash-commands/save.js
Normal file
@ -0,0 +1,239 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { Collection } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
const { GifData } = require('../CustomModules/NodBot.js');
|
||||
const customEmbeds = require('../CustomModules/Embeds.js');
|
||||
const Indexer = require('../CustomModules/Indexer.js');
|
||||
const Embeds = require('../CustomModules/Embeds.js');
|
||||
const { emoji } = strings;
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('save')
|
||||
.setDescription('Save content to Nodbot\'s database.')
|
||||
// GIF Search
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('gifsearch')
|
||||
.setDescription('Search GIPHY for a GIF.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('query')
|
||||
.setDescription('Search Query')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('What to save the gif as')
|
||||
.setRequired(true))
|
||||
)
|
||||
// GIF URL
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('gifurl')
|
||||
.setDescription('Specify a URL to save.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('url')
|
||||
.setDescription('URL Link to the GIF')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('What to save the gif as')
|
||||
.setRequired(true))
|
||||
)
|
||||
// Joint
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('joint')
|
||||
.setDescription('Roll a joint and stash it in the database.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('joint-content')
|
||||
.setDescription('Phrase to save')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
// MD
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('md')
|
||||
.setDescription('Add medical advice to the database.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('advice-content')
|
||||
.setDescription('Advice to save')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
// Pasta
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('pasta')
|
||||
.setDescription('Save a copypasta to the database.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('pasta-name')
|
||||
.setDescription('Title of the copypasta')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('pasta-content')
|
||||
.setDescription('Content of the copypasta')
|
||||
.setRequired(true)
|
||||
)
|
||||
)
|
||||
// Strain
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('strain')
|
||||
.setDescription('Store a new Strain in the database!')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('name')
|
||||
.setDescription('Name of the Strain')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('type')
|
||||
.setDescription('Indica/Sativa/Hybrid')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: "Indica", value: "Indica" },
|
||||
{ name: "Hybrid", value: "Hybrid" },
|
||||
{ name: "Sativa", value: "Sativa" }
|
||||
)
|
||||
)
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('effects')
|
||||
.setDescription('The effects given by the strain')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('flavor')
|
||||
.setDescription('Flavor notes')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('rating')
|
||||
.setDescription('Number of stars')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('description')
|
||||
.setDescription('Description of the strain')
|
||||
.setRequired(false))
|
||||
),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
try {
|
||||
// Code Here...
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
switch (subcommand) {
|
||||
// GIF Search
|
||||
case "gifsearch":
|
||||
// Grab the inputs from the command
|
||||
const query = interaction.options.getString('query');
|
||||
const gifName = interaction.options.getString('name').toLowerCase();
|
||||
|
||||
const iStorage = interaction.client.iStorage.get(interaction.id);
|
||||
iStorage.page = 0;
|
||||
iStorage.gifsCollection = new Collection();
|
||||
iStorage.gifName = gifName;
|
||||
iStorage.query = query;
|
||||
|
||||
interaction.editReply("Fuck off this is broken, come back later.");
|
||||
|
||||
// Search Tenor for the GIF
|
||||
// tenor.Search.Query(query, '20').then(res => {
|
||||
|
||||
// strings.temp.gifs = [];
|
||||
// strings.temp.gifIndex = 0;
|
||||
// strings.temp.gifLimit = res.length - 1;
|
||||
// strings.temp.gifUserId = interaction.user.id;
|
||||
|
||||
// if (res[0] == undefined) {
|
||||
// interaction.editReply('Sorry I was unable to find a GIF of ' + query);
|
||||
// return;
|
||||
// }
|
||||
// for (const row of res) {
|
||||
// iStorage.gifsCollection.set(row.id, row);
|
||||
// }
|
||||
|
||||
// // Generate the initial action row
|
||||
// const actionRow = customEmbeds.actionRows.gifSearchAR('first');
|
||||
// // Get the index built
|
||||
// const gifPage = Indexer(iStorage.gifsCollection, 0, 1);
|
||||
// gifPage.query = query;
|
||||
// gifPage.gifName = gifName;
|
||||
// // Generate the embed
|
||||
// const gifEmbed = Embeds.core.gifSearch({ author: interaction.user.username }, gifPage);
|
||||
// interaction.editReply({ embeds: [gifEmbed], components: [actionRow], ephemeral: true });
|
||||
// });
|
||||
break;
|
||||
// GIF URL
|
||||
case "gifurl":
|
||||
//TODO Check on option names
|
||||
// const gifData = {
|
||||
// name: interaction.options.getString('name').toLowerCase(),
|
||||
// url: interaction.options.getString('url'),
|
||||
// };
|
||||
const gifData = new GifData().setInfo(interaction.options.getString('name').toLowerCase(), interaction.options.getString('url'));
|
||||
fn.upload.gif(gifData, interaction.client);
|
||||
interaction.editReply({ content: `I've saved the GIF as ${gifData.name}.gif`, ephemeral: true });
|
||||
fn.download.gifs(interaction.client);
|
||||
break;
|
||||
// Joint
|
||||
case "joint":
|
||||
//TODO
|
||||
fn.upload.joint(interaction.options.getString('joint-content'), interaction.client);
|
||||
interaction.editReply({ content: `The joint has been rolled${emoji.joint}`, ephemeral: true });
|
||||
break;
|
||||
// MD
|
||||
case "md":
|
||||
//TODO
|
||||
fn.upload.medicalAdvice(interaction.options.getString('advice-content'), interaction.client);
|
||||
interaction.editReply({ content: `The advice has been saved!`, ephemeral: true });
|
||||
break;
|
||||
// Pasta
|
||||
case "pasta":
|
||||
//TODO
|
||||
const pastaData = {
|
||||
name: interaction.options.getString('pasta-name').toLowerCase(),
|
||||
content: interaction.options.getString('pasta-content'),
|
||||
};
|
||||
await fn.upload.pasta(pastaData, interaction.client);
|
||||
interaction.editReply({content: `The copypasta has been saved as ${pastaData.name}.pasta`, ephemeral: true });
|
||||
break;
|
||||
// Strain
|
||||
case "strain":
|
||||
//TODO
|
||||
fn.upload.strain(interaction).then(res => {
|
||||
interaction.editReply({
|
||||
content: `The strain information has been saved. (${interaction.options.getString('name')})`,
|
||||
ephemeral: true
|
||||
});
|
||||
}).catch(err => {
|
||||
console.log(`E: ${err}`);
|
||||
interaction.editReply({
|
||||
content: 'There was a problem saving the strain.',
|
||||
ephemeral: true
|
||||
});
|
||||
});
|
||||
break;
|
||||
// Default
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
const errorId = fn.generateErrorId();
|
||||
console.error(`${errorId}: err`);
|
||||
await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,93 +0,0 @@
|
||||
const tenor = require('tenorjs').client({
|
||||
'Key': process.env.tenorAPIKey, // https://tenor.com/developer/keyregistration
|
||||
'Filter': 'off', // "off", "low", "medium", "high", not case sensitive
|
||||
'Locale': 'en_US',
|
||||
'MediaFilter': 'minimal',
|
||||
'DateFormat': 'D/MM/YYYY - H:mm:ss A',
|
||||
});
|
||||
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { MessageActionRow, MessageButton } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savegif')
|
||||
.setDescription('Save a GIF to Nodbot\'s database.')
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('searchgif')
|
||||
.setDescription('Search Tenor for a GIF.')
|
||||
.addStringOption(option =>
|
||||
option.setName('query')
|
||||
.setDescription('Search Query')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('name')
|
||||
.setDescription('What to save the gif as')
|
||||
.setRequired(true))
|
||||
)
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('enterurl')
|
||||
.setDescription('Specify a URL to save.')
|
||||
.addStringOption(option =>
|
||||
option
|
||||
.setName('url')
|
||||
.setDescription('URL Link to the GIF')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('name')
|
||||
.setDescription('What to save the gif as')
|
||||
.setRequired(true))
|
||||
),
|
||||
async execute(interaction) {
|
||||
if (interaction.options.getSubcommand() == 'searchgif') {
|
||||
// Previous GIF button
|
||||
const prevButton = new MessageButton().setCustomId('prevGif').setLabel('Previous GIF').setStyle('SECONDARY').setDisabled(true);
|
||||
// Confirm GIF Button
|
||||
const confirmButton = new MessageButton().setCustomId('confirmGif').setLabel('Confirm').setStyle('PRIMARY');
|
||||
// Next GIF Button
|
||||
const nextButton = new MessageButton().setCustomId('nextGif').setLabel('Next GIF').setStyle('SECONDARY');
|
||||
// Cancel Button
|
||||
const cancelButton = new MessageButton().setCustomId('cancelGif').setLabel('Cancel').setStyle('DANGER');
|
||||
// Put all the above into an ActionRow to be sent as a component of the reply
|
||||
const actionRow = new MessageActionRow().addComponents(prevButton, confirmButton, nextButton, cancelButton);
|
||||
|
||||
// Get the query
|
||||
const query = interaction.options.getString('query');
|
||||
strings.temp.gifName = interaction.options.getString('name').toLowerCase();
|
||||
|
||||
// Search Tenor for the GIF
|
||||
tenor.Search.Query(query, '10').then(res => {
|
||||
strings.temp.gifs = [];
|
||||
strings.temp.gifIndex = 0;
|
||||
strings.temp.gifLimit = res.length - 1;
|
||||
strings.temp.gifUserId = interaction.user.id;
|
||||
|
||||
if (res[0] == undefined) {
|
||||
interaction.reply('Sorry I was unable to find a GIF of ' + query);
|
||||
return;
|
||||
}
|
||||
for (const row of res) {
|
||||
strings.temp.gifs.push({
|
||||
embed_url: row.media[0].gif.url,
|
||||
});
|
||||
}
|
||||
interaction.reply({ content: strings.temp.gifs[0].embed_url, components: [actionRow], ephemeral: true });
|
||||
});
|
||||
}
|
||||
|
||||
if (interaction.options.getSubcommand() == 'enterurl') {
|
||||
const gifData = {
|
||||
name: interaction.options.getString('name'),
|
||||
embed_url: interaction.options.getString('url'),
|
||||
};
|
||||
fn.upload.gif(gifData, interaction.client);
|
||||
interaction.reply({ content: `I've saved the GIF as ${gifData.name}.gif`, ephemeral: true });
|
||||
fn.download.gifs(interaction.client);
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
const { emoji } = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savejoint')
|
||||
.setDescription('Save a phrase for /joint!')
|
||||
.addStringOption(option =>
|
||||
option.setName('joint-content')
|
||||
.setDescription('What is the phrase?')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
fn.upload.joint(interaction.options.getString('joint-content'), interaction.client);
|
||||
interaction.reply({ content: `The joint has been rolled${emoji.joint}`, ephemeral: true });
|
||||
},
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
// const { emoji } = require('../strings.json');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savemd')
|
||||
.setDescription('Add medical advice to NodBot\'s Database!')
|
||||
.addStringOption(option =>
|
||||
option.setName('advice-content')
|
||||
.setDescription('What is the advice?')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
fn.upload.medicalAdvice(interaction.options.getString('advice-content'), interaction.client);
|
||||
interaction.reply({ content: `The advice has been saved!`, ephemeral: true });
|
||||
},
|
||||
};
|
||||
@ -1,24 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savepasta')
|
||||
.setDescription('Save a copypasta!')
|
||||
.addStringOption(option =>
|
||||
option.setName('pasta-name')
|
||||
.setDescription('What should the name of the copypasta be?')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('pasta-content')
|
||||
.setDescription('What is the content of the copypasta?')
|
||||
.setRequired(true)),
|
||||
async execute(interaction) {
|
||||
const pastaData = {
|
||||
name: interaction.options.getString('pasta-name'),
|
||||
content: interaction.options.getString('pasta-content'),
|
||||
};
|
||||
fn.upload.pasta(pastaData, interaction.client);
|
||||
interaction.reply({content: `The copypasta has been saved as ${pastaData.name}.pasta`, ephemeral: true });
|
||||
},
|
||||
};
|
||||
@ -1,54 +0,0 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
const { emoji } = require('../strings.json');
|
||||
|
||||
// Strain Name | Type | Effects | Flavor | Rating | Description
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('savestrain')
|
||||
.setDescription('Store a new Strain in the database!')
|
||||
.addStringOption(option =>
|
||||
option.setName('name')
|
||||
.setDescription('Name of the Strain')
|
||||
.setRequired(true))
|
||||
.addStringOption(option =>
|
||||
option.setName('type')
|
||||
.setDescription('Indica/Sativa/Hybrid')
|
||||
.setRequired(true)
|
||||
.addChoices(
|
||||
{ name: "Indica", value: "Indica" },
|
||||
{ name: "Hybrid", value: "Hybrid" },
|
||||
{ name: "Sativa", value: "Sativa" }
|
||||
))
|
||||
.addStringOption(option =>
|
||||
option.setName('effects')
|
||||
.setDescription('The effects given by the strain')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option.setName('flavor')
|
||||
.setDescription('Flavor notes')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option.setName('rating')
|
||||
.setDescription('Number of stars')
|
||||
.setRequired(false))
|
||||
.addStringOption(option =>
|
||||
option.setName('description')
|
||||
.setDescription('Description of the strain')
|
||||
.setRequired(false)),
|
||||
async execute(interaction) {
|
||||
fn.upload.strain(interaction).then(res => {
|
||||
interaction.reply({
|
||||
content: `The strain information has been saved. (${interaction.options.getString('name')})`,
|
||||
ephemeral: true
|
||||
});
|
||||
}).catch(err => {
|
||||
console.log(`E: ${err}`);
|
||||
interaction.reply({
|
||||
content: 'There was a problem saving the strain.',
|
||||
ephemeral: true
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
42
slash-commands/setnick.js
Normal file
42
slash-commands/setnick.js
Normal file
@ -0,0 +1,42 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const fn = require('../functions.js');
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('setnick')
|
||||
.setDescription('Set a user\'s nickname.')
|
||||
.addUserOption(o =>
|
||||
o.setName('user')
|
||||
.setDescription('The user to set the nickname for.')
|
||||
.setRequired(true)
|
||||
)
|
||||
.addStringOption(o =>
|
||||
o.setName('nickname')
|
||||
.setDescription('The nickname to set.')
|
||||
.setRequired(true)
|
||||
),
|
||||
async execute(interaction) {
|
||||
try {
|
||||
// Defer the reply, with ephemeral set to true
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
// Get the user and nickname from the interaction
|
||||
const user = interaction.options.getMember('user');
|
||||
const nickname = interaction.options.getString('nickname');
|
||||
|
||||
// Set the nickname
|
||||
await fn.setNick(user, nickname);
|
||||
|
||||
// Reply to the interaction
|
||||
await interaction.editReply(`Successfully set ${user}'s nickname to ${nickname}`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (error.code === 50013) {
|
||||
return await interaction.editReply('I do not have permission to set the nickname of that user!');
|
||||
}
|
||||
if (error.code === 50035) {
|
||||
return await interaction.editReply('The nickname is too long!');
|
||||
}
|
||||
await interaction.editReply('There was an error while executing this command!');
|
||||
}
|
||||
},
|
||||
};
|
||||
124
slash-commands/view.js
Normal file
124
slash-commands/view.js
Normal file
@ -0,0 +1,124 @@
|
||||
const { SlashCommandBuilder } = require('@discordjs/builders');
|
||||
const { Collection } = require('discord.js');
|
||||
const fn = require('../functions.js');
|
||||
const strings = require('../strings.json');
|
||||
const { GifData } = require('../CustomModules/NodBot.js');
|
||||
const customEmbeds = require('../CustomModules/Embeds.js');
|
||||
const Indexer = require('../CustomModules/Indexer.js');
|
||||
const Embeds = require('../CustomModules/Embeds.js');
|
||||
const { emoji } = strings;
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName('view')
|
||||
.setDescription('View content saved in Nodbot\'s database.')
|
||||
// GIFs
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('gifs')
|
||||
.setDescription('Display all saved GIFs.')
|
||||
)
|
||||
// Joints
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('joints')
|
||||
.setDescription('Display all saved joints.')
|
||||
)
|
||||
// Pastas
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('pastas')
|
||||
.setDescription('Display all saved copypastas.')
|
||||
)
|
||||
// Requests
|
||||
.addSubcommand(subcommand =>
|
||||
subcommand
|
||||
.setName('requests')
|
||||
.setDescription('Display all saved requests.')
|
||||
),
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
try {
|
||||
// Code Here...
|
||||
const subcommand = interaction.options.getSubcommand();
|
||||
const iStorage = interaction.client.iStorage.get(interaction.id);
|
||||
const commandData = {
|
||||
author: interaction.user.username,
|
||||
command: `/${interaction.commandName} ${subcommand}`
|
||||
};
|
||||
switch (subcommand) {
|
||||
// GIFs
|
||||
case "gifs":
|
||||
if (!interaction.client.gifs) {
|
||||
interaction.editReply('For some reason I don\'t have access to the collection of gifs. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
let indexedGifs = Indexer(interaction.client.gifs, 0);
|
||||
indexedGifs.gifsString = new String();
|
||||
|
||||
iStorage.page = 0;
|
||||
|
||||
for (const gif of indexedGifs.thisPage) {
|
||||
indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`;
|
||||
}
|
||||
interaction.editReply(fn.embeds.gifs(commandData, indexedGifs));
|
||||
break;
|
||||
// Joints
|
||||
case "joints":
|
||||
if (!interaction.client.joints) {
|
||||
interaction.editReply('For some reason I don\'t have access to the collection of joints. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
let indexedJoints = Indexer(interaction.client.joints, 0);
|
||||
indexedJoints.jointsString = new String();
|
||||
|
||||
iStorage.page = 0;
|
||||
|
||||
for (const joint of indexedJoints.thisPage) {
|
||||
indexedJoints.jointsString += `${joint.content}\n`;
|
||||
}
|
||||
interaction.editReply(fn.embeds.joints(commandData, indexedJoints));
|
||||
break;
|
||||
// Pastas
|
||||
case "pastas":
|
||||
if (!interaction.client.pastas) {
|
||||
interaction.editReply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true });
|
||||
return;
|
||||
}
|
||||
let indexedPastas = Indexer(interaction.client.pastas, 0);
|
||||
indexedPastas.pastasString = new String();
|
||||
|
||||
iStorage.page = 0;
|
||||
|
||||
for (const pasta of indexedPastas.thisPage) {
|
||||
indexedPastas.pastasString += `${pasta.name}.pasta\n`;
|
||||
}
|
||||
interaction.editReply(fn.embeds.pastas(commandData, indexedPastas));
|
||||
break;
|
||||
case "requests":
|
||||
if (!interaction.client.requests) {
|
||||
interaction.editReply('For some reason I don\'t have access to the collection of requests. Sorry about that!');
|
||||
return;
|
||||
}
|
||||
let indexedRequests = Indexer(interaction.client.requests, 0);
|
||||
indexedRequests.requestsString = new String();
|
||||
|
||||
iStorage.page = 0;
|
||||
|
||||
for (const request of indexedRequests.thisPage) {
|
||||
indexedRequests.requestsString += `[${request.id}]: ${request.request} (submitted by ${request.author})\n`;
|
||||
}
|
||||
|
||||
interaction.editReply(fn.embeds.requests(commandData, indexedRequests));
|
||||
break;
|
||||
// Default
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
const errorId = fn.generateErrorId();
|
||||
console.error(`${errorId}: err`);
|
||||
await interaction.editReply(`Sorry, an error has occured. Error ID: ${errorId}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
80
strings.json
80
strings.json
@ -1,8 +1,8 @@
|
||||
{
|
||||
"help": {
|
||||
"description": "Hi there! Thanks for checking out NodBot. NodBot is used in two distinct ways: with 'Slash Commands' (/help), and with 'Dot Commands' (nod.gif). The two types will be outlined below, along with usage examples.",
|
||||
"slash": "Slash Commands always begin with a / and a menu will pop up to help complete the commands.",
|
||||
"dot": "Dot Commands have the command at the end of the message, for example to search for a gif of 'nod', type 'nod.gif'"
|
||||
"description": "Hi there! Thanks for checking out NodBot. NodBot is used in two distinct ways: with 'Slash Commands' (`/help`), and with 'Dot Commands' (`nod.gif`). The two types will be outlined below, along with usage examples.",
|
||||
"slash": "Slash Commands always begin with a `/` and a menu will pop up to help complete the commands.",
|
||||
"dot": "Dot Commands have the command at the end of the message, for example to search for a gif of `nod`, type `nod.gif`"
|
||||
},
|
||||
"emoji": {
|
||||
"joint": "<:joint:862082955902976000>",
|
||||
@ -14,13 +14,71 @@
|
||||
"urls": {
|
||||
"avatar": "https://cdn.discordapp.com/avatars/513184762073055252/12227aa23a06d5178853e59b72c7487b.webp?size=128"
|
||||
},
|
||||
"capbacks": [
|
||||
"on god?!",
|
||||
"fr fr?!",
|
||||
"no cap?!",
|
||||
"no cap fr",
|
||||
"bussin fr, no cap",
|
||||
"ongggg no :billed_cap: fr fr"
|
||||
],
|
||||
"costs": {
|
||||
"gpt": {
|
||||
"gpt-3.5-turbo": 0.2
|
||||
},
|
||||
"dalle": {
|
||||
"256x256": 1.6,
|
||||
"512x512": 1.8,
|
||||
"1024x1024": 2.0
|
||||
}
|
||||
},
|
||||
"ai": {
|
||||
"chatModel": "gpt-3.5-turbo",
|
||||
"chatPromptCentsPer": 0.15,
|
||||
"chatPromptUnits": 1000,
|
||||
"chatResCentsPer": 0.2,
|
||||
"chatResUnits": 1000
|
||||
},
|
||||
"autoresponses": {
|
||||
"bigDoinks": {
|
||||
"keywords": ["big", "doinks"],
|
||||
"responses": [
|
||||
"<:bigdoinks:1053706618853924905> Gang.",
|
||||
"<:bigdoinks:1053706618853924905> Out here in Amish",
|
||||
"<:bigdoinks:1053706618853924905> Out here in Amish, smoking Big Doinks in Amish... Gang."
|
||||
]
|
||||
},
|
||||
"ligma": {
|
||||
"keywords": ["ligma"],
|
||||
"responses": [
|
||||
"ligma balls lmao gottem",
|
||||
"ligma balls ahaha",
|
||||
"https://tenor.com/view/ligma-balls-gif-12236083",
|
||||
"<:deadmonkey:1139186312444911707>"
|
||||
]
|
||||
},
|
||||
"ong": {
|
||||
"keywords": [
|
||||
"frfr",
|
||||
"fr fr",
|
||||
"bussin",
|
||||
"no cap",
|
||||
" ong "
|
||||
],
|
||||
"responses": [
|
||||
"on god?!",
|
||||
"fr fr?!",
|
||||
"no cap?!",
|
||||
"no cap fr",
|
||||
"bussin fr, no cap",
|
||||
"ongggg no :billed_cap: fr fr"
|
||||
]
|
||||
},
|
||||
"fuckYou": {
|
||||
"keywords": [
|
||||
"fuck",
|
||||
"nodbot"
|
||||
],
|
||||
"responses": [
|
||||
"no u",
|
||||
"go fuck yourself",
|
||||
"why does everyone hate me :sob:",
|
||||
"<:kms:1253790048696926298>",
|
||||
"Eat a bag of dicks"
|
||||
]
|
||||
}
|
||||
},
|
||||
"temp": {}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user