Everything for Lorenzo's Server in one place:
- Client theme: the Field Notebook look, matching lscaturchio.xyz. Night or paper ground, forest-green ink, Fraunces headings and IBM Plex Mono wall labels.
- Server setup (
server/): roles, channels, Onboarding and AutoMod as code. - Front Desk (
bot/): the server's own bot, with squad-up, stats and more.
Roadmap and design: docs/superpowers/specs/2026-10-01-front-desk-bot-design.md.
1. Client theme (theme/field-notebook.theme.css)
Works with Vencord (recommended) or BetterDiscord.
- Vencord: Settings → Vencord → Themes → Open Themes Folder, drop the file in, then toggle it on.
- BetterDiscord: Settings → Themes → Open Themes Folder, drop the file in, then toggle it on.
Discord's own Appearance setting picks the variant: Dark, Darker or Midnight gives the night notebook, and Light gives the paper one.
Heads up: both mods break Discord's Terms of Service. Bans for themes alone are very rare, but it's your call. Discord renames its internal classes every few months. If a detail stops applying (for example the mono category labels), the colours and fonts still work because they're driven by CSS variables.
2. Server makeover (server/)
This is a one-shot bot run. It creates or renames roles, categories and channels,
makes the front-desk channels read-only, sets the join-message and AFK channels,
and posts rules and welcome embeds. It never deletes anything. Anything not
in layout.py gets listed so you can clean it up by hand.
One-time setup
- Go to https://discord.com/developers/applications → New Application → Bot → Reset Token, and copy the token.
- Copy
.env.exampleto.envand paste the token and your server ID into it. - Invite the bot with Administrator (replace
APP_IDwith the Application ID from the General Information page):https://discord.com/oauth2/authorize?client_id=APP_ID&scope=bot&permissions=8 - Server Settings → Roles: drag the bot's role to the very top.
Run it
cd server
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python setup_server.py # dry run: shows the plan
.venv\Scripts\python setup_server.py --apply # do it
Edit layout.py first. GAMES at the top holds your group's actual games, and
each one gets a ping role plus a channel. RETIRED_GAMES under it holds games the
server used to list: their roles stay, they are no longer offered, and
public_mode.py archives their channels. The script is safe to re-run: it
matches existing channels and roles by plain name, so #general becomes
#💬・general instead of a duplicate.
Second pass: Discord's built-in features
.venv\Scripts\python polish_server.py # dry run
.venv\Scripts\python polish_server.py --apply
This turns on Community and Onboarding, so new members pick their own game
roles on join. It also sets up AutoMod (spam, mention raids and slurs, with
alerts to the mod channel), defaults notifications to @mentions only, and
removes the empty channels listed in TRIM. Everything is configured at the
bottom of layout.py. The bot's role must be at the top for Onboarding to
assign roles.
The server icon comes from assets/make_icon.py (pip install pillow).
Hosted bots (always online, set up in their dashboards)
| Bot | Job | Setup |
|---|---|---|
| Captcha.bot | DMs new members a web captcha; passing gives @Verified | Verification channel #💬・general, role @Verified (Add role). Its role must sit above @Verified. |
| Replaced by Front Desk Levels | Turn leveling off in its dashboard, then kick it and delete its role, so members don't get two level numbers and two sets of level-up pings. | |
| GiveawayBot | Giveaways | Nothing to set up: /gstart <time> <winners> <prize>. |
MEE6 was tried and dropped: its Levels and Welcome plugins now need Premium.
polish_server.py also handles the media lock: @everyone can't post images,
files or link embeds until they have one of the MEDIA_ROLES (Captcha.bot's
@Verified covers new members). The lock is off for now (layout.MEDIA_LOCK = False,
captcha paused since 2026-10-28), so polish_server.py gives @everyone media instead;
set it back to True when Captcha.bot returns. The bot roles are ordered by the keep entries
in layout.ROLES, and every bot gets @Bots so they're grouped in the member list.
Afterwards
- Give yourself @Keeper and your friends @Squad.
- Take Administrator back off the bot's role. Front Desk now stays as the server's permanent bot (section 3) with only the permissions it needs.
- For later changes use
public_mode.py: it never reorders or deletes, so it works with Front Desk sitting below the staff roles. It adds what is missing, with one exception: a game inRETIRED_GAMESleaves the "What do you play?" question and its channel moves into the staff-only99 · archivecategory (messages and the role are kept; the dry run lists every channel it would hide). - To give newcomers a short channel list, run
focus_mode.py(--planprints the plan fromlayout.pyalone, no arguments is a dry run against the server, then--apply). The Onboarding default channels become the nine inONBOARDING_DEFAULT_CHANNELS, and the other public channels become answers to one optional question, "What else do you want to see?". No permission changes and nothing is deleted: every channel stays open to @everyone and current members keep the list they have.--revertmakes every channel a default again. - Reset the bot token in the portal if you shared
.envanywhere. - Optional: set
ICON_PATHinlayout.pyto a square image for the server icon.
Emoji & sounds
The server has its own emoji pack (20 emoji, such as :gg:, :take_w:, :clutch:, :touchgrass:
and :frontdesk:) and 8 soundboard sounds. Code generates all of them from scratch: Pillow
draws the emoji and numpy synthesizes the sounds. No samples or outside art are used. The emoji
share the look of the server icon: a night-paper stamp with one mark in one ink (pen green for
wins and hype, near-white for neutral words, muted for losses), drawn to read at 22 to 48 px on
both Discord themes. assets/expressions/contact-sheet.png shows the whole set at those sizes.
The lettering is set in Bahnschrift Bold, so make_emoji.py needs Windows 10 or 11; the font
itself is not in this repo.
.venv\Scripts\pip install pillow numpy # ffmpeg must be on PATH for the MP3s
.venv\Scripts\python ..\assets\expressions\make_emoji.py # -> assets/expressions/emoji/*.png
.venv\Scripts\python ..\assets\expressions\make_sounds.py # -> assets/expressions/sounds/*.mp3
.venv\Scripts\python expressions.py # dry run: what fits in your slots
.venv\Scripts\python expressions.py --apply # upload
.venv\Scripts\python expressions.py --replace # dry run: which emoji have new art
.venv\Scripts\python expressions.py --replace --apply # swap them, one at a time
The bot's role needs Create Expressions. Names that already exist are skipped, and anything over the emoji or soundboard slot limit (8 sounds unboosted) is listed rather than uploaded. Sounds are MP3 because discord.py only accepts MP3 for the soundboard.
--replace is for emoji that were redrawn. Discord cannot change an emoji's picture, so each
emoji whose local art differs from the server's is uploaded again under the same name and the
old one is deleted (upload first when a slot is free; --delete-first turns that round). The
emoji gets a new ID: typing :name: shows the new art, but messages and reactions that already
used the old one do not change. It needs Manage Expressions as well, unless the bot uploaded
every emoji it deletes. A touch-up too small for the comparison to notice can be named with
--force NAME. Nothing in the bot refers to these emoji, by name or by ID.
Snapshot
snapshot_server.py is read-only. It writes the live server's config to server/snapshot/
(server, roles, channels, onboarding, automod and welcome .json). It uses names,
not IDs, and sorts its output, so git diff server/snapshot shows exactly what changed since
the last snapshot you committed. It never records members, messages, invites or the token.
cd server
.venv/Scripts/python snapshot_server.py # write the snapshot
.venv/Scripts/python snapshot_server.py --compare # also list roles/channels that differ from layout.py
.venv/Scripts/python -m pytest tests # unit tests
If the bot lacks a permission for a fetch (AutoMod rules need Manage Server), that file
gets an "error" note instead and the rest is still written.
3. Front Desk bot (bot/)
Front Desk is the server's own bot. It runs on LORENZO-COMPUTE around the clock and needs no
day-to-day attention. Design and history:
docs/superpowers/specs/2026-10-01-front-desk-bot-design.md. Privacy: PRIVACY.md.
What it does
| Area | Commands and automatic jobs |
|---|---|
| Squad-up | /lfg posts in 🎮・lfg with Join/Leave/Close buttons; posts close after 3 h; a host's posts ping the game roles at most once per 30 min (later ones post without pings) |
| Stats | /stats, /leaderboard, /privacy; weekly MVP Sundays 18:00 (accounts 30+ days old; fresh accounts don't count as voice company for it; if 💬・general is missing at that time the week is retried, not skipped) |
| Growth | /invites, /recruiters (Recruiter role), /bumpers, /bumpping; Disboard bump reminders |
| Community | welcome after Onboarding, /report and "Report message", mod log |
| Hall of fame | 3 ⭐ from other members (accounts 30+ days old) reposts to ⭐・hall-of-fame (public channels only) |
| Clips | clip of the week poll Sundays 18:05 (retried, not skipped, while 📸・clips is missing); winner gets Clip of the Week (only votes from accounts 30+ days old count, and not the clip author's own) |
| Voice | join ➕ New Squad for your own channel; /squad name, /squad limit, /squad claim |
| Events | /gamenight; /watchparty (a Stage event in 🎙・stage, or a voice event in the Lobby when the bot can't schedule on the stage; accounts 30+ days old; shares the game-night limits; its reminder pings the Game Night role only when staff host it); a Town Hall on the first Saturday of each month at 12:00 (TOWNHALL_TIME), created a week ahead and announced once; 15-min reminders; free Epic/Steam games Thursdays 18:00 |
| Economy | /daily, /balance, /give (accounts 30+ days old), /coinflip, /richest; coins for voice, chat, clips, squads |
| Games | /slots, /blackjack, /trivia, /predict |
| Shop and seasons | /shop, /buy (colour role, Hype, shoutout), /season; monthly champions |
| Engagement | question of the day 12:00 and this-or-that poll 18:00 (each pauses while nobody answers it: see Quiet rooms), 🔢・counting, auto game night Fridays, /birthday |
| Moderation | /warn, /timeout, /untimeout, /cases, /purge (shown only to members with Timeout Members, like /xp and /suggestion); /ban (staff who also hold Ban Members; DMs the reason first, plus the appeal code when appeals are set up; one line in 📋・mod-log per ban); auto-escalation, anti-spam, anti-raid |
| Operations | daily DB backup 04:00, error alerts, back-online note, weekly config drift check, /status; permission self-check a minute after start and daily at 09:00 (every channel, permission and role the loaded modules need: one mod-log note when something is missing, one when it is fixed, and a line in /status) |
| Utility | /remind, /reminders, /afk, suggestion voting, member/online stat channels, tickets in 🆘・help |
| Welcome cards | an image card in 👋・welcome when someone finishes Onboarding (names them without a ping; the #general welcome is the one ping); a card that fails to post is retried every 10 minutes for up to 24 hours while the member is still here |
| Tournaments | /tournament create/start/cancel/bracket; sign-up buttons, brackets with byes, both players confirm results, 1000/400 coin prizes, Tournament Champ role |
| Achievements | 23 badges, /profile, /badges; checked on activity and hourly; Founding Member is granted without a post |
| Creators | /creator link/verify/unlink/list (members prove ownership with a code in their channel description), staff /creator approve/remove; YouTube uploads and Twitch go-live in 📺・creators |
| Levels | XP from chat (60 s cooldown) and voice; Regular/Veteran/Elite/Legend/Mythic at levels 5/10/20/30/50; /rank card, /levels, staff /xp; level-ups are announced from level 5 (the first reward role) |
| Recap | weekly recap in 📣・announcements Mondays 10:00, owner digest by DM 10:05, member milestones, monthly invite contest (1500/750/300 coins) on the 1st. The digest has a "Games list" field that compares the last 28 days of play with GAMES: worth adding (an unlisted game that 3+ members played for 5+ hours combined, a retired one included) and worth retiring (a listed game nobody played); game names, player counts and hours only, and it never changes the list. Its "Newcomers" field says whether the people who join stick: for the week and for the last 28 days, how many joined, left since, picked roles, said something and joined voice; of the week before's joiners, how many are still here and how many came back in week 2; and one sentence of reading. Counts only, never a name. Members with tracking off count as joined and left and in nothing else; the other steps are "of the N with stats on", so the field shows how many recent joiners have tracking off. No field when nobody joined in 28 days |
| Self roles | button panel in 🎭・roles (platform, region, play time, pings, games), /roles; Game Night role pinged when a game night starts, SMP Ping when 3+ are on Monkey SMP |
| Partners | /partner apply → staff review card in 📋・mod-log → 🤝・partners; weekly dead-invite sweep; staff /partner remove |
| Starter quest | /quest: pick roles, say hi, join a squad, claim /daily, join voice; the Settled In badge, plus 500 coins for new members (account 30+ days old, finished within 30 days of joining); one nudge DM after a day |
| Heartbeat | writes bot/data/heartbeat every minute while connected to the gateway (stops during a reconnect loop, so the watchdog alerts); logs a warning when the event loop stalls for 1 s+ |
| Auto tournaments | first Monday of the month: a 16-player bracket for the most-played game (if 🏆・tournaments is missing it waits for the channel, until it is too late to open sign-ups), Discord event, Saturday 19:00 auto-start (4+ entrants), 1 h reminder, nudges for unreported matches; matches still undecided after 48 h (including a report the opponent never confirmed) go to staff in mod-log |
| Weekly challenges | 3 rotating goals each week (150/250/400 coins + 300 bonus), board in 🎲・games Mondays 09:00, /challenges with Claim button, hourly auto-claim; squads and voice company only count from accounts 30+ days old, a game night counts once it starts (not if cancelled), a tournament entry once it starts |
| Daily Word | /word guess/today/stats/leaderboard: one 5-letter word a day (order salted with a private secret kept in the database), spoiler-free results in 🎲・games, coins for wins, streaks |
| Vibes | conversation starter when 💬・general is quiet for 3 h (not within an hour of the Tonight voice line), join anniversaries, booster thanks + 1000 coins and a monthly stipend, member of the month (squads = joining someone else's post) |
| Matchmaking | /queue join/leave/status: pick a game, mode and size; when the queue fills, a voice channel is made and the players are pinged in 🎲・games; entries expire after 60 min |
| Economy extras | /raffle buy/info (weekly draw Sundays 20:00, 80% of the pot to the winner, 20% burned), Gift Hype, Spotlight (pinned shoutout for 24 h); staff /economy for supply and minted vs burned. Audit: docs/ECONOMY.md |
| Help | /help (categories built from the live command list, staff commands hidden from members, "Start here" button), /about; the bot's status rotates every 5 min |
| Game news | official Steam announcements and verified feeds (bot/assets/news_sources.json) posted in each game's channel every 30 min, no pings; staff /news test |
| Staff applications | /apply staff (30+ days in the server, 90+ day account, clean record), /apply status; review card in 📋・mod-log with Approve/Deny (Keepers) and Interview; the Moderator role is then given by hand, since it sits above Front Desk |
| Text filter | slurs (with evasion spellings) and other servers' invite links are refused in member text the bot reposts: /lfg and /gamenight notes, shoutouts and Spotlight, tournament names, partner applications |
| Seasonal events | Halloween (Oct 20 to Nov 1) and Winter Fest (Dec 15 to Jan 2) from bot/assets/events.json, every year: 6-10 drops a day in 💬・general while chat is active (Grab button, 25-75 coins + 1 point, gone after 10 min, max 12 a day each), a daily quest (+3 points, 100 coins), /event with your points and the top 10; at the end the top 3 win 2000/1000/500 coins, first place gets the Event Champ role and 10+ points earns the event badge; accounts under 30 days old earn nothing |
| Game accounts | /link steam + /link verify (members prove ownership with a code in their Steam profile summary), /unlink, /accounts; staff /link approve/remove; persona, Steam link and hours in the server's games show on /profile, refreshed daily. No API key |
| Vote hub | /vote (private): Disboard with your bump count and when the next bump is ready, discord.me, top.gg ("coming soon" until listed) and the share link discord.lscaturchio.xyz/join; Sundays 17:00 the week's top 3 Disboard bumpers are thanked in 🎲・games and paid 300/200/100 coins (accounts 30+ days old) |
| Catch-up | /catchup [channel] [hours]: a private AI summary (5-8 bullets plus a "good moment to jump in") of a public channel's last 1-48 hours (default 12). Two providers: a local Ollama model on the bot's own machine (OLLAMA_MODEL, default llama3.2:3b; nothing leaves the machine, one summary at a time) or the Anthropic API (ANTHROPIC_API_KEY); CATCHUP_PROVIDER picks one, otherwise the key wins and Ollama is the fallback, and with neither it says summaries aren't set up. Accounts 30+ days old, 3 a day per member and 60 a day server-wide; bots and members with tracking off are left out; staff /catchup-usage shows the provider, requests and tokens |
| Houses | three houses (Fern, Kestrel, Otter): established members are sorted into the smallest one every 10 min and get its role (one introduction post in 📣・announcements the first time; the permission self-check lists the house roles until public_mode.py --apply has made them); /house me/standings/join/leave/history (leaving starts a 30-day wait), staff /house-admin move; points come from what the other modules already record, capped at 100 per member a week and scored per member of the house; House Cup Mondays 10:10 in 📣・announcements (100 coins to each member of the winning house with 10+ points, once 2 qualify); a season trophy each calendar quarter |
| Welcome Crew | /crew with Join/Leave buttons (accounts 30+ days old, 14 days here, no moderation case in 30 days; a new case takes the role back); one crew ping in 💬・general per 30 min for first-time joins (CREW_PING_MINUTES; someone who left and came back is not announced again), never 22:00 to 09:00 (CREW_QUIET_HOURS), during a raid or with no crew member online; 40 coins when a newcomer answers you (once per newcomer, 3 paid a day), the Welcoming Committee badg |
