Compare commits

..

82 Commits
nodGPT ... main

Author SHA1 Message Date
eddf16a821 Update .gitea/workflows/production-docker.yml 2024-09-29 17:23:58 +00:00
80f89d0117 Update .gitea/workflows/dev-docker.yml 2024-09-29 17:23:46 +00:00
d06cac6f3d Update .gitea/workflows/dev-docker.yml 2024-09-29 17:23:29 +00:00
7e2664083e Add .gitea/workflows/dev-docker.yml 2024-09-29 17:12:41 +00:00
d898398780 Merge pull request 'v3.3.3: Paged Commands' (#20) from v3.3.3 into main
Reviewed-on: #20
2024-09-26 13:24:54 +00:00
3a35b829ad Documentation update, final for v.3.3.3
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 17s
2024-09-26 09:23:25 -04:00
8148890574 Add listing of aliases to /help
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 7s
2024-09-26 09:22:16 -04:00
09901b5e58 Add aliases
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 18s
2024-09-26 09:07:26 -04:00
4b75dfa885 v3.3.3 Documentation 2024-09-26 09:07:20 -04:00
d0528c3637 MVP for paged /joints
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 55s
2024-09-26 08:47:42 -04:00
c3fa30ea64 MVP for paged /requests 2024-09-26 08:37:16 -04:00
709c8cfab7 MVP for paged /pastas 2024-09-26 08:27:11 -04:00
7aa3d5d0a1 MVP for Paged /gifs Browser 2024-09-26 08:09:09 -04:00
8e5931e0d4 Working on storing interaction data in the client 2024-09-25 23:22:22 -04:00
e4c389be71 Merge branch 'v3.3.3' of https://git.vfsh.dev/voidf1sh/nodbot into v3.3.3 2024-09-25 19:20:57 -04:00
be83b9ea74 WIP 2024-09-25 18:56:33 -04:00
ecbfc2bc2a Remove unnecessary function calls now that classes exist 2024-09-25 15:35:01 -04:00
00df6074d6 Fixed duplication of valid commands 2024-09-25 15:30:51 -04:00
23f081c6c1 Move CommandData and checkCommand to NodBot Classes 2024-09-25 11:54:33 -04:00
1a0817a89c Versioning -- v3.3.3 2024-09-25 11:54:04 -04:00
e5d520a73b 'v3.3.2 The Help Fix' (#17) from v3.3.2 into main
Reviewed-on: #17

Vastly improved the help message formatting and fixed a bug causing the help message to crash due to too many fields. Fixed by moving away from fields, then by filtering the command names to be unique. Aliases are being loaded as entirely new commands which will need to be fixed later.
2024-09-24 02:05:13 +00:00
fdcb56998c Ignore new env format
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 6s
2024-09-23 22:02:01 -04:00
28443611e4 Vastly improved the /help command 2024-09-23 22:01:19 -04:00
f231df89d8 Move help from fields to the description.
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 1m7s
2024-09-23 21:21:38 -04:00
0c2eae76aa Remove vestigial code 2024-09-23 21:10:08 -04:00
7b967cca8c Versioning -- v3.3.2 2024-09-23 21:06:35 -04:00
f3ce349ceb 'v3.3.0-metars' (#14) from v3.3.0-metars into main
Reviewed-on: #14

Seems to work, send it.
2024-09-24 01:04:48 +00:00
9d9a1447f3 Fixed Prod Deployment
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 1m5s
2024-09-23 18:51:43 -04:00
70fc12d458 Fix branch references 2024-09-23 18:51:34 -04:00
65470f2c57 Merge branch 'main' into v3.3.0-metars 2024-09-23 18:43:59 -04:00
38d90d7f6b Update CI/CD 2024-09-22 18:49:27 -04:00
8f7ed605a4 Update .github/workflows/pe-docker.yml
Some checks failed
NodBot PE Dockerization / build (push) Failing after 1m56s
Match from pe branch
2024-09-22 22:26:09 +00:00
2f9d29d891 Updating docs 2024-06-22 10:42:26 -04:00
ecb50148b9 Fix body/content 2024-06-22 10:26:01 -04:00
806d70b292 Move METAR raw to message body 2024-06-22 10:19:29 -04:00
798aaef4ea Adjust embed appearance 2024-06-22 10:16:21 -04:00
4c20d08471 Adjust embed appearance 2024-06-22 10:10:05 -04:00
550eb79374 Fix split ATISes 2024-06-22 10:08:28 -04:00
b11d14b72d Add and tweak D-ATIS 2024-06-22 09:42:02 -04:00
444bad7935 Add D-ATIS from clowd.io 2024-06-22 09:17:24 -04:00
6700e36907 Make update script 2024-06-21 20:20:07 -04:00
66dbdceb2a Minor tweaks 2024-06-21 20:19:05 -04:00
5a9b91ade1 Fix cloud layer CLR 2024-06-21 19:19:37 -04:00
13553d7e5a Add KCQX 2024-06-21 19:15:22 -04:00
388bc4d021 Add Metars with Decoding TODO: add TAFs 2024-06-21 18:35:12 -04:00
2a3b53ea08 Fix kms emoji is 2024-06-21 15:14:38 -04:00
99af5ca8b4 Add f u nodbot response 2024-06-21 15:11:34 -04:00
7003351ecc Updating how autoresponses are handled to be cleaner 2024-06-21 15:02:22 -04:00
bfa6e10011 Versioning v3.2.3 2024-06-21 15:02:08 -04:00
22f2ac58df I hate you 2024-06-21 15:01:56 -04:00
3dcc4e021f Merge pull request 'v3.2.2-dev Merged new fixed sbs and OpenAI deintegration' (#12) from v3.2.2-dev into main
Reviewed-on: #12
2024-06-15 02:24:16 +00:00
d1e2152de9 Disabled OpenAI init
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-06-14 22:06:54 -04:00
1a024b216a Merge branch 'dev' into v3.2.2-dev 2024-06-14 22:00:33 -04:00
0106983a5f Fixed how replies work with .sb
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-06-14 21:38:36 -04:00
caefe0e228 Fix silly replace method for getting command 2024-06-14 20:34:51 -04:00
928f12fe01 Versioning 2024-06-14 20:30:34 -04:00
ae9ce308b2 Ignore Mac BS
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-06-14 14:43:23 -04:00
2ea94092db Ignore Mac BS 2024-06-14 14:43:06 -04:00
3f5cc0a9ff Fix exec dir
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-01-01 15:47:14 -05:00
545756b762 Fix log overwriting error
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-01-01 15:44:55 -05:00
d61ffbaaf7 Remove chat and dalle commands
Some checks failed
NodBot Production Dockerization / build (pull_request) Has been cancelled
2024-01-01 15:42:17 -05:00
15d39e3381 commmenting
Some checks reported warnings
NodBot Production Dockerization / build (pull_request) Has been cancelled
2023-12-14 10:54:05 -05:00
97b4136b64 Fix joint randomization and add more aliases
Some checks reported warnings
NodBot Production Dockerization / build (pull_request) Has been cancelled
2023-12-14 10:43:40 -05:00
82f65a800c bugfixes
Some checks reported warnings
NodBot Production Dockerization / build (pull_request) Has been cancelled
2023-12-13 16:49:48 -05:00
f995d9a643 Upgrade to gpt-3.5-turbo
Some checks failed
NodBot Production Dockerization / build (pull_request) Failing after 10m32s
2023-10-18 17:33:03 -04:00
ff3a423a30 Merge pull request 'Adding and Restructuring Commands and Classes' (#8) from dev into main
Some checks failed
NodBot Production Dockerization / build (pull_request) Failing after 2s
Reviewed-on: #8
2023-08-08 13:38:29 -07:00
7321f4535b Merge branch 'main' into dev
Some checks reported warnings
NodBot Production Dockerization / build (pull_request) Has been cancelled
2023-08-08 13:36:54 -07:00
c562c06925 Fix incorrect insert statement
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 13s
2023-08-08 15:06:29 -04:00
b28e193b55 Update to use Node 18 LTS
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 1m0s
2023-08-08 14:44:59 -04:00
6e062ffef2 Use Head Ref for main PRs
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 1m5s
2023-08-08 14:39:32 -04:00
f3051ec944 /edit pasta, setFooter, PastaData Classification 2023-08-08 14:34:46 -04:00
cb834cb7ee Classes, Embed.SetAuthor, /gifs, /edit, PARTIAL 2023-08-08 13:25:07 -04:00
0cfa91eb10 Merge pull request 'Restructure /save* commands into subcommands' (#7) from dev into main
Reviewed-on: #7
2023-08-06 14:37:17 -07:00
17a9bd4ea2 Restructure /save commands
All checks were successful
NodBot Production Dockerization / build (pull_request) Successful in 14s
2023-08-06 17:29:17 -04:00
18ef6e56c2 Shouldn't be pushing to main 2023-08-06 09:14:44 -04:00
1bff94e71b Forgot to toggle
All checks were successful
NodBot Production Dockerization / build (push) Successful in 55s
2023-08-06 09:04:24 -04:00
10021d6926 changed HED_REF to main hardcoded
All checks were successful
NodBot Production Dockerization / build (push) Successful in 2m1s
2023-08-06 08:58:35 -04:00
38fdc07e4e Revert .sb functionality
All checks were successful
NodBot Production Dockerization / build (push) Successful in 19s
2023-08-06 08:51:28 -04:00
e99e6b2e1f Run CI on push to main 2023-08-06 08:51:12 -04:00
98acb652af Merge branch 'nodGPT' 2023-08-06 08:47:35 -04:00
c8d059e36d Ignore all .env 2023-08-06 08:47:15 -04:00
1f85b97467 CI/CD Update for Gitea Actions 2023-08-04 20:06:09 -04:00
41 changed files with 1616 additions and 694 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -0,0 +1,46 @@
name: NodBot PE Dockerization
on:
push:
tags:
- 'v*-dev*'
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: tags/${{ 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-dev
cd nodbot-dev
else
cd nodbot-dev
git checkout main
git pull
fi
git checkout tags/${{ gitea.ref_name }}
- name: Build the Docker image
run: |
cd /var/lib/act_runner/nodbot-dev
docker build . --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-dev
docker push v0idf1sh/nodbot-dev
- name: Restart the container
run: |
cd /srv/docker/nodbot-dev
docker-compose down
docker-compose up -d

View File

@ -0,0 +1,45 @@
name: NodBot PE Dockerization
on:
push:
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: |
echo "Branch: ${{ gitea.head_ref }}"
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.head_ref }}
- 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

View File

@ -0,0 +1,45 @@
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: tags/${{ 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 tags/${{ gitea.ref_name }}
- name: Build the Docker image
run: |
cd /var/lib/act_runner/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 /var/lib/act_runner/nodbot
docker push v0idf1sh/nodbot
- name: Restart the container
run: |
cd /srv/docker/nodbot
docker-compose down
docker-compose up -d

View File

@ -1,23 +0,0 @@
name: Docker Image CI
on:
workflow_dispatch:
env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: 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: docker push v0idf1sh/nodbot

5
.gitignore vendored
View File

@ -2,8 +2,8 @@
.vscode .vscode
package-lock.json package-lock.json
.VSCodeCounter/ .VSCodeCounter/
env.dev .env*
env.prod *.env
# Custom folders # Custom folders
# gifs/* # gifs/*
@ -115,3 +115,4 @@ dist
# TernJS port file # TernJS port file
.tern-port .tern-port
.DS_Store

24
CHANGELOG.md Normal file
View File

@ -0,0 +1,24 @@
## 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

View File

@ -0,0 +1,151 @@
const customEmbeds = require('../CustomModules/Embeds.js');
const InteractionStorage = require('../CustomModules/InteractionStorage.js');
const indexer = require('../CustomModules/Indexer.js');
const fn = require('../functions.js');
const requests = require('../slash-commands/requests.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;
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.member.displayName}, 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.member.displayName}, 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.member.displayName}, 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.member.displayName}, indexedJoints));
}
}

139
CustomModules/Embeds.js Normal file
View File

@ -0,0 +1,139 @@
const { MessageActionRow, MessageButton } = require('discord.js');
module.exports = {
gifSearchAR(state) {
// Setup the buttons
const previousButton = new MessageButton()
.setCustomId('prevGif')
.setLabel('⬅️')
.setStyle('SECONDARY');
const confirmButton = new MessageButton()
.setCustomId('confirmGif')
.setLabel('✅')
.setStyle('PRIMARY');
const nextButton = new MessageButton()
.setCustomId('nextGif')
.setLabel('➡️')
.setStyle('SECONDARY');
const cancelButton = new MessageButton()
.setCustomId('cancelGif')
.setLabel('❌')
.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');
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);
}
}

