Author Topic: Twitch Plays style chat integration with VoiceAttack?  (Read 3938 times)

Robertsmania

  • Newbie
  • *
  • Posts: 46
Twitch Plays style chat integration with VoiceAttack?
« on: July 22, 2020, 12:59:40 PM »
I have a project I want to do with VoiceAttack and Twitch chat that is similar to the "Twitch Plays" kind of thing.

The goal is to have viewers in Twitch chat be able to enter commands that trigger actions in VoiceAttack. 

Has this sort of thing already been done?  I've searched these forums and the internet a bit, and haven't found anything thats a match so far, but it seems like that's the first question to ask.

----
I broadcast iRacing and have setup commands within VoiceAttack to do dynamic replays and camera switches while I'm driving.

Right now its setup and triggered by voice commands on my side.  The command sequences within VoiceAttack send keystrokes to the iRacing simulation for cameras and replay, and to OBS for scene transitions.  So far, its been great and adds a lot to my streams. 

Here's a really brief example video:
https://www.youtube.com/watch?v=_uims3ULXNQ



The project I want to do next is to make it so viewers can trigger the same commands in VoiceAttack from the Twitch chat.

Someone watching could enter something like: !replay car 11

And the goal is to have it trigger the same command I get when I say "replay car 11"



The chat system is really IRC under the hood, and developing IRC bots to interact with viewers is not uncommon. I think what I want to do is have an IRC bot that can interact with VoiceAttack.  My naive thought would be to have it send keystrokes to that VoiceAttack, but maybe there are more elegant ways.  Perhaps a VoiceAttack plugin that integrates with the chat bot?

If people have suggestions, ideas or questions I'd love to get feedback and any help is appreciated.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4782
  • RTFM
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #1 on: July 22, 2020, 01:07:18 PM »
A plugin could certainly work, but if you have a chat bot that can launch applications with parameters, you can have it trigger VoiceAttack commands.

You can execute commands in a running instance of VoiceAttack using the "-command" parameter, E.G.
Code: [Select]
"C:\Program Files (x86)\VoiceAttack\VoiceAttack.exe" -command "my command"
There is a list of available parameters in the "Command Line Options" section of VoiceAttackHelp.pdf (Press F1 while VoiceAttack has focus to open VoiceAttackHelp.pdf in your default PDF viewer)

Robertsmania

  • Newbie
  • *
  • Posts: 46
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #2 on: July 22, 2020, 02:03:07 PM »
Yes, that should work.  Thank you so much for the prompt reply!

Robertsmania

  • Newbie
  • *
  • Posts: 46
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #3 on: July 29, 2020, 02:13:28 AM »
The VoiceAttack portion of the integration was very easy!

Checking the syntax of the command from Twitch chat, and other little details took a lot more effort.
But in the end, it totally works!!!

https://www.youtube.com/watch?v=77f-t9Ad4Ww

Thanks again.

Robertsmania

  • Newbie
  • *
  • Posts: 46
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #4 on: December 22, 2021, 10:43:23 AM »
I get messages on this forum from time to time from users asking about this kind of Twitch Chat / VoiceAttack integration.  I'm happy to try and help and reply to them, but I also thought it might be good to post some of the information here for posterity.

The way it works is a Streamlabs Chatbot python script takes commands from the Twitch Chat, does some logic to decide what to do.  If appropriate, it uses the subprocess.call() method in python to run the voiceattack.exe as if from the command line to actually execute a command.


