How-To Guides
My own personal setup
#!/usr/bin/env/python3
"""TheLabCat's Rumchat Actor setup
My RumChat Actor live stream configuration.
S.D.G"""
import glob
import os.path as op
import subprocess
import rumchat_actor
import pyaudio # For Piper TTS
from pygame import mixer # For a sound-making command I have
with open("../../rumble_thelabcat_api_url_wkey.txt") as f:
API_URL = f.read().strip()
with open("../../rumble_thelabcat_credentials.txt") as f:
USERNAME, PASSWORD = f.read().splitlines()[:2]
# Timed messages to send
TIMED_MESSAGES = [
"There are lots of buttons under the video. Press some of them if you haven't already. :-)",
"Want some subtitles for a video, but aren't satisfied with auto-generated? I sell handmade subtitles on Fiverr! https://www.fiverr.com/s/qDDKm9V",
"I have chat commands. Send \"!help\" to see them, send \"!help commandName\" for more information on that command.",
]
# Piper TTS settings
MODEL_PATH = "/home/wilbur/bin/pipertts/voices/"
PIPER_DEFAULT = "troutt"
PIPER_MODELS = {
f.split("-")[1]: f
for f in glob.glob("*.onnx", root_dir=MODEL_PATH)
}
# Directory where my sound effects are
SOUND_EFFECTS_DIR = "/run/media/wilbur/WJHDD1/Audio/sound_effects"
mixer.init()
PA = pyaudio.PyAudio()
print("Setting up actor...")
actor = rumchat_actor.RumbleChatActor(
api_url=API_URL,
username=USERNAME,
password=PASSWORD,
channel="MarswideBGL"
)
# The Sisyphus command
sisyphus_music = mixer.Sound(op.join(SOUND_EFFECTS_DIR, "sisyphus_short.mp3"))
sisyphus_music.set_volume(0.15) # Just my arbitrary volume preference
def sisyphus(message, act_props, actor):
"""One must imagine a gamer happy
Args:
message (cocorum.chatapi.Message): The chat message to run this action on.
act_props (dict): Action properties, aka metadata about what other things did with this message
actor (RumbleChatActor): The chat actor."""
sisyphus_music.play()
actor.send_message(f"@{message.user.username} One must imagine a gamer happy.")
actor.register_command(
rumchat_actor.commands.ChatCommand(
name="sisyphus",
actor=actor,
cooldown=120,
target=sisyphus
)
)
# TTS command
def piper_tts(text, voice=PIPER_DEFAULT):
"""Synthesize text with Piper TTS"""
assert voice in PIPER_MODELS, "Invalid voice selection"
# The format here is always 8, but just to show how I got it, it is the
# format for unsigned 16-bit (i.e. 2 byte, hence the 2).
stream = PA.open(
format=PA.get_format_from_width(2), channels=1, rate=22050, output=True
)
try:
# Run Piper TTS via a command
cp = subprocess.run(
[
"piper",
"--model",
op.join(MODEL_PATH, PIPER_MODELS[voice]),
"--output-raw"
],
# Send Piper the text to speak
input=text.encode(),
# Grab the output raw sound data
capture_output=True
)
# Play the sound data
stream.write(cp.stdout)
# Make sure we always close the PyAudio stream
finally:
stream.close()
tts_command = rumchat_actor.commands.TTSCommand(
actor=actor,
voices={
voice: eval(f"lambda text: piper_tts(text, \"{voice}\")")
for voice in PIPER_MODELS
}
)
tts_command.voices["default"] = lambda text: piper_tts(text, PIPER_DEFAULT)
actor.register_command(tts_command)
# Killswitch command
actor.register_command(rumchat_actor.commands.KillswitchCommand(actor=actor))
# Lurk command
actor.register_command(rumchat_actor.commands.MessageCommand(
actor=actor,
name="lurk",
text="@{} is lurking in the chat",
))
# Clip command
clip_command = rumchat_actor.commands.ClipRecordingCommand(
actor=actor,
recording_load_path="/home/wilbur/Videos/",
clip_save_path="/home/wilbur/Videos/stream_clips/",
)
# Auto-upload clips to my clips channel on Rumble
clip_uploader = rumchat_actor.misc.ClipUploader(
actor,
clip_command,
channel_id=6350778, # Marswide BGL Clips
)
actor.register_command(clip_command)
# Help command
actor.register_command(rumchat_actor.commands.HelpCommand(actor=actor))
# Send timed messages
tmm = rumchat_actor.actions.TimedMessagesManager(
actor, messages=TIMED_MESSAGES, delay=300, in_between=5
)
actor.register_message_action(tmm.action)
# Follower / subscriber / etc. thanking system
thanker = rumchat_actor.actions.Thanker(actor)
actor.register_message_action(thanker)
# Message blipper
actor.register_message_action(
rumchat_actor.actions.ChatBlipper(op.join(SOUND_EFFECTS_DIR, "pop.wav"))
)
# User announcer
# Announce Izsak's arrival
izsak_intro = mixer.Sound(op.join(SOUND_EFFECTS_DIR, "izsak_intro.mp3"))
def announce_izsak(message, act_props, actor):
"""Announce when Izsak arrives in the chat
Args:
message (cocorum.chatapi.Message): The chat message to run this action on.
act_props (dict): Action properties, aka metadata about what other things did with this message
actor (RumbleChatActor): The chat actor.
Returns:
act_props (dict): Dictionary of recorded properties from running this action."""
izsak_intro.play()
return {"sound": True}
announcer = rumchat_actor.actions.UserAnnouncer(
special_announcers={
"Jorash": announce_izsak,
}
)
actor.register_message_action(announcer)
# Run the bot continuously
print("Starting mainloop...")
actor.mainloop()
PA.terminate()
S.D.G.