v0.7 · Eternal Ronin

CyberPUP
Security Docs

Complete reference for CyberPUP's two-bot Telegram moderation stack — the ML-powered Security bot and the full-feature Core moderation bot. Built by Frisky Dev for the Haus of Howl community.

aiogram 3.29 Pyrogram ML NSFW Detection Reputation System Auto-Ban MongoDB Frisky Dev

🐾What is CyberPUP?

CyberPUP is Frisky Dev's Telegram group protection stack — two complementary bots that together handle automated moderation, ML-based content screening, reputation tracking, and community management for the Haus of Howl community and partner groups.

🛡 CyberPUP Security
The primary sentinel. Runs on aiogram 3.29, powered by machine learning for NSFW and spam detection. Tracks per-user reputation, enforces content thresholds, and operates silently unless a violation is detected. Every moderation action flows to a configured log channel.
⚙️ CyberPUP Core
The moderation workbench. Built on Pyrogram + wbb framework. Provides the command interface admins use daily — ban/kick/warn/mute, blacklists, keyword filters, cross-group federations, global bans, notes, and the /setlog log channel. Think of it as the tool belt that Security's log channel receives events into.
ℹ️ Both bots run on the same DigitalOcean droplet (64.23.130.34) inside separate Docker containers: cyberpup-security and cyberpup-core.

🏗Architecture

Two independent processes, one droplet, shared logging destination.

CyberPUP Security
Framework: aiogram 3.29 (async)
DB: SQLite (aiosqlite) / PostgreSQL via DB_URL
ML: Image classifier (NSFW), text spam model
Config: config.toml + environment variables
Deployment: Docker, /opt/cyberpup-security
Health: HTTP healthcheck on port 8080
CyberPUP Core
Framework: Pyrogram 2.x + wbb
DB: MongoDB (claw-mongo motor async)
Modules: ~50 auto-discovered Python modules
Config: Environment variables + MongoDB
Deployment: Docker, /opt/cyberpup-core
Hot-patch: docker cp + restart (no rebuild)
Privacy Mode must be OFF for Security to read all group messages. BotFather → /mybots → Bot Settings → Group Privacy → Turn off → remove + re-add bot to each group.

📊Reputation System

CyberPUP Security tracks every member's message count and reputation points. Clean messages earn points; violations cost them. Thresholds gate which actions the bot applies to each user.

Member Levels

Level is determined by total message count. Admins and the group owner are always exempt from automated restrictions regardless of level.

L0
< 100 msgs
Noname
All content restrictions active
L1
100 – 499
Newbie
Gaining baseline trust
L2
500 – 999
Experienced
Most spam filters relax
L3
1 000 – 1 999
Professional
Trusted community member
L4
2 000 – 2 999
Veteran
Core contributor
L5
3 000 – 4 999
Master
Community pillar
L6
5 000+
Legend
No restrictions

Reputation Thresholds

These rep-point thresholds control which automatic restrictions apply. All values are configurable in config.toml under [spam].

rep 20
Media restriction lifted — photos, videos, documents allowed
rep 30
Forwards allowed (non-channel external forwards)
rep 30
Single-emoji spam check active below this
rep 50
Link spam filter active below this (URLs, t.me invites)
rep 50
Cross-chat replies (Reply in Another Chat) blocked below this
rep 50
NSFW profile + in-chat image checks active below this
rep 50
Early comment filter: first 30s of a new channel post

Reputation Labels

< -2000
🚨 Wanted
-2000 to -1000
⚠️ Dangerous
-1000 to -500
🌑 Shady
-500 to 0
📌 Violator
0 to 100
⬜ Neutral
100 to 500
🔵 Good Standing
500 to 1000
🟢 Very Good
1000+
⭐ Generous

Rep Changes

+1 Reputation
Every clean message that passes all filters earns +1 rep. This is the primary way members build trust. Spam, profanity, and link violations prevent this from accumulating.
Rep Losses
Profanity: -20
Link spam: -10
Forward violation: -10
Cross-chat reply: -10
Location blocked: -10
Voice messages: -10
Invisible spacing: -10
Single emoji: -5
CJK spam: -5
ML spam: -5
Promo violation: -5

🔍Spam Detections

Security's message pipeline runs a cascade of checks in order. Admins and group owners bypass every check. Trusted users (100+ clean messages, high rep) skip the expensive ML check.

Pipeline order matters. Each check can short-circuit the rest. The ML spam model only runs when earlier checks pass AND the user isn't trusted.

Active Content Checks

CheckConditionActionRep Penalty
CJK Spam Message contains Chinese characters (CJK Unified Ideographs) Delete, log to channel -5
Invisible Spacing Hangul filler chars (U+115F, U+1160, U+3164, U+FFA0) present; user rep < 50 Delete, log to channel -10
Profanity Built-in bad-word list matches any member regardless of rep Delete, log to channel -20
Single Emoji Message is exactly one emoji; user rep < 30 Delete, log to channel -5
Link Spam Message has URL or text_link entity; user rep < 50; not a promo slot Delete, log to channel -10
ML Spam Model Text classified as spam; user not trusted; low message count or low rep Delete (no log entry by itself) -5

Message Type Checks

Content TypeRuleAction
Media (photos, videos, documents, audio, video notes, animations) User rep < 20 Delete silently
Forwards (external, non-auto-forward) User rep < 30 AND not a channel forward Delete, log Forward Filter, -10
Channel Forwards (external channels) Counted as promo — see Promo Guard section Promo Guard flow
Cross-chat replies ("Reply in Another Chat") User rep < 50 Delete, log Cross-Chat Reply Filter, -10
Voice messages 75% probability trigger (all users) Reply discouraging message, -10
Contacts Sender is not admin Delete silently
Locations Sender rep < 50 Delete, log Location Filter, -10
Early comments Reply to linked channel post within 30s; user rep < 50 Delete, log Early Comment Filter
Join notifications All NEW_CHAT_MEMBERS service messages Delete; log member joined
🔗 Linked channel auto-forwards and forwards from current group members always pass — only truly external sources are filtered.

🔞NSFW Detection

CyberPUP Security runs an ML image classifier on photos from low-rep users. Both in-chat images and profile photos are checked. Results are cached to avoid re-downloading the same file.

⚠️ Detection requires companion signals. A single elevated category alone won't trigger — the model produces false positives on clean anime art and attractive-but-safe photos. Confirmation requires corroborating categories to exceed multiple thresholds simultaneously.

Classification Categories

Normal
Clean content — when high Normal score + low explicit, safe classification
Enticing / Sensual
Suggestive but not explicit. Triggers with supporting Porn score.
Pornography
Explicit content. Strong alone (>0.85) or combined with Sensual (>0.15 each).
Hentai
Animated NSFW. Requires Porn or Sensual companion to avoid FP on clean anime.
Anime
Non-NSFW animated. High Anime + low explicit = safe path.

What Gets Checked

In-Chat Photos
Any photo posted by a user with rep < 50. Result is cached per file_unique_id to avoid repeated downloads. If NSFW: message deleted, review sent to log channel with action buttons.
Profile Photos
Checked when a low-rep user sends any message, subject to a per-user hourly cooldown (3600s). Also checks the display name for violations (e.g., "check my profile" names).

Log Channel Review

When NSFW is detected, two action buttons appear in the log channel:

🚫 Confirm NSFW + Block Account
Bans the user from the group. One-click from the log channel — no need to return to the group.
✅ Mark Safe
Dismisses the review. The user's content remains and no action is taken.

📢Promo Guard

Each user gets one promotional post per 24-hour rolling window per group. Admins and owners are always exempt. Reposts of the same content (same URL or same channel) reuse the existing slot without consuming a new one.

What Counts as a Promo?