32
CustomModules/Indexer.js Normal file
View File

@ -0,0 +1,32 @@
module.exports = (collection, page) => {
const itemsPerPage = 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}`
};
}

View 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;
}
}

82
CustomModules/NodBot.js Normal file
View File

@ -0,0 +1,82 @@
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;
return this;
}
validate(dotCommands) {
if (this.args.startsWith('http')) return false;
if (this.args.startsWith('www')) return false;
for (const [key, value] of dotCommands) {
if (key === this.command) {
this.isValid = true;
return this;
} else if (value.alias && 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
}
}
}

View File

@ -1,8 +1,9 @@
FROM node:16 FROM node:18
RUN mkdir -p /usr/src/app RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app WORKDIR /usr/src/app
COPY package.json ./ COPY package.json ./
RUN npm install RUN npm install
COPY . . 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/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"]

View File

@ -11,15 +11,6 @@ Use the `/help` command to see the bot's help message.
## Push Docker Image ## Push Docker Image
`docker push name/nodbot` `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 # Deploy NodBot Yourself
1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications) 1. Create an application at the [Discord Developer Portal](https://discord.com/developers/applications)
@ -98,14 +89,3 @@ ownerId=<your Discord user ID>
statusChannelId=<Discord channel ID of channel used for status messages> statusChannelId=<Discord channel ID of channel used for status messages>
clientId=<Discord user ID of your bot> 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

View File

@ -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

View File

@ -1,4 +1,7 @@
{ {
"guildId": "868542949737246730", "guildId": "868542949737246730",
"validCommands": [] "validCommands": [],
"roaches": [],
"icaoIds": [],
"datisICAOs": []
} }

28
dot-commands/datis.js Normal file
View 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);
}
}
}
}

