Skip to content
arduinodiscordPublic

About

The new and improved custom discord bot to power the official Arduino discord server @ https://arduino.cc/discord

Resources

Stars

3 stars

Watchers

2 watching

Forks

Repository files navigation

🤖 Arduino Discord Bot

The community bot powering the official Arduino Discord

Helpful tags, image-spam moderation, and server automation — built for the people who keep the server running.

License: GPL-3.0 discord.js Sapphire TypeScript Node


✨ Highlights

🏷️ Slash-command tags Curated troubleshooting guides (/tag) — AVRDUDE errors, level shifters, powering boards, and more
🛡️ Image-spam automod Catches the image-cluster spam wave with burst + cross-channel fan-out + perceptual-hash detection, and a one-click moderator console
🧰 Server automation Auto-crosspost, join/leave + invite-source logging, role-select buttons, message-link quoting
🗣️ Staff tooling /say to broadcast rich embeds as the bot
🐳 One-command deploy docker compose up — bot + Postgres, migrations applied automatically

🚀 Quick start

With Docker (recommended)

git clone https://github.com/arduinodiscord/bot.git
cd bot
cp .env.example .env          # add your BOT_TOKEN (+ any feature channel IDs)
docker compose up -d --build  # builds the bot, starts Postgres, applies migrations

That's it — the bot connects, registers its slash commands, and is ready.

Local development

npm install
cp .env.example .env          # set BOT_TOKEN
npm run dev                   # rebuilds on change (tsc -w) and restarts the bot (node --watch)

Privileged intents: enable Message Content and Server Members for your application in the Discord Developer Portal. Invite it with the permission set in SETUP.md (section 1), which includes a ready-made invite URL.


⚙️ Configuration

Everything is driven by environment variables (see .env.example). Only BOT_TOKEN is required; every other feature self-disables until you give it a channel or role id, so you can adopt them one at a time.

Variable Purpose
BOT_TOKEN Required. Your Discord bot token
MOD_LOG_CHANNEL_ID Enables the image-spam automod console
DATABASE_URL Postgres connection (optional — runs in-memory without it; wired automatically by Docker)
JOIN_LEAVE_LOG_CHANNEL_ID Member join/leave + invite-source logging
CROSSPOST_CHANNEL_IDS · CROSSPOST_LOG_CHANNEL_ID Auto-publish announcement channels
ROLE_SELECT_MESSAGE_ID · EVENT_NOTIFS_ROLE_ID · SERVER_UPDATE_NOTIFS_ROLE_ID Button-based opt-in roles
AUTOMOD_* Detector thresholds & timeouts (sensible defaults; see .env.example)

💬 Commands

All commands are Discord slash commands — type / in the server.

Command Description
/tag name:<tag> [user:@user] Post a curated troubleshooting guide, optionally pinging someone
/solved [helper:@user] Mark the current help post solved (and thank a helper); closes the thread
/openposts List open help posts waiting for an answer, oldest first (ephemeral)
/about Bot, Node, and version info
/ping Latency & uptime
/say channel:<#ch> title:… description:… Staff only — send a custom embed (optional fields as name | value per line, and thumbnail)

Plus a "Request more info" right-click (message context-menu) action that posts the needinfo checklist to an asker in one click.

📚 Available /tag topics

ai · ask · avrdude · codeblock · debounce · espcomm · help · hid · lab · language · levelShifter · libmissing · needinfo · ninevolt · power · pullup · reinstall · wiki


🛡️ Image-spam automod

A wave of spam — both freshly-joined accounts and compromised long-time members — posts clusters of images to advertise. This bot targets exactly that pattern without ever disabling image sharing server-wide.

Detection

  • Burst — several image messages from one user in a short window (stricter for new members) → alerts mods.
  • Cross-channel fan-out — the same image across multiple channels → high confidence (this is what catches compromised veterans, where account age tells you nothing).
  • Known-spam blocklist — once a mod confirms an alert, that image is fingerprinted and future copies are caught instantly.

Each image gets a cheap metadata signature (catches identical re-uploads) and a perceptual hash (dHash from a tiny thumbnail — catches re-encoded / resized copies via Hamming distance).

Response (tiered): high-confidence hits auto-delete + timeout and then alert; bursts only alert. Every alert lands in the mod-log channel with Confirm / Timeout / Ban / Delete / Not spam buttons — a human stays in the loop. Members with Manage Messages (or a configured immune role) are never inspected.

Complements your existing YAGPDB AutoMod rather than replacing them.


🌊 Text-flooding automod

Some users fragment a single thought across a stream of one-word messages instead of sending it as one message — which buries ongoing conversation and leaves no room to reply. The bot flags this pattern: N short messages from one user inside a short window (AUTOMOD_FLOOD_*, defaults: 5 messages ≤ 25 chars in 15 s) lands a Message flooding alert in the same mod-log console as the image automod, with the same Confirm / Timeout / Ban / Delete / Not-spam buttons. By default it only alerts (flooding is usually a habit, not an attack); set AUTOMOD_FLOOD_AUTO_TIMEOUT=true to also time the user out automatically. Members with Manage Messages (or a configured immune role) are never flagged.


📡 Cross-channel question-spam automod

