Commit Graph

33 Commits

Author SHA1 Message Date
user
2514aa777d feat: add granular ACL tiers (trusted/oper/admin)
4-tier permission model: user < trusted < oper < admin.
Commands specify a required tier via tier= parameter.
Backward compatible: admin=True maps to tier="admin".

- TIERS constant and Handler.tier field in plugin.py
- _get_tier() method in bot.py with pattern matching
- _is_admin() preserved as thin wrapper
- operators/trusted config lists in config.py
- whoami shows tier, admins shows all configured tiers
- 32 test cases in test_acl.py
2026-02-21 17:59:05 +01:00
user
aebe1589d2 feat: add URL shortening to subscription announcements
Bot.shorten_url() method delegates to flaskpaste plugin when loaded.
RSS, YouTube, and pastemoni announcements auto-shorten links.
Includes test_flaskpaste.py (9 cases) and FakeBot updates in 3 test files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:35:03 +01:00
user
6f1f4b2fc8 Revert "feat: add --llm mode for LLM-friendly stdout filtering"
This reverts commit ea6f07914e.
2026-02-19 20:39:36 +01:00
user
ea6f07914e feat: add --llm mode for LLM-friendly stdout filtering
Split output when running with --llm: addressed messages from owners
go to stdout, everything else (chatter, logs, plugin loads) goes to
info.log. Adds owner privilege level (superset of admin) for gating
LLM access. Status lines (connect, ping, disconnect, reconnect) and
bot replies also appear on stdout for session awareness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 20:11:23 +01:00
user
1836fa50af feat: paste overflow via FlaskPaste for long replies
Add Bot.long_reply() that sends lines directly when under threshold,
or creates a FlaskPaste paste with preview + link when over. Refactor
abuseipdb, alert history, crtsh, dork, exploitdb, and subdomain
plugins to use long_reply(). Configurable paste_threshold (default: 4).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 22:07:31 +01:00
user
94f563d55a feat: connection pooling via urllib3 + batch OG fetching
Replace per-request SOCKS5+TLS handshakes with urllib3 SOCKSProxyManager
connection pool (20 pools, 4 conns/host). Batch _fetch_og calls via
ThreadPoolExecutor to parallelize OG tag enrichment in alert polling.
Cache flaskpaste SSL context at module level.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 20:52:22 +01:00
user
a2a607baa2 fix: write tracemalloc dump to file instead of logger
Podman's log buffer truncates the output. Write full traceback dump
to data/derp.malloc with per-allocation stack traces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 13:16:22 +01:00
user
76301ac8f2 perf: concurrent fetches for multi-instance alert backends
Add _fetch_many() helper using ThreadPoolExecutor to query instances
in parallel. Refactors PeerTube, Mastodon, Lemmy, and SearXNG from
sequential to concurrent fetches. Also adds retries parameter to
derp.http.urlopen; multi-instance backends use retries=1 since
instance redundancy already provides resilience.