View File

@ -9,11 +9,12 @@ const dotenv = require('dotenv').config();
module.exports = { module.exports = {
name: 'gif', name: 'gif',
description: 'Send a 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', usage: '<GIF name or Search Query>.gif',
async execute(message, commandData) { async execute(message, commandData) {
// if (message.deletable) message.delete(); // if (message.deletable) message.delete();
const client = message.client; const client = message.client;
if (!client.gifs.has(commandData.args)) { if (!client.gifs.has(commandData.args.toLowerCase())) {
if (process.env.isDev) console.log('https://tenor.googleapis.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.tenorAPIKey}` + '&limit=1&contentfilter=off'); if (process.env.isDev) console.log('https://tenor.googleapis.com/v2/search?' + `&q=${commandData.args}` + `&key=${process.env.tenorAPIKey}` + '&limit=1&contentfilter=off');
const q = await axios.get( const q = await axios.get(
'https://tenor.googleapis.com/v2/search?' + 'https://tenor.googleapis.com/v2/search?' +
@ -43,11 +44,11 @@ module.exports = {
// }).catch(err => console.error(err)); // }).catch(err => console.error(err));
} else { } else {
// message.reply(commandData.args + ' requested by ' + message.author.username + '\n' + client.gifs.get(commandData.args).embed_url); // 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.reply(fn.embeds.gif(commandData));
// message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`); // message.channel.send(`> ${commandData.author} - ${commandData.args}.gif`);
// message.channel.send(commandData.embed_url); // message.channel.send(commandData.embed_url);
message.reply(commandData.embed_url); message.reply(gifData.url);
} }
} }
} }

View File

@ -5,13 +5,33 @@ module.exports = {
name: 'joint', name: 'joint',
description: 'Send a random weed-themed phrase.', description: 'Send a random weed-themed phrase.',
usage: '.joint', 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'],
execute(message, commandData) { execute(message, commandData) {
let joints = []; let joints = [];
// Create a simple array of the joint texts
for (const entry of message.client.joints.map(joint => joint.content)) { for (const entry of message.client.joints.map(joint => joint.content)) {
joints.push(entry); joints.push(entry);
} }
const randIndex = Math.floor(Math.random() * joints.length); // 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.reply(`${joints[randIndex]} ${emoji.joint}`); message.reply(`${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");
} }
} }

26
dot-commands/metar.js Normal file
View File

@ -0,0 +1,26 @@
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);
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);
}
}
}
}

View File

@ -6,14 +6,14 @@ module.exports = {
usage: '<Copypasta Name>.pasta', usage: '<Copypasta Name>.pasta',
execute(message, commandData) { execute(message, commandData) {
const client = message.client; const client = message.client;
let replyBody = ''; let pastaData;
let iconUrl;
if (!client.pastas.has(commandData.args)) { 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 { } else {
commandData.content = client.pastas.get(commandData.args).content; pastaData = client.pastas.get(commandData.args);
commandData.iconUrl = client.pastas.get(commandData.args).iconUrl;
} }
message.reply(fn.embeds.pasta(commandData)); message.reply(fn.embeds.pasta(commandData, pastaData));
} }
} }

View File

@ -10,20 +10,33 @@ module.exports = {
// message.reply(fn.spongebob(commandData)).then(() => { // message.reply(fn.spongebob(commandData)).then(() => {
// message.delete(); // message.delete();
// }); // });
if (message.reference != undefined) { if (message.reference != undefined) { // message.reference is undefined if the message isn't a reply to another message
const repliedMessageId = message.reference.messageId; if (commandData.args !== "") { // If the replying message isn't just .sb
message.channel.messages.fetch(repliedMessageId) const repliedMessageId = message.reference.messageId; // grab the message Id of the replied-to msg
.then(repliedMessage => { message.channel.messages.fetch(repliedMessageId) // Fetch the message because with our luck it isn't in the cache
repliedMessage.reply(fn.spongebob({ args: repliedMessage.content })).then(() => { .then(repliedMessage => {
if (message.deletable) message.delete(); 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);
}); });
}) } else { // We're working with a basic ".sb" and can proceed as we did before...
.catch(err => { const repliedMessageId = message.reference.messageId; // grab the message Id of the replied-to msg
console.error(err); message.channel.messages.fetch(repliedMessageId) // Fetch the message because with our luck it isn't in the cache
}); .then(repliedMessage => {
} else { 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(() => { 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.
}); });
} }
} }

View File