New members often fire the same question into every channel at once instead of the one that fits. Two tenure-aware signals catch this (AUTOMOD_CROSSPOST_*):

  • Near-identical fan-out (any tenure) — the same question (token-overlap ≥ SIMILARITY_PCT, default 80%) across CHANNELS+ channels (default 2) inside the window. High confidence, so the bot auto-deletes the duplicate copies, keeping the first, then alerts.
  • New-member spread (recent joiners only) — posting substantive messages in SPREAD_CHANNELS+ channels (default 3) at once, even when reworded enough to dodge the similarity check. Alerts only — these aren't strict duplicates.

Both surface in the same mod-log console with the usual action buttons, reuse the existing new-member window, and respect the immune-role/permission checks. Short greetings and reactions (below MIN_CHARS) are ignored.


🧰 Server management

Ambient helpers, each self-disabling until configured:

  • Auto-crosspost announcement channels, with logging.
  • Join/leave logging with invite-source attribution (+ optional analytics when a DB is present).
  • Role-select buttons to toggle event / server-update opt-in roles.
  • Message-link flattening — quote a linked message inline for context.

🙌 Helper assist

Features aimed at taking repetitive load off the community members who answer the most questions:

  • Keyword → tag suggestions — when a message matches a known signature (AVRDUDE, missing-library, ESP upload, level shifters, power, pull-ups, 9V, HID, debounce…), the bot offers the matching tag via a single button, so askers self-serve before a helper repeats a canned answer. Each trigger lives next to its tag in tags.ts. Per-user cooldown; toggle with TAG_SUGGEST_ENABLED. Only fires for members with no server role — helpers, knowledgeable members, and any other role-holder are never shown suggestions.
  • Unformatted-code nudge — detects code pasted as plain text and offers the codeblock tag, the single most-repeated ask. Toggle CODE_FORMAT_SUGGEST_ENABLED. Subject to the same role exemption as keyword suggestions.
  • "Just ask" nudge — replies to low-effort pings ("can I ask?", "anyone here?") with the ask tag. Conservative patterns; off by default, enable with ASK_SUGGEST_ENABLED=true. Subject to the same role exemption as keyword suggestions.
  • Suggestion ignore list — set SUGGEST_IGNORE_CHANNEL_IDS to suppress all three suggestions in specific channels (e.g. staff channels). Listing a forum channel's ID silences all its threads.
  • "Request more info" context-menu — right-click any message → Request more info to post the needinfo checklist to the asker in one click. Helpers (REQUEST_INFO_ROLE_IDS) and staff only.
  • Auto-needinfo on thin posts — when a new help thread lacks substance (no code, image, URL, or inline code, and the post title + body together are under HELP_NEEDINFO_MIN_CHARS characters), the bot sends a concise checklist and the Mark Solved button in a single message. Off by default (HELP_AUTO_NEEDINFO=true to enable). The full /tag needinfo checklist used by helpers is unaffected.
  • Solve workflow — a Mark Solved button on new help threads (set HELP_CHANNEL_IDS) plus /solved [helper:@user], which closes the post and credits whoever helped.
  • Stale-post nudge & auto-archive — abandoned help posts get a "still need help?" nudge after a few days, then auto-archive only if no one replies for a while longer (defaults 3 then 7 days — helpers often take a while; tune with HELP_STALE_NUDGE_HOURS / HELP_STALE_ARCHIVE_HOURS).
  • /openposts — an ephemeral digest of open help posts, oldest-waiting first, so helpers can pick up whatever's been waiting longest.

Help channels can be forum or text channels. List both kinds in HELP_CHANNEL_IDS — forum posts and threads opened inside text help channels get the same Mark-Solved / needinfo / stale-sweep / /openposts treatment. The keyword/code/ask suggestions and Request more info work server-wide regardless of channel type (subject to role exemptions and ignore lists). Plain, thread-less messages in a text channel can't be archived, so the thread lifecycle simply doesn't apply to them.

🏗️ Project structure

src/
├── index.ts                 # client bootstrap (intents, presence, login)
├── commands/                # slash commands (about, ping, tag, say, solved, openposts)
├── listeners/               # gateway events (ready, messages, members, invites…)
├── interaction-handlers/    # button handlers (spam console, role-select, tag buttons)
└── utils/
    ├── automod/             # spam detection: tracker, flood, crosspost, phash, blocklist, console, incidents
    ├── config.ts            # env-driven configuration
    ├── db.ts                # optional Prisma (pg driver adapter) with in-memory fallback
    ├── tags.ts              # tag content + schema
    └── embed.ts             # shared base embed
prisma/                      # schema + migrations

Stack: discord.js v14 · Sapphire framework · TypeScript 6 · Prisma 7 (+ Postgres, optional).


🤝 Contributing

Want to add a tag or a feature? PRs welcome!

  1. Fork & branch.
  2. Add a tag: edit src/utils/tags.ts and add it to the .addChoices() list in src/commands/tag.ts.
  3. npm run build to type-check.
  4. Open a PR against staging with a clear description.

Found a bug or have an idea? Open an issue.


License: GPL-3.0-or-later · Made with ❤️ by the Arduino Discord community

About

The new and improved custom discord bot to power the official Arduino discord server @ https://arduino.cc/discord

Resources

Stars

3 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages