StreamGremlin
code.streamgremlin.com

StreamGremlin’s code

StreamGremlin is free and open source under the Apache License 2.0. Its Go module is named code.streamgremlin.com/streamgremlin, so the name stays the same wherever the code is kept. Today it’s on GitHub.

  • Get the code

    git clone https://github.com/shawnbutts/StreamGremlin.git

    CONTRIBUTING.md says what to install and how to build the app. make verify runs every check.

  • The command line

    go install code.streamgremlin.com/streamgremlin/cmd/streamgremlin@latest

    Needs Go 1.27 or newer. It talks to the StreamGremlin app running on the same computer; the app itself comes from the download page.

  • Help make it better

    Report a bug or an idea in GitHub’s issues, and a security problem privately. Every commit is signed off under the Developer Certificate of Origin.

Programs

What’s built from this code. The desktop app is a module of its own, code.streamgremlin.com/streamgremlin/cmd/desktop.

Import pathWhat it is
cmd/desktopCommand desktop is the StreamGremlin desktop app: the engine plus a window.
cmd/mockdiscordCommand mockdiscord runs a stand-in for Discord's webhooks, to try StreamGremlin's Discord posts without Discord.
cmd/mockobsCommand mockobs runs a stand-in for OBS's WebSocket server, to try StreamGremlin without OBS.
cmd/mocktwitchCommand mocktwitch runs a stand-in for Twitch's sign-in server, to try StreamGremlin without a registered Twitch app.
cmd/mockyoutubeCommand mockyoutube runs a stand-in for Google's sign-in and YouTube's Data API, to try StreamGremlin's YouTube sign-in without Google.
cmd/noticesCommand notices writes THIRD-PARTY-LICENSES.txt: the licence of every Go module built into the app or the command line, on macOS, Windows or Linux.
cmd/releaseCommand release makes the pieces of StreamGremlin's installers that need code, for make release (D-084).
cmd/screenshotsCommand screenshots photographs the desktop app for the website and its Help (M9 slice 4, D-112).
cmd/siteCommand site builds StreamGremlin's website into a folder the user hosts (M9 slice 4, D-112).
cmd/streamgremlinCommand streamgremlin is StreamGremlin's command line.

Packages

StreamGremlin’s own parts, under internal/: Go lets only StreamGremlin import them. ARCHITECTURE.md says how they fit together.

