Archive all completed REQs — bridge clear for Bitch Bot task
This commit is contained in:
152
docs/code-bridge/archive/REQ-2026-04-12-discord-rules-fork.md
Normal file
152
docs/code-bridge/archive/REQ-2026-04-12-discord-rules-fork.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Code Request — Task #69: Discord Rules Mod Generic Fork
|
||||
|
||||
**Filed by:** Chronicler #84
|
||||
**Date:** 2026-04-12
|
||||
**Priority:** Medium
|
||||
**Task DB ID:** 69
|
||||
|
||||
---
|
||||
|
||||
## What You're Building
|
||||
|
||||
Fork the Firefrost Rules Mod into a generic, community-ready mod called **"Discord Rules"** for CurseForge publication. Strip all Firefrost branding and make colors configurable.
|
||||
|
||||
The source is in `services/rules-mod/` (3 versions: 1.21.1, 1.20.1, 1.16.5).
|
||||
Put the generic fork in `services/discord-rules/` (same 3-version structure).
|
||||
|
||||
---
|
||||
|
||||
## Changes Required Per Version
|
||||
|
||||
### 1. Package rename
|
||||
- `com.firefrostgaming.rules` → `com.discordrules`
|
||||
|
||||
### 2. Mod metadata (mods.toml / neoforge.mods.toml)
|
||||
- modId: `serverrules` → `discordrules`
|
||||
- displayName: `"Firefrost Rules"` → `"Discord Rules"`
|
||||
- description: generic (see below)
|
||||
- authors: `"Firefrost Gaming"` → `"FirefrostGaming"` (keep as author, it's our brand)
|
||||
- license: add `MIT`
|
||||
- logoFile: remove or replace with generic
|
||||
|
||||
### 3. DiscordFormatter.java — replace hardcoded color logic
|
||||
|
||||
Current (remove this entire block):
|
||||
```java
|
||||
ChatFormatting headerColor = ChatFormatting.DARK_PURPLE;
|
||||
ChatFormatting bodyColor = ChatFormatting.LIGHT_PURPLE;
|
||||
if (lowerText.contains("fire") || lowerText.contains("[fire]")) {
|
||||
headerColor = ChatFormatting.GOLD;
|
||||
bodyColor = ChatFormatting.YELLOW;
|
||||
} else if (lowerText.contains("frost") || lowerText.contains("[frost]")) {
|
||||
headerColor = ChatFormatting.AQUA;
|
||||
bodyColor = ChatFormatting.DARK_AQUA;
|
||||
}
|
||||
```
|
||||
|
||||
Replace with: read `headerColor` and `bodyColor` from config (see step 4).
|
||||
|
||||
Also in `convertEmojis()`: remove the Fire/Frost/Arcane specific replacements:
|
||||
```java
|
||||
// REMOVE these 3 lines:
|
||||
.replace("\uD83D\uDD25", "[Fire]")
|
||||
.replace("\u2744\uFE0F", "[Frost]")
|
||||
.replace("\uD83D\uDC9C", "[Arcane]")
|
||||
```
|
||||
Keep the generic emoji strip: `.replaceAll("[\\x{1F300}-\\x{1F9FF}]", "")`
|
||||
But make the emoji stripping a config toggle (see step 4).
|
||||
|
||||
### 4. ServerRulesConfig.java — add display section
|
||||
|
||||
Add a new `display` config section with these fields:
|
||||
|
||||
```
|
||||
[display]
|
||||
# Header color (bold lines). Valid values: BLACK, DARK_BLUE, DARK_GREEN, DARK_AQUA,
|
||||
# DARK_RED, DARK_PURPLE, GOLD, GRAY, DARK_GRAY, BLUE, GREEN, AQUA, RED,
|
||||
# LIGHT_PURPLE, YELLOW, WHITE
|
||||
header_color = "GOLD"
|
||||
|
||||
# Body color (regular lines and bullet points)
|
||||
body_color = "YELLOW"
|
||||
|
||||
# Strip emojis that Minecraft can't render (recommended: true)
|
||||
strip_emojis = true
|
||||
```
|
||||
|
||||
The formatter reads these at display time (not cached — config changes take effect on next /rules without restart).
|
||||
|
||||
### 5. DiscordFormatter.java — wire config reads
|
||||
|
||||
```java
|
||||
// At top of formatRules():
|
||||
ChatFormatting headerColor = parseColor(ServerRulesConfig.HEADER_COLOR.get(), ChatFormatting.GOLD);
|
||||
ChatFormatting bodyColor = parseColor(ServerRulesConfig.BODY_COLOR.get(), ChatFormatting.YELLOW);
|
||||
|
||||
// Add helper method:
|
||||
private static ChatFormatting parseColor(String name, ChatFormatting fallback) {
|
||||
try {
|
||||
return ChatFormatting.valueOf(name.toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For emoji stripping, gate `convertEmojis()` on the config value:
|
||||
```java
|
||||
String processedText = ServerRulesConfig.STRIP_EMOJIS.get()
|
||||
? convertEmojis(rawDiscordText)
|
||||
: rawDiscordText;
|
||||
```
|
||||
|
||||
### 6. ServerRules.java — update mod ID references
|
||||
- Any string `"serverrules"` → `"discordrules"`
|
||||
- Any string `"Firefrost"` or `"FireFrost"` in user-visible text → remove or make generic
|
||||
|
||||
---
|
||||
|
||||
## Build Instructions
|
||||
|
||||
Same as Task #136 — use the Dev Panel build environment:
|
||||
|
||||
```bash
|
||||
# Java 21 for 1.21.1
|
||||
use-java 21
|
||||
cd /opt/mod-builds/firefrost-services/services/discord-rules/1.21.1
|
||||
/opt/gradle-8.8/bin/gradle build
|
||||
|
||||
# Java 17 for 1.20.1
|
||||
use-java 17
|
||||
cd /opt/mod-builds/firefrost-services/services/discord-rules/1.20.1
|
||||
/opt/gradle-8.8/bin/gradle build
|
||||
|
||||
# Java 8 for 1.16.5
|
||||
use-java 8
|
||||
cd /opt/mod-builds/firefrost-services/services/discord-rules/1.16.5
|
||||
/opt/gradle-7.6.4/bin/gradle build
|
||||
```
|
||||
|
||||
Output jars go in `build/libs/` — grab the one without `-sources` or `-dev`.
|
||||
|
||||
---
|
||||
|
||||
## Deliverable
|
||||
|
||||
3 built jars in `services/discord-rules/`:
|
||||
- `discord-rules-1.0.0-1.21.1-neoforge.jar`
|
||||
- `discord-rules-1.0.0-1.20.1-forge.jar`
|
||||
- `discord-rules-1.0.0-1.16.5-forge.jar`
|
||||
|
||||
Commit source + jars to `firefrost-services` main branch, then update `ACTIVE_CONTEXT.md`.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Don't touch `services/rules-mod/` — that stays as Firefrost's internal version
|
||||
- Default colors GOLD/YELLOW chosen to match Firefrost's visual identity (subtle brand nod)
|
||||
- MIT license — maximizes community adoption
|
||||
- Chronicler is writing the CurseForge project page copy in parallel
|
||||
|
||||
**Fire + Frost + Foundation** 💙🔥❄️
|
||||
@@ -0,0 +1,19 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Phase 11 Prerequisites — Need credentials and env values before Code can begin
|
||||
|
||||
## 1. Current State
|
||||
Phase 11 spec is archived and understood. Code is ready to begin building the licensing system (database migration, Arbiter API routes, Blueprint extension changes).
|
||||
|
||||
## 2. The Blocker
|
||||
Cannot start implementation without infrastructure values that only the Chronicler (via Command Center access) can provide.
|
||||
|
||||
## 3. Questions for Chronicler
|
||||
|
||||
1. **PostgreSQL credentials** — What are the connection details for arbiter_db on Command Center? (host, port, user, password — or confirm it's 127.0.0.1 with the existing Arbiter DB user)
|
||||
2. **Existing Arbiter DB schema** — Can you dump the current table list from arbiter_db so I know what's already there? (`\dt` output)
|
||||
3. **BuiltByBit resource IDs** — Have the two listings (Standard / Professional) been created yet? If so, what are BBB_STANDARD_RESOURCE_ID and BBB_PRO_RESOURCE_ID? If not, should Code proceed with placeholder values?
|
||||
4. **Arbiter .env current state** — What env vars does Arbiter already have? (So I don't overwrite anything when adding the new ones)
|
||||
5. **Discord role + channel setup** — Has the "ModpackChecker Customer" role been created on the Firefrost Discord? Does the bot have permission to assign roles?
|
||||
6. **Deployment readiness** — Once Code writes the migration SQL and API routes, should I commit and push for Chronicler to deploy? Or is there a staging step first?
|
||||
39
docs/code-bridge/archive/REQ-2026-04-12-phase11a-deploy.md
Normal file
39
docs/code-bridge/archive/REQ-2026-04-12-phase11a-deploy.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Deploy Phase 11A — MVC licensing migration + Arbiter routes
|
||||
|
||||
## 1. Current State
|
||||
Phase 11A code is committed and pushed to `main` (fd50009). Migration SQL and API routes are ready.
|
||||
|
||||
## 2. The Blocker
|
||||
Code cannot deploy to Command Center — Chronicler handles Arbiter deployments via Trinity Core.
|
||||
|
||||
## 3. Deployment Steps for Chronicler
|
||||
|
||||
1. Run migration on Command Center:
|
||||
```
|
||||
psql -U arbiter -d arbiter_db -f services/arbiter-3.0/migrations/138_mvc_licensing.sql
|
||||
```
|
||||
|
||||
2. Deploy Arbiter code (standard pattern):
|
||||
- Clone to /tmp
|
||||
- Copy to /opt/arbiter-3.0
|
||||
- Restart Arbiter service
|
||||
- Verify health endpoint
|
||||
|
||||
3. Add these env vars to Arbiter .env (placeholders until BBB listings go live):
|
||||
```
|
||||
BBB_STANDARD_RESOURCE_ID=PLACEHOLDER_STANDARD
|
||||
BBB_PRO_RESOURCE_ID=PLACEHOLDER_PRO
|
||||
BBB_WEBHOOK_SECRET=PLACEHOLDER_SECRET
|
||||
```
|
||||
|
||||
4. Verify new endpoints respond:
|
||||
```
|
||||
curl https://discord-bot.firefrostgaming.com/api/mvc/latest-version
|
||||
```
|
||||
Expected: `{"version":"1.0.0","download_url":"..."}`
|
||||
|
||||
## 4. After Deployment
|
||||
Once confirmed, Code will proceed to Phase 11B/C (Discord /verify-mvc command + customer role).
|
||||
35
docs/code-bridge/archive/REQ-2026-04-12-phase11bc-deploy.md
Normal file
35
docs/code-bridge/archive/REQ-2026-04-12-phase11bc-deploy.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Deploy Phase 11B/C — /verify-mvc command + Discord role
|
||||
|
||||
## 1. Current State
|
||||
Phase 11B/C code pushed (7c58cea). New `/verify-mvc` slash command ready. Needs Arbiter restart + Discord role creation.
|
||||
|
||||
## 2. The Blocker
|
||||
Code cannot create Discord roles or restart Arbiter on Command Center.
|
||||
|
||||
## 3. Deployment Steps for Chronicler
|
||||
|
||||
1. **Create Discord role** — "ModpackChecker Customer" on Firefrost Gaming guild
|
||||
- Color: whatever fits the brand (suggestion: green or teal)
|
||||
- No special permissions needed — it's just a tag/gating role
|
||||
- Note the role ID
|
||||
|
||||
2. **Update Arbiter .env** on Command Center:
|
||||
```
|
||||
MVC_CUSTOMER_ROLE_ID=<the role ID from step 1>
|
||||
```
|
||||
|
||||
3. **Deploy Arbiter code** (standard rsync pattern):
|
||||
- New file: `src/discord/verifymvc.js`
|
||||
- Modified: `src/discord/events.js`, `src/index.js`
|
||||
- Restart service
|
||||
|
||||
4. **Verify:**
|
||||
- Health check: `curl https://discord-bot.firefrostgaming.com/health`
|
||||
- Check bot logs for "Successfully reloaded application (/) commands"
|
||||
- Test `/verify-mvc` appears in Discord command list
|
||||
|
||||
## 4. After Deployment
|
||||
Code proceeds to Phase 11D — Blueprint extension license activation UI, phone-home cron, and tier gating.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Debug dashboard badge injection — need ServerRow.tsx contents
|
||||
|
||||
## 1. Current State
|
||||
UpdateBadge.tsx is copied correctly, but the sed injection into ServerRow.tsx may not be matching. Need to see what's actually on disk.
|
||||
|
||||
## 2. Questions — Please Run These on Dev Panel
|
||||
|
||||
```bash
|
||||
# 1. Does ServerRow.tsx exist?
|
||||
find /var/www/pterodactyl/resources/scripts -name "ServerRow.tsx" 2>/dev/null
|
||||
|
||||
# 2. Is UpdateBadge import present?
|
||||
grep -n "UpdateBadge" /var/www/pterodactyl/resources/scripts/components/dashboard/ServerRow.tsx 2>/dev/null
|
||||
|
||||
# 3. What does the server name line look like?
|
||||
grep -n "server.name" /var/www/pterodactyl/resources/scripts/components/dashboard/ServerRow.tsx 2>/dev/null
|
||||
|
||||
# 4. Was yarn build run after the injection?
|
||||
ls -la /var/www/pterodactyl/public/assets/manifest.json 2>/dev/null
|
||||
|
||||
# 5. Any JS errors visible?
|
||||
# Michael: open browser console (F12) on dashboard, look for red errors
|
||||
```
|
||||
|
||||
The sed pattern `{server.name}</p>` has to match exactly. If the actual JSX is different (e.g. `{server.name}</span>` or uses a different variable), the injection silently fails.
|
||||
|
||||
Also: **did `yarn build:production` run after build.sh?** The React components need to be compiled for the frontend to pick them up.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Dashboard badges blocked by Blueprint CSS module build errors — decision needed
|
||||
|
||||
## 1. Current State
|
||||
Badge injection into ServerRow.tsx is correct. But `yarn build:production` fails on 7 pre-existing CSS module errors from Blueprint beta — not caused by our code.
|
||||
|
||||
## 2. The Blocker
|
||||
Blueprint beta-2026-01's webpack config doesn't have a CSS module loader configured. This breaks `yarn build:production` for any panel with Blueprint installed. Our badge code can't compile.
|
||||
|
||||
## 3. Decision for Michael
|
||||
|
||||
**Option A: Skip badges for v1.0.0 launch** (recommended)
|
||||
- Admin page + console widget both work
|
||||
- Badges are nice-to-have, not core functionality
|
||||
- Add badges in v1.1.0 when Blueprint fixes their build or we find a workaround
|
||||
- Zero risk to launch timeline
|
||||
|
||||
**Option B: Patch webpack config**
|
||||
- Add `css-loader` with modules support to Blueprint's webpack
|
||||
- Risk: could break other Blueprint components, and gets overwritten on Blueprint updates
|
||||
- Not recommended 52 hours before launch
|
||||
|
||||
Which way do you want to go?
|
||||
64
docs/code-bridge/archive/REQ-2026-04-12-phase11d-deploy.md
Normal file
64
docs/code-bridge/archive/REQ-2026-04-12-phase11d-deploy.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Deploy Phase 11D to Dev Panel — Blueprint license integration
|
||||
|
||||
## 1. Current State
|
||||
Phase 11D code pushed (8872f67). Blueprint extension has license activation UI, phone-home cron, and tier gating. Ready for testing on Dev Panel.
|
||||
|
||||
## 2. Deploy Target
|
||||
**Dev Panel (64.50.188.128)** — NOT live Panel VPS.
|
||||
|
||||
## 3. Files Changed
|
||||
|
||||
**New files:**
|
||||
- `blueprint-extension/app/Services/LicenseService.php`
|
||||
- `blueprint-extension/app/Console/Commands/ValidateLicense.php`
|
||||
|
||||
**Modified files:**
|
||||
- `blueprint-extension/admin/controller.php` — added LicenseService DI, license activation/deactivation handling
|
||||
- `blueprint-extension/admin/view.blade.php` — license UI, status banners, dynamic tier gating
|
||||
|
||||
## 4. Dependencies
|
||||
- No new Composer packages — uses Laravel's built-in `Http` facade (already available)
|
||||
- No database migrations needed (license state stored in Blueprint's dbGet/dbSet)
|
||||
|
||||
## 5. Deployment Steps
|
||||
|
||||
1. Rebuild the .blueprint package:
|
||||
```bash
|
||||
cd /opt/mod-builds/firefrost-services/services/modpack-version-checker/blueprint-extension
|
||||
bash build.sh
|
||||
```
|
||||
|
||||
2. Install on Dev Panel:
|
||||
```bash
|
||||
blueprint -install modpackchecker
|
||||
```
|
||||
|
||||
3. Register the daily cron (add to Panel's crontab):
|
||||
```
|
||||
0 4 * * * cd /var/www/pterodactyl && php artisan mvc:validate >> /dev/null 2>&1
|
||||
```
|
||||
|
||||
4. For immediate testing, run manually:
|
||||
```bash
|
||||
cd /var/www/pterodactyl && php artisan mvc:validate
|
||||
```
|
||||
|
||||
## 6. What "Working" Looks Like
|
||||
|
||||
1. **Admin page loads** — visit `/admin/extensions/modpackchecker`, no errors
|
||||
2. **License section visible** — "BuiltByBit Order ID" text input + gray "Not Activated" badge
|
||||
3. **Activate test** — enter a test order ID, click Save. Should show error "License not found" (expected — no real license exists yet). Confirms Arbiter connectivity.
|
||||
4. **Pro fields locked** — Check Interval dropdown and Discord Webhook URL should be disabled (grayed out)
|
||||
5. **Artisan command runs** — `php artisan mvc:validate` outputs "No license configured" (expected for fresh install)
|
||||
6. **No PHP errors in logs** — `tail -f /var/www/pterodactyl/storage/logs/laravel.log`
|
||||
|
||||
## 7. Smoke Test with Real License
|
||||
To fully test the flow, Chronicler can provision a test license directly in the DB:
|
||||
```sql
|
||||
INSERT INTO mvc_licenses (order_id, buyer_id, tier, max_activations)
|
||||
VALUES ('TEST-001', 'chronicler', 'professional', 5);
|
||||
```
|
||||
Then enter `TEST-001` in the admin UI — should activate, show green badge, unlock pro fields.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-12
|
||||
**Topic:** Phase 11E scope — what does the GitBook knowledge base need?
|
||||
|
||||
## 1. Current State
|
||||
Phases 11A–11D complete. Phase 11E is "GitBook knowledge base migration" but the spec doesn't detail what content is needed or where GitBook is hosted.
|
||||
|
||||
## 2. Questions for Chronicler
|
||||
|
||||
1. **Is there an existing GitBook instance?** If so, what's the URL and how does Code push content to it? (API? Git repo? Manual paste?)
|
||||
2. **Or is this Markdown docs that get uploaded to GitBook later?** If so, Code can write the docs as Markdown files in the repo and Chronicler handles the GitBook publishing.
|
||||
3. **What pages are needed?** Suggested structure:
|
||||
- Getting Started (installation, activation)
|
||||
- Configuration (CurseForge API key, egg variables, file detection)
|
||||
- Dashboard Badge (how it works, what the colors mean)
|
||||
- Console Widget (manual checks, rate limits)
|
||||
- Professional Tier Features (auto-cron, Discord webhooks, FTB)
|
||||
- Troubleshooting (common errors, support contact)
|
||||
- License & Activation (how to activate, deactivation, grace period)
|
||||
4. **Should Code write these docs now, or skip to 11F (BuiltByBit listing) which is more launch-critical?**
|
||||
|
||||
54 hours to launch — happy to prioritize whatever moves the needle most.
|
||||
323
docs/code-bridge/archive/REQ-2026-04-13-arbiter-discord-roles.md
Normal file
323
docs/code-bridge/archive/REQ-2026-04-13-arbiter-discord-roles.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# Feature Request: Arbiter Native Discord Role Management
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Topic:** Replace Carlbot with native Arbiter button roles, welcome messages, and automated get-roles message lifecycle
|
||||
**Priority:** POST-LAUNCH — build and test in parallel with Carlbot, cut over week of April 20
|
||||
**Filed by:** Chronicler #86
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
Carlbot currently handles three things for Firefrost Gaming:
|
||||
1. Welcome messages on member join
|
||||
2. Reaction roles in #get-roles
|
||||
3. Wanderer role assignment on join
|
||||
|
||||
Every time a Minecraft server is created (/createserver) or deleted (/delserver), staff must manually update Carlbot's reaction role config and add/remove emoji from the #get-roles message. This is error-prone and completely automatable.
|
||||
|
||||
**Goal:** Arbiter owns all three functions natively. When Pterodactyl fires a server lifecycle webhook, the #get-roles message updates automatically — no manual steps.
|
||||
|
||||
**Architecture review:** Two rounds of Gemini consultation completed.
|
||||
- docs/consultations/gemini-arbiter-discord-roles-round-1-2026-04-13.md (in ops manual)
|
||||
- docs/consultations/gemini-arbiter-discord-roles-round-2-2026-04-13.md (in ops manual)
|
||||
|
||||
---
|
||||
|
||||
## 1. New File: src/discord/client.js
|
||||
|
||||
Module-level singleton for the discord.js Client. Shared across the entire app.
|
||||
|
||||
```javascript
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
]
|
||||
});
|
||||
|
||||
client.once('ready', () => {
|
||||
console.log(`[Discord] Gateway connected as ${client.user.tag}`);
|
||||
});
|
||||
|
||||
module.exports = client;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Modify: src/index.js (startup)
|
||||
|
||||
Start Express and Discord concurrently — do NOT block Express on Discord ready.
|
||||
|
||||
```javascript
|
||||
const client = require('./discord/client');
|
||||
const discordEvents = require('./discord/events');
|
||||
|
||||
discordEvents.register(client);
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`[Arbiter] Express listening on port ${PORT}`);
|
||||
});
|
||||
|
||||
client.login(process.env.DISCORD_BOT_TOKEN).catch(err => {
|
||||
console.error('[Discord] Gateway login failed:', err);
|
||||
});
|
||||
```
|
||||
|
||||
If a Pterodactyl webhook fires before Gateway is ready, check `client.isReady()` and return 503 if not.
|
||||
|
||||
---
|
||||
|
||||
## 3. New File: src/discord/events.js
|
||||
|
||||
```javascript
|
||||
const { sendWelcomeMessage } = require('./welcome');
|
||||
const { handleInteraction } = require('./interactions');
|
||||
|
||||
function register(client) {
|
||||
client.on('guildMemberAdd', async (member) => {
|
||||
if (process.env.WELCOME_MESSAGES_ENABLED !== 'true') return;
|
||||
await sendWelcomeMessage(member);
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
await handleInteraction(interaction);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { register };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. New File: src/discord/welcome.js
|
||||
|
||||
```javascript
|
||||
const WELCOME_CHANNEL_ID = process.env.DISCORD_WELCOME_CHANNEL_ID;
|
||||
|
||||
async function sendWelcomeMessage(member) {
|
||||
try {
|
||||
const channel = await member.guild.channels.fetch(WELCOME_CHANNEL_ID);
|
||||
if (!channel) return;
|
||||
await channel.send({
|
||||
embeds: [{
|
||||
title: `Welcome to Firefrost Gaming, ${member.user.username}! 🔥❄️`,
|
||||
description: `Head to <#${process.env.DISCORD_GET_ROLES_CHANNEL_ID}> to grab your server roles!`,
|
||||
color: 0xFF6B35,
|
||||
thumbnail: { url: member.user.displayAvatarURL() },
|
||||
timestamp: new Date().toISOString(),
|
||||
}]
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[Welcome] Failed to send welcome message:', err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { sendWelcomeMessage };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. New File: src/discord/getRolesMessage.js
|
||||
|
||||
Manages the persistent #get-roles button message. Called by webhook.js on /createserver and /delserver.
|
||||
|
||||
```javascript
|
||||
const client = require('./client');
|
||||
const db = require('../db');
|
||||
|
||||
const GET_ROLES_CHANNEL_ID = process.env.DISCORD_GET_ROLES_CHANNEL_ID;
|
||||
|
||||
async function updateGetRolesMessage(servers) {
|
||||
if (!client.isReady()) {
|
||||
console.warn('[GetRoles] Discord client not ready — skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
const channel = await client.channels.fetch(GET_ROLES_CHANNEL_ID);
|
||||
const embed = buildEmbed(servers);
|
||||
const components = buildButtons(servers);
|
||||
const storedMessageId = await db.getSetting('discord_get_roles_message_id');
|
||||
|
||||
if (storedMessageId) {
|
||||
try {
|
||||
await channel.messages.edit(storedMessageId, { embeds: [embed], components });
|
||||
return;
|
||||
} catch (err) {
|
||||
if (err.code !== 10008) throw err;
|
||||
console.warn('[GetRoles] Stored message not found, reposting...');
|
||||
}
|
||||
}
|
||||
|
||||
const message = await channel.send({ embeds: [embed], components });
|
||||
await db.setSetting('discord_get_roles_message_id', message.id);
|
||||
}
|
||||
|
||||
function buildEmbed(servers) {
|
||||
return {
|
||||
title: '🎮 Choose Your Servers',
|
||||
description: servers.length > 0
|
||||
? 'Click a button to get access to a server channel. Click again to remove it.'
|
||||
: 'No servers are currently active.',
|
||||
color: 0x4ECDC4,
|
||||
footer: { text: 'Firefrost Gaming — Role Assignment' },
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
function buildButtons(servers) {
|
||||
if (servers.length === 0) return [];
|
||||
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
|
||||
const rows = [];
|
||||
let currentRow = new ActionRowBuilder();
|
||||
let count = 0;
|
||||
|
||||
for (const server of servers) {
|
||||
if (count > 0 && count % 5 === 0) {
|
||||
rows.push(currentRow);
|
||||
currentRow = new ActionRowBuilder();
|
||||
}
|
||||
currentRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(`toggle_role_${server.roleId}`)
|
||||
.setLabel(server.name)
|
||||
.setStyle(ButtonStyle.Secondary)
|
||||
.setEmoji(server.emoji || '🎮')
|
||||
);
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count % 5 !== 0 || count === 0) rows.push(currentRow);
|
||||
return rows;
|
||||
}
|
||||
|
||||
module.exports = { updateGetRolesMessage };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. New File: src/discord/interactions.js
|
||||
|
||||
```javascript
|
||||
async function handleInteraction(interaction) {
|
||||
if (!interaction.isButton()) return;
|
||||
if (interaction.customId.startsWith('toggle_role_')) {
|
||||
await handleRoleToggle(interaction);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRoleToggle(interaction) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
const roleId = interaction.customId.replace('toggle_role_', '');
|
||||
const member = interaction.member;
|
||||
const guild = interaction.guild;
|
||||
|
||||
try {
|
||||
const role = await guild.roles.fetch(roleId);
|
||||
if (!role) {
|
||||
await interaction.editReply({ content: '⚠️ Role not found. Please contact an admin.' });
|
||||
return;
|
||||
}
|
||||
const hasRole = member.roles.cache.has(roleId);
|
||||
if (hasRole) {
|
||||
await member.roles.remove(role);
|
||||
await interaction.editReply({ content: `✅ Removed the **${role.name}** role.` });
|
||||
} else {
|
||||
await member.roles.add(role);
|
||||
await interaction.editReply({ content: `✅ You now have the **${role.name}** role!` });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[Interactions] Role toggle failed:', err);
|
||||
await interaction.editReply({ content: '⚠️ Something went wrong. Please try again.' });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { handleInteraction };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Modify: src/routes/webhook.js
|
||||
|
||||
After successful server creation or deletion, call updateGetRolesMessage with the current active server list.
|
||||
|
||||
---
|
||||
|
||||
## 8. Database: Settings Table
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key VARCHAR(255) PRIMARY KEY,
|
||||
value TEXT
|
||||
);
|
||||
```
|
||||
|
||||
Add getSetting/setSetting helpers to DB module.
|
||||
|
||||
---
|
||||
|
||||
## 9. New .env Variables
|
||||
|
||||
```
|
||||
DISCORD_GET_ROLES_CHANNEL_ID=1403980899464384572
|
||||
DISCORD_WELCOME_CHANNEL_ID=1403980049530490911
|
||||
WELCOME_MESSAGES_ENABLED=false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Server Role ID Reference
|
||||
|
||||
| Server Name | Role ID |
|
||||
|-------------|---------|
|
||||
| All The Mods: To the Sky | 1491028496284258304 |
|
||||
| Stoneblock 4 | 1491028769132253274 |
|
||||
| Society: Sunlit Valley | 1491028885981102270 |
|
||||
| All The Mons | 1491029000108380170 |
|
||||
| Mythcraft 5 | 1491029070190870548 |
|
||||
| Beyond Depth | 1491029215963906149 |
|
||||
| Beyond Ascension | 1491029284159094904 |
|
||||
| Wold's Vaults | 1491029373640376330 |
|
||||
| Otherworld [Dungeons & Dragons] | 1491029454011629749 |
|
||||
| DeceasedCraft | 1491029615739801800 |
|
||||
| Submerged 2 | 1491029708878647356 |
|
||||
| Sneak's Pirate Pack | 1491029809273508112 |
|
||||
| Cottage Witch | 1491029870002569298 |
|
||||
| Homestead | 1491030015746510939 |
|
||||
| Farm Crossing 6 | 1493352900997415134 |
|
||||
|
||||
---
|
||||
|
||||
## 11. Carlbot Cutover Sequence (Week of April 20)
|
||||
|
||||
DO NOT execute before April 15 launch.
|
||||
|
||||
1. Confirm Arbiter role system verified in test channel
|
||||
2. Deploy final build to production
|
||||
3. Disable Carlbot Reaction Roles for #get-roles in Carlbot dashboard
|
||||
4. Delete old Carlbot #get-roles message
|
||||
5. Trigger updateGetRolesMessage() to post new button message
|
||||
6. Disable Carlbot Welcome module
|
||||
7. Set WELCOME_MESSAGES_ENABLED=true in .env
|
||||
8. Restart arbiter-3
|
||||
9. Verify welcome fires on test join
|
||||
10. Remove Carlbot from server
|
||||
|
||||
---
|
||||
|
||||
## Rate Limit Handling
|
||||
|
||||
Native retry on 429. No queue needed at current scale.
|
||||
|
||||
---
|
||||
|
||||
## Notes for Code
|
||||
|
||||
- Feature flag (WELCOME_MESSAGES_ENABLED) is critical — don't skip it
|
||||
- 404 fallback in updateGetRolesMessage is important — admins will delete that message
|
||||
- Keep Discord client strictly in src/discord/
|
||||
- testserver role (1491487727928217815) exists — do NOT include in button list
|
||||
- This is post-launch work — no rush, but nice change from MVC 😄
|
||||
@@ -0,0 +1,45 @@
|
||||
# REQ-2026-04-13-missing-modpack-installations-migration
|
||||
|
||||
**Filed by:** Chronicler #85
|
||||
**Date:** 2026-04-13
|
||||
**Priority:** HIGH — blocks Dev Panel deploy, will block any fresh install
|
||||
**Status:** OPEN
|
||||
|
||||
## Problem
|
||||
|
||||
The `modpack_installations` table exists on live panel (panel-vps) but has **no migration file** in the repo. It was created directly on the server at some point and never committed.
|
||||
|
||||
This caused an API error on Dev Panel during today's deploy — the table simply didn't exist. Chronicler manually created it via raw SQL as a temporary fix to unblock testing.
|
||||
|
||||
## Live Panel Schema (confirmed via Trinity Core)
|
||||
|
||||
```
|
||||
Field | Type | Null | Key | Default | Extra
|
||||
provider | varchar(191) | NO | | NULL |
|
||||
modpack_id | varchar(191) | NO | | NULL |
|
||||
server_id | bigint(20) unsigned | NO | PRI | NULL |
|
||||
finalized | tinyint(1) | NO | | 0 |
|
||||
```
|
||||
|
||||
PRIMARY KEY on `server_id`. Engine InnoDB, charset utf8mb4.
|
||||
|
||||
## What Code Needs to Do
|
||||
|
||||
Create a proper Laravel migration file:
|
||||
|
||||
```
|
||||
services/modpack-version-checker/blueprint-extension/database/migrations/2026_04_13_000001_create_modpack_installations_table.php
|
||||
```
|
||||
|
||||
Using the schema above. Standard Blueprint/Laravel migration format matching the existing migration files in that directory.
|
||||
|
||||
## Why It Matters
|
||||
|
||||
Without this, any fresh Blueprint install (new panel, wipe and reinstall) will throw:
|
||||
`SQLSTATE[42S02]: Table 'panel.modpack_installations' doesn't exist`
|
||||
|
||||
This will affect every BuiltByBit customer who purchases ModpackChecker.
|
||||
|
||||
## After Code Pushes
|
||||
|
||||
Chronicler will pull on Dev Panel and confirm the migration file is present before live panel deploy.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Bug Fix + Feature Request: Rules Mod Config Reset + Versioning
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Topic:** Fix Forge config reset on startup, introduce version control
|
||||
**Priority:** HIGH — rules mod broken on Otherworld
|
||||
**Filed by:** Chronicler #86
|
||||
**Status:** RESOLVED in v1.0.2
|
||||
@@ -0,0 +1,74 @@
|
||||
# Bug Follow-up v2: COMMON Config File Not Read at Runtime
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Topic:** firefrostrules-common.toml has correct values but mod still serves default rules
|
||||
**Priority:** HIGH — Otherworld still broken
|
||||
**Filed by:** Chronicler #86
|
||||
**Related to:** REQ-2026-04-13-rules-mod-config-still-resetting.md
|
||||
|
||||
---
|
||||
|
||||
## What We Did
|
||||
|
||||
1. Deployed firefrostrules-1.0.2-1.20.1-forge.jar to Otherworld (NC1, volume d4790f45)
|
||||
2. Deleted old world/serverconfig/firefrostrules-server.toml
|
||||
3. Started server — generated config/firefrostrules-common.toml with defaults
|
||||
4. Stopped server — wrote real values to config/firefrostrules-common.toml
|
||||
5. Restarted — config file still has correct values (confirmed)
|
||||
6. Restarted again — config file still has correct values (confirmed)
|
||||
|
||||
Config persistence is fixed. ✅
|
||||
|
||||
## What's Still Wrong
|
||||
|
||||
Player ran `/rules` and got the hardcoded default rules, not Discord content.
|
||||
|
||||
Log evidence:
|
||||
|
||||
```
|
||||
[22:42:38] [modloading-worker-0/INFO] [com.firefrostgaming.rules.ServerRules/]: Firefrost Rules Mod Initialized.
|
||||
[22:43:08] [main/INFO] [com.firefrostgaming.rules.ServerRules/]: Registered /rules command.
|
||||
[22:43:58] [Server thread/INFO] [minecraft/MinecraftServer/]: [Fire] Server Rules
|
||||
Please check Discord for the full rules list.
|
||||
```
|
||||
|
||||
Two problems:
|
||||
|
||||
1. **Loading handler never fired** — Code's `onConfigLoaded` handler should have logged "Rules mod config loaded successfully. Channel: 1493228237860638770" — it's completely absent from logs. The handler is not executing.
|
||||
|
||||
2. **Default rules served** — `/rules` returned hardcoded defaults, meaning `ServerRulesConfig.BOT_TOKEN.get()` is returning `YOUR_TOKEN_HERE` at runtime even though the file has the real token.
|
||||
|
||||
## What This Means
|
||||
|
||||
The config file has correct values but `ServerRulesConfig.SPEC` is not reading from it. The `ModConfig.Type.COMMON` registration may not be correctly linking the spec to the file on disk, or the config values are being read before the file is loaded.
|
||||
|
||||
## Hypothesis
|
||||
|
||||
In Forge 1.20.1, `ModConfig.Type.COMMON` configs are loaded from `config/<modid>-common.toml`. But the filename Forge generates may need to match exactly what the spec expects. Check:
|
||||
|
||||
1. What filename is Forge actually generating? Is it `firefrostrules-common.toml` or something else like `firefrostrules-server.toml` even with COMMON type?
|
||||
2. Is `ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ServerRulesConfig.SPEC)` being called before or after the config file is read?
|
||||
3. Is there a `ModConfigEvent.Loading` listener that confirms the spec is bound to the correct file?
|
||||
|
||||
## Current File State on Server
|
||||
|
||||
```
|
||||
Path: /var/lib/pterodactyl/volumes/d4790f45-b1bc-43b8-98c4-425238387ee3/config/firefrostrules-common.toml
|
||||
bot_token = "MTQ4NzA4MDE2Njk2OTU3NzUwMg.GU5EsT.mqBwo7XUHsciN9jNy9OygTRkaMZ9qJ2tHw7HbI"
|
||||
channel_id = "1493228237860638770"
|
||||
message_id = "1493228916998013101"
|
||||
```
|
||||
|
||||
File is correct. Mod is not reading it.
|
||||
|
||||
## What Code Needs to Do
|
||||
|
||||
1. Add a debug log immediately after `ServerRulesConfig.BOT_TOKEN.get()` in `RulesCommand.java` to print the actual value being read at command execution time
|
||||
2. Verify the `ModConfigEvent.Loading` listener is actually being registered and firing
|
||||
3. Check if there's a timing issue — config might need to be read after `FMLServerStartedEvent` rather than at command time
|
||||
4. Verify COMMON type generates correct filename in 1.20.1
|
||||
|
||||
## Applies To
|
||||
|
||||
1.20.1 confirmed broken. 1.21.1 and 1.16.5 status unknown — may have same issue.
|
||||
Bump to 1.0.3 once fixed. Update CHANGELOG.md.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Bug Follow-up v3: /rules Still Returns Hardcoded Defaults Despite Config Loading
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Topic:** Config loads correctly (Loading handler confirmed), but /rules still returns hardcoded default text
|
||||
**Priority:** HIGH — Otherworld still broken
|
||||
**Filed by:** Chronicler #86
|
||||
**Related to:** REQ-2026-04-13-rules-mod-config-not-read-at-runtime.md
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
v1.0.3 deployed to Otherworld. Config loading confirmed:
|
||||
|
||||
```
|
||||
[22:59:13] [main/INFO] [com.firefrostgaming.rules.ServerRules/]: Rules mod config loaded successfully. Channel: 1493228237860638770
|
||||
```
|
||||
|
||||
Config file on disk has correct values. Survives restarts. ✅
|
||||
|
||||
## What's Still Wrong
|
||||
|
||||
`/rules` run twice after server fully loaded — both return hardcoded defaults:
|
||||
|
||||
```
|
||||
[23:00:54] [Server thread/INFO] [minecraft/MinecraftServer]: [Fire] Server Rules
|
||||
1. Be respectful to all players.
|
||||
2. No griefing or cheating.
|
||||
3. Follow staff instructions.
|
||||
Please check Discord for the full rules list.
|
||||
|
||||
[23:01:19] Same result.
|
||||
```
|
||||
|
||||
## What The Logs Show
|
||||
|
||||
No `DiscordFetcher` log output at all — no 401, no 403, no fetch attempt, no fallback warning. The fetcher is completely silent. Only the hardcoded output appears.
|
||||
|
||||
## What This Means
|
||||
|
||||
`DiscordFetcher` is not being called, OR it's being called but returning null/empty silently and falling back to defaults without logging.
|
||||
|
||||
Two likely causes:
|
||||
|
||||
**Cause A — isMessageIdValid() returning false:**
|
||||
`ServerRulesConfig.isMessageIdValid()` checks that message_id matches `^\d{17,20}$`. If the config value is being read with surrounding quotes or whitespace, the regex fails and the command returns defaults without attempting the fetch.
|
||||
|
||||
Add a debug log immediately in `RulesCommand.java` before the validity check:
|
||||
```java
|
||||
LOGGER.info("Rules command: token={}, channel={}, messageId={}",
|
||||
ServerRulesConfig.BOT_TOKEN.get(),
|
||||
ServerRulesConfig.CHANNEL_ID.get(),
|
||||
ServerRulesConfig.MESSAGE_ID.get());
|
||||
LOGGER.info("isMessageIdValid={}", ServerRulesConfig.isMessageIdValid());
|
||||
```
|
||||
|
||||
**Cause B — DiscordFetcher silently returning null:**
|
||||
The async fetch may be completing but returning null or empty string, and `RulesCommand` falls back to defaults without logging. Add explicit logging in `DiscordFetcher` for every code path — success, failure, null return.
|
||||
|
||||
## What Code Needs To Do
|
||||
|
||||
1. Add the debug log in `RulesCommand.java` showing actual runtime values of all three config fields
|
||||
2. Add explicit logging in `DiscordFetcher` for: fetch attempt started, HTTP status received, response body preview, null/empty result
|
||||
3. Bump to v1.0.4, push
|
||||
|
||||
Chronicler will pull v1.0.4, deploy to Otherworld, and report what the new log lines show.
|
||||
|
||||
## Server Info
|
||||
|
||||
- Otherworld, NC1, volume `d4790f45-b1bc-43b8-98c4-425238387ee3`
|
||||
- Mod version: firefrostrules-1.0.3-1.20.1-forge.jar
|
||||
- Config: `/var/lib/pterodactyl/volumes/d4790f45-b1bc-43b8-98c4-425238387ee3/config/firefrostrules-common.toml`
|
||||
@@ -0,0 +1,51 @@
|
||||
# REQ-2026-04-13-status-route-mismatch
|
||||
|
||||
**Filed by:** Chronicler #85
|
||||
**Date:** 2026-04-13
|
||||
**Priority:** HIGH — widget does not auto-load on page open
|
||||
**Status:** OPEN
|
||||
|
||||
## Problem
|
||||
|
||||
The widget's `useEffect` fires on page load and calls:
|
||||
```
|
||||
GET /api/client/extensions/modpackchecker/servers/{uuid}/status
|
||||
```
|
||||
|
||||
But the registered route is:
|
||||
```
|
||||
GET /api/client/extensions/modpackchecker/status
|
||||
```
|
||||
|
||||
Result: `useEffect` hits a 404 on load → `catch(() => setData(null))` fires → widget shows nothing. Only the manual refresh button works because it hits `/check` (POST) which IS registered correctly.
|
||||
|
||||
## Evidence
|
||||
|
||||
Route list from Dev Panel:
|
||||
```
|
||||
GET|HEAD api/client/extensions/modpackchecker/status ModpackAPIController@...
|
||||
POST api/client/extensions/modpackchecker/servers/{server}/check ModpackAPIController@...
|
||||
```
|
||||
|
||||
Widget call in `wrapper.tsx` line 46:
|
||||
```js
|
||||
http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/status`)
|
||||
```
|
||||
|
||||
## What Needs to Fix
|
||||
|
||||
Either:
|
||||
- **Option A:** Update the route registration to match the widget: `servers/{server}/status`
|
||||
- **Option B:** Update the widget's `useEffect` to call the existing route (passing uuid as a query param)
|
||||
|
||||
Option A is cleaner — keeps the route pattern consistent with `/check` and future endpoints.
|
||||
|
||||
## Files Involved
|
||||
|
||||
- Route registration: check `routes/` directory in blueprint extension
|
||||
- Controller: `app/Http/Controllers/ModpackAPIController.php` — status method may need `$server` parameter
|
||||
- Widget: `views/server/wrapper.tsx` line 46 (may not need changes if Option A)
|
||||
|
||||
## After Code Pushes
|
||||
|
||||
Chronicler will pull, redeploy PHP + routes to Dev Panel, verify widget auto-loads on page open.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Architectural Request
|
||||
|
||||
**Date:** 2026-04-13
|
||||
**Topic:** Consolidated v1.1.0 deploy — all 5 priorities complete
|
||||
|
||||
## 1. What Changed
|
||||
|
||||
### New Migration
|
||||
- `2026_04_13_000000_add_file_id_and_ignored.php` — adds `current_file_id`, `latest_file_id`, `is_ignored`
|
||||
- `2026_04_12_000000_add_detection_columns.php` — adds `detection_method`, `is_user_overridden`
|
||||
|
||||
### Modified PHP Files
|
||||
- `CheckModpackUpdates.php` — hybrid detection (modpack_installations → egg → file → BCC log), file ID comparison, date-time seeding, is_ignored skip
|
||||
- `ModpackApiService.php` — all platforms return `file_id`, new `fetchFileHistory()` for CurseForge/Modrinth/FTB
|
||||
- `ModpackAPIController.php` — 4 new endpoints (serverStatus, releases, calibrate, toggleIgnore), modpack_installations in manualCheck
|
||||
- `routes/client.php` — 4 new routes
|
||||
|
||||
### Modified TSX Files
|
||||
- `views/server/wrapper.tsx` — zero-click widget with recalibrate dropdown + ignore button
|
||||
|
||||
## 2. Deployment Steps
|
||||
|
||||
```bash
|
||||
# 1. Pull latest
|
||||
cd /path/to/firefrost-services && git pull
|
||||
|
||||
# 2. Copy PHP files
|
||||
cp blueprint-extension/app/Console/Commands/CheckModpackUpdates.php /var/www/pterodactyl/app/Console/Commands/
|
||||
cp blueprint-extension/app/Services/ModpackApiService.php /var/www/pterodactyl/app/Services/
|
||||
cp blueprint-extension/app/Http/Controllers/ModpackAPIController.php /var/www/pterodactyl/app/Http/Controllers/
|
||||
cp blueprint-extension/routes/client.php [blueprint routes path]
|
||||
|
||||
# 3. Run migrations
|
||||
cd /var/www/pterodactyl && php artisan migrate
|
||||
|
||||
# 4. Rebuild Blueprint (copies TSX + runs yarn build)
|
||||
blueprint -install modpackchecker
|
||||
|
||||
# 5. Clear caches
|
||||
php artisan optimize:clear
|
||||
|
||||
# 6. Test
|
||||
php artisan modpackchecker:check
|
||||
```
|
||||
|
||||
## 3. Verification
|
||||
|
||||
1. `php artisan modpackchecker:check` — should detect 19+ servers via modpack_installations
|
||||
2. Dashboard badges show on server cards
|
||||
3. Console widget loads automatically (no click needed)
|
||||
4. Console widget shows version comparison (current → latest)
|
||||
5. Calibrate button shows release dropdown
|
||||
6. Ignore button hides non-modpack servers
|
||||
|
||||
## 4. Deploy to Both Panels
|
||||
- Dev Panel (64.50.188.128) first
|
||||
- Live Panel (45.94.168.138) after verification
|
||||
126
docs/code-bridge/archive/REQ-2026-04-14-pwa-phase1.md
Normal file
126
docs/code-bridge/archive/REQ-2026-04-14-pwa-phase1.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Code Request — Trinity Console PWA Phase 1
|
||||
|
||||
**Filed by:** Chronicler #88
|
||||
**Date:** 2026-04-14
|
||||
**Priority:** Medium
|
||||
**Task DB:** task_number 117
|
||||
|
||||
---
|
||||
|
||||
## What To Build
|
||||
|
||||
Make Trinity Console installable as a Progressive Web App. Phase 1 only —
|
||||
foundation that enables "Add to Home Screen" on Android and iOS.
|
||||
|
||||
---
|
||||
|
||||
## Files To Create
|
||||
|
||||
### 1. `src/public/manifest.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Trinity Console",
|
||||
"short_name": "Trinity",
|
||||
"description": "Firefrost Gaming Operations Console",
|
||||
"start_url": "/admin",
|
||||
"display": "standalone",
|
||||
"background_color": "#1a1a1a",
|
||||
"theme_color": "#06b6d4",
|
||||
"orientation": "portrait-primary",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/images/trinity-icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/images/trinity-icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. `src/public/sw.js` (Service Worker)
|
||||
|
||||
Cache static assets only. Never cache admin API responses or HTMX partials.
|
||||
|
||||
```javascript
|
||||
const CACHE_NAME = 'trinity-console-v1';
|
||||
const STATIC_ASSETS = [
|
||||
'/css/app.css',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then(cache => cache.addAll(STATIC_ASSETS))
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
// Only cache GET requests for static assets
|
||||
if (event.request.method !== 'GET') return;
|
||||
if (event.request.url.includes('/admin/')) return; // Never cache admin routes
|
||||
|
||||
event.respondWith(
|
||||
caches.match(event.request).then(cached => cached || fetch(event.request))
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Icon placeholders
|
||||
|
||||
Create simple placeholder PNGs at:
|
||||
- `src/public/images/trinity-icon-192.png`
|
||||
- `src/public/images/trinity-icon-512.png`
|
||||
|
||||
Use the cyan (#06b6d4) Firefrost color with a simple ❄️ or "T" — we'll replace
|
||||
with proper branding later.
|
||||
|
||||
---
|
||||
|
||||
## Files To Modify
|
||||
|
||||
### `src/views/layout.ejs` — Add to `<head>`:
|
||||
|
||||
```html
|
||||
<!-- PWA -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<meta name="theme-color" content="#06b6d4">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Trinity">
|
||||
<link rel="apple-touch-icon" href="/images/trinity-icon-192.png">
|
||||
|
||||
<!-- Service Worker Registration -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/sw.js').catch(err => console.log('SW registration failed:', err));
|
||||
});
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
Make sure `<meta name="viewport" content="width=device-width, initial-scale=1">`
|
||||
exists in the head (add if missing).
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Phase 2 (mobile layout) and Phase 3 (push notifications) are separate tasks
|
||||
- Icons are placeholders — proper branding assets come later
|
||||
- The service worker should be conservative — cache as little as possible to
|
||||
avoid stale admin data
|
||||
- Test by opening Trinity Console in Chrome mobile → three-dot menu →
|
||||
"Add to Home Screen"
|
||||
|
||||
---
|
||||
|
||||
**Fire + Frost + Foundation** 💙🔥❄️
|
||||
78
docs/code-bridge/archive/REQ-2026-04-14-rules-mod-1182.md
Normal file
78
docs/code-bridge/archive/REQ-2026-04-14-rules-mod-1182.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Code Request — Rules Mod 1.18.2 Build
|
||||
|
||||
**Filed by:** Chronicler #88
|
||||
**Date:** 2026-04-14
|
||||
**Priority:** Medium
|
||||
**Target:** Both `rules-mod` (Firefrost internal) and `discord-rules` (CurseForge generic)
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
We have the rules mod built for 1.16.5, 1.20.1, and 1.21.1. DeceasedCraft runs
|
||||
Forge 1.18.2-40.2.4 and needs it too. Build a 1.18.2 version of both variants.
|
||||
|
||||
---
|
||||
|
||||
## What To Build
|
||||
|
||||
Backport the current 1.20.1 version of both mods to 1.18.2. Use the 1.20.1 source as the base for both.
|
||||
|
||||
### 1. `services/rules-mod/1.18.2/` — Firefrost internal version
|
||||
- Backport from `services/rules-mod/1.20.1/`
|
||||
- Target: Forge 1.18.2-40.2.4
|
||||
- Java version: **Java 17**
|
||||
- Output jar: `firefrostrules-1.0.5-1.18.2-forge.jar`
|
||||
|
||||
### 2. `services/discord-rules/1.18.2/` — CurseForge generic version
|
||||
- Backport from `services/discord-rules/1.20.1/`
|
||||
- Same Forge/Java targets as above
|
||||
- Output jar: `discord-rules-1.0.5-1.18.2-forge.jar`
|
||||
|
||||
---
|
||||
|
||||
## Build Instructions
|
||||
|
||||
```bash
|
||||
use-java 17
|
||||
cd /opt/mod-builds/firefrost-services/services/rules-mod/1.18.2
|
||||
/opt/gradle-8.8/bin/gradle build
|
||||
|
||||
cd /opt/mod-builds/firefrost-services/services/discord-rules/1.18.2
|
||||
/opt/gradle-8.8/bin/gradle build
|
||||
```
|
||||
|
||||
Output jars go in `build/libs/` — grab the one without `-sources` or `-dev`.
|
||||
|
||||
---
|
||||
|
||||
## Deliverable
|
||||
|
||||
2 built jars committed to `firefrost-services` main:
|
||||
- `services/rules-mod/discord-rules-1.0.5-1.18.2-forge.jar`
|
||||
- `services/discord-rules/discord-rules-1.0.5-1.18.2-forge.jar`
|
||||
|
||||
Deploy `firefrostrules-1.0.5-1.18.2-forge.jar` to DeceasedCraft:
|
||||
- Server UUID: `8950fa1e-acd6-4db9-9595-076007cc26af`
|
||||
- Node: NC1
|
||||
- Mods path: `/var/lib/pterodactyl/volumes/8950fa1e-acd6-4db9-9595-076007cc26af/mods/`
|
||||
- Config path: `world/serverconfig/firefrostrules-server.toml`
|
||||
|
||||
After deploy, update `ACTIVE_CONTEXT.md`.
|
||||
|
||||
---
|
||||
|
||||
## Config for DeceasedCraft After Deploy
|
||||
|
||||
```toml
|
||||
bot_token = "MTQ4NzA4MDE2Njk2OTU3NzUwMg.GU5EsT.mqBwo7XUHsciN9jNy9OygTRkaMZ9qJ2tHw7HbI"
|
||||
channel_id = "DECEASEDCRAFT_RULES_CHANNEL_ID"
|
||||
message_id = "DECEASEDCRAFT_RULES_MESSAGE_ID"
|
||||
```
|
||||
|
||||
Michael needs to provide the channel_id and message_id from Discord — the
|
||||
#server-rules-for-bot channel message for DeceasedCraft.
|
||||
|
||||
---
|
||||
|
||||
**Fire + Frost + Foundation** 💙🔥❄️
|
||||
@@ -0,0 +1,128 @@
|
||||
# Code Request — Task Module Improvements
|
||||
|
||||
**Filed by:** Chronicler #88
|
||||
**Date:** 2026-04-14
|
||||
**Priority:** Medium
|
||||
**Task DB:** task_number 105 (Trinity Console Review Workflow — repurpose or create new)
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
The current task module at `/admin/tasks` shows a flat list. Michael wants
|
||||
significant UX improvements. This request covers everything Code can build
|
||||
without further architecture decisions.
|
||||
|
||||
---
|
||||
|
||||
## Feature 1: Click Task → Slide-Out Detail Panel
|
||||
|
||||
When clicking a task name, show a slide-out panel (right side) with:
|
||||
- Full title, status badge, priority badge, owner
|
||||
- Full description (rendered as markdown)
|
||||
- Tags
|
||||
- Created/updated/completed timestamps
|
||||
- Completed by
|
||||
- spec_path (if set)
|
||||
- Close button (X or click outside)
|
||||
|
||||
Do NOT navigate away from the list — panel overlays on top.
|
||||
|
||||
Use a simple CSS transition (translate from right). No external libraries needed.
|
||||
|
||||
---
|
||||
|
||||
## Feature 2: Sorting
|
||||
|
||||
Add sort controls above the task list:
|
||||
|
||||
```
|
||||
Sort by: [Number ▼] [Priority] [Status] [Updated]
|
||||
```
|
||||
|
||||
Default: task_number ASC. Clicking active sort toggles ASC/DESC.
|
||||
Persist sort preference in localStorage.
|
||||
|
||||
---
|
||||
|
||||
## Feature 3: Filter Chips
|
||||
|
||||
Add filter chips row above the list:
|
||||
|
||||
**Status:** All | Open | In Progress | Blocked | Done | Obsolete
|
||||
**Priority:** All | Critical | High | Medium | Low | Wish
|
||||
|
||||
Chips are toggleable. Multiple status/priority selections allowed.
|
||||
Active chips highlighted in cyan.
|
||||
|
||||
---
|
||||
|
||||
## Feature 4: Saved Filter Presets
|
||||
|
||||
Add a "Presets" dropdown with these saved filters:
|
||||
|
||||
- **Launch Fires** — status=open, priority=high OR critical
|
||||
- **Code Queue** — tags contains 'code' OR title contains 'Code'
|
||||
- **Post-Launch** — status=open, priority=low OR wish
|
||||
- **All Open** — status IN (open, in_progress, blocked)
|
||||
|
||||
Presets are hardcoded in the view for now (no CRUD needed yet).
|
||||
|
||||
---
|
||||
|
||||
## Feature 5: Kanban View
|
||||
|
||||
Add a view toggle: **[List] [Kanban]**
|
||||
|
||||
Kanban shows 4 columns: Open | In Progress | Blocked | Done
|
||||
|
||||
Cards show: task_number, title, priority badge.
|
||||
Cards are NOT draggable yet (drag-to-reorder is Phase 2).
|
||||
|
||||
---
|
||||
|
||||
## Feature 6: Session Summary Badge
|
||||
|
||||
Add a small banner or badge at the top of the task list:
|
||||
|
||||
```
|
||||
✅ 3 tasks completed today
|
||||
```
|
||||
|
||||
Query: `SELECT COUNT(*) FROM tasks WHERE completed_at::date = CURRENT_DATE`
|
||||
|
||||
Show only if count > 0.
|
||||
|
||||
---
|
||||
|
||||
## Feature 7: Code Queue Indicator
|
||||
|
||||
In the nav sidebar next to "Tasks", show a badge with the count of tasks
|
||||
where tags contains 'code' AND status IN ('open', 'in_progress').
|
||||
|
||||
Query:
|
||||
```sql
|
||||
SELECT COUNT(*) FROM tasks
|
||||
WHERE 'code' = ANY(tags) AND status IN ('open', 'in_progress')
|
||||
```
|
||||
|
||||
Badge style: small cyan circle with white number, same as Discord notification badges.
|
||||
|
||||
---
|
||||
|
||||
## API Notes
|
||||
|
||||
All data comes from existing `/admin/tasks` route. Check what data is already
|
||||
passed to the view and add any missing fields (description, tags, completed_by,
|
||||
spec_path) to the route query if needed.
|
||||
|
||||
---
|
||||
|
||||
## Deliverable
|
||||
|
||||
- Updated `src/views/admin/tasks/` view(s)
|
||||
- Updated `src/routes/admin/tasks.js` if additional fields needed
|
||||
- No new tables required
|
||||
- Update `ACTIVE_CONTEXT.md`
|
||||
|
||||
**Fire + Frost + Foundation** 💙🔥❄️
|
||||
98
docs/code-bridge/archive/REQ-2026-04-14-version-ui-matrix.md
Normal file
98
docs/code-bridge/archive/REQ-2026-04-14-version-ui-matrix.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Code Request — Version UI in Server Matrix Body
|
||||
|
||||
**Filed by:** Chronicler #88
|
||||
**Date:** 2026-04-14
|
||||
**Priority:** Medium
|
||||
**Context:** Pre-launch, defer until stable
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
Chronicler #88 added modpack version tracking infrastructure today:
|
||||
- `current_version` column on `server_config`
|
||||
- `server_version_history` table (version, who, when)
|
||||
- `POST /admin/servers/:id/set-version` route
|
||||
- `GET /admin/servers/:id/version-history` route
|
||||
- Helper JS functions `saveVersion()`, `toggleVersionForm()`, `hideVersionForm()` in `servers/index.ejs`
|
||||
|
||||
The UI addition to `_matrix_body.ejs` kept breaking EJS due to single quotes
|
||||
inside ternary expressions inside HTML attributes. The file was reverted to
|
||||
clean state. This task is to add the version UI properly.
|
||||
|
||||
---
|
||||
|
||||
## What To Add
|
||||
|
||||
At the bottom of each server card in `_matrix_body.ejs` (there are TWO loops —
|
||||
TX1 and NC1), add a version display section BEFORE the closing `</div>`.
|
||||
|
||||
The tricky part: avoid single quotes inside `<%= %>` tags that are inside
|
||||
HTML attributes. Use a local variable assignment instead:
|
||||
|
||||
```ejs
|
||||
<% var currentVersion = config && config.current_version ? config.current_version : null; %>
|
||||
<div class="px-4 pb-4" style="border-top:1px solid #333;padding-top:10px;margin-top:4px;">
|
||||
<div style="font-size:10px;color:#666;text-transform:uppercase;letter-spacing:0.1em;margin-bottom:6px;">📦 Installed Version</div>
|
||||
<div style="display:flex;gap:6px;align-items:center;" id="version-display-<%= server.identifier %>">
|
||||
<span style="font-size:13px;font-weight:600;color:<%= currentVersion ? '#4ade80' : '#555' %>;" id="version-text-<%= server.identifier %>">
|
||||
<%= currentVersion || 'Not set' %>
|
||||
</span>
|
||||
<button id="version-edit-btn-<%= server.identifier %>"
|
||||
onclick="toggleVersionForm('<%= server.identifier %>')"
|
||||
style="font-size:10px;background:#333;border:1px solid #555;color:#aaa;padding:2px 8px;border-radius:4px;cursor:pointer;">
|
||||
✏️ Edit
|
||||
</button>
|
||||
</div>
|
||||
<div id="version-form-<%= server.identifier %>" style="display:none;margin-top:6px;">
|
||||
<div style="display:flex;gap:6px;align-items:center;">
|
||||
<input type="text" id="version-input-<%= server.identifier %>"
|
||||
placeholder="e.g. 1.4.2"
|
||||
style="font-size:12px;background:#1a1a1a;border:1px solid #555;color:#e0e0e0;padding:4px 8px;border-radius:4px;width:140px;" />
|
||||
<button onclick="saveVersion('<%= server.identifier %>')"
|
||||
style="font-size:11px;background:#2563eb;color:#fff;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;">Save</button>
|
||||
<button onclick="hideVersionForm('<%= server.identifier %>')"
|
||||
style="font-size:11px;background:#333;border:1px solid #555;color:#aaa;padding:4px 8px;border-radius:4px;cursor:pointer;">Cancel</button>
|
||||
</div>
|
||||
<div id="version-result-<%= server.identifier %>" style="margin-top:4px;font-size:11px;"></div>
|
||||
<div style="margin-top:6px;">
|
||||
<button hx-get="/admin/servers/<%= server.identifier %>/version-history"
|
||||
hx-target="#version-history-<%= server.identifier %>"
|
||||
hx-swap="innerHTML"
|
||||
style="font-size:10px;background:transparent;border:none;color:#555;cursor:pointer;padding:0;text-decoration:underline;">View history</button>
|
||||
</div>
|
||||
<div id="version-history-<%= server.identifier %>" style="margin-top:4px;background:#1a1a1a;border-radius:4px;padding:4px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Rule
|
||||
|
||||
**Never put single quotes inside `<%= %>` tags that are inside HTML attribute values.**
|
||||
Use `<% var x = ...; %>` to assign first, then `<%= x %>` in the attribute.
|
||||
|
||||
---
|
||||
|
||||
## Validation
|
||||
|
||||
After adding, run this on the Dev Panel to verify EJS compiles:
|
||||
```bash
|
||||
cd /opt/mod-builds/firefrost-services/services/arbiter-3.0
|
||||
node -e "const ejs = require('ejs'); const fs = require('fs'); try { ejs.compile(fs.readFileSync('src/views/admin/servers/_matrix_body.ejs', 'utf8')); console.log('EJS OK'); } catch(e) { console.log('ERROR:', e.message); }"
|
||||
```
|
||||
|
||||
Must print `EJS OK` before committing.
|
||||
|
||||
---
|
||||
|
||||
## Files To Edit
|
||||
|
||||
- `services/arbiter-3.0/src/views/admin/servers/_matrix_body.ejs`
|
||||
|
||||
Do NOT touch `_server_card.ejs` or `index.ejs` — those are fine.
|
||||
|
||||
---
|
||||
|
||||
**Fire + Frost + Foundation** 💙🔥❄️
|
||||
119
docs/code-bridge/archive/REQ-2026-04-15-reaction-roles.md
Normal file
119
docs/code-bridge/archive/REQ-2026-04-15-reaction-roles.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# REQ-2026-04-15-reaction-roles
|
||||
|
||||
**From:** Chronicler #92
|
||||
**Date:** 2026-04-15
|
||||
**Priority:** HIGH — pre-launch
|
||||
**Status:** PENDING
|
||||
|
||||
## Summary
|
||||
|
||||
Add reaction role handling to Arbiter. The `#get-roles` channel has been rebuilt with bot-owned messages. Need Arbiter to handle all three reaction role sets.
|
||||
|
||||
## Message IDs (bot-owned, in #get-roles)
|
||||
|
||||
| Message ID | Purpose |
|
||||
|---|---|
|
||||
| `1493930565395681402` | Choose Your Path |
|
||||
| `1493930595435286548` | Notification Preferences |
|
||||
| `1493930614066253908` | Server Roles |
|
||||
|
||||
## Emoji → Role Mappings
|
||||
|
||||
### Message 1 — Choose Your Path
|
||||
| Emoji | Role ID | Role Name |
|
||||
|---|---|---|
|
||||
| 🔥 | `1482490890453782612` | 🔥 Fire Path |
|
||||
| ❄️ | `1482491234378448946` | ❄️ Frost Path |
|
||||
|
||||
### Message 2 — Notification Preferences
|
||||
| Emoji | Role ID | Role Name |
|
||||
|---|---|---|
|
||||
| 📢 | `1491778391060381776` | Announcements |
|
||||
| 🎉 | `1491778662922457199` | Events |
|
||||
| 🗒️ | `1491778706312532171` | Patch Notes |
|
||||
|
||||
### Message 3 — Server Roles
|
||||
| Emoji | Role ID | Role Name |
|
||||
|---|---|---|
|
||||
| 🪨 | `1491028769132253274` | Stoneblock 4 |
|
||||
| 🏝️ | `1491028496284258304` | All The Mods: To the Sky |
|
||||
| 🔴 | `1491029000108380170` | All The Mons |
|
||||
| 🧙 | `1491029070190870548` | Mythcraft 5 |
|
||||
| ⚔️ | `1491029454011629749` | Otherworld [Dungeons & Dragons] |
|
||||
| 🧟 | `1491029615739801800` | DeceasedCraft |
|
||||
| 🍽️ | `1493352900997415134` | Farm Crossing 6 |
|
||||
| 🏡 | `1491030015746510939` | Homestead |
|
||||
| 🌌 | `1491028885981102270` | Society: Sunlit Valley |
|
||||
| 🌊 | `1491029215963906149` | Beyond Depth |
|
||||
| ☁️ | `1491029284159094904` | Beyond Ascension |
|
||||
| 🏆 | `1491029373640376330` | Wold's Vaults |
|
||||
| 🤿 | `1491029708878647356` | Submerged 2 |
|
||||
| 🌙 | `1491029870002569298` | Cottage Witch |
|
||||
| 🌿 | `1493924685170343978` | vanilla |
|
||||
|
||||
## Files to Create/Modify
|
||||
|
||||
### 1. CREATE `src/discord/reactionRoles.js`
|
||||
- Export a `REACTION_ROLE_MAP` object keyed by message ID
|
||||
- Each entry maps emoji name/id → Discord role ID
|
||||
- Export `handleReactionAdd(reaction, user)` and `handleReactionRemove(reaction, user)`
|
||||
- Fetch partial reactions if needed (`reaction.partial`)
|
||||
- Skip bot reactions
|
||||
- Look up guild member and add/remove role
|
||||
|
||||
### 2. MODIFY `src/index.js`
|
||||
- Add `GatewayIntentBits.GuildMessageReactions` to the client intents
|
||||
|
||||
### 3. MODIFY `src/discord/events.js`
|
||||
- Import reactionRoles handlers
|
||||
- Register `messageReactionAdd` and `messageReactionRemove` events on the client
|
||||
|
||||
## Notes
|
||||
- Follow the existing pattern in events.js (clean, minimal)
|
||||
- Silent-fail on missing roles/members (don't crash)
|
||||
- Log adds/removes to console for debugging
|
||||
- Deploy pattern: backup → clone to /tmp → copy to /opt/arbiter-3.0 → restart arbiter-3
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Additional Scope — Carl-bot Migration
|
||||
|
||||
Take over these two Carl-bot behaviors so Carl-bot can be removed.
|
||||
|
||||
### 4. MODIFY `src/discord/events.js`
|
||||
Register `guildMemberAdd` event:
|
||||
- Assign Wanderer role (`1487267974367805545`) to new member
|
||||
- Send welcome DM:
|
||||
|
||||
```
|
||||
Hey {username}! Welcome to Firefrost Gaming!
|
||||
You just landed as a Wanderer — the door is open, come explore!
|
||||
|
||||
Quick links:
|
||||
• Check out #rules first
|
||||
• Say hi in #introductions
|
||||
• Head to #get-roles to pick your path and grab your server channels
|
||||
|
||||
Questions? We're here. Welcome to the family!
|
||||
```
|
||||
|
||||
- Silent-fail if DMs are closed
|
||||
|
||||
### 5. MODIFY `src/services/discordRoleSync.js` (or stripe.js)
|
||||
When a subscriber reaches Awakened tier or above (i.e. after successful Stripe checkout assigns their role), send them a DM:
|
||||
|
||||
```
|
||||
Hey {username}! You're now part of the Firefrost family! 🎉
|
||||
|
||||
One quick step — head to #link-your-account and use the /link command to connect your Minecraft account so we can whitelist you on our servers.
|
||||
|
||||
See you in-game!
|
||||
```
|
||||
|
||||
- This should fire from the Stripe checkout flow after role assignment
|
||||
- Silent-fail if DMs are closed
|
||||
|
||||
### Notes
|
||||
- `GuildMembers` intent is already enabled — `guildMemberAdd` will work
|
||||
- Check where luckpermsSync fires post-checkout — link reminder DM should fire at the same point
|
||||
Reference in New Issue
Block a user