Note: This project is still in active development. The name "Eventy" is a remnant from its original purpose as an event management tool - it has since evolved into a verification and server management bot. The name will likely change in the future.
This bot was developed as a volunteer project to help migrate an organization from Facebook to Discord. Facebook's notification system didn't meet the organization's needs, so I built this bot to handle verification, role-based access control, and seasonal member management.
A Discord bot built in Rust that provides automated user verification through OAuth2, role management, and channel access control. The bot supports seasonal member management and ensures only verified users can access member-only channels.
- OAuth2-based user verification via web interface
- Season-based member and channel management
- Configurable roles and permissions through JSON files
- TLS/HTTPS support for secure verification
- Automatic role assignment and channel permission management
git clone https://github.com/fjodborg/eventy
cd eventyCreate a .env file in the project root:
# Discord Bot Configuration
DISCORD_TOKEN="your_discord_bot_token_here"
DISCORD_CLIENT_ID="your_client_id_here"
DISCORD_CLIENT_SECRET="your_client_secret_here"
DISCORD_GUILD_ID="your_guild_id_here"
# Web Server Configuration
WEB_BASE_URL="https://your-domain.com"
TLS_CERT_PATH=certs/cert.pem
TLS_KEY_PATH=certs/key.pem
HTTPS_PORT=443
HTTP_PORT=80
# Admin API (required for /admin/api/* endpoints)
ADMIN_API_TOKEN="set-a-long-random-token"- Go to Discord Developer Portal
- Create a new application
- Navigate to "Bot" section and copy the bot token
- Navigate to "OAuth2" section and copy the Client ID and Client Secret
- Add your redirect URL under OAuth2 > Redirects (e.g.,
https://your-domain.com/callback) - Enable the following Privileged Gateway Intents:
- Server Members Intent
- Message Content Intent
Place your TLS certificates in the certs/ directory:
mkdir -p certs
# Add your cert.pem and key.pem filesFor development/testing, you can use a tunnel service like Cloudflare:
podman run --rm -it --network host cloudflare/cloudflared:latest tunnel --url http://localhost:3000Invite your bot with the following permissions:
Manage RolesManage NicknamesManage ChannelsView ChannelsSend MessagesRead Message HistoryUse Slash Commands
The bot uses a JSON-based configuration system:
data/
├── global/
│ ├── roles.json # Role definitions (colors, permissions)
│ ├── permissions.json # Permission presets (read, readwrite, admin, etc.)
│ └── assignments.json # User role assignments
└── seasons/
├── template/ # Template for new seasons
│ ├── season.json # Season configuration
│ └── users.json # Member list
└── 2025E/ # Example season
├── season.json
└── users.json
{
"roles": [
{
"name": "Medlem2025E",
"color": "#2ecc71",
"hoist": false,
"mentionable": true,
"is_default_member_role": true
}
]
}{
"definitions": {
"read": {
"allow": ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY"],
"deny": ["SEND_MESSAGES"]
},
"readwrite": {
"allow": ["VIEW_CHANNEL", "READ_MESSAGE_HISTORY", "SEND_MESSAGES", "ATTACH_FILES", "ADD_REACTIONS"],
"deny": []
}
}
}{
"name": "Spring 2025",
"active": true,
"member_role": "Medlem2025E",
"channels": [
{
"name": "general",
"type": "text",
"position": 0,
"role_permissions": {
"Medlem2025E": "readwrite"
}
}
]
}[
{
"Name": "John Doe",
"DiscordId": "unique-user-id-123"
}
]cargo runcargo run --release- User receives a verification link:
https://your-domain.com/verify/<user-id> - User clicks the link and authenticates with Discord OAuth2
- Bot verifies the user ID against the database
- Upon successful verification:
- User receives the appropriate season member role
- Nickname is updated to match the database
- Channel permissions are applied automatically
# Test bot connectivity
/ping
# Manually trigger verification
/verify
# List all users (requires Administrator)
/list_usersEndpoint:
POST /admin/api/v1/initialize_users
Authorization: Bearer <ADMIN_API_TOKEN>
Content-Type: application/json
Request body:
{
"season_name": "2026 Foraar",
"role_name": "Medlem2026F",
"users": [
{ "Name": "Alice Example", "DiscordId": "a1b2c3d4-1111-2222-3333-444455556666" },
{ "Name": "Bob Example", "DiscordId": "b2c3d4e5-7777-8888-9999-000011112222" }
]
}Local testing (HTTPS on localhost with non-matching cert):
curl -sS -k -X POST "https://localhost:3000/admin/api/v1/initialize_users" \
-H "Authorization: Bearer ${ADMIN_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"season_name": "2026 Foraar",
"role_name": "Medlem2026F",
"users": [
{ "Name": "Alice Example", "DiscordId": "a1b2c3d4-1111-2222-3333-444455556666" },
{ "Name": "Bob Example", "DiscordId": "b2c3d4e5-7777-8888-9999-000011112222" }
]
}'Production usage:
curl -sS -X POST "${WEB_BASE_URL}/admin/api/v1/initialize_users" \
-H "Authorization: Bearer ${ADMIN_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"season_name": "2026 Foraar",
"role_name": "Medlem2026F",
"users": [
{ "Name": "Alice Example", "DiscordId": "a1b2c3d4-1111-2222-3333-444455556666" },
{ "Name": "Bob Example", "DiscordId": "b2c3d4e5-7777-8888-9999-000011112222" }
]
}'Notes:
season_nameis used as the folder name underdata/seasons/.usersmust useNameandDiscordIdkeys exactly.- Existing seasons are upserted (users.json is overwritten).
- New seasons are created from the currently active season template and then synced.
- Common error responses:
multiple_active_seasonsunauthorized
- Verify
DISCORD_TOKENis correct - Check bot has required permissions in server
- Ensure Server Members and Message Content intents are enabled
- Verify
DISCORD_CLIENT_IDandDISCORD_CLIENT_SECRETare correct - Check
WEB_BASE_URLmatches your redirect URI in Discord Developer Portal - Ensure TLS certificates are valid and accessible
- Verify the user ID exists in the season's
users.json
- Bot role must be higher than target user roles in the role hierarchy
- Ensure bot has
Manage RolesandManage Nicknamespermissions - Bot needs
Manage Channelsfor channel permission overwrites
The bot uses structured logging with levels: ERROR, WARN, INFO, DEBUG
2025-01-15T10:30:45.123Z INFO eventy: Bot logged in as: VerificationBot
2025-01-15T10:30:45.124Z INFO eventy: Successfully loaded 150 users from database
2025-01-15T10:31:12.456Z INFO eventy: OAuth verification completed for user: john_doe