canary: generate realistic fake credentials (token/aws/basic) for
planting as canary tripwires. Per-channel state persistence.
tcping: TCP connect latency probe through SOCKS5 proxy with
min/avg/max reporting. Proxy-compatible alternative to traceroute.
archive: save URLs to Wayback Machine via Save Page Now API,
routed through SOCKS5 proxy.
resolve: bulk DNS resolution (up to 10 hosts) via TCP DNS through
SOCKS5 proxy with concurrent asyncio.gather.
83 new tests (1010 total), docs updated.
YouTube's InnerTube /player endpoint now returns LOGIN_REQUIRED for the
WEB client. Switch to ANDROID client context which still returns full
videoDetails. Fixes missing duration in announcements and broken channel
resolution from video URLs.
Extract shared _innertube_player() helper to deduplicate payload
construction between _resolve_via_innertube and _fetch_duration.
Free, keyless API returning open ports, hostnames, CPEs, tags, and
known CVEs for any public IP. All requests routed through SOCKS5.
21 test cases (927 total).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop GeoLite2-ASN.mmdb dependency (required license key) in favor of
iptoasn.com ip2asn-v4.tsv (no auth, public domain). Bisect-based
lookup in pure stdlib, downloaded via SOCKS5 in update-data.sh.
Adds 30 test cases covering load, lookup, and command handler.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches duration via InnerTube player API for new videos at
announcement time. Displayed as compact h:mm:ss before views/likes.
Gracefully omitted for Shorts and unavailable content.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Alert backends now populate structured `extra` field with engagement
metrics (views, stars, votes, etc.) instead of embedding them in titles.
Subscription plugins show richer announcements: Twitch viewer counts,
YouTube views/likes/dates, RSS published dates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Poll Pastebin archive and GitHub Gists for keyword matches,
announce hits to subscribed IRC channels. Follows rss.py
polling/subscription pattern with state persistence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Event-driven plugin that auto-fetches page titles for URLs posted in
channel messages. HEAD-then-GET via SOCKS5 pool, og:title priority,
cooldown dedup, !-suppression, binary/host filtering. 52 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Mastodon, Bluesky, GitHub, GitLab, npm, PyPI, and arXiv backends
no longer truncate content/descriptions in titles. Full text is
shown on the PRIVMSG line; only !alert history keeps truncation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ACTION carries the tag/date/URL, PRIVMSG carries the uncropped title.
Removes _truncate on alert output for better readability.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
Per-backend error counts with exponential backoff: after 5 consecutive
failures a backend is skipped every 2^(n-5) cycles (capped at 32).
Working backends are no longer penalized by one flaky backend doubling
the entire poll interval.
Migrates last_error (string) to last_errors (dict per backend).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reply immediately with empty seen, run a silent _poll_once in a
background task to populate seen IDs, then start the poller.
Eliminates the 30-120s blocking wait from 27 sequential backend queries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Store shortened URLs in the results DB at poll time alongside the
original URL. History output uses the stored short URL directly,
only regenerating (and persisting) when no short URL exists yet.
Original URL always preserved for re-shortening if needed.
When secrets/flaskpaste/derp.crt and derp.key are present, load them
into the SSL context for mutual TLS auth and skip the PoW challenge
entirely. Fall back to PoW only when no client cert is available.
- PoW-authenticated paste creation and URL shortening via FlaskPaste
- !paste <text> creates a paste, !shorten <url> shortens a URL
- Module-level shorten_url/create_paste helpers for cross-plugin use
- Alert plugin auto-shortens URLs in announcements and history output
- Custom TLS CA cert support via secrets/flaskpaste/derp.crt
- No SOCKS proxy -- direct urllib.request to FlaskPaste instance
Tor exit nodes poison plain DNS on port 53 and MITM some HTTPS
connections. Replace raw TCP DNS with DoH (Google, Cloudflare, Quad9)
and retry up to 5 times across providers to find a clean exit node.
MX results are now sorted by priority.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
v2.0.0 sprint 1 -- five standalone plugins requiring no core changes:
- jwt: decode JWT header/payload, flag alg=none/expired/nbf issues
- mac: IEEE OUI vendor lookup, random MAC generation, OUI download
- abuseipdb: IP reputation check + abuse reporting (admin) via API
- virustotal: hash/IP/domain/URL lookup via VT APIv3, 4/min rate limit
- emailcheck: SMTP RCPT TO verification via MX + SOCKS proxy (admin)
Also adds update_oui() to update-data.sh and documents all five
plugins in USAGE.md and CHEATSHEET.md.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Wikipedia, Stack Exchange, GitLab, npm, PyPI, Docker Hub,
arXiv, Lobsters, DEV.to, Medium, and Hugging Face backends to
the alert plugin (16 -> 27 total). Fix PyPI backend to use RSS
updates feed (web search now requires JS challenge). Fix DEV.to
to use public articles API (feed_content endpoint returns empty).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Video URLs (watch, shorts, embed, youtu.be) now resolve the channel
ID through the InnerTube player API -- a small JSON POST instead of
fetching the full 1MB watch page. Much more resilient to transient
proxy failures. Page scraping remains as fallback for handle URLs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each alert result gets a deterministic 8-char base36 ID derived from
backend:item_id. IDs appear in announcements and history, and can be
looked up with !alert info <id> for full details. Existing rows are
backfilled on startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hacker News (hn) uses Algolia search_by_date API for stories,
appends point count to title, falls back to HN discussion URL
when no external link. GitHub (gh) searches repositories sorted
by recently updated, shows star count and truncated description.
Both routed through SOCKS5 proxy via _urlopen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bluesky (bs) searches public post API, constructs bsky.app URLs
from at:// URIs. Lemmy (ly) queries 4 instances (lemmy.ml,
lemmy.world, programming.dev, infosec.pub) with cross-instance
dedup. Odysee (od) uses LBRY JSON-RPC claim_search for video,
audio, and documents with lbry:// to odysee.com URL conversion.
Archive.org (ia) searches via advanced search API sorted by date.
All routed through SOCKS5 proxy via _urlopen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kick (kk) searches channels and livestreams via public search API.
Dailymotion (dm) queries video API sorted by recent. PeerTube (pt)
searches across 4 federated instances with per-instance timeout.
All routed through SOCKS5 proxy via _urlopen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DuckDuckGo (dg) searches via HTML lite endpoint with HTMLParser,
resolves DDG redirect URLs to actual targets. Google News (gn)
queries public RSS feed, parses RFC 822 dates. Both routed through
SOCKS5 proxy via _urlopen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Query general, news, videos, and social media categories
separately with time_range=day. Dedup results by URL across
categories to avoid announcing the same item twice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Search Reddit posts (rd) via JSON API and Mastodon hashtag
timelines (ft) across 4 fediverse instances. Both public,
no auth required.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the bot accepts an admin INVITE, the channel is stored in
bot.state under chanmgmt/autojoin:<channel>. On reconnect, persisted
channels are rejoined alongside configured ones. If the bot is kicked,
the channel is removed from the auto-rejoin list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Matched results were announced then discarded. Add a dedicated SQLite
database (data/alert_history.db) to store every announced result with
channel, alert name, backend, title, URL, date, and timestamp. Add
!alert history <name> [n] subcommand to query recent results.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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.
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>
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>