Here is another simple Twitch enabled wearable. This time it is all about how to help streams keep up with there large chat rooms in somewhat of a trolly way.
Let's mimic our favorite purple villain with a snap.
Let's be real. Everyone is going to come back from the decimation, so lets just time out half of the chat rather than a full blown ban.
DetailsBuild instructionsStep 1
The Snap glove is rather straightforward with the esp32. I hooked it up to a single cell lipo for power. Gpio 14 is a capacitive touch pin, called T6 when coding. I attached a wire to gpio 14 and soldered it to silver conductive thread!
*Remember to keep your touch pads small so they have good sensitive control. Too large of a pad will result in no touch triggers or constant touch depending on how the sensitivity is set.
Step 2
Conductive Fabric is definitely difficult to solder (if you are me). I find that getting your iron just hot enough to melt your solder and nothing beyond that is where you want to stay.
Tin your wire by melting some solder onto it, place it on the conductive fabric, and apply heat from with your soldering iron in burst of 3 seconds. Check the solder joint after each burst to see if more heat needs to be applied or if it will work.
Just take it slow, and don't be afraid to mess this step up.
Step 3
I stitched all the sensor pads in with regular thread, and sewed some of the cabling as well. This helps with repeated bending of the solder joint.
The sensor that is closest to your thumb is where your middle finger ends, while snapping. <
igure>
Step 4
The files for this code is hosted in the files section of this project.
Lets just start by importing the Socket, time, random, and math libraries.
import cfg
import socket
import time
import random
import math
They cfg file is one custom made with your username, password, and channel name for the bot you are putting into a twitch channel.
Now let's define how our timeout command will function.
def timeout(sock, user, secs):
"""
Time out a user for a set period of time.
Keyword arguments:
sock -- the socket over which to send the timeout command
user -- the user to be timed out
secs -- the length of the timeout in seconds (default 600)
"""
chat(sock, "/timeout {}".format(user, secs))
def untimeout(sock,user):
chat(sock,"/unban {}".format(user))
When you call this function, you'll need to call out which socket you are trying to communicate with, the username you are timing out, and how long they will be banned for.
I also included an undo timeout command utilizing Twitch's unban function.
s = socket.socket()
s.connect((cfg.HOST,cfg.PORT))
s.send("PASS {}\r\n".format(cfg.PASS).encode("utf-8"))
s.send("NICK {}\r\n".format(cfg.NICK).encode("utf-8"))
s.send("JOIN {}\r\n".format(cfg.CHAN).encode("utf-8"))
CHAT_MSG=re.compile(r"^:\w+!\w+@\w+.tmi.twitch.tv PRIVMSG #\w+ :")
This section of code creates the socket, and passes information sorted in the cfg file I mentioned earlier. It also creates a compiler/decompiler for twitch messages.
Let's create some variables to store the usernames and even a chat counter for each person.
vips = []
vrem = []
vipc = []
vipCount = 0
Now Let's create an infinite loop to run through, and wait for a message from twitch (chat). We then are going to check if it is a ping. A pong will need to be sent if our bot is to remain connected to Twitch.
while True:
response = s.recv(1024).decode("utf-8")
if response == "PING :tmi.twitch.tv\r\n":
s.send("PONG :tmi.twitch.tv\r\n".encode("utf-8"))
print('sent')
But what if it isn't a ping, but an actual chat we want to pay attention to? We will separate the message and username out of the info we received.
else:
username = re.search(r"\w+", response).group(0) # return the entire match
message = CHAT_MSG.sub("", response)
# s.send(s,username +" + " + message)
print(username + ": " + message)
With this information we are going to start collecting everyone in chat, put them into a list, and count how many times they speak in chat.
if username in vips: # will need to edit out tvheadbot, atltvhead, tmi from the messages collected! before doing the thanos glove
# if its tvhead bot do nothing
ind = vips.index(username)
vipCount = vipc[ind]
vipCount = vipCount + 1
vipc[ind] = vipCount
print(vips)
print(vipc)
# print(username + " has spoken " + str(vipCount) + " times.")
else:
vips.append(username)
vipc.append(1)
If the username is not in the list VIPS, it will add them to it. It also adds a integer into another list to count their interactions. If the user is in the list, it will add to their integer.
Next lets see if the message is our SNAP command. The Snap command will create a new list for those who will be timed out at random, without repeating. This new list is calculated to be half of all of those that spoke in chat, rounded up. It also accounts for the twitch entity in all chats tmi, and your chatbot. in my case tvheadbot.
if message.strip() == "SNAP" and username == "tvheadbot":
# these operations move half of the chat to a new list for removal
oglength = len(vips)
delenght = math.ceil((oglength-3)/2)
while len(vrem) < delenght:
userToMove = random.choice(vips)
if userToMove == "tmi" or userToMove == "tvheadbot" or userToMove == "atltvhead":
# do nothing
print("Encountered either Tmi or tvheadbot or atltvhead")
else:
if userToMove in vrem:
print("User " + userToMove + " is already to be timed out.")
else:
vrem.append(userToMove)
Once we have sorted half of the chat into a the removal list. it is time to time them out.
# this is where I do the timeout
for q in vrem:
# print(q)
timeout(s, q, 10)
time.sleep(1 / cfg.RATE)
# remember to wipe out vrem I want to unban / untimout
time.sleep(1 / cfg.RATE)
Keeping everyone in a list will allow you to unban them with a snap as well!
Some things that your chatbot cannot do is timeout moderators and set your chat into emote only mod. I wish I could enable emote only mode, but I'll have to make it with the streamers username.
Step 5
I am using an older version of IRCClient which I modified to work with Twitch. If you use the most up to date version, it will work with Twitch out of the box.<>
The first thing to add are the libraries, defining our wifi network, and information to pass to twitch.
#include <WiFi.h>
#include <IRCClient.h>
/*-----------------------------------------------------------------------------------*/
#define ssid1 "Wifi"
#define password1 "Password"
#define IRC_SERVER "irc.chat.twitch.tv"
#define IRC_PORT 6667
#define IRC_NICK "Bot_Name"
#define IRC_CHAN "#Channel_Name"
#define IRC_PASS "oauth:####"
WiFiClient wiFiClient;
IRCClient client(IRC_SERVER, IRC_PORT, wiFiClient);
We call out our wificlient, and our ircclients here with the proper twitch ports listed. your bot, channel name, and password are all things you will need change.
Let's define some variables that will help us not time people out when we don't want to.
boolean snapboo = false;
boolean emoteboo = false;
This next section of code is for the touch sensors built into the esp 32. We are also creating a function that will be called for the snap is triggered.
int thresholdTwo = 60;
bool touch2detected = false;
byte touchCountTwo = 0;
unsigned long touchTimeTwo;
unsigned long oldTouchTimeTwo;
void gotTouch2(){
//figure out calibration here!
//get time and incriment timer
touchTimeTwo = millis();
touchCountTwo++;
// if counter is above a certain # in a certain timeout decrease sensitivity (time is going to be half second)
if(touchCountTwo >=3 && (touchTimeTwo-oldTouchTimeTwo)<=400 && thresholdTwo > 20){
thresholdTwo=thresholdTwo-1;
// reset count
touchCountTwo=0;
}
// if counter is below a # and a certain timeout increase sensitivity (time is going to be 2 min?)
else if((touchTimeTwo-oldTouchTimeTwo)>=60000){ // touchCount<1 && probably doesn't need the <1 touch count. if it creates too sensitive of a sensor, it will be backed off imediatly after. it should jut create more triggers after the high five
thresholdTwo++;
// reset counter
touchCountTwo=0;
}
// if counter is below # and timer is between sensitivity triggers touch detected
else if(400<(touchTimeTwo-oldTouchTimeTwo) && (touchTimeTwo-oldTouchTimeTwo)<60000){
touch2detected = true;
delay(500);
// reset counter
}
// time saved to new variable and reset
oldTouchTimeTwo = touchTimeTwo;
}
Every time a snap is triggered, it will calibrate the sensor, checking that it is a real touch by comparison of time intervals and number of triggers.
Next lets create our setup loop and attach our interrupts. The wifi and clients will also begin.
void setup() {
WiFi.begin(ssid1, password1);
client.setCallback(callback);
client.setSentCallback(debugSentCallback);
touchAttachInterrupt(T7,gotTouch2,thresholdTwo);
}
Now, in our main loop, we are going to pass our information to twitch, and keep passing it until we are connected.
void loop() {
// put your main code here, to run repeatedly:
if (!client.connected()) {
//Serial.println("Attempting IRC connection...");
// Attempt to connect
if (client.connect(IRC_NICK, IRC_CHAN, IRC_PASS)) {
//Serial.println("connected");
client.sendMessage(IRC_CHAN, "Hello everyone! I'm TvheadBot, a construct, assistant within the head of Atltvhead. If you have any questions type !help , and I'll post a link to all the channel commands. Let's Tune into Good Vibes! <3");
} else {
//Serial.println("failed... try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
return;
}
We will call our callback function which checks for the twitch messages and breaks it down into the ping/pong interaction, chat message, and username. We will also start checking to see if a snap was detected and seeing if any of our keys have been unlocked.
client.loop();
//Time Outs or Emote Only
if(touch2detected){
touch2detected = false;
if(snapboo){
snapboo = false;
client.sendMessage(IRC_CHAN,"SNAP");
} else if(emoteboo){
emoteboo = false;
client.sendMessage(IRC_CHAN,"IT'S EMOTE TIME!");
} else{
client.sendMessage(IRC_CHAN,"High Five Mode Initiated");
}
}
}
Unfortunately emote only mode cannot be triggered by a chat bot, so sad.
These commands are recognized by our python script to initiate the timeouts.
Mentioned above, we breakdown the twitch messages, for ping/pong, username, message.
if a particular word is written by a particular username (username) then unlock either the snap, or emote only mode (which again I am dumb and a chatbot cannot trigger).
void callback(IRCMessage ircMessage) {
//Serial.println("In CallBack");
// PRIVMSG ignoring CTCP messages
if (ircMessage.command == "PRIVMSG" && ircMessage.text[0] != '\001') {
//Serial.println("Passed private message.");
String message("<" + ircMessage.nick + "> " + ircMessage.text);
if(ircMessage.text == "sk" && ircMessage.nick == "username"){
snapboo = true;
client.sendMessage(IRC_CHAN,"The Snap has been unlocked!");
}
else if(ircMessage.text == "ek" && ircMessage.nick == "username"){
emoteboo = true;
client.sendMessage(IRC_CHAN,"Emote only ready!");
}
return;
}
}
Lastly, just setup a debug for the clients.
void debugSentCallback(String data) {
//Serial.println("I am in debug");
Serial.println(data);
}
Step 6
How off your build and snap your chat!
Don't forget to cut a hole so that your finger can contact the sensor!
FilesSnap_Glove.ino: Snap Glove code for the ESP32 and Arduino
Thanos Twitch Bot.py: A basic Twitch bot that collects all users in a chat, segments half of them off for timeouts, and will time them out when a specific message is sent by a specific person Is missing the cfg file for communicating with twitch (its passwords and what not)-> # cfg.pyHOST = "irc.twitch.tv" # the Twitch IRC serverPORT = 6667 # always use port 6667!NICK = "twitch_username" # your Twitch username, lowercasePASS = "oauth:xxxxxxxxxxxxxxxxxxxx" # your Twitch OAuth tokenCHAN = "#channel" # the channel you want to join
Comments