Commit Graph

81 Commits

Author SHA1 Message Date
user
181d6dbfad fix: call YouTube API directly, announce all matched results
YouTube InnerTube is a public API -- no need to route through SOCKS5
proxy, which was causing SSL EOF errors on every poll. Switch to
direct urllib.request.urlopen.

Remove _MAX_ANNOUNCE cap; all matched results are now announced
individually instead of truncating with "... and N more".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:04:15 +01:00
user
6d6f4e7343 fix: handle null publishedDate from SearXNG results
SearXNG can return "publishedDate": null, which bypasses the default
value in dict.get() and passes None to _parse_date / re.search.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:42:07 +01:00
user
7698d079f2 fix: switch to k8s-file log driver for reliable log capture
journald was dropping early startup logs. k8s-file writes directly
to disk, captures from process start, and is lighter on the Pi.
Capped at 10 MB with automatic rotation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:40:53 +01:00
user
604a0a5830 feat: display published date in alert announcements
Extract dates from multiple sources:
- SearXNG: publishedDate field from search results
- YouTube: publishedTimeText from InnerTube response
- OG fallback: article:published_time, og:updated_time, date,
  dc.date, dcterms.date, sailthru.date meta tags

Date is shown as (YYYY-MM-DD) or relative time after the tag prefix.
OG tags are fetched for date even when title/URL already matched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:30:48 +01:00
user
e36ec350f5 feat: check og:title/og:description for keyword match in alerts
When a search result's title/URL doesn't contain the keyword, fetch
the page's first 64 KB and parse og:title and og:description meta
tags. If the keyword appears there, the result is announced. Prefers
og:title as display title when it's richer than the search result
title.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:28:48 +01:00
user
0d5855dda3 fix: filter alert results to require keyword match in title/URL
Search backends return loosely relevant results. Now only announces
items where the keyword actually appears in the title or URL
(case-insensitive). All results are still marked as seen to prevent
re-checking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 21:27:03 +01:00
user
33c6032329 fix: enable unbuffered Python output in container
Without PYTHONUNBUFFERED=1, Python buffers stdout causing
podman logs to show no output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 19:25:21 +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
6d86e8d7f8 fix: retry transient SSL/connection errors in alert backends
Add retry loop (3 attempts, exponential backoff) for SSLError,
ConnectionError, TimeoutError, and OSError in alert poll cycle.
Non-transient errors fail immediately. Also fixes searx test
mocks to match direct urlopen usage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 18:51:28 +01:00
user
f046cced28 fix: use public SearXNG URL without proxy
Switch to searx.mymx.me (public domain) for SearXNG queries
without routing through SOCKS5 proxy. Internal address is not
reachable from the container network.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 18:28:29 +01:00
user
b973635445 fix: route SearXNG direct via static route, drop proxy
SearXNG instance at 192.168.122.119 is reachable via grokbox
static route -- no need to tunnel through SOCKS5. Reverts searx
and alert plugins to stdlib urlopen for SearXNG queries. YouTube
and Twitch in alert.py still use the proxy. Also removes cprofile
flag from docker-compose command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 17:52:43 +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
29e77f97b2 fix: route searx and alert SearXNG traffic through SOCKS5 proxy
Both plugins called urllib.request.urlopen directly, bypassing the
proxy. Switch to derp.http.urlopen and update the SearXNG endpoint
to the public domain (searx.mymx.me). Update test mocks to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:56:45 +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
87b43e211a fix: install PySocks in container image
All plugins importing derp.http failed to load because PySocks
was missing from the container. Add it alongside maxminddb.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:37:51 +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
7520bba192 fix: mount src and data volumes in container targets
Bind-mount src/ and data/ alongside plugins/ and config so the
container picks up code changes without rebuilding. Update Makefile
targets, compose file, and INSTALL.md to match.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 16:11:59 +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
10f62631be feat: add SearX search plugin and alert backend
Add standalone !searx command for on-demand SearXNG search (top 3 results).
Add SearX as a third backend (sx) to the alert plugin for keyword monitoring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:28:00 +01:00
user
4c9dffaaf2 docs: document keyword alert subscription plugin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:16:32 +01:00
user
8fd6393273 feat: add keyword alert subscription plugin
Search keywords across YouTube (InnerTube) and Twitch (GQL)
simultaneously, announcing new results per channel. Supports
add/del/list/check subcommands with per-platform seen lists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:16:29 +01:00
user
abcac95846 docs: document Twitch notification plugin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:50:34 +01:00
user
50fb8015cd feat: add Twitch livestream notification plugin
Poll Twitch streamers via public GQL endpoint and announce
offline-to-live transitions in IRC channels. Tracks stream ID
to avoid re-announcing the same stream.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:50:30 +01:00
user
e6419c1ffc docs: document YouTube follow plugin 2026-02-15 14:34:24 +01:00
user
3955935da4 feat: add YouTube channel follow plugin
Follow YouTube channels via Atom feeds with !yt follow/unfollow/list/check.
Resolves any YouTube URL to a channel ID, polls for new videos, and
announces them in IRC channels.
2026-02-15 14:34:20 +01:00
user
d9f7a3f7cc docs: document invite auto-join behavior 2026-02-15 13:52:21 +01:00
user
6b7572defc feat: auto-join channels on admin invite 2026-02-15 13:52:15 +01:00
user
ca29729ee5 docs: document RSS feed plugin 2026-02-15 13:36:28 +01:00
user
125a4c5d4d feat: add per-channel RSS feed subscription plugin
Subscribe RSS/Atom feeds to IRC channels with periodic polling,
new-item announcements, deduplication, and persistence across restarts.
Supports conditional HTTP requests (ETag/Last-Modified), automatic
backoff on errors, and per-channel feed limits.
2026-02-15 13:36:23 +01:00
user
476b94967e docs: document debounced oper detection on JOIN
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 13:11:40 +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
2333af0624 docs: update docs for calendar reminders
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:39:47 +01:00
user
f888faf2bd feat: add calendar-based reminders (at/yearly) with persistence
Calendar reminders use bot.state (SQLite KV) for persistence across
restarts. Supports one-shot at specific date/time and yearly recurring
reminders with leap day handling. Restored automatically on connect
via 001 event handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:39:42 +01:00
user
021a0ddbe3 test: comprehensive remind plugin tests with IndexError fix
Expand test coverage from 23 pure helper tests to 53 tests covering
the full plugin: _cleanup, _remind_once, _remind_repeat, and the
complete cmd_remind handler (usage, oneshot, repeating, list, cancel,
target routing). Fix IndexError on `!remind every` with no arguments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:17:31 +01:00
user
70d203f96e feat: add remind plugin with one-shot and repeating reminders
Supports duration parsing (5m, 1h30m, 2d12h), short hex IDs for
tracking, list/cancel subcommands, and repeating intervals via
`!remind every <duration> <text>`. Includes 23 unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 12:09:34 +01:00
user
f48b32cd65 fix: resolve test_crtsh.py import for plugins/ directory
Same importlib fix as test_username.py -- load plugins.crtsh from
file path since plugins/ is not a Python package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:18:06 +01:00
user
7184c43b08 fix: resolve test_username.py import for plugins/ directory
Load plugins.username via importlib.util.spec_from_file_location
since plugins/ is not a Python package on sys.path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:17:11 +01:00
user
5dd4460d59 docs: mark integration tests complete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:09:18 +01:00
user
fa88422c36 test: add integration tests with mock IRC server
Queue-based _MockConnection replaces IRCConnection to test the full
bot pipeline (registration -> dispatch -> handler -> response) without
network I/O. 14 tests cover CAP negotiation, PING/PONG, command
dispatch, prefix matching, admin enforcement, channel filtering,
and CTCP responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 06:09:14 +01:00
user
ddefeb4242 docs: clean up stale TODO.md backlog
Remove completed items (username, dork, wayback, per-channel filtering,
message truncation, reconnect backoff, structured logging, bot/config
tests).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 04:58:46 +01:00
user
0ba9b6b742 docs: document username plugin
Add !username section to USAGE.md with examples. Add OSINT quick
reference entries to CHEATSHEET.md. Mark username plugin done in
ROADMAP.md and TASKS.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 04:47:31 +01:00
user
13c1f76767 test: add username plugin tests
53 tests covering regex validation, service registry integrity,
classification logic (status/json/body), formatting, and category
grouping.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 04:47:26 +01:00
user
32197d71ae feat: add username enumeration plugin
Cross-platform username OSINT across ~25 services (GitHub, GitLab,
Reddit, Docker Hub, Keybase, Dev.to, Twitch, Steam, etc).  Hybrid
approach using HTTP status probes, JSON APIs, and body search.
8 parallel workers via ThreadPoolExecutor, 20s overall timeout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 04:47:23 +01:00
user
9db02212b7 fix: filter help output by per-channel plugin config
!help now only lists commands from plugins allowed in the current
channel. !help <cmd> and !help <plugin> return "unknown" for
filtered plugins. PMs remain unrestricted.
2026-02-15 04:30:28 +01:00
user
16425046c8 docs: document channel config and structured logging
Add per-channel plugin control and JSON logging sections to USAGE.md,
CHEATSHEET.md, and derp.toml.example. Mark items done in ROADMAP.md.
2026-02-15 04:16:54 +01:00