Compare commits

...

62 Commits
dev ... 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
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
35 changed files with 1110 additions and 477 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,35 +0,0 @@
name: NodBot Production Dockerization
on:
commit:
branches:
- pe
env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
jobs:
build:
runs-on: self-hosted
steps:
- name: Pull latest from Git
run: |
pwd
whoami
cd /root/nodbot
git pull
git checkout pe
- name: Build the Docker image
run: |
cd /root/nodbot
docker build . --file Dockerfile --tag v0idf1sh/nodbot-pe
- name: Log into Docker Hub
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
- name: Push image to Docker Hub
run: |
cd /root/nodbot
docker push v0idf1sh/nodbot-pe

View File

@ -1,35 +0,0 @@
name: NodBot Production Dockerization
on:
pull_request:
branches:
- main
env:
DHUB_UNAME: ${{ secrets.DHUB_UNAME }}
DHUB_PWORD: ${{ secrets.DHUB_PWORD }}
jobs:
build:
runs-on: self-hosted
steps:
- name: Pull latest from Git
run: |
pwd
whoami
cd /root/nodbot
git pull
git checkout $GITHUB_HEAD_REF
- name: Build the Docker image
run: |
cd /root/nodbot
docker build . --file Dockerfile --tag v0idf1sh/nodbot
- name: Log into Docker Hub
run: docker login -u $DHUB_UNAME -p $DHUB_PWORD
- name: Push image to Docker Hub
run: |
cd /root/nodbot
docker push v0idf1sh/nodbot

2
.gitignore vendored
View File

@ -3,6 +3,7 @@
package-lock.json package-lock.json
.VSCodeCounter/ .VSCodeCounter/
.env* .env*
*.env
# Custom folders # Custom folders
# gifs/* # gifs/*
@ -114,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;
}
}

View File

