diff --git a/.dockerignore b/.dockerignore
index 1d84211..e02b65f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,6 +1,9 @@
node_modules
.eslintrc.json
+eslint.config.mjs
.gitignore
*.md
.github
.do
+test
+docs
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index dbe9884..cfc582b 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -13,18 +13,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ github.repository_owner }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push ava
- uses: docker/build-push-action@v4
+ uses: docker/build-push-action@v6
with:
platforms: ${{ env.PLATFORMS }}
push: true
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index ff9e38a..47510e9 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -9,10 +9,13 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install Node
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
+ with:
+ node-version: 'lts/*'
- run: npm ci
- run: npm run lint
env:
CI: true
+ - run: npm test
diff --git a/Dockerfile b/Dockerfile
index 5fbf4d3..f86b57d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
-FROM node
+FROM node:22-alpine
ENV NODE_ENV=production
WORKDIR /usr/src/app
+COPY package.json package-lock.json ./
+RUN npm ci --omit=dev
COPY . .
-RUN npm install
-CMD ["npm", "start"]
\ No newline at end of file
+CMD ["node", "--no-deprecation", "./index.js"]
\ No newline at end of file
diff --git a/README.md b/README.md
index e5241fd..aa617aa 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://discord.com/api/oauth2/authorize?client_id=876487225716662302&permissions=34359863296&scope=bot)

