Skip to content

Repository files navigation

NextGoal

NextGoal

One sub/member goal for OBS — Twitch subs, YouTube members, and Kick subs, combined into a single auto-incrementing counter.

Stable · Built with Electron and Vue 3

Release Platform Built with License PRs welcome

Install · For devs · Architecture · Security · Contributing

Warning

This project was built with AI assistance (vibe coded). While it works, the code hasn't been professionally audited — use at your own risk. If you get a chance, feel free to review the code before deploying. PRs and fixes are always welcome!

Overview

NextGoal is a desktop goal tracker for live streamers. It combines Twitch subs, YouTube members, and Kick subs into one counter and shows it on stream through OBS. It starts at 0 each stream (or syncs to your current totals), and every time you hit the goal it raises itself by your chosen increment — so there's always a next goal.

It runs as a free-form desktop window with a tray icon for quick show/hide, and feeds OBS over obs-websocket with no plugin required (plus a text-file fallback). Twitch counts in real time (EventSub); YouTube and Kick are polled for their current totals on an interval (default 60s), since neither offers a practical real-time feed for a desktop app.

What makes NextGoal different:

  • One counter, three platforms. Twitch, YouTube, and Kick sub/member counts roll into a single number. Link whichever platforms you stream on; the ones you skip simply don't appear.
  • No OBS plugin. It talks to OBS over obs-websocket and can also write a plain text file, so it works with any OBS setup without installing anything.
  • No client secret to leak. Twitch uses Device Code Flow with a Public client — nothing sensitive is baked into the binary, and the access token is encrypted at rest.
  • Lives in the tray. A free-form, always-available window with a one-click tray toggle and idle/live status icon — built to sit quietly next to OBS.

Installation

  1. Download NextGoal-Setup.exe — the button above grabs it straight from the latest release.
  2. Run it — it's a one-click, per-user installer, so no admin rights are needed and it installs just for you.
  3. On first launch, Windows SmartScreen may warn that the app is unsigned. Click More info → Run anyway (see Security for why that happens).
  4. Open NextGoal, link your Twitch account (YouTube and Kick are optional), set your goal and increment, and point it at OBS.
  5. After that it updates itself — new releases download in the background and install on quit.

Windows only for now. Prefer to build it yourself, or on another platform? See Run it and Build an installer.

For devs

Run it

npm install
npm run dev          # launches Electron with hot reload

For local dev, each platform login needs its own client credentials, passed as env vars. Twitch is required; YouTube and Kick are optional (leave them out and that platform just can't be linked):

# bash
TWITCH_CLIENT_ID=... \
YOUTUBE_CLIENT_ID=... YOUTUBE_CLIENT_SECRET=... \
KICK_CLIENT_ID=... KICK_CLIENT_SECRET=... \
npm run dev
  • Twitch — register a Public client at dev.twitch.tv/console/apps, redirect http://localhost, category Broadcasting Suite. Scope: channel:read:subscriptions.
  • YouTube — a Google Cloud project with YouTube Data API v3 enabled and an OAuth client of type Desktop app (Authorization Code + PKCE via a loopback redirect — Google auto-allows http://localhost, no redirect to register). Add the youtube.readonly and youtube.channel-memberships.creator scopes; the memberships one is sensitive, so keep the app in Testing mode and add your channel's Google account as a test user to use it without full verification. The channel must be in the Partner Program with memberships on. Desktop-app clients carry a client secret; for an installed app Google treats it as non-confidential (it ships in the binary). To drop the "unverified app" warning for the public (rather than just test users), submit the consent screen for Google verification and publish it to Production — the scopes are sensitive, not restricted, so it's brand review only (verified domain, privacy policy, homepage, logo, demo video), with no paid security assessment.
  • Kick — a Kick Developer App (OAuth 2.1 + PKCE, a confidential client: client ID and secret). Register http://localhost as an allowed redirect and request the user:read, channel:read, and events:subscribe scopes. Kick has no "read subscriber count" scope — sub data comes through the events subscription, so confirm the counting approach against the Kick dev docs.

Client IDs are public info (they ship in the built app). The YouTube and Kick secrets are the installed-app kind treated as non-confidential.

Build an installer

$env:TWITCH_CLIENT_ID="your_id"
npm run dist:win

Produces a one-click per-user installer in release/. The build injects your client ID into the bundle (scripts/inject-client-id.mjs) and refuses to build without it, so you can't ship a broken app.

Architecture

Main process (src/main)

  • index.js — wires everything, owns app state, all IPC
  • windows.js — one BrowserWindow: framed onboarding + free-form app window. Closing hides to the tray; quit from the tray menu.
  • tray.js — left-click shows/hides the window, right-click menu, idle/live icon
  • services/
    • twitchAuth.js — Device Code Flow, no client secret, single-use refresh handling
    • tracker.js — EventSub WebSocket, gift-bomb double-count fix
    • obs.js — obs-websocket: connect, list/create/set text sources
    • config.js — settings JSON + refresh token in Electron safeStorage (encrypted)
    • goal.js — the increment math
    • updater.js — electron-updater wrapper, prerelease-aware

Preload (src/preload/index.js) — the only bridge. Renderer sees window.ng and nothing else. contextIsolation on, nodeIntegration off.

Renderer (src/renderer) — Vue 3 + hash router. /onboarding and /app. The main process picks which loads. Never sees the token, never opens a socket.

Project structure

Directory Purpose
src/main/ Electron main process: app state, IPC, tray, and platform services
src/preload/ The single context-isolated bridge exposed to the renderer
src/renderer/ Vue 3 UI — onboarding, main app window, and settings
scripts/ Build helpers (client-ID injection) and platform auth probes
test/ Headless tests — anchor positioning, sources, and updater logic
build/ Installer icons and packaging assets

Security

  • No client secret. Device Code Flow + Public client. Nothing extractable from the binary.
  • Token encrypted at rest via safeStorage (Windows Credential Manager). Falls back to a file with a visible warning only if encryption is unavailable.
  • Minimal scope: channel:read:subscriptions. Can't post, moderate, or change anything.
  • OBS password held in memory only, never written to disk.
  • Unsigned build → SmartScreen warns on first run. Code signing (~$100–300/yr) is the fix if you want it gone.

Test

npm test    # anchor positioning + updater prerelease logic

Both suites run headless (electron stubbed), no display needed.

Contributing

Contributions are welcome. To get started:

npm install
npm run dev     # hot-reload dev build (needs TWITCH_CLIENT_ID — see "Run it")
npm test        # run the headless test suites before opening a PR

Open an issue for bugs or ideas, and keep pull requests focused. If a change touches platform auth, OBS output, or the updater, please note how you tested it against the real service.

License

MIT © Sid Kapahi

About

One auto-incrementing sub and member goal for OBS, across Twitch, YouTube, and Kick.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages