Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
85ddbdd
chore: update .gitignore
travisfriesen Jul 5, 2025
18c7aed
feat: containerize the bot
travisfriesen Jul 5, 2025
c6f7bd9
feat: sql message logging (message delete is currently disabled)
travisfriesen Jul 5, 2025
aecb298
chore: update some colours, add icm email
travisfriesen Jul 5, 2025
52bc439
feat: auto initalize new databases for new servers
travisfriesen Jul 5, 2025
c72133b
auto setup databases
travisfriesen Jul 5, 2025
3e56254
feat: alumni verification button
travisfriesen Aug 26, 2025
82e7436
fix: db errors
travisfriesen Aug 26, 2025
16ffa21
chore: update error messages
travisfriesen Aug 26, 2025
f3bf18c
feat: command to unverify all students
travisfriesen Aug 26, 2025
f0ef376
feat: added nickname to verification messages
travisfriesen Aug 26, 2025
2887806
fix: disabled instructor verification
travisfriesen Aug 26, 2025
e0b8486
fix: only logs when its in a guild
travisfriesen Sep 15, 2025
80aca25
fix: updated docker files
travisfriesen Sep 15, 2025
b360f08
fix: update port number
travisfriesen Sep 15, 2025
a4259b3
fix: update lockb
travisfriesen Sep 15, 2025
0bd145c
feat: remove button upon verify alumni
travisfriesen Oct 23, 2025
42ce1d9
feat: move name function before send verification code
travisfriesen Oct 23, 2025
cda055d
fix: updated verification prompt
travisfriesen Jan 5, 2026
c0c2577
fix: moved hardcoded credentials to variables
travisfriesen Jan 5, 2026
876f9b8
fix: update start script
travisfriesen Jan 5, 2026
e5c238a
TECH-126 Create a command for leaderboard with pages
cjlangan Mar 16, 2026
d11c6b1
Merge pull request #2 from umanitoba-cssa/TECH-126-Create-a-command-f…
cjlangan Mar 16, 2026
4425b1c
Revert "TECH-126 Create a command for leaderboard with pages"
travisfriesen Apr 8, 2026
c284a05
Merge pull request #4 from umanitoba-cssa/revert-2-TECH-126-Create-a-…
travisfriesen Apr 8, 2026
0e93a44
fix: improper variables in docker-compose file
travisfriesen Apr 8, 2026
7da26a8
fix: Spelling/variable errors
travisfriesen Apr 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copy this file to .env and fill in your values

# Discord Configuration
DISCORD_CLIENT_ID="your_discord_client_id"
DISCORD_GUILD_ID="your_discord_guild_id"
DISCORD_TOKEN="your_discord_bot_token"

# PocketBase Configuration
POCKETBASE_EMAIL="admin@example.com"
POCKETBASE_PASSWORD="your_secure_password"

# MySQL Configuration
MYSQL_USER=test
MYSQL_PASSWORD=pass
MYSQL_ROOT_PASSWORD=test_pass