-A discord implementation of the famous ava bot
+A discord and slack implementation of the famous ava bot
@@ -13,8 +13,21 @@ A discord implementation of the famous ava bot
## Usage
+Ava starts whichever platforms have tokens configured. Set Discord tokens, Slack tokens, or both to run them simultaneously.
+
```bash
+# Discord only
+export AVA_DISCORD_TOKEN=
+
+# Slack only
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
+
+# Both platforms simultaneously
export AVA_DISCORD_TOKEN=
+export AVA_SLACK_BOT_TOKEN=
+export AVA_SLACK_APP_TOKEN=
+
npm i
npm start
```
@@ -24,7 +37,10 @@ npm start
| | | |
|----------------------------|----------|---------|
| Environment Variable | Required | Default |
-| `AVA_DISCORD_TOKEN` | Yes | `""` |
+| `AVA_DISCORD_TOKEN` | Yes (Discord) | `""` |
+| `AVA_SLACK_BOT_TOKEN` | Yes (Slack) | `""` |
+| `AVA_SLACK_APP_TOKEN` | Yes (Slack) | `""` |
+| `AVA_SLACK_SIGNING_SECRET` | No | `""` |
| `AVA_DB_DIR` | No | `./` |
| `AVA_ENABLE_REMINDERS` | No | `false` |
| `AVA_REDDIT_CLIENT_ID` | No | `""` |
@@ -66,7 +82,14 @@ npm start
## Docker
```bash
+# Discord only
docker run -e AVA_DISCORD_TOKEN="" circa10a/ava
+
+# Slack only
+docker run -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
+
+# Both platforms simultaneously
+docker run -e AVA_DISCORD_TOKEN="" -e AVA_SLACK_BOT_TOKEN="" -e AVA_SLACK_APP_TOKEN="" circa10a/ava
```
## Contribution
diff --git a/commands/8ball.js b/commands/8ball.js
index c768f92..cf5ab2b 100644
--- a/commands/8ball.js
+++ b/commands/8ball.js
@@ -1,33 +1,36 @@
-import { Events } from 'discord.js';
-import eightball from '8ball';
-import { messageForAva , splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
-import dammit from 'dammit';
+import { randomItemFromArray, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
+const eightBallResponses = [
+ 'It is certain', 'It is decidedly so', 'Without a doubt',
+ 'Yes definitely', 'You may rely on it', 'As I see it, yes',
+ 'Most likely', 'Outlook good', 'Yes', 'Signs point to yes',
+ 'Reply hazy try again', 'Ask again later', 'Better not tell you now',
+ 'Cannot predict now', 'Concentrate and ask again',
+ 'Don\'t count on it', 'My reply is no', 'My sources say no',
+ 'Outlook not so good', 'Very doubtful',
+];
+
+const dammitPhrases = [
+ 'Dammit', 'Damn', 'Damnit', 'Well damn', 'Oh damn',
+ 'Son of a bitch', 'Hell', 'Crap', 'Shit', 'Bloody hell',
+ 'For fuck\'s sake', 'What the hell', 'God dammit',
+];
+
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const question = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!question) {
- message.reply('No question provided');
- return;
- }
- try {
- message.reply(`${dammit({NSFW: true})}... ${eightball()}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ if (!question) {
+ message.reply('No question provided');
+ return;
+ }
+ try {
+ message.reply(`${randomItemFromArray(dammitPhrases)}... ${randomItemFromArray(eightBallResponses)}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/bored.js b/commands/bored.js
index 77f8d19..ea3c385 100644
--- a/commands/bored.js
+++ b/commands/bored.js
@@ -1,6 +1,4 @@
-import { Events } from 'discord.js';
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -8,29 +6,18 @@ const boredEndpoint = 'https://boredapi.com/api/activity';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const response = await fetch(boredEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.reply(jsonResponse.activity);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ execute: async (message) => {
+ try {
+ const response = await fetch(boredEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.reply(jsonResponse.activity);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/brewery.js b/commands/brewery.js
index 7ea43e8..3a158dc 100644
--- a/commands/brewery.js
+++ b/commands/brewery.js
@@ -1,7 +1,6 @@
-import { EmbedBuilder, Events } from 'discord.js';
-import fetch from 'node-fetch';
+import { EmbedBuilder } from 'discord.js';
import { embedColor } from '../config/config.js';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -10,50 +9,40 @@ const beerThumbnail = 'https://i.imgur.com/6ZKnMaw.jpg';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const city = args[2];
- if (userCmd === command) {
- if (!city) {
- message.reply('No city provided');
- return;
- }
- try {
- const response = await fetch(`${breweryEndpoint}?by_city=${city}`, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const breweries = await response.json();
- const randomBrewery = randomItemFromArray(breweries);
- if (!randomBrewery) {
- message.reply('City not found');
- return;
+ if (!city) {
+ message.reply('No city provided');
+ return;
+ }
+ try {
+ const response = await fetch(`${breweryEndpoint}?by_city=${city}`, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
}
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomBrewery.name)
- .setURL(randomBrewery.website_url)
- .setDescription(`${randomBrewery.street}\n${randomBrewery.city}, ${randomBrewery.state}`)
- .setThumbnail(beerThumbnail)
- .addFields(
- { name: 'Google Maps Link', value: `https://www.google.com/maps?q=${randomBrewery.latitude},${randomBrewery.longitude}`},
- { name: 'Phone', value: randomBrewery.phone ?? 'Not listed' },
- { name: 'Brewery Type', value: randomBrewery.brewery_type ?? 'Not listed' },
- );
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
+ });
+ const breweries = await response.json();
+ const randomBrewery = randomItemFromArray(breweries);
+ if (!randomBrewery) {
+ message.reply('City not found');
+ return;
}
+ const embed = new EmbedBuilder()
+ .setColor(embedColor)
+ .setTitle(randomBrewery.name)
+ .setURL(randomBrewery.website_url)
+ .setDescription(`${randomBrewery.street}\n${randomBrewery.city}, ${randomBrewery.state}`)
+ .setThumbnail(beerThumbnail)
+ .addFields(
+ { name: 'Google Maps Link', value: `https://www.google.com/maps?q=${randomBrewery.latitude},${randomBrewery.longitude}`},
+ { name: 'Phone', value: randomBrewery.phone ?? 'Not listed' },
+ { name: 'Brewery Type', value: randomBrewery.brewery_type ?? 'Not listed' },
+ );
+ message.channel.send({ embeds: [embed] });
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/catfact.js b/commands/catfact.js
index aff752d..e530148 100644
--- a/commands/catfact.js
+++ b/commands/catfact.js
@@ -1,6 +1,4 @@
-import { Events } from 'discord.js';
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -8,29 +6,18 @@ const catFactEndpoint = 'https://catfact.ninja/fact';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const response = await fetch(catFactEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.reply(jsonResponse.fact);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ execute: async (message) => {
+ try {
+ const response = await fetch(catFactEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.reply(jsonResponse.fact);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/coffee.js b/commands/coffee.js
index 79a735d..941a2d5 100644
--- a/commands/coffee.js
+++ b/commands/coffee.js
@@ -1,41 +1,30 @@
import { default as wiki } from 'wikijs';
-import { EmbedBuilder, Events } from 'discord.js';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { EmbedBuilder } from 'discord.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
import { embedColor } from '../config/config.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- try {
- const coffeeBrands = await wiki().pagesInCategory('Category:Coffee_brands');
- const randomBrand = randomItemFromArray(coffeeBrands);
- const wikiPage = await wiki().page(randomBrand);
- const wikiPageUrl = wikiPage.fullurl;
- const brandTitle = wikiPage.title;
- const [pageImage, summary] = await Promise.all([wikiPage.pageImage(), wikiPage.summary()]);
+ execute: async (message) => {
+ try {
+ const coffeeBrands = await wiki().pagesInCategory('Category:Coffee_brands');
+ const randomBrand = randomItemFromArray(coffeeBrands);
+ const wikiPage = await wiki().page(randomBrand);
+ const wikiPageUrl = wikiPage.fullurl;
+ const brandTitle = wikiPage.title;
+ const [pageImage, summary] = await Promise.all([wikiPage.pageImage(), wikiPage.summary()]);
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(brandTitle)
- .setURL(wikiPageUrl)
- .setDescription(summary)
- .setThumbnail(pageImage);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ const embed = new EmbedBuilder()
+ .setColor(embedColor)
+ .setTitle(brandTitle)
+ .setURL(wikiPageUrl)
+ .setDescription(summary)
+ .setThumbnail(pageImage);
+ message.channel.send({ embeds: [embed] });
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/compliment.js b/commands/compliment.js
index f214f9d..e3f59d5 100644
--- a/commands/compliment.js
+++ b/commands/compliment.js
@@ -1,6 +1,4 @@
-import { Events } from 'discord.js';
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -8,39 +6,29 @@ const complimentEndpoint = 'https://complimentr.com/api';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
let userToCompliment = args[2];
- if (userCmd === command) {
- if (!userToCompliment) {
- message.reply('No user to compliment provided');
- return;
- }
+ if (!userToCompliment) {
+ message.reply('No user to compliment provided');
+ return;
+ }
- if (userToCompliment === 'me') {
- userToCompliment = message.author.toString();
- }
+ if (userToCompliment === 'me') {
+ userToCompliment = message.author.toString();
+ }
- try {
- const response = await fetch(complimentEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.channel.send(`${userToCompliment}, ${jsonResponse.compliment}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const response = await fetch(complimentEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.channel.send(`${userToCompliment}, ${jsonResponse.compliment}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/contribute.js b/commands/contribute.js
index 281196f..426852c 100644
--- a/commands/contribute.js
+++ b/commands/contribute.js
@@ -1,5 +1,4 @@
-import { Events } from 'discord.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,18 +6,7 @@ const projectURL = 'https://github.com/circa10a/ava';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd == command) {
- message.reply(projectURL);
- }
+ message.reply(projectURL);
},
};
diff --git a/commands/emoji.js b/commands/emoji.js
index 178a4b4..db22e1e 100644
--- a/commands/emoji.js
+++ b/commands/emoji.js
@@ -1,29 +1,18 @@
-import { Events } from 'discord.js';
import imageFinder from 'image-search-engine';
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const searchTerms = getAllArgsAsStr(args);
- if (userCmd === command) {
- try {
- const img = await imageFinder.find(searchTerms, { size: 'small', color: 'transparent' });
- message.reply(img);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const img = await imageFinder.find(searchTerms, { size: 'small', color: 'transparent' });
+ message.reply(img);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/floridaman.js b/commands/floridaman.js
index 8c085a2..37ed02e 100644
--- a/commands/floridaman.js
+++ b/commands/floridaman.js
@@ -1,5 +1,4 @@
-import { Events } from 'discord.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
import { getRandomSubmission } from '../lib/reddit/submissions.js';
const command = getFileName(import.meta.url);
@@ -8,29 +7,18 @@ const subreddit = 'FloridaMan';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
+ execute: async (message) => {
+ let randomSubmission;
+ try {
+ randomSubmission = await getRandomSubmission({ subreddit });
+ } catch(e) {
+ message.reply(e.toString());
return;
}
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission = {};
- try{
- randomSubmission = await getRandomSubmission({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- message.reply(`${randomSubmission.title}\n${randomSubmission.url_overridden_by_dest}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ message.reply(`${randomSubmission.title}\n${randomSubmission.url_overridden_by_dest}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
},
};
diff --git a/commands/fuck.js b/commands/fuck.js
index 6df3ad1..ab94b0b 100644
--- a/commands/fuck.js
+++ b/commands/fuck.js
@@ -1,6 +1,4 @@
-import { Events } from 'discord.js';
-import fetch from 'node-fetch';
-import { randomItemFromArray, messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getFileName } from '../lib/utils/utils.js';
import { avaPrefix } from '../config/config.js';
const foaasRootEndpoint = 'https://foaas.com';
@@ -19,58 +17,51 @@ const avaInsults = [
'yourself',
];
+// Cache filtered FOAAS operations (static list, fetched once)
+let cachedOps = null;
+
+const getFilteredOps = async () => {
+ if (cachedOps) return cachedOps;
+ const response = await fetch('https://foaas.com/operations');
+ const data = await response.json();
+ cachedOps = data.filter(item => item.fields.length === 2 && item.url.includes(':name') && !notFucks.includes(item.name));
+ return cachedOps;
+};
+
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const cmd = args[1];
+ execute: async (message, args) => {
const thingToInsult = args[2];
- if (cmd === command) {
- // Ava fights back if people diss her
- if (avaInsults.includes(thingToInsult.toLowerCase())) {
- message.reply('No, fuck you!');
- return;
- }
-
- // Someone said 'ava fuck'
- if (args.length < 3) {
- message.reply('Fuck you');
- return;
- }
-
- try {
- // Get the list of operations (filtered)
- const ops = await fetch('https://foaas.com/operations').
- then(response => response.json()).
- then(data => data.filter(item => item.fields.length === 2 && item.url.includes(':name') && !notFucks.includes(item.name)));
+ // Ava fights back if people diss her
+ if (avaInsults.includes(thingToInsult?.toLowerCase())) {
+ message.reply('No, fuck you!');
+ return;
+ }
- // Pick a random one
- const selection = randomItemFromArray(ops);
- const userArg = encodeURIComponent(args.slice(2).join(' '));
+ // Someone said 'ava fuck'
+ if (args.length < 3) {
+ message.reply('Fuck you');
+ return;
+ }
- // Do the needful
- const selectedUrl = selection.url.replace(':name', userArg).replace(':from', message.author.username);
+ try {
+ const ops = await getFilteredOps();
+ const selection = randomItemFromArray(ops);
+ const userArg = encodeURIComponent(args.slice(2).join(' '));
+ const selectedUrl = selection.url.replace(':name', userArg).replace(':from', message.author.username);
- const resp = await fetch(foaasRootEndpoint + selectedUrl, {
- method: 'get',
- headers: {
- 'Accept': 'text/plain'
- }
- });
+ const resp = await fetch(foaasRootEndpoint + selectedUrl, {
+ method: 'get',
+ headers: {
+ 'Accept': 'text/plain'
+ }
+ });
- const msg = await resp.text();
- message.channel.send(msg);
- } catch (e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ const msg = await resp.text();
+ message.channel.send(msg);
+ } catch (e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/gcp.js b/commands/gcp.js
index 54ef88d..eb1e1f8 100644
--- a/commands/gcp.js
+++ b/commands/gcp.js
@@ -1,22 +1,10 @@
-import { Events } from 'discord.js';
-import { messageForAva, splitArgs, getFileName} from '../lib/utils/utils.js';
+import { getFileName} from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- message.reply('GCP is trash and Google should be embarrassed');
- }
+ message.reply('GCP is trash and Google should be embarrassed');
},
};
diff --git a/commands/help.js b/commands/help.js
index db4059f..47c1a1f 100644
--- a/commands/help.js
+++ b/commands/help.js
@@ -1,26 +1,13 @@
-import { EmbedBuilder, Events } from 'discord.js';
-import * as fs from 'fs';
-import { commandsDir, embedColor } from '../config/config.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { EmbedBuilder } from 'discord.js';
+import { embedColor } from '../config/config.js';
-const command = getFileName(import.meta.url);
+const command = 'help';
-const eventFiles = fs.readdirSync(`./${commandsDir}`).filter(file => file.endsWith('.js'));
-const availableCommands = eventFiles.map(event => event.replace('.js', ''));
-
-export default {
- commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
+const help = (opts = {}) => {
+ return {
+ commandName: command,
+ execute(message) {
+ const { availableCommands = [] } = opts;
const embed = new EmbedBuilder()
.setColor(embedColor)
.setTitle('Available Commands')
@@ -29,6 +16,8 @@ export default {
.setThumbnail('https://i.imgur.com/XbO6CSl.jpg');
message.channel.send({ embeds: [embed] });
- }
- },
+ },
+ };
};
+
+export default help;
diff --git a/commands/hp.js b/commands/hp.js
index df570a0..09dbca4 100644
--- a/commands/hp.js
+++ b/commands/hp.js
@@ -1,45 +1,7 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'HarryPotterMemes';
-
-export default {
- commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission = {};
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'HarryPotterMemes',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/insult.js b/commands/insult.js
index fa03abc..6b32ecf 100644
--- a/commands/insult.js
+++ b/commands/insult.js
@@ -1,7 +1,5 @@
-import { Events } from 'discord.js';
import { decode } from 'html-entities';
-import fetch from 'node-fetch';
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -9,40 +7,29 @@ const insultEndpoint = 'https://evilinsult.com/generate_insult.php?lang=en&type=
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
let thingToInsult = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!thingToInsult) {
- message.reply('Nothing to insult');
- return;
- }
+ if (!thingToInsult) {
+ message.reply('Nothing to insult');
+ return;
+ }
- if (thingToInsult === 'me') {
- thingToInsult = message.author.toString();
- }
+ if (thingToInsult === 'me') {
+ thingToInsult = message.author.toString();
+ }
- try {
- const response = await fetch(insultEndpoint, {
- method: 'get',
- headers: {
- 'Accept': 'application/json',
- }
- });
- const jsonResponse = await response.json();
- message.channel.send(`${thingToInsult}, ${decode(jsonResponse.insult)}`);
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
+ try {
+ const response = await fetch(insultEndpoint, {
+ method: 'get',
+ headers: {
+ 'Accept': 'application/json',
+ }
+ });
+ const jsonResponse = await response.json();
+ message.channel.send(`${thingToInsult}, ${decode(jsonResponse.insult)}`);
+ } catch(e) {
+ message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
}
}
};
diff --git a/commands/java.js b/commands/java.js
index 2f46938..5f8e01d 100644
--- a/commands/java.js
+++ b/commands/java.js
@@ -1,5 +1,4 @@
-import { Events } from 'discord.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
+import { getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
@@ -7,18 +6,7 @@ const javaImage = 'https://i.redd.it/o4w97sa7iidz.jpg';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd == command) {
- message.reply(javaImage);
- }
+ message.reply(javaImage);
},
};
diff --git a/commands/karen.js b/commands/karen.js
index b93c827..8494a97 100644
--- a/commands/karen.js
+++ b/commands/karen.js
@@ -1,45 +1,7 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'FuckYouKaren';
-
-export default {
- commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission = {};
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try{
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'FuckYouKaren',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/meme.js b/commands/meme.js
index 89f5314..2b27491 100644
--- a/commands/meme.js
+++ b/commands/meme.js
@@ -1,45 +1,7 @@
-import { EmbedBuilder, Events } from 'discord.js';
+import { createRedditEmbedCommand } from '../lib/commands/redditEmbed.js';
+import { getFileName } from '../lib/utils/utils.js';
-import { embedColor } from '../config/config.js';
-import { getRandomSubmissionWithImage } from '../lib/reddit/submissions.js';
-import { messageForAva, splitArgs, getFileName } from '../lib/utils/utils.js';
-
-const command = getFileName(import.meta.url);
-
-const subreddit = 'dankmemes';
-
-export default {
- commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async (message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
-
- if (userCmd === command) {
- let randomSubmission = {};
- try {
- randomSubmission = await getRandomSubmissionWithImage({subreddit});
- } catch(e) {
- message.reply(e.toString());
- return;
- }
- try {
- let description = randomSubmission.selftext || subreddit;
- const embed = new EmbedBuilder()
- .setColor(embedColor)
- .setTitle(randomSubmission.title)
- .setURL(`https://reddit.com${randomSubmission.permalink}`)
- .setDescription(description)
- .setImage(randomSubmission.url_overridden_by_dest);
- message.channel.send({ embeds: [embed] });
- } catch(e) {
- message.channel.send(`\`\`\`log\n${e.toString()}\`\`\``);
- }
- }
- },
-};
+export default createRedditEmbedCommand({
+ subreddit: 'dankmemes',
+ commandName: getFileName(import.meta.url),
+});
diff --git a/commands/mock.js b/commands/mock.js
index 2700825..2e0faa4 100644
--- a/commands/mock.js
+++ b/commands/mock.js
@@ -1,35 +1,18 @@
-import { Events } from 'discord.js';
-import { messageForAva, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
-const mockText = (str) => {
- let newStr = '';
- str.split('').forEach((el, idx) => {
- newStr += idx % 2 === 0 ? el.toLowerCase() : el.toUpperCase();
- });
- return newStr;
-};
+const mockText = (str) => str.split('').map((ch, i) => i % 2 === 0 ? ch.toLowerCase() : ch.toUpperCase()).join('');
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute(message) {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute(message, args) {
const userArg = getAllArgsAsStr(args);
- if (userCmd === command) {
- if (!userArg) {
- message.reply('Nothing to mock');
- return;
- }
- message.reply(mockText(userArg));
+ if (!userArg) {
+ message.reply('Nothing to mock');
+ return;
}
+ message.reply(mockText(userArg));
},
};
diff --git a/commands/recipe.js b/commands/recipe.js
index 8d5d5f7..8c153d2 100644
--- a/commands/recipe.js
+++ b/commands/recipe.js
@@ -1,6 +1,5 @@
-import { Events } from 'discord.js';
import { parse } from 'node-html-parser';
-import { randomItemFromArray, messageForAva, getBody, splitArgs, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
+import { randomItemFromArray, getBody, getAllArgsAsStr, getFileName } from '../lib/utils/utils.js';
const command = getFileName(import.meta.url);
const seriousEatsBaseURL = 'https://www.seriouseats.com';
@@ -9,53 +8,42 @@ const itemsHTMLClass ='#schema-lifestyle_1-0';
export default {
commandName: command,
- name: Events.MessageCreate,
- once: false,
- execute: async(message) => {
- // Ensure message is intended for ava
- if (!messageForAva(message)) {
- return;
- }
-
- const args = splitArgs(message);
- const userCmd = args[1];
+ execute: async (message, args) => {
const searchTerms = getAllArgsAsStr(args);
- if (userCmd === command) {
- try {
- // If search terms specified
- if (searchTerms) {
- const body = await getBody(`${seriousEatsBaseURL}/search?q=${encodeURIComponent(searchTerms)}`);
- const root = parse(body);
- const results = root.querySelectorAll('.card');
- if (results.length >= 1 ) {
- const firstSearchResult = results[0]._attrs.href;
- message.reply(firstSearchResult);
- return;
- } else {
- message.reply('No recipes found');
- return;
- }
+ try {
+ // If search terms specified
+ if (searchTerms) {
+ const body = await getBody(`${seriousEatsBaseURL}/search?q=${encodeURIComponent(searchTerms)}`);
+ const root = parse(body);
+ const results = root.querySelectorAll('.card');
+ if (results.length >= 1 ) {
+ const firstSearchResult = results[0]._attrs.href;
+ message.reply(firstSearchResult);
+ return;
+ } else {
+ message.reply('No recipes found');
+ return;
}
+ }
- // Random (if no search terms)
- if (!searchTerms) {
- const body = await getBody(allRecipesEndpoint);
- const root = parse(body);
- // JSON in