Worst-case wall time per backend drops from N*timeout to 1*timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 12:02:57 +01:00
user
a123eba32a feat: add --tracemalloc flag for memory profiling
Starts tracemalloc before the event loop and dumps the top 25
allocations on shutdown. Accepts optional nframes depth (default 10).
Can be combined with --cprofile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 10:41:45 +01:00
user
933d9e1ddd perf: cache default HTTP opener at module level
Avoid rebuilding _ProxyHandler + build_opener() on every request.
Default-context callers (16 of 18 plugins) reuse one cached opener;
custom-context callers still get a fresh one.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 10:15:20 +01:00
user
e8d803abe6 fix: account for server prefix in IRC line splitting
The 512-byte IRC limit includes the :nick!user@host prefix the server
prepends when relaying. Reserve 64 bytes for it and prefer splitting at
space boundaries instead of mid-word. Also strip the command prefix and
"Commands:" label from help output to keep the listing compact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:02:52 +01:00
user
118cf0de21 fix: centralize retry logic in proxy transport layer
Add exponential-backoff retry (3 attempts) for transient SSL,
connection, timeout, and OS errors to all three proxy functions:
urlopen, create_connection, open_connection. Remove per-plugin
retry from alert.py since transport layer now handles it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 18:55:21 +01:00
user
23ba7dc474 feat: add graceful SIGTERM handling for clean shutdown
Install a SIGTERM signal handler that stops the bot loop and closes
the IRC connection, allowing cProfile stats to flush before exit.
2026-02-15 17:06:37 +01:00
user
6e591a85b2 fix: use host networking for container proxy access
Bridge networking can't reach the host's loopback. Switch to
network_mode: host so the container shares the host network stack
and can reach the SOCKS5 proxy at 127.0.0.1:1080. Revert proxy
address back to 127.0.0.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:46:24 +01:00
user
a7f0246dac fix: use LAN address for SOCKS5 proxy
Container can't reach 127.0.0.1 on the host. Use the host's LAN
address 192.168.129.11 so containerized plugins can reach the
SOCKS5 forwarder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:43:45 +01:00
user
fd8e9f85b6 fix: point Tor DNS resolver at relay address 10.200.1.13
Was incorrectly set to 127.0.0.1. The Tor DNSPort runs on the
remote relay at 10.200.1.13:9053. Alt relays noted in comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:20:08 +01:00
user
d5866a9867 fix: route blacklist and subdomain DNS through Tor resolver
Both plugins duplicated wire-format helpers and queried the system
resolver on port 53. Switch to shared derp.dns helpers and point
queries at the local Tor DNS resolver (127.0.0.1:9053) so lookups
go through Tor like all other outbound traffic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:16:57 +01:00
user
26063a0e8f feat: add TCP DNS plugin with SOCKS5 proxy support
Extract shared DNS wire-format helpers into src/derp/dns.py so both
the UDP plugin (dns.py) and the new TCP plugin (tdns.py) share the
same encode/decode/build/parse logic.

The !tdns command routes queries through the SOCKS5 proxy via
derp.http.open_connection, using TCP framing (2-byte length prefix).
Default server: 1.1.1.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:09:35 +01:00
user
1bdba0ea06 feat: route raw TCP traffic through SOCKS5 proxy
Add create_connection and open_connection helpers to the shared proxy
module, covering portcheck, whois, tlscheck, and crtsh live-cert check.
UDP-based plugins (dns, blacklist, subdomain) stay direct.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:01:17 +01:00
user
97bbc6a825 feat: route plugin HTTP traffic through SOCKS5 proxy
Add PySocks dependency and shared src/derp/http.py module providing
proxy-aware urlopen() and build_opener() that route through
socks5h://127.0.0.1:1080. Subclassed SocksiPyHandler passes SSL
context through to HTTPS connections.

