]> git.0d.be Git - botaradio.git/blob - command.py
fix: ways to see if parameter is empty
[botaradio.git] / command.py
1 # coding=utf-8
2 import logging
3 import os.path
4 import pymumble.pymumble_py3 as pymumble
5 import re
6
7 import constants
8 import media.file
9 import media.playlist
10 import media.radio
11 import media.system
12 import media.url
13 import util
14 import variables as var
15 from librb import radiobrowser
16 from database import Database
17
18
19 def register_all_commands(bot):
20     bot.register_command(constants.commands('joinme'), cmd_joinme)
21     bot.register_command(constants.commands('user_ban'), cmd_user_ban)
22     bot.register_command(constants.commands('user_unban'), cmd_user_unban)
23     bot.register_command(constants.commands('url_ban'), cmd_url_ban)
24     bot.register_command(constants.commands('url_unban'), cmd_url_unban)
25     bot.register_command(constants.commands('play'), cmd_play)
26     bot.register_command(constants.commands('pause'), cmd_pause)
27     bot.register_command(constants.commands('play_file'), cmd_play_file)
28     bot.register_command(constants.commands('play_file_match'), cmd_play_file_match)
29     bot.register_command(constants.commands('play_url'), cmd_play_url)
30     bot.register_command(constants.commands('play_playlist'), cmd_play_playlist)
31     bot.register_command(constants.commands('play_radio'), cmd_play_radio)
32     bot.register_command(constants.commands('rb_query'), cmd_rb_query)
33     bot.register_command(constants.commands('rb_play'), cmd_rb_play)
34     bot.register_command(constants.commands('help'), cmd_help)
35     bot.register_command(constants.commands('stop'), cmd_stop)
36     bot.register_command(constants.commands('clear'), cmd_clear)
37     bot.register_command(constants.commands('kill'), cmd_kill)
38     bot.register_command(constants.commands('update'), cmd_update)
39     bot.register_command(constants.commands('stop_and_getout'), cmd_stop_and_getout)
40     bot.register_command(constants.commands('volume'), cmd_volume)
41     bot.register_command(constants.commands('ducking'), cmd_ducking)
42     bot.register_command(constants.commands('ducking_threshold'), cmd_ducking_threshold)
43     bot.register_command(constants.commands('ducking_volume'), cmd_ducking_volume)
44     bot.register_command(constants.commands('current_music'), cmd_current_music)
45     bot.register_command(constants.commands('skip'), cmd_skip)
46     bot.register_command(constants.commands('remove'), cmd_remove)
47     bot.register_command(constants.commands('list_file'), cmd_list_file)
48     bot.register_command(constants.commands('queue'), cmd_queue)
49     bot.register_command(constants.commands('random'), cmd_random)
50     bot.register_command(constants.commands('repeat'), cmd_repeat)
51     bot.register_command(constants.commands('mode'), cmd_mode)
52     bot.register_command(constants.commands('drop_database'), cmd_drop_database)
53
54 def send_multi_lines(bot, lines, text):
55     msg = ""
56     br = ""
57     for newline in lines:
58         msg += br
59         br = "<br>"
60         if len(msg) + len(newline) > 5000:
61             bot.send_msg(msg, text)
62             msg = ""
63         msg += newline
64
65     bot.send_msg(msg, text)
66
67 # ---------------- Commands ------------------
68
69
70 def cmd_joinme(bot, user, text, command, parameter):
71     bot.mumble.users.myself.move_in(
72         bot.mumble.users[text.actor]['channel_id'], token=parameter)
73
74
75 def cmd_user_ban(bot, user, text, command, parameter):
76     if bot.is_admin(user):
77         if parameter:
78             bot.mumble.users[text.actor].send_text_message(util.user_ban(parameter))
79         else:
80             bot.mumble.users[text.actor].send_text_message(util.get_user_ban())
81     else:
82         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
83     return
84
85
86 def cmd_user_unban(bot, user, text, command, parameter):
87     if bot.is_admin(user):
88         if parameter:
89             bot.mumble.users[text.actor].send_text_message(util.user_unban(parameter))
90     else:
91         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
92     return
93
94
95 def cmd_url_ban(bot, user, text, command, parameter):
96     if bot.is_admin(user):
97         if parameter:
98             bot.mumble.users[text.actor].send_text_message(util.url_ban(util.get_url_from_input(parameter)))
99         else:
100             bot.mumble.users[text.actor].send_text_message(util.get_url_ban())
101     else:
102         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
103     return
104
105
106 def cmd_url_unban(bot, user, text, command, parameter):
107     if bot.is_admin(user):
108         if parameter:
109             bot.mumble.users[text.actor].send_text_message(util.url_unban(util.get_url_from_input(parameter)))
110     else:
111         bot.mumble.users[text.actor].send_text_message(constants.strings('not_admin'))
112     return
113
114
115 def cmd_play(bot, user, text, command, parameter):
116     if var.playlist.length() > 0:
117         if parameter:
118             if parameter.isdigit() and int(parameter) > 0 and int(parameter) <= len(var.playlist):
119                 bot.interrupt_playing()
120                 bot.launch_music(int(parameter) - 1)
121             else:
122                 bot.send_msg(constants.strings('invalid_index', index=parameter), text)
123
124         elif bot.is_pause:
125             bot.resume()
126         else:
127             bot.send_msg(util.format_current_playing(), text)
128     else:
129         bot.is_pause = False
130         bot.send_msg(constants.strings('queue_empty'), text)
131
132
133 def cmd_pause(bot, user, text, command, parameter):
134     bot.pause()
135     bot.send_msg(constants.strings('paused'))
136
137
138 def cmd_play_file(bot, user, text, command, parameter):
139     music_folder = var.config.get('bot', 'music_folder')
140     # if parameter is {index}
141     if parameter.isdigit():
142         files = util.get_recursive_filelist_sorted(music_folder)
143         if int(parameter) < len(files):
144             filename = files[int(parameter)].replace(music_folder, '')
145             music = {'type': 'file',
146                      'path': filename,
147                      'user': user}
148             music = var.playlist.append(music)
149             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
150             bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
151
152     # if parameter is {path}
153     else:
154         # sanitize "../" and so on
155         path = os.path.abspath(os.path.join(music_folder, parameter))
156         if not path.startswith(os.path.abspath(music_folder)):
157             bot.send_msg(constants.strings('no_file'), text)
158             return
159
160         if os.path.isfile(path):
161             music = {'type': 'file',
162                      'path': parameter,
163                      'user': user}
164             music = var.playlist.append(music)
165             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
166             bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
167             return
168
169         # if parameter is {folder}
170         elif os.path.isdir(path):
171             if parameter != '.' and parameter != './':
172                 if not parameter.endswith("/"):
173                     parameter += "/"
174             else:
175                 parameter = ""
176
177             files = util.get_recursive_filelist_sorted(music_folder)
178             music_library = util.Dir(music_folder)
179             for file in files:
180                 music_library.add_file(file)
181
182             files = music_library.get_files(parameter)
183             msgs = [constants.strings('multiple_file_added')]
184             count = 0
185
186             for file in files:
187                 count += 1
188                 music = {'type': 'file',
189                          'path': file,
190                          'user': user}
191                 music = var.playlist.append(music)
192                 logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
193                 msgs.append("{} ({})".format(music['title'], music['path']))
194
195             if count != 0:
196                 send_multi_lines(bot, msgs, text)
197             else:
198                 bot.send_msg(constants.strings('no_file'), text)
199
200         else:
201             # try to do a partial match
202             files = util.get_recursive_filelist_sorted(music_folder)
203             matches = [(index, file) for index, file in enumerate(files) if parameter.lower() in file.lower()]
204             if len(matches) == 0:
205                 bot.send_msg(constants.strings('no_file'), text)
206             elif len(matches) == 1:
207                 music = {'type': 'file',
208                          'path': matches[0][1],
209                          'user': user}
210                 music = var.playlist.append(music)
211                 logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
212                 bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
213             else:
214                 msgs = [ constants.strings('multiple_matches')]
215                 for match in matches:
216                     msgs.append("<b>{:0>3d}</b> - {:s}".format(match[0], match[1]))
217                 send_multi_lines(bot, msgs, text)
218
219
220 def cmd_play_file_match(bot, user, text, command, parameter):
221     music_folder = var.config.get('bot', 'music_folder')
222     if parameter:
223         files = util.get_recursive_filelist_sorted(music_folder)
224         msgs = [ constants.strings('multiple_file_added')]
225         count = 0
226         try:
227             for file in files:
228                 match = re.search(parameter, file)
229                 if match:
230                     count += 1
231                     music = {'type': 'file',
232                              'path': file,
233                              'user': user}
234                     music = var.playlist.append(music)
235                     logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
236
237                     msgs.append("{} ({})".format(music['title'], music['path']))
238
239             if count != 0:
240                 send_multi_lines(bot, msgs, text)
241             else:
242                 bot.send_msg(constants.strings('no_file'), text)
243
244         except re.error as e:
245             msg = constants.strings('wrong_pattern', error=str(e))
246             bot.send_msg(msg, text)
247     else:
248         bot.send_msg(constants.strings('bad_parameter', command))
249
250
251 def cmd_play_url(bot, user, text, command, parameter):
252     music = {'type': 'url',
253              # grab the real URL
254              'url': util.get_url_from_input(parameter),
255              'user': user,
256              'ready': 'validation'}
257
258     music = bot.validate_music(music)
259     if music:
260         music = var.playlist.append(music)
261         logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
262         bot.send_msg(constants.strings('file_added', item=util.format_song_string(music)), text)
263         if var.playlist.length() == 2:
264             # If I am the second item on the playlist. (I am the next one!)
265             bot.async_download_next()
266     else:
267         bot.send_msg(constants.strings('unable_download'), text)
268
269
270 def cmd_play_playlist(bot, user, text, command, parameter):
271     offset = 0  # if you want to start the playlist at a specific index
272     try:
273         offset = int(parameter.split(" ")[-1])
274     except ValueError:
275         pass
276
277     url = util.get_url_from_input(parameter)
278     logging.debug("cmd: fetching media info from playlist url %s" % url)
279     items = media.playlist.get_playlist_info(url=url, start_index=offset, user=user)
280     if len(items) > 0:
281         var.playlist.extend(items)
282         for music in items:
283             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
284
285
286 def cmd_play_radio(bot, user, text, command, parameter):
287     if not parameter:
288         all_radio = var.config.items('radio')
289         msg = constants.strings('preconfigurated_radio')
290         for i in all_radio:
291             comment = ""
292             if len(i[1].split(maxsplit=1)) == 2:
293                 comment = " - " + i[1].split(maxsplit=1)[1]
294             msg += "<br />" + i[0] + comment
295         bot.send_msg(msg, text)
296     else:
297         if var.config.has_option('radio', parameter):
298             parameter = var.config.get('radio', parameter)
299             parameter = parameter.split()[0]
300         url = util.get_url_from_input(parameter)
301         if url:
302             music = {'type': 'radio',
303                      'url': url,
304                      'user': user}
305             var.playlist.append(music)
306             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
307             bot.async_download_next()
308         else:
309             bot.send_msg(constants.strings('bad_url'))
310
311
312 def cmd_rb_query(bot, user, text, command, parameter):
313     logging.info('cmd: Querying radio stations')
314     if not parameter:
315         logging.debug('rbquery without parameter')
316         msg = constants.strings('rb_query_empty')
317         bot.send_msg(msg, text)
318     else:
319         logging.debug('cmd: Found query parameter: ' + parameter)
320         # bot.send_msg('Searching for stations - this may take some seconds...', text)
321         rb_stations = radiobrowser.getstations_byname(parameter)
322         msg = constants.strings('rb_query_result')
323         msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th></tr>'
324         if not rb_stations:
325             logging.debug('cmd: No matches found for rbquery ' + parameter)
326             bot.send_msg('Radio-Browser found no matches for ' + parameter, text)
327         else:
328             for s in rb_stations:
329                 stationid = s['id']
330                 stationname = s['stationname']
331                 country = s['country']
332                 codec = s['codec']
333                 bitrate = s['bitrate']
334                 genre = s['genre']
335                 # msg += f'<tr><td>{stationid}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td></tr>'
336                 msg += '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td></tr>' % (
337                     stationid, stationname, genre, codec, bitrate, country)
338             msg += '</table>'
339             # Full message as html table
340             if len(msg) <= 5000:
341                 bot.send_msg(msg, text)
342             # Shorten message if message too long (stage I)
343             else:
344                 logging.debug('Result too long stage I')
345                 msg = constants.strings('rb_query_result') + ' (shortened L1)'
346                 msg += '\n<table><tr><th>!rbplay ID</th><th>Station Name</th></tr>'
347                 for s in rb_stations:
348                     stationid = s['id']
349                     stationname = s['stationname']
350                     # msg += f'<tr><td>{stationid}</td><td>{stationname}</td>'
351                     msg += '<tr><td>%s</td><td>%s</td>' % (stationid, stationname)
352                 msg += '</table>'
353                 if len(msg) <= 5000:
354                     bot.send_msg(msg, text)
355                 # Shorten message if message too long (stage II)
356                 else:
357                     logging.debug('Result too long stage II')
358                     msg = constants.strings('rb_query_result') + ' (shortened L2)'
359                     msg += '!rbplay ID - Station Name'
360                     for s in rb_stations:
361                         stationid = s['id']
362                         stationname = s['stationname'][:12]
363                         # msg += f'{stationid} - {stationname}'
364                         msg += '%s - %s' % (stationid, stationname)
365                     if len(msg) <= 5000:
366                         bot.send_msg(msg, text)
367                     # Message still too long
368                     else:
369                         bot.send_msg('Query result too long to post (> 5000 characters), please try another query.',
370                                      text)
371
372
373 def cmd_rb_play(bot, user, text, command, parameter):
374     logging.debug('cmd: Play a station by ID')
375     if not parameter:
376         logging.debug('rbplay without parameter')
377         msg = constants.strings('rb_play_empty')
378         bot.send_msg(msg, text)
379     else:
380         logging.debug('cmd: Retreiving url for station ID ' + parameter)
381         rstation = radiobrowser.getstationname_byid(parameter)
382         stationname = rstation[0]['name']
383         country = rstation[0]['country']
384         codec = rstation[0]['codec']
385         bitrate = rstation[0]['bitrate']
386         genre = rstation[0]['tags']
387         homepage = rstation[0]['homepage']
388         msg = 'Radio station added to playlist:'
389         # msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
390         #       f'<tr><td>{parameter}</td><td>{stationname}</td><td>{genre}</td><td>{codec}/{bitrate}</td><td>{country}</td><td>{homepage}</td></tr></table>'
391         msg += '<table><tr><th>ID</th><th>Station Name</th><th>Genre</th><th>Codec/Bitrate</th><th>Country</th><th>Homepage</th></tr>' + \
392                '<tr><td>%s</td><td>%s</td><td>%s</td><td>%s/%s</td><td>%s</td><td>%s</td></tr></table>' \
393                % (parameter, stationname, genre, codec, bitrate, country, homepage)
394         logging.debug('cmd: Added station to playlist %s' % stationname)
395         bot.send_msg(msg, text)
396         url = radiobrowser.geturl_byid(parameter)
397         if url != "-1":
398             logging.info('cmd: Found url: ' + url)
399             music = {'type': 'radio',
400                      'title': stationname,
401                      'artist': homepage,
402                      'url': url,
403                      'user': user}
404             var.playlist.append(music)
405             logging.info("cmd: add to playlist: " + util.format_debug_song_string(music))
406             bot.async_download_next()
407         else:
408             logging.info('cmd: No playable url found.')
409             msg += "No playable url found for this station, please try another station."
410             bot.send_msg(msg, text)
411
412
413 def cmd_help(bot, user, text, command, parameter):
414     bot.send_msg(constants.strings('help'), text)
415     if bot.is_admin(user):
416         bot.send_msg(constants.strings('admin_help'), text)
417
418
419 def cmd_stop(bot, user, text, command, parameter):
420     bot.stop()
421     bot.send_msg(constants.strings('stopped'), text)
422
423
424 def cmd_clear(bot, user, text, command, parameter):
425     bot.clear()
426     bot.send_msg(constants.strings('cleared'), text)
427
428
429 def cmd_kill(bot, user, text, command, parameter):
430     if bot.is_admin(user):
431         bot.pause()
432         bot.exit = True
433     else:
434         bot.mumble.users[text.actor].send_text_message(
435             constants.strings('not_admin'))
436
437
438 def cmd_update(bot, user, text, command, parameter):
439     if bot.is_admin(user):
440         bot.mumble.users[text.actor].send_text_message(
441             constants.strings('start_updating'))
442         msg = util.update(bot.version)
443         bot.mumble.users[text.actor].send_text_message(msg)
444     else:
445         bot.mumble.users[text.actor].send_text_message(
446             constants.strings('not_admin'))
447
448
449 def cmd_stop_and_getout(bot, user, text, command, parameter):
450     bot.stop()
451     if bot.channel:
452         bot.mumble.channels.find_by_name(bot.channel).move_in()
453
454
455 def cmd_volume(bot, user, text, command, parameter):
456     # The volume is a percentage
457     if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
458         bot.volume_set = float(float(parameter) / 100)
459         bot.send_msg(constants.strings('change_volume',
460             volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text)
461         var.db.set('bot', 'volume', str(bot.volume_set))
462         logging.info('cmd: volume set to %d' % (bot.volume_set * 100))
463     else:
464         bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text)
465
466
467 def cmd_ducking(bot, user, text, command, parameter):
468     if parameter == "" or parameter == "on":
469         bot.is_ducking = True
470         var.db.set('bot', 'ducking', True)
471         bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
472         bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000)
473         bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
474                                           bot.ducking_sound_received)
475         bot.mumble.set_receive_sound(True)
476         logging.info('cmd: ducking is on')
477         msg = "Ducking on."
478         bot.send_msg(msg, text)
479     elif parameter == "off":
480         bot.is_ducking = False
481         bot.mumble.set_receive_sound(False)
482         var.db.set('bot', 'ducking', False)
483         msg = "Ducking off."
484         logging.info('cmd: ducking is off')
485         bot.send_msg(msg, text)
486
487
488 def cmd_ducking_threshold(bot, user, text, command, parameter):
489     if parameter and parameter.isdigit():
490         bot.ducking_threshold = int(parameter)
491         var.db.set('bot', 'ducking_threshold', str(bot.ducking_threshold))
492         msg = "Ducking threshold set to %d." % bot.ducking_threshold
493         bot.send_msg(msg, text)
494     else:
495         msg = "Current ducking threshold is %d." % bot.ducking_threshold
496         bot.send_msg(msg, text)
497
498
499 def cmd_ducking_volume(bot, user, text, command, parameter):
500     # The volume is a percentage
501     if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
502         bot.ducking_volume = float(float(parameter) / 100)
503         bot.send_msg(constants.strings('change_ducking_volume',
504             volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text)
505         # var.db.set('bot', 'volume', str(bot.volume_set))
506         var.db.set('bot', 'ducking_volume', str(bot.ducking_volume))
507         logging.info('cmd: volume on ducking set to %d' % (bot.ducking_volume * 100))
508     else:
509         bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text)
510
511
512 def cmd_current_music(bot, user, text, command, parameter):
513     reply = ""
514     if var.playlist.length() > 0:
515         bot.send_msg(util.format_current_playing())
516     else:
517         reply = constants.strings('not_playing')
518     bot.send_msg(reply, text)
519
520
521 def cmd_skip(bot, user, text, command, parameter):
522     if var.playlist.length() > 0:
523         bot.stop()
524         bot.launch_music()
525         bot.async_download_next()
526     else:
527         bot.send_msg(constants.strings('queue_empty'), text)
528
529
530 def cmd_remove(bot, user, text, command, parameter):
531     # Allow to remove specific music into the queue with a number
532     if parameter and parameter.isdigit() and int(parameter) > 0 \
533             and int(parameter) <= var.playlist.length():
534
535         index = int(parameter) - 1
536
537         removed = None
538         if index == var.playlist.current_index:
539             removed = var.playlist.remove(index)
540
541             if index < len(var.playlist):
542                 if not bot.is_pause:
543                     bot.interrupt_playing()
544                     var.playlist.current_index -= 1
545                     # then the bot will move to next item
546
547             else: # if item deleted is the last item of the queue
548                 var.playlist.current_index -= 1
549                 if not bot.is_pause:
550                     bot.interrupt_playing()
551         else:
552             removed = var.playlist.remove(index)
553
554         # the Title isn't here if the music wasn't downloaded
555         bot.send_msg(constants.strings('removing_item',
556             item=removed['title'] if 'title' in removed else removed['url']), text)
557
558         logging.info("cmd: delete from playlist: " + str(removed['path'] if 'path' in removed else removed['url']))
559     else:
560         bot.send_msg(constants.strings('bad_parameter', command=command))
561
562
563 def cmd_list_file(bot, user, text, command, parameter):
564     folder_path = var.config.get('bot', 'music_folder')
565
566     files = util.get_recursive_filelist_sorted(folder_path)
567     msgs = [ "<br> <b>Files available:</b>" if not parameter else "<br> <b>Matched files:</b>" ]
568     try:
569         count = 0
570         for index, file in enumerate(files):
571             if parameter:
572                 match = re.search(parameter, file)
573                 if not match:
574                     continue
575
576             count += 1
577             msgs.append("<b>{:0>3d}</b> - {:s}".format(index, file))
578
579         if count != 0:
580             send_multi_lines(bot, msgs, text)
581         else:
582             bot.send_msg(constants.strings('no_file'), text)
583
584     except re.error as e:
585         msg = constants.strings('wrong_pattern', error=str(e))
586         bot.send_msg(msg, text)
587
588
589 def cmd_queue(bot, user, text, command, parameter):
590     if len(var.playlist) == 0:
591         msg = constants.strings('queue_empty')
592         bot.send_msg(msg, text)
593     else:
594         msgs = [ constants.strings('queue_contents')]
595         for i, value in enumerate(var.playlist):
596             newline = ''
597             if i == var.playlist.current_index:
598                 newline = '<b>{} ▶ ({}) {} ◀</b>'.format(i + 1, value['type'],
599                                                            value['title'] if 'title' in value else value['url'])
600             else:
601                 newline = '<b>{}</b> ({}) {}'.format(i + 1, value['type'],
602                                                      value['title'] if 'title' in value else value['url'])
603
604             msgs.append(newline)
605
606         send_multi_lines(bot, msgs, text)
607
608 def cmd_random(bot, user, text, command, parameter):
609     bot.interrupt_playing()
610     var.playlist.randomize()
611
612 def cmd_repeat(bot, user, text, command, parameter):
613     repeat = 1
614     if parameter and parameter.isdigit():
615         repeat = int(parameter)
616
617     music = var.playlist.current_item()
618     for _ in range(repeat):
619         var.playlist.insert(
620             var.playlist.current_index + 1,
621             music
622         )
623         logging.info("bot: add to playlist: " + util.format_debug_song_string(music))
624
625     bot.send_msg(constants.strings("repeat", song=util.format_song_string(music), n=str(repeat)), text)
626
627 def cmd_mode(bot, user, text, command, parameter):
628     if not parameter:
629         bot.send_msg(constants.strings("current_mode", mode=var.playlist.mode), text)
630         return
631     if not parameter in ["one-shot", "repeat", "random"]:
632         bot.send_msg(constants.strings('unknown_mode', mode=parameter), text)
633     else:
634         var.db.set('playlist', 'playback_mode', parameter)
635         var.playlist.set_mode(parameter)
636         logging.info("command: playback mode changed to %s." % parameter)
637         bot.send_msg(constants.strings("change_mode", mode=var.playlist.mode,
638                                        user=bot.mumble.users[text.actor]['name']), text)
639         if parameter == "random":
640             bot.stop()
641             var.playlist.randomize()
642             bot.launch_music(0)
643
644
645 def cmd_drop_database(bot, user, text, command, parameter):
646     var.db.drop_table()
647     var.db = Database(var.dbfile)
648     bot.send_msg(constants.strings('database_dropped'), text)