def get_user_id(server_id, name): """Gets the user ID from a readable name or nickname.""" users_data = servermanager.servers_data[server_id]['users'] name = name.strip() if name.startswith('<@') and name.endswith('>'): # Mention try: # Check if user exists users_data[name[2:-1]] return name[2:-1] except KeyError: pass discovered_already = False found_id = '' for user_id in users_data: # Iterate to find name or nickname if users_data[user_id]['name'] == name or users_data[user_id]['nickname'] == name: if not discovered_already: found_id = user_id discovered_already = True else: raise bot_exception(EXCEPT_TYPE, "Duplicate names found for '{}'! Specify user with a mention".format(name)) if found_id: return found_id try: # Raw user ID - last resort users_data[name] return name except KeyError: raise bot_exception(EXCEPT_TYPE, "User '{}' was not found".format(name))
def wikipedia_query(query, simple_result=False): if not query: return "Try searching for *something* next time, knucklehead." try: page = wikipedia.page(query, auto_suggest=True) if simple_result: # Just return the url of the found page return page.url else: # Return the first ~500 characters of the summary title = page.title summary = page.summary for i in range(0, (len(summary) if len(summary) < 500 else 500) - 1): if summary[i] == '=' and summary[i+1] == '=': summary = summary[0:i] break; if len(summary) >= 500: summary = summary[0:500] summary += ' ...*`[truncated]`*' return "***```{title}```***\n{summary}".format(title=title, summary=summary) except wikipedia.exceptions.PageError: raise bot_exception(WIKIPEDIA_EXCEPTION, "Page doesn't exist. Trying for some suggestions...", '```{}```'.format( (wikipedia.suggest(query) if wikipedia.suggest(query) is not None else "None"))) except wikipedia.exceptions.DisambiguationError as tp: # Try to get list of suggestions suggestions = wikipedia.search(query, results=5) if len(suggestions) > 0: formatted_suggestions = '```\n' for suggestion in suggestions: formatted_suggestions += '{}\n'.format(suggestion) formatted_suggestions += '```' raise bot_exception(WIKIPEDIA_EXCEPTION, "Query is too ambiguous. Here are some suggestions:", formatted_suggestions) else: raise bot_exception(WIKIPEDIA_EXCEPTION, "Query is too ambiguous. No suggestions found.")
def check_tag_access(server_id, tag_data, tag_name, user_id, need_owner=False): """Ensures that the given user is author of the tag (or a bot admin) if the tag is private, otherwise throw an exception.""" if (tag_data['private'] or need_owner) and (tag_data['author_id'] != user_id and not servermanager.is_admin(server_id, user_id)): tag_author = usermanager.get_name(server_id, tag_data['author_id']) if need_owner: raise bot_exception(EXCEPT_TYPE, "User is not the author of tag '{tag_name}', created by {tag_author}".format(tag_name=tag_name, tag_author=tag_author)) else: raise bot_exception(EXCEPT_TYPE, "The tag '{tag_name}' was made private by the author, {tag_author}".format(tag_name=tag_name, tag_author=tag_author))
async def update_color(server_id, user_id): """Refreshes the color role so that it is on top.""" if not botmanager.has_role_permissions(server_id): raise bot_exception(EXCEPT_TYPE, "Bot must be able to manage permissions in order to change role colors.") color = servermanager.servers_data[server_id]['users'][user_id]['color'] if color: converted = int(color[1:], 16) await botmanager.update_color_role(server_id, user_id, converted) else: raise bot_exception(EXCEPT_TYPE, "You currently don't have a custom color!") return "Color successfully refreshed!"
def add_ban(server_id, user_id, add=True): """Bans or unbans the given user.""" if (add and is_banned(server_id, user_id)) or (not add and not is_banned(server_id, user_id)): raise bot_exception(EXCEPT_TYPE, "User is already {}banned".format('' if add else "un")) elif add and is_admin(server_id, user_id): raise bot_exception(EXCEPT_TYPE, "Admins cannot be banned (remove admin status first to ban)") if add: servers_data[server_id]['bans'].append(user_id) else: servers_data[server_id]['bans'].remove(user_id) write_data() return "User successfully {}banned".format('' if add else "un")
def add_admin(server_id, user_id, add=True): """Adds or revokes admin privileges for given user.""" if (add and is_admin(server_id, user_id)) or (not add and not is_admin(server_id, user_id)): raise bot_exception(EXCEPT_TYPE, "User is already {} as an admin".format("added" if add else "removed")) if add: servers_data[server_id]['admins'].append(user_id) else: if user_id == configmanager.config['owner_id']: raise bot_exception(EXCEPT_TYPE, "You can't remove yourself, silly goose!") servers_data[server_id]['admins'].remove(user_id) write_data() return "User successfully {} as an admin".format("added" if add else "removed")
def get_random_avatar(): """Returns a bytes-like file from a downloaded image specified in avatars.txt.""" if os.stat(config_directory + '/avatars.txt').st_size > 0: image_url = random.choice(list(open(config_directory + '/avatars.txt'))) try: # Make sure the file can actually be downloaded urllib.request.urlretrieve(image_url, data_directory + '/tempavatar') with open(data_directory + '/tempavatar', 'rb') as avatar: return avatar.read() except: raise bot_exception(EXCEPT_TYPE, "Failed to download avatar from url {}\n(ensure that the url is a direct link to a PNG or JPG image, or try uploading the image to a better host, such as imgur.com or mixtape.moe)".format(image_url)) else: raise bot_exception(EXCEPT_TYPE, "Failed to update the avatar because the avatars.txt file is empty")
async def play_sound_tag(server_id, voice_channel_id, sound_tag_name, user_id): """Plays the sound from the given sound tag if it is available.""" try: if servermanager.is_muted(server_id, voice_channel_id): raise bot_exception(EXCEPT_TYPE, "The bot is muted in this voice channel") except KeyError: raise bot_exception(EXCEPT_TYPE, "You are not in a voice channel (are you perhaps on a different server?)") sound_tag_data = get_sound_tag_data(server_id, sound_tag_name) check_sound_tag_access(server_id, sound_tag_data, user_id, need_owner=False) update_sound_tag(server_id, sound_tag_name, increment_hits=True) # Increment hits from jshbot.botmanager import client from jshbot.botmanager import voice_player global timeout_goal channel = botmanager.get_voice_channel(server_id, voice_channel_id) if client.voice == None or client.voice.channel != channel or not client.voice.is_connected(): # Connect to channel if client.voice: # Disconnect from old channel await client.voice.disconnect() client.voice = await client.join_voice_channel(channel) voice_player.server_id = server_id if voice_player.player is not None and voice_player.player.is_playing(): # Stop if playing voice_player.player.stop() if sound_tag_data['type'] == 'YouTube': # To prevent playlist downloads # 'noplaylist': True voice_player.player = await client.voice.create_ytdl_player(sound_tag_data['url']) else: # Direct download (or stream? Not sure) try: # One day, I will figure out how to stream this crap. But today is not that day. #response = urllib.request.urlopen(sound_tag_data['url']) #voice_player.player = client.voice.create_ffmpeg_player(response, use_avconv=True) urllib.request.urlretrieve (sound_tag_data['url'], '{}/tempsound'.format(configmanager.data_directory)) voice_player.player = client.voice.create_ffmpeg_player('{}/tempsound'.format(configmanager.data_directory)) except: raise bot_exception(EXCEPT_TYPE, "An error occurred when downloading the sound file") voice_player.player.start() timeout = configmanager.config['voice_timeout'] if timeout >= 0: timeout_reset_lock.acquire() timeout_goal = time.time() + ((timeout*60) if timeout > 0 else (sound_tag_data['length']+1)) timeout_reset_lock.release() await timeout_disconnect()
def get_tag_data(server_id, tag_name): """Ensures that the given tag exists and return tag data, otherwise throw an exception.""" try: return servermanager.servers_data[server_id]['tags'][tag_name] except KeyError: raise bot_exception(EXCEPT_TYPE, "Tag '{}' doesn't exist".format(tag_name))
def check_tag_access(server_id, tag_data, tag_name, user_id, need_owner=False): """Ensures that the given user is author of the tag (or a bot admin) if the tag is private, otherwise throw an exception.""" if (tag_data['private'] or need_owner) and ( tag_data['author_id'] != user_id and not servermanager.is_admin(server_id, user_id)): tag_author = usermanager.get_name(server_id, tag_data['author_id']) if need_owner: raise bot_exception( EXCEPT_TYPE, "User is not the author of tag '{tag_name}', created by {tag_author}" .format(tag_name=tag_name, tag_author=tag_author)) else: raise bot_exception( EXCEPT_TYPE, "The tag '{tag_name}' was made private by the author, {tag_author}" .format(tag_name=tag_name, tag_author=tag_author))
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # Wikipedia query if command in wikipedia_commands and ((num_options == 1 and options[0] in ['s', 'simple', 'url']) or num_options == 0): simple_result = False if using_shortcut or num_options == 1: simple_result = True return wikipedia_query(arguments[0] if num_arguments == 1 else arguments_blocks[0], simple_result=simple_result) # Wolfram|Alpha query elif (command in wolfram_alpha_commands and num_arguments >= 1 and ((num_options == 1 and (options[0].startswith('results=') or options[0] in ['s', 'simple']) or num_options == 0))): simple_result = False results_limit = 2 if using_shortcut or (num_options == 1 and options[0] in ['s', 'simple']): simple_result = True elif num_options == 1: # Result number specification results_limit = int(options[0][8:]) wolfram_query = arguments[0] if num_arguments == 1 else arguments_blocks[0] return wolfram_alpha_query(wolfram_query, simple_result=simple_result, results_limit=results_limit) # Urban Dictionary definition elif command in urban_dictionary_commands and num_arguments >= 1 and num_options == 0: urban_query = arguments[0] if num_arguments == 1 else arguments_blocks[0] return urban_dictionary_definition(urban_query) # Invalid command command, EXCEPT_TYPE = get_command_info(command) raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string(command))
def set_nickname(server_id, user_id, nickname_text): """Sets the user nickname as the given text.""" if len(nickname_text) > 50: raise bot_exception(EXCEPT_TYPE, "Nickname cannot be more than 50 characters long") servers_data = servermanager.servers_data servers_data[server_id]['users'][user_id]['nickname'] = nickname_text write_data() return "Nickname successfully {}!".format("set" if nickname_text else "cleared")
def set_status(server_id, user_id, status_text): """Sets the user status as the given text.""" if len(status_text) > 1000: raise bot_exception(EXCEPT_TYPE, "Status cannot be more than 1000 characters long") servers_data = servermanager.servers_data servers_data[server_id]['users'][user_id]['status'] = status_text write_data() return "Status successfully {}!".format("set" if status_text else "cleared")
def add_admin(server_id, user_id, add=True): """Adds or revokes admin privileges for given user.""" if (add and is_admin(server_id, user_id)) or ( not add and not is_admin(server_id, user_id)): raise bot_exception( EXCEPT_TYPE, "User is already {} as an admin".format( "added" if add else "removed")) if add: servers_data[server_id]['admins'].append(user_id) else: if user_id == configmanager.config['owner_id']: raise bot_exception(EXCEPT_TYPE, "You can't remove yourself, silly goose!") servers_data[server_id]['admins'].remove(user_id) write_data() return "User successfully {} as an admin".format( "added" if add else "removed")
def add_ban(server_id, user_id, add=True): """Bans or unbans the given user.""" if (add and is_banned(server_id, user_id)) or ( not add and not is_banned(server_id, user_id)): raise bot_exception( EXCEPT_TYPE, "User is already {}banned".format('' if add else "un")) elif add and is_admin(server_id, user_id): raise bot_exception( EXCEPT_TYPE, "Admins cannot be banned (remove admin status first to ban)") if add: servers_data[server_id]['bans'].append(user_id) else: servers_data[server_id]['bans'].remove(user_id) write_data() return "User successfully {}banned".format('' if add else "un")
def mute_server(server_id, mute=True): """Mutes or unmutes the specified server.""" server_muted = servers_data[server_id]['muted'] if (mute and server_muted) or (not mute and not server_muted): raise bot_exception(EXCEPT_TYPE, "Server is already {}muted".format('' if mute else "un")) servers_data[server_id]['muted'] = mute write_data() return "Server successfully {}muted".format('' if mute else "un")
def get_random_status(): """Returns a random status from the statuses.txt file.""" if os.stat(config_directory + '/statuses.txt').st_size > 0: with open(config_directory + '/statuses.txt', 'r') as statuses_file: return str(random.choice(list(statuses_file))).rstrip() else: raise bot_exception(EXCEPT_TYPE, "Failed to update the status because the statuses.txt file is empty")
def update_tag(server_id, tag_name, increment_hits=False, **kwargs): """Updates or creates a tag in the server under the given author ID. Keyword arguments: increment_hits -- increments the hits counter of the tag user_id -- user trying to modify the tag author_id -- author of the tag tag_text -- text to go along with the tag private -- whether or not the tag can only be called by the author hits -- how many times the tag has been called full_name -- what the full name (with spaces) is (never passed in) """ to_return = '' full_name = tag_name tag_name = tag_name.replace(' ', '') servers_data = servermanager.servers_data if increment_hits: # Updating hit counter servers_data[server_id]['tags'][tag_name]['hits'] += 1 else: # Creating or modifying a tag try: # Modify a tag tag_data = servers_data[server_id]['tags'][tag_name] try: check_tag_access(server_id, tag_data, tag_name, kwargs['user_id'], need_owner=True) except KeyError: # user_id not found, meant to create but tag exists raise bot_exception(EXCEPT_TYPE, "Tag '{}' already exists".format(tag_name)) del kwargs['user_id'] # Don't write user_id to updated tag servers_data[server_id]['tags'][tag_name].update(kwargs) to_return += "Tag '{}' successfully modified!".format(full_name) except KeyError: # Tag doesn't exist. Create it. if configmanager.config['tags_per_server'] > 0 and len(servers_data[server_id]['tags']) >= configmanager.config['tags_per_server']: raise bot_exception(EXCEPT_TYPE, "This server has hit the tag limit of {}".format(configmanager.config['tags_per_server'])) if 'tag_text' not in kwargs: raise bot_exception(EXCEPT_TYPE, "Tag '{}' does not exist".format(tag_name)) if len(tag_name) > 50: raise bot_exception(EXCEPT_TYPE, "Tag names cannot be larger than 50 characters long") if len(kwargs['tag_text']) > 2000: # This shouldn't really happen, ever raise bot_exception(EXCEPT_TYPE, "Tag text cannot be larger than 2000 characters long") # Edit safety if 'user_id' in kwargs: kwargs['author_id'] = kwargs['user_id'] del kwargs['user_id'] kwargs['full_name'] = full_name servers_data[server_id]['tags'][tag_name] = {**kwargs, 'hits':0, 'date_created':time.strftime("%c")} to_return += "Tag '{}' successfully created!".format(full_name) write_data() return to_return
def mute_channel(server_id, channel_id, mute=True): """Mutes or unmutes the specified channel.""" channel_muted = servers_data[server_id]['channels'][channel_id]['muted'] if (mute and channel_muted) or (not mute and not channel_muted): raise bot_exception(EXCEPT_TYPE, "Channel is already {}muted".format('' if mute else "un")) servers_data[server_id]['channels'][channel_id]['muted'] = mute write_data() return "Channel successfully {}muted".format('' if mute else "un")
def mute_channel(server_id, channel_id, mute=True): """Mutes or unmutes the specified channel.""" channel_muted = servers_data[server_id]['channels'][channel_id]['muted'] if (mute and channel_muted) or (not mute and not channel_muted): raise bot_exception( EXCEPT_TYPE, "Channel is already {}muted".format('' if mute else "un")) servers_data[server_id]['channels'][channel_id]['muted'] = mute write_data() return "Channel successfully {}muted".format('' if mute else "un")
def mute_server(server_id, mute=True): """Mutes or unmutes the specified server.""" server_muted = servers_data[server_id]['muted'] if (mute and server_muted) or (not mute and not server_muted): raise bot_exception( EXCEPT_TYPE, "Server is already {}muted".format('' if mute else "un")) servers_data[server_id]['muted'] = mute write_data() return "Server successfully {}muted".format('' if mute else "un")
async def set_color(server_id, user_id, color): """Gives the user the given color as a role.""" if not botmanager.has_role_permissions(server_id): # Check bot permissions first raise bot_exception(EXCEPT_TYPE, "Bot must be able to manage permissions in order to change role colors.") if color is None: servermanager.servers_data[server_id]['users'][user_id]['color'] = '' write_data() await botmanager.update_color_role(server_id, user_id, None) return "Color successfully cleared!" if color.startswith('#'): # Just in case people just copy values over color = color[1:] try: # Make sure we're given a valid hex value here converted = int(color, 16) except ValueError: raise bot_exception(EXCEPT_TYPE, "'{}' does not appear to be a valid hex color.".format(color)) await botmanager.update_color_role(server_id, user_id, converted) servermanager.servers_data[server_id]['users'][user_id]['color'] = '#{}'.format(color.upper()) write_data() return "Color successfully set!"
def get_random_status(): """Returns a random status from the statuses.txt file.""" if os.stat(config_directory + '/statuses.txt').st_size > 0: with open(config_directory + '/statuses.txt', 'r') as statuses_file: return str(random.choice(list(statuses_file))).rstrip() else: raise bot_exception( EXCEPT_TYPE, "Failed to update the status because the statuses.txt file is empty" )
def get_random_avatar(): """Returns a bytes-like file from a downloaded image specified in avatars.txt.""" if os.stat(config_directory + '/avatars.txt').st_size > 0: image_url = random.choice(list(open(config_directory + '/avatars.txt'))) try: # Make sure the file can actually be downloaded urllib.request.urlretrieve(image_url, data_directory + '/tempavatar') with open(data_directory + '/tempavatar', 'rb') as avatar: return avatar.read() except: raise bot_exception( EXCEPT_TYPE, "Failed to download avatar from url {}\n(ensure that the url is a direct link to a PNG or JPG image, or try uploading the image to a better host, such as imgur.com or mixtape.moe)" .format(image_url)) else: raise bot_exception( EXCEPT_TYPE, "Failed to update the avatar because the avatars.txt file is empty" )
async def stop_sounds(server_id, timeout=False): """Stops all audio and disconnects the bot from the voice channel.""" from jshbot.botmanager import client from jshbot.botmanager import voice_player if voice_player.player and voice_player.player.is_playing(): if not timeout: if voice_player.server_id != server_id: # Ensure we're stopping the bot on the server it is playing at raise bot_exception(EXCEPT_TYPE, "The bot is not connected to this server") voice_player.player.stop() if client.voice: await client.voice.disconnect() voice_player.server_id = None elif client.voice.is_connected(): if not timeout: if voice_player.server_id != server_id: # Ensure we're stopping the bot on the server it is playing at raise bot_exception(EXCEPT_TYPE, "The bot is not connected to this server") await client.voice.disconnect() if voice_player: voice_player.server_id = None elif not timeout: raise bot_exception(EXCEPT_TYPE, "No sound is playing") return "Stopping all sounds and disconnecting..."
def urban_dictionary_definition(query): parsed_query = urllib.parse.quote_plus(query) query_url = 'http://www.urbandictionary.com/define.php?term={}'.format(parsed_query) try: urban_data = requests.get('http://api.urbandictionary.com/v0/define?term={}'.format(parsed_query)).json() except: raise bot_exception(URBAN_DICTIONARY_EXCEPTION, "Failed to get definition from Urban Dictionary", query_url) if urban_data['result_type'] != 'exact': # Probably 'no_result', but this is safer raise bot_exception(URBAN_DICTIONARY_EXCEPTION, "The query '{}' returned no results".format(query), query_url) definition = urban_data['list'][0] rating = '{}% rating (:thumbsup:{} | :thumbsdown:{})'.format( int(100*definition['thumbs_up'] / (definition['thumbs_up'] + definition['thumbs_down'])), definition['thumbs_up'], definition['thumbs_down']) # Truncate definition and examples so that they aren't so freakin' long if len(definition['definition']) > 500: definition['definition'] = '{} ...*`[truncated]`*'.format(definition['definition'][:499]) if len(definition['example']) > 500: definition['example'] = '{} ...`[truncated]`'.format(definition['example'][:499]) elif len(definition['example']) == 0: definition['example'] = 'No example provided' return '***```{definition[word]}```***\n{definition[definition]}\n\n*{definition[example]}*\n\n*{query_url}* - {rating}'.format( definition=definition, rating=rating, query_url=query_url)
def get_channel_info(server_id, channel_id): """Retrieves a bundle of channel information.""" if channel_id == 0: # No voice channel raise bot_exception(EXCEPT_TYPE, "You are not in a voice channel on this server") return """Channel information for {channel_data[name]}: ``` ID: {channel_id} Name: {channel_data[name]} Muted: {channel_data[muted]} Position: {channel_data[position]} Voice channel: {channel_data[voice]} Default: {channel_data[default]} ```""".format(channel_id=channel_id, channel_data=servers_data[server_id]['channels'][channel_id], config=configmanager.config)
def get_random_number(minimum=1, maximum=100, is_private=True, **kwargs): """Obtains a random number with the specified minimum and maximum values. Same keyword arguments as flip_coin.""" try: # Typecheck minimum and maximum values minimum = int(minimum) maximum = int(maximum) except: raise bot_exception(EXCEPT_TYPE, "Inputs are not valid integers") if (minimum == maximum): return "You must think you're really clever." if (minimum > maximum): minimum, maximum = maximum, minimum return "{identifier} got {result}.".format( identifier="You" if is_private else usermanager.get_name(kwargs['server_id'], kwargs['user_id']), result=random.randint(minimum, maximum))
def roll_die(faces=6, is_private=True, **kwargs): """Rolls a 6 sided die unless specified. Same keyword arguments as flip_coin.""" try: # Typecheck faces value faces = int(faces) except: raise bot_exception(EXCEPT_TYPE, "Input is not a valid integer") to_return = "You" if is_private else usermanager.get_name(kwargs['server_id'], kwargs['user_id']) if (faces == 1): to_return += " rolled a {}. Just kidding.".format(random.randint(1,2147483647)) elif (faces <= 0): return "Look, I can't roll something that breaks the laws of physics." else: # Valid roll to_return += " rolled a {}.".format(random.randint(1, faces)) return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # Flip a coin if ((command in ['flip'] and num_options == 0 and num_arguments == 0) or (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['f', 'flip'])): public_info = {} if not is_private: # Bundle server and user ID if we need a name public_info = {'server_id':server_id, 'user_id':user_id} return flip_coin(is_private=is_private, **public_info) # Roll dice elif ((command in ['roll'] and num_arguments <= 1) or (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['r', 'roll', 'd', 'dice'])): public_info = {} if not is_private: public_info = {'server_id':server_id, 'user_id':user_id} return roll_die(faces=arguments[0] if num_arguments == 1 else 6, is_private=is_private, **public_info) # Pick a choice elif ((command in ['pick'] and num_options == 0 and num_arguments >= 0) or (not using_shortcut and num_options == 1 and num_arguments >= 0 and options[0] in ['p', 'pick', 'c', 'choose', 'd', 'decide', 'f', 'fluky'])): return pick_choice(*arguments) # Number of arguments is checked in function # Get a random tag lolwat elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['t', 'tag']: return tagmanager.get_random_tag(server_id, user_id) # Get a random tag uw0t elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['st', 'soundtag']: return await soundtagmanager.get_random_sound_tag(server_id, voice_channel_id, user_id) # Get random number elif not using_shortcut and num_options == 0 and (num_arguments == 0 or num_arguments == 2): public_info = {} if not is_private: public_info = {'server_id':server_id, 'user_id':user_id} if num_arguments == 2: return get_random_number(minimum=arguments[0], maximum=arguments[1], is_private=is_private, **public_info) else: return get_random_number(is_private=is_private, **public_info) # Invalid command raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
def get_random_number(minimum=1, maximum=100, is_private=True, **kwargs): """Obtains a random number with the specified minimum and maximum values. Same keyword arguments as flip_coin.""" try: # Typecheck minimum and maximum values minimum = int(minimum) maximum = int(maximum) except: raise bot_exception(EXCEPT_TYPE, "Inputs are not valid integers") if (minimum == maximum): return "You must think you're really clever." if (minimum > maximum): minimum, maximum = maximum, minimum return "{identifier} got {result}.".format( identifier="You" if is_private else usermanager.get_name( kwargs['server_id'], kwargs['user_id']), result=random.randint(minimum, maximum))
def roll_die(faces=6, is_private=True, **kwargs): """Rolls a 6 sided die unless specified. Same keyword arguments as flip_coin.""" try: # Typecheck faces value faces = int(faces) except: raise bot_exception(EXCEPT_TYPE, "Input is not a valid integer") to_return = "You" if is_private else usermanager.get_name( kwargs['server_id'], kwargs['user_id']) if (faces == 1): to_return += " rolled a {}. Just kidding.".format( random.randint(1, 2147483647)) elif (faces <= 0): return "Look, I can't roll something that breaks the laws of physics." else: # Valid roll to_return += " rolled a {}.".format(random.randint(1, faces)) return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # User information if (command in ['userinfo', 'info', 'user', 'u'] and (num_options == 0 or (num_options == 1 and options[0] in ['i', 'info']))): if num_arguments == 0: # Self information return get_info(server_id, user_id) else: # Other user information return get_info(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0])) # User status elif ((command in ['status'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['s', 'status'])): if num_arguments == 0: # Self status return get_status(server_id, user_id) else: # Other user status return get_status(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0])) # User nickname elif ((command in ['nickname', 'nick'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['n', 'nick', 'nickname'])): if num_arguments == 0: # Self nickname return get_nickname(server_id, user_id) else: # Other user nickname return get_nickname(server_id, get_user_id(server_id, arguments[0] if num_arguments == 1 else arguments_blocks[0])) # Modify status elif ((command in ['setstatus'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['ss', 'setstatus'])): if num_arguments == 0: # Clear status return set_status(server_id, user_id, '') else: # Set status return set_status(server_id, user_id, arguments[0] if num_arguments == 1 else arguments_blocks[0]) # Modify nickname elif ((command in ['setnickname', 'setnick'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['setnickname', 'setnick', 'sn'])): if num_arguments == 0: # Clear nickname return set_nickname(server_id, user_id, '') else: # Set nickname return set_nickname(server_id, user_id, arguments[0] if num_arguments == 1 else arguments_blocks[0]) # Set role color elif (not using_shortcut and num_options == 1 and options[0] in ['setcolor', 'sc'] and num_arguments <= 1): if num_arguments == 0: # Clear nickname return await set_color(server_id, user_id, None) else: # Set nickname return await set_color(server_id, user_id, arguments[0]) # Refresh color elif (not using_shortcut and num_options == 1 and options[0] in ['updatecolor', 'uc'] and num_arguments == 0): return await update_color(server_id, user_id) # Invalid command raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
def update_tag(server_id, tag_name, increment_hits=False, **kwargs): """Updates or creates a tag in the server under the given author ID. Keyword arguments: increment_hits -- increments the hits counter of the tag user_id -- user trying to modify the tag author_id -- author of the tag tag_text -- text to go along with the tag private -- whether or not the tag can only be called by the author hits -- how many times the tag has been called full_name -- what the full name (with spaces) is (never passed in) """ to_return = '' full_name = tag_name tag_name = tag_name.replace(' ', '') servers_data = servermanager.servers_data if increment_hits: # Updating hit counter servers_data[server_id]['tags'][tag_name]['hits'] += 1 else: # Creating or modifying a tag try: # Modify a tag tag_data = servers_data[server_id]['tags'][tag_name] try: check_tag_access(server_id, tag_data, tag_name, kwargs['user_id'], need_owner=True) except KeyError: # user_id not found, meant to create but tag exists raise bot_exception(EXCEPT_TYPE, "Tag '{}' already exists".format(tag_name)) del kwargs['user_id'] # Don't write user_id to updated tag servers_data[server_id]['tags'][tag_name].update(kwargs) to_return += "Tag '{}' successfully modified!".format(full_name) except KeyError: # Tag doesn't exist. Create it. if configmanager.config['tags_per_server'] > 0 and len( servers_data[server_id] ['tags']) >= configmanager.config['tags_per_server']: raise bot_exception( EXCEPT_TYPE, "This server has hit the tag limit of {}".format( configmanager.config['tags_per_server'])) if 'tag_text' not in kwargs: raise bot_exception(EXCEPT_TYPE, "Tag '{}' does not exist".format(tag_name)) if len(tag_name) > 50: raise bot_exception( EXCEPT_TYPE, "Tag names cannot be larger than 50 characters long") if len(kwargs['tag_text'] ) > 2000: # This shouldn't really happen, ever raise bot_exception( EXCEPT_TYPE, "Tag text cannot be larger than 2000 characters long") # Edit safety if 'user_id' in kwargs: kwargs['author_id'] = kwargs['user_id'] del kwargs['user_id'] kwargs['full_name'] = full_name servers_data[server_id]['tags'][tag_name] = { **kwargs, 'hits': 0, 'date_created': time.strftime("%c") } to_return += "Tag '{}' successfully created!".format(full_name) write_data() return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # Flip a coin if ((command in ['flip'] and num_options == 0 and num_arguments == 0) or (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['f', 'flip'])): public_info = {} if not is_private: # Bundle server and user ID if we need a name public_info = {'server_id': server_id, 'user_id': user_id} return flip_coin(is_private=is_private, **public_info) # Roll dice elif ((command in ['roll'] and num_arguments <= 1) or (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['r', 'roll', 'd', 'dice'])): public_info = {} if not is_private: public_info = {'server_id': server_id, 'user_id': user_id} return roll_die(faces=arguments[0] if num_arguments == 1 else 6, is_private=is_private, **public_info) # Pick a choice elif ((command in ['pick'] and num_options == 0 and num_arguments >= 0) or (not using_shortcut and num_options == 1 and num_arguments >= 0 and options[0] in ['p', 'pick', 'c', 'choose', 'd', 'decide', 'f', 'fluky'])): return pick_choice( *arguments) # Number of arguments is checked in function # Get a random tag lolwat elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[ 0] in ['t', 'tag']: return tagmanager.get_random_tag(server_id, user_id) # Get a random tag uw0t elif not using_shortcut and num_options == 1 and num_arguments == 0 and options[ 0] in ['st', 'soundtag']: return await soundtagmanager.get_random_sound_tag( server_id, voice_channel_id, user_id) # Get random number elif not using_shortcut and num_options == 0 and (num_arguments == 0 or num_arguments == 2): public_info = {} if not is_private: public_info = {'server_id': server_id, 'user_id': user_id} if num_arguments == 2: return get_random_number(minimum=arguments[0], maximum=arguments[1], is_private=is_private, **public_info) else: return get_random_number(is_private=is_private, **public_info) # Invalid command raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
def wolfram_alpha_query(query, simple_result=False, results_limit=2): """Returns a query result from Wolfram|Alpha, either in full text or just one result.""" if results_limit < 1: raise bot_exception(EXCEPT_TYPE, "Invalid number of results (1 to 8)") results_limit += 1 # Increment to account for input result, which doesn't really count query_url = "Query URL: http://www.wolframalpha.com/input/?i={}\n".format(urllib.parse.quote_plus(query)) to_return = '' try: query_result = wolframalpha.Client(configmanager.config['wolfram_api_key']).query(query) except: raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha is not configured for use right now, sorry") result_root = query_result.tree.getroot() # Error handling if result_root.get('success') == 'false': try: # suggestion = result_root.find('didyoumeans').find('didyoumean').text except Exception as e: # TODO: Get proper exception type print("Something bad happened to the query:\n" + str(e)) # DEBUG raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha could not interpret your query\n{}".format(query_url)) raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha could not interpret your query. Trying for first suggestion '{}'...".format(suggestion), wolfram_alpha_query(suggestion)) elif result_root.get('timedout'): if len(query_result.pods) == 0: raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Query timed out", query_url) elif not simple_result: to_return += "```\nWarning: query timed out but returned some results:```\n" elif len(query_result.pods) == 0: raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "No result given (general error)", query_url) number_of_results = 0 # Format answer if simple_result: # Return a straight, single answer if len(list(query_result.results)) > 0: to_return += list(query_result.results)[0].text + "\n" else: # No explicit 'result' was found try: to_return += "Closest result:\n{}\n".format(list(query_result.pods)[1].text) except IndexError: to_return += "No valid result returned. This is a bug! Avert your eyes!" except Exception as e: # This shouldn't happen, really print("Something bad happened to the query (returning simple result):\n" + str(e)) # DEBUG raise bot_exception(WOLFRAM_ALPHA_EXCEPTION, "Wolfram|Alpha is now dead. Nice work.") else: # Full answer, up to 1800 characters long for pod in list(query_result.pods): for sub_pod in list(pod.node): image = sub_pod.find('img') if image is not None: to_return += "{pod_title}: {image_url}\n".format(pod_title=pod.__dict__['title'], image_url=image.get('src')) number_of_results += 1 if len(to_return) > 1800: # We're getting a very large result. Truncate. to_return += "```\nWarning: truncating very long result here...```\n" break if number_of_results >= results_limit: break to_return += query_url return to_return
def get_sound_tag_data(server_id, sound_tag_name): """Ensures that the given sound tag exists and return sound tag data, otherwise throw an exception.""" try: return servermanager.servers_data[server_id]['sound_tags'][sound_tag_name] except KeyError: raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' doesn't exist".format(sound_tag_name))
def update_sound_tag(server_id, sound_tag_name, increment_hits=False, **kwargs): """Updates or creates a tag in the server under the given author ID. Keyword arguments: increment_hits -- increments the hits counter of the tag user_id -- user trying to modify the tag author_id -- author of the tag duration -- how long the sound should play for url -- url of the audio source private -- whether or not the tag can only be called by the author hits -- how many times the tag has been called type -- tag type (either YouTube or direct) length -- how long the sound tag is in seconds full_name -- what the full name (with spaces) is """ to_return = '' full_name = sound_tag_name sound_tag_name = sound_tag_name.replace(' ', '') servers_data = servermanager.servers_data if increment_hits: # Updating hit counter servers_data[server_id]['sound_tags'][sound_tag_name]['hits'] += 1 else: # Creating or modifying a tag if kwargs['url'].startswith('https://www.youtube.com/') or kwargs[ 'url'].startswith('https://youtu.be/'): kwargs['type'] = 'YouTube' else: kwargs['type'] = 'direct' # Check tag length if kwargs['type'] == 'YouTube': # Find length of YouTube video try: length = pafy.new(kwargs['url']).length except: raise bot_exception(EXCEPT_TYPE, "Invalid YouTube video") else: # Download file and check length try: urllib.request.urlretrieve( kwargs['url'], '{}/tempsound'.format(configmanager.data_directory)) length = MP3('{}/tempsound'.format( configmanager.data_directory)).info.length except: raise bot_exception(EXCEPT_TYPE, "Invalid direct download file or URL") length_limit = int(configmanager.config['sound_tag_length_limit']) if length_limit > 0 and int(length) > length_limit: raise bot_exception( EXCEPT_TYPE, "Sound tags can be no longer than {} second{}".format( length_limit, 's' if length_limit > 1 else '')) kwargs['length'] = int(length) try: sound_tag_data = servers_data[server_id]['sound_tags'][ sound_tag_name] try: check_sound_tag_access(server_id, sound_tag_data, kwargs['user_id'], need_owner=True) except KeyError: # user_id not found, meant to create but tag exists raise bot_exception( EXCEPT_TYPE, "Sound tag '{}' already exists".format(sound_tag_name)) del kwargs['user_id'] # Don't write user_id to updated tag servers_data[server_id]['sound_tags'][sound_tag_name].update( kwargs) to_return += "Sound tag '{}' successfully modified!".format( full_name) except KeyError: # Tag doesn't exist. Create it. if configmanager.config['sound_tags_per_server'] > 0 and len( servers_data[server_id]['sound_tags'] ) >= configmanager.config['sound_tags_per_server']: raise bot_exception( EXCEPT_TYPE, "This server has hit the sound tag limit of {}".format( configmanager.config['sound_tags_per_server'])) if 'url' not in kwargs: raise bot_exception( EXCEPT_TYPE, "Sound tag '{}' does not exist".format(sound_tag_name)) if len(sound_tag_name) > 50: raise bot_exception( EXCEPT_TYPE, "Sound tag names cannot be larger than 50 characters long") if len(kwargs['url']) > 2000: # This shouldn't really happen, ever raise bot_exception( EXCEPT_TYPE, "Sound tag url cannot be larger than 2000 characters long (how did you do this)" ) # Edit safety if 'user_id' in kwargs: kwargs['author_id'] = kwargs['user_id'] del kwargs['user_id'] kwargs['full_name'] = full_name servers_data[server_id]['sound_tags'][sound_tag_name] = { **kwargs, 'hits': 0, 'date_created': time.strftime("%c") } to_return += "Sound tag '{}' successfully created!".format( full_name) write_data() return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # Play sounds if (not using_shortcut and num_options == 0 and num_arguments >= 1): try: # For convenience, try with both raw parameters and single argument if num_arguments == 1: await play_sound_tag(server_id, voice_channel_id, arguments[0].lower().replace(' ', ''), user_id) return None; except bot_exception: pass await play_sound_tag(server_id, voice_channel_id, arguments_blocks[0].lower().replace(' ', ''), user_id) return None; # Create sound tag elif (num_arguments == 2 and ((command in ['stc'] and num_options == 0) or (not using_shortcut and (num_options == 1 or (num_options == 2 and options[1] in ['p', 'private'])) and options[0] in ['c', 'create']))): use_private = num_options == 2 and options[1] in ['p', 'private'] # Private tag return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), url=arguments[1], author_id=user_id, private=use_private) # Remove sound tag elif (num_arguments >= 1 and ((command in ['str'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['r', 'remove']))): sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return remove_sound_tag(server_id, sound_tag_name, user_id) # List sound tags elif ((command in ['stl'] and num_options == 0) or (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['l', 'list'])): return list_sound_tags(server_id, user_id=usermanager.get_user_id(server_id, arguments[0]) if num_arguments == 1 else '') # Search sound tags elif (num_arguments >= 1 and ((command in ['sts'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['s', 'search']))): sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return search_sound_tags(server_id, sound_tag_name) # Sound tag info elif not using_shortcut and num_options == 1 and num_arguments >= 1 and options[0] in ['i', 'info']: sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return get_sound_info(server_id, sound_tag_name) # Edit sound tag elif not using_shortcut and (num_options in range(1,3)) and num_arguments <= 2 and options[0] in ['e', 'edit']: if num_options == 2 and num_arguments == 1: # Set private or public if options[1] == 'setpublic': # Explicit to follow a strict syntax return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=False) elif options[1] == 'setprivate': return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=True) elif num_options == 1 and num_arguments == 2: # Modify sound tag url return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, url=arguments[1], private=False) # Stop sounds elif ((command in ['stfu'] and num_arguments == 0 and num_options == 0) or (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['stop', 'silence', 'silent', 'fu'])): return await stop_sounds(server_id) # Invalid command raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())
async def play_sound_tag(server_id, voice_channel_id, sound_tag_name, user_id): """Plays the sound from the given sound tag if it is available.""" try: if servermanager.is_muted(server_id, voice_channel_id): raise bot_exception(EXCEPT_TYPE, "The bot is muted in this voice channel") except KeyError: raise bot_exception( EXCEPT_TYPE, "You are not in a voice channel (are you perhaps on a different server?)" ) sound_tag_data = get_sound_tag_data(server_id, sound_tag_name) check_sound_tag_access(server_id, sound_tag_data, user_id, need_owner=False) update_sound_tag(server_id, sound_tag_name, increment_hits=True) # Increment hits from jshbot.botmanager import client from jshbot.botmanager import voice_player global timeout_goal channel = botmanager.get_voice_channel(server_id, voice_channel_id) if client.voice == None or client.voice.channel != channel or not client.voice.is_connected( ): # Connect to channel if client.voice: # Disconnect from old channel await client.voice.disconnect() client.voice = await client.join_voice_channel(channel) voice_player.server_id = server_id if voice_player.player is not None and voice_player.player.is_playing( ): # Stop if playing voice_player.player.stop() if sound_tag_data['type'] == 'YouTube': # To prevent playlist downloads # 'noplaylist': True voice_player.player = await client.voice.create_ytdl_player( sound_tag_data['url']) else: # Direct download (or stream? Not sure) try: # One day, I will figure out how to stream this crap. But today is not that day. #response = urllib.request.urlopen(sound_tag_data['url']) #voice_player.player = client.voice.create_ffmpeg_player(response, use_avconv=True) urllib.request.urlretrieve( sound_tag_data['url'], '{}/tempsound'.format(configmanager.data_directory)) voice_player.player = client.voice.create_ffmpeg_player( '{}/tempsound'.format(configmanager.data_directory)) except: raise bot_exception( EXCEPT_TYPE, "An error occurred when downloading the sound file") voice_player.player.start() timeout = configmanager.config['voice_timeout'] if timeout >= 0: timeout_reset_lock.acquire() timeout_goal = time.time() + ((timeout * 60) if timeout > 0 else (sound_tag_data['length'] + 1)) timeout_reset_lock.release() await timeout_disconnect()
def update_sound_tag(server_id, sound_tag_name, increment_hits=False, **kwargs): """Updates or creates a tag in the server under the given author ID. Keyword arguments: increment_hits -- increments the hits counter of the tag user_id -- user trying to modify the tag author_id -- author of the tag duration -- how long the sound should play for url -- url of the audio source private -- whether or not the tag can only be called by the author hits -- how many times the tag has been called type -- tag type (either YouTube or direct) length -- how long the sound tag is in seconds full_name -- what the full name (with spaces) is """ to_return = '' full_name = sound_tag_name sound_tag_name = sound_tag_name.replace(' ', '') servers_data = servermanager.servers_data if increment_hits: # Updating hit counter servers_data[server_id]['sound_tags'][sound_tag_name]['hits'] += 1 else: # Creating or modifying a tag if kwargs['url'].startswith('https://www.youtube.com/') or kwargs['url'].startswith('https://youtu.be/'): kwargs['type'] = 'YouTube' else: kwargs['type'] = 'direct' # Check tag length if kwargs['type'] == 'YouTube': # Find length of YouTube video try: length = pafy.new(kwargs['url']).length except: raise bot_exception(EXCEPT_TYPE, "Invalid YouTube video") else: # Download file and check length try: urllib.request.urlretrieve (kwargs['url'], '{}/tempsound'.format(configmanager.data_directory)) length = MP3('{}/tempsound'.format(configmanager.data_directory)).info.length except: raise bot_exception(EXCEPT_TYPE, "Invalid direct download file or URL") length_limit = int(configmanager.config['sound_tag_length_limit']) if length_limit > 0 and int(length) > length_limit: raise bot_exception(EXCEPT_TYPE, "Sound tags can be no longer than {} second{}".format( length_limit, 's' if length_limit > 1 else '')) kwargs['length'] = int(length) try: sound_tag_data = servers_data[server_id]['sound_tags'][sound_tag_name] try: check_sound_tag_access(server_id, sound_tag_data, kwargs['user_id'], need_owner=True) except KeyError: # user_id not found, meant to create but tag exists raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' already exists".format(sound_tag_name)) del kwargs['user_id'] # Don't write user_id to updated tag servers_data[server_id]['sound_tags'][sound_tag_name].update(kwargs) to_return += "Sound tag '{}' successfully modified!".format(full_name) except KeyError: # Tag doesn't exist. Create it. if configmanager.config['sound_tags_per_server'] > 0 and len(servers_data[server_id]['sound_tags']) >= configmanager.config['sound_tags_per_server']: raise bot_exception(EXCEPT_TYPE, "This server has hit the sound tag limit of {}".format(configmanager.config['sound_tags_per_server'])) if 'url' not in kwargs: raise bot_exception(EXCEPT_TYPE, "Sound tag '{}' does not exist".format(sound_tag_name)) if len(sound_tag_name) > 50: raise bot_exception(EXCEPT_TYPE, "Sound tag names cannot be larger than 50 characters long") if len(kwargs['url']) > 2000: # This shouldn't really happen, ever raise bot_exception(EXCEPT_TYPE, "Sound tag url cannot be larger than 2000 characters long (how did you do this)") # Edit safety if 'user_id' in kwargs: kwargs['author_id'] = kwargs['user_id'] del kwargs['user_id'] kwargs['full_name'] = full_name servers_data[server_id]['sound_tags'][sound_tag_name] = {**kwargs, 'hits':0, 'date_created':time.strftime("%c")} to_return += "Sound tag '{}' successfully created!".format(full_name) write_data() return to_return
async def parse(text, server_id, channel_id, author_id, voice_channel_id, is_private=False): """Parses the text to separate the command, options, and arguments, and passes them into the apropriate modules.""" is_admin = servermanager.is_admin(server_id, author_id) if not is_private else False text_partition, _, text = text[1:].partition(' ') # Remove invoker character text = text.strip() raw_parameters = text # Used for some functions (mostly shortcuts) command = text_partition.lower() option_invoker = configmanager.config['option_invoker'] # Look for options options = [] while text.startswith(option_invoker): option_partition, _, text = text[1:].partition(' ') text = text.strip() if option_partition: options.append(option_partition) # Look for arguments arguments = [] arguments_blocks = [] while text: if len(arguments_blocks) < 4: arguments_blocks.append(text) if text.startswith('"'): # Search for end quote try: text = text[1:] closed_quote_index = 0 next_quote_index = text[closed_quote_index:].index('"') # Maybe use do while? while text[closed_quote_index + next_quote_index - 1] == '\\': # Run until proper closed quote found closed_quote_index += next_quote_index text = text[:closed_quote_index - 1] + text[closed_quote_index:] next_quote_index = text[closed_quote_index:].index('"') except ValueError: raise bot_exception(EXCEPT_TYPE, "An argument has an unclosed quote") closed_quote_index += next_quote_index if closed_quote_index > 0: # No null argument to_append = text[:closed_quote_index].strip() if to_append: # No empty string arguments.append(to_append) text = text[closed_quote_index + 1:].strip() else: # If there is a null argument, remove it text = text[2:].strip() else: # Search for next space argument_partition, _, text = text.partition(' ') arguments.append(argument_partition) text = text.strip() # Debug printing print("DEBUG:\n{}\n{}\n{}\n{}\n------".format(command, options, arguments, arguments_blocks)) # Get proper response to_return = ['', False] # Text response, TTS get_response_function = help # Function placeholder # TODO: See if there is an object representation of modules if command in tagmanager.commands: # Tag manager get_response_function = getattr(tagmanager, 'get_response') elif command in soundtagmanager.commands: # Sound tag manager get_response_function = getattr(soundtagmanager, 'get_response') elif command in usermanager.commands: # User manager get_response_function = getattr(usermanager, 'get_response') elif command in decider.commands: # Decider get_response_function = getattr(decider, 'get_response') elif command in utilities.commands: # Utilities get_response_function = getattr(utilities, 'get_response') elif command in base_commands: # Base commands get_response_function = get_response if get_response_function != help: # Found a response function to_return[0] = await get_response_function( command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, author_id, is_admin, is_private) else: to_return[0] += "Sorry, I can't seem to do that right now. You should never see this, by the way. What is most likely going on is that you are hallucinating this error." if to_return[0] is not None and len(to_return[0]) > 1900: # Split responses up if it is too long to_return[0] = "```\n***Looks like the response is very large. It might look a little messed up because it's gettin' chopped up.***```\n" + to_return[0][:1900] return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Responds to the basic commands that are always available.""" to_return = '' # Base commands (sans admin) if command == 'ping': to_return += "Pong!\nOptions: {options}\nArguments: {arguments}\nArguments blocks: {arguments_blocks}".format( options = str(options), arguments = str(arguments), arguments_blocks = str(arguments_blocks)) elif command == 'help': if len(arguments) > 1: raise bot_exception(BASE_EXCEPT_TYPE, "You can only get help on one command") to_return += help(arguments[0] if len(arguments) == 1 else '') # Admin commands elif command == 'admin' and len(options) == 1 and not is_private: if not is_admin: raise bot_exception(BASE_EXCEPT_TYPE, "You must be an admin or the bot owner for these commands") if len(arguments) == 1 and not is_private: if options[0] in ['ban', 'unban']: # All checks here are explicit to enforce strict syntax to_return += servermanager.add_ban(server_id, usermanager.get_user_id(server_id, arguments[0]), add=(options[0] == 'ban')) elif options[0] in ['add', 'remove']: if not servermanager.is_owner(user_id): raise bot_exception(BASE_EXCEPT_TYPE, "You must be the bot owner for this command") to_return += servermanager.add_admin(server_id, usermanager.get_user_id(server_id, arguments[0]), add=(options[0] == 'add')) elif options[0] in ['mute', 'unmute']: to_mute = options[0] == 'mute' if arguments[0] in ['channel', 'voicechannel']: to_return += servermanager.mute_channel(server_id, channel_id if arguments[0] == 'channel' else voice_channel_id, mute=to_mute) elif arguments[0] == 'server': to_return += servermanager.mute_server(server_id, mute=to_mute) elif options[0] == 'info': if arguments[0] in ['channel', 'voicechannel']: to_return += servermanager.get_channel_info(server_id, channel_id if arguments[0] == 'channel' else voice_channel_id) elif arguments[0] == 'server': to_return += servermanager.get_server_info(server_id) elif len(arguments) == 0: if options[0] == 'version': to_return += '**`{version}`** ({date})'.format(version=botmanager.BOT_VERSION, date=botmanager.BOT_DATE) elif options[0] == 'ip': s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('8.8.8.8', 80)) # Thanks Google, you da real MVP ret = s.getsockname()[0] s.close() to_return += "Local IP: " + ret elif options[0] == 'halt': if not servermanager.is_owner(user_id): raise bot_exception(BASE_EXCEPT_TYPE, "You must be the bot owner for this command") print("Halting execution...") if not is_private: await botmanager.interrupt_broadcast(server_id, channel_id, "Going down...") await botmanager.disconnect_bot() await asyncio.sleep(2) sys.exit() elif options[0] == 'source': to_return += random.choice([ "It's shit. I'm sorry.", "You want to see what the Matrix is like?", "Script kiddie level stuff in here.", "Beware the lack of PEP 8 guidelines inside!", "Snarky comments inside and out.", "The last codebender. And he's shit at it.", "Years down the road, this will all just be a really embarrassing but funny joke.", "Made with ~~love~~ pure hatred.", "At least he's using version control."]) to_return += "\nhttps://github.com/TheJsh/JshBot" elif options[0] == 'clear': to_return += '```\n' for i in range(0, 80): to_return += '.\n' to_return += random.choice([ "Think twice before scrolling up.", "clear ver {}".format(botmanager.BOT_VERSION), "Can you find the one comma?", "Are people watching? If so, best not to scroll up.", "Don't worry, just censorship doing its thing.", "This is why we can't have nice things.", "The only one who can spam is ME.", "That made me feel a bit queasy...", "We need a better content filter. 18+ checks, maybe?", "You ANIMALS. At least I'm not one.", "Scroll up if you want to be on a list.", "I'll bet the NSA will have a fun time scrolling up.", "So much wasted space...", "This is pretty annoying, huh? Well TOO BAD.", "No time to delete!"]) to_return += '```\n' elif options[0] == 'ca': await botmanager.change_avatar() to_return += "Avatar changed" elif options[0] == 'cs': await botmanager.change_status() to_return += "Status changed" elif options[0] == 'uptime': uptime_total_seconds = int(time.time()) - botmanager.bot_turned_on_precise uptime_struct = time.gmtime(uptime_total_seconds) days = int(uptime_total_seconds / 86400) hours = uptime_struct.tm_hour minutes = uptime_struct.tm_min seconds = uptime_struct.tm_sec return "The bot has been on since **{initial}**\n{days} days\n{hours} hours\n{minutes} minutes\n{seconds} seconds".format( initial=botmanager.bot_turned_on_date, days=days, hours=hours, minutes=minutes, seconds=seconds) if to_return: return to_return else: # Invalid command raise bot_exception(BASE_EXCEPT_TYPE, "Invalid syntax") return to_return
async def get_response(command, options, arguments, arguments_blocks, raw_parameters, server_id, channel_id, voice_channel_id, user_id, is_admin, is_private): """Gets a response from the command and parameters.""" to_return = '' num_options = len(options) num_arguments = len(arguments) using_shortcut = command in commands_dictionary['shortcut_commands'] # Play sounds if (not using_shortcut and num_options == 0 and num_arguments >= 1): try: # For convenience, try with both raw parameters and single argument if num_arguments == 1: await play_sound_tag(server_id, voice_channel_id, arguments[0].lower().replace(' ', ''), user_id) return None except bot_exception: pass await play_sound_tag(server_id, voice_channel_id, arguments_blocks[0].lower().replace(' ', ''), user_id) return None # Create sound tag elif (num_arguments == 2 and ((command in ['stc'] and num_options == 0) or (not using_shortcut and (num_options == 1 or (num_options == 2 and options[1] in ['p', 'private'])) and options[0] in ['c', 'create']))): use_private = num_options == 2 and options[1] in ['p', 'private' ] # Private tag return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), url=arguments[1], author_id=user_id, private=use_private) # Remove sound tag elif (num_arguments >= 1 and ((command in ['str'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['r', 'remove']))): sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return remove_sound_tag(server_id, sound_tag_name, user_id) # List sound tags elif ((command in ['stl'] and num_options == 0) or (not using_shortcut and num_options == 1 and num_arguments <= 1 and options[0] in ['l', 'list'])): return list_sound_tags( server_id, user_id=usermanager.get_user_id(server_id, arguments[0]) if num_arguments == 1 else '') # Search sound tags elif (num_arguments >= 1 and ((command in ['sts'] and num_options == 0) or (not using_shortcut and num_options == 1 and options[0] in ['s', 'search']))): sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return search_sound_tags(server_id, sound_tag_name) # Sound tag info elif not using_shortcut and num_options == 1 and num_arguments >= 1 and options[ 0] in ['i', 'info']: sound_tag_name = (arguments[0].lower() if num_arguments == 1 else arguments_blocks[0].lower()).replace(' ', '') return get_sound_info(server_id, sound_tag_name) # Edit sound tag elif not using_shortcut and (num_options in range( 1, 3)) and num_arguments <= 2 and options[0] in ['e', 'edit']: if num_options == 2 and num_arguments == 1: # Set private or public if options[1] == 'setpublic': # Explicit to follow a strict syntax return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=False) elif options[1] == 'setprivate': return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, private=True) elif num_options == 1 and num_arguments == 2: # Modify sound tag url return update_sound_tag(server_id, sound_tag_name=arguments[0].lower(), user_id=user_id, url=arguments[1], private=False) # Stop sounds elif ((command in ['stfu'] and num_arguments == 0 and num_options == 0) or (not using_shortcut and num_options == 1 and num_arguments == 0 and options[0] in ['stop', 'silence', 'silent', 'fu'])): return await stop_sounds(server_id) # Invalid command raise bot_exception(EXCEPT_TYPE, "Invalid syntax", get_formatted_usage_string())