Send query result to channel

This commit is contained in:
elpatron68 2019-07-27 18:18:07 +02:00
parent 92412bb29f
commit f62cb1afe4
3 changed files with 901 additions and 72 deletions

View File

@ -27,6 +27,7 @@ import media.file
import media.playlist import media.playlist
import media.radio import media.radio
import media.system import media.system
from radiobrowser import getstations_byname
""" """
FORMAT OF A MUSIC INTO THE PLAYLIST FORMAT OF A MUSIC INTO THE PLAYLIST
@ -70,13 +71,16 @@ class MumbleBot:
# Set specific format for the log # Set specific format for the log
FORMAT = '%(asctime)s: %(message)s' FORMAT = '%(asctime)s: %(message)s'
if args.verbose: if args.verbose:
logging.basicConfig(format=FORMAT, level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S') logging.basicConfig(
format=FORMAT, level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S')
logging.debug("Starting in DEBUG loglevel") logging.debug("Starting in DEBUG loglevel")
elif args.quiet: elif args.quiet:
logging.basicConfig(format=FORMAT, level=logging.ERROR, datefmt='%Y-%m-%d %H:%M:%S') logging.basicConfig(
format=FORMAT, level=logging.ERROR, datefmt='%Y-%m-%d %H:%M:%S')
logging.error("Starting in ERROR loglevel") logging.error("Starting in ERROR loglevel")
else: else:
logging.basicConfig(format=FORMAT, level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S') logging.basicConfig(
format=FORMAT, level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
logging.info("Starting in INFO loglevel") logging.info("Starting in INFO loglevel")
# the playlist is... a list (Surprise !!) # the playlist is... a list (Surprise !!)
@ -84,7 +88,8 @@ class MumbleBot:
var.user = args.user var.user = args.user
var.music_folder = var.config.get('bot', 'music_folder') var.music_folder = var.config.get('bot', 'music_folder')
var.is_proxified = var.config.getboolean("webinterface", "is_web_proxified") var.is_proxified = var.config.getboolean(
"webinterface", "is_web_proxified")
self.exit = False self.exit = False
self.nb_exit = 0 self.nb_exit = 0
self.thread = None self.thread = None
@ -94,7 +99,8 @@ class MumbleBot:
wi_addr = var.config.get("webinterface", "listening_addr") wi_addr = var.config.get("webinterface", "listening_addr")
wi_port = var.config.getint("webinterface", "listening_port") wi_port = var.config.getint("webinterface", "listening_port")
interface.init_proxy() interface.init_proxy()
tt = threading.Thread(target=start_web_interface, args=(wi_addr, wi_port)) tt = threading.Thread(
target=start_web_interface, args=(wi_addr, wi_port))
tt.daemon = True tt.daemon = True
tt.start() tt.start()
@ -131,7 +137,8 @@ class MumbleBot:
self.mumble = pymumble.Mumble(host, user=self.username, port=port, password=password, tokens=tokens, self.mumble = pymumble.Mumble(host, user=self.username, port=port, password=password, tokens=tokens,
debug=var.config.getboolean('debug', 'mumbleConnection'), certfile=certificate) debug=var.config.getboolean('debug', 'mumbleConnection'), certfile=certificate)
self.mumble.callbacks.set_callback("text_received", self.message_received) self.mumble.callbacks.set_callback(
"text_received", self.message_received)
self.mumble.set_codec_profile("audio") self.mumble.set_codec_profile("audio")
self.mumble.start() # start the mumble thread self.mumble.start() # start the mumble thread
@ -146,7 +153,8 @@ class MumbleBot:
# Set the CTRL+C shortcut # Set the CTRL+C shortcut
def ctrl_caught(self, signal, frame): def ctrl_caught(self, signal, frame):
logging.info("\nSIGINT caught, quitting, {} more to kill".format(2 - self.nb_exit)) logging.info(
"\nSIGINT caught, quitting, {} more to kill".format(2 - self.nb_exit))
self.exit = True self.exit = True
self.stop() self.stop()
if self.nb_exit > 1: if self.nb_exit > 1:
@ -181,16 +189,19 @@ class MumbleBot:
logging.info(command + ' - ' + parameter + ' by ' + user) logging.info(command + ' - ' + parameter + ' by ' + user)
if command == var.config.get('command', 'joinme'): if command == var.config.get('command', 'joinme'):
self.mumble.users.myself.move_in(self.mumble.users[text.actor]['channel_id'], token=parameter) self.mumble.users.myself.move_in(
self.mumble.users[text.actor]['channel_id'], token=parameter)
return return
# Anti stupid guy function # Anti stupid guy function
if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_other_channel_message') and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself['channel_id']: if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_other_channel_message') and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself['channel_id']:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_in_my_channel')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_in_my_channel'))
return return
if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_private_message') and text.session: if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_private_message') and text.session:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'pm_not_allowed')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'pm_not_allowed'))
return return
### ###
@ -198,49 +209,61 @@ class MumbleBot:
### ###
for i in var.db.items("user_ban"): for i in var.db.items("user_ban"):
if user.lower() == i[0]: if user.lower() == i[0]:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'user_ban')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'user_ban'))
return return
if command == var.config.get('command', 'user_ban'): if command == var.config.get('command', 'user_ban'):
if self.is_admin(user): if self.is_admin(user):
if parameter: if parameter:
self.mumble.users[text.actor].send_text_message(util.user_ban(parameter)) self.mumble.users[text.actor].send_text_message(
util.user_ban(parameter))
else: else:
self.mumble.users[text.actor].send_text_message(util.get_user_ban()) self.mumble.users[text.actor].send_text_message(
util.get_user_ban())
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
return return
elif command == var.config.get('command', 'user_unban'): elif command == var.config.get('command', 'user_unban'):
if self.is_admin(user): if self.is_admin(user):
if parameter: if parameter:
self.mumble.users[text.actor].send_text_message(util.user_unban(parameter)) self.mumble.users[text.actor].send_text_message(
util.user_unban(parameter))
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
return return
elif command == var.config.get('command', 'url_ban'): elif command == var.config.get('command', 'url_ban'):
if self.is_admin(user): if self.is_admin(user):
if parameter: if parameter:
self.mumble.users[text.actor].send_text_message(util.url_ban(self.get_url_from_input(parameter))) self.mumble.users[text.actor].send_text_message(
util.url_ban(self.get_url_from_input(parameter)))
else: else:
self.mumble.users[text.actor].send_text_message(util.get_url_ban()) self.mumble.users[text.actor].send_text_message(
util.get_url_ban())
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
return return
elif command == var.config.get('command', 'url_unban'): elif command == var.config.get('command', 'url_unban'):
if self.is_admin(user): if self.is_admin(user):
if parameter: if parameter:
self.mumble.users[text.actor].send_text_message(util.url_unban(self.get_url_from_input(parameter))) self.mumble.users[text.actor].send_text_message(
util.url_unban(self.get_url_from_input(parameter)))
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
return return
if parameter: if parameter:
for i in var.db.items("url_ban"): for i in var.db.items("url_ban"):
if self.get_url_from_input(parameter.lower()) == i[0]: if self.get_url_from_input(parameter.lower()) == i[0]:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'url_ban')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'url_ban'))
return return
### ###
@ -259,16 +282,19 @@ class MumbleBot:
var.playlist.append(music) var.playlist.append(music)
else: else:
# try to do a partial match # try to do a partial match
matches = [file for file in util.get_recursive_filelist_sorted(music_folder) if parameter.lower() in file.lower()] matches = [file for file in util.get_recursive_filelist_sorted(
music_folder) if parameter.lower() in file.lower()]
if len(matches) == 0: if len(matches) == 0:
self.send_msg(var.config.get('strings', 'no_file'), text) self.send_msg(var.config.get(
'strings', 'no_file'), text)
elif len(matches) == 1: elif len(matches) == 1:
music = {'type': 'file', music = {'type': 'file',
'path': matches[0], 'path': matches[0],
'user': user} 'user': user}
var.playlist.append(music) var.playlist.append(music)
else: else:
msg = var.config.get('strings', 'multiple_matches') + '<br />' msg = var.config.get(
'strings', 'multiple_matches') + '<br />'
msg += '<br />'.join(matches) msg += '<br />'.join(matches)
self.send_msg(msg, text) self.send_msg(msg, text)
else: else:
@ -277,7 +303,8 @@ class MumbleBot:
elif command == var.config.get('command', 'play_url') and parameter: elif command == var.config.get('command', 'play_url') and parameter:
music = {'type': 'url', music = {'type': 'url',
'url': self.get_url_from_input(parameter), # grab the real URL # grab the real URL
'url': self.get_url_from_input(parameter),
'user': user, 'user': user,
'ready': 'validation'} 'ready': 'validation'}
var.playlist.append(music) var.playlist.append(music)
@ -285,18 +312,21 @@ class MumbleBot:
if media.url.get_url_info(): if media.url.get_url_info():
if var.playlist[-1]['duration'] > var.config.getint('bot', 'max_track_duration'): if var.playlist[-1]['duration'] > var.config.getint('bot', 'max_track_duration'):
var.playlist.pop() var.playlist.pop()
self.send_msg(var.config.get('strings', 'too_long'), text) self.send_msg(var.config.get(
'strings', 'too_long'), text)
else: else:
for i in var.db.options("url_ban"): for i in var.db.options("url_ban"):
if var.playlist[-1]['url'] == i: if var.playlist[-1]['url'] == i:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'url_ban')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'url_ban'))
var.playlist.pop() var.playlist.pop()
return return
var.playlist[-1]['ready'] = "no" var.playlist[-1]['ready'] = "no"
self.async_download_next() self.async_download_next()
else: else:
var.playlist.pop() var.playlist.pop()
self.send_msg(var.config.get('strings', 'unable_download'), text) self.send_msg(var.config.get(
'strings', 'unable_download'), text)
elif command == var.config.get('command', 'play_playlist') and parameter: elif command == var.config.get('command', 'play_playlist') and parameter:
offset = 1 # if you want to start the playlist at a specific index offset = 1 # if you want to start the playlist at a specific index
@ -310,7 +340,8 @@ class MumbleBot:
elif command == var.config.get('command', 'play_radio'): elif command == var.config.get('command', 'play_radio'):
if not parameter: if not parameter:
all_radio = var.config.items('radio') all_radio = var.config.items('radio')
msg = var.config.get('strings', 'preconfigurated_radio') + " :" msg = var.config.get(
'strings', 'preconfigurated_radio') + " :"
for i in all_radio: for i in all_radio:
comment = "" comment = ""
if len(i[1].split(maxsplit=1)) == 2: if len(i[1].split(maxsplit=1)) == 2:
@ -336,11 +367,16 @@ class MumbleBot:
logging.info('rbquery without parameter') logging.info('rbquery without parameter')
else: else:
logging.info('Found query parameter: ' + parameter) logging.info('Found query parameter: ' + parameter)
stations = getstations_byname(parameter)
for s in stations:
msg += "<br />" + s
self.send_msg(msg, text)
elif command == var.config.get('command', 'help'): elif command == var.config.get('command', 'help'):
self.send_msg(var.config.get('strings', 'help'), text) self.send_msg(var.config.get('strings', 'help'), text)
if self.is_admin(user): if self.is_admin(user):
self.send_msg(var.config.get('strings', 'admin_help'), text) self.send_msg(var.config.get(
'strings', 'admin_help'), text)
elif command == var.config.get('command', 'stop'): elif command == var.config.get('command', 'stop'):
self.stop() self.stop()
@ -350,16 +386,19 @@ class MumbleBot:
self.stop() self.stop()
self.exit = True self.exit = True
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
elif command == var.config.get('command', 'update'): elif command == var.config.get('command', 'update'):
if self.is_admin(user): if self.is_admin(user):
self.mumble.users[text.actor].send_text_message("Starting the update") self.mumble.users[text.actor].send_text_message(
"Starting the update")
# Need to be improved # Need to be improved
msg = util.update(version) msg = util.update(version)
self.mumble.users[text.actor].send_text_message(msg) self.mumble.users[text.actor].send_text_message(msg)
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'not_admin'))
elif command == var.config.get('command', 'stop_and_getout'): elif command == var.config.get('command', 'stop_and_getout'):
self.stop() self.stop()
@ -374,14 +413,16 @@ class MumbleBot:
int(self.volume * 100), self.mumble.users[text.actor]['name']), text) int(self.volume * 100), self.mumble.users[text.actor]['name']), text)
var.db.set('bot', 'volume', str(self.volume)) var.db.set('bot', 'volume', str(self.volume))
else: else:
self.send_msg(var.config.get('strings', 'current_volume') % int(self.volume * 100), text) self.send_msg(var.config.get(
'strings', 'current_volume') % int(self.volume * 100), text)
elif command == var.config.get('command', 'current_music'): elif command == var.config.get('command', 'current_music'):
if len(var.playlist) > 0: if len(var.playlist) > 0:
source = var.playlist[0]["type"] source = var.playlist[0]["type"]
if source == "radio": if source == "radio":
reply = "[radio] {title} on {url} by {user}".format( reply = "[radio] {title} on {url} by {user}".format(
title=media.radio.get_radio_title(var.playlist[0]["url"]), title=media.radio.get_radio_title(
var.playlist[0]["url"]),
url=var.playlist[0]["title"], url=var.playlist[0]["title"],
user=var.playlist[0]["user"] user=var.playlist[0]["user"]
) )
@ -411,19 +452,23 @@ class MumbleBot:
self.send_msg(reply, text) self.send_msg(reply, text)
elif command == var.config.get('command', 'skip'): elif command == var.config.get('command', 'skip'):
if parameter is not None and parameter.isdigit() and int(parameter) > 0: # Allow to remove specific music into the queue with a number # Allow to remove specific music into the queue with a number
if parameter is not None and parameter.isdigit() and int(parameter) > 0:
if int(parameter) < len(var.playlist): if int(parameter) < len(var.playlist):
removed = var.playlist.pop(int(parameter)) removed = var.playlist.pop(int(parameter))
# the Title isn't here if the music wasn't downloaded # the Title isn't here if the music wasn't downloaded
self.send_msg(var.config.get('strings', 'removing_item') % (removed['title'] if 'title' in removed else removed['url']), text) self.send_msg(var.config.get('strings', 'removing_item') % (
removed['title'] if 'title' in removed else removed['url']), text)
else: else:
self.send_msg(var.config.get('strings', 'no_possible'), text) self.send_msg(var.config.get(
'strings', 'no_possible'), text)
elif self.next(): # Is no number send, just skip the current music elif self.next(): # Is no number send, just skip the current music
self.launch_music() self.launch_music()
self.async_download_next() self.async_download_next()
else: else:
self.send_msg(var.config.get('strings', 'queue_empty'), text) self.send_msg(var.config.get(
'strings', 'queue_empty'), text)
self.stop() self.stop()
elif command == var.config.get('command', 'list'): elif command == var.config.get('command', 'list'):
@ -439,10 +484,12 @@ class MumbleBot:
if len(var.playlist) <= 1: if len(var.playlist) <= 1:
msg = var.config.get('strings', 'queue_empty') msg = var.config.get('strings', 'queue_empty')
else: else:
msg = var.config.get('strings', 'queue_contents') + '<br />' msg = var.config.get(
'strings', 'queue_contents') + '<br />'
i = 1 i = 1
for value in var.playlist[1:]: for value in var.playlist[1:]:
msg += '[{}] ({}) {}<br />'.format(i, value['type'], value['title'] if 'title' in value else value['url']) msg += '[{}] ({}) {}<br />'.format(i, value['type'],
value['title'] if 'title' in value else value['url'])
i += 1 i += 1
self.send_msg(msg, text) self.send_msg(msg, text)
@ -451,7 +498,8 @@ class MumbleBot:
var.playlist.append(var.playlist[0]) var.playlist.append(var.playlist[0])
else: else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'bad_command')) self.mumble.users[text.actor].send_text_message(
var.config.get('strings', 'bad_command'))
@staticmethod @staticmethod
def is_admin(user): def is_admin(user):
@ -478,7 +526,8 @@ class MumbleBot:
logging.debug("launch_music asked" + str(var.playlist[0])) logging.debug("launch_music asked" + str(var.playlist[0]))
if var.playlist[0]["type"] == "url": if var.playlist[0]["type"] == "url":
# Delete older music is the tmp folder is too big # Delete older music is the tmp folder is too big
media.system.clear_tmp_folder(var.config.get('bot', 'tmp_folder'), var.config.getint('bot', 'tmp_folder_max_size')) media.system.clear_tmp_folder(var.config.get(
'bot', 'tmp_folder'), var.config.getint('bot', 'tmp_folder_max_size'))
# Check if the music is ready to be played # Check if the music is ready to be played
if var.playlist[0]["ready"] == "downloading": if var.playlist[0]["ready"] == "downloading":
@ -493,9 +542,11 @@ class MumbleBot:
audio = EasyID3(uri) audio = EasyID3(uri)
title = "" title = ""
if audio["title"]: if audio["title"]:
title = audio["title"][0] # take the title from the file tag # take the title from the file tag
title = audio["title"][0]
path_thumbnail = var.playlist[0]['path'][:-4] + '.jpg' # Remove .mp3 and add .jpg # Remove .mp3 and add .jpg
path_thumbnail = var.playlist[0]['path'][:-4] + '.jpg'
thumbnail_html = "" thumbnail_html = ""
if os.path.isfile(path_thumbnail): if os.path.isfile(path_thumbnail):
# Create the image message # Create the image message
@ -504,33 +555,39 @@ class MumbleBot:
buffer = BytesIO() buffer = BytesIO()
im.save(buffer, format="JPEG") im.save(buffer, format="JPEG")
thumbnail_base64 = base64.b64encode(buffer.getvalue()) thumbnail_base64 = base64.b64encode(buffer.getvalue())
thumbnail_html = '<img - src="data:image/PNG;base64,' + thumbnail_base64.decode() + '"/>' thumbnail_html = '<img - src="data:image/PNG;base64,' + \
thumbnail_base64.decode() + '"/>'
logging.debug("Thunbail data " + thumbnail_html) logging.debug("Thunbail data " + thumbnail_html)
if var.config.getboolean('bot', 'announce_current_music'): if var.config.getboolean('bot', 'announce_current_music'):
self.send_msg(var.config.get('strings', 'now_playing') % (title, thumbnail_html)) self.send_msg(var.config.get(
'strings', 'now_playing') % (title, thumbnail_html))
else: else:
logging.error("Error with the path during launch_music") logging.error("Error with the path during launch_music")
pass pass
elif var.playlist[0]["type"] == "file": elif var.playlist[0]["type"] == "file":
uri = var.config.get('bot', 'music_folder') + var.playlist[0]["path"] uri = var.config.get('bot', 'music_folder') + \
var.playlist[0]["path"]
elif var.playlist[0]["type"] == "radio": elif var.playlist[0]["type"] == "radio":
uri = var.playlist[0]["url"] uri = var.playlist[0]["url"]
title = media.radio.get_radio_server_description(uri) title = media.radio.get_radio_server_description(uri)
var.playlist[0]["title"] = title var.playlist[0]["title"] = title
if var.config.getboolean('bot', 'announce_current_music'): if var.config.getboolean('bot', 'announce_current_music'):
self.send_msg(var.config.get('strings', 'now_playing') % (title, "URL : " + uri)) self.send_msg(var.config.get('strings', 'now_playing') %
(title, "URL : " + uri))
if var.config.getboolean('debug', 'ffmpeg'): if var.config.getboolean('debug', 'ffmpeg'):
ffmpeg_debug = "debug" ffmpeg_debug = "debug"
else: else:
ffmpeg_debug = "warning" ffmpeg_debug = "warning"
command = ("ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i', uri, '-ac', '1', '-f', 's16le', '-ar', '48000', '-') command = ("ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i',
uri, '-ac', '1', '-f', 's16le', '-ar', '48000', '-')
logging.info("FFmpeg command : " + " ".join(command)) logging.info("FFmpeg command : " + " ".join(command))
self.thread = sp.Popen(command, stdout=sp.PIPE, bufsize=480) # The ffmpeg process is a thread # The ffmpeg process is a thread
self.thread = sp.Popen(command, stdout=sp.PIPE, bufsize=480)
self.is_playing = True self.is_playing = True
def download_music(self, index): def download_music(self, index):
@ -539,7 +596,8 @@ class MumbleBot:
if var.playlist[index]['duration'] > var.config.getint('bot', 'max_track_duration'): if var.playlist[index]['duration'] > var.config.getint('bot', 'max_track_duration'):
# Check the length, useful in case of playlist, it wasn't checked before) # Check the length, useful in case of playlist, it wasn't checked before)
var.playlist.pop() var.playlist.pop()
logging.info("the music " + var.playlist[index]["url"] + " has a duration of " + var.playlist[index]['duration'] + "s -- too long") logging.info(
"the music " + var.playlist[index]["url"] + " has a duration of " + var.playlist[index]['duration'] + "s -- too long")
self.send_msg(var.config.get('strings', 'too_long')) self.send_msg(var.config.get('strings', 'too_long'))
return return
else: else:
@ -580,9 +638,11 @@ class MumbleBot:
'preferredquality': '192'}, 'preferredquality': '192'},
{'key': 'FFmpegMetadata'}] {'key': 'FFmpegMetadata'}]
} }
self.send_msg(var.config.get('strings', "download_in_progress") % var.playlist[index]['title']) self.send_msg(var.config.get(
'strings', "download_in_progress") % var.playlist[index]['title'])
logging.info("Information before start downloading :" + str(var.playlist[index])) logging.info("Information before start downloading :" +
str(var.playlist[index]))
with youtube_dl.YoutubeDL(ydl_opts) as ydl: with youtube_dl.YoutubeDL(ydl_opts) as ydl:
for i in range(2): # Always try 2 times for i in range(2): # Always try 2 times
try: try:
@ -600,7 +660,8 @@ class MumbleBot:
# Do nothing in case the next music is already downloaded # Do nothing in case the next music is already downloaded
logging.info("Async download next asked") logging.info("Async download next asked")
if len(var.playlist) > 1 and var.playlist[1]['type'] == 'url' and var.playlist[1]['ready'] in ["no", "validation"]: if len(var.playlist) > 1 and var.playlist[1]['type'] == 'url' and var.playlist[1]['ready'] in ["no", "validation"]:
th = threading.Thread(target=self.download_music, kwargs={'index': 1}) th = threading.Thread(
target=self.download_music, kwargs={'index': 1})
else: else:
return return
logging.info("Start downloading next in thread") logging.info("Start downloading next in thread")
@ -632,7 +693,8 @@ class MumbleBot:
raw_music = self.thread.stdout.read(480) raw_music = self.thread.stdout.read(480)
if raw_music: if raw_music:
# Adjust the volume and send it to mumble # Adjust the volume and send it to mumble
self.mumble.sound_output.add_sound(audioop.mul(raw_music, 2, self.volume)) self.mumble.sound_output.add_sound(
audioop.mul(raw_music, 2, self.volume))
else: else:
time.sleep(0.1) time.sleep(0.1)
else: else:
@ -686,30 +748,44 @@ def start_web_interface(addr, port):
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Bot for playing music on Mumble') parser = argparse.ArgumentParser(
description='Bot for playing music on Mumble')
# General arguments # General arguments
parser.add_argument("--config", dest='config', type=str, default='configuration.ini', help='Load configuration from this file. Default: configuration.ini') parser.add_argument("--config", dest='config', type=str, default='configuration.ini',
parser.add_argument("--db", dest='db', type=str, default='db.ini', help='database file. Default db.ini') help='Load configuration from this file. Default: configuration.ini')
parser.add_argument("--db", dest='db', type=str,
default='db.ini', help='database file. Default db.ini')
parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", help="Only Error logs") parser.add_argument("-q", "--quiet", dest="quiet",
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="Show debug log") action="store_true", help="Only Error logs")
parser.add_argument("-v", "--verbose", dest="verbose",
action="store_true", help="Show debug log")
# Mumble arguments # Mumble arguments
parser.add_argument("-s", "--server", dest="host", type=str, help="Hostname of the Mumble server") parser.add_argument("-s", "--server", dest="host",
parser.add_argument("-u", "--user", dest="user", type=str, help="Username for the bot") type=str, help="Hostname of the Mumble server")
parser.add_argument("-P", "--password", dest="password", type=str, help="Server password, if required") parser.add_argument("-u", "--user", dest="user",
parser.add_argument("-T", "--tokens", dest="tokens", type=str, help="Server tokens, if required") type=str, help="Username for the bot")
parser.add_argument("-p", "--port", dest="port", type=int, help="Port for the Mumble server") parser.add_argument("-P", "--password", dest="password",
parser.add_argument("-c", "--channel", dest="channel", type=str, help="Default channel for the bot") type=str, help="Server password, if required")
parser.add_argument("-C", "--cert", dest="certificate", type=str, default=None, help="Certificate file") parser.add_argument("-T", "--tokens", dest="tokens",
type=str, help="Server tokens, if required")
parser.add_argument("-p", "--port", dest="port",
type=int, help="Port for the Mumble server")
parser.add_argument("-c", "--channel", dest="channel",
type=str, help="Default channel for the bot")
parser.add_argument("-C", "--cert", dest="certificate",
type=str, default=None, help="Certificate file")
args = parser.parse_args() args = parser.parse_args()
var.dbfile = args.db var.dbfile = args.db
config = configparser.ConfigParser(interpolation=None, allow_no_value=True) config = configparser.ConfigParser(interpolation=None, allow_no_value=True)
parsed_configs = config.read(['configuration.default.ini', args.config], encoding='latin-1') parsed_configs = config.read(
['configuration.default.ini', args.config], encoding='latin-1')
db = configparser.ConfigParser(interpolation=None, allow_no_value=True, delimiters='²') db = configparser.ConfigParser(
interpolation=None, allow_no_value=True, delimiters='²')
db.read(var.dbfile, encoding='latin-1') db.read(var.dbfile, encoding='latin-1')
if 'url_ban' not in db.sections(): if 'url_ban' not in db.sections():
@ -720,7 +796,8 @@ if __name__ == '__main__':
db.add_section('user_ban') db.add_section('user_ban')
if len(parsed_configs) == 0: if len(parsed_configs) == 0:
logging.error('Could not read configuration from file \"{}\"'.format(args.config), file=sys.stderr) logging.error('Could not read configuration from file \"{}\"'.format(
args.config), file=sys.stderr)
sys.exit() sys.exit()
var.config = config var.config = config