To start off, you can open the windows command prompt and see if you can get VoiceAttack to run commands for you manually.  Have VoiceAttack running and then from the prompt, try commands like this:
Code: [Select]
VoiceAttack.exe -command testThat should fire off the corresponding "test" command in voice attack.  Either make a simple command called "test" or use something that does exist in your profile.  If voiceattack.exe is not in your path, you may have to change to the directory where its linstalled (C:\Program Files(x86)\VoiceAttack" by default).

If you can run the commands manually from the command line, then the next step is to get a python script to run them in response to interactions with chat.

The python script I have works inside the Streamlabs Chatbot framework.  I recommend starting with this tutorial if this kind of thing is new to you:
https://betterprogramming.pub/creating-a-twitch-command-script-with-streamlabs-chatbot-step-by-step-a9f8cccd680d

That guide is what I started with when I had no experience with any of this stuff (python, chatbot, stream labs) and it was good in terms of a step by step setup for the supporting software and framework.  If you can get the script they have you make working, then chances are good you can expand that to make it do what you want with Twitch chat to VoiceAttack integration.


If you can get their demo script setup and working, this is a sample of the first draft of my !PitGIrl script.  Depending on what you are trying to do, it may be a good place to start in terms of parsing the Twitch chat and calling VoiceAttack. 

Code: [Select]
ScriptName = "PitGirl"
Website = "http://robertsmania.com/"
Description = "PitGirl is love"
Creator = "Robertsmania"
Version = "1.0.0"
Command = "!pitgirl"
va_binary = r"C:\Program Files (x86)\VoiceAttack\VoiceAttack.exe"

camera_dict = OrderedDict()
camera_dict["chase"] = "set camera to chase"
camera_dict["cockpit"] = "set camera to cockpit"
camera_list = ', '.join(camera_dict.keys())

def Execute(data):
    if data.GetParam(0) != Command:
        return
    elif data.GetParam(1) != "set" or data.GetParam(2) != "camera" or data.GetParam(3) != "to":
        usage()
        return
    elif not camera_dict.get(data.GetParam(4)):
        send_message(data.GetParam(4) + " is not a valid camera")
        usage()
        return

    send_message(camera_dict.get(data.GetParam(4)))
    va_cmd = [va_binary, "-command", camera_dict.get(data.GetParam(4))]
    subprocess.call(va_cmd, shell=True)
    return

def usage():
    send_message("use: !pitgirl set camera to (camera)")
    send_message("cameras are: " + camera_list)
    return

def send_message(message):
    Parent.SendStreamMessage(message)
    return

Thats just a snip of the script, but it follows the format they present in their example.  I have VoiceAttack commands for "set camera to chase" and "set camera to cockpit" that get called if the logic indicates that's what the user requested.  This was a first draft and is strict in terms of what it will accept.  In this example, if the user enters either of these commands into chat, it will fire off the corresponding command in VoiceAttack:
Code: [Select]
!pitgirl set camera to chase
!pitgirl set camera to cockpit


If you get something working so your Chatbot runs commands from VoiceAttack, you probably will want to go a little further and have it send additional parameters as well.  That way the commands can be more powerful/general and you can have VoiceAttack say things specific to the request.  In my case, I found having it say the viewer's name who made the request was really popular.

To do that, you can use the -passedText option when you call VoiceAttack.exe.  Again, try it manually first and make sure your commands work properly from the command line like so:
Code: [Select]
VoiceAttack.exe -command test -passedText "\"text here\""
Inside your VoiceAttack command, you access those values as  “~passedInteger1”, “~passedDecimal1”, “~passedBoolean1” and  “~passedText1”. 
See the documentation for handling these kind of passed values.
There is also this thread on the forum that might have helpful information:
https://forum.voiceattack.com/smf/index.php?topic=3357.30

Here's a sample of how to make the corresponding call from within python:
Code: [Select]
va_binary = r"C:\Program Files (x86)\VoiceAttack\VoiceAttack.exe"

def test(data):
    cmd_test = "test"
    prm_test = "\"text here\""
    run_command_test(cmd_test, prm_test)
    return


def run_command_test(command_string, param_string):
    va_cmd = [settings["va_binary"], "-command", command_string, "-passedText", param_string]
    subprocess.call(va_cmd, shell=True)
    log(2, str(va_cmd))
    return

« Last Edit: December 22, 2021, 03:09:12 PM by Robertsmania »

aezrath

  • Newbie
  • *
  • Posts: 4
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #5 on: January 02, 2022, 06:18:33 PM »
Great job, Robertsmania and thanks for sharing.

So i found this to be very interesting and useful feature as it allows for a lot of interactivity for a lot of games that are otherwise not Twitch interactive. So i made something similar but in node.js, without having to rely on streamlabs or python and it is way less resource heavy.

I made it as an example for Marbles on Stream switching cameras between players.

Here is a snippet if it helps anyone of my main file, index.js:

Code: [Select]
require('dotenv').config();
const tmi = require('tmi.js');

const client = new tmi.Client({
  options: { debug: true },
  connection: {
    secure: true,
    reconnect: true
  },
  identity: {
    username: 'username',
    password: process.env.TWITCH_OAUTH_TOKEN
  },
  channels: ['channel_name']
});
var block = false;
client.connect();

client.on('message', (channel, tags, message, self) => {
  // Ignore echoed messages.
  if(self) return;

  if(message.toLowerCase() === '!camera 1') {
    if (!block) {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command one", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, You changed camera to #1!`);
    block = true;
                    setTimeout(() => {
                        block = false;
                    }, (60 * 1000))
}
else
{
    client.say(channel, `@${tags.username}, this command is on cooldown for 1 minute.`);
}
}

if(message.toLowerCase() === '!camera 2') {
    if (!block) {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command two", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, You changed camera to #2!`);
    block = true;
                    setTimeout(() => {
                        block = false;
                    }, (60 * 1000))
}
else
{
    client.say(channel, `@${tags.username}, this command is on cooldown for 1 minute.`);
}
}

if(message.toLowerCase() === '!camera 3') {
    if (!block) {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command three", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, You changed camera to #3!`);
    block = true;
                    setTimeout(() => {
                        block = false;
                    }, (60 * 1000))
}
else
{
    client.say(channel, `@${tags.username}, this command is on cooldown for 1 minute.`);
}
}

if(message.toLowerCase() === '!camera 4') {
    if (!block) {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command four", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, You changed camera to #4!`);
    block = true;
                    setTimeout(() => {
                        block = false;
                    }, (60 * 1000))
}
else
{
    client.say(channel, `@${tags.username}, this command is on cooldown for 1 minute.`);
}
}

if(message.toLowerCase() === '!camera 5') {
    if (!block) {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command five", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, You changed camera to #5!`);
    block = true;
                    setTimeout(() => {
                        block = false;
                    }, (60 * 1000))
}
else
{
    client.say(channel, `@${tags.username}, this command is on cooldown for 1 minute.`);
}
}

if(message.toLowerCase() === '!help') {
    var exec = require("child_process").exec;
    exec("D:\\Program^ Files\\VoiceAttack\\VoiceAttack.exe -command help", function (err, stdout, stderr) {
        if (err) {
            throw err;
        }
    })
    client.say(channel, `@${tags.username}, to change the camera use: !camera [1, 5]`);
}

});

You can get your auth token from: https://twitchapps.com/tmi/ and post it in .env file as:

Code: [Select]
TWITCH_OAUTH_TOKEN='oauth:yourtoken'
The path to VoiceAttack may differ and need to be edited, it is very basic and probably not the most efficient way to write code in Node, but it works and it has cooldown included.

Hope this helps someone who wants to do this without having to learn python.

Some dependencies are:

1. NodeJS
2. NPM
3. tmi.js
3. child_process NPM module
4. dotenv NPM module

That is as much of basic information i will give without holding people's hand, basics are easily found on the internet such as how to do
Code: [Select]
npm init and how to run the bot.
« Last Edit: January 02, 2022, 08:24:50 PM by aezrath »

Robertsmania

  • Newbie
  • *
  • Posts: 46
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #6 on: January 05, 2022, 11:58:29 AM »
Wow, thanks Aezrath!  Its great to see another approach.  Hopefully this will help make the whole thing more approachable.

In my case, I'm pretty happy with the Streamlabs Chatbot solution.  My script has gotten a lot more complex and I'm using a bunch of features in the Streamlabs framework that would be difficult for me to implement on my own (timeouts and user validation stuff).

aezrath

  • Newbie
  • *
  • Posts: 4
Re: Twitch Plays style chat integration with VoiceAttack?
« Reply #7 on: January 05, 2022, 01:54:17 PM »
Yeah, mine was also very upgraded for my own personal needs.

I just wanted to share with people who might find learning Python or Node.js too complicated or don't wanna use chatbots and only want to use this with nothing else.

Thanks for sharing and inspiring me.