Browse Source

Adhere to TS and Markdown Style Guidelines (#46)

* Update package.json

* Adhere to Markdown and TS Style Guidelines

* Adhere to ESLint

* Some more edits to adhere to the ESLint standard

* style: modify slightly to adhere to ESLint spec

Co-authored-by: Piyush Acharya <acharyapiyush1@outlook.com>

Co-authored-by: Abheek <abheekd@protonmail.com>
pull/48/head
Piyush Acharya 2 years ago
committed by GitHub
parent
commit
b0763f7524
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 108
      .eslintrc.json
  2. 2
      .github/CODE_OF_CONDUCT.md
  3. 16
      .github/CONTRIBUTING.md
  4. 16
      .github/workflows/codeql.yml
  5. 10
      .github/workflows/eslint.yml
  6. 23
      README.md
  7. 26
      docker-compose.yml
  8. 4
      src/commands/about.ts
  9. 3
      src/commands/help.ts
  10. 21
      src/commands/rounds.ts
  11. 7
      src/commands/score.ts
  12. 30
      src/commands/settings.ts
  13. 6
      src/commands/top.ts
  14. 33
      src/commands/train.ts
  15. 18
      src/deploy-commands.js
  16. 8
      src/helpers/db.ts
  17. 21
      src/helpers/util/pagination.ts
  18. 201
      tsconfig.json

108
.eslintrc.json

@ -10,39 +10,106 @@
"node": true, "node": true,
"es6": true "es6": true
}, },
"ignorePatterns": ["src/deploy-commands.js"], "ignorePatterns": [
"src/deploy-commands.js"
],
"rules": { "rules": {
"arrow-spacing": ["error", { "before": true, "after": true }], "arrow-spacing": [
"brace-style": ["error", "stroustrup", { "allowSingleLine": true }], "error",
"comma-dangle": ["error", "always-multiline"], {
"before": true,
"after": true
}
],
"brace-style": [
"error",
"stroustrup",
{
"allowSingleLine": true
}
],
"comma-dangle": [
"error",
"always-multiline"
],
"comma-spacing": "error", "comma-spacing": "error",
"comma-style": "error", "comma-style": "error",
"curly": ["error", "multi-line", "consistent"], "curly": [
"dot-location": ["error", "property"], "error",
"multi-line",
"consistent"
],
"dot-location": [
"error",
"property"
],
"handle-callback-err": "off", "handle-callback-err": "off",
"indent": ["error", "tab"], "indent": [
"error",
"tab"
],
"keyword-spacing": "error", "keyword-spacing": "error",
"max-nested-callbacks": ["error", { "max": 4 }], "max-nested-callbacks": [
"max-statements-per-line": ["error", { "max": 2 }], "error",
{
"max": 4
}
],
"max-statements-per-line": [
"error",
{
"max": 2
}
],
"no-console": "off", "no-console": "off",
"no-empty-function": "error", "no-empty-function": "error",
"no-floating-decimal": "error", "no-floating-decimal": "error",
"no-lonely-if": "error", "no-lonely-if": "error",
"no-multi-spaces": "error", "no-multi-spaces": "error",
"no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1, "maxBOF": 0 }], "no-multiple-empty-lines": [
"no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], "error",
"no-trailing-spaces": ["error"], {
"max": 2,
"maxEOF": 1,
"maxBOF": 0
}
],
"no-shadow": [
"error",
{
"allow": [
"err",
"resolve",
"reject"
]
}
],
"no-trailing-spaces": [
"error"
],
"no-var": "error", "no-var": "error",
"object-curly-spacing": ["error", "always"], "object-curly-spacing": [
"error",
"always"
],
"prefer-const": "error", "prefer-const": "error",
"quotes": ["error", "single"], "quotes": [
"semi": ["error", "always"], "error",
"single"
],
"semi": [
"error",
"always"
],
"space-before-blocks": "error", "space-before-blocks": "error",
"space-before-function-paren": ["error", { "space-before-function-paren": [
"anonymous": "never", "error",
"named": "never", {
"asyncArrow": "always" "anonymous": "never",
}], "named": "never",
"asyncArrow": "always"
}
],
"space-in-parens": "error", "space-in-parens": "error",
"space-infix-ops": "error", "space-infix-ops": "error",
"space-unary-ops": "error", "space-unary-ops": "error",
@ -50,4 +117,3 @@
"yoda": "error" "yoda": "error"
} }
} }