@ -1,4 +1,34 @@
module.exports = { 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 { GifData: class {
constructor() { constructor() {
this.id = 0; this.id = 0;

View File

@ -5,4 +5,5 @@ 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,6 +9,7 @@ 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();

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

@ -8,7 +8,9 @@ module.exports = {
const client = message.client; const client = message.client;
let pastaData; let pastaData;
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 {
pastaData = client.pastas.get(commandData.args); pastaData = client.pastas.get(commandData.args);
} }

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,27 +20,15 @@ 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');
@ -58,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();
@ -89,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');
}, },
@ -161,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 = {
@ -172,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 + 1).toLowerCase();
commandData.author = `${message.author.username}`;
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: {
@ -213,51 +176,73 @@ const functions = {
const helpEmbed = new Discord.MessageEmbed() const helpEmbed = new Discord.MessageEmbed()
.setColor('BLUE') .setColor('BLUE')
.setAuthor({name: '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)) {
slashSeenNames.push(e.data.name);
const command = {
name: e.data.name, name: e.data.name,
description: e.data.description description: e.data.description
}; };
}) return command;
} else {
for (const e of slashCommandsMap) { return null;
slashCommandsFields.push({
name: `- /${e.name}`,
value: e.description,
inline: false,
});
} }
});
for (const e of slashCommandsMap) {
slashCommandsFields.push(`- \`/${e.name}\` - ${e.description}`);
}
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)) {
dotSeenNames.push(e.name);
let command = {
name: e.name, name: e.name,
description: e.description, description: e.description,
usage: e.usage usage: e.usage
}; };
}); command.aliasString = new String();
if (e.alias != undefined && typeof e.alias === 'object') {
for (const e of dotCommandsMap) { for (const a of e.alias) {
dotCommandsFields.push({ command.aliasString += `\`.${a}\`, `;
name: `- .${e.name}`,
value: `${e.description}\nUsage: ${e.usage}`,
inline: false,
});
} }
} 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) {
if (e != null) {
dotCommandsFields.push(`- \`.${e.name}\` - ${e.description}\n\tUsage: ${e.usage}\n\tAliases: ${e.aliasString}`);
}
}
console.log(dotCommandsFields);
helpEmbed.addField('Slash Commands', strings.help.slash); // 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
@ -274,34 +259,39 @@ const functions = {
return { embeds: [ new Discord.MessageEmbed() return { embeds: [ new Discord.MessageEmbed()
.setAuthor({name: `${commandData.args}.${commandData.command}`}) .setAuthor({name: `${commandData.args}.${commandData.command}`})
.setDescription(pastaData.content) .setDescription(pastaData.content)
.setThumbnail(pastaData.iconUrl) .setThumbnail("https://assets.vfsh.dev/shednod.png")
.setTimestamp() .setTimestamp()
.setFooter({text: commandData.author})]}; .setFooter({text: commandData.author})]};
}, },
pastas(commandData) { pastas(commandData, indexedPastas) {
const pastasArray = [];
const pastasEmbed = new Discord.MessageEmbed() const pastasEmbed = new Discord.MessageEmbed()
.setAuthor({name: commandData.command}) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter({text: 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, gifList) { gifs(commandData, indexedGifs) {
const gifsEmbed = new Discord.MessageEmbed() const gifsEmbed = new Discord.MessageEmbed()
.setAuthor({name: commandData.command}) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter({text: commandData.author}) .setFooter({text: `Page: ${indexedGifs.pagesString}`})
.setDescription(gifList.join('\n')); .setDescription(indexedGifs.gifsString);
return { embeds: [gifsEmbed] }; const gifsPageAR = customEmbeds.gifsPageAR(indexedGifs.state);
return { embeds: [gifsEmbed], components: [gifsPageAR], ephemeral: true };
},
joints(commandData, indexedJoints) {
const jointsEmbed = new Discord.MessageEmbed()
.setAuthor({name: commandData.command})
.setTimestamp()
.setFooter({text: `Page: ${indexedJoints.pagesString}`})
.setDescription(indexedJoints.jointsString);
const jointsPageAR = customEmbeds.jointsPageAR(indexedJoints.state);
return { embeds: [jointsEmbed], components: [jointsPageAR], ephemeral: true };
}, },
text(commandData) { text(commandData) {
return { embeds: [new Discord.MessageEmbed() return { embeds: [new Discord.MessageEmbed()
@ -310,24 +300,15 @@ const functions = {
.setTimestamp() .setTimestamp()
.setFooter({text: commandData.author})]}; .setFooter({text: commandData.author})]};
}, },
requests(commandData) { requests(commandData, indexedRequests) {
const requestsEmbed = new Discord.MessageEmbed() const requestsEmbed = new Discord.MessageEmbed()
.setAuthor({name: commandData.command}) .setAuthor({name: commandData.command})
.setTimestamp() .setTimestamp()
.setFooter({text: 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()
@ -379,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() {
@ -391,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: {
@ -407,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)}) ON DUPLICATE KEY UPDATE content=${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)})`;
@ -495,16 +534,16 @@ 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);
}); });
@ -516,16 +555,16 @@ const functions = {
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}`,
@ -540,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);
}); });
@ -567,37 +606,6 @@ const functions = {
} }
} }
}, },
openAI: {
chatPrompt(userPrompt) {
return new Promise(async (resolve, reject) => {
const response = await openai.createCompletion({
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) {
return new Promise(async (resolve, reject) => {
try {
const response = await openai.createImage({
prompt: userPrompt,
size: size,
user: userId
});
resolve(response.data.data[0].url);
} catch (e) {
reject(e);
return;
}
});
}
},
search: { search: {
gifs(query, client) { gifs(query, client) {
const gifSearcher = new FuzzySearch(client.gifs.map(element => element.name)); const gifSearcher = new FuzzySearch(client.gifs.map(element => element.name));
@ -654,6 +662,159 @@ const functions = {
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 = [];

47
main.js
View File

@ -27,20 +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 } = require('./CustomModules/NodBot.js'); 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`);
}); });
@ -50,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 {
@ -63,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;
@ -172,6 +184,7 @@ 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;
} }
} }
@ -220,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.2", "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}`);
}
},
};

View File

@ -1,36 +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 => {e.name, e.url}); let iStorage = interaction.client.iStorage.get(interaction.id);
// const commandData = { let indexedGifs = indexer(interaction.client.gifs, 0);
// gifs: [], indexedGifs.gifsString = new String();
// command: 'gifs',
// author: interaction.user.tag, iStorage.page = 0;
// };
// for (const row of gifsMap) { for (const gif of indexedGifs.thisPage) {
// commandData.gifs.push({ indexedGifs.gifsString += `[${gif.name}.gif](${gif.url})\n`;
// id: row.id, }
// name: row.name,
// });
// }
let gifList = [];
interaction.client.gifs.forEach(element => {
gifList.push(`[${element.name}](${element.url})`);
});
const commandData = { const commandData = {
command: "/gifs", command: "/gifs",
author: interaction.member.displayName author: interaction.member.displayName
}; };
interaction.reply(fn.embeds.gifs(commandData, gifList)); 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) {
interaction.reply('For some reason I don\'t have access to the collection of requests. Sorry about that!');
return;
}
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 = { const commandData = {
author: interaction.user.tag, command: "/requests",
command: interaction.commandName, author: interaction.member.displayName
requests: [],
}; };
const requestsMap = interaction.client.requests.map(e => { interaction.reply(fn.embeds.requests(commandData, indexedRequests));
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));
}, },
}; };

View File

@ -11,6 +11,7 @@ const { MessageActionRow, MessageButton } = require('discord.js');
const fn = require('../functions.js'); const fn = require('../functions.js');
const strings = require('../strings.json'); const strings = require('../strings.json');
const { GifData } = require('../CustomModules/NodBot.js'); const { GifData } = require('../CustomModules/NodBot.js');
const customEmbeds = require('../CustomModules/Embeds.js');
const { emoji } = strings; const { emoji } = strings;
module.exports = { module.exports = {
@ -142,16 +143,7 @@ module.exports = {
// GIF Search // GIF Search
case "gifsearch": case "gifsearch":
// TODO Check on option names // TODO Check on option names
// Previous GIF button const actionRow = customEmbeds.gifSearchAR();
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 // Get the query
const query = interaction.options.getString('query'); const query = interaction.options.getString('query');
@ -207,7 +199,7 @@ module.exports = {
name: interaction.options.getString('pasta-name').toLowerCase(), name: interaction.options.getString('pasta-name').toLowerCase(),
content: interaction.options.getString('pasta-content'), content: interaction.options.getString('pasta-content'),
}; };
fn.upload.pasta(pastaData, interaction.client); await fn.upload.pasta(pastaData, interaction.client);
interaction.editReply({content: `The copypasta has been saved as ${pastaData.name}.pasta`, ephemeral: true }); interaction.editReply({content: `The copypasta has been saved as ${pastaData.name}.pasta`, ephemeral: true });
break; break;
// Strain // Strain

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