@ -1,7 +1,6 @@
/* eslint-disable comma-dangle */ /* eslint-disable comma-dangle */
// dotenv for handling environment variables // dotenv for handling environment variables
const dotenv = require('dotenv'); const dotenv = require('dotenv').config();
dotenv.config();
// Assignment of environment variables for database access // Assignment of environment variables for database access
const dbHost = process.env.dbHost; const dbHost = process.env.dbHost;
const dbUser = process.env.dbUser; const dbUser = process.env.dbUser;
@ -9,11 +8,11 @@ const dbName = process.env.dbName;
const dbPass = process.env.dbPass; const dbPass = process.env.dbPass;
const dbPort = process.env.dbPort; const dbPort = process.env.dbPort;
const isDev = process.env.isDev; const isDev = process.env.isDev;
const ownerId = process.env.ownerId; const ownerId = process.env.ownerId;
// filesystem // filesystem
const fs = require('fs'); const fs = require('fs');
const zlib = require('zlib');
// Discord.js // Discord.js
const Discord = require('discord.js'); const Discord = require('discord.js');
@ -21,30 +20,19 @@ const Discord = require('discord.js');
// Fuzzy text matching for db lookups // Fuzzy text matching for db lookups
const FuzzySearch = require('fuzzy-search'); const FuzzySearch = require('fuzzy-search');
// OpenAI // Axios for APIs
const { Configuration, OpenAIApi } = require("openai"); const axios = require('axios');
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
async function openAIStatus(o) {
const response = await o.listModels();
const models = response.data.data;
models.forEach(e => {
console.log(`Model ID: ${e.id}`);
});
};
openAIStatus(openai);
// Various imports from other files // Various imports from other files
const config = require('./config.json'); const config = require('./config.json');
const strings = require('./strings.json'); const strings = require('./strings.json');
const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js')); const slashCommandFiles = fs.readdirSync('./slash-commands/').filter(file => file.endsWith('.js'));
const dotCommandFiles = fs.readdirSync('./dot-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 // MySQL database connection
const mysql = require('mysql'); const mysql = require('mysql');
const { GifData, PastaData } = require('./CustomModules/NodBot');
const db = new mysql.createPool({ const db = new mysql.createPool({
connectionLimit: 10, connectionLimit: 10,
host: dbHost, host: dbHost,
@ -57,6 +45,11 @@ const db = new mysql.createPool({
const functions = { const functions = {
// Functions for managing and creating Collections // Functions for managing and creating Collections
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 // Create the collection of slash commands
slashCommands(client) { slashCommands(client) {
if (!client.slashCommands) client.slashCommands = new Discord.Collection(); if (!client.slashCommands) client.slashCommands = new Discord.Collection();
@ -88,13 +81,13 @@ const functions = {
for (const file of dotCommandFiles) { for (const file of dotCommandFiles) {
const dotCommand = require(`./dot-commands/${file}`); const dotCommand = require(`./dot-commands/${file}`);
client.dotCommands.set(dotCommand.name, dotCommand); client.dotCommands.set(dotCommand.name, dotCommand);
if (Array.isArray(dotCommand.alias)) { // if (Array.isArray(dotCommand.alias)) {
dotCommand.alias.forEach(element => { // dotCommand.alias.forEach(element => {
client.dotCommands.set(element, dotCommand); // client.dotCommands.set(element, dotCommand);
}); // });
} else if (dotCommand.alias != undefined) { // } else if (dotCommand.alias != undefined) {
client.dotCommands.set(dotCommand.alias, dotCommand); // client.dotCommands.set(dotCommand.alias, dotCommand);
} // }
} }
if (isDev) console.log('Dot Commands Collection Built'); if (isDev) console.log('Dot Commands Collection Built');
}, },
@ -102,12 +95,13 @@ const functions = {
if (!client.gifs) client.gifs = new Discord.Collection(); if (!client.gifs) client.gifs = new Discord.Collection();
client.gifs.clear(); client.gifs.clear();
for (const row of rows) { for (const row of rows) {
const gif = { // const gif = {
id: row.id, // id: row.id,
name: row.name, // name: row.name,
embed_url: row.embed_url // embed_url: row.embed_url
}; // };
client.gifs.set(gif.name, gif); const gifData = new GifData().setInfo(row.name, row.embed_url);
client.gifs.set(gifData.name, gifData);
} }
if (isDev) console.log('GIFs Collection Built'); if (isDev) console.log('GIFs Collection Built');
}, },
@ -127,13 +121,8 @@ const functions = {
if (!client.pastas) client.pastas = new Discord.Collection(); if (!client.pastas) client.pastas = new Discord.Collection();
client.pastas.clear(); client.pastas.clear();
for (const row of rows) { for (const row of rows) {
const pasta = { const pastaData = new PastaData().setInfo(row.name, row.content, row.iconurl, row.id);
id: row.id, client.pastas.set(pastaData.name, pastaData);
name: row.name,
content: row.content,
iconUrl: row.iconurl,
};
client.pastas.set(pasta.name, pasta);
} }
if (isDev) console.log('Pastas Collection Built'); if (isDev) console.log('Pastas Collection Built');
}, },
@ -164,7 +153,7 @@ const functions = {
if (isDev) console.log('Strains Collection Built'); if (isDev) console.log('Strains Collection Built');
}, },
medicalAdvice(rows, client) { medicalAdvice(rows, client) {
if (!client.medicalAdviceCol) client.medicalAdviceColl = new Discord.Collection(); if (!client.medicalAdviceColl) client.medicalAdviceColl = new Discord.Collection();
client.medicalAdviceColl.clear(); client.medicalAdviceColl.clear();
for (const row of rows) { for (const row of rows) {
const medicalAdvice = { const medicalAdvice = {
@ -175,39 +164,10 @@ const functions = {
} }
if (isDev) console.log('Medical Advice Collection Built'); if (isDev) console.log('Medical Advice Collection Built');
}, },
}, roaches(client) {
dot: { if (!client.roaches) client.roaches = new Discord.Collection();
getCommandData(message) { client.roaches.clear();
const commandData = {}; if (isDev) console.log('Medical Advice Collection Built');
// 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);
},
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;
} }
}, },
embeds: { embeds: {
@ -215,52 +175,74 @@ const functions = {
// Construct the Help Embed // Construct the Help Embed
const helpEmbed = new Discord.MessageEmbed() const helpEmbed = new Discord.MessageEmbed()
.setColor('BLUE') .setColor('BLUE')
.setAuthor('Help Page') .setAuthor({name: 'Help Page'})
.setDescription(strings.help.description)
.setThumbnail(strings.urls.avatar); .setThumbnail(strings.urls.avatar);
// Construct the Slash Commands help // Construct the Slash Commands help
let slashCommandsFields = []; let slashCommandsFields = [];
let slashSeenNames = new Array();
const slashCommandsMap = interaction.client.slashCommands.map(e => { const slashCommandsMap = interaction.client.slashCommands.map(e => {
return { if (!slashSeenNames.includes(e.data.name)) {
name: e.data.name, slashSeenNames.push(e.data.name);
description: e.data.description const command = {
}; name: e.data.name,
}) description: e.data.description
};
return command;
} else {
return null;
}
});
for (const e of slashCommandsMap) { for (const e of slashCommandsMap) {
slashCommandsFields.push({ slashCommandsFields.push(`- \`/${e.name}\` - ${e.description}`);
name: `- /${e.name}`,
value: e.description,
inline: false,
});
} }
console.log(slashCommandsFields);
// Construct the Dot Commands Help // Construct the Dot Commands Help
let dotCommandsFields = []; let dotCommandsFields = new Array();
let dotSeenNames = new Array();
const dotCommandsMap = interaction.client.dotCommands.map(e => { const dotCommandsMap = interaction.client.dotCommands.map(e => {
return { if (!dotSeenNames.includes(e.name)) {
name: e.name, dotSeenNames.push(e.name);
description: e.description, let command = {
usage: e.usage 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) { for (const e of dotCommandsMap) {
dotCommandsFields.push({ if (e != null) {
name: `- .${e.name}`, dotCommandsFields.push(`- \`.${e.name}\` - ${e.description}\n\tUsage: ${e.usage}\n\tAliases: ${e.aliasString}`);
value: `${e.description}\nUsage: ${e.usage}`, }
inline: false,
});
} }
console.log(dotCommandsFields);
helpEmbed.addField('Slash Commands', strings.help.slash); // Construct the Description Fields
helpEmbed.addFields(slashCommandsFields); const descriptionFields = [
helpEmbed.addField('Dot Commands', strings.help.dot); `${strings.help.description}\n`,
helpEmbed.addFields(dotCommandsFields); `**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: [ return { embeds: [
helpEmbed helpEmbed
@ -268,76 +250,65 @@ const functions = {
}, },
gif(commandData) { gif(commandData) {
return { embeds: [new Discord.MessageEmbed() return { embeds: [new Discord.MessageEmbed()
.setAuthor(`${commandData.args}.${commandData.command}`) .setAuthor({name: `${commandData.args}.${commandData.command}`})
.setImage(commandData.embed_url) .setImage(commandData.embed_url)
.setTimestamp() .setTimestamp()
.setFooter(commandData.author)]}; .setFooter({text: commandData.author})]};
}, },
pasta(commandData) { pasta(commandData, pastaData) {
return { embeds: [ new Discord.MessageEmbed() return { embeds: [ new Discord.MessageEmbed()
.setAuthor(`${commandData.args}.${commandData.command}`) .setAuthor({name: `${commandData.args}.${commandData.command}`})
.setDescription(commandData.content) .setDescription(pastaData.content)
.setThumbnail(commandData.iconUrl) .setThumbnail("https://assets.vfsh.dev/shednod.png")
.setTimestamp() .setTimestamp()
.setFooter(commandData.author)]}; .setFooter({text: commandData.author})]};
}, },
pastas(commandData) { pastas(commandData, indexedPastas) {
const pastasArray = [];
const pastasEmbed = new Discord.MessageEmbed() const pastasEmbed = new Discord.MessageEmbed()
.setAuthor(commandData.command) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter(commandData.author); .setFooter({text: `Page: ${indexedPastas.pagesString}`})
.setDescription(indexedPastas.pastasString);
for (const row of commandData.pastas) { const pastasPageAR = customEmbeds.pastasPageAR(indexedPastas.state);
pastasArray.push(`#${row.id} - ${row.name}.pasta`); return { embeds: [pastasEmbed], components: [pastasPageAR], ephemeral: true };
}
const pastasString = pastasArray.join('\n');
pastasEmbed.setDescription(pastasString);
return { embeds: [pastasEmbed], ephemeral: true };
}, },
gifs(commandData) { gifs(commandData, indexedGifs) {
const gifsArray = [];
const gifsEmbed = new Discord.MessageEmbed() const gifsEmbed = new Discord.MessageEmbed()
.setAuthor(commandData.command) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter(commandData.author); .setFooter({text: `Page: ${indexedGifs.pagesString}`})
.setDescription(indexedGifs.gifsString);
for (const row of commandData.gifs) { const gifsPageAR = customEmbeds.gifsPageAR(indexedGifs.state);
gifsArray.push(`#${row.id} - ${row.name}.gif`); 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'); const jointsPageAR = customEmbeds.jointsPageAR(indexedJoints.state);
gifsEmbed.setDescription(gifsString); return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true };
return { embeds: [gifsEmbed] };
}, },
text(commandData) { text(commandData) {
return { embeds: [new Discord.MessageEmbed() return { embeds: [new Discord.MessageEmbed()
.setAuthor(commandData.command) .setAuthor({name: commandData.command})
.setDescription(commandData.content) .setDescription(commandData.content)
.setTimestamp() .setTimestamp()
.setFooter(commandData.author)]}; .setFooter({text: commandData.author})]};
}, },
requests(commandData) { requests(commandData, indexedRequests) {
const requestsEmbed = new Discord.MessageEmbed() const requestsEmbed = new Discord.MessageEmbed()
.setAuthor(commandData.command) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter(commandData.author); .setFooter({text: `Page: ${indexedRequests.pagesString}`})
.setDescription(indexedRequests.requestsString);
const requestsArray = []; const requestsPageAR = customEmbeds.requestsPageAR(indexedRequests.state);
return { embeds: [requestsEmbed], components: [requestsPageAR], ephemeral: true };
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 };
}, },
strain(strainInfo, interaction) { strain(strainInfo, interaction) {
const strainEmbed = new Discord.MessageEmbed() const strainEmbed = new Discord.MessageEmbed()
@ -389,9 +360,9 @@ const functions = {
}, },
gpt(prompt, response, usage) { gpt(prompt, response, usage) {
const gptEmbed = new Discord.MessageEmbed() const gptEmbed = new Discord.MessageEmbed()
.setAuthor({ name: "NodBot powered by GPT-3", iconURL: "https://assets.vfsh.dev/openai-logos/PNGs/openai-logomark.png" }) .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}`) .setDescription(`**Prompt**\n${prompt}\n\n**Response**\n${response}`)
.setFooter({ text: `This prompt used ${usage.tokens} tokens for a cost of ${usage.usdc}¢` }) .setFooter({ text: `This prompt used ${usage.tokens} tokens for a cost of ${usage.usdc}¢. Generated using ${strings.ai.chatModel}` })
return { embeds: [gptEmbed] }; return { embeds: [gptEmbed] };
}, },
generatingResponse() { generatingResponse() {
@ -401,6 +372,63 @@ const functions = {
.setDescription("Generating a response, please stand by.") .setDescription("Generating a response, please stand by.")
.setFooter({ text: "Ligma balls" }); .setFooter({ text: "Ligma balls" });
return { embeds: [embed] }; 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;
}
} }
}, },
collect: { collect: {
@ -417,12 +445,13 @@ const functions = {
functions.download.requests(client); functions.download.requests(client);
}); });
}, },
pasta(pastaData, client) { async pasta(pastaData, client) {
const query = `INSERT INTO pastas (name, content) VALUES (${db.escape(pastaData.name)},${db.escape(pastaData.content)})`; 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)}`;
db.query(query, (err, rows, fields) => { await db.query(query, (err, rows, fields) => {
if (err) throw err; if (err) throw err;
functions.download.pastas(client); functions.download.pastas(client);
}); });
return;
}, },
joint(content, client) { joint(content, client) {
const query = `INSERT INTO joints (content) VALUES (${db.escape(content)})`; const query = `INSERT INTO joints (content) VALUES (${db.escape(content)})`;
@ -431,9 +460,9 @@ const functions = {
functions.download.joints(client); functions.download.joints(client);
}); });
}, },
gif(gifData, client) { async gif(gifData, client) {
const query = `INSERT INTO gifs (name, embed_url) VALUES (${db.escape(gifData.name)}, ${db.escape(gifData.embed_url)})`; 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)}`;
db.query(query, (err, rows, fields) => { await db.query(query, (err, rows, fields) => {
if (err) throw err; if (err) throw err;
functions.download.gifs(client); functions.download.gifs(client);
}); });
@ -487,7 +516,7 @@ const functions = {
const description = db.escape(( interaction.options.getString('description') || 'Unknown' )); const description = db.escape(( interaction.options.getString('description') || 'Unknown' ));
const flavor = db.escape(( interaction.options.getString('flavor') || 'Unknown' )); const flavor = db.escape(( interaction.options.getString('flavor') || 'Unknown' ));
const rating = db.escape(( interaction.options.getString('rating') || '3' )); 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); console.log(strainQuery);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.query(strainQuery, (err, rows, fields) => { db.query(strainQuery, (err, rows, fields) => {
@ -505,37 +534,37 @@ const functions = {
} }
}, },
download: { download: {
requests(client) { async requests(client) {
const query = 'SELECT * FROM requests WHERE status = \'Active\' ORDER BY id DESC'; 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; if (err) throw err;
functions.collections.requests(rows, client); functions.collections.requests(rows, client);
}); });
}, },
pastas(client) { async pastas(client) {
const query = 'SELECT * FROM pastas ORDER BY id ASC'; 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; if (err) throw err;
functions.collections.pastas(rows, client); functions.collections.pastas(rows, client);
}); });
}, },
gifs(client) { async gifs(client) {
const query = 'SELECT * FROM gifs ORDER BY id ASC'; 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; if (err) throw err;
functions.collections.gifs(rows, client); functions.collections.gifs(rows, client);
}); });
}, },
joints(client) { async joints(client) {
const query = 'SELECT * FROM joints ORDER BY id ASC'; 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; if (err) throw err;
functions.collections.joints(rows, client); 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)}`; 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) { if (rows != undefined) {
const strainInfo = { const strainInfo = {
id: `${rows[0].id}`, id: `${rows[0].id}`,
@ -550,16 +579,16 @@ const functions = {
} }
}); });
}, },
strains(client) { async strains(client) {
const query = 'SELECT id, strain FROM strains'; const query = 'SELECT id, strain FROM strains';
db.query(query, (err, rows, fields) => { await db.query(query, (err, rows, fields) => {
if (err) throw err; if (err) throw err;
functions.collections.strains(rows, client); functions.collections.strains(rows, client);
}); });
}, },
medicalAdvice(client) { async medicalAdvice(client) {
const query = 'SELECT * FROM medical_advice ORDER BY id ASC'; 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; if (err) throw err;
functions.collections.medicalAdvice(rows, client); functions.collections.medicalAdvice(rows, client);
}); });
@ -577,35 +606,14 @@ const functions = {
} }
} }
}, },
openAI: { search: {
chatPrompt(userPrompt) { gifs(query, client) {
return new Promise(async (resolve, reject) => { const gifSearcher = new FuzzySearch(client.gifs.map(element => element.name));
const response = await openai.createCompletion({ return gifSearcher.search(query).slice(0,25);
model: 'text-davinci-003',
prompt: userPrompt,
temperature: 0.7,
max_tokens: 250
}).catch(e => {
reject(e);
return null;
});
resolve(response.data);
});
}, },
imagePrompt(userPrompt, size, userId) { pastas(query, client) {
return new Promise(async (resolve, reject) => { const pastaSearcher = new FuzzySearch(client.pastas.map(element => element.name));
try { return pastaSearcher.search(query).slice(0,25);
const response = await openai.createImage({
prompt: userPrompt,
size: size,
user: userId
});
resolve(response.data.data[0].url);
} catch (e) {
reject(e);
return;
}
});
} }
}, },
// Parent-Level functions (miscellaneuous) // Parent-Level functions (miscellaneuous)
@ -627,6 +635,7 @@ const functions = {
}, },
spongebob(commandData) { spongebob(commandData) {
let newText = ''; let newText = '';
let lastIsUpper = 0;
for (const letter of commandData.args) { for (const letter of commandData.args) {
if (letter == ' ') { if (letter == ' ') {
newText += letter; newText += letter;
@ -634,21 +643,178 @@ const functions = {
} }
if (letter == 'i' || letter == 'I') { if (letter == 'i' || letter == 'I') {
newText += 'i'; newText += 'i';
lastIsUpper = 0;
continue; continue;
} }
if (letter == 'l' || letter == 'L') { if (letter == 'l' || letter == 'L') {
newText += 'L'; newText += 'L';
lastIsUpper = 1;
continue; continue;
} }
if (Math.random() > 0.5) { if (lastIsUpper === 0) {
newText += letter.toUpperCase(); newText += letter.toUpperCase();
lastIsUpper = 1;
} else { } else {
newText += letter.toLowerCase(); newText += letter.toLowerCase();
lastIsUpper = 0;
} }
} }
return newText + ' <:spongebob:1053398825965985822>'; return newText + ' <:spongebob:1053398825965985822>';
}, },
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() { generateErrorId() {
const digitCount = 10; const digitCount = 10;
const digits = []; const digits = [];

96
main.js
View File

@ -27,19 +27,27 @@ const { MessageActionRow, MessageButton } = require('discord.js');
const fn = require('./functions.js'); const fn = require('./functions.js');
const config = require('./config.json'); const config = require('./config.json');
const strings = require('./strings.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; const isDev = process.env.isDev;
client.once('ready', () => { client.once('ready', async () => {
fn.collections.interactionStorage(client);
fn.collections.slashCommands(client); fn.collections.slashCommands(client);
fn.collections.dotCommands(client); fn.collections.dotCommands(client);
fn.collections.setvalidCommands(client); fn.collections.setvalidCommands(client);
fn.download.gifs(client); fn.collections.roaches(client);
fn.download.pastas(client); await fn.download.gifs(client);
fn.download.joints(client); await fn.download.pastas(client);
fn.download.requests(client); await fn.download.joints(client);
fn.download.strains(client); await fn.download.requests(client);
fn.download.medicalAdvice(client); await fn.download.strains(client);
await fn.download.medicalAdvice(client);
console.log('Ready!'); console.log('Ready!');
await fn.avWx.metar.getAllICAOs();
await fn.avWx.datis.getAllICAOs();
// console.log(JSON.stringify(icaoArray));
client.channels.fetch(statusChannelId).then(channel => { client.channels.fetch(statusChannelId).then(channel => {
channel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`); channel.send(`${new Date().toISOString()} -- <@${process.env.ownerId}>\nStartup Sequence Complete`);
}); });
@ -49,10 +57,14 @@ client.once('ready', () => {
client.on('interactionCreate', async interaction => { client.on('interactionCreate', async interaction => {
if (interaction.isCommand()) { if (interaction.isCommand()) {
if (isDev) { if (isDev) {
console.log(interaction); console.log('Interaction ID: ' + interaction.id);
} }
const { commandName } = interaction; const { commandName } = interaction;
if (!client.iStorage.has(interaction.id)) {
new InteractionStorage(interaction.id, interaction);
}
if (client.slashCommands.has(commandName)) { if (client.slashCommands.has(commandName)) {
client.slashCommands.get(commandName).execute(interaction); client.slashCommands.get(commandName).execute(interaction);
} else { } else {
@ -62,7 +74,8 @@ client.on('interactionCreate', async interaction => {
} }
if (interaction.isButton()) { if (interaction.isButton()) {
if (interaction.user.id != strings.temp.gifUserId) return; if (isDev) console.log('Origin Interaction ID: ' + interaction.message.interaction.id);
if (isDev) console.log('Button ID: ' + interaction.component.customId);
// Get some meta info from strings // Get some meta info from strings
const index = strings.temp.gifIndex; const index = strings.temp.gifIndex;
const limit = strings.temp.gifLimit; const limit = strings.temp.gifLimit;
@ -108,10 +121,11 @@ client.on('interactionCreate', async interaction => {
interaction.update(strings.temp.gifs[newIndex].embed_url); interaction.update(strings.temp.gifs[newIndex].embed_url);
break; break;
case 'confirmGif': case 'confirmGif':
const gifData = { // const gifData = {
name: strings.temp.gifName, // name: strings.temp.gifName,
embed_url: strings.temp.gifs[strings.temp.gifIndex].embed_url, // url: strings.temp.gifs[strings.temp.gifIndex].embed_url,
}; // };
const gifData = new GifData().setInfo(strings.temp.gifName, strings.temp.gifs[strings.temp.gifIndex].embed_url);
fn.upload.gif(gifData, client); fn.upload.gif(gifData, client);
interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] }); interaction.update({ content: `I've saved the GIF as ${gifData.name}.gif`, components: [] });
fn.download.gifs(interaction.client); fn.download.gifs(interaction.client);
@ -170,20 +184,46 @@ client.on('interactionCreate', async interaction => {
interaction.update({ content: 'Canceled.', components: [row] }); interaction.update({ content: 'Canceled.', components: [row] });
break; break;
default: default:
ButtonHandlers.baseEvent(interaction);
break; break;
} }
} }
// Handle autocomplete requests // Handle autocomplete requests
if (interaction.isAutocomplete()) { if (interaction.isAutocomplete()) {
if (interaction.commandName == 'strain') { switch (interaction.commandName) {
const searchString = interaction.options.getFocused(); case 'strain':
const choices = fn.weed.strain.lookup(searchString, interaction.client); const searchString = interaction.options.getFocused();
await interaction.respond( const choices = fn.weed.strain.lookup(searchString, interaction.client);
choices.map(choice => ({ name: choice, value: choice })) await interaction.respond(
) choices.map(choice => ({ name: choice, value: choice }))
} else { );
return; 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 +233,15 @@ client.on('messageCreate', message => {
// Some basic checking to prevent running unnecessary code // Some basic checking to prevent running unnecessary code
if (message.author.bot) return; 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(); const lowerContent = message.content.toLowerCase();
if (lowerContent.includes('big') && lowerContent.includes('doinks')) message.reply('gang.'); const autoresponses = fn.autoresponses.checkForAll(lowerContent);
if (lowerContent.includes('ligma')) message.reply('ligma balls, goteem'); autoresponses.forEach(e => {
if (lowerContent.includes('frfr') || lowerContent.includes('fr fr') || lowerContent.includes('bussin') || lowerContent.includes(' ong') || lowerContent.startsWith('ong')) { fn.autoresponses.send(message, e);
const randIndex = Math.floor(Math.random() * strings.capbacks.length); });
message.reply(strings.capbacks[randIndex]);
}
// Break the message down into its components and analyze it // 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); console.log(commandData);
if (commandData.isValid && commandData.isCommand) { if (commandData.isValid && commandData.isCommand) {

View File

@ -1,6 +1,6 @@
{ {
"name": "nodbot", "name": "nodbot",
"version": "3.2.0", "version": "3.3.3",
"description": "Nods and Nod Accessories, now with ChatGPT!", "description": "Nods and Nod Accessories, now with ChatGPT!",
"main": "main.js", "main": "main.js",
"dependencies": { "dependencies": {
@ -12,7 +12,7 @@
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"fuzzy-search": "^3.2.1", "fuzzy-search": "^3.2.1",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"openai": "^3.2.1", "openai": "^4.12.0",
"tenorjs": "^1.0.10" "tenorjs": "^1.0.10"
}, },
"engines": { "engines": {

View File

@ -1,27 +0,0 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const fn = require('../functions.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('chat')
.setDescription('Send a message to ChatGPT')
.addStringOption(o =>
o.setName("prompt")
.setDescription("Prompt to send to ChatGPT")
.setRequired(true)
),
async execute(interaction) {
await interaction.deferReply();
await interaction.editReply(fn.embeds.generatingResponse());
const userPrompt = interaction.options.getString("prompt");
const response = await fn.openAI.chatPrompt(userPrompt).catch(e => console.error(e));
const responseText = response.choices[0].text;
const usage = {
tokens: response.usage.total_tokens,
usdc: response.usage.total_tokens * ( 0.2 / 1000 ) // 0.2¢ per 1000 tokens or 0.0002¢ per token.
};
const gptEmbed = fn.embeds.gpt(userPrompt, responseText, usage);
await interaction.editReply(gptEmbed);
fn.upload.openai(interaction.user.id, userPrompt, "gpt-3.5-turbo", usage.tokens, usage.usdc);
},
};

View File

@ -1,40 +0,0 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const fn = require('../functions.js');
const strings = require("../strings.json");
module.exports = {
data: new SlashCommandBuilder()
.setName('dalle')
.setDescription('Generate an image with DALL-e')
.addStringOption(o =>
o.setName("prompt")
.setDescription("Prompt to send to DALL-e")
.setRequired(true)
)
.addStringOption(o =>
o.setName("size")
.setDescription("1024x1024, 512x512, 256x256")
.setRequired(false)
.addChoices(
{ name: "1024x1024 (2¢)", value: "1024x1024" },
{ name: "512x512 (1.8¢)", value: "512x512" },
{ name: "256x256 (1.6¢)", value: "256x256" }
)),
async execute(interaction) {
try {
await interaction.deferReply();
await interaction.editReply(fn.embeds.generatingResponse());
const userPrompt = interaction.options.getString("prompt");
const size = interaction.options.getString("size") ? interaction.options.getString("size") : "512x512";
const imageUrl = await fn.openAI.imagePrompt(userPrompt, size);
const dalleEmbed = fn.embeds.dalle(userPrompt, imageUrl, size);
await interaction.editReply(dalleEmbed);
fn.upload.openai(interaction.user.id, userPrompt, "dalle", 0, strings.costs.dalle[size]);
} catch (err) {
const errorId = fn.generateErrorId();
console.error(`${errorId}: ${err}`);
await interaction.editReply(`An error has occured. Error ID: ${errorId}\n${err}`);
}
},
};

110
slash-commands/edit.js Normal file
View File

@ -0,0 +1,110 @@
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 { 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`);
}
};

View File

@ -1,33 +1,30 @@
const { SlashCommandBuilder } = require('@discordjs/builders'); const { SlashCommandBuilder } = require('@discordjs/builders');
const { config } = require('dotenv'); const { config } = require('dotenv');
const fn = require('../functions.js'); const fn = require('../functions.js');
const indexer = require('../CustomModules/Indexer.js');
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('gifs') .setName('gifs')
.setDescription('Get a list of currently saved GIFs.'), .setDescription('Get a list of currently saved GIFs.'),
async execute(interaction) { execute(interaction) {
if (!interaction.client.gifs) { if (!interaction.client.gifs) {
interaction.reply('For some reason I don\'t have access to the collection of gifs. Sorry about that!'); interaction.reply('For some reason I don\'t have access to the collection of gifs. Sorry about that!');
return; return;
} }
const gifsMap = interaction.client.gifs.map(e => { let iStorage = interaction.client.iStorage.get(interaction.id);
return { let indexedGifs = indexer(interaction.client.gifs, 0);
id: e.id, indexedGifs.gifsString = new String();
name: e.name,
}; iStorage.page = 0;
});
const commandData = { for (const gif of indexedGifs.thisPage) {
gifs: [], indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`;
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)); const commandData = {
}, command: "/gifs",
author: interaction.member.displayName
};
interaction.reply(fn.embeds.gifs(commandData, indexedGifs));
}
}; };

View File

@ -4,29 +4,8 @@ const fn = require('../functions.js');
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('help') .setName('help')
.setDescription('Send the help page.') .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'))
,
async execute(interaction) { 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)); await interaction.reply(fn.embeds.help(interaction));
}, }
}; };

View File

@ -1,15 +1,29 @@
const { SlashCommandBuilder } = require('@discordjs/builders'); const { SlashCommandBuilder } = require('@discordjs/builders');
const fn = require('../functions.js'); const fn = require('../functions.js');
const indexer = require('../CustomModules/Indexer.js');
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('joints') .setName('joints')
.setDescription('Send a list of all the /joint phrases.'), .setDescription('Send a list of all the /joint phrases.'),
async execute(interaction) { async execute(interaction) {
let joints = []; if (!interaction.client.joints) {
interaction.client.joints.map(e => { interaction.reply('For some reason I don\'t have access to the collection of joints. Sorry about that!');
joints.push(e.content); return;
}); }
interaction.reply({ content: 'Here are all the `.joint` phrases I have saved:\n\n' + joints.join('\n'), ephemeral: true }); let iStorage = interaction.client.iStorage.get(interaction.id);
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`;
}
const commandData = {
command: "/joints",
author: interaction.member.displayName
};
interaction.reply(fn.embeds.joints(commandData, indexedJoints));
}, },
}; };

View File

@ -1,6 +1,7 @@
const { SlashCommandBuilder } = require('@discordjs/builders'); const { SlashCommandBuilder } = require('@discordjs/builders');
const { config } = require('dotenv'); const { config } = require('dotenv');
const fn = require('../functions.js'); const fn = require('../functions.js');
const indexer = require('../CustomModules/Indexer.js');
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@ -11,23 +12,19 @@ module.exports = {
interaction.reply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true }); interaction.reply({ content: 'For some reason I don\'t have access to the collection of copypastas. Sorry about that!', ephemeral: true });
return; return;
} }
const commandData = { let iStorage = interaction.client.iStorage.get(interaction.id);
author: interaction.user.tag, let indexedPastas = indexer(interaction.client.pastas, 0);
command: interaction.commandName, indexedPastas.pastasString = new String();
pastas: [],
}; iStorage.page = 0;
const pastasMap = interaction.client.pastas.map(e => {
return { for (const pasta of indexedPastas.thisPage) {
id: e.id, indexedPastas.pastasString += `${pasta.name}.pasta\n`;
name: e.name,
};
});
for (const row of pastasMap) {
commandData.pastas.push({
id: row.id,
name: row.name,
});
} }
interaction.reply(fn.embeds.pastas(commandData)); const commandData = {
command: "/pastas",
author: interaction.member.displayName
};
interaction.reply(fn.embeds.pastas(commandData, indexedPastas));
}, },
}; };

View File

@ -1,39 +1,31 @@
const { SlashCommandBuilder } = require('@discordjs/builders'); const { SlashCommandBuilder } = require('@discordjs/builders');
const { config } = require('dotenv'); const { config } = require('dotenv');
const fn = require('../functions.js'); const fn = require('../functions.js');
const indexer = require('../CustomModules/Indexer.js');
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName('requests') .setName('requests')
.setDescription('Get a list of Active requests from the database') .setDescription('Get a list of Active requests from the database'),
.addStringOption(option =>
option
.setName('page')
.setDescription('Page Number')
.setRequired(true)),
async execute(interaction) { async execute(interaction) {
const pageNum = interaction.options.getString('page'); if (!interaction.client.requests) {
const commandData = { interaction.reply('For some reason I don\'t have access to the collection of requests. Sorry about that!');
author: interaction.user.tag, return;
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)); let iStorage = interaction.client.iStorage.get(interaction.id);
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`;
}
const commandData = {
command: "/requests",
author: interaction.member.displayName
};
interaction.reply(fn.embeds.requests(commandData, indexedRequests));
}, },
}; };

232
slash-commands/save.js Normal file
View File

@ -0,0 +1,232 @@
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');
const { GifData } = require('../CustomModules/NodBot.js');
const customEmbeds = 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 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))
)
// 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":
// TODO Check on option names
const actionRow = customEmbeds.gifSearchAR();
// 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.editReply('Sorry I was unable to find a GIF of ' + query);
return;
}
for (const row of res) {
strings.temp.gifs.push({
embed_url: row.media_formats.gif.url,
});
}
interaction.editReply({ content: strings.temp.gifs[0].embed_url, 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}`);
}
}
};

View File

@ -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);
}
},
};

View File

@ -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 });
},
};

View File

@ -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 });
},
};

View File

@ -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 });
},
};

View File

@ -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
});
});
},
};

View File

@ -1,8 +1,8 @@
{ {
"help": { "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.", "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.", "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'" "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": { "emoji": {
"joint": "<:joint:862082955902976000>", "joint": "<:joint:862082955902976000>",
@ -14,14 +14,6 @@
"urls": { "urls": {
"avatar": "https://cdn.discordapp.com/avatars/513184762073055252/12227aa23a06d5178853e59b72c7487b.webp?size=128" "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": { "costs": {
"gpt": { "gpt": {
"gpt-3.5-turbo": 0.2 "gpt-3.5-turbo": 0.2
@ -32,5 +24,61 @@
"1024x1024": 2.0 "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": {} "temp": {}
} }

4
update.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/bash
git pull
docker build . -t v0idf1sh/nodbot
docker push v0idf1sh/nodbot