2
.github/CODE_OF_CONDUCT.md

@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban ### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community **Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals. individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within **Consequence**: A permanent ban from any sort of public interaction within

16
.github/CONTRIBUTING.md

@ -1,22 +1,32 @@
# Contributing # Contributing
## Cloning for Contribution ## Cloning for Contribution
To set up the development environment to make changes to the code, clone the repo, install dependencies, and run it directly with `node` as Docker doesn't expose as much.
To set up the development environment to make changes to the code, clone the repo, install dependencies, and run it
directly with `node` as Docker doesn't expose as much.
## Making Changes ## Making Changes
To edit the code please keep a few things in mind: To edit the code please keep a few things in mind:
1. Be able to explain changes you make; they should be legible and understandable. 1. Be able to explain changes you make; they should be legible and understandable.
2. Please try to abide by ESLint's rules, setting up ESLint is quite easy to do and not too difficult to follow. 2. Please try to abide by ESLint's rules, setting up ESLint is quite easy to do and not too difficult to follow.
## Testing Your Code ## Testing Your Code
For testing, make sure to set up the prerequisites: For testing, make sure to set up the prerequisites:
1. MongoDB: have a MongoDB server set up. 1. MongoDB: have a MongoDB server set up.
2. Environment Variables: take a look at the `docker-compose.yml` for examples. 2. Environment Variables: take a look at the `docker-compose.yml` for examples.
Once that's done, you can run `yarn tsc` or `npx tsc` to build to JavaScript in the `built` directory. Here you'll find `deploy-commands.js` which you need to deploy the slash commands, and `index.js` which you can run to start the bot. Once that's done, you can run `yarn tsc` or `npx tsc` to build to JavaScript in the `built` directory. Here you'll
find `deploy-commands.js` which you need to deploy the slash commands, and `index.js` which you can run to start the
bot.
## Submitting Your Contribution ## Submitting Your Contribution
Creating a PR is easy enough and there are plenty of tutorials to do so. When contributing, please make sure to highlight and explain your change, with any import information additionally included.
Creating a PR is easy enough and there are plenty of tutorials to do so. When contributing, please make sure to
highlight and explain your change, with any import information additionally included.
--- ---

16
.github/workflows/codeql.yml

@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v1 uses: github/codeql-action/init@v1
with: with:
languages: 'javascript' languages: 'javascript'
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1 uses: github/codeql-action/analyze@v1

10
.github/workflows/eslint.yml

@ -11,8 +11,8 @@ jobs:
lint: lint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install modules - name: Install modules
run: yarn run: yarn
- name: Run ESLint - name: Run ESLint
run: yarn run lint run: yarn run lint

23
README.md