# Note: Database connection details are automatically configured via Docker containers
# The following variables are set automatically in docker-compose.yml:
# - POCKETBASE_HOST=http://moderation-db:8080
# - MYSQL_HOST=message-db
# - MYSQL_PORT=3306
#
# Access the pocketbase admin panel at: http://localhost:8080/_/ (or your server ip) to setup your admin account
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
.env
pocketbase/*
.env

# Database data directories
pocketbase-db/data/
message-db/data/
.DS_Store
32 changes: 32 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM alpine:latest

ARG PB_VERSION=0.20.3

RUN apk add --no-cache \
unzip \
ca-certificates

Comment on lines +1 to +8
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This root-level Dockerfile appears to be a duplicate of pocketbase-db/Dockerfile, but docker-compose.yml builds PocketBase from ./pocketbase-db/. Keeping an unused duplicate Dockerfile is confusing and increases maintenance overhead. Consider deleting it or clearly documenting what it’s for.

Copilot uses AI. Check for mistakes.
# download and unzip PocketBase
ADD https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_amd64.zip /tmp/pb.zip
RUN unzip /tmp/pb.zip -d /pb/

# create data directory for databases
RUN mkdir -p /pb/pb_data

# copy startup script
COPY start.sh /pb/start.sh
RUN chmod +x /pb/start.sh

# uncomment to copy the local pb_migrations dir into the image
# COPY ./pb_migrations /pb/pb_migrations

# uncomment to copy the local pb_hooks dir into the image
# COPY ./pb_hooks /pb/pb_hooks

EXPOSE 8080

# create volume for persistent data storage
VOLUME ["/pb/pb_data"]

# start PocketBase with auto-admin setup
CMD ["/pb/start.sh"]
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,84 @@ Honkbot2 is the new discord bot for the UManitoba Computer Science Lounge writte

Honkbot2 contains a wide variety of moderation and verification functionalities.
To learn more read the [Moderation Handbook](https://umanitobacssa.ca/docs/discordModHandbook.pdf)

## Quick Start

1. **Clone the repository**
```bash
git clone <repository-url>
Comment on lines +8 to +12
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description doesn’t mention the containerization / MySQL message-logging changes (docker-compose, database init, message logging events), but the diff includes significant infra changes beyond the listed verification features. Please update the PR description to reflect these additional changes for reviewers and release notes.

Copilot uses AI. Check for mistakes.
cd honkbot2
```

2. **Set up environment variables**
```bash
cp .env.example .env
# Edit .env with your Discord bot credentials
```

3. **Start the containers**
```bash
docker compose up -d
```
*See [Development Commands](#development-commands) for more options for log viewing and rebuilding containers*

4. **Set up the pocketbase admin account**

Go to http://localhost:8080/_/ (or the ip of your remote server) and set up the account with the same credentials as your .env

The bot will automatically build and start all docker containers, all database networking connections are configured through the created docker network, and it will also auto create the required databases for the first message sent in a discord server.

## Environment Variables

The following variables need to be set in your `.env` file:

- `DISCORD_CLIENT_ID` - Your Discord application client ID
- `DISCORD_GUILD_ID` - Your Discord server ID
- `DISCORD_TOKEN` - Your Discord bot token
- `POCKETBASE_EMAIL` - Email for PocketBase admin
- `POCKETBASE_PASSWORD` - Password for PocketBase admin

## Database Configuration

The bot uses two databases that are automatically configured:

### MySQL (Message Database)
- **Host**: `message-db` (container name)
- **Port**: `3306`
- **User**: `test`
- **Password**: `pass`

### PocketBase (Moderation Database)
- **Host**: `moderation-db:8080` (container name)
- **Admin Panel**: http://localhost:8080/_/

## Development Commands

```bash
# Start all services
docker-compose up -d

# View bot logs
docker-compose logs honkbot

# View PocketBase logs
docker-compose logs pocketbase-db

# View MySQL logs
docker-compose logs mysql

# Stop all services
docker-compose down

# Rebuild and restart
docker-compose up --build -d
```

## Database Schema

The MySQL database automatically creates the following tables per Discord server:

- `messages` - Stores all user messages
- `message_deleted` - Stores deleted messages for moderation
- `message_edited` - Stores message edit history
- `counters` - Stores user statistics (reactions, message counts, etc.)
18 changes: 18 additions & 0 deletions bot/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Use the official Bun image
# see all versions at https://hub.docker.com/r/oven/bun/tags
FROM oven/bun:1
WORKDIR /usr/src/app

# Copy package files and install dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Copy all project files
COPY . .

# Set environment to production
ENV NODE_ENV=production

# Run the app
USER bun
ENTRYPOINT [ "bun", "run", "index.ts" ]
Binary file modified bot/bun.lockb
Binary file not shown.
5 changes: 4 additions & 1 deletion bot/data/Events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ export module Events {
VerifyStudent = 'btn-verify-student',
VerifyAlumni = 'btn-verify-alumni',
VerifyInstructor = 'btn-verify-instructor',
ForceVerifyPending = 'btn-force-verify-pending',
SetPreferredName = 'set-preferred-name',
};

export enum Modal {
VerifyStudentModal = 'modal-verify-student',
VerifyAlumniModal = 'modal-verify-alumni',
VerifyInstructorModal = 'modal-verify-instructor'
VerifyInstructorModal = 'modal-verify-instructor',
PreferredNameModal = 'modal-preferred-name'
};

}
20 changes: 19 additions & 1 deletion bot/data/Registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ export function RegisterButtonHandler(id: Events.Button, handler: (interaction:
}

export function GetButtonHandler(id: string): ((interaction: ButtonInteraction) => void) | undefined {
return _buttonHandlers.get(id);
// Exact match
if (_buttonHandlers.has(id)) return _buttonHandlers.get(id);
// Prefix match for parameterized IDs
for (const key of _buttonHandlers.keys()) {
if (id.startsWith(key)) return _buttonHandlers.get(key);
}
return undefined;
}

export async function LoadAllModules() {
Expand All @@ -54,4 +60,16 @@ export async function LoadAllModules() {
await module.hb_init();
}
}

// Also load template modules that might have handlers
const templatesModulePath = path.join(import.meta.dir, '../templates');
const templatesModuleFiles = fs.readdirSync(templatesModulePath, {recursive: true, encoding: 'utf-8'}).filter(file => file.endsWith('.ts'));
for (const file of templatesModuleFiles) {
const filePath = path.join(templatesModulePath, file);
const module = await import(filePath);
if (module.hb_init) {
// console.log(`Loading template module ${file}`);
await module.hb_init();
}
}
}
18 changes: 16 additions & 2 deletions bot/database/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ export async function GetPendingVerification(id: string): Promise<PendingVerific
.collection("pending_verifications")
.getOne(id);
} catch (error) {
console.warn(`Failed to fetch from pending_verifications: ${error}`);
console.warn(`WARN: Failed to fetch from pending_verifications: ${error}`);
try {
pendingVerification = await pb
.collection("pending_alumni_verifications")
.getOne(id);
} catch (alumniError) {
console.error(`Failed to fetch from pending_alumni_verifications: ${alumniError}`);
console.error(`WARN: Failed to fetch from pending_alumni_verifications: ${alumniError}`);
}
}

Expand Down Expand Up @@ -353,4 +353,18 @@ export async function AddModMail(guild_id: string, target_user_id: string, issue
incoming
}
return await pb.collection("mod_mail").create(data);
}

export async function GetAllVerifiedStudents(guild_id: string): Promise<VerifiedUser[]> {
const pb = await getPb();
const verifiedStudents: VerifiedUser[] = await pb
.collection("verified_users")
.getFullList({ filter: `guild_id = "${guild_id}" && type = "student"` });

return verifiedStudents;
}

export async function RemoveVerifiedUser(id: string): Promise<void> {
const pb = await getPb();
await pb.collection("verified_users").delete(id);
}
Loading