Skip to content

Development

WolfwithSword edited this page Dec 22, 2025 · 8 revisions

Local Setup

Open the solution project in your IDE of choice and install the dependencies and resolve the project using dotnet restore SubathonManager.sln

When you build and run locally through your IDE, you should be running SubathonManager.UI as the main project.

Design

The design is documented in this drawio diagram. Right-click the link and select "save lined content as" to download.

You can view renders of the diagram here.

It contains the overall flow, flow per service, db organization, enums, config, and websocket data.

If you make any new changes that require updates to the design, detail them in your pull requests such that we can update it in the source.

Issues and Feature Requests

Please submit a github issue if you have a bug or feature request.

For bugs, if requested, you may be asked to attached a logfile as well, which you can get from opening the data folder from the settings page, and going to the logs directory.

Please check if an issue or feature request exists similar to yours before submitting!

Pull Requests

For all pull requests, they must tie to an issue or enhancement before they will be reviewed.

For fixes or new features, please describe in detail use cases or situations your changes will affect and, if possible, provide screenshots or recordings of said change.

Maintainers will review all PR's prior to merging.

New Widget Presets

If you are submitting a new preset to include, please make sure your PR only includes net-new files in the presets folder.

Ideally, if you make your own preset widget(s), that you would like to share freely, you can have them in your own repo and link to this project.

We would like to have a diverse set of presets in the base project, but also want to avoid bloating it for users. Eventually, we may create a list of overlays linked within the wiki here. So submit an issue or discussion for these! We'd love to see them.

External Services

We support external services that are not native to be integrated into the Subathon Manager.

Specifically, external commands, ExternalDonation's, and ExternalSub's.

External Commands

POST /api/data/control

{
    "user": "name", // string, optional. Defaults to "External"
    "command": "CommandName", // string, must match a SubathonCommandType Enum Name. Required.
    "message": "", // string, command parameters or empty - required.
    "type": "Command" // required
}

The commands will be processed through the CommandService, so formatting for message parameters will be the exact same as if it were from a Twitch or Youtube Chat command. See Usage - Commands for details.

ExternalDonation

POST /api/data/control

{
    "user": "name", // string, optional. Defaults to "External"
    "currency": "USD", // string. any valid 3 char currency code. Required.
    "amount": "12.34", // float as string. Required.
    "type": "ExternalDonation", // required
    "id": "5fa57900-a69d-4944-9ff1-64c25801fb62" // guid. optional - if you want to persist a unique guid from your service.
}

External donations are accepted and will follow the configuration value for seconds/points per dollar unit of default currency after conversion. Exception to configuration value will be certain overrides, such as KoFiDonation.

user of "SYSTEM" will treat it as a Simulated event source instead of External.

ExternalSub

POST /api/data/control

{
    "user": "name", // string, optional. Defaults to "External"
    "amount": 1, // int, optional. Default 1. This is number of "subs". Like if it were a mass gift of subs.
    "value": "Tier 1", // string, optional. Will be used for logging and informational display reasons. If empty, will be "External".
    "seconds": 60, // int. Required. Can be 0. How many seconds should this event add?
    "points": 1, // int. Required, Can be 0. How many points should this event add?
    "type": "ExternalSub", // required
    "id": "5fa57900-a69d-4944-9ff1-64c25801fb62" // guid. optional - if you want to persist a unique guid from your service.
}

External subscriptions or memberships are accepted. They will require (unless exception, such as KoFiSub override) seconds to add and points to add to be supplied, as no in-app configuration exists. It will be affected by active multipliers.

user of "SYSTEM" will treat it as a Simulated event source instead of External.

Config Management

We support a way to remotely fetch and reconfigure the seconds or points of most EventTypes.

GET /api/data/values

[
  {
    "eventType": "TwitchSub",
    "source": "Twitch",
    "meta": "1000",
    "seconds": 60,
    "points": 1
  },
  {
    "eventType": "TwitchSub",
    "source": "Twitch",
    "meta": "2000",
    "seconds": 120,
    "points": 2
  }
  //...
]

Will return all subathon values that have a points or seconds configuration, along with their source and meta data.

The format it comes in will also be used to send PATCH/POST/PUT requests to update it.

PATCH /api/data/values

POST

PUT

payload

[
  {
    "eventType": "TwitchSub",
    "source": "Twitch",
    "meta": "1000",
    "seconds": 20,
    "points": 1
  }
]

You can send any number of values to update, and it will only modify those provided. Seconds or Points can be absent (but one must be present) - absent on will not be updated.

Meta must be included, but for most will be an empty string. It is primarily for Subs/Memberships that have a tier name/value.

EventType and Source are required.

Any changes done via the remote PATCH will be logged (depending on configuration) to the Error log Webhook URL (ignore the name).

Other

Amounts

GET /api/data/amounts

View a summary of all event amounts that have come in, such as number of subs of X type, total dollars per currency, etc.

All events properly processed by the subathon. It will be split with "real" and "simulated"/"system" events.

Partial example - it only shows value things that have come in.

{
  "simulated": {
    "DonationAdjustment": {
      "CAD": -138990
    },
    "TwitchCheer": 15300,
    "TwitchCharityDonation": {
      "CAD": 310
    },
    "TwitchSub": {
      "T1": 100
    },
    "TwitchGiftSub": {
      "T1": 1595,
      "T2": 240,
      "T3": 50
    },
    "TwitchRaid": {
      "count": 2,
      "total_viewers": 50
    },
    "YouTubeSuperChat": {
      "CAD": 20
    },
    "YouTubeMembership": {
      "DEFAULT": 1
    },
    "YouTubeGiftMembership": {
      "DEFAULT": 199
    }
  },
  "real": {
    "TwitchFollow": 1,
    "ExternalDonation": {
      "USD": 119057.12
    }
  }
}

Status

GET /api/data/status

Get the current status of the subathon.

Ex

{
  "millis_cumulated": 2231441768,
  "millis_elapsed": 11652000,
  "millis_remaining": 2219789768,
  "total_seconds": 2219789,
  "days": 25,
  "hours": 16,
  "minutes": 36,
  "seconds": 29,
  "points": 2754,
  "is_paused": true,
  "is_locked": false,
  "is_reversed": false,
  "multiplier": {
    "running": false,
    "apply_points": false,
    "apply_time": false,
    "is_from_hypetrain": false,
    "started_at": "2025-12-21T18:05:06.9150335",
    "duration_seconds": 0,
    "duration_remaining_seconds": 0
  }
}

Clone this wiki locally