Swapped 14 external-facing plugins to use the proxied helpers.
Local-only traffic (SearXNG, raw DNS/TLS sockets) stays direct.
Updated test mocks in test_twitch and test_alert accordingly.
2026-02-15 15:53:49 +01:00
user
02ea81d059 fix: debounce WHO on JOIN to prevent flood on netsplit recovery
WHO doesn't support multiple targets (absent from TARGMAX on all
major IRCds). Replace per-nick WHO with a debounced per-channel WHO:
on JOIN, schedule WHO #channel after 2s delay. Subsequent JOINs
within the window reset the timer, so a netsplit producing dozens
of JOINs results in a single WHO.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 13:03:35 +01:00
user
fd8f72c3cc fix: detect oper status when users join channels
Previously the bot only sent WHO on connect (001), so users joining
after the initial scan were never checked for oper status. Now sends
WHO <nick> on every JOIN event to detect opers mid-session.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:53:12 +01:00
user
b32c9efb8a feat: add structured JSON logging
New JsonFormatter emits one JSON object per log line (JSONL). Activated
via format = "json" in [logging] config. Default remains "text".
2026-02-15 04:16:45 +01:00
user
7bbfa9b345 feat: add per-channel plugin filtering
Channels with a [channels."#name"] section and `plugins` list only run
those plugins. Unconfigured channels run everything. Core is always
active. PMs are unrestricted. Denied commands are silently ignored.
2026-02-15 04:16:41 +01:00
user
1a6c6de38b feat: add IRC 512-byte message truncation
Split outgoing messages at UTF-8 safe boundaries to comply with
RFC 2812 line limit. Accounts for PRIVMSG overhead and CRLF.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 03:26:57 +01:00
user
f86cd1ad49 feat: add IRCv3 cap negotiation, channel management, state persistence
Implement CAP LS 302 flow with configurable ircv3_caps list, replacing
the minimal SASL-only registration. Parse IRCv3 message tags (@key=value)
with proper value unescaping. Add channel management plugin (kick, ban,
unban, topic, mode) and bot API methods. Add SQLite key-value StateStore
for plugin state persistence with !state inspection command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 03:07:06 +01:00
user
f96224afb1 feat: add admin/owner permission system
Hostmask-based admin controls with automatic IRCOP detection via WHO.
Permission enforcement in the central dispatch path denies restricted
commands to non-admins. Includes !whoami and !admins commands, marks
load/reload/unload as admin-only.

Also lands previously-implemented SASL PLAIN auth, token-bucket rate
limiting, and CTCP VERSION/TIME/PING responses that were staged but
uncommitted.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 02:26:27 +01:00
user
36b21e2463 feat: concurrent command dispatch and profiling test client
Replace sequential await in command/event dispatch with
asyncio.create_task() so slow commands (whois, httpcheck, tlscheck)
no longer block the read loop. Add _spawn() for task lifecycle
tracking. Enable cProfile in docker-compose for profiling. Add
scripts/test_client.py for end-to-end plugin testing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 02:09:53 +01:00
user
530f33be76 feat: add wave 2 plugins and --cprofile CLI flag
Add 7 new pure-stdlib plugins: whois (raw TCP port 43), portcheck
(async TCP connect scan with internal-net guard), httpcheck (HTTP
status/redirects/timing), tlscheck (TLS version/cipher/cert inspect),
blacklist (parallel DNSBL check against 10 RBLs), rand (password/hex/
uuid/bytes/int/coin/dice), and timer (async countdown notifications).

Add --cprofile flag to CLI for profiling bot runtime. Update all docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:58:47 +01:00
user
5c0b23464c feat: add !uptime command
Track bot start time via monotonic clock, display as compact
duration (e.g. "up 3d 2h 15m 42s").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:46:06 +01:00
user
77f9a364e6 feat: add hot-reload, shorthand commands, and plugin help
- Plugin registry: add unload_plugin(), reload_plugin(), path tracking
- Bot: add load_plugin(), reload_plugin(), unload_plugin() public API
- Core plugin: add !load, !reload, !unload, !plugins commands
- Command dispatch: support unambiguous prefix matching (!h -> !help)
- Help: support !help <plugin> to show plugin description and commands
- Tests: 17 new tests covering hot-reload, prefix matching

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 01:15:59 +01:00
user
118fbb75d1 feat: add tls_verify option for self-signed certs
Skip certificate verification when tls_verify = false in config.
Defaults to true for safety.
2026-02-15 00:43:44 +01:00
user
bf45abcbad feat: initial implementation
Asyncio IRC bot with decorator-based plugin system.
Zero external dependencies, Python 3.11+.

- IRC protocol: message parsing, formatting, async TCP/TLS connection
- Plugin system: @command and @event decorators, file-based loading
- Bot orchestrator: connect, dispatch, reconnect, nick recovery
- CLI: argparse entry point with TOML config
- Built-in plugins: ping, help, version, echo
- 28 unit tests for parser and plugin system
2026-02-15 00:37:31 +01:00