Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite of CSSC-bot in Typescript using Discord.js and WOKCommands (Part 1-5) #1

Merged
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
**/__pycache__
**/__pycache__
.vscode
.env
node_modules
unused-commands
d.py/
144 changes: 24 additions & 120 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,35 +2,20 @@

## Table of Contents

1. [Prerequisites](#prerequisites)
2. [Setting up the bot](#setting-up-the-bot)
3. [Project structure](#project-structure)
4. [Features](#features)
5. [Resources](#resources)
1. [WARNING](#warning)
2. [Prerequisites](#prerequisites)
3. [Setting up the bot](#setting-up-the-bot)
4. [Project structure](#project-structure)
5. [Features](#features)
6. [Resources](#resources)

## Prerequisites
## warning

**Python 3.8 or later is required to run this bot.** To check your python version, open a terminal and run:
```bash
$ python --version
```
You can also try:
```bash
$ python2 --version
```
and
```bash
$ python3 --version
```
Out of those three, use the interpreter of a version that is equal or higher than the one mentioned above. If you do not have the correct version, [you can download it here.](https://www.python.org/downloads/release/python-3105/)
This branch is **NOT** ready for production use. Do not use it. No support is provided at this time.

**Once that's done, [download Discord.py here](https://discordpy.readthedocs.io/en/stable/intro.html).** Make sure you get the most recent **stable** version, which is v.1.7.4. The available versions can be viewed by clicking a dropdown in the lower right corner of the webpage.
## Prerequisites

Next, clone the project to a folder on your machine:
```bash
git clone https://github.com/llisaeva/CSSC-Bot.git
```
If you don't have git, you can either download the project as a zip file **(Code > Download ZIP)**, or [download git](https://git-scm.com/downloads).
TBD - This branch is a complete rewrite of the existing codebase so no prerequisites are provided yet.

**You will need a server to test the bot on. Create your own 'private' Discord server.**

@@ -60,120 +45,39 @@ On your new bots page, select **Reset Token**. Once the new token appears, copy

![Step #6](/demo/6.jpg)

In the project folder, open the **tkn.py** file. Paste the token between the quotes next to the TOKEN variable.

```python
TOKEN="<paste your token here>"
```
In the project folder, create a file called **.env**.
Fill it out using the template in **.env.template**.

**Why you should keep your bot token a secret**:
a bot token is used to attach a script to your bot. If someone has your bot token, they can control your bot with any script they attach to it. If you want your own remote branch in this project, **please do not commit changes to this file.**

The **MAKER** variable in **tkn.py** is used to associate the bot with an author - you can replace the ID with your own (but do not commit the change). This bot has a command that will DM your ID, check the [Features](#features) section.

After that is done, generate a URL for your bot. Select **OAuth2** from the left menu, and click **URL Generator**. Choose the **bot** checkbox, and select the **Administrator** checkbox in the section that appears. Copy the URL at the bottom of the page.

![Step #7](/demo/7.jpg)

Follow this link in a browser, and choose the private server you created to add your bot to. Go to the project folder, and run

```py
python rolebot.py
```
(replace *python* with the interpreter that has the correct version from the first step)

The bot should become responsive. You can check for errors in **logs/bot.txt**.


## Project Structure

All python files are located in the **modules** folder, the only exception is **rolebot.py** - it is the interface of the bot.

**modules/logger.py** - code for logging errors for this project. There are 3 log files: **logs/bot.txt**, **logs/discord.txt**, and **logs/server.txt**. bot.txt is used for errors caused by the bot, discord.txt is used for errors that are sent from your Discord server, and server.txt is used for errors logged by the machine the bot is running on. Nothing in the project links to the last file, it's just a place that I use to dump errors from the physical server.

**modules/persistance.py** - used for creating json files that need to be persisted. Currently, it keeps track of the IDs of the messages that the bot creates. This makes those messages accessable by the bot after it reboots. The message ID JSON file is stored at **json/msg_ids.json**.

**modules/role_poll.py** - module for the role reaction polls. There are two, one for selecting a college year, and another for choosing an occupation at UWM. The role reaction poll options are represented by objects that extend `ReactionPoll`. `ReactionPoll` is a custom base class, its purpose is to keep all reaction polls in the same format. The existing classes that extend it are `CollegeYearPoll` and `CollegeStaffPoll`. You can add a new reaction poll by creating a key, extending the `ReactionPoll` class, and editing the `polls` variable, like so:

```python

MY_NEW_POLL_KEY = "new_poll_key"

class MyNewPoll(ReactionPoll):
pass

polls = {

...,

MY_NEW_POLL_KEY:
{
TITLE: "New Poll",
DESCRIPTION: "This is your description:",
OPTIONS:
[
MyNewPoll("#1", 0xaaa, "🙂", "yay"),
MyNewPoll("#2", 0xccc, "😐", "meh"),
MyNewPoll("#3", 0xeee, "🙁", "boo"),
]
}
}

```bash
npm install
npm install -g typescript ts-node
ts-node index.ts
```

This will assign the roles `#1`, `#2` or `#3` to the user that selects the corresponding reaction. Their colors will be `0xaaa`, `0xccc` and `0xeee`. If those roles do not exist on the server, they will be created (when checking for an existing role, only the name is matched - color is used during role creation by the bot). The last parameter needs to be unique among all polls, it is used to assign a role to a user with a command.
Alternately a Docker image will be provided soon for more reliable operation.

The base `ReactionPoll` class has one function `assign()` that can be overridden. It assigns the role that it represents to the argument **member** from the argument **server** (Guild). The `CollegeYearPoll` overrides this to make sure that the member only has one college year.
The bot should become responsive. You can check for errors in the console.

**modules/utils.py** - this file is for utility functions. Currently, it has one, it is used to find the argument `roleName` in the argument `guild` (server). If it is not found, then a role named by the argument `roleName` of the argument `roleColor` color will be created in the argument `guild`. The new or retrieved role is returned.

**tkn.py** - contains the bot token and the authors ID.

**txt/cmds.txt** - the content that is displayed when the user wants to find out what commands can be used with the bot. The **txt** folder should contain all of the project's text files that are used for displaying content.

**img** - contains project images.

**demo** - contains content for the README.md file.
## Project Structure

**json** - folder that contains persisted JSON files. **json/msg_ids.json** has the IDs of all of the messages that this bot posts, this makes those messages accessible by the bot after it reboots. **modules/persistance.py** should be used to create the JSON files.
TBD - This branch is a complete rewrite of the existing codebase so no structure is provided yet.

## Features
The commands work on Discord servers and in private messages sent to the bot. However, it cannot assign roles inside of DMs. To assign roles, the command for role assignment must be sent in a Discord server where the roles can be created (this is possible to change).

**$cmds** - list the commands that can be used with this bot

**$poll collegeYear** - post a college year reaction poll. All active polls made by this bot work, you can use this to summon a poll anywhere for newcomers.

![College Year Poll](demo/college-year-poll.png)

**$poll collegeStaff** - post a college occupation reaction poll.

![College Staff Poll](demo/college-staff-poll.png)

The bot only resonds to the event when a reaction **is added**. Currently, nothing happens when a reaction is removed.

**$assign role** - assign yourself a role, where **role** is: *prefrosh*, *freshman*, *sophomore*, *junior*, *senior*, *gradstudent*, *alumni*, *professor*, *tutor*, *ta*, *sileader*

**$assign clear** - this will only clear the roles mentioned above, **not** any special roles, like *leet gamer* or *club mod*

**$dm id** - make the bot DM your ID. This can be assigned to
the `MAKER` variable in **tkn.py**, which will make all of the
maker commands accessible only by you. This command is not a
maker command.

### **Maker commands**
These can only be used by an account with an ID that matches `MAKER` in **tkn.py**. You can
change the `MAKER` ID to your own ID.

**$dm botlogs** - DMs logs/bot.txt

**$dm discordlogs** - DMs logs/discord.txt

**$dm serverlogs** - DMs logs/server.txt

**$die** - shuts down the bot
TBD - This branch is a complete rewrite of the existing codebase so no feature explanations are provided yet.

## Resources

- [Discord.py Tutorial](https://discordpy.readthedocs.io/en/stable/ext/commands/commands.html)
- [Discord.py API](https://discordpy.readthedocs.io/en/stable/api.html)
- [Discord.js Docs](https://discord.js.org/#/docs)
- [WOKcommands Docs](https://docs.wornoffkeys.com/)
- [AntaresBot Codebase](https://playantares.com/antaresbot)
9 changes: 9 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
**TODO**
- Add validation that roles exist in the server
- Add code to create roles that do not exist in the server if required
- Add error checking anytime roles are accessed
- Add error checking where necessary
- Add a few other misc features
- Remove lots of unoptimized and duplicate code. Refactor functions that can be refactored.
- When WOKCommands [updates to Discord.js v14](https://github.com/AlexzanderFlores/WOKCommands/issues/228), update WOKcommands
- Add logging for most functions
57 changes: 57 additions & 0 deletions commands/owner/staffPoll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { MessageEmbed, MessageActionRow, MessageSelectMenu } from "discord.js";
import { ICommand } from "wokcommands";

export default {
name: "staffPoll",
category: "owner",
description: "Posts the College Staff Poll",
slash: true,
testOnly: false,
guildOnly: true,
requiredPermissions: ["SEND_MESSAGES"],
ownerOnly: true,

callback: async ({ client, interaction: msgInt }) => {
// Define embeds used in this command
const infoEmbed = new MessageEmbed()
.setTitle("Choose a role")
.setColor("#0099ff")
.setDescription("Select the option with your current occupation at UWM.")
.setFooter({
text: `Delivered in: ${client.ws.ping}ms | CSSC-Bot | ${process.env.VERSION}`,
iconURL: "https://playantares.com/resources/CSSC-bot/icon.jpg",
});

// Create row one of the buttons for the poll
const row = new MessageActionRow().addComponents(
new MessageSelectMenu()
.setCustomId("collegeStaffPoll")
.setPlaceholder("Select an option")
.addOptions(
{
label: "Tutor",
value: "tutor",
},
{
label: "SI Leader",
value: "sileader",
},
{
label: "TA",
value: "ta",
},
{
label: "Professor",
value: "professor",
},
{
label: "Student Employee",
value: "studentemployee",
}
)
);

// Send the embed and message component rows
msgInt.reply({ embeds: [infoEmbed], components: [row] });
},
} as ICommand;
66 changes: 66 additions & 0 deletions commands/owner/yearPoll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { MessageEmbed, MessageActionRow, MessageSelectMenu } from "discord.js";
import { ICommand } from "wokcommands";

export default {
name: "yearPoll",
category: "owner",
description: "Posts the College Year Poll",
slash: true,
testOnly: false,
guildOnly: true,
requiredPermissions: ["SEND_MESSAGES"],
ownerOnly: true,

callback: async ({ client, interaction: msgInt }) => {
// Define embeds used in this command
const infoEmbed = new MessageEmbed()
.setTitle("Choose a role")
.setColor("#0099ff")
.setDescription("Select the button with your current college year.")
.setFooter({
text: `Delivered in: ${client.ws.ping}ms | CSSC-Bot | ${process.env.VERSION}`,
iconURL: "https://playantares.com/resources/CSSC-bot/icon.jpg",
});


// Create row one of the buttons for the poll
const row = new MessageActionRow().addComponents(
new MessageSelectMenu()
.setCustomId("collegeYearPoll")
.setPlaceholder("Select an option.")
.addOptions(
{
label: "Prefrosh",
value: "prefrosh",
},
{
label: "Freshman",
value: "freshman",
},
{
label: "Sophomore",
value: "sophomore",
},
{
label: "Junior",
value: "junior",
},
{
label: "Senior",
value: "senior",
},
{
label: "Graduate Student",
value: "graduatestudent",
},
{
label: "Alumni",
value: "alumni",
}
)
);

// Send the embed and message component rows
msgInt.reply({ embeds: [infoEmbed], components: [row] });
},
} as ICommand;
24 changes: 24 additions & 0 deletions commands/user/clearRoles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { GuildMember } from "discord.js";
import { ICommand } from "wokcommands";
import { returnRoles as roleDictionary } from "../../definitions";
import { removePrevRole } from "../../rolesOps";

export default {
name: "clear",
category: "user",
description: "removes all roles the bot has assigned to a user",
slash: true,
testOnly: false,
guildOnly: true,
requiredPermissions: ["SEND_MESSAGES"],

callback: async ({ interaction }) => {
// Remove roles from user
if (!interaction.member) return;
const member = interaction.member as GuildMember;
removePrevRole(member, 0);
removePrevRole(member, 1);
// Reply to the user
interaction.reply({ content: "Cleared all roles that I have assigned to you.", ephemeral: true });
},
} as ICommand;
32 changes: 32 additions & 0 deletions commands/user/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { MessageEmbed, TextChannel } from "discord.js";
import { ICommand } from "wokcommands";

export default {
name: "github",
category: "user",
description: "Sends an embed with a link to the github repo for the bot.",
slash: true,
testOnly: false,
guildOnly: true,
requiredPermissions: ["SEND_MESSAGES"],

callback: async ({ client, interaction }) => {
// Embed values
const color = "#0099ff";
const thumbnail = "https://playantares.com/resources/CSSC-bot/cssc-server-icon.png";
const title = "Github";
const description = "Click here to go to the CSSC-bot repo: \n https://github.com/llisaeva/CSSC-Bot";
const footer = `Delivered in: ${client.ws.ping}ms | CSSC-bot | ${process.env.VERSION}`;
const footerIcon = "https://playantares.com/resources/CSSC-bot/icon.jpg";

// Embed construction
const Embed = new MessageEmbed()
.setColor(color)
.setTitle(title)
.setThumbnail(thumbnail)
.setDescription(description)
.setFooter({ text: footer, iconURL: footerIcon });

interaction.reply({ embeds: [Embed] });
},
} as ICommand;
Loading