Forwarded from external channel Message contains URL entity Message contains text_link entity

Regular messages, stickers, voice notes, photos without links, and forwards from linked channels are not counted as promos.

How the Limit Works

User sends a promotional message (link or channel forward). Bot identifies the content key: for channel forwards, the channel's ID; for links, the normalized URL (scheme/www/query stripped).
Bot checks if a slot for that key exists in the past 24 hours. Same content? Slot refreshed — the post goes through silently. User can repost their own promo freely.
Different content + slot available: new slot consumed, message passes.
Over limit: message deleted. If no warning sent in the past 24h, bot replies: "You've used your daily promo slot for this group. Come back tomorrow!"
💾 Storage: Promo records persist in MongoDB (wbb.promo_limits collection). If MongoDB is unavailable, an in-memory fallback is used (lost on restart). The limit defaults to 1/day and is configurable via PROMO_LIMIT_PER_DAY or config.toml [promo].

Configuration

KeyDefaultDescription
PROMO_ENABLEDtrueEnable/disable promo guard entirely
PROMO_LIMIT_PER_DAY1Max promo slots per user per group per 24h

🚫Auto-Ban

When a user accumulates enough spam violations while maintaining low reputation, CyberPUP Security automatically bans them without waiting for manual action.

≥ 100
Total spam violations (all types combined)
< 100
Reputation points at time of trigger

Both conditions must be true simultaneously. A user with 200 violations but high reputation won't be auto-banned. A user with 50 violations and low rep also won't. Only when violations cross 100 AND rep is below 100 does the ban fire.

🚫 What triggers violation count: CJK spam, invisible spacing, link spam, cross-chat replies, promo violations, ML spam, shared location. Each adds 1 to violations_count_spam.
🔧 Auto-ban can be disabled: set autoban_enabled = false in [spam] in config.toml, or set AUTOBAN_ENABLED=false env var.

⌨️Security Commands

CyberPUP Security uses a ! prefix for owner commands and / prefix for member commands. All owner commands require the sender to be configured as an owner in config.toml or BOT_OWNER env var.

Member Commands (in main groups)

CommandDescription
/me /info /лвл /whoami /neofetch /fastfetchShow your member signal snapshot — level, reputation, message count. Reply to another member to see their info. Throttled: once per minute per user per group.
/rules /правилаShow the group rules message. Throttled: once per minute per group (all aliases share the throttle).
!бу /буFun command — the bot pretends to be startled. Random response from the bot's "bu-responses" string set.

Owner Commands (in main groups)