@ -1,4 +1,3 @@
![logo](https://user-images.githubusercontent.com/67982792/160637166-b8c3a390-e4f9-46d1-8738-dcb2d8b9baa7.png) ![logo](https://user-images.githubusercontent.com/67982792/160637166-b8c3a390-e4f9-46d1-8738-dcb2d8b9baa7.png)
<h1 align="center">AwesomeSciBo</h1> <h1 align="center">AwesomeSciBo</h1>
@ -16,23 +15,35 @@
<p align="center">Open-source Discord bot that aims to aid up-and-coming Scibowlers with randomly generated (non-stock) rounds and training. It has many features, including subject-targeted training, a cross-server leaderboard, and a plethora of slash commands!</p> <p align="center">Open-source Discord bot that aims to aid up-and-coming Scibowlers with randomly generated (non-stock) rounds and training. It has many features, including subject-targeted training, a cross-server leaderboard, and a plethora of slash commands!</p>
## Deployment ## Deployment
Deploying this bot to your Discord server is relatively simple: you can add it to your own server by using [this link](https://adat.link/awesomescibo).
Deploying this bot to your Discord server is relatively simple: you can add it to your own server by
using [this link](https://adat.link/awesomescibo).
## Contributing ## Contributing
Please see [CONTRIBUTING.md](https://github.com/ADawesomeguy/AwesomeSciBo/blob/master/.github/CONTRIBUTING.md).
Please see [CONTRIBUTING.md](https://github.com/ADawesomeguy/AwesomeSciBo/blob/master/.github/CONTRIBUTING.md).
## Installation ## Installation
There are basically two ways to install it: There are basically two ways to install it:
### Method 1 (Node): ### Method 1 (Node):
After cloning the repository, dependencies can be installed with `yarn` or `npm i`. The bot can then be compile to JavaScript with `yarn tsc` or `npx tsc`, and will be deployed in the `built/` directory. Finally, the bot can be run by entering said directory and running `./index.js` or `node index.js`.
After cloning the repository, dependencies can be installed with `yarn` or `npm i`. The bot can then be compile to
JavaScript with `yarn tsc` or `npx tsc`, and will be deployed in the `built/` directory. Finally, the bot can be run by
entering said directory and running `./index.js` or `node index.js`.
### Method 2 (Docker): ### Method 2 (Docker):
This bot has a Dockerfile within the repository which can be built using `docker build . -t [tag]`. Alternatively and preferably, the image can be taken from [DockerHub](https://hub.docker.com/r/adawesomeguy/awesomescibo).
This bot has a Dockerfile within the repository which can be built using `docker build . -t [tag]`. Alternatively and
preferably, the image can be taken from [DockerHub](https://hub.docker.com/r/adawesomeguy/awesomescibo).
## Usage ## Usage
This bot uses slash commands now :). You can just click on the bot icon after typing `/` to see a list of commands. This bot uses slash commands now :). You can just click on the bot icon after typing `/` to see a list of commands.
## Credit ## Credit
The bot was made by [@ADawesomeguy](https://github.com/ADawesomeguy). However, the API was made by [@CQCumbers](https://github.com/CQCumbers). Go give [his API](https://github.com/CQCumbers/ScibowlDB) a star, he totally deserves it!
The bot was made by [@ADawesomeguy](https://github.com/ADawesomeguy). However, the API was made
by [@CQCumbers](https://github.com/CQCumbers). Go give [his API](https://github.com/CQCumbers/ScibowlDB) a star, he
totally deserves it!

26
docker-compose.yml

@ -1,17 +1,17 @@
# Use root/example as user/password credentials # Use root/example as user/password credentials
version: '3.1' version: '3.1'
services: services:
mongo: mongo:
image: mongo:4.4 image: mongo:4.4
restart: always restart: always
volumes: volumes:
- ./data:/data/db - ./data:/data/db
awesomescibo: awesomescibo:
image: docker.io/adawesomeguy/awesomescibo:latest image: docker.io/adawesomeguy/awesomescibo:latest
restart: unless-stopped restart: unless-stopped
environment: environment:
MONGO_URI: "mongodb://mongo:27017/AWESOME" MONGO_URI: "mongodb://mongo:27017/AWESOME"
TOKEN: "" TOKEN: ""
CLIENT_ID: "" CLIENT_ID: ""
TESTING_GUILD: "" TESTING_GUILD: ""

4
src/commands/about.ts

@ -11,11 +11,11 @@ export const data = new SlashCommandBuilder()
.setName('about') .setName('about')
.setDescription('Commands regarding the creation/development of the bot'); .setDescription('Commands regarding the creation/development of the bot');
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
await interaction.deferReply(); await interaction.deferReply();
const client = interaction.client; const client = interaction.client;
const embeds : MessageEmbed[] = []; const embeds: MessageEmbed[] = [];
const contributorEmbed = new MessageEmbed().setTitle('Contributors') const contributorEmbed = new MessageEmbed().setTitle('Contributors')
.addField('Creator', '<@745063586422063214> [ADawesomeguy#3602]', true) .addField('Creator', '<@745063586422063214> [ADawesomeguy#3602]', true)

3
src/commands/help.ts

@ -5,7 +5,8 @@ export const data = new SlashCommandBuilder()
.setName('help') .setName('help')
.setDescription('Replies with a help message explaining what the bot can do'); .setDescription('Replies with a help message explaining what the bot can do');
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
await interaction.deferReply();
await interaction.deferReply(); await interaction.deferReply();
const helpEmbed = new MessageEmbed() const helpEmbed = new MessageEmbed()

21
src/commands/rounds.ts

@ -28,20 +28,20 @@ export const data = new SlashCommandBuilder()
return subcommand; return subcommand;
}); });
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
const action = interaction.options.getSubcommand(); const action = interaction.options.getSubcommand();
switch (action) { switch (action) {
case 'generate': { case 'generate': {
interaction.deferReply({ ephemeral: true }); interaction.deferReply({ ephemeral: true });
let finalizedHTML = '<html><head><link rel=\'preconnect\' href=\'https://fonts.gstatic.com\'><link href=\'https://fonts.googleapis.com/css2?family=Ubuntu&display=swap\' rel=\'stylesheet\'> </head><body style=\'width: 70%; margin-left: auto; margin-right: auto;\'><h2 style=\'text-align: center; text-decoration: underline overline; padding: 7px;\'>ROUND GENERATED BY AWESOMESCIBO USING THE SCIBOWLDB API</h2>'; let finalizedHTML = '<html><head><link rel=\'preconnect\' href=\'https://fonts.gstatic.com\'><link href=\'https://fonts.googleapis.com/css2?family=Ubuntu&display=swap\' rel=\'stylesheet\'> </head><body style=\'width: 70%; margin-left: auto; margin-right: auto;\'><h2 style=\'text-align: center; text-decoration: underline overline; padding: 7px;\'>ROUND GENERATED BY AWESOMESCIBO USING THE SCIBOWLDB API</h2>';
let tossup_question : string; let tossup_question: string;
let question_category : string; let question_category: string;
let tossup_format : string; let tossup_format: string;
let tossup_answer : string; let tossup_answer: string;
let bonus_question : string; let bonus_question: string;
let bonus_format : string; let bonus_format: string;
let bonus_answer : string; let bonus_answer: string;
let htmlContent = ''; let htmlContent = '';
await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] }) await axios.post('https://scibowldb.com/api/questions', { categories: ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'] })
.then((response) => { .then((response) => {
@ -72,7 +72,10 @@ export async function execute(interaction : CommandInteraction) {
log({ logger: 'rounds', content: `Saving round to DB failed: ${err}`, level: 'error' }); log({ logger: 'rounds', content: `Saving round to DB failed: ${err}`, level: 'error' });
return; return;
} }
interaction.followUp({ content: `Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`, ephemeral: true }); interaction.followUp({
content: `Here's your round: https://api.adawesome.tech/round/${round._id.toString()}`,
ephemeral: true,
});
}); });
}); });
break; break;

7
src/commands/score.ts

@ -16,7 +16,7 @@ export const data = new SlashCommandBuilder()
return option; return option;
}); });
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
const scoreEmbed = new MessageEmbed() const scoreEmbed = new MessageEmbed()
.setColor('#ffffff'); .setColor('#ffffff');
@ -27,7 +27,10 @@ export async function execute(interaction : CommandInteraction) {
} }
if (!score) { if (!score) {
await interaction.reply({ content: 'Unfortunately, that user does not seem to have used AwesomeSciBo yet.', ephemeral: true }); await interaction.reply({
content: 'Unfortunately, that user does not seem to have used AwesomeSciBo yet.',
ephemeral: true,
});
return; return;
} }

