Repository navigation
Make the bot self-hostable with env config and no external data sharing - #1
Conversation
Prepare the fork to run in the Appwrite Discord on a VPS without Docker. Data and privacy: - Remove the hardcoded feedback channel in the upstream author's server. Detection feedback and !spamreport copies now go to FEEDBACK_CHANNEL_ID, which is off when unset. - Remove local analytics and the /spam command. Configuration: - Move every setting to environment variables (src/config.js), validated at startup. Remove /setup and its JSON settings file; the bot keeps no state. - Require MODERATION_CHANNEL_ID. Check at startup that the bot can post in the alert channels, and only moderate the server that owns the moderation channel. Drop the public "Message deleted" fallback notice. - Warn at startup about EXCLUDED_ROLE_IDS that do not exist. Detection: - Keep only the hiring/recruitment text rule (TEXT_SCAM_ENABLED) and tighten it so help requests and event invites are not flagged. Remove the identity, remote-career, and giveaway rules. - Allow the word "NSFW"; remove porn-related wording instead. - Gate known spammer IDs behind SPAM_MESSAGES_ENABLED. - Remove Appwrite's audit channel from scam-image-channels.json. - Put !spamreport behind SPAM_REPORT_ENABLED (off) with a lookback window. Fixes: - Stop the NSFW invite check from recursing forever on invites to servers with a welcome screen, which skipped all later checks for the message. - Act once on a message when Discord sends an update (link preview) while it is still being handled, instead of alerting twice. - Show the real timeout result in raid alerts. - Disable the ONNX Runtime memory arena: about 1.6 GB instead of 3.1 GB of memory after 160 images, at the same speed and accuracy. Removed: Anti-Nova & VoidBox, the easter egg, Docker files, the GHCR deploy workflow (replaced by a test-only workflow), and the upstream legal pages. The README now covers setup, every setting, and running under systemd with a restart limit that protects the bot token.
🔵 Tier A · Mergeable after minor fixes
Makes the bot self-hostable with validated environment configuration, optional feedback routing, and moderation restricted to one configured server. Removes upstream data sharing and obsolete features, narrows scam detection, bounds manual reports, and fixes invite recursion and duplicate moderation actions. Adds automated VPS installation and updates, a systemd service, revised operating instructions, and shell-script checks in CI. Latest changes: The newest commits add root-run install and update scripts with a templated systemd service, rewrite the README around that deployment workflow, and add ShellCheck to CI.
Fix with agent prompt### Issue 1
deploy/spam-guard-bot.service:14-17
**Run the bot service under a dedicated unprivileged user**
Without `User=`, this system service runs as root, including the native image decoders processing attachments from Discord users; `NoNewPrivileges` and `ProtectSystem=full` do not remove its existing root privileges. Please provision a dedicated service user in the installer and give it access to the token, generated manifest, and OCR cache so a compromised bot process does not also compromise the VPS as root.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.📂 Walkthrough · 23
Reviewed the commits since |
…issions
- Only allow role qualifiers ("a senior full-stack", "2 React Native")
between "looking for" and a role in the recruitment rule. Help requests
such as "looking for help from a developer ... DM me" no longer match.
- Check each alert channel for the permissions it needs at startup: Attach
Files for the feedback channel, which receives image copies, and Send
Messages in Threads when a channel is a thread. The error lists every
missing permission.
- deploy/spam-guard-bot.service: the service file, kept in the repo. - scripts/install.sh: installs Node.js 22 and pnpm if missing, installs dependencies, creates .env and asks for the token (hidden), then installs and starts the service. Safe to rerun; clears the restart limit. - scripts/update.sh: pulls the latest code and reruns the installer. - README: install is now clone plus one command; adds everyday commands. - CI: shellcheck the scripts. Tested in a fresh Ubuntu 24.04 container: fresh install, hidden token prompt, rerun, update, systemd-analyze verify, OCR, and bot startup.
| [Service] | ||
| WorkingDirectory=@BOT_DIR@ | ||
| ExecStartPre=@NODE@ scripts/build-visual-reference-manifest.mjs | ||
| ExecStart=@NODE@ src/index.js |
There was a problem hiding this comment.
Run the bot service under a dedicated unprivileged user
Without User=, this system service runs as root, including the native image decoders processing attachments from Discord users; NoNewPrivileges and ProtectSystem=full do not remove its existing root privileges. Please provision a dedicated service user in the installer and give it access to the token, generated manifest, and OCR cache so a compromised bot process does not also compromise the VPS as root.
Prompt To Fix With AI
This is a comment left during a code review.
Path: deploy/spam-guard-bot.service
Line: 14-17
Comment:
**Run the bot service under a dedicated unprivileged user**
Without `User=`, this system service runs as root, including the native image decoders processing attachments from Discord users; `NoNewPrivileges` and `ProtectSystem=full` do not remove its existing root privileges. Please provision a dedicated service user in the installer and give it access to the token, generated manifest, and OCR cache so a compromised bot process does not also compromise the VPS as root.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.🟡 Minor · security · Reply if this doesn't apply.
Summary
Prepares this fork of DH-555/spam-guard-bot to run in the Appwrite Discord on a VPS, without Docker. All settings now come from environment variables, the bot no longer sends anything to the upstream author's server, and detection is tuned for a developer community.
Why
An audit of the fork found:
!spamreportcopies (message text, images, user IDs) were sent to a channel hardcoded to the upstream author's Discord server. This was on by default.!spamreportscan that paged through every channel's entire history.Changes
Data and configuration
FEEDBACK_CHANNEL_ID, which is off when unset./spamcommand..env.exampleand the README document each one./setupand itsdata/settings.jsonstore. The bot keeps no state on disk apart from the OCR model cache.MODERATION_CHANNEL_IDis required. At startup the bot checks it can post in the alert channels and exits if not. It only moderates the server that owns that channel. The public "Message deleted: @user" fallback notice is removed.EXCLUDED_ROLE_IDSrole by name and warns about IDs that don't exist in the server.Detection
TEXT_SCAM_ENABLED). It now needs real hiring intent ("hiring", "looking for a developer", "join our team"), a tech role, and a contact request. Bare "looking for" and "join" no longer count. The identity, remote-career, and giveaway rules are removed.spam-users.jsonnow followSPAM_MESSAGES_ENABLED; before, they applied even with spam protection off.740463504602955806fromscam-image-channels.json. Upstream had listed it as a scam-image source, but it is Appwrite's audit channel. Tests use a made-up ID.!spamreport: behindSPAM_REPORT_ENABLED(off by default), withSPAM_REPORT_LOOKBACK_MINUTES(default 60) capping the scan. Replies are in English.ANTI_RAID_LEVELdefaults tomedium..env.exampleships with the two team role IDs, the audit channel, and a 60-minute launch timeout.Fixes
Removed
Anti-Nova & VoidBox, the easter egg,
Dockerfileand compose files, the GHCR deploy workflow (replaced by a test-only workflow), and the upstream privacy and terms pages.Testing
pnpm test: 105 tests pass. There are new tests for config parsing, the hiring rule (7 ads caught, 8 normal messages allowed), porn wording, the update race, welcome-screen invites,!spamreportgating and lookback, and the feedback channel.Before launch
Each step is in the README:
.env.exampleto.envon the server and addDISCORD_TOKEN.Open question
The lists inherited from upstream still need a decision:
spam-users.json(5 user IDs),malicious-servers.json(13 server IDs, 2 no longer exist), andzangi.cominblocked-domains.json, which is a real messaging app.