diff --git a/mumble-bot/README.md b/mumble-bot/README.md index a7186be..23642ce 100644 --- a/mumble-bot/README.md +++ b/mumble-bot/README.md @@ -7,10 +7,7 @@ Install -------- ``` -git clone https://github.com/azlux/pymumble # quest non sta su pypy -cd pymumble -pip3 install --user -r requirements.txt -python3 setup.py install --user +pip3 install git+https://github.com/azlux/pymumble # quest non sta su pypy ``` Usi @@ -21,7 +18,7 @@ Usi con pulseaudio ``` -python3 bot.py --stream --channel "canale" | paplay --raw --rate=48000 --format=s16le --channels=2 +python3 bot.py --stream --channel "canale" | paplay -n mumblebot --raw --rate=48000 --format=s16le --channels=2 ``` ### Stream to icecast diff --git a/mumble-bot/bot.py b/mumble-bot/bot.py index 3e01497..a8bb93c 100644 --- a/mumble-bot/bot.py +++ b/mumble-bot/bot.py @@ -1,107 +1,136 @@ -import pymumble_py3 as pymumble -from pymumble_py3.constants import * -import subprocess as sp -import time -import sys -import os -import fcntl -import audioop import argparse +import audioop +import logging +import sys +import time +from functools import partial -def message_received(message): +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 + 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() - -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('--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) - -sys.argv.pop(0) -args = parser.parse_args(sys.argv) - - -pwd = "" # password -server = args.server -nick = args.name -channel = args.channel -port = args.port -is_streaming=False -stream_always= args.stream -auto_suspend_stream = args.auto_suspend_stream -silence_limit = 30 +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("--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 + ) + return parser -# Spin up a client and connect to mumble server -mumble = pymumble.Mumble(server, nick, password=pwd, port=port) -mumble.callbacks.set_callback(PYMUMBLE_CLBK_TEXTMESSAGERECEIVED, message_received) -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_name(channel).move_in() -mumble.users.myself.mute() +def main(): + global is_streaming + global silence_time + args = get_parser().parse_args() + logging.basicConfig(level=logging.DEBUG) -if is_streaming: - mumble.users.myself.recording() + pwd = "" # password + server = args.server + nick = args.name + channel = args.channel + port = args.port + is_streaming = False + stream_always = args.stream -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 = None -cursor_time = time.time() - BUFFER -silence_time = 0 -silence_limit_ms = silence_limit * 1000 + # Spin up a client and connect to mumble server + mumble = pymumble.Mumble(server, nick, password=pwd, port=port) -while mumble.is_alive(): - if cursor_time < time.time() - BUFFER: - base_sound = None + 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_name(channel).move_in() + mumble.users.myself.mute() - try: - for user in mumble.users.values(): # check the audio queue of each user - if user.sound.is_sound(): - # 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 == 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: + mumble.users.myself.recording() - 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) + 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 - if auto_suspend_stream and (silence_time >= silence_limit_ms) and is_streaming: - is_streaming = False - mumble.my_channel().send_text_message("Diretta terminata in automatico dopo "+str(silence_limit)+" secondi circa di silenzio") - mumble.users.myself.unrecording() + while mumble.is_alive(): + if cursor_time < time.time() - BUFFER: + base_sound = None - cursor_time += FLOAT_RESOLUTION - else: - time.sleep(FLOAT_RESOLUTION) + try: + for user in mumble.users.values(): # check the audio queue of each user + if user.sound.is_sound(): + # 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() diff --git a/mumble-bot/requirements.txt b/mumble-bot/requirements.txt new file mode 100644 index 0000000..8a6d413 --- /dev/null +++ b/mumble-bot/requirements.txt @@ -0,0 +1 @@ +opuslib==3.0.1