30
src/commands/settings.ts

@ -26,7 +26,7 @@ export const data = new SlashCommandBuilder()
return subcommand; return subcommand;
}); });
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
const action = interaction.options.getSubcommand(); const action = interaction.options.getSubcommand();
switch (action) { switch (action) {
case 'display': { case 'display': {
@ -70,7 +70,11 @@ export async function execute(interaction : CommandInteraction) {
const vals = dispChoice.values; const vals = dispChoice.values;
const config = await userConfig.findById(interaction.user.id); const config = await userConfig.findById(interaction.user.id);
if (!config) { if (!config) {
await interaction.editReply({ content: 'You don\'t have a configuration!', embeds: [], components: [] }); await interaction.editReply({
content: 'You don\'t have a configuration!',
embeds: [],
components: [],
});
} }
else if (vals.length === 1 && vals.at(0) === 'subjects') { else if (vals.length === 1 && vals.at(0) === 'subjects') {
await interaction.editReply({ await interaction.editReply({
@ -138,7 +142,10 @@ export async function execute(interaction : CommandInteraction) {
.then(async lvlChoice => { .then(async lvlChoice => {
const vals = lvlChoice.values; const vals = lvlChoice.values;
const levels = new Array<string>(); const levels = new Array<string>();
await userConfig.findOneAndUpdate({ _id: interaction.user.id }, { gradeLevels: vals }, { upsert: true, new: true }); await userConfig.findOneAndUpdate({ _id: interaction.user.id }, { gradeLevels: vals }, {
upsert: true,
new: true,
});
await vals.forEach(v => { await vals.forEach(v => {
switch (v) { switch (v) {
case 'MS': case 'MS':
@ -148,7 +155,11 @@ export async function execute(interaction : CommandInteraction) {
levels.push('High School'); levels.push('High School');
} }
}); });
await interaction.editReply({ content: `Level set to: ${levels.toString().split(',').join(', ')}`, embeds: [], components: [] }); await interaction.editReply({
content: `Level set to: ${levels.toString().split(',').join(', ')}`,
embeds: [],
components: [],
});
}); });
})); }));
break; break;
@ -219,12 +230,19 @@ export async function execute(interaction : CommandInteraction) {
(subjectMsg as Message).awaitMessageComponent({ filter: subjectFilter, componentType: 'SELECT_MENU' }) (subjectMsg as Message).awaitMessageComponent({ filter: subjectFilter, componentType: 'SELECT_MENU' })
.then(async subjectChoice => { .then(async subjectChoice => {
const vals = subjectChoice.values; const vals = subjectChoice.values;
await userConfig.findOneAndUpdate({ _id: interaction.user.id }, { subjects: vals }, { upsert: true, new: true }); await userConfig.findOneAndUpdate({ _id: interaction.user.id }, { subjects: vals }, {
upsert: true,
new: true,
});
const subjects = new Array<string>(); const subjects = new Array<string>();
await vals.forEach(v => { await vals.forEach(v => {
subjects.push(v.toLowerCase().split(' ').map(w => w[0].toUpperCase() + w.substring(1)).join(' ')); subjects.push(v.toLowerCase().split(' ').map(w => w[0].toUpperCase() + w.substring(1)).join(' '));
}); });
await interaction.editReply({ content: `Subjects set to: ${subjects.toString().split(',').join(', ')}`, components: [], embeds: [] }); await interaction.editReply({
content: `Subjects set to: ${subjects.toString().split(',').join(', ')}`,
components: [],
embeds: [],
});
}); });
})); }));
break; break;

6
src/commands/top.ts

@ -8,7 +8,7 @@ export const data = new SlashCommandBuilder()
.setName('top') .setName('top')
.setDescription('Lists top ten scores across servers (server specific leaderboard WIP)'); .setDescription('Lists top ten scores across servers (server specific leaderboard WIP)');
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
await interaction.deferReply(); await interaction.deferReply();
userScore userScore
@ -21,13 +21,13 @@ export async function execute(interaction : CommandInteraction) {
} }
if (obj.length < 10) { if (obj.length < 10) {
// Need at least 10 scores for top 10 // Need at least 10 scores for top 10
return interaction.followUp( return interaction.followUp(
`There are only ${obj.length} users, we need at least 10!`, `There are only ${obj.length} users, we need at least 10!`,
); );
} }
const embeds : MessageEmbed[] = []; const embeds: MessageEmbed[] = [];
let lbMessageContent = ''; let lbMessageContent = '';
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {

33
src/commands/train.ts

@ -31,7 +31,7 @@ export const data = new SlashCommandBuilder()
return option; return option;
}); });
export async function execute(interaction : CommandInteraction) { export async function execute(interaction: CommandInteraction) {
await interaction.deferReply(); await interaction.deferReply();
const subject = interaction.options.get('subject') ? interaction.options.get('subject')?.value : null; const subject = interaction.options.get('subject') ? interaction.options.get('subject')?.value : null;
@ -44,7 +44,10 @@ export async function execute(interaction : CommandInteraction) {
if (!obj) { if (!obj) {
score = 0; score = 0;
const firstTimeEmbed = new MessageEmbed() const firstTimeEmbed = new MessageEmbed()
.setAuthor({ name: interaction.client.user?.tag ? interaction.client.user?.tag : '', iconURL: interaction.client.user?.displayAvatarURL() }) .setAuthor({
name: interaction.client.user?.tag ? interaction.client.user?.tag : '',
iconURL: interaction.client.user?.displayAvatarURL(),
})
.setDescription('Hey! It seems like it\'s your first time using AwesomeSciBo. Here\'s some information regarding the bot if you need it (for issues, contributions, etc.):') .setDescription('Hey! It seems like it\'s your first time using AwesomeSciBo. Here\'s some information regarding the bot if you need it (for issues, contributions, etc.):')
.addField('Creator', '<@745063586422063214> [@abheekd#3602]') .addField('Creator', '<@745063586422063214> [@abheekd#3602]')
.addField('GitHub', '[Link](https://github.com/ADawesomeguy/AwesomeSciBo) (a star couldn\'t hurt...)') .addField('GitHub', '[Link](https://github.com/ADawesomeguy/AwesomeSciBo) (a star couldn\'t hurt...)')
@ -60,7 +63,7 @@ export async function execute(interaction : CommandInteraction) {
} }
}); });
let categoryArray : string[] = []; let categoryArray: string[] = [];
const allCategories = ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH']; const allCategories = ['BIOLOGY', 'PHYSICS', 'CHEMISTRY', 'EARTH AND SPACE', 'ASTRONOMY', 'MATH'];
const configCategories = await userConfig.findById(interaction.user.id); const configCategories = await userConfig.findById(interaction.user.id);
@ -154,7 +157,10 @@ export async function execute(interaction : CommandInteraction) {
} }
else { else {
const overrideEmbed = new MessageEmbed() const overrideEmbed = new MessageEmbed()
.setAuthor({ name: answerMsg?.author.tag ? answerMsg.author.tag : '', iconURL: answerMsg?.author.displayAvatarURL() }) .setAuthor({
name: answerMsg?.author.tag ? answerMsg.author.tag : '',
iconURL: answerMsg?.author.displayAvatarURL(),
})
.addField('Correct answer', `\`${tossupAnswer}\``) .addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription('It seems your answer was incorrect. Please react with <:override:955265585086857236> to override your answer if you think you got it right.') .setDescription('It seems your answer was incorrect. Please react with <:override:955265585086857236> to override your answer if you think you got it right.')
.setColor('#ffffff') .setColor('#ffffff')
@ -174,7 +180,7 @@ export async function execute(interaction : CommandInteraction) {
const overrideFilter = i => { const overrideFilter = i => {
return ( return (
['override'].includes(i.customId) && ['override'].includes(i.customId) &&
i.user.id === answerMsg.author.id i.user.id === answerMsg.author.id
); );
}; };
overrideMsg overrideMsg
@ -186,8 +192,16 @@ export async function execute(interaction : CommandInteraction) {
await i.reply(msgToReply); await i.reply(msgToReply);
overrideMsg.edit({ components: [] }); overrideMsg.edit({ components: [] });
}); });
}).catch(err => log({ logger: 'train', content: `Failed to override score: ${err}`, level: 'error' })); }).catch(err => log({
}).catch(err => log({ logger: 'train', content: `Failed to send override message: ${err}`, level: 'error' })); logger: 'train',
content: `Failed to override score: ${err}`,
level: 'error',
}));
}).catch(err => log({
logger: 'train',
content: `Failed to send override message: ${err}`,
level: 'error',
}));
} }
interaction.editReply({ components: [sourceButton] }); interaction.editReply({ components: [sourceButton] });
}).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' })); }).catch(err => log({ logger: 'train', content: `${err}`, level: 'error' }));
@ -224,7 +238,10 @@ export async function execute(interaction : CommandInteraction) {
} }
else { else {
const incorrectEmbed = new MessageEmbed() const incorrectEmbed = new MessageEmbed()
.setAuthor({ name: interaction.user.tag, iconURL: interaction.user.displayAvatarURL() }) .setAuthor({
name: interaction.user.tag,
iconURL: interaction.user.displayAvatarURL(),
})
.addField('Correct answer', `\`${tossupAnswer}\``) .addField('Correct answer', `\`${tossupAnswer}\``)
.setDescription(`It seems your answer ${mcChoice.customId.toUpperCase()} was incorrect.`) .setDescription(`It seems your answer ${mcChoice.customId.toUpperCase()} was incorrect.`)
.setColor('#ffffff') .setColor('#ffffff')

18
src/deploy-commands.js

@ -1,20 +1,20 @@
#!/usr/bin/env node #!/usr/bin/env node
const fs = require('node:fs'); const fs = require('node:fs');
const { REST } = require('@discordjs/rest'); const {REST} = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9'); const {Routes} = require('discord-api-types/v9');
const { clientId, token } = require('./helpers/env'); const {clientId, token} = require('./helpers/env');
const commands = []; const commands = [];
const commandFiles = fs.readdirSync(__dirname + '/commands').filter(file => file.endsWith('.js')); const commandFiles = fs.readdirSync(__dirname + '/commands').filter(file => file.endsWith('.js'));
for (const file of commandFiles) { for (const file of commandFiles) {
const command = require(`${__dirname}/commands/${file}`); const command = require(`${__dirname}/commands/${file}`);
commands.push(command.data.toJSON()); commands.push(command.data.toJSON());
} }
const rest = new REST({ version: '9' }).setToken(token); const rest = new REST({version: '9'}).setToken(token);
rest.put(Routes.applicationCommands(clientId), { body: commands }) rest.put(Routes.applicationCommands(clientId), {body: commands})
.then(() => console.log('Successfully registered application commands.')) .then(() => console.log('Successfully registered application commands.'))
.catch(console.error); .catch(console.error);

8
src/helpers/db.ts

@ -3,7 +3,7 @@ import mongoose from 'mongoose';
import log from '../helpers/log'; import log from '../helpers/log';
import userScore from '../models/userScore'; import userScore from '../models/userScore';
export async function updateScore(isCorrect : boolean, score : number, authorId : string) { export async function updateScore(isCorrect: boolean, score: number, authorId: string) {
if (!isCorrect) { if (!isCorrect) {
return `Nice try! Your score is still ${score}.`; return `Nice try! Your score is still ${score}.`;
} }
@ -42,6 +42,10 @@ export async function connect(mongoUri) {
useNewUrlParser: true, useNewUrlParser: true,
}) })
.then(() => log({ logger: 'db', content: `Connected to the database at ${mongoUri}!`, level: 'info' })) .then(() => log({ logger: 'db', content: `Connected to the database at ${mongoUri}!`, level: 'info' }))
.catch(err => log({ logger: 'db', content: `Failed to connect to the database at ${mongoUri}: ${err}`, level: 'fatal' })); .catch(err => log({
logger: 'db',
content: `Failed to connect to the database at ${mongoUri}: ${err}`,
level: 'fatal',
}));
} }