732
mumbleBot.py.bak Normal file
View File

@ -0,0 +1,732 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import threading
import time
import sys
import signal
import configparser
import audioop
import subprocess as sp
import argparse
import os.path
import pymumble.pymumble_py3 as pymumble
import interface
import variables as var
import hashlib
import youtube_dl
import logging
import util
import base64
from PIL import Image
from io import BytesIO
from mutagen.easyid3 import EasyID3
import re
import media.url
import media.file
import media.playlist
import media.radio
import media.system
from radiobrowser import getstations_byname
"""
FORMAT OF A MUSIC INTO THE PLAYLIST
type : url
url
title
path
duration
thundnail
user
ready (validation, no, downloading, yes)
from_playlist (yes,no)
playlist_title
playlist_url
type : radio
url
name
current_title
user
type : file
path
title
duration
user
"""
version = 2
class MumbleBot:
def __init__(self, args):
signal.signal(signal.SIGINT, self.ctrl_caught)
self.volume = var.config.getfloat('bot', 'volume')
if db.has_option('bot', 'volume'):
self.volume = var.db.getfloat('bot', 'volume')
self.channel = args.channel
# Set specific format for the log
FORMAT = '%(asctime)s: %(message)s'
if args.verbose:
logging.basicConfig(format=FORMAT, level=logging.DEBUG, datefmt='%Y-%m-%d %H:%M:%S')
logging.debug("Starting in DEBUG loglevel")
elif args.quiet:
logging.basicConfig(format=FORMAT, level=logging.ERROR, datefmt='%Y-%m-%d %H:%M:%S')
logging.error("Starting in ERROR loglevel")
else:
logging.basicConfig(format=FORMAT, level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
logging.info("Starting in INFO loglevel")
# the playlist is... a list (Surprise !!)
var.playlist = []
var.user = args.user
var.music_folder = var.config.get('bot', 'music_folder')
var.is_proxified = var.config.getboolean("webinterface", "is_web_proxified")
self.exit = False
self.nb_exit = 0
self.thread = None
self.is_playing = False
if var.config.getboolean("webinterface", "enabled"):
wi_addr = var.config.get("webinterface", "listening_addr")
wi_port = var.config.getint("webinterface", "listening_port")
interface.init_proxy()
tt = threading.Thread(target=start_web_interface, args=(wi_addr, wi_port))
tt.daemon = True
tt.start()
if args.host:
host = args.host
else:
host = var.config.get("server", "host")
if args.port:
port = args.port
else:
port = var.config.getint("server", "port")
if args.password:
password = args.password
else:
password = var.config.get("server", "password")
if args.certificate:
certificate = args.certificate
else:
certificate = var.config.get("server", "certificate")
if args.tokens:
tokens = args.tokens
else:
tokens = var.config.get("server", "tokens")
tokens = tokens.split(',')
if args.user:
self.username = args.user
else:
self.username = var.config.get("bot", "username")
self.mumble = pymumble.Mumble(host, user=self.username, port=port, password=password, tokens=tokens,
debug=var.config.getboolean('debug', 'mumbleConnection'), certfile=certificate)
self.mumble.callbacks.set_callback("text_received", self.message_received)
self.mumble.set_codec_profile("audio")
self.mumble.start() # start the mumble thread
self.mumble.is_ready() # wait for the connection
self.set_comment()
self.mumble.users.myself.unmute() # by sure the user is not muted
if self.channel:
self.mumble.channels.find_by_name(self.channel).move_in()
self.mumble.set_bandwidth(200000)
self.loop()
# Set the CTRL+C shortcut
def ctrl_caught(self, signal, frame):
logging.info("\nSIGINT caught, quitting, {} more to kill".format(2 - self.nb_exit))
self.exit = True
self.stop()
if self.nb_exit > 1:
logging.info("Forced Quit")
sys.exit(0)
self.nb_exit += 1
# All text send to the chat is analysed by this function
def message_received(self, text):
message = text.message.strip()
user = self.mumble.users[text.actor]['name']
if var.config.getboolean('command', 'split_username_at_space'):
# in can you use https://github.com/Natenom/mumblemoderator-module-collection/tree/master/os-suffixes , you want to split the username
user = user.split()[0]
if message[0] == var.config.get('command', 'command_symbol'):
# remove the symbol from the message
message = message[1:].split(' ', 1)
# use the first word as a command, the others one as parameters
if len(message) > 0:
command = message[0]
parameter = ''
if len(message) > 1:
parameter = message[1]
else:
return
logging.info(command + ' - ' + parameter + ' by ' + user)
if command == var.config.get('command', 'joinme'):
self.mumble.users.myself.move_in(self.mumble.users[text.actor]['channel_id'], token=parameter)
return
# Anti stupid guy function
if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_other_channel_message') and self.mumble.users[text.actor]['channel_id'] != self.mumble.users.myself['channel_id']:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_in_my_channel'))
return
if not self.is_admin(user) and not var.config.getboolean('bot', 'allow_private_message') and text.session:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'pm_not_allowed'))
return
###
# Admin command
###
for i in var.db.items("user_ban"):
if user.lower() == i[0]:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'user_ban'))
return
if command == var.config.get('command', 'user_ban'):
if self.is_admin(user):
if parameter:
self.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
else:
self.mumble.users[text.actor].send_text_message(util.get_user_ban())
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
return
elif command == var.config.get('command', 'user_unban'):
if self.is_admin(user):
if parameter:
self.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
return
elif command == var.config.get('command', 'url_ban'):
if self.is_admin(user):
if parameter:
self.mumble.users[text.actor].send_text_message(util.url_ban(self.get_url_from_input(parameter)))
else:
self.mumble.users[text.actor].send_text_message(util.get_url_ban())
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
return
elif command == var.config.get('command', 'url_unban'):
if self.is_admin(user):
if parameter:
self.mumble.users[text.actor].send_text_message(util.url_unban(self.get_url_from_input(parameter)))
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
return
if parameter:
for i in var.db.items("url_ban"):
if self.get_url_from_input(parameter.lower()) == i[0]:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'url_ban'))
return
###
# everyday commands
###
if command == var.config.get('command', 'play_file') and parameter:
music_folder = var.config.get('bot', 'music_folder')
# sanitize "../" and so on
path = os.path.abspath(os.path.join(music_folder, parameter))
if path.startswith(music_folder):
if os.path.isfile(path):
filename = path.replace(music_folder, '')
music = {'type': 'file',
'path': filename,
'user': user}
var.playlist.append(music)
else:
# try to do a partial match
matches = [file for file in util.get_recursive_filelist_sorted(music_folder) if parameter.lower() in file.lower()]
if len(matches) == 0:
self.send_msg(var.config.get('strings', 'no_file'), text)
elif len(matches) == 1:
music = {'type': 'file',
'path': matches[0],
'user': user}
var.playlist.append(music)
else:
msg = var.config.get('strings', 'multiple_matches') + '<br />'
msg += '<br />'.join(matches)
self.send_msg(msg, text)
else:
self.send_msg(var.config.get('strings', 'bad_file'), text)
self.async_download_next()
elif command == var.config.get('command', 'play_url') and parameter:
music = {'type': 'url',
'url': self.get_url_from_input(parameter), # grab the real URL
'user': user,
'ready': 'validation'}
var.playlist.append(music)
if media.url.get_url_info():
if var.playlist[-1]['duration'] > var.config.getint('bot', 'max_track_duration'):
var.playlist.pop()
self.send_msg(var.config.get('strings', 'too_long'), text)
else:
for i in var.db.options("url_ban"):
if var.playlist[-1]['url'] == i:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'url_ban'))
var.playlist.pop()
return
var.playlist[-1]['ready'] = "no"
self.async_download_next()
else:
var.playlist.pop()
self.send_msg(var.config.get('strings', 'unable_download'), text)
elif command == var.config.get('command', 'play_playlist') and parameter:
offset = 1 # if you want to start the playlist at a specific index
try:
offset = int(parameter.split(" ")[-1])
except ValueError:
pass
if media.playlist.get_playlist_info(url=self.get_url_from_input(parameter), start_index=offset, user=user):
self.async_download_next()
elif command == var.config.get('command', 'play_radio'):
if not parameter:
all_radio = var.config.items('radio')
msg = var.config.get('strings', 'preconfigurated_radio') + " :"
for i in all_radio:
comment = ""
if len(i[1].split(maxsplit=1)) == 2:
comment = " - " + i[1].split(maxsplit=1)[1]
msg += "<br />" + i[0] + comment
self.send_msg(msg, text)
else:
if var.config.has_option('radio', parameter):
parameter = var.config.get('radio', parameter)
parameter = parameter.split()[0]
url = self.get_url_from_input(parameter)
if url:
music = {'type': 'radio',
'url': url,
'user': user}
var.playlist.append(music)
self.async_download_next()
else:
self.send_msg(var.config.get('strings', 'bad_url'))
elif command == var.config.get('command', 'rb_query'):
logging.info('Querying radio stations')
if not parameter:
logging.info('rbquery without parameter')
else:
logging.info('Found query parameter: ' + parameter)
stations = getstations_byname(parameter)
elif command == var.config.get('command', 'help'):
self.send_msg(var.config.get('strings', 'help'), text)
if self.is_admin(user):
self.send_msg(var.config.get('strings', 'admin_help'), text)
elif command == var.config.get('command', 'stop'):
self.stop()
elif command == var.config.get('command', 'kill'):
if self.is_admin(user):
self.stop()
self.exit = True
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
elif command == var.config.get('command', 'update'):
if self.is_admin(user):
self.mumble.users[text.actor].send_text_message("Starting the update")
# Need to be improved
msg = util.update(version)
self.mumble.users[text.actor].send_text_message(msg)
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'not_admin'))
elif command == var.config.get('command', 'stop_and_getout'):
self.stop()
if self.channel:
self.mumble.channels.find_by_name(self.channel).move_in()
elif command == var.config.get('command', 'volume'):
# The volume is a percentage
if parameter is not None and parameter.isdigit() and 0 <= int(parameter) <= 100:
self.volume = float(float(parameter) / 100)
self.send_msg(var.config.get('strings', 'change_volume') % (
int(self.volume * 100), self.mumble.users[text.actor]['name']), text)
var.db.set('bot', 'volume', str(self.volume))
else:
self.send_msg(var.config.get('strings', 'current_volume') % int(self.volume * 100), text)
elif command == var.config.get('command', 'current_music'):
if len(var.playlist) > 0:
source = var.playlist[0]["type"]
if source == "radio":
reply = "[radio] {title} on {url} by {user}".format(
title=media.radio.get_radio_title(var.playlist[0]["url"]),
url=var.playlist[0]["title"],
user=var.playlist[0]["user"]
)
elif source == "url" and 'from_playlist' in var.playlist[0]:
reply = "[playlist] {title} (from the playlist <a href=\"{url}\">{playlist}</a> by {user}".format(
title=var.playlist[0]["title"],
url=var.playlist[0]["playlist_url"],
playlist=var.playlist[0]["playlist_title"],
user=var.playlist[0]["user"]
)
elif source == "url":
reply = "[url] {title} (<a href=\"{url}\">{url}</a>) by {user}".format(
title=var.playlist[0]["title"],
url=var.playlist[0]["url"],
user=var.playlist[0]["user"]
)
elif source == "file":
reply = "[file] {title} by {user}".format(
title=var.playlist[0]["title"],
user=var.playlist[0]["user"])
else:
reply = "ERROR"
logging.error(var.playlist)
else:
reply = var.config.get('strings', 'not_playing')
self.send_msg(reply, text)
elif command == var.config.get('command', 'skip'):
if parameter is not None and parameter.isdigit() and int(parameter) > 0: # Allow to remove specific music into the queue with a number
if int(parameter) < len(var.playlist):
removed = var.playlist.pop(int(parameter))
# the Title isn't here if the music wasn't downloaded
self.send_msg(var.config.get('strings', 'removing_item') % (removed['title'] if 'title' in removed else removed['url']), text)
else:
self.send_msg(var.config.get('strings', 'no_possible'), text)
elif self.next(): # Is no number send, just skip the current music
self.launch_music()
self.async_download_next()
else:
self.send_msg(var.config.get('strings', 'queue_empty'), text)
self.stop()
elif command == var.config.get('command', 'list'):
folder_path = var.config.get('bot', 'music_folder')
files = util.get_recursive_filelist_sorted(folder_path)
if files:
self.send_msg('<br>'.join(files), text)
else:
self.send_msg(var.config.get('strings', 'no_file'), text)
elif command == var.config.get('command', 'queue'):
if len(var.playlist) <= 1:
msg = var.config.get('strings', 'queue_empty')
else:
msg = var.config.get('strings', 'queue_contents') + '<br />'
i = 1
for value in var.playlist[1:]:
msg += '[{}] ({}) {}<br />'.format(i, value['type'], value['title'] if 'title' in value else value['url'])
i += 1
self.send_msg(msg, text)
elif command == var.config.get('command', 'repeat'):
var.playlist.append(var.playlist[0])
else:
self.mumble.users[text.actor].send_text_message(var.config.get('strings', 'bad_command'))
@staticmethod
def is_admin(user):
list_admin = var.config.get('bot', 'admin').split(';')
if user in list_admin:
return True
else:
return False
@staticmethod
def next():
logging.debug("Next into the queue")
if len(var.playlist) > 1:
var.playlist.pop(0)
return True
elif len(var.playlist) == 1:
var.playlist.pop(0)
return False
else:
return False
def launch_music(self):
uri = ""
logging.debug("launch_music asked" + str(var.playlist[0]))
if var.playlist[0]["type"] == "url":
# Delete older music is the tmp folder is too big
media.system.clear_tmp_folder(var.config.get('bot', 'tmp_folder'), var.config.getint('bot', 'tmp_folder_max_size'))
# Check if the music is ready to be played
if var.playlist[0]["ready"] == "downloading":
return
elif var.playlist[0]["ready"] != "yes":
logging.info("Current music wasn't ready, Downloading...")
self.download_music(index=0)
# get the Path
uri = var.playlist[0]['path']
if os.path.isfile(uri):
audio = EasyID3(uri)
title = ""
if audio["title"]:
title = audio["title"][0] # take the title from the file tag
path_thumbnail = var.playlist[0]['path'][:-4] + '.jpg' # Remove .mp3 and add .jpg
thumbnail_html = ""
if os.path.isfile(path_thumbnail):
# Create the image message
im = Image.open(path_thumbnail)
im.thumbnail((100, 100), Image.ANTIALIAS)
buffer = BytesIO()
im.save(buffer, format="JPEG")
thumbnail_base64 = base64.b64encode(buffer.getvalue())
thumbnail_html = '<img - src="data:image/PNG;base64,' + thumbnail_base64.decode() + '"/>'
logging.debug("Thunbail data " + thumbnail_html)
if var.config.getboolean('bot', 'announce_current_music'):
self.send_msg(var.config.get('strings', 'now_playing') % (title, thumbnail_html))
else:
logging.error("Error with the path during launch_music")
pass
elif var.playlist[0]["type"] == "file":
uri = var.config.get('bot', 'music_folder') + var.playlist[0]["path"]
elif var.playlist[0]["type"] == "radio":
uri = var.playlist[0]["url"]
title = media.radio.get_radio_server_description(uri)
var.playlist[0]["title"] = title
if var.config.getboolean('bot', 'announce_current_music'):
self.send_msg(var.config.get('strings', 'now_playing') % (title, "URL : " + uri))
if var.config.getboolean('debug', 'ffmpeg'):
ffmpeg_debug = "debug"
else:
ffmpeg_debug = "warning"
command = ("ffmpeg", '-v', ffmpeg_debug, '-nostdin', '-i', uri, '-ac', '1', '-f', 's16le', '-ar', '48000', '-')
logging.info("FFmpeg command : " + " ".join(command))
self.thread = sp.Popen(command, stdout=sp.PIPE, bufsize=480) # The ffmpeg process is a thread
self.is_playing = True
def download_music(self, index):
if var.playlist[index]['type'] == 'url' and var.playlist[index]['ready'] == "validation":
if media.url.get_url_info(index=index):
if var.playlist[index]['duration'] > var.config.getint('bot', 'max_track_duration'):
# Check the length, useful in case of playlist, it wasn't checked before)
var.playlist.pop()
logging.info("the music " + var.playlist[index]["url"] + " has a duration of " + var.playlist[index]['duration'] + "s -- too long")
self.send_msg(var.config.get('strings', 'too_long'))
return
else:
var.playlist[index]['ready'] = "no"
else:
var.playlist.pop(index)
logging.error("Error while fetching info from the URL")
self.send_msg(var.config.get('strings', 'unable_download'))
if var.playlist[index]['type'] == 'url' and var.playlist[index]['ready'] == "no":
# download the music
var.playlist[index]['ready'] = "downloading"
logging.debug("Download index:" + str(index))
logging.debug(var.playlist[index])
url = var.playlist[index]['url']
url_hash = hashlib.md5(url.encode()).hexdigest()
path = var.config.get('bot', 'tmp_folder') + url_hash + ".%(ext)s"
mp3 = path.replace(".%(ext)s", ".mp3")
var.playlist[index]['path'] = mp3
# if os.path.isfile(mp3):
# audio = EasyID3(mp3)
# var.playlist[index]['title'] = audio["title"][0]
ydl_opts = ""
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': path,
'noplaylist': True,
'writethumbnail': True,
'updatetime': False,
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192'},
{'key': 'FFmpegMetadata'}]
}
self.send_msg(var.config.get('strings', "download_in_progress") % var.playlist[index]['title'])
logging.info("Information before start downloading :" + str(var.playlist[index]))
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
for i in range(2): # Always try 2 times
try:
ydl.extract_info(url)
if 'ready' in var.playlist[index] and var.playlist[index]['ready'] == "downloading":
var.playlist[index]['ready'] = "yes"
except youtube_dl.utils.DownloadError:
pass
else:
break
return
def async_download_next(self):
# Function start if the next music isn't ready
# Do nothing in case the next music is already downloaded
logging.info("Async download next asked")
if len(var.playlist) > 1 and var.playlist[1]['type'] == 'url' and var.playlist[1]['ready'] in ["no", "validation"]:
th = threading.Thread(target=self.download_music, kwargs={'index': 1})
else:
return
logging.info("Start downloading next in thread")
th.daemon = True
th.start()
@staticmethod
# Parse the html from the message to get the URL
def get_url_from_input(string):
if string.startswith('http'):
return string
p = re.compile('href="(.+?)"', re.IGNORECASE)
res = re.search(p, string)
if res:
return res.group(1)
else:
return False
# Main loop of the Bot
def loop(self):
raw_music = ""
while not self.exit and self.mumble.isAlive():
while self.mumble.sound_output.get_buffer_size() > 0.5 and not self.exit:
# If the buffer isn't empty, I cannot send new music part, so I wait
time.sleep(0.01)
if self.thread:
# I get raw from ffmpeg thread
raw_music = self.thread.stdout.read(480)
if raw_music:
# Adjust the volume and send it to mumble
self.mumble.sound_output.add_sound(audioop.mul(raw_music, 2, self.volume))
else:
time.sleep(0.1)
else:
time.sleep(0.1)
if self.thread is None or not raw_music:
# Not music into the buffet
if self.is_playing:
# get next music
self.is_playing = False
self.next()
if len(var.playlist) > 0:
if var.playlist[0]['type'] in ['radio', 'file'] \
or (var.playlist[0]['type'] == 'url' and var.playlist[0]['ready'] not in ['validation', 'downloading']):
# Check if the music can be start before launch the music
self.launch_music()
self.async_download_next()
while self.mumble.sound_output.get_buffer_size() > 0:
# Empty the buffer before exit
time.sleep(0.01)
time.sleep(0.5)
if self.exit:
# The db is not fixed config like url/user ban and volume
util.write_db()
def stop(self):
# Kill the ffmpeg thread and empty the playlist
if self.thread:
self.thread.kill()
self.thread = None
var.playlist = []
self.is_playing = False
def set_comment(self):
self.mumble.users.myself.comment(var.config.get('bot', 'comment'))
def send_msg(self, msg, text=None):
# text if the object message, contain information if direct message or channel message
if not text or not text.session:
own_channel = self.mumble.channels[self.mumble.users.myself['channel_id']]
own_channel.send_text_message(msg)
else:
self.mumble.users[text.actor].send_text_message(msg)
def start_web_interface(addr, port):
logging.info('Starting web interface on {}:{}'.format(addr, port))
interface.web.run(port=port, host=addr)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Bot for playing music on Mumble')
# General arguments
parser.add_argument("--config", dest='config', type=str, default='configuration.ini', help='Load configuration from this file. Default: configuration.ini')
parser.add_argument("--db", dest='db', type=str, default='db.ini', help='database file. Default db.ini')
parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", help="Only Error logs")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help="Show debug log")
# Mumble arguments
parser.add_argument("-s", "--server", dest="host", type=str, help="Hostname of the Mumble server")
parser.add_argument("-u", "--user", dest="user", type=str, help="Username for the bot")
parser.add_argument("-P", "--password", dest="password", type=str, help="Server password, if required")
parser.add_argument("-T", "--tokens", dest="tokens", type=str, help="Server tokens, if required")
parser.add_argument("-p", "--port", dest="port", type=int, help="Port for the Mumble server")
parser.add_argument("-c", "--channel", dest="channel", type=str, help="Default channel for the bot")
parser.add_argument("-C", "--cert", dest="certificate", type=str, default=None, help="Certificate file")
args = parser.parse_args()
var.dbfile = args.db
config = configparser.ConfigParser(interpolation=None, allow_no_value=True)
parsed_configs = config.read(['configuration.default.ini', args.config], encoding='latin-1')
db = configparser.ConfigParser(interpolation=None, allow_no_value=True, delimiters='²')
db.read(var.dbfile, encoding='latin-1')
if 'url_ban' not in db.sections():
db.add_section('url_ban')
if 'bot' not in db.sections():
db.add_section('bot')
if 'user_ban' not in db.sections():
db.add_section('user_ban')
if len(parsed_configs) == 0:
logging.error('Could not read configuration from file \"{}\"'.format(args.config), file=sys.stderr)
sys.exit()
var.config = config
var.db = db
botamusique = MumbleBot(args)

20
radiobrowser.py Normal file
View File

@ -0,0 +1,20 @@
from rbRadios import RadioBrowser
rb = RadioBrowser()
def getstations_byname(query):
results = rb.stations_byname(query)
stations = []
for st in results:
try:
url = rb.playable_station(st['id'])['url']
station = {'stationname': st['name'], 'url': url}
stations.append(station)
except:
pass
return stations
if __name__ == "__main__":
r = getstations_byname('r.sh')
pass