random/mumble-bot/bot.py

150 lines
4.9 KiB
Python

import argparse
import audioop
import logging
import sys
import time
from functools import partial
import pymumble_py3 as pymumble
from pymumble_py3.constants import PYMUMBLE_CLBK_TEXTMESSAGERECEIVED
is_streaming = False
silence_time = 0
def message_received(mumble, message):
global is_streaming
global silence_time
command = message.message
if command == "/start":
is_streaming = True
silence_time = 0
mumble.my_channel().send_text_message("Diretta iniziata")
logging.info("Diretta iniziata")
mumble.users.myself.recording()
elif command == "/stop":
is_streaming = False
mumble.my_channel().send_text_message("Diretta terminata")
logging.info("Diretta terminata")
mumble.users.myself.unrecording()
def get_parser():
parser = argparse.ArgumentParser(description="Regia pienamente automatizzata")
parser.add_argument("--channel", help="Set channel", default="")
parser.add_argument("--name", help="Set bot nickname", default="RadioRobbot")
parser.add_argument("--server", help="Set server", default="mumble.esiliati.org")
parser.add_argument("--password", help="Set password", default="")
parser.add_argument("--port", help="Set port", type=int, default=64738)
parser.add_argument(
"--stream",
action="store_true",
help="Ignore commands in chat and stream everything",
)
parser.add_argument(
"--auto-suspend-stream",
action="store_true",
help="Ignore commands in chat and stream everything",
)
parser.add_argument(
"--max-silence-time", type=int, help="max silence time in seconds", default=30
)
parser.add_argument(
"--tokens",
help="Set tokens list",
nargs="*"
)
parser.add_argument(
"--ignore",
help="List of ignored users",
nargs="*",
default=[]
)
return parser
def main():
global is_streaming
global silence_time
args = get_parser().parse_args()
logging.basicConfig(level=logging.DEBUG)
pwd = args.password
server = args.server
nick = args.name
channel = args.channel
port = args.port
tokens = args.tokens
is_streaming = False
stream_always = args.stream
ignored_users = args.ignore
# Spin up a client and connect to mumble server
mumble = pymumble.Mumble(server, nick, password=pwd, port=port, tokens=tokens)
mumble.callbacks.set_callback(
PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, partial(message_received, mumble)
)
mumble.set_receive_sound(1) # Enable receiving sound from mumble server
mumble.start()
mumble.is_ready() # Wait for client is ready
mumble.channels.find_by_tree(channel.split('/')).move_in()
mumble.users.myself.mute()
if is_streaming:
mumble.users.myself.recording()
BUFFER = 0.1
BITRATE = 48000
RESOLUTION = 10 # in ms
FLOAT_RESOLUTION = float(RESOLUTION) / 1000
MONO_CHUNK_SIZE = BITRATE * 2 * RESOLUTION / 1000
STEREO_CHUNK_SIZE = MONO_CHUNK_SIZE * 2
silent = b"\x00" * int(STEREO_CHUNK_SIZE)
cursor_time = time.time() - BUFFER
while mumble.is_alive():
if cursor_time < time.time() - BUFFER:
base_sound = None
try:
for user in mumble.users.values(): # check the audio queue of each user
if user.sound.is_sound() and user["name"] not in ignored_users:
# available sound is to be treated now and not later
sound = user.sound.get_sound(FLOAT_RESOLUTION)
stereo_pcm = audioop.tostereo(sound.pcm, 2, 1, 1)
if base_sound is None:
base_sound = stereo_pcm
else:
base_sound = audioop.add(base_sound, stereo_pcm, 2)
except RuntimeError:
print("ignored exception in stderr...", file=sys.stderr)
if is_streaming or stream_always:
if base_sound:
silence_time = 0
sys.stdout.buffer.write(base_sound)
else:
silence_time += RESOLUTION
sys.stdout.buffer.write(silent)
if (
args.auto_suspend_stream
and (silence_time >= args.max_silence_time * 1000)
and is_streaming
):
is_streaming = False
logging.info("max-silence-time reached")
mumble.my_channel().send_text_message(
"Diretta terminata in automatico dopo %d secondi circa di silenzio"
% args.max_silence_time
)
mumble.users.myself.unrecording()
cursor_time += FLOAT_RESOLUTION
else:
time.sleep(FLOAT_RESOLUTION)
if __name__ == "__main__":
main()