21
src/helpers/util/pagination.ts

@ -1,6 +1,6 @@
import { CommandInteraction, Message, MessageActionRow, MessageButton, MessageEmbed } from 'discord.js'; import { CommandInteraction, Message, MessageActionRow, MessageButton, MessageEmbed } from 'discord.js';
export async function paginateMessage(message : Message, embeds : MessageEmbed[]) { export async function paginateMessage(message: Message, embeds: MessageEmbed[]) {
let index = 0; let index = 0;
const row = new MessageActionRow; const row = new MessageActionRow;
@ -19,7 +19,10 @@ export async function paginateMessage(message : Message, embeds : MessageEmbed[]
.then(async paginatorMessage => { .then(async paginatorMessage => {
const filter = m => m.author.id === message.author.id; const filter = m => m.author.id === message.author.id;
const paginatorCollector = paginatorMessage.createMessageComponentCollector({ componentType: 'BUTTON', filter: filter }); const paginatorCollector = paginatorMessage.createMessageComponentCollector({
componentType: 'BUTTON',
filter: filter,
});
paginatorCollector.on('collect', async i => { paginatorCollector.on('collect', async i => {
switch (i.customId) { switch (i.customId) {
@ -37,7 +40,7 @@ export async function paginateMessage(message : Message, embeds : MessageEmbed[]
}); });
} }
export async function paginateInteraction(interaction : CommandInteraction, embeds : MessageEmbed[]) { export async function paginateInteraction(interaction: CommandInteraction, embeds: MessageEmbed[]) {
let index = 0; let index = 0;
const row = new MessageActionRow; const row = new MessageActionRow;
@ -52,12 +55,20 @@ export async function paginateInteraction(interaction : CommandInteraction, embe
.setStyle('SECONDARY') .setStyle('SECONDARY')
); );
await interaction.followUp({ content: `Page 1 of ${embeds.length}:`, embeds: [embeds[index]], components: [row], fetchReply: true }) await interaction.followUp({
content: `Page 1 of ${embeds.length}:`,
embeds: [embeds[index]],
components: [row],
fetchReply: true,
})
.then(async p => { .then(async p => {
const paginatorMessage = p as Message; const paginatorMessage = p as Message;
const filter = i => i.user.id === interaction.user.id; const filter = i => i.user.id === interaction.user.id;
const paginatorCollector = paginatorMessage.createMessageComponentCollector({ componentType: 'BUTTON', filter: filter }); const paginatorCollector = paginatorMessage.createMessageComponentCollector({
componentType: 'BUTTON',
filter: filter,
});
paginatorCollector.on('collect', async i => { paginatorCollector.on('collect', async i => {
switch (i.customId) { switch (i.customId) {

201
tsconfig.json

@ -1,101 +1,104 @@
{ {
"compilerOptions": { "compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */ /* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Projects */
/* Projects */ "incremental": true,
"incremental": true, /* Enable incremental compilation */ /* Enable incremental compilation */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
/* Language and Environment */ "target": "es2016",
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */ // "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
/* Modules */
/* Modules */ "module": "commonjs",
"module": "commonjs", /* Specify what module code is generated. */ /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */ // "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ "moduleResolution": "node",
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ /* Specify how TypeScript looks up a file from a given module specifier. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "resolveJsonModule": true, /* Enable importing .json files */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */ // "resolveJsonModule": true, /* Enable importing .json files */
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */ /* JavaScript Support */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ "allowJs": true,
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
/* Emit */ /* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
"outDir": "./built", /* Specify an output folder for all emitted files. */ "outDir": "./built",
// "removeComments": true, /* Disable emitting comments. */ /* Specify an output folder for all emitted files. */
// "noEmit": true, /* Disable emitting files from a compilation. */ // "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "noEmit": true, /* Disable emitting files from a compilation. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "newLine": "crlf", /* Set the newline character for emitting files. */ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ // "newLine": "crlf", /* Set the newline character for emitting files. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */ // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */ /* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ "esModuleInterop": true,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true,
/* Type Checking */ /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */ /* Type Checking */
"noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ "strict": true,
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ /* Enable all strict type-checking options. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ "noImplicitAny": false,
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ /* Enable error reporting for expressions and declarations with an implied `any` type.. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ // "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ // "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */ // "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ // "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
/* Completeness */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */ /* Completeness */
} // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true
/* Skip type checking all .d.ts files. */
}
} }
Loading…
Cancel
Save