CommandUsageDescription
!spamReply to a messageFlag message as spam. Deletes it (if user isn't admin), creates DB record, sends to log channel with ban / mark-safe / cleanup buttons.
!setlvl!setlvl 500 (reply)Override a member's message count and boost their reputation by the same value. Useful for manually promoting trusted members.
!reward!reward 100 (reply)Award N reputation points to the replied member. Max 100 000 per call.
!punish!punish 50 (reply)Remove N reputation points from the replied member. Max 100 000 per call.
!rresetReply to a messageReset a member's reputation to equal their message count (clean slate).

Owner System Commands

CommandUsageDescription
!setmain!setmain -100xxxxSet authorized main group(s) — comma-separated IDs. Only groups in this list will be moderated.
!setreports!setreports -100xxxxSet the channel where reported messages are sent.
!setlogs!setlogs -100xxxxSet the channel where moderation events are logged.
!pingIn any authorized groupConfirm the bot is monitoring this group.
!menuDM or any groupOpen the inline navigation menu (Status / Moderation / Reports / Help / Configure).

Auto-Activation

🔓 When the owner adds the bot to a group, it auto-activates immediately: the group is appended to groups.main, persisted to MongoDB, and a confirmation panel is posted. No !setmain needed. If added by anyone else, the bot stays silent and posts a message explaining how the owner can activate it.

🛠Admin Commands (Core)

CyberPUP Core provides the full moderation toolkit. Commands require the calling user to have the relevant admin permission in the group (e.g., can_restrict_members for bans). The Sudoers list can bypass permission checks.

Ban & Kick

CommandDescription
/banRemove a user from the chat permanently.
/dbanDelete the replied message and ban its sender.
/tbanBan a user for a limited time (e.g., /tban 2h).
/unbanRestore a banned user's access.
/listbanBan a user across all chats listed in a replied message.
/listunbanRemove a cross-chat ban applied by /listban.
/kickRemove a user and allow them to rejoin.
/dkickDelete the replied message and kick its sender.
/ban_ghostsRemove all deleted Telegram accounts from the chat.

Warn System

CommandDescription
/warnAdd a warning to a user. Reply to their message.
/dwarnDelete the replied message and warn its sender.
/warnsShow a user's current warning count.
/rmwarnsClear all warnings for a user.

Mute

CommandDescription
/mutePut a user in read-only mode.
/tmuteMute a user for a limited time (e.g., /tmute 30m).
/unmuteRestore sending access to a muted user.

Messages & Promotion

CommandDescription
/delDelete the replied message.
/purgeClear messages from the replied point upward.
/purge NClear exactly N messages from the replied point.
/promotePromote a member to admin with standard rights.
/fullpromotePromote a member with every available admin right.
/demoteRemove admin rights from a member.
/pinPin or unpin the replied message.
/inviteShare the group or supergroup invite link.
/report @admins @adminSignal a message to the admin team.

📋Blacklist & Filters

CyberPUP Core separates two concepts: the Blacklist (explicit phrases that trigger enforcement) and Filters (keyword-triggered bot responses). Both are per-chat.

Important: Automatic profanity detection is OFF in Core. The blacklist ships with zero built-in words. Only phrases you explicitly add with /blacklist are enforced. General cursing passes freely.

Blacklist Commands

CommandDescription
/blacklist [WORD|PHRASE]Add a phrase to this chat's blacklist. Requires can_change_info admin right.
/whitelist [WORD|PHRASE]Remove a phrase from the blacklist.
/blacklistedList all blacklisted phrases. Large lists are paginated; very large lists sent as a text file to avoid flooding.
🔒 Owner ID 8581086019 is hard-coded as a never-restrict user — the blacklist enforcer will never delete their messages regardless of content.

Filters Commands

Filters let you save a Telegram message (text, photo, video, audio, animation, etc.) as a trigger. When any member sends a message matching the filter name, the bot responds with the saved content.

CommandDescription
/filter [NAME]Save a filter, replying to the message you want as the response. Use underscores for multi-word names: /filter Hey_there.
/stop [NAME]Remove one filter.
/stopallPermanently remove all filters in this chat.
/filtersList all saved filters in this chat.

🌐Federations

Federations let you synchronize bans across multiple groups. A spammer banned in one group can be auto-banned in all others in the same federation. Federation admins can ban across all member groups without being an admin in each one individually.

ℹ️ Federation management commands are documented in the Core bot's /feds help. Creating a federation, inviting groups, and designating fed admins requires the Sudoers list or fed ownership. Support channel: Frisky Signal.

📡/setlog — Log Channels (Dual-Emit)

CyberPUP uses a dual-channel log architecture — every moderation event is sent to two destinations simultaneously:

  • INDIVIDUAL — the group's own private log channel (set with /setlog from inside that group). Only events from that specific group land here.
  • GENERAL — a master aggregate feed shared by all groups (set with /setlog from DM). Every group's events arrive here, giving a unified view across the network.

If a group has no INDIVIDUAL channel set, its events still appear in GENERAL — nothing goes dark. If INDIVIDUAL == GENERAL for a group, the message is sent only once (de-duped). Three privilege tiers can run /setlog, each with different scope.

Access Tiers

Owner
Full access — all subcommands (off, no-arg status, @handle, numeric ID), any context (DM, group, inside channel).
Sudoers
Same as owner — full access to all subcommands and contexts.
Group Admin
May run /setlog -100xxxx, /setlog @handle, or bare /setlog (forward mode) from within a group they administer — sets the INDIVIDUAL channel for that group only. May also use /setlog off to clear the INDIVIDUAL mapping (group still appears in GENERAL). Cannot change the GENERAL channel. Cannot run from a group where they are not an admin — no cross-group leakage.
🔒 Non-authorized users are silently ignored — the handler returns without any reply to avoid command enumeration.

Usage Reference

SyntaxWhoDescription
/setlog (bare, in group) ★ INDIVIDUALOwner / Sudoer / Group AdminActivates forward mode (5 min TTL) — sets the INDIVIDUAL channel for this group. Forward any post from the target log channel. Events from this group will go to BOTH this channel AND the GENERAL channel. Other groups are unaffected. Security reads the mapping at event time (instant, no delay).
/setlog (bare, in DM) ★ GENERALOwner / Sudoer onlyActivates forward mode (5 min TTL) — sets the GENERAL (aggregate) channel. All groups' events land here regardless of their INDIVIDUAL setting. Also writes to Security's MongoDB — Security applies within ~30 s, no restart needed.
/setlog -100xxxxOwner / Sudoer / Group AdminSet by numeric ID. In a group → sets INDIVIDUAL for that group. In DM → sets GENERAL. Group admins must send from within their group.
/setlog @channelnameOwner / Sudoer / Group AdminSet by @username. Same INDIVIDUAL vs. GENERAL routing as above. Bot must already be a member of the target channel.
/setlog (inside channel)Owner / Sudoer onlyAuto-register this channel as the GENERAL aggregate log channel. Bot probes and sets immediately.
/setlog offOwner / Sudoer / Group AdminIn a group → clears the INDIVIDUAL mapping (group still appears in GENERAL). In DM → disables the GENERAL channel for Core and Security (Owner/Sudoer only).
/logstatusOwner / Sudoer / Group AdminReports both channels: this group's INDIVIDUAL (if set) and the GENERAL aggregate channel — for both Core and Security — in one reply.
Dual-emit: every moderation event is sent to both channels. INDIVIDUAL (wbb.log_channels._id=<group_id>) is written by /setlog in a group — Core caches it in-memory, Security queries it at event time (no delay, no restart). GENERAL (core_settings.log_group_id / security_settings.groups_logs) is written by /setlog in DM — Security applies within ~30 s. If a group has no INDIVIDUAL set, only GENERAL is emitted (cutover-safe). If INDIVIDUAL == GENERAL, the event is sent exactly once. Run /logstatus in a group to see both layers.

How it Works

Context decision: When /setlog is invoked, the handler checks message.chat.type. If it's a group/supergroup → INDIVIDUAL path (stores source_group_id in the pending dict). If it's DM or channel → GENERAL path (source_group_id = None).
Forward mode: Bare /setlog enters forward mode (5 min TTL). _pending_setlog[user_id] stores (expiry, source_group_id_or_None). When a forwarded channel post arrives, the handler checks the stored context to determine INDIVIDUAL vs. GENERAL write.
Explicit path: /setlog -100xxxx or /setlog @handle resolves the channel and calls _apply_log_channel(log_channel_id, ..., source_group_id=<int|None>).
Bot sends a probe message (immediately deleted) to verify it can post. If the bot lacks Post Messages permission, the command fails with a specific error — no partial state is saved.
INDIVIDUAL write: Calls wbb.utils.log_channels.set_log_channel(source_group_id, log_channel_id). Upserts wbb.log_channels._id=source_group_id in MongoDB and updates in-memory cache. At log time, Core calls get_log_destinations(chat_id) which returns [individual, general] deduped — both messages are sent in a loop. Security calls get_group_log_channel(chat_id) on the same collection and dedupes vs. config.groups.logs inline.
GENERAL write: Writes to core_settings.log_group_id and patches every loaded wbb submodule via sys.modules iteration. Also writes security_settings.groups_logs — Security's config-reload loop applies within ~30 s. Groups with no INDIVIDUAL set emit to GENERAL only (cutover-safe, nothing goes dark).
🔧 If the probe fails (bot lacks Post Messages permission), the command reports the specific reason and doesn't save — fix permissions first, then retry.
✏️ Input normalization: The channel argument is auto-normalized before lookup — @@channelname@channelname, and a bare channelname (no @) → @channelname. Double-@ typos (a known UX trap when copy-pasting) no longer cause "Can't find @@…" errors. Error messages now distinguish two failure modes: "username doesn't exist" (UsernameNotOccupied) vs. "bot is not yet a member" (PeerIdInvalid).

👑Sudoers (Global Ops)

The Sudoers list in CyberPUP Core is the highest privilege tier. Sudoers can execute global operations across all groups the bot serves.

CommandDescription
/gban [user] [reason]Ban a user across every served group. Requires a reason. Sends the user a DM explaining the ban.
/gunban [user]Reverse a global ban, removing the ban across all served groups.
/statsLive system stats: uptime, CPU, RAM, disk, bot process memory.
/gstatsNetwork-wide stats: total served chats, served users, global ban count.
/broadcastSend a replied message to all served groups.
/ubroadcastSend a replied message to all served users.
/setlogs [id]Set the primary log group ID (Sudoers version — integer-only).
/setgbanlogs [id]Set the global ban log group.
/setdump [id]Set the message dump chat.
/evalExecute Python code in the bot's process.
/shExecute shell commands on the host.
/updatePull latest changes and restart.
/restartRestart the bot.
/clean_dbRemove stale served-chat records from the database.
/servedchatsList every group and channel the bot is serving, with chat IDs and type. Sent as a .txt file if > 60 lines. Sudoers + owner only.
⚠️ /eval and /sh provide full process-level access. Only trust users in the Sudoers list. Adding someone to Sudoers grants them the same capability as the bot owner.

🚀Initial Setup

Both bots run as Docker containers on a DigitalOcean droplet. Here's how each is brought live for a new group.

CyberPUP Security — New Group

Add the bot to the group. If the adder is the configured owner, the group auto-activates and a confirmation panel appears. Otherwise, the bot posts the group ID and tells you to run !setmain.
Make the bot admin with Delete Messages + Ban Users rights. Without these, it can observe but can't remove content or ban users.
Turn off Privacy Mode in BotFather: /mybots → your bot → Bot Settings → Group Privacy → Turn off. Then remove and re-add the bot to the group.
Run !ping in the group to confirm monitoring is active.
Optionally run !setreports -100xxxx and !setlogs -100xxxx to point the reports and log channels.

CyberPUP Core — New Group

Add CyberPUP Core to the group and promote it to admin with Delete + Ban + Restrict rights.
Core auto-discovers the group — it begins tracking all served chats in MongoDB. No activation step needed.
Set up dual-emit log channels: (1) From DM, run /setlog and forward a post from your master log channel — this sets the GENERAL aggregate feed that all groups write to. (2) From inside each group, run /setlog and forward from that group's private channel — this sets the INDIVIDUAL channel. Events from that group then arrive in both places. Repeat step 2 for each group. Run /logstatus in the group to confirm both INDIVIDUAL and GENERAL are set correctly.
Set up blacklisted phrases with /blacklist WORD and filters with /filter NAME as needed.

⚙️Configuration

CyberPUP Security is configured via config.toml and environment variables. Environment variables take precedence over the file.

Key Environment Variables

VariableDescription
BOT_TOKENTelegram bot token (required)
BOT_OWNERPrimary owner Telegram ID
BOT_OWNERSComma-separated additional owner IDs
GROUPS_MAINComma-separated main group IDs to moderate
GROUPS_REPORTSReports channel ID
GROUPS_LOGSLog channel ID
LINKED_CHANNELSComma-separated linked channel IDs (forwards allowed)
DB_URLDatabase URL (default: SQLite)
PROMO_ENABLEDtrue/false — enable promo guard
PROMO_LIMIT_PER_DAYPromo slots per user per 24h (default: 1)
LOG_LEVELPython logging level (default: INFO)
HONEYBADGER_API_KEYOptional: Honeybadger error reporting

config.toml Sections

[bot]
owner, owners, token, version, version_codename
[groups]
main, reports, logs, linked_channels, new_users_nomedia
[spam]
All reputation thresholds, autoban settings, forward/link/media rep gates, penalty values
[nsfw]
Enabled flag, per-category detection thresholds, check rep threshold, cooldown
[promo]
enabled, limit_per_day
[throttling]
Rate limit (0.5s default), max messages (20), time window (60s)
[announcements]
Enabled, history size, max stack, sleep window, per-group UTC offset
[ml]
Auto-unload, spam/NSFW model TTL, check interval
💡 Bootstrap state (data/bootstrap-state.json) stores the owner_id, group_id, reports_id, and logs_id from the web activation portal. This file is loaded on startup and fills in any config values that weren't explicitly set.

🔧Troubleshooting

Common issues and their resolutions.

Security bot isn't deleting spam in my group
1. Verify Privacy Mode is OFF in BotFather → re-add the bot to the group.
2. Confirm the group ID is in GROUPS_MAIN (or was auto-activated).
3. Check the bot has admin rights (Delete + Ban).
4. Run !ping in the group to confirm the bot responds.
Core bot isn't responding to commands
Check docker logs cyberpup-core --tail 50 on the droplet. Confirm the module table shows the relevant module (e.g., admin, blacklist). If the container crashed, docker start cyberpup-core.
/setlog says "Invalid parse mode" or similar error
Was a bug in the initial /setlog deployment (parse_mode="html" string instead of ParseMode.HTML enum). Fixed in v2+. If you still see it, verify the deployed setlog.py contains from pyrogram.enums import … ParseMode and uses parse_mode=ParseMode.HTML.
Core responds to /cmd@OtherBot (command targeted at a different bot)
Pyrogram's built-in filters.command() already rejects commands addressed to another bot — /ban@OtherBot won't trigger the ban handler. The one gap is the keyword filter engine (filters_re): if a saved filter word matches a command name (e.g., a saved filter "ban"), it would fire on /ban@OtherBot because filters_re scans raw text. Fix deployed in v4: filters_re now skips any message that starts with a slash command whose @mention targets a different bot's username.
Hot-patching a Core module
1. Edit the file at /opt/cyberpup-core/wbb/modules/<module>.py (host source — survives rebuild).
2. docker cp /opt/cyberpup-core/wbb/modules/<module>.py cyberpup-core:/wbb/wbb/modules/<module>.py
3. docker restart cyberpup-core
MongoDB connection errors in Core logs
Core uses MongoDB on the claw-mem Docker network. If the claw-mongo container is down: docker start claw-mongo. The promo guard falls back to in-memory (lost on restart). The log channel setting also persists in MongoDB — run /setlog again after recovery.
SSH "Connection refused" on port 22 to the droplet
The droplet rate-limits rapid successive SSH connections. Wait ~30–60 seconds and retry. Use a persistent ControlMaster socket if running many commands: ssh -o ControlMaster=auto -o ControlPath=/tmp/cyberpup-ssh root@64.23.130.34.
Events from a group only appear in one channel, not both
Dual-emit requires both an INDIVIDUAL and a GENERAL channel to be set. Run /logstatus in the group — if either shows "not set", run /setlog from DM (for GENERAL) or from the group (for INDIVIDUAL). Core uses wbb.utils.log_channels.get_log_destinations(chat_id) which returns [individual, general] deduped — both messages are sent in a loop. Security reads wbb.log_channels for the individual and config.groups.logs for general inline at each call site. If only one channel receives events, verify the other is configured.

For the GENERAL path: from wbb import LOG_GROUP_ID at module import creates a local copy. setlog.py uses sys.modules iteration (_patch_all_modules) to update every loaded submodule. Verify this function is present in the deployed setlog.py if GENERAL writes aren't taking effect.

FAQ

Can group admins use /setlog?
Yes — they set the INDIVIDUAL channel for their own group only. A group admin may run /setlog -100xxxx, /setlog @handle, or bare /setlog (forward mode) from within a group they administer — this writes an INDIVIDUAL mapping (wbb.log_channels) that affects only that group. Events from that group then go to both their INDIVIDUAL channel AND the GENERAL aggregate channel. They may also run /setlog off to clear the INDIVIDUAL mapping (group still appears in GENERAL). They cannot change the GENERAL channel, run it from a group where they are not an admin, or affect other groups. Sudoers and the owner have full access including GENERAL writes. Non-authorized users are silently ignored.
Why does the Security bot delete Chinese text even from trusted members?
The CJK filter is applied to all users including high-rep members — it has no reputation gate. If your group has legitimate CJK speakers, this filter would need to be removed or modified in the source code. There is currently no config toggle for it.
A user says they're getting their messages deleted unfairly. How do I check?
Run /me as a reply to one of their messages in the group (Security bot). This shows their rep, level, and message count. If rep is very low, the appropriate thresholds are active. The owner can use !reward to boost rep or !setlvl to raise their level if they're a trusted member who joined recently.
What is the difference between the reports channel and the log channel?
Reports channel receives messages flagged by members using /report, @admins, or @admin — user-initiated. Log channel receives automatic moderation events (bans, NSFW detections, spam removals, member joins, global bans, etc.) — bot-initiated. Set them separately with !setreports and !setlogs in Security, or /setlog in Core.
Does CyberPUP Core automatically block profanity like Security does?
No. Auto profanity filtering is disabled in Core (AUTO_PROFANITY_FILTER = False). Core ships with no built-in wordlist. The blacklist only enforces words you explicitly add with /blacklist WORD. General cursing passes freely in Core. CyberPUP Security has its own built-in profanity detection which is separate.
How do I add a second owner to CyberPUP Security?
Set BOT_OWNERS=id1,id2 in the environment or add owners = [id1, id2] to [bot] in config.toml. Settings loaded from MongoDB at startup merge with these — Mongo's list is never allowed to replace the env/config list to prevent a stale Mongo doc from locking out configured owners.
What happens if MongoDB is down?
CyberPUP Core's promo guard falls back to in-memory storage (promo limits lost on restart). INDIVIDUAL log channel mappings (wbb.log_channels) also live in Mongo — if Mongo was down when you ran /setlog from a group, the in-memory cache update still works for the current session but the INDIVIDUAL mapping won't survive a restart. The GENERAL channel (core_settings.log_group_id) also persists in Mongo. Security uses SQLite for member/spam data so isn't affected by Mongo outages for moderation, but both INDIVIDUAL and GENERAL log channel lookups require Mongo for Security. If Mongo recovers, mappings resume automatically; re-run /setlog if any were lost. While Mongo is down, Core emits to GENERAL only (in-memory value); Security emits to config.groups.logs only.
The NSFW filter flagged a clean anime image. What do I do?
Click ✅ Mark Safe in the log channel review message. The model uses multiple companion thresholds to reduce false positives on clean anime art (pure Hentai score alone is not sufficient — it requires Porn or Sensual companion above 0.05 / 0.1 respectively). If false positives are frequent, the thresholds in [nsfw] can be raised.
Can I use CyberPUP Security without the reputation system?
Not currently. The reputation system is core to how every threshold-based filter operates. However, you can tune all thresholds to be very permissive (high values that most users quickly exceed) to effectively neutralize the rep gates without disabling them.