Developer · Bot Builder · Infrastructure Nerd
I build bots, back-end infrastructure, and the automation that ties it all together. From custom Twitch and joystick.tv bots and real-time alert systems to community moderation tooling, data pipelines, and self-hosted services running across a pair of cloud servers, I keep the infrastructure humming so the chaos can flow uninterrupted.
Tools I've built and run on a pair of self-hosted Oracle Cloud servers: live event handling for Twitch and Joystick.tv streams, community moderation, the data pipelines that feed this site, and a few bits of personal automation. The parts that matter don't lean on third-party services.
The main bot for the Twitch channel. It handles EventSub webhooks for follows, raids, and channel-point redeems, and runs the chat commands: 4:20 milestone counts, F1 schedule lookups, a TMDB-backed watchlist. Cheers set off meat-emoji sequences. Everything lands in SQLite for analytics and gets mirrored to a Google Sheet, and the events are passed along to the alerts relay and the Discord bot so the platforms stay in step.
Runs on Joystick.tv, holding a persistent ActionCable WebSocket open per channel so it can react to chat, tips, and stream start/stop as they happen. It runs a Chat Streak economy where regulars earn daily tokens to spend on perks, and forwards tips to MixItUp to fire TITS throws. Settings are per channel, so each stream behaves on its own terms. It shares its database and command framework with the Twitch bot.
The community's Discord bot. It listens for webhook events from the Twitch and Joystick bots and posts live-stream notifications. Its slash-command cogs cover the watchlist and game suggestions (TMDB and Steam) plus an AI assistant that forwards questions to a local LLM over Tailscale. Another cog reads streaming plans out of chat with Google Gemini and writes the JSON schedule the website reads. When something throws, Gotify pushes the error straight to my phone.
A small relay for audio, video, and GIF alerts. Bots POST normalized events to it and it pushes them over WebSocket to the OBS browser sources on the overlay. Which event plays which clip lives in a TOML file that reloads on the fly, no restart needed, and cooldowns (per user and global) keep the alerts from spamming. A Cloudflare Tunnel handles the public WebSocket endpoint, so there's no port forwarding and no third-party alert service in the loop.
Two Oracle Cloud free-tier boxes joined over a Tailscale mesh. Server A runs the Python bots as systemd units, each restartable on its own. Server B runs the supporting services as Docker Compose stacks, with Dockhand managing containers and Caddy handling TLS and reverse proxying. Public endpoints for the webhooks, alerts, and site all come in through Cloudflare Tunnels. The rest is self-hosted too: Gotify for push notifications, sqlite-web for poking at the database remotely, Filebrowser for files.
A handful of bots that keep a large subreddit running. One archives the show's episode-discussion threads: it digs the season number out of each title (they're never formatted the same way, so it tries a few patterns), then builds and maintains the per-show wiki pages and the index that ties them together. Another manages the recurring discussion threads with a small SQLite state machine: retiring old threads and pointing readers at the current one, linking the standard and spoiler versions, handing sticky-comment duty to Discord, and rolling the day counter over at midnight. A scheduler posts the timed threads. Gotify keeps me in the loop on what they're all up to.
A self-hosted stand-in for the platform's built-in auto-moderation, written after the platform hinted it might drop the feature. It reads the same YAML rule file the mods already maintain (compound conditions, regex, placeholders, priorities, per-rule actions), so nobody has to rewrite their rules or learn a new format. It watches submissions, comments, edits, and the mod queue over several streams that survive reconnects, and a durable exactly-once ledger means a restart picks up whatever it missed without acting twice. A two-pass check plus a cached karma lookup keep it under the shared API limit. For now it runs in shadow mode next to the real filter, logging what it would have done so I can compare against actual mod actions before switching individual rules live through an allowlist.
The platform only keeps about three months of moderator action logs, so this keeps its own. An archiver writes down every mod action, removal reason, and a frozen copy of the removed content the moment it happens, building a permanent record that stays searchable long after the platform has dropped its version. A read API serves it two ways. A companion browser userscript fills in removal reasons and a user's full history inline while mods work, without spending the platform's rate limit. And a dozen-plus Discord slash commands put the same data in chat: one user's full action chain and mod notes, keyword search over removed content, per-mod leaderboards, reason breakdowns, overturn rankings, activity-over-time charts.
Two halves: a listener in the cloud and a worker at home. The listener records every new submission from a monitored community to a shared database as it's posted, and archives the moderation record alongside it. The home worker checks that database on a schedule and pulls the flagged media down to local storage. Splitting it this way keeps the heavy downloads off the 1 GB cloud box, which has no room for them.
The service behind the website dashboard. It polls Twitch, an F1 API, Letterboxd, and a couple of release calendars on staggered schedules, leaning in harder while a stream is live. Letterboxd has no API, so it's scraped directly into rated, tagged diary entries. The release data comes from a local iCal feed, enriched through the Metron API. Each source writes a static JSON file the site reads, so the page never makes an API call of its own.
An Android app for reading the bot logs from my phone. It SSHes into Server A over Tailscale and tails the live output, or the last 1,000 lines, from any of the bot services. Errors, warnings, and ordinary lines are colour-coded so problems stand out, and you can flip between live tail and history. It's built in Kotlin with Jetpack Compose and SSHJ, and the terminal look matches the site's teal-on-navy.
Watches a prepaid account and works out when it'll hit zero. It compares the balance against what's going out, projects the run-dry date from the burn rate, and emails and pushes a warning early enough to top up before anything lapses. It runs on a schedule, and I can also kick it off from my phone.
Gets new media from my desktop onto a tablet without me touching it. A Windows scanner notices new files and stages them to the server over SFTP; a Tasker flow on the tablet grabs them the moment they land. Every transfer is size-checked on both ends so nothing arrives half-written, and the tablet side is hardened against Android's habit of killing background apps and reclaiming storage.
Live status of the running services. The streaming bots are each a single process; the moderation side is several independent pieces, some always-on services and some scheduled jobs, each reporting in on its own.
Running bots and self-hosted infrastructure has real costs. If you'd like to help keep the lights on, donations are appreciated.
Support on Ko-fi