Import pathWhat it is
internal/actionsPackage actions registers the steps workflows can use, on top of the Twitch and OBS services.
internal/adsPackage ads asks Twitch for ad breaks and snoozes, and records what it asked and what Twitch said happened, apart (docs/adr/0024-ads.md).
internal/apiPackage api is the authenticated local API.
internal/audiencePackage audience says who's around while you're live (M6 slice 7, D-102): every few minutes, the people who spoke in chat lately, on Twitch and YouTube ("active chatters"), and each minute Twitch's viewer count when it changes enough.
internal/backupPackage backup makes, checks and restores backups of the data folder (docs/adr/0012-backups-and-retention.md).
internal/banlistsPackage banlists keeps the streamer's own ban lists and applies them to Twitch (M8 slice 2, D-106, docs/adr/0049-ban-lists.md).
internal/brandPackage brand holds the product name and every name derived from it.
internal/buildinfoPackage buildinfo reports the version the binary was built as.
internal/buttonsPackage buttons keeps the streamer's buttons (M7, D-075): the Live screen shows them, and later the Stream Deck presses them.
internal/chatPackage chat holds what chat means on every platform, starting with recognising a command such as "!so @friend".
internal/chatsendPackage chatsend sends one chat message to Twitch, YouTube or both (M6 slice 3, D-098): the send box on Live, workflows' "Send a chat message" and the API all use it.
internal/clientPackage client calls the local API from the command line.
internal/clientsPackage clients pairs outside programs with the engine and gives each its own API token (M7 slice 4; docs/adr/0042-paired-clients.md).
internal/commandsPackage commands keeps one answer per chat command (docs/adr/0011-command-ownership.md).
internal/datadirPackage datadir finds, creates and locks the data folder: the one place StreamGremlin keeps its database, API token, logs and backups.
internal/desktopPackage desktop is the desktop app's side of the engine: it serves the UI and hands the UI's /v1/* requests to the API in-process, adding the token.
internal/discordPackage discord posts to Discord channels through webhooks (D-089): a link Discord makes for one channel, which needs no bot account.
internal/earningPackage earning pays points for taking part (docs/adr/0033-earning-rules.md): chatting (with a wait between awards per viewer), a first follow (once per viewer, ever), subscribing and resubscribing by tier, gifting subs (to the gifter), and cheering bits.
internal/eventsPackage events is the event inbox: every outside or simulated event is checked, stored once in arrival order, and readable from any position (docs/adr/0004-events.md).
internal/filtersPackage filters catches spam in chat and acts on it as the streamer (D-085): links, capitals, symbols, emotes, repeats and the streamer's own words.
internal/firstwordsPackage firstwords finds each viewer's first chat message of a stream and adds a chat.first_words.received event for it (docs/adr/0021-first-words.md).
internal/followbotsPackage followbots finds follows that look like follow bots, for the streamer to review (M8 slice 5, D-109): Twitch follows from accounts made only days before they followed, that never chatted, especially when several came within minutes.
internal/giveawaysPackage giveaways runs chat giveaways (docs/adr/0025-giveaways.md): viewers enter with a chat command, every entry has the same chance (unless subscribers were given more, said in chat), the entry list is fingerprinted when entries close, and each winner is drawn with crypto/rand and recorded so anyone can check the draw.
internal/givesPackage gives gives points to everyone in a group at once (docs/adr/0035-giving-to-everyone.md): everyone in chat (Twitch's chatter list), everyone who chatted in the last N minutes, or subscribers (Twitch's subscriber list).
internal/hostPackage host starts and stops the engine: data folder lock, database, services and the local API.
internal/linksPackage links joins a Twitch viewer and a YouTube viewer who are one person (M6 slice 6, D-101, docs/adr/0048-viewer-links.md).
internal/loggingPackage logging sets up structured logs to stderr and to a file in the data folder.
internal/luaPackage lua runs streamers' Lua scripts in separate worker processes, with time and memory limits (docs/adr/0037-lua-scripts.md).
internal/maintenancePackage maintenance keeps the data folder tidy while the engine runs (docs/adr/0012-backups-and-retention.md): a backup every day, keeping the newest few, and a sweep of old events and finished runs.
internal/mockdiscordPackage mockdiscord is a stand-in for Discord's webhook endpoints (D-089): Get Webhook with Token and Execute Webhook, as Discord's reference documents them, for tests and for trying StreamGremlin without Discord.
internal/mockobsPackage mockobs is a stand-in for OBS's obs-websocket 5 server, for tests and for trying StreamGremlin without OBS.
internal/mocktwitchPackage mocktwitch is a stand-in for Twitch's OAuth server (id.twitch.tv), for tests and for trying the app without a real Twitch app.
internal/mockyoutubePackage mockyoutube is a stand-in for Google's sign-in and the parts of YouTube's Data API StreamGremlin uses, for tests and to try StreamGremlin without Google (M6, D-096).
internal/moderationPackage moderation sends the streamer's moderating requests to Twitch or YouTube and records each one with the platform's answer (M7 slice 3, M6 slice 5; docs/adr/0041-moderation-record.md).
internal/noticesPackage notices collects the licences of the Go modules built into StreamGremlin's programs, for THIRD-PARTY-LICENSES.txt, and checks that THIRD-PARTY-NOTICES.md names each of them.
internal/obsPackage obs connects to OBS Studio over obs-websocket 5: scenes, sources, filters, audio, text, media, recording, the replay buffer and screenshots, plus scene snapshots that switch back only while StreamGremlin still owns the scene (docs/adr/0007-obs.md).
internal/overlaysPackage overlays serves OBS browser sources (docs/adr/0017-alert-overlays.md).
internal/overlays/themesoundsPackage themesounds makes the built-in wheel looks' sounds (docs/adr/0036-packages-plugins-and-themes.md, D-065).
internal/overviewPackage overview works out what Home shows (D-018 in docs/DECISIONS.md): the Get set up checklist, ticked from real state and never from what the streamer clicked, and what needs the streamer now.
internal/packagesPackage packages installs, lists and removes packages: overlay themes now, Lua plugins later (docs/adr/0036-packages-plugins-and-themes.md).
internal/pluginsPackage plugins runs installed Lua plugins (docs/adr/0038-lua-plugins.md): their steps in workflows, one worker for each that's on, restarting one that dies, doing what each asks within its approved permissions, and recording every call it makes.
internal/pointsPackage points keeps the points ledger (docs/adr/0032-viewers-and-points.md): append-only and double-entry.
internal/quotesPackage quotes keeps numbered quotes from the stream (docs/adr/0014-quotes.md), as Streamer.bot's Quotes: add, show one at random or by number, edit and delete.
internal/raidguardPackage raidguard notices a hate raid as it starts (M8 slice 3, D-107): a burst of chatters StreamGremlin has never seen, or of Twitch accounts made only days ago, within a short time, watched more closely for a while after a raid arrives.
internal/releasePackage release makes the pieces of StreamGremlin's installers that need code: the Windows icon file, the macOS app's Info.plist, and the version in the forms each system wants (D-084).
internal/roundsPackage rounds keeps plugins' rounds (docs/adr/0039-points-for-plugins.md): viewers' stakes held for a game, paid out or refunded, exactly once.
internal/secretsPackage secrets keeps sign-in tokens and passwords out of the database, logs and API answers (docs/adr/0003-secrets.md).
internal/settingsPackage settings stores small, typed settings documents in the settings table.
internal/soundsPackage sounds keeps the sound library and asks the desktop app to play sounds (docs/adr/0016-sounds.md).
internal/storagePackage storage opens the SQLite database and applies its schema.
internal/templatesPackage templates holds the ready-made workflows the gallery offers (D-019 in docs/DECISIONS.md).
internal/timersPackage timers runs Streamer.bot-style timed actions (docs/adr/0013-timers.md): a timer goes off every so often, or on a cron schedule, and records a timers.timer.fired event that workflows match by timer_id.
internal/twitchPackage twitch connects to Twitch: sign-in with the device code flow for the streamer's account and an optional bot account, keeping tokens fresh and valid (docs/adr/0005-twitch-sign-in.md).
internal/variablesPackage variables keeps the values workflows save between runs and restarts (docs/adr/0009-variables.md).
internal/viewersPackage viewers keeps a record per platform account that has taken part (docs/adr/0032-viewers-and-points.md): their names, when they were first and last seen, and how much they've chatted.
internal/wheelsPackage wheels spins wheels of weighted choices (docs/adr/0030-wheels.md): the next game, a challenge, a mystery pick.
internal/workflowPackage workflow runs streamers' automations: when an event matches a trigger, run steps (docs/adr/0008-workflows.md).
internal/youtubePackage youtube connects to YouTube (M6, D-095, D-096): Google's sign-in for desktop apps for the streamer's channel and an optional bot channel, keeping tokens fresh, and counting the Data API's daily quota (docs/adr/0046-youtube-sign-in.md).
internal/youtube/livechatPackage livechat is the wire format of YouTube's live chat stream, liveChatMessages.streamList over gRPC (M6 slice 2, D-097, docs/adr/0047-youtube-chat-stream.md).