async def _delrole(message, command, silent=False): """Unassign a role from the user Usage: !delrole @user role_name""" if not await Util.check_args_count(message, command, silent, min=3): return user = command[1] role_name = ' '.join(command[2:]) role = discord.utils.get(message.guild.roles, name=role_name) if role is None: return null(await Msg.response(message, f"Role '{role_name}' does not exist", silent)) member = await message.guild.fetch_member(message.mentions[0].id) if member is None: return null(await Msg.response(message, f"User '{user}' does not exist", silent)) try: await member.remove_roles(role) except discord.HTTPException as e: return await null( Msg.response( message, f"Role '{role_name}' could not be assigned to user '{user}'. ERROR: '{e}'", silent)) await Msg.response( message, f"Successfully assigned role '{role_name}' to user '{user}'", silent)
async def print_yt_info(message, video_url, silent, full_description=False): """Print YT video info in embed""" r = const.YT_VIDEO_REGEX.match(video_url) if r is None: return null(await Msg.response(message, "Please, provide valid YT link", silent)) ydl_opts = {} try: with youtube_dl.YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(video_url, download=False) except Exception as e: return null(await Msg.response( message, f"ERROR: Getting YT video info failed: {e}", silent)) ud = info['upload_date'] e = DiscordEmbed() e.title(info['title']) e.title_url(info['webpage_url']) e.description(info['description'][:2048] if full_description else '') e.color(0xcc1818) e.thumbnail(info['thumbnail']) e.add_field("Views", str(info['view_count']), True) e.add_field("Likes", str(info['like_count']), True) e.add_field("Channel", f"[{info['uploader']}]({info['uploader_url']})", True) e.add_field( "Uploaded", f"{datetime.date(int(ud[0:4]), int(ud[4:6]), int(ud[6:8]))}", True) e.add_field("Duration", f"{datetime.timedelta(seconds=info['duration'])}", True) await Msg.response(message, None, silent, embed=e.get())
async def _quote(message, command, silent=False): """Print some quote from quotes database Examples: !quote !quote 1""" if not await Util.check_args_count(message, command, silent, min=1, max=2): return if not bc.config.quotes: return null(await Msg.response(message, "<Quotes database is empty>", silent)) if len(command) == 2: index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of quote", silent) if index is None: return else: index = random.choice(list(bc.config.quotes.keys())) if index not in bc.config.quotes.keys(): return null(await Msg.response(message, "Invalid index of quote!", silent)) quote = bc.config.quotes[index] e = DiscordEmbed() e.title("Quote") e.description(quote.message) e.color(random.randint(0x000000, 0xffffff)) e.timestamp(datetime.datetime.strptime(str(quote.timestamp), const.TIMESTAMP_DATETIME_FORMAT)) e.add_field("Index", str(index), True) e.add_field("Author", quote.author if quote.author else "<unknown>", True) e.add_field("Added by", quote.added_by, True) await Msg.response(message, None, silent, embed=e.get())
async def _getmarkovword(message, command, silent=False): """Get particular word from Markov model by regex Examples: !getmarkovword hello -a <- get amount of found words !getmarkovword hello 0 <- get word by index""" if not await Util.check_args_count( message, command, silent, min=3, max=3): return regex = command[1] try: found = bc.markov.find_words(regex) except re.error as e: return null(await Msg.response(message, f"Invalid regular expression: {e}", silent)) amount = len(found) if command[2] == '-a': result = str(amount) await Msg.response(message, result, silent) return result index = await Util.parse_int( message, command[2], f"Third parameter '{command[2]}' should be a valid index", silent) if index is None: return if not 0 <= index < amount: return null(await Msg.response( message, f"Wrong index in list '{command[2]}' (should be in range [0..{amount-1}])", silent)) result = found[index] await Msg.response(message, result, silent) return result
async def _eqstrs(message, command, silent=False): """Check if two strings separated by ';' are equal or not Example: !eqstrs a;b""" if not await Util.check_args_count(message, command, silent, min=2): return options = ' '.join(command[1:]).split(';') if len(options) < 2: return null(await Msg.response(message, "Too few options to compare", silent)) if len(options) > 2: return null(await Msg.response(message, "Too many options to compare", silent)) result = "true" if options[0] == options[1] else "false" await Msg.response(message, result, silent) return result
async def _vqpush(message, command, silent=False): """Push YT video or playlist to queue in voice channel Usage: !vqpush <youtube_url>""" if not await Util.check_args_count(message, command, silent, min=2): return for i in range(1, len(command)): yt_url = command[i] r = const.YT_VIDEO_REGEX.match( yt_url) or const.YT_PLAYLIST_REGEX.match(yt_url) if r is None: return null(await Msg.response(message, "🔊 Please, provide YT link", silent)) # Parse YT url parse_url = urllib.parse.urlparse(yt_url) params = urllib.parse.parse_qs(parse_url.query) if parse_url.path == '/playlist' and 'list' in params.keys( ) and params['list']: # Process YT playlist ydl_opts = { 'dump_single_json': True, 'extract_flat': True, } try: with youtube_dl.YoutubeDL(ydl_opts) as ydl: yt_playlist_data = ydl.extract_info(yt_url, download=False) except Exception as e: return null(await Msg.response( message, f"🔊 ERROR: Fetching YT playlist data failed: {e}", silent)) yt_video_ids = [ entry["id"] for entry in yt_playlist_data["entries"] ] download_promises = [] for yt_video_id in yt_video_ids: download_promises.append( _VoiceInternals.push_video( message, f"https://youtu.be/{yt_video_id}", silent)) await asyncio.gather(*download_promises) await Msg.response( message, f"Downloading playlist '{params['list'][0]}' is finished", silent) else: # Process YT video await _VoiceInternals.push_video(message, yt_url, silent)
async def _findmarkov(message, command, silent=False): """Match words in Markov model using regex Examples: !findmarkov hello !findmarkov hello -f""" if not await Util.check_args_count( message, command, silent, min=2, max=3): return regex = command[1] try: found = bc.markov.find_words(regex) except re.error as e: return null(await Msg.response(message, f"Invalid regular expression: {e}", silent)) amount = len(found) if not (len(command) > 2 and command[2] == '-f' and bc.config.users[message.author.id].permission_level >= const.Permission.MOD.value): found = found[:100] await Msg.response( message, f"Found {amount} words in model: {found}" f"{f' and {amount - len(found)} more...' if amount - len(found) > 0 else ''}", silent, suppress_embeds=True)
async def _setreminderchannel(message, command, silent=False): """Set channel where reminder will be sent Example: !setreminderchannel 1 <channel_id>""" if not await Util.check_args_count( message, command, silent, min=3, max=3): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) rem = bc.config.reminders[index] channel_id = await Util.parse_int( message, command[2], f"Third parameter for '{command[0]}' should be channel id", silent) if channel_id is None: return rem.channel_id = channel_id await Msg.response( message, f"Set channel id {channel_id} for reminder {index}", silent)
async def _updresponse(message, command, silent=False): """Update bot response Example: !updresponse index regex;text""" if not await Util.check_args_count(message, command, silent, min=3): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should an index (integer)", silent) if index is None: return if index in bc.config.responses.keys(): parts = ' '.join(command[2:]).split(';', 1) if len(parts) < 2: return null(await Msg.response( message, "You need to provide regex and text that are separated by semicolon (;)", silent)) regex, text = parts bc.config.responses[index] = Response(regex, text) await Msg.response( message, f"Response '{text}' on '{regex}' successfully updated", silent) else: await Msg.response(message, "Incorrect index of response!", silent)
async def _vq(message, command, silent=False): """List voice channel queue Usage: !vq""" if not await Util.check_args_count( message, command, silent, min=1, max=1): return if not bc.voice_client_queue: e = DiscordEmbed() e.title("🔊 Voice queue 🔊") e.color(0xcc1818) e.description("<empty>") return null(await Msg.response(message, None, silent, embed=e.get())) voice_client_queue = list(bc.voice_client_queue) pos = 0 for voice_queue_chunk in Msg.split_by_chunks( voice_client_queue, const.DISCORD_MAX_EMBED_FILEDS_COUNT): e = DiscordEmbed() e.title("🔊 Voice queue 🔊") e.color(0xcc1818) for entry in voice_queue_chunk: pos += 1 e.add_field( f"{entry.title}", f"Position {pos} (YT: {entry.id}) requested by {entry.requested_by}" ) await Msg.response(message, None, silent, embed=e.get())
async def _autostartplugin(message, command, silent=False): """Check if plugin automatically starts when bot loads up and set autostart flag for plugin Usage: !autostartplugin <plugin_name> <- check if autostart is enabled !autostartplugin <plugin_name> enable <- enable autostart for plugin !autostartplugin <plugin_name> disable <- disable autostart for plugin""" if not await Util.check_args_count(message, command, silent, min=2, max=3): return plugin_name = command[1] if len(command) == 2: if plugin_name not in bc.config.plugins.keys(): return null(await Msg.response(message, f"Could not find plugin '{plugin_name}'", silent)) is_enabled = bc.config.plugins[plugin_name]["autostart"] await Msg.response( message, f"Autostart for plugin '{plugin_name}' is '{'enabled' if is_enabled else 'disabled'}'", silent) else: # len == 3 cmd = command[2] if cmd == "enable": bc.config.plugins[plugin_name]["autostart"] = True await Msg.response(message, f"Autostart for plugin '{plugin_name}' has been enabled", silent) elif cmd == "disable": bc.config.plugins[plugin_name]["autostart"] = False await Msg.response(message, f"Autostart for plugin '{plugin_name}' has been disabled", silent) else: await Msg.response(message, f"Unknown subcommand '{cmd}' for plugin autostart manipulation", silent)
async def _timeuntilreminder(message, command, silent=False): """Show time until particular reminder Example: !timeuntilreminder 1""" if not await Util.check_args_count( message, command, silent, min=2, max=2): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) rem = bc.config.reminders[index] rem_time = datetime.datetime.strptime( rem.time, const.REMINDER_DATETIME_FORMAT) - datetime.datetime.now() if rem_time < datetime.timedelta(days=1): rem_time = "0 days, " + str(rem_time) result = f"Time until reminder {index} ('{rem.message}') is {rem_time}" await Msg.response(message, result, silent) return result
async def _unloadplugin(message, command, silent=False): """Unload plugin by its name Usage: !unloadplugin <plugin_name>""" if not await Util.check_args_count(message, command, silent, min=2, max=2): return plugin_name = command[1] if plugin_name not in bc.plugin_manager.get_plugins_list(): return null(await Msg.response(message, f"Could not find plugin '{plugin_name}'", silent)) await bc.plugin_manager.send_command(plugin_name, "close") await Msg.response(message, f"Plugin '{plugin_name}' has been unloaded", silent)
async def _setprereminders(message, command, silent=False): """Set pre reminders notifying that reminder will be sent in particular time. For example, send pre reminder 10 minutes before actual event (to prepare or something) Usage: !setprereminders <reminder_id> [<time_before_reminder_in_minutes> ...] Examples: !setprereminders 1 10 !setprereminders 2 5 10 15""" if not await Util.check_args_count(message, command, silent, min=2): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) rem = bc.config.reminders[index] prereminders_list = [] for i in range(2, len(command)): time_before_reminder = await Util.parse_int( message, command[i], f"Parameter #{i} for '{command[0]}' should be time in minutes", silent) if time_before_reminder is None: return prereminders_list.append(time_before_reminder) if time_before_reminder <= 0: return null(await Msg.response( message, "Pre reminder time should be more than 0 minutes", silent)) if time_before_reminder > 24 * 60: return null(await Msg.response( message, "Pre reminder time should be less than 1 day", silent)) rem.prereminders_list = prereminders_list rem.used_prereminders_list = [False] * len(prereminders_list) result = f"Set prereminders list for reminder {index}: {', '.join([str(x) for x in rem.prereminders_list])}" await Msg.response(message, result, silent)
async def _delmarkov(message, command, silent=False): """Delete all words in Markov model by regex Example: !delmarkov hello""" if not await Util.check_args_count(message, command, silent, min=2): return regex = ' '.join(command[1:]) try: removed = bc.markov.del_words(regex) except re.error as e: return null(await Msg.response(message, f"Invalid regular expression: {e}", silent)) await Msg.response( message, f"Deleted {len(removed)} words from model: {removed}", silent, suppress_embeds=True)
async def _stoptimer(message, command, silent=False): """Stop timer Usage: !stoptimer 1""" if not await Util.check_args_count(message, command, silent, min=2, max=2): return id_ = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an integer", silent) if id_ is None: return if id_ not in bc.timers.keys(): return null(await Msg.response(message, f"⏰ Unknown timer id: {id_}", silent)) bc.timers[id_] = False await Msg.response(message, f"⏰ Timer #{id_} is stopped!", silent)
async def _updreminder(message, command, silent=False): """Update reminder by index Examples: !updreminder 0 2020-01-01 00:00 Happy new year! !updreminder 0 2020-01-01 00:00 Happy new year! !updreminder 0 today 08:00 Wake up !updreminder 0 tomorrow 08:00 Wake up !updreminder 0 monday 09:00 Time to work !updreminder 0 sat 11:00 Time to chill !updreminder 0 2d 08:00 Wake up <- 2 days !updreminder 0 1w 08:00 Wake up <- 1 week !addreminder 0 1m Monthly event !addreminder 0 1y Annual event !updreminder 0 in 1w5d10h5m Test reminder !updreminder 0 in 1w Test reminder 2 !updreminder 0 in 5h10m Test reminder 3 """ if not await Util.check_args_count(message, command, silent, min=5): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index in bc.config.reminders.keys(): text = ' '.join(command[4:]) if command[2] == "in": time = await _ReminderInternals.parse_reminder_args_in( message, command[3], silent) else: time = await _ReminderInternals.parse_reminder_args( message, command[2], command[3], silent) if time is None: return if datetime.datetime.strptime( str(time), const.REMINDER_DATETIME_FORMAT) < datetime.datetime.now(): return null(await Msg.response( message, "Reminder timestamp is earlier than now", silent)) bc.config.reminders[index] = Reminder( str(time), text, message.channel.id, bc.config.reminders[index].author, datetime.datetime.now().strftime( const.REMINDER_DATETIME_FORMAT)) await Msg.response( message, f"Successfully updated reminder {index}: '{text}' at {time}", silent) else: await Msg.response(message, "Invalid index of reminder!", silent)
async def _skipreminder(message, command, silent=False): """Skip next instance of recurring (repeating) reminder Example: !skipreminder 1 Note: only recurring (repeating) reminders are affected by this command""" if not await Util.check_args_count( message, command, silent, min=2, max=2): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) if bc.config.reminders[index].repeat_after == 0: return null(await Msg.response(message, "This reminder is not recurring!", silent)) rem = bc.config.reminders[index] new_time = datetime.datetime.strftime( datetime.datetime.strptime(rem.time, const.REMINDER_DATETIME_FORMAT) + rem.get_next_event_delta(), const.REMINDER_DATETIME_FORMAT) id_ = bc.config.ids["reminder"] bc.config.reminders[id_] = Reminder( str(new_time), rem.message, message.channel.id, bc.config.reminders[index].author, datetime.datetime.now().strftime(const.REMINDER_DATETIME_FORMAT)) bc.config.reminders[id_].repeat_after = rem.repeat_after bc.config.ids["reminder"] += 1 bc.config.reminders.pop(index) await Msg.response( message, f"Skipped reminder {index} at {rem.time}, " f"next reminder {id_} will be at {bc.config.reminders[id_].time}", silent)
async def _timer(message, command, silent=False): """Set timer Usage: !timer 10""" if not await Util.check_args_count(message, command, silent, min=2, max=2): return start = datetime.datetime.now() duration = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be duration in seconds", silent) if duration is None: return if duration < 0: return null(await Msg.response(message, "Timer duration should be more than 0 seconds", silent)) if duration > const.MAX_TIMER_DURATION_IN_SECONDS: return null(await Msg.response(message, "Timer duration should be less than 24 hours", silent)) finish = datetime.datetime.now() + datetime.timedelta(seconds=duration) id_ = bc.config.ids["timer"] bc.config.ids["timer"] += 1 timer_msg = await Msg.response(message, f"⏰ Timer #{id_}: {finish - start}", silent) bc.do_not_update[DoNotUpdateFlag.TIMER] += 1 bc.timers[id_] = True print_counter = 0 while True: current = datetime.datetime.now() if not bc.timers[id_]: await timer_msg.edit(content=f"⏰ Timer #{id_}: {finish - current}! (stopped)") bc.do_not_update[DoNotUpdateFlag.TIMER] -= 1 break if current >= finish: await timer_msg.edit(content=f"⏰ Timer #{id_}: 0:00:00.000000!") await Msg.response(message, f"⏰ Timer #{id_}: Time is up!", silent) bc.do_not_update[DoNotUpdateFlag.TIMER] -= 1 break print_counter += 1 if print_counter >= 10 * bc.do_not_update[DoNotUpdateFlag.TIMER]: await timer_msg.edit(content=f"⏰ Timer #{id_}: {finish - current}") print_counter = 0 await asyncio.sleep(0.1)
async def parse_reminder_args_in(message, time, silent): r = const.REMINDER_IN_REGEX.match(time) if r is None: return null(await Msg.response( message, ("Provide relative time in the following format: " "<weeks>w<days>d<hours>h<minutes>m. " "All parts except one are optional"), silent)) weeks = int(r.group(2)) if r.group(2) is not None else 0 days = int(r.group(4)) if r.group(4) is not None else 0 hours = int(r.group(6)) if r.group(6) is not None else 0 minutes = int(r.group(8)) if r.group(8) is not None else 0 time = (datetime.datetime.now() + datetime.timedelta( weeks=weeks, days=days, hours=hours, minutes=minutes)).strftime( const.REMINDER_DATETIME_FORMAT) return time
async def _calc(message, command, silent=False): """Calculate mathematical expression Examples: !calc 2+2*2 !calc 4/2-1""" if not await Util.check_args_count(message, command, silent, min=2): return expr = ' '.join(command[1:]) try: result = str(MathExprEvaluator().evaluate(expr)) except Exception as e: return null(await Msg.response(message, f"Expression evaluation failed: {e}", silent)) await Msg.response(message, result, silent) return result
async def _addresponse(message, command, silent=False): """Add bot response on message that contains particular regex Example: !addresponse regex;text""" if not await Util.check_args_count(message, command, silent, min=2): return parts = ' '.join(command[1:]).split(';', 1) if len(parts) < 2: return null(await Msg.response( message, "You need to provide regex and text that are separated by semicolon (;)", silent)) regex, text = parts bc.config.responses[bc.config.ids["response"]] = Response(regex, text) bc.config.ids["response"] += 1 await Msg.response( message, f"Response '{text}' on '{regex}' successfully added", silent)
async def _addreminder(message, command, silent=False): """Print message at particular time Examples: !addreminder 2020-01-01 00:00 Happy new year! !addreminder today 08:00 Wake up !addreminder tomorrow 08:00 Wake up !addreminder monday 09:00 Time to work !addreminder sat 11:00 Time to chill !addreminder 2d 08:00 Wake up <- 2 days !addreminder 1w 08:00 Wake up <- 1 week !addreminder 1m Monthly event !addreminder 1y Annual event !addreminder in 1w5d10h5m Test reminder !addreminder in 1w Test reminder 2 !addreminder in 5h10m Test reminder 3 """ if not await Util.check_args_count(message, command, silent, min=4): return text = ' '.join(command[3:]) if command[1] == "in": time = await _ReminderInternals.parse_reminder_args_in( message, command[2], silent) else: time = await _ReminderInternals.parse_reminder_args( message, command[1], command[2], silent) if time is None: return id_ = bc.config.ids["reminder"] if datetime.datetime.strptime( str(time), const.REMINDER_DATETIME_FORMAT) < datetime.datetime.now(): return null(await Msg.response(message, "Reminder timestamp is earlier than now", silent)) bc.config.reminders[id_] = Reminder( str(time), text, message.channel.id, message.author.name, datetime.datetime.now().strftime(const.REMINDER_DATETIME_FORMAT)) bc.config.ids["reminder"] += 1 await Msg.response(message, f"Reminder '{text}' with id {id_} added at {time}", silent)
async def _addremindernotes(message, command, silent=False): """Add reminder notes Example: !addremindernotes 1 Some text""" if not await Util.check_args_count(message, command, silent, min=3): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) rem = bc.config.reminders[index] rem.notes = ' '.join(command[2:]) await Msg.response(message, f"Set notes for reminder {index}: {rem.notes}", silent)
async def push_video(message, yt_video_url, silent): """Push video by its URL to voice queue""" r = const.YT_VIDEO_REGEX.match(yt_video_url) if r is None: return yt_video_id = r.groups()[0] output_file_name = f'{Util.tmp_dir()}/yt_{yt_video_id}.mp3' ydl_opts = { 'format': 'bestaudio/best', 'outtmpl': output_file_name, 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', }], } try: with youtube_dl.YoutubeDL(ydl_opts) as ydl: video_info = ydl.extract_info(yt_video_url, download=False) if not os.path.exists(output_file_name): log.debug(f"Downloading YT video {yt_video_url} ...") loop = asyncio.get_event_loop() await loop.run_in_executor(None, ydl.download, [yt_video_url]) log.debug(f"Downloaded {yt_video_url}") else: log.debug(f"Found in cache: {yt_video_url}") except Exception as e: return null(await Msg.response(message, f"🔊 ERROR: Downloading failed: {e}", silent)) bc.voice_client_queue.append( VoiceQueueEntry(message.channel, video_info['title'], video_info['id'], output_file_name, message.author.name)) await Msg.response( message, f"🔊 Added {video_info['title']} (YT: {video_info['id']}) to the queue " f"at position #{len(bc.voice_client_queue)}", silent)
async def _reminder(message, command, silent=False): """Print information about reminder Example: !reminder 1""" if not await Util.check_args_count( message, command, silent, min=2, max=2): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) reminder = bc.config.reminders[index] e = DiscordEmbed() e.title("Reminder info") e.description(reminder.message) e.footer( f"{reminder.author} • {datetime.datetime.strptime(reminder.time, const.REMINDER_DATETIME_FORMAT)}" ) e.add_field("Index", str(index), True) e.add_field("Channel", f"<#{reminder.channel_id}>", True) if reminder.repeat_after: e.add_field( "Repeats every", f"{reminder.repeat_after} {reminder.repeat_interval_measure}", True) e.add_field("Created", reminder.time_created, True) if reminder.prereminders_list: e.add_field( "Pre reminders (in minutes)", ', '.join([str(x) for x in reminder.prereminders_list]), True) if reminder.notes: e.add_field("Notes", reminder.notes, True) await Msg.response(message, None, silent, embed=e.get())
async def _vjoin(message, command, silent=False): """Join voice channel Usage: !vjoin <voice_channel_id>""" if not await Util.check_args_count( message, command, silent, min=2, max=2): return voice_channel_id = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an id of voice channel", silent) if voice_channel_id is None: return voice_channels = message.guild.voice_channels for v in voice_channels: if v.id == voice_channel_id: if bc.voice_client is not None: log.debug("Disconnecting from previous voice channel...") await bc.voice_client.disconnect() log.debug("Disconnected from previous voice channel") bc.voice_client = None log.debug( f"Connecting to the voice channel {voice_channel_id}...") try: bc.voice_client = await v.connect() except Exception as e: return null(await Msg.response(message, f"ERROR: Failed to connect: {e}", silent)) log.debug("Connected to the voice channel") break else: await Msg.response( message, f"🔊 Could not find voice channel with id {voice_channel_id}", silent)
async def _if(message, command, silent=False): """If expression is true (!= 0) then return first expression otherwise return the second one Examples: !if 1 It's true;It's false -> It's true !if 0 It's true;It's false -> It's false """ if not await Util.check_args_count(message, command, silent, min=3): return condition = command[1] true = ["true"] false = ["false"] if condition.lower() not in true + false: condition = await Util.parse_int( message, command[1], f"Second parameter should be either number or {', '.join(true + false)}", silent) if condition is None: return else: # Handle keywords that can be used in conditions if condition.lower() in true: condition = 1 elif condition.lower() in false: condition = 0 expressions = ' '.join(command[2:]).split(';') if len(expressions) != 2: return null(await Msg.response( message, f"There should be only 2 branches ('then' and 'else') " f"separated by ';' in '{command[0]}' command", silent)) result = expressions[0] if condition != 0 else expressions[1] await Msg.response(message, result, silent) return result
async def _repeatreminder(message, command, silent=False): """Make reminder repeating with particular period Examples: !repeatreminder 1 1 !repeatreminder 1 hourly !repeatreminder 1 daily !repeatreminder 1 weekly !repeatreminder 1 monthly !repeatreminder 1 annually !repeatreminder 1 2h !repeatreminder 1 2d !repeatreminder 1 2w !repeatreminder 1 2m !repeatreminder 1 2y !repeatreminder 1 0 Note: number without postfix is translated to minutes. 0 means disabling repetition""" if not await Util.check_args_count( message, command, silent, min=3, max=3): return index = await Util.parse_int( message, command[1], f"Second parameter for '{command[0]}' should be an index of reminder", silent) if index is None: return if index not in bc.config.reminders.keys(): return null(await Msg.response(message, "Invalid index of reminder!", silent)) if command[2] == "hourly": command[2] = "1h" elif command[2] == "daily": command[2] = "1d" elif command[2] == "weekly": command[2] = "1w" elif command[2] == "monthly": command[2] = "1m" elif command[2] == "annually": command[2] = "1y" if command[2].endswith("h"): duration = command[2][:-1] duration = await Util.parse_int( message, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days", silent) if duration is None: return duration *= 60 elif command[2].endswith("d"): duration = command[2][:-1] duration = await Util.parse_int( message, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days", silent) if duration is None: return duration *= 1440 elif command[2].endswith("w"): duration = command[2][:-1] duration = await Util.parse_int( message, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days", silent) if duration is None: return duration *= 10080 elif command[2].endswith("m"): duration = command[2][:-1] duration = await Util.parse_int( message, duration, "You need to specify amount of days before 'm'. Example: 3m for 3 months", silent) if duration is None: return bc.config.reminders[index].repeat_interval_measure = "months" elif command[2].endswith("y"): duration = command[2][:-1] duration = await Util.parse_int( message, duration, "You need to specify amount of days before 'y'. Example: 3y for 3 years", silent) if duration is None: return bc.config.reminders[index].repeat_interval_measure = "years" else: duration = await Util.parse_int( message, command[2], f"Third parameter for '{command[0]}' should be duration of period between reminders", silent) if duration is None: return if duration < 0: return null(await Msg.response( message, "Duration should be positive or zero (to disable repetition)!", silent)) bc.config.reminders[index].repeat_after = duration if duration == 0: return null(await Msg.response( message, f"Repetition is disabled for reminder {index}", silent)) await Msg.response( message, f"Reminder {index} will be repeated every {duration} " f"{bc.config.reminders[index].repeat_interval_measure}!", silent)
async def parse_reminder_args(message, date, time, silent): WEEK_DAYS_FULL = ("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday") WEEK_DAYS_ABBREV = ("mon", "tue", "wed", "thu", "fri", "sat", "sun") if date == "today": date = datetime.datetime.strftime(datetime.datetime.now(), const.REMINDER_DATE_FORMAT) elif date == "tomorrow": date = datetime.datetime.strftime( datetime.datetime.now() + datetime.timedelta(days=1), const.REMINDER_DATE_FORMAT) elif date.lower() in WEEK_DAYS_FULL or date in WEEK_DAYS_ABBREV: if date.lower() in WEEK_DAYS_FULL: weekday = WEEK_DAYS_FULL.index(date.lower()) elif date in WEEK_DAYS_ABBREV: weekday = WEEK_DAYS_ABBREV.index(date.lower()) else: return null(await Msg.response( message, "Unexpected error during day of week processing!", silent)) days_delta = (weekday - datetime.datetime.today().weekday() + 7) % 7 if days_delta == 0: days_delta = 7 date = datetime.datetime.strftime( datetime.datetime.now() + datetime.timedelta(days=days_delta), const.REMINDER_DATE_FORMAT) elif date.endswith("d"): days_amount = date[:-1] days_amount = await Util.parse_int( message, days_amount, "You need to specify amount of days before 'd'. Example: 3d for 3 days", silent) if days_amount is None: return date = datetime.datetime.strftime( datetime.datetime.now() + datetime.timedelta(days=days_amount), const.REMINDER_DATE_FORMAT) elif date.endswith("d"): days_amount = date[:-1] days_amount = await Util.parse_int( message, days_amount, "You need to specify amount of days before 'd'. Example: 3d for 3 days", silent) if days_amount is None: return date = datetime.datetime.strftime( datetime.datetime.now() + datetime.timedelta(days=days_amount), const.REMINDER_DATE_FORMAT) elif date.endswith("w"): weeks_amount = date[:-1] weeks_amount = await Util.parse_int( message, weeks_amount, "You need to specify amount of weeks before 'w'. Example: 2w for 2 weeks", silent) if weeks_amount is None: return date = datetime.datetime.strftime( datetime.datetime.now() + datetime.timedelta(days=weeks_amount * 7), const.REMINDER_DATE_FORMAT) elif date.endswith("m"): months_amount = date[:-1] months_amount = await Util.parse_int( message, months_amount, "You need to specify amount of months before 'm'. Example: 3m for 3 months", silent) if months_amount is None: return date = datetime.datetime.strftime( datetime.datetime.now() + dateutil.relativedelta.relativedelta(months=months_amount), const.REMINDER_DATE_FORMAT) elif date.endswith("y"): years_amount = date[:-1] years_amount = await Util.parse_int( message, years_amount, "You need to specify amount of years before 'y'. Example: 3y for 3 years", silent) if years_amount is None: return date = datetime.datetime.strftime( datetime.datetime.now() + dateutil.relativedelta.relativedelta(years=years_amount), const.REMINDER_DATE_FORMAT) time = date + ' ' + time try: time = datetime.datetime.strptime( time, const.REMINDER_DATETIME_FORMAT).strftime( const.REMINDER_DATETIME_FORMAT) except ValueError: return null(await Msg.response( message, f"{time} does not match format {const.REMINDER_DATETIME_FORMAT}\n" "More information about format: <https://strftime.org/>", silent)) return time