def add_manuals(bot): """Reads all manuals in the config folder and adds them to the bot.""" manual_order = [] directory = bot.path + '/config/' for plugin in bot.plugins: try: with open(directory + plugin + '-manual.json', 'r') as manual_file: loaded_manual = (plugin, json.load(manual_file)) if 'entries' not in loaded_manual[1]: raise BotException( EXCEPTION, "The manual for plugin {} has no entries.".format(plugin)) if 'order' not in loaded_manual[1]: raise BotException( EXCEPTION, "The manual for plugin {} has no order.".format(plugin)) for entry in loaded_manual[1]['order']: if entry not in loaded_manual[1]['entries']: raise BotException( EXCEPTION, "The manual for plugin {0} is missing " "the entry {1}.".format(plugin, entry)) if plugin == 'base': bot.manuals.append(loaded_manual) else: manual_order.append(loaded_manual) except FileNotFoundError: if plugin == 'base': raise BotException(EXCEPTION, "The base manual was not found.", error_type=ErrorTypes.STARTUP) else: pass bot.manuals += sorted(manual_order)
def add_configurations(bot): configurations_list = {} directory = bot.path + '/config/' try: with open(directory + 'config.json', 'r') as config_file: configurations_list['core'] = json.load(config_file) except Exception as e: raise BotException(EXCEPTION, "Could not open the core configuration file", e=e, error_type=ErrorTypes.STARTUP) for plugin in bot.plugins: try: with open(directory + plugin + '.json', 'r') as config_file: configurations_list[plugin] = json.load(config_file) except FileNotFoundError: module = bot.plugins[plugin][0] if (getattr(module, 'uses_configuration', False) and module.uses_configuration): raise BotException(EXCEPTION, "Plugin {} requires a configuration file, " "but it was not found.".format(plugin), error_type=ErrorTypes.STARTUP) except Exception as e: raise BotException( EXCEPTION, "Could not open the {} configuration file.".format(plugin), e=e, error_type=ErrorTypes.STARTUP) bot.configurations = configurations_list return configurations_list
def get_channel(bot, identity, server=None, attribute=None, safe=False, strict=False): """Like get_member(), but gets the channel instead.""" if identity.startswith('<#') and identity.endswith('>'): identity = identity.strip('<#>') if server is None: channels = bot.get_all_channels() else: channels = server.channels result = discord.utils.get(channels, id=identity) if result is None: result = discord.utils.get(channels, name=identity) if result: if attribute: if hasattr(result, attribute): return getattr(result, attribute) elif safe: return None else: raise BotException( EXCEPTION, "Invalid attribute, '{}'.".format(attribute)) else: return result else: if safe: return None else: raise BotException(EXCEPTION, "{} not found.".format(identity))
def guess_index(bot, text, safe=True): """Guesses the closest command and returns the base and index.""" if not text: if safe: return (None, -1) else: raise BotException(EXCEPTION, "No guess text.") text = text.strip() split_content = text.split(' ', 1) if len(split_content) == 1: split_content.append('') base, parameters = split_content base = base.lower() try: command = bot.commands[base] except KeyError: if safe: return (None, -1) else: raise BotException(EXCEPTION, "Invalid base.") parameters, quoted_indices = split_parameters(parameters, quote_list=True) return (base, match_blueprint(bot, base, parameters, quoted_indices, command, find_index=True))
def get_configurations(bot): configurations_list = {} directory = bot.path + '/config' try: with open(directory + '/config.json', 'r') as config_file: configurations_list['core'] = json.load(config_file) except Exception as e: raise BotException(ErrorTypes.STARTUP, EXCEPTION, "Could not open the core configuration file", e=e) directory += '/' for plugin in bot.plugins: try: with open(directory + plugin + '.json', 'r') as config_file: configurations_list[plugin] = json.load(config_file) except FileNotFoundError: if (getattr(bot.plugins[plugin][0], 'uses_configuration', False) and bot.plugins[plugin][0].uses_configuration): raise BotException( ErrorTypes.STARTUP, EXCEPTION, "Plugin " + plugin + " requires a configuration file, " "but it was not found.") except Exception as e: raise BotException(ErrorTypes.STARTUP, EXCEPTION, "Could not open the " + plugin + " configuration file.", e=e) return configurations_list
def fill_shortcut(bot, shortcut, base, parameters, server=None): """ Replaces elements in the syntax using the template with the parameters. Example: (<('create {} {}', ':^')>, 'tag', '"my tag" tag text']) Returns: 'create "my tag" tag text' """ parameters = split_parameters(parameters, include_quotes=True) parameters_length = len(parameters) base_index = shortcut.bases.index(base) syntax, template = shortcut.format_pairs[base_index] if not template: if parameters: raise BotException( EXCEPTION, "Shortcut requires no arguments, but some were given.", commands.usage_reminder(bot, base, index=base_index, shortcut=True, server=server)) return syntax try: current = 0 to_add = [] for argument_type in template: while (current < parameters_length and parameters[current].isspace()): current += 1 if argument_type == ':': to_add.append(parameters[current]) elif argument_type in ('&', '#'): to_add.append(''.join(parameters[current:])) elif argument_type in ('^', '+'): if len(parameters[current:]) == 1 and argument_type == '^': combined = parameters[current] else: combined = ''.join(parameters[current:]) assert combined to_add.append(combined) current += 1 syntax = syntax.format(*to_add) except: reminder = commands.usage_reminder(bot, base, index=base_index, shortcut=True, server=server) raise BotException(EXCEPTION, "Invalid shortcut syntax.", reminder) return syntax
async def join_and_ready(bot, voice_channel, include_player=False, is_mod=False, reconnect=False): """Joins the voice channel and stops any player if it exists. Returns the voice_client object from bot.join_voice_channel. If include_player is True, this will return a tuple of both the voice client and the player (None if not found). """ server = voice_channel.server muted = voice_channel.id in data.get(bot, 'base', 'muted_channels', server_id=server.id, default=[]) if voice_channel == server.afk_channel: raise BotException(EXCEPTION, "This is the AFK channel.") if muted and not is_mod: raise BotException(EXCEPTION, "The bot is muted in this voice channel.") if reconnect: await leave_and_stop(bot, server) if not bot.is_voice_connected(server): try: voice_client = await bot.join_voice_channel(voice_channel) except Exception as e: raise BotException(EXCEPTION, "Failed to join the voice channel.", e=e) else: voice_client = bot.voice_client_in(server) if voice_client.channel != voice_channel: try: await voice_client.move_to(voice_channel) except Exception as e: raise BotException(EXCEPTION, "Failed to move to the voice channel.", e=e) player = get_player(bot, server.id) if player is not None: if player.is_playing(): player.stop() elif not player.is_done(): # Can this even happen? raise BotException(EXCEPTION, "Audio is pending, please try again later.") if include_player: return (voice_client, player) else: return voice_client
def add_plugins(bot): """ Gets a list of all of the plugins and stores them as a key/value pair of the plugin name and the module itself (renamed to plugin for the user). In addition, this also sets the commands given by each plugin. """ directory = '{}/plugins'.format(bot.path) data_directory = '{}/plugins/plugin_data'.format(bot.path) if os.path.isdir(data_directory): logging.debug("Setting plugin_data as plugin import path.") sys.path.append(data_directory) try: plugins_list = os.listdir(directory) except FileNotFoundError: raise BotException(EXCEPTION, "Plugins directory not found", error_type=ErrorTypes.STARTUP) valid_plugins = {} # Add base plugin from jshbot import base plugin_commands = base.get_commands() commands.add_commands(bot, plugin_commands, base) valid_plugins['base'] = [base, plugin_commands] # Get plugin commands for plugin in plugins_list: if (plugin[0] in ('.', '_') or plugin == 'base' or not plugin.endswith('.py')): continue try: spec = importlib.util.spec_from_file_location( plugin, '{}/{}'.format(directory, plugin)) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) plugin_commands = module.get_commands() commands.add_commands(bot, plugin_commands, module) except Exception as e: traceback.print_exc() raise BotException(EXCEPTION, "Failed to import external plugin", plugin, e=e, error_type=ErrorTypes.STARTUP) else: logging.debug("Adding plugin {}".format(plugin)) valid_plugins[plugin] = [module, plugin_commands] if len(valid_plugins): logging.debug("Loaded {} plugin(s)".format(len(valid_plugins))) bot.plugins = valid_plugins
def fill_shortcut(bot, parameters, blueprint, modifiers): ''' Replaces elements in the blueprint with the modifiers specified. Example: fill_shortcut('"my tag" tag text', 'tag -create {} {}', ':^') Returns: ('tag', '-create "my tag" tag text') ''' # Split parameters split = re.split('( +)', parameters) split.append('-') it = 0 format_list = [] for modifier in modifiers: # :^&+# if modifier == ':': # Insert single argument block, it = get_argument_block(split, it, get_all=True) if not block or block == '-': it = -1 # Set error break format_list.append('"' + block + '"') else: # Insert remaining trailing arguments remaining = '' while it < len(split) - 1: if modifier in ['^', '&']: # Single remaining += split[it] else: # Split block, it = get_argument_block(split, it, get_all=True) remaining += '"' + block + '" ' it += 1 format_list.append(remaining.strip()) it += 1 if it < len(split) - 1: base = blueprint.split(' ', 1)[0] raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "Invalid syntax.", bot.usage_reminder(base)) # Check for modifiers length mismatch if len(modifiers) == 0 and len(split) - 2: raise BotException( ErrorTypes.RECOVERABLE, EXCEPTION, "Shortcut requires no arguments, but some were given.") # Insert elements from the format list filled_blueprint = blueprint.format(*format_list) blueprint_split = filled_blueprint.split(' ', 1) if len(blueprint_split) == 1: blueprint_split.append('') return blueprint_split
def list_data_remove(bot, plugin_name, key, value=None, server_id=None, channel_id=None, user_id=None, default=None, safe=False, volatile=False): """Remove data from list at location. Works like remove, but manipulates the list at the location. If the value is not specified, it will pop the first element. """ current, location_key = get_location(bot, server_id, channel_id, user_id, volatile) if (not current or plugin_name not in current or key not in current[plugin_name]): if safe: return default else: raise BotException(EXCEPTION, "Key '{}' not found.".format(key)) current = current[plugin_name][key] if type(current) is not list: if safe: return default else: raise BotException(EXCEPTION, "Data is not a list.") elif not current: # Empty, can't pop if safe: return default else: raise BotException(EXCEPTION, "List is empty.") if not volatile and location_key not in bot.data_changed: bot.data_changed.append(location_key) if value is None: return current.pop() else: # Pop value if value not in current: if safe: return default else: raise BotException( EXCEPTION, "Value '{}' not found in list.".format(value)) else: current.remove(value) return value
def get_random_line(bot, name): ''' Gets a random line in the file given by the name argument. ''' file_path = bot.path + '/data/simple_bot_manager.py/' + name try: if os.stat(file_path).st_size > 0: with open(file_path, 'r') as data_file: return str(random.choice(list(data_file))).rstrip() else: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "The {} file is empty.".format(name)) except: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "The {} file was not found.".format(name))
def delete_temporary_file(bot, filename, safe=True): """Deletes the given file from the temp folder.""" try: os.remove('{0}/temp/{1}'.format(bot.path, filename)) except Exception as e: if not safe: raise BotException(EXCEPTION, "File could not be deleted.", e=e)
def remove(bot, plugin_name, key, server_id=None, channel_id=None, user_id=None, default=None, safe=False, volatile=False): """Removes the given key from the specified location. If the key does not exist and the safe flag is not set, this will throw an exception. If the safe flag is set, it will return default. Otherwise, this will return the found value, and remove it from the dictionary. If the key is None, it removes all of the data associated with that plugin for the given location. Use with caution. """ current, location_key = get_location(bot, server_id, channel_id, user_id, volatile) if (not current or plugin_name not in current or key not in current[plugin_name]): if safe: return default else: raise BotException(EXCEPTION, "Key '{}' not found.".format(key)) if not volatile and location_key not in bot.data_changed: bot.data_changed.append(location_key) elif key: return current[plugin_name].pop(key) else: # Remove all data associated with that plugin for the given location return current.pop(plugin_name)
def get_text_as_file(bot, text): """Converts the text into a bytes object using BytesIO.""" try: # return io.BytesIO(bytes(str(text)), str.encode) return io.BytesIO(bytes(str(text), 'utf-8')) except Exception as e: raise BotException(EXCEPTION, "Failed to convert text to a file.", e=e)
def modify_mute_status(bot, change, mute): ''' Mutes or unmutes the given type of to_change (either server or channel). ''' change_id = change.id if type(change) is discord.Channel: # Channel change_reference = bot.servers_data[change.server.id]['muted_channels'] currently_muted = (change.id in change_reference) change = 'Channel' else: # Server currently_muted = bot.servers_data[change.id]['muted'] change = 'Server' if not (currently_muted ^ mute): raise BotException( ErrorTypes.RECOVERABLE, EXCEPTION, "{} is already {}muted.".format(change, '' if mute else 'un')) else: if change == 'Channel': if mute: change_reference.append(change_id) else: change_reference.remove(change_id) else: bot.servers_data[change_id]['muted'] = mute return change_id
def get_mastery_table(bot, static, watcher, name, champion=None): ''' Gets mastery information for the given summoner. If the champion argument is specified, it will find the details of that champion only. The table generated will be the top 10 champions of the summoner. ''' summoner = get_summoner_wrapper(watcher, name) if champion: try: champion_id = static[1][champion.replace(' ', '').lower()]['id'] champion_data = get_mastery_wrapper(bot, summoner['id'], champion_id=champion_id) except KeyError: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "Champion not found.") else: champion_data = get_mastery_wrapper(bot, summoner['id'], top=False) labels = '# | Champion | Points | Lvl | Box | Grade | Last Played ' line = '---|---------------|-----------|-----|-----|-------|-------------' if champion: labels = labels[5:] line = line[5:] response = '```\n{}\n{}\n'.format(labels, line) if champion: response += get_formatted_mastery_data(static, champion_data) else: for it in range(10): response += '{}'.format(it + 1).ljust(3) + '| ' response += get_formatted_mastery_data(static, champion_data[it]) return response + '```'
def list_data_append(bot, plugin_name, key, value, server_id=None, channel_id=None, user_id=None, volatile=False, duplicates=True): """Add data to list at location. Works like add, but manipulates the list at the location instead to append the given key. It creates the list if it doesn't exist. If the duplicates flag is set to false, this will not append the data if it is already found inside the list. """ current, location_key = get_location(bot, server_id, channel_id, user_id, volatile) if plugin_name not in current: current[plugin_name] = {} if key not in current[plugin_name]: # List doesn't exist current[plugin_name][key] = [value] else: # List already exists current = current[plugin_name][key] if type(current) is not list: raise BotException(EXCEPTION, "Data is not a list.") elif duplicates or value not in current: current.append(value) if not volatile and location_key not in bot.data_changed: bot.data_changed.append(location_key)
async def parallelize(coroutines, return_exceptions=False): """Uses asyncio.gather to "parallelize" the coroutines (not really).""" try: return await asyncio.gather(*coroutines, return_exceptions=return_exceptions) except Exception as e: raise BotException(EXCEPTION, "Failed to await coroutines.", e=e)
def check_and_add(dictionary, key, value): if key in dictionary: raise BotException( EXCEPTION, "Attempting to add a command that already exists.", key, error_type=ErrorTypes.FATAL) dictionary[key] = value
def get_manual(bot, entry, server=None): """Gets the given manual entry.""" invoker = utilities.get_invoker(bot, server=server) base_invoker = utilities.get_invoker(bot) if entry <= 0: raise BotException(EXCEPTION, "Invalid manual entry.") for manual in bot.manuals: manual_length = len(manual[1]['order']) if manual_length >= entry: entry_title = manual[1]['order'][entry - 1] found_entry = manual[1]['entries'][entry_title] response = '***`{0}`*** -- {1}\n\n'.format(manual[0], entry_title) return response + found_entry.format(invoker=invoker, base_invoker=base_invoker) else: entry -= manual_length raise BotException(EXCEPTION, "Invalid manual entry.")
def convert_and_check(self, bot, message, value): if self.convert: try: if isinstance(value, list): new_values = [] for entry in value: new_values.append(self.convert(bot, message, entry)) value = new_values else: value = self.convert(bot, message, value) except Exception as e: if hasattr(e, 'error_details'): # BotException if getattr(self.convert, 'pass_error', False): raise e convert_error = e.error_details elif hasattr(self.convert, 'get_convert_error'): convert_error = self.convert.get_convert_error( bot, message, value) else: convert_error = self.convert_error raise BotException( 'Parser', convert_error.format(b=bot, m=message, v=value), embed_fields=self.subcommand.help_embed_fields) if self.check: try: if isinstance(value, list): for entry in value: assert self.check(bot, message, entry) else: assert self.check(bot, message, value) except Exception as e: if hasattr(e, 'error_details'): # BotException if getattr(self.check, 'pass_error', False): raise e check_error = e.error_details elif hasattr(self.check, 'get_check_error'): check_error = self.check.get_check_error( bot, message, value) else: check_error = self.check_error raise BotException( 'Parser', check_error.format(b=bot, m=message, v=value), embed_fields=self.subcommand.help_embed_fields) return value
async def upload_to_discord(bot, fp, filename=None, rewind=True, close=False): """Uploads the given file-like object to the upload channel. If the upload channel is specified in the configuration files, files will be uploaded there. Otherwise, a new server will be created, and used as the upload channel.""" channel_id = configurations.get(bot, 'core', 'upload_channel') if not channel_id: # Check to see if a server was already created channel_id = data.get(bot, 'core', 'upload_channel') channel = bot.get_channel(channel_id) if channel is None: # Create server logging.debug("Creating server for upload channel...") try: server = await bot.create_server('uploads') except Exception as e: raise BotException( EXCEPTION, "Failed to create upload server. This bot is not whitelisted " "to create servers.", e=e) data.add(bot, 'core', 'upload_channel', server.id) channel = bot.get_channel(server.id) if channel is None: # Shouldn't happen raise BotException(EXCEPTION, "Failed to get upload channel.") try: message = await bot.send_file(channel, fp, filename=filename) upload_url = message.attachments[0]['url'] except Exception as e: raise BotException(EXCEPTION, "Failed to upload file.", e=e) if close: try: fp.close() except: pass elif rewind: try: fp.seek(0) except: pass return upload_url
def modify_user_group(bot, server, identity, add, which): ''' Add and remove users (either blocked, or moderators) to the servers_data dictionary. Throws an error if trying to add and the user is already on the list, specified by the which argument. ''' user_id = get_id(bot, identity, server=server) which_data = bot.servers_data[server.id][which] if add: if user_id in which_data: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "User already in the {} list.".format(which)) bot.servers_data[server.id][which].append(user_id) else: if user_id not in which_data: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "User was not in the {} list.".format(which)) bot.servers_data[server.id][which].remove(user_id) return user_id
async def add_to_cache(bot, url, name=None, file_location=None): """Downloads the URL and saves to the audio cache folder. If the cache folder has surpassed the cache size, this will continually remove the least used file (by date) until there is enough space. If the downloaded file is more than half the size of the total cache, it will not be stored. Returns the final location of the downloaded file. If name is specified, it will be stored under that name instead of the url. If file_location is specified, it will move that file instead of downloading the URL. """ if file_location: cleaned_name = utilities.get_cleaned_filename(file_location) else: file_location, cleaned_name = await utilities.download_url( bot, url, include_name=True) if name: cleaned_name = utilities.get_cleaned_filename(name) try: download_stat = os.stat(file_location) except FileNotFoundError: raise BotException( EXCEPTION, "The audio could not be saved. Please try again later.") cache_limit = bot.configurations['core']['cache_size_limit'] * 1000 * 1000 store = cache_limit > 0 and download_stat.st_size < cache_limit / 2 if store: cached_location = '{0}/audio_cache/{1}'.format(bot.path, cleaned_name) else: cached_location = '{}/temp/tempsound'.format(bot.path) try: os.remove(cached_location) except: # Doesn't matter if file doesn't exist pass os.rename(file_location, cached_location) if store: cache_entries = [] total_size = 0 for entry in os.scandir('{}/audio_cache'.format(bot.path)): stat = entry.stat() cache_entries.append((stat.st_atime, stat.st_size, entry.path)) total_size += stat.st_size cache_entries.sort(reverse=True) # TODO: Check complexity of list entry removal while total_size > cache_limit: entry = cache_entries.pop() os.remove(entry[2]) total_size -= entry[1] return cached_location
def get(bot, plugin_name, key=None, extra=None, extension='json'): """Gets the configuration file for the given plugin. Keyword arguments: key -- Gets the specified key from the config file, otherwise everything. extra -- Looks for <plugin_name>-<extra>.<extension> extension -- If 'json', reads the file as json, otherwise reads it as text. """ if extra: # Open from external configuration file filename = '{0}/config/{1}-{2}.{3}'.format(bot.path, plugin_name, extra, extension) else: # Open from configuration dictionary try: config = bot.configurations[plugin_name] except KeyError: raise BotException( EXCEPTION, "Plugin {} not found in the configurations dictionary.".format( plugin_name)) try: if key: return config[key] else: return config except KeyError: raise BotException( EXCEPTION, "Key {} not found in the configuration file.".format(key)) try: with open(filename, 'r') as config_file: if extension.lower() == 'json': return json.load(config_file) else: return config_file.read() except FileNotFoundError: raise BotException(EXCEPTION, "File {} not found.".format(filename)) except Exception as e: raise BotException(EXCEPTION, "Failed to read {} properly.".format(filename), e=e)
async def execute(bot, message, command, parsed_input, initial_data): """Calls get_response of the given plugin associated with the base.""" if bot.selfbot and command.no_selfbot: raise BotException(EXCEPTION, "This command cannot be used in selfbot mode.") if not initial_data[3] and command.base in bot.locked_commands: raise BotException(EXCEPTION, "This command is locked by the bot owner.") if message.channel.is_private: if not command.allow_direct: raise BotException(EXCEPTION, "Cannot use this command in a direct message.") elif 0 < command.elevated_level < 3: raise BotException( EXCEPTION, "Special permissions commands cannot be used in " "direct messages.") disabled_commands = [] else: disabled_commands = data.get(bot, 'base', 'disabled', server_id=message.server.id, default=[]) if command.elevated_level > 0: if command.elevated_level == 1 and not any(initial_data[1:]): raise BotException(EXCEPTION, "Only bot moderators can use this command.") elif command.elevated_level == 2 and not any(initial_data[2:]): raise BotException(EXCEPTION, "Only the server owner can use this command.") elif command.elevated_level >= 3 and not initial_data[3]: raise BotException(EXCEPTION, "Only the bot owner(s) can use this command.") for disabled_base, disabled_index in disabled_commands: if (command.base == disabled_base and disabled_index in (-1, parsed_input[1]) and not any(initial_data[1:])): raise BotException(EXCEPTION, "This command is disabled on this server.") if command.function: given_function = command.function else: given_function = command.plugin.get_response return await (given_function(bot, message, *parsed_input, initial_data[0]))
async def download_url(bot, url, include_name=False, extension=None): """Asynchronously downloads the given file to the temp folder. Returns the path of the downloaded file. If include_name is True, returns a tuple of the file location and the file name. """ cleaned_name = get_cleaned_filename(url, extension=extension) file_location = '{0}/temp/{1}'.format(bot.path, cleaned_name) try: response_code, downloaded_bytes = await get_url( bot, url, get_bytes=True, headers={'User-Agent': 'Mozilla/5.0'}) if response_code != 200: raise BotException(EXCEPTION, "Failed to download file.", response_code) with open(file_location, 'wb') as download: download.write(downloaded_bytes) if include_name: return (file_location, cleaned_name) else: return file_location except Exception as e: raise BotException(EXCEPTION, "Failed to download the file.", e=e)
def get_help(bot, base, topic=None, is_owner=False, server=None): """Gets the help of the base command, or the topic of a help command.""" try: base = base.lower() command = bot.commands[base] except KeyError: raise BotException( EXCEPTION, "Invalid command base. Ensure sure you are not " "including the command invoker.") if command.hidden and not is_owner: return '```\nCommand is hidden.```' if command.shortcut and base in command.shortcut.bases: shortcut_index = command.shortcut.bases.index(base) return usage_reminder(bot, base, index=shortcut_index, shortcut=True, is_owner=is_owner, server=server) # Handle specific topic help if topic is not None: try: topic_index = int(topic) except: # Guess the help index guess = parser.guess_index(bot, '{0} {1}'.format(base, topic)) topic_index = None if guess[1] == -1 else guess[1] + 1 return get_help(bot, base, topic=topic_index, is_owner=is_owner, server=server) else: # Proper index given return usage_reminder(bot, base, index=topic_index, server=server) response = '' invoker = utilities.get_invoker(bot, server=server) if command.description: response += '**Description**:\n{}\n\n'.format( command.description.format(invoker=invoker)) response += usage_reminder(bot, base, is_owner=is_owner, server=server) + '\n' if command.shortcut: response += usage_reminder( bot, base, shortcut=True, is_owner=is_owner, server=server) + '\n' if command.other: response += '**Other information**:\n{}'.format( command.other.format(invoker=invoker)) return response.rstrip()
def get_summoner_wrapper(watcher, name): ''' Wraps the obtaining of a summoner information with exception handling. ''' try: summoner = watcher.get_summoner(name=name) except LoLException as e: if e == error_429: api_cooldown() else: try: # Maybe we were given an ID summoner = watcher.get_summoner(_id=name) except Exception as e: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "Summoner \"" + name + "\" not found.", e=e) except Exception as e: raise BotException(ErrorTypes.RECOVERABLE, EXCEPTION, "Failed to retrieve summoner information.", e=e) return summoner
async def leave_and_stop(bot, server, member=None, safe=True): """Leaves any voice channel in the given server and stops any players. Keyword arguments: member -- Checks that the the bot is connected to the member's voice channel. The safe option overrides this. safe -- Prevents exceptions from being thrown. Can be seen as 'silent'. """ player = get_player(bot, server.id) if player is not None and player.is_playing(): player.stop() voice_client = bot.voice_client_in(server) if not voice_client: if not safe: raise BotException(EXCEPTION, "Bot not connected to a voice channel.") elif member and voice_client.channel != member.voice_channel: if not safe: raise BotException(EXCEPTION, "Bot not connected to your voice channel.") else: await voice_client.disconnect()