- Ensure you have Node.js version 14 or later installed
- Optionally, install VSCode for easier editing
- Run the command
npm ci
in this directory to install the necessary packages. This may take a few minutes - Follow the steps in the App Settings section
- Install the
ffplay
binary. See the Audio Requirement section - Run
npm run start
to begin the script - Type
exit
in the terminal to exit. You can useCTRL+C
to exit as a last resort.
You can type the following commands directly into the terminal window while the application is running.
Command | Purpose |
---|---|
exit | Safely ends the application |
restart | Stops and then starts all of the services. Some state information is not cleared, making this approach better than an exit/start while stream is live. |
In order to play audio, you will need to download a binary file for this application to use.
- Go to https://www.gyan.dev/ffmpeg/builds/
- Scroll down to the Release section
- Download the file that ends with
ffmpeg-release-essentials.7z
- if you don't have 7-Zip installed, download the one ending in
.zip
instead of.7z
- if you don't have 7-Zip installed, download the one ending in
- Create a folder in this project's root folder called
.bin
- Extract the file
ffplay.exe
from theffmpeg-4.4-essentials_build/bin
folder, into the.bin
folder
When running the application either for the first time ever, or first time after an update, the settings file will be evaluated. If the file is lacking in settings, it will be updated with placeholder information, a warning message will be displayed, and the app will exit. If this happens, please update the settings according to the following sections.
This file contains confidential information. It will not leave your PC, and you should not show its contents to anyone else.
Property | Description |
---|---|
channel | The Twitch channel name to connect to |
identity | Your identity to use when connecting to the Twitch channel for chat functionality |
pubsub | Credentials to use for Twitch PubSub notifications of Bits, Points, and Subs. |
streamElements | StreamElements API token. Currently not used. |
obsWebsocket | Websocket connection info for OBS functionality |
arrivalNotifications | Notifications when certain users arrive in chat |
commandTriggers | Scenes to trigger when certain commands are issued in chat |
bitTriggers | Scenes to trigger when bit cheers happen |
pointTriggers | Scenes to trigger when point redemptions happen |
counter | Configuration for a counter. See Using a Counter for more info |
Go to https://dev.twitch.tv/console/apps
- Click Register Your Application button
- Name: Type any name
- URL: http://localhost
- Category: Chat Bot
- Click Create button
You should see a Client ID token.
Go to https://twitchapps.com/tmi/
- Click Connect
- Authorize TMI with Twitch, if prompted
You should see a text field starting with "oauth:". Copy the entire text, this is your password.
- Go to https://twitchtokengenerator.com/
- Under Helix, toggle Yes for these
bits:read
chat:read
channel:read:redemptions
channel:read:subscriptions
- Click the green Generate Token! button.
- Authenticate with Twitch.
- Copy the Access Token, Refresh Token, and Client Id.
- Paste those values into the appropriate config fields
In your app.settings.json
file, fill in the address
and password
fields.
address
will likely just be localhost:4444
as long as you haven't changed the default settings.
In your app.settings.json
file, add settings for each user you wish to have notifications for.
Property | Description |
---|---|
disabled | if true, this arrival notification will not be activated |
username | the case-insensitive name of the user on Twitch. |
sceneName | the name of the scene in OBS which has the sourceName to be toggled. |
sourceName | the name of the source in OBS which will be turned on, and then off. |
durationInSeconds | is how long to keep the source active for, in seconds. |
"arrivalNotifications": [
{
"disabled": true,
"username": "UserA",
"sceneName": "Scene",
"sourceName": "Image1",
"durationInSeconds": 5
},
{
"username": "UserB",
"sceneName": "Scene",
"sourceName": "Image2",
"durationInSeconds": 15
}
]
In your app.settings.json
file, add settings for each command you wish to activate a scene for.
See the "What is a Scene" section for more information about the individual tasks that comprise a scene.
With a command trigger, you will need to supply the command
which triggers it. This command will be issues in chat like !command Hello!
though when writing your rule, exclude the leading exclamation point as well as any text after the command.
The command
text can be empty if you wish to drive this command by local keyboard presses only. If this case, add the key
property (See Key Shortcuts) and leave the command
property as an empty string ("command": ""
)
The command
text can only be alphanumeric characters with no spacing. ABCabc123
is acceptable for a command, though keep in mind that we evaluate the command as lowercase for matching. So ABCabc123
is the same as abcabc123
and if you have multiple entries with the same command text, only the first will be ran.
The following are optional properties
Property | Description |
---|---|
disabled | If true, the command will be disabled |
restrictions | Restricts who can issue the command. See User Permissions for info |
users | A list of Twitch usernames who can execute the command |
cooldown | A cooldown, in seconds. Subsequent similar commands issued during a cooldown period will be rejected. |
key | If provided, this keypress will execute the action. See Key Shortcuts for important info. |
aliases | A list of other commands which can trigger this |
"commandTriggers": [
{
"command": "test",
"aliases": ["butt", "butts"],
"restrictions": {
"moderator": true,
},
"users": [
"GrumbleMutt"
],
"cooldown": 10,
"directives": [
{
"type": "chat",
"message": "Hello, {{username}}"
},
{
"type": "chat",
"delayInSeconds": 3,
"message": "Goodbye, {{username}}"
},
{
"type": "audio",
"filename": "c:/temp/file_example_OOG_1MG.ogg"
},
{
"type": "obs",
"durationInSeconds": 3,
"sceneName": "Scene",
"sourceName": "Image"
}
]
}
]
The above command trigger will:
- Trigger when
!test
is issued in chat - Be available for moderators only
- Will be rejected if there's another command like this queued
- Will wait until at least 10 seconds after the last command of this type ran
- When executed, will immediately
- say "Hello, user" in chat
- start playing the audio file
- turn on the "Image" source in OBS
- Then, 3 seconds later
- say "Goodbye, user" in chat
- turn off the "Image" source in OBS
If the audio clip is longer than 3 seconds, the trigger will not conclude until it has finished playing.
In your app.settings.json
file, add settings for each command you wish to activate a scene for.
See the "What is a Scene" section for more information about the individual tasks that comprise a scene.
With a bit trigger, you will need to supply the minAmount
which triggers it. The trigger with the highest minAmount
which the bit cheer passes will be used.
The following are optional properties
Property | Description |
---|---|
disabled | If true, the command will be disabled |
key | If provided, this keypress will execute the action. See Key Shortcuts for important info. |
"bitTriggers": [
{
"minAmount": 0,
"directives": [
{
"type": "chat",
"message": "{{username}} cheered {{amount}} bits"
}
]
}
]
In your app.settings.json
file, add settings for each command you wish to activate a scene for.
See the "What is a Scene" section for more information about the individual tasks that comprise a scene.
With a point trigger, you will need to supply the rewardTitle
which triggers it. Casing does not matter, but it should otherwise be the complete title.
The following are optional properties
Property | Description |
---|---|
disabled | If true, the command will be disabled |
key | If provided, this keypress will execute the action. See Key Shortcuts for important info. |
"pointTriggers": [
{
"rewardTitle": "Posture Check!",
"directives": [
{
"type": "chat",
"message": "{{username}} redeemed {{rewardTitle}}"
}
]
}
]
For Chat, Bit, and Point commands, you can invoke them with a single keyboard key. When these commands are ran via keypress, they are added to the queue without regard for delay timing, user restrictions, or any other specific restrictions.
The message replacement strings for username
will always be the name of the channel
from the settings file.
For a list of key names to use in your settings file, please look at ./keys/mappings.ts
. You need to use the right-side text, as-is, in your configuration.
If you have multiple active commands with the same key, they will all be queued. Order of queueing is not guaranteed.
{
"key": "Page Up",
"directive": []
}
If you have trouble with finding a key in the mappings.ts
file, you can run npm run keys:debug
which will start a listener for keypresses. Press the key you wish to use. The numeric code and the mapping text (if available) will be shown.
For features which can be restricted by the user's information.
{
"broadcaster": true,
"moderator" : true,
"subscriber" : true,
"vip": true
}
User Is | VIP | Subscriber | Moderator | Broadcaster | Regular |
---|---|---|---|---|---|
Broadcaster | O | O | O | O | O |
Moderator | O | O | O | O | |
Subscriber | O | O | |||
VIP | O | O | |||
Regular | O |
This is not referring to an OBS scene, but rather a grouping of tasks to be performed, using the available functionality of this app. You will use these scenes as reactions to events like commands, bits, and cheers.
The following task types are available.
All tasks in a scene will execute at the same time. You can specify the optional delayInSeconds
property in any task to delay its execution.
Will play audio locally.
{
"type": "audio",
"filename": "c:/folder/file.ogg"
}
If you provide an array of files in the filename
property, one of the files will be played at random
{
"type": "audio",
"filename": [ "c:/folder/file.ogg", "c:/folder/file2.ogg" ]
}
Will toggle the visibility of a scene in OBS
{
"type": "obs",
"sourceName": "Image-1",
"sceneName": "Main",
"durationInSeconds": 10
}
If you provide an array of source names in the sourceName
property, one of the sources will be used at random
{
"type": "obs",
"sourceName": ["Image-1", "Image-2"],
"sceneName": "Main",
"durationInSeconds": 10
}
Will send a message to chat. The message text can contain replacements for certain variables.
The following variables are currently available
Token | Description |
---|---|
{{username}} | Replaced with the user who triggered the event |
{
"type": "chat",
"message": "Hello, {{username}}. I love you."
}
Will start and stop the motor on a connected Lego Hub.
{
"type": "motor",
"power": 100,
"durationInSeconds": 3.5
}
Changes the current counter value by the number specified.
{
"type": "counter",
"change": 1
}
To decrement, use a negative number.
{
"type": "counter",
"change": -10
}
To use a counter, first add some basic information to the app.settings.json
file's counter
setting.
Property | Description |
---|---|
sourceDirectory | The directory to store the "database" or counts, and the overlay text file for OBS |
gameId | The name of the game being played |
text | (Optional) Text to write to the OBS overlay file. Can use the token {{count}} to replace with the count |
disabled | (Optional) If true, will disable this section |
This counter will store a JSON file and a text file in the sourceDirectory
folder. When starting the app, changing the gameId
or the disabled
properties, the contents of the JSON file will be read to find a matching entry for gameId
. Case matters, so please reference that file if you've forgotten the game id.
Reference the Counter scene above for how to add the incrememnt / decrement to a command.
This isn't really used yet (and may not be used ever), but the bones of using their API are in place. Don't bother with this section until/unless it's implemented in this project.
To get your token:
- Log into Stream Elements web app
- Go to your account page ( https://streamelements.com/dashboard/account/channels )
- Toggle
Show secrets
- Copy the contents of the
JWT token
You can add that token the the app.settings.json
file as such:
"streamElements": {
"token": ""
},