async def code(self, ctx): """ code command Parameters ------------ ctx : discord.context The message context object """ try: (arguments, message) = parse.args(ctx.message.content) # Explain all the arguments to the user if "help" in arguments or (len(message) == 0 and len(arguments) == 0): title = "!code - User Guide" description = ( "Remote code execution. Allows users to write code and submit it to the bot for " + "remote execution, after which the bot prints out the results, any error codes, " + "the execution time, and memory consumption. The command supports 65 different " + "languages, including Java, Python 2/3, PHP, Swift, C++, Rust, and Go.\n\nTo invoke " + "execution, include a block of code using the multi-line Discord code format " + "( https://support.discordapp.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-" + "Formatting-Bold-Italic-Underline- ). To use this formatting, use three backticks " + "(`, the key above the tab key), followed immediately (no spaces!) by the language " + "tag, followed by a linebreak, followed by your code. Close off the code block with " + "three more backticks. It's a little complicated, I apologize. It's Discord's " + "formatting rules, not mine.\n\n**Be advised**\nRemote execution will time out after " + "5 seconds and does not support external libraries or access to the internet." ) helpdict = { "-help": "Shows this user guide", "-full": "Shows the full list of supported languages", "-lang": "Shows the common languages supported by this command", } await self.bot.say(embed=utils.embedfromdict( helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["code"])) return if "ls" in arguments or "full" in arguments: message = "`Name` (`compiler version`) - `tag`\n---------" for langname in self.langs.keys(): name = self.langs[langname][0] # Name message += "\n**" + name + "** : " + langname if "full" in arguments: message += "\n---------" for langname in self.esolangs.keys(): name = self.esolangs[langname][0] # Name message += "\n**" + name + "** : " + langname await self.bot.say(utils.trimtolength(message, 2000)) return if "dev" in arguments: [json, resp_code ] = await utils.get_json_with_post(url=self.credits_check_url, json={ "clientId": self.JDOODLE_ID, "clientSecret": self.JDOODLE_SECRET }) if resp_code != 200: await utils.report(self.bot, "Failed to check credits\n" + str(json), source="!code dev", ctx=ctx) return if "used" not in json.keys(): await utils.report( self.bot, json, "Failed to get credit count for JDOODLE account", ctx=ctx) await self.bot.say( "Forces external to your request have caused this command to fail." ) return await self.bot.say(json['used']) return if "```" in message: trimmedmessage = message[message.find("```") + 3:] if "```" in trimmedmessage: trimmedmessage = trimmedmessage[:trimmedmessage.find("```" )] splitmessage = trimmedmessage.split("\n", maxsplit=1) if len(trimmedmessage) == 0: await self.bot.say( "You need to put code inside the backticks") return if trimmedmessage[0] not in [" ", "\n" ] and len(splitmessage) > 1: [language, script] = splitmessage language = language.strip() for key in self.esolangs.keys(): self.langs[key] = self.esolangs[key] if language.lower() in self.langs.keys(): response = await utils.get_json_with_post( url=self.execute_url, json={ "clientId": self.JDOODLE_ID, "clientSecret": self.JDOODLE_SECRET, "script": script, "language": self.langs[language.lower()][1], "versionIndex": self.langs[language.lower()][2] }) [json, resp_code] = response if resp_code == 429: await utils.report( self.bot, json, "Bot has reached its JDOODLE execution limit", ctx=ctx) await self.bot.say( "The bot has reached its code execution limit for the day." ) return output_embed = Embed() if "error" in json.keys(): output_embed.description = json['error'] output_embed.title = "ERROR" output_embed.colour = EMBED_COLORS["error"] await self.bot.say(embed=output_embed) return output_embed.title = "Output" output_embed.colour = EMBED_COLORS["code"] output_embed.add_field(name="CPU Time", value=str(json['cpuTime']) + " seconds") output_embed.add_field(name="Memory Usage", value=json['memory']) if len(json['output']) > 2046: output_embed.description = "``` " + utils.trimtolength( json['output'], 2040) + "```" else: output_embed.description = "``` " + json[ 'output'] + "```" await self.bot.say(embed=output_embed) else: await self.bot.say( "I don't know the language '" + language + "'. Type `!code -full` " + "to see the list of languages I support, or type `!code -ls` to see " + "the most popular ones") else: await self.bot.say( "There was no language tag. Remember to include the language tag " + "immediately after the opening backticks. Type `!code -ls` or " + "`!code -full` to find your language's tag") else: await self.bot.say("You didn't close your triple backticks" ) else: await self.bot.say("I don't see any code") except Exception as e: await utils.report(self.bot, str(e), source="!code command", ctx=ctx)
async def tag(self, ctx): """ Tag command Parameters ------------ ctx : discord.context The message context object """ try: # -------------------------------- SET-UP # Makes the apostrophe types consistent # (See function documentation for explanation) parsed_ctx = parse.apos(ctx.message.content) # Parses CLI arguments (arguments, message) = parse.args(parsed_ctx) # -------------------------------- ARGUMENTS # Acting on arguments if "help" in arguments or (len(message) == 0 and len(arguments) == 0): title = "!tag - User Guide" description = ( "Bot call and response. Allows the user to pair a message or attached image with a tag. " + "These tags can then be used to have the bot respond with the associated content. By " + "default, these tags are server wide, but by using the user list argument (`-u`, e.g. " + "`!tag -u [base] All your base are belong to us`) the user can specify personal tags. " + "This allows a user to store a different value for a key than the server value and to " + "make tags that will be available for that user across servers and in DMs.") helpdict = { "-ap [<key>] <value>": "If the tag entered already exists, the " + "new text will be appended to the end after a space", "-apnl [<key>] <value>": "If the tag entered already exists, " + "the new text will be appended to the end after a line break", "-edit [<key>] <value>": "If the tag entered already exists, " + "the existing tag will be overwritten", "-help": "Show this guide", "-ls": "Lists the tags within the selected group. Overrides any " + "other argument", "-rm <key>": "removes a tag from the group", "-u": "Selects your specific tag group instead of the server " + "tags for the following command"} await self.bot.say(embed=utils.embedfromdict(helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["tag"])) return # Rejects ambiguous argument pairs if "rm" in arguments and "edit" in arguments: await self.bot.say("`-rm` and `-edit` are incompatible arguments") return elif ("apnl" in arguments or "ap" in arguments) and ("edit" in arguments or "rm" in arguments): await self.bot.say("`ap` or `apnl` are incompatible with the arguments `-rm` or `-edit`") return elif "ap" in arguments and "apnl" in arguments: await self.bot.say("`-ap` and `-apnl` is an ambiguous argument pair") return # flag for if the user is overwriting an existing tag edit = False # flag for if the user is appending to an existing tag append = False # flag for if the user wants a line break before their append is added newline = False # String specifying tag domain domain = "server" if ctx.message.server is None or "u" in arguments: domain = "user" # MySQL parameter to specify owner of the tag tagowner = ctx.message.author.id # if user does not have a user tag group if ctx.message.author.id not in self.tags["user"].keys(): # Creates a user tag group self.tags["user"][ctx.message.author.id] = {} # Changes the selected tag group to the user's tags selected_tags = self.tags["user"][ctx.message.author.id] else: # Selecting tag group (default is 'server') if ctx.message.server.id not in self.tags["server"].keys(): self.tags["server"][ctx.message.server.id] = {} # Gets domain tag group selected_tags = self.tags["server"][ctx.message.server.id] # MySQL parameter to specify owner of the tag tagowner = ctx.message.server.id # Adds global tags to server tag group for key in self.tags["global"].keys(): selected_tags[key] = self.tags["global"][key] # List the saved tags in the selected tag group if "ls" in arguments: taglist = "" for tagkey in sorted(selected_tags.keys()): if tagkey in self.tags["global"].keys(): taglist += ", `" + tagkey + "`" else: taglist += ", " + tagkey if taglist == "": if domain == "user": await self.bot.say("You do not have any saved tags") else: await self.bot.say("This server does not have any saved tags") return taglist = taglist[2:] # pulls the extraneous comma off await self.bot.say("**The tags I know are**\n" + taglist + "") return # Reject empty messages (`-ls` calls have already been handled) if len(message) == 0: await self.bot.say("I see some arguments `` " + str(arguments) + " ``, but no key or value to work with :/") return # Deletes a saved tag if "rm" in arguments: key = message.lower() if key in selected_tags.keys(): del selected_tags[key] await self.bot.say("Okay. I deleted it") self.update_tag_remove(key, tagowner, domain) else: # If that tag didn't exist await self.bot.say("Hmmm, that's funny. I didn't see the tag `` " + message + " `` in the saved tags list.") return # ------ Set or Get ------ # Check for modification flags if any(edit_arg in arguments for edit_arg in ["edit", "apnl", "ap"]): edit = True # Allows modification of existing tag if any(append_arg in arguments for append_arg in ["apnl", "ap"]): append = True # Adds new text to end of existing tag if "apnl" in arguments: newline = True # Adds a new line before appending text # Setting if message[0] is "[": tag_keyvalue = parse.key_value(message, ctx.message.attachments) if tag_keyvalue[0] is None: error_messages = {"EMPTY KEY": "There was no key to store", "UNCLOSED KEY": "You didn't close your brackets", "NO VALUE": "There was no text to save for the key provided", "WHITESPACE KEY": "Just because this bot is written in Python " + "does not mean whitespace is an acceptable tag", "KEY STARTS WITH -": "The `-` character denotes the start of " + "an argument and cannot be used in tag keys"} await self.bot.say(error_messages[tag_keyvalue[1]]) return else: tagkey = tag_keyvalue[0].lower() tagvalue = tag_keyvalue[1] if tagkey in selected_tags.keys(): if tagkey in self.tags["global"].keys(): await self.bot.say( "I'm sorry, but the key `` " + tagkey + " `` has already been reserved for a global tag") return elif edit is False: await self.bot.say("I already have a value stored for the tag `` " + tagkey + " ``. Add `-edit` to overwrite existing self.tags") return elif append is True: if newline is True: selected_tags[tagkey] = selected_tags[tagkey] + "\n" + tagvalue else: selected_tags[tagkey] = selected_tags[tagkey] + " " + tagvalue self.update_tag_edit(tagkey, selected_tags[tagkey], tagowner, domain) await self.bot.say("Edited!") return else: selected_tags[tagkey] = tagvalue self.update_tag_edit(tagkey, tagvalue, tagowner, domain) await self.bot.say("Edited!") return selected_tags[tagkey] = tagvalue self.update_tag_add(tagkey, tagvalue, tagowner, domain) await self.bot.say("Saved!") # Getting else: key = message.lower() if key in selected_tags.keys(): await self.bot.say(utils.trimtolength(selected_tags[key], 2000)) elif domain == "user": await self.bot.say("I don't think I have a tag `" + key + "` stored for you. Type `!tag -u -ls` to see the self.tags I have " + "saved for you") else: await self.bot.say("I don't think I have a tag `" + key + "`. Type `!tag -ls` to see the tags " + "I have saved for this server") except Exception as e: await utils.report(self.bot, str(e), source="Tag command", ctx=ctx)
async def dev(ctx): global currently_playing try: if ctx.message.author.id not in AUTHORIZED_IDS: await bot.say("You are not authorized to use these commands") return [func, parameter] = parse.func_param(ctx.message.content) if func in ["help", ""]: title = "`!dev` User Guide" description = "A list of features useful for " helpdict = { "channelid": "Posts the ID of the current channel", "colton": "Check the `!colton` data", "dump": "A debug command for the bot to dump a variable into chat", "flag": "Tests the `flag` function", "load": "Loads an extension", "playing": "Sets the presence of the bot (what the bot says it's currently playing)", "reload": "Reloads an extension", "report": "Tests the `report` function", "serverid": "Posts the ID of the current channel", "swears": "Find out who swears the most", "test": "A catch-all command for inserting code into the bot to test", } await bot.say("`!dev` User Guide", embed=embedfromdict(helpdict, title=title, description=description)) elif func == "channelid": await bot.say("Channel ID: " + ctx.message.channel.id) elif func == "colton": await bot.say( "Colton has mentioned being forever alone {} times.\n" "The last time he mentioned being forever alone was **{}**". format(total_colton, last_colton.strftime("%c"))) elif func == "dump": await bot.say("AT THE DUMP") elif func == "flag": await bot.say("Triggering flag...") await utils.flag(bot, "Test", description="This is a test of the flag ability", ctx=ctx) elif func == "load": """Loads an extension.""" try: bot.load_extension("cogs." + parameter) except (AttributeError, ImportError) as e: await utils.report(bot, "```py\n{}: {}\n```".format( type(e).__name__, str(e)), source="Loading extension (!dev)", ctx=ctx) return await bot.say("`` {} `` loaded.".format(parameter)) elif func == "nick": try: if ctx.message.server is None: await bot.say("I can't do that here") return newnick = parameter if newnick == "": newnick = None botmember = ctx.message.server.get_member( "340287898433748993") # Gets the bot's own member object await bot.change_nickname(botmember, newnick) except Exception as e: await utils.report(bot, str(e), source="!dev nick", ctx=ctx) elif func == "playing": try: currently_playing = parameter await bot.change_presence(game=discord.Game( name=currently_playing)) utils.update_cache(bot.dbconn, "currPlaying", currently_playing) await bot.say("I'm now playing `" + parameter + "`") except discord.InvalidArgument as e: await utils.report(bot, "Failed to change presence to `" + parameter + "`\n" + str(e), source="dev playing", ctx=ctx) elif func == "serverid": await bot.say("Server ID: " + ctx.message.server.id) elif func == "swears": sortedlist = sorted(list(swear_tally.items()), key=lambda user_tally: user_tally[1][2], reverse=True) message = "" for i, user in enumerate(sortedlist): message += ("**" + str(i + 1) + ".** " + bot.users[user[0]] + " - " + str(round(user[1][2] * 100, 2)) + "%\n") if len(message) > 0: await bot.say(utils.trimtolength(message, 2000)) elif func == "reload": bot.unload_extension(parameter) await bot.say("`` {} `` unloaded.".format(parameter)) try: bot.load_extension("cogs." + parameter) except (AttributeError, ImportError) as e: await utils.report(bot, "```py\n{}: {}\n```".format( type(e).__name__, str(e)), source="Loading extension (!dev)", ctx=ctx) return await bot.say("`` {} `` loaded.".format(parameter)) elif func == "report": await bot.say("Triggering report...") await utils.report(bot, "This is a test of the report system", source="dev report command", ctx=ctx) elif func == "test": try: await bot.say("hello") except Exception as e: await utils.report(bot, str(e), source="dev test", ctx=ctx) elif func == "unload": """ Unoads an extension """ bot.unload_extension(parameter) await bot.say("`` {} `` unloaded.".format(parameter)) else: await bot.say("I don't recognize the command `" + func + "`. You can type `!dev` for a list of " + "available functions") except Exception as e: await utils.report(bot, str(e), source="dev command", ctx=ctx)
async def wiki(self, ctx): # ---------------------------------------------------- HELPER METHODS # Find an article with a matching title async def search_for_term(term): wiki_search_url = ( "http://en.wikipedia.org/w/api.php?action=query&format=json" + "&prop=&list=search&titles=&srsearch=" + quote(term)) # Looks for articles matching the search term wiki_search_json = await utils.get_json_with_get(wiki_search_url) if wiki_search_json[0]['query']['searchinfo']['totalhits'] == 0: return None return wiki_search_json[0]['query']['search'][0]['title'] # Gets the details of an article with an exact URL title (not always the same as the article title) # Returns a tuple of the article's title, its extract, and a brief description of the subject # Returns None if no article was found or the result has no extract async def query_article(querytitle): # Makes the title URL friendly quoted_article_title = quote(querytitle) wiki_query_url = ( "http://en.wikipedia.org/w/api.php?action=query&format=json" + "&prop=info%7Cextracts%7Cdescription&titles=" + quoted_article_title + "&exlimit=max&explaintext=1&exsectionformat=plain") # Gets the article details response = await utils.get_json_with_get(wiki_query_url) # If Wikipedia found nothing if "-1" in response[0]['query']['pages'].keys(): return None # Gets the first result response = list(response[0]['query']['pages'].values())[0] # If the returned result isn't usable if "extract" not in response.keys(): await utils.report( self.bot, utils.trimtolength(response, 2000), source="Wiki command found no useful article", ctx=ctx) return None response_title = response['title'] response_extract = response['extract'] if 'description' in response.keys(): response_description = response['description'] else: response_description = None return response_title, response_extract, response_description # ---------------------------------------------------- COMMAND LOGIC try: # WIKI -------------------------------- SET-UP (arguments, search_term) = parse.args(ctx.message.content) # WIKI -------------------------------- ARGUMENTS # Act on arguments if "help" in arguments: # provides help using this command title = "`!wiki` User Guide" description = "Wikipedia search engine. Searches wikipedia for an article with the title provided " \ "and returns the opening section or the full article text. Additionally, it can " \ "return the sections of an article or the text of a designated section." helpdict = { "-help": "Displays this user guide. Gives instructions on how to use the command and its features", "-full <title>": "Displays the full extract of the article, up to the embed character limit" } await self.bot.say(embed=utils.embedfromdict( helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["wiki"])) return # Performs article search if search_term == "": await self.bot.say("I need a term to search") return article_title = await search_for_term(search_term) if article_title is None: await self.bot.say("I found no articles matching the term '" + search_term + "'") return # Shows the text of the article searched for [title, extract, summary] = await query_article(article_title) wiki_embed = Embed() wiki_embed.title = title wiki_embed.set_footer( text="Wikipedia", icon_url= "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/" "Wikipedia-logo-v2.svg/800px-Wikipedia-logo-v2.svg.png") if "full" in arguments or "f" in arguments: description = utils.trimtolength(extract, 2048) else: description = utils.trimtolength(extract[:extract.find("\n")], 2048) wiki_embed.description = description wiki_embed.add_field(name="Summary", value=summary, inline=False) wiki_embed.add_field(name="Full Article", value="http://en.wikipedia.org/wiki/" + quote(title), inline=False) wiki_embed.colour = EMBED_COLORS['wiki'] # Make the embed white await self.bot.say(embed=wiki_embed) except Exception as e: await utils.report(self.bot, str(e), source="Wiki command", ctx=ctx)
async def dev(ctx): global currently_playing try: if ctx.message.author.id not in AUTHORIZED_IDS: await bot.say("You are not authorized to use these commands") return [func, parameter] = parse.func_param(ctx.message.content) if func in ["help", ""]: title = "`!dev` User Guide" description = "A list of features useful for " helpdict = { "channelid": "Posts the ID of the current channel", "dump": "A debug command for the bot to dump a variable into chat", "flag": "Tests the `flag` function", "load": "Loads an extension", "playing": "Sets the presence of the bot (what the bot says it's currently playing)", "reload": "Reloads an extension", "report": "Tests the `report` function", "serverid": "Posts the ID of the current channel", "test": "A catch-all command for inserting code into the bot to test", } await bot.say("`!dev` User Guide", embed=embedfromdict(helpdict, title=title, description=description)) elif func == "channelid": await bot.say("Channel ID: " + ctx.message.channel.id) elif func == "dump": await bot.say("hello") elif func == "flag": await bot.say("Triggering flag...") await utils.flag(bot, "Test", description="This is a test of the flag ability", ctx=ctx) elif func == "load": """Loads an extension.""" try: bot.load_extension("cogs." + parameter) except (AttributeError, ImportError) as e: await utils.report(bot, "```py\n{}: {}\n```".format( type(e).__name__, str(e)), source="Loading extension (!dev)", ctx=ctx) return await bot.say("`` {} `` loaded.".format(parameter)) elif func == "nick": try: if ctx.message.server is None: await bot.say("I can't do that here") return new_nick = parameter if new_nick == "": new_nick = None bot_member = ctx.message.server.get_member(tokens["CLIENT_ID"]) await bot.change_nickname(bot_member, new_nick) except Exception as e: await utils.report(bot, str(e), source="!dev nick", ctx=ctx) elif func == "playing": try: currently_playing = parameter await bot.change_presence(game=discord.Game( name=currently_playing)) utils.update_cache(bot.dbconn, "currPlaying", currently_playing) await bot.say("I'm now playing `" + parameter + "`") except discord.InvalidArgument as e: await utils.report(bot, "Failed to change presence to `" + parameter + "`\n" + str(e), source="dev playing", ctx=ctx) elif func == "serverid": await bot.say("Server ID: " + ctx.message.server.id) elif func == "reload": bot.unload_extension(parameter) await bot.say("`` {} `` unloaded.".format(parameter)) try: bot.load_extension("cogs." + parameter) except (AttributeError, ImportError) as e: await utils.report(bot, "```py\n{}: {}\n```".format( type(e).__name__, str(e)), source="Loading extension (!dev)", ctx=ctx) return await bot.say("`` {} `` loaded.".format(parameter)) elif func == "report": await bot.say("Triggering report...") await utils.report(bot, "This is a test of the report system", source="dev report command", ctx=ctx) elif func == "test": try: await bot.say("hello") except Exception as e: await utils.report(bot, str(e), source="dev test", ctx=ctx) elif func == "unload": """ Unoads an extension """ bot.unload_extension(parameter) await bot.say("`` {} `` unloaded.".format(parameter)) else: await bot.say("I don't recognize the command `" + func + "`. You can type `!dev` for a list of " + "available functions") except Exception as e: await utils.report(bot, str(e), source="dev command", ctx=ctx)
async def anime(self, ctx): """ anime command Parameters ------------ ctx : discord.context The message context object """ try: # Gets the arguments from the message (arguments, searchterm) = parse.args(ctx.message.content) if "dev" in arguments: await self.bot.say(self.character_synonyms) return # Explain all the arguments to the user if "help" in arguments or searchterm == "help": title = "!anime - User Guide" description = ( "A search tool for anime shows and characters. This command uses the AniList.co API to " + "return details on the search term provided for the show/character whose name is provide " + "with the command (e.g. `!anime Kill la Kill`). For shows, information like a show " + "description, air date, rating, and genre information will be returned. For characters, " + "the bot provides a description, their nicknames, and the media they've been in.\n\nThe " + "bot uses a synonym engine to make it easier to find popular characters and allow for " + "searching for entries using unofficial nicknames (e.g. `!anime klk` redirects to " + "`!anime Kill la Kill`). The bot's synonym table can be modified by users to allow for a " + "more complete table of synonyms") helpdict = { "<search>": "Searches the database for an anime", "-help": "Displays this message", "-add [<Synonym>] <Value>": ("Adds a synonym to the list. Will search for " + "`Value` when a user types `Synonym`. Can be used " + "with the `-char` command to create character " + "synonyms. `<Synonym>` also supports semicolon-" + "delineated lists"), "-char <search>": "Searches the database for a character page", "-info <search>": ("Shows the complete details for an anime. Has no effect on " + "`-char` searches"), "-ls": ("Lists the anime synonym table. If used with the `-char` command, it " + "lists the character synonym table"), "-raw <search>": "Disables synonym correction for search terms", "-remove <Search Value>": ("Removes a synonym from the list. Can be used with " + "the `-char` command to remove character synonyms") } await self.bot.say(embed=utils.embedfromdict( helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["anime"])) return if "ls" in arguments: if "char" in arguments: searchdict = self.character_synonyms title = "Character name synonyms" else: searchdict = self.anime_synonyms title = "Anime title synonyms" message = "" for changeto in sorted(list(searchdict.keys())): message += "**" + changeto + "** <- " + ", ".join( searchdict[changeto]) + "\n" embed = Embed() embed.title = title embed.description = utils.trimtolength(message, 2040) embed.colour = EMBED_COLORS["anime"] await self.bot.say(embed=embed) return if "add" in arguments and "remove" in arguments: await self.bot.say( "I don't know how you possibly expect me to both add and " + "remove a synonym in the same command") if "char" in arguments: synonymdict = self.character_synonyms searchtype = "character" else: synonymdict = self.anime_synonyms searchtype = "anime" # Add search synonym if "add" in arguments: tag_kv = parse.key_value(searchterm) if tag_kv[0] is None: errormessages = { "EMPTY KEY": "There was no synonym for the search value", "UNCLOSED KEY": "You didn't close your brackets", "NO VALUE": "There was no search value to save for the synonym", "WHITESPACE KEY": ("Just because this bot is written in Python does not mean whitespace is an " + "acceptable synonym"), "KEY STARTS WITH -": ("The `-` character denotes the start of an argument and cannot be used to " + "start a synonym") } await self.bot.say(errormessages[tag_kv[1]]) return changefrom = tag_kv[0].lower() changeto = tag_kv[1] collision = checkforexistingsynonym(changefrom, synonymdict) if collision is None: if changeto not in synonymdict.keys(): synonymdict[changeto] = list() changefromlist = changefrom.split(";") for element in changefromlist: synonymdict[changeto].append(element.strip()) self.add_synonym(searchtype.upper(), changeto, element) await self.bot.say("All " + searchtype + " searches for `" + "` or `".join(changefromlist) + "` will now correct to `" + changeto + "`") else: await self.bot.say( "The synonym `` {} `` already corrects to `` {} ``. Pick a different " + "word/phrase or remove the existing synonym with the command " + "``!anime -remove {} ``".format( changefrom, collision, changefrom)) return # Remove search synonym if "remove" in arguments: correction = checkforexistingsynonym(searchterm, synonymdict) if correction is not None: synonymdict[correction].remove(searchterm) if len(synonymdict[correction]) == 0: del synonymdict[correction] self.remove_synonym(searchtype.upper(), searchterm) await self.bot.say("Alright, `" + searchterm + "` will no longer correct to `" + correction + "` for " + searchtype + " searches") else: await self.bot.say(( "The synonym you searched for does not exist. Check your use " + "(or lack thereof) of the `-char` command, or use the `-ls` command to check " + "that everything is spelled correctly")) return if searchterm == "": await self.bot.say( "I don't see a search term to look up. Type `!anime -help` for a user guide" ) return embedgenerator = None if "char" in arguments: if "raw" not in arguments: for key in self.character_synonyms.keys(): if searchterm.lower() in self.character_synonyms[key]: searchterm = key char_var = {'name': searchterm} [json, status] = await utils.get_json_with_post(self.api_url, json={ 'query': self.char_query, 'variables': char_var }) if "json" in arguments: await self.bot.say(utils.trimtolength(json, 2000)) if status == 200: embedgenerator = Character(json['data']['Character']) else: if "raw" not in arguments: for key in self.anime_synonyms.keys(): if searchterm.lower() in self.anime_synonyms[key]: searchterm = key anime_var = {'title': searchterm} [json, status] = await utils.get_json_with_post(self.api_url, json={ 'query': self.anime_query, 'variables': anime_var }) if "json" in arguments: await self.bot.say(utils.trimtolength(json, 2000)) if status == 200: embedgenerator = Anime(json['data']['Media']) if status != 200: if status == 500: await utils.flag(self.bot, "500 Server Error on search term " + searchterm, description=str(json), ctx=ctx) await self.bot.say( "`500 Server Error`. The AniList servers had a brief hiccup. Try again in a little bit" ) elif status == 404: await utils.flag(self.bot, "Failed to find result for search term " + searchterm, description=str(json), ctx=ctx) await self.bot.say("I found no results for `" + searchterm + "`") elif status == 400: await utils.report( self.bot, str(json), source="Error in `!anime` search for term " + searchterm, ctx=ctx) await self.bot.say( "The bot made an error. My bad. A bug report has been automatically submitted" ) else: await utils.report(self.bot, str(json), source="Unknown Error Type", ctx=ctx) await self.bot.say( "Something went wrong and I don't know why") return if "info" in arguments: await self.bot.say(embed=embedgenerator.info_embed()) else: await self.bot.say(embed=embedgenerator.embed()) except Exception as e: await utils.report(self.bot, str(e), source="!anime command", ctx=ctx)
async def say(self, ctx): """ Makes SuitsBot say a stored audio clip in voice via a tag system. (e.g. `!say anthem`) The command also supports several CLI arguments for different tasks "-help": Shows the command's help embed "-ls": Lists all audio clip tags "-stop": Stops the current audio clip Parameters ------------ ctx : discord.context The message context object """ # List of quote files quotes = { "ah f**k": ["Ah f**k. I can't believe you've done this", "ah-f**k.mp3"], "anthem": [ "**SOYUZ NERUSHIMY RESPUBLIK SVOBODNYKH SPLOTILA NAVEKI VELIKAYA RUS'!**", "anthem.mp3" ], "austin": ["IT'S ME, AUSTIN!", "itsMeAustin.mp3"], "beat my dick": [ "Good evening Twitter, it's ya boi EatDatPussy445.", "beatTheFuck.wav" ], "boi": ["B O I", "boi.mp3"], "bold strategy": [ "It's a bold strategy cotton, let's see if it pays off for 'em", "bold-strategy-cotton.mp3" ], "careless whisper": ["*sexy sax solo intensifies*", "careless_whispers.mp3"], "cavalry": ["*britishness intensifies*", "cheersLove.ogg"], "deja vu": ["Ever get that feeling of deja vu?", "dejaVu.ogg"], "disco": [ "Reminder: You can stop media using the `!say -stop` command", "platinumDisco.mp3" ], "do it": ["*Do it*", "doIt.mp3"], "everybody": ["Se *no*!", "everybody.wav"], "ftsio": ["F**k this shit I'm out", "f**k-this-shit-im-out.mp3"], "f**k you": ["**F**k yoooooou!**", "fuckYou.mp3"], "gfd": ["God *f*****g* dammit!", "gfd.mp3"], "hentai": ["It's called hentai, and it's *art*", "itsCalledHentai.mp3"], "hello darkness": ["*Hello darkness my old friend...*", "helloDarkness.mp3"], "hello there": ["**GENERAL KENOBI**", "hello_there_obi.mp3"], "heroes never die": ["Heroes never die!", "heroesNeverDie.ogg"], "high noon": ["It's hiiiiiigh nooooooon...", "itsHighNoon.ogg"], "how": ["**I MADE MY MISTAKES**", "howCould.mp3"], "i tried so hard": ["Woah there, don't cut yourself on that edge", "inTheEnd.mp3"], "it was me": ["Ko! No! Dio! Da!", "itWasMeDio.mp3"], "laser sights": ["*Fooking laser sights*", "fookin-laser-sights.mp3"], "leroy": ["LEEEEEEROOOOOOOOOOOOOY", "leroy.mp3"], "love": [ "AND IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...", "iWillAlwaysLoveYou.mp3" ], "my power": [ "*You underestimate my power!*", "you-underestimate-my-power.mp3" ], "nerf this": ["It's nerf or nothing", "nerfThis.ogg"], "nani": ["NANI SORE!?", "nani-sore.mp3"], "nico": [("Nico Nico-nii~ Anata no Heart ni Nico Nico-nii, Egao todokeru Yazawa " + "Nico Nico~ Nico-nii te oboeteru Love Nico~ XD"), "nico_nico_nii.mp3"], "nyan": [("Naname nanajyuunana-do no narabi de nakunaku inanaku " + "nanahan nanadai nannaku narabete naganagame.\nOwO"), "nyan.mp3"], "omg": ["OH MY GOD!", "omg.mp3"], "oof": ["OOF!", "oof.mp3"], "over 9000": ["It's over **9000!!!**", "over9000.mp3"], "pingas": ["Pingas.", "pingas.mp3"], "rimshot": ["Badum, tiss", "rimshot.mp3"], "roundabout": ["To be continued...", "roundabout.mp3"], "sanic": ["goTtA gO faSt!", "sanic.mp3"], "satania": ["BWAHAHAHA!", "sataniaLaugh.mp3"], "sob": ["SON OF A BITCH!", "sob.mp3"], "somebody": ["What are you doing in my swamp?!", "somebodyClipping.wav"], "star destroyers": ["**IT BROKE NEW GROUND**", "starDestroyers.mp3"], "stop": ["It's time to stop!", "stop.mp3"], "take a sip": ["Take a f****n' sip, babes...", "takeASip.mp3"], "tea": ["I've got f*****g tea, ya dickhead!", "gotTea.wav"], "trash": ["**Endless trash**", "Endless Trash.mp3"], "tuturu": ["TUTURUUUUUUUU", "tuturu.mp3"], "violin": ["*sadness intensifies*", "sadViolin.mp3"], "wake me up": ["**I CAN'T WAKE UP**", "wakeMeUp.mp3"], "winky face": [":wink:", "winkyFace.ogg"], "woomy": ["Woomy!", "woomy.mp3"], "wow": ["Wow", "wow.mp3"], "wtf": ["**Hey Todd?!**", "whatTheFuck.mp3"], "yeah": ["*Puts on sunglasses*", "yeah.mp3"], "yes": ["YES YES YES YES... **YES**!", "yes.mp3"], "your way": ["Don't lose your waaaaaaaaay!", "dontloseyourway.mp3"], "zaworldo": ["ZA WARLDO!", "za_warudo.mp3"] } try: (arguments, key) = parse.args(ctx.message.content) # ------------------------------ NO AUDIO ARGUMENTS # Acting on arguments if "help" in arguments: # provides help using this command title = "`!say` User Guide" description = ( "Plays an audio clip in voice. If not already in the user's voice channel, the bot " + "will automatically join the voice channel of the user issuing the command. The bot " + "holds a list of stored audio files which can be summoned using predefined tags " + "(the list of tags can be viewed using the `!say -ls` command). If an audio clip is " + "currently playing, another tag cannot be started. If all users leave the audio " + "channel the bot is in, the bot will leave as well. If the user is not in a voice " + "channel, the command will be rejected") helpdict = { "<tag>": ("Plays the predetermined audio clip for that tag. Tags are case-insensitive, but make " + "sure your spelling is right!"), "-help": "Shows this list", "-ls": "Lists the tags for all the available audio clips", "-stop": "Stops the current voice clip" } await self.bot.say(embed=utils.embedfromdict( helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["say"])) return if "ls" in arguments: message = "The audio clips I know are: \n" for quoteKey in quotes.keys(): message += quoteKey + ", " await self.bot.say(message[:-2]) return if "stop" in arguments: if not self.bot.player.is_playing(): await self.bot.say("I'm not saying anything...") else: self.bot.player.stop() await self.bot.say("Shutting up.") return # ------------------------------ VALIDATING KEY if key == "": await self.bot.say( "You need to type the name of an audio clip for me to say. Type `!say -ls` for " + "a list of my audio clips or type `!say -help` for a full list of my arguments" ) return if key not in quotes.keys(): await self.bot.say("I don't see an audio clip tagged '" + key + "'. Type `!say -ls` for a list of tags") return # ------------------------------ AUDIO INITIALIZATION # Gets the voice channel the author is in author_voice_channel = ctx.message.author.voice_channel if author_voice_channel is None: # Ignores the command if the author is not in voice await self.bot.say("You are not in a voice channel right now") return # If the bot is connected to voice, if self.bot.is_voice_connected(ctx.message.server): voice_channel = self.bot.voice_client_in( ctx.message.server).channel # Does nothing if the bot is already in the author's voice channel if voice_channel != author_voice_channel: # Stops any active voice clip self.bot.player.stop() voice = self.bot.voice_client_in(ctx.message.server) # Moves the bot to the new channel await voice.move_to(author_voice_channel) else: await self.bot.join_voice_channel(author_voice_channel) # ------------------------------ PLAYING AUDIO # Ignores command if bot is already playing a voice clip if self.bot.player is not None and self.bot.player.is_playing(): await self.bot.say("Currently processing other voice command") return await self.bot.say(quotes[key][0] ) # Responds with the text of the voice clip voice = self.bot.voice_client_in( ctx.message.server) # Gets the active voice client player = voice.create_ffmpeg_player( self.quote_folder + quotes[key][1] ) # Gets the voice clip and creates a ffmpeg player self.bot.player = player # Assigns the player to the bot player.start() # Plays the voice clip except Exception as e: await utils.report(self.bot, str(e), source="Say command", ctx=ctx)
async def rand(self, ctx): try: # Extracts the function and parameter from the message [func, parameter] = parse.func_param(ctx.message.content) # !rand help if func in ["help", ""]: title = "!rand - User Guide" description = "Randomizer. This command is used for randomization in several circumstances. From " \ "coin flips and dice rolls to random numbers and picking from lists, let the bot " \ "generate random numbers for you.\n\n**WARNING**\nThis command is only pseudorandom " \ "and not cryptographically secure" helpdict = { "!rand": "No function. Will present this help list", "!rand help": "This command. Shows user guide", "!rand coin": "Flips a coin", "!rand item <A>, <B>, <C>...": "Returns a random item from a comma delineated list", "!rand num": "Returns a random decimal", "!rand num <A>": "Returns a random integer from 0 to A", "!rand num <A> <B>": "Returns a random integer between A and B", "!rand roll <num>d<sides>,...": "Rolls the number of n-sided dice presented. " "Multiple dice types can be rolled with a comma separated " "list" } await self.bot.say(embed=utils.embedfromdict( helpdict, title=title, description=description, thumbnail_url=COMMAND_THUMBNAILS["rand"])) return # !rand coin if func == "coin": await self.bot.say(utils.random_element(["Heads!", "Tails!"])) return # !rand item <item>, <item>, <item> if func == "item": if len(parameter) == 0: await self.bot.say( "I need a comma delineated list (e.g. '!random item A, B, C, D, E' etc.) " "to pick from") return itemlist = list(filter(None, parameter.split(","))) if len(itemlist) == 0: await self.bot.say( "There aren't any items here for me to choose from!") return elif len(itemlist) == 1: await self.bot.say( "There's only one item. That's an easy choice: " + itemlist[0]) return await self.bot.say("I choose... " + utils.random_element(itemlist).strip()) return # rand num # rand num <num> # rand num <num> <num> if func == "num" or func == "number": if len(parameter) == 0: await self.bot.say(str(random.random())) return numbers = parameter.split(" ") if len(numbers) == 1: try: bound = int(numbers[0]) except ValueError: await self.bot.say("I can't seem to parse '" + numbers[0] + "'") return await self.bot.say(str(random.randint(0, bound))) else: try: lowerbound = int(numbers[0]) except ValueError: await self.bot.say("I can't seem to parse '" + numbers[0] + "'") return try: upperbound = int(numbers[1]) except ValueError: await self.bot.say("I can't seem to parse '" + numbers[1] + "'") return if upperbound < lowerbound: temp = upperbound upperbound = lowerbound lowerbound = temp message = str(random.randint(lowerbound, upperbound)) if len(numbers) > 2: message += "\n\nFYI, this function takes a maximum of two only arguments" await self.bot.say(message) return # !rand roll <num>d<sides>, <num>d<sides> if func == "roll": dice = list(filter(None, parameter.split(","))) total = 0 message = "" for die in dice: die = die.strip() dloc = die.find("d") # if there is no "d" if dloc == -1: await self.bot.say( "I don't see a 'd' in the argument '" + die + "'.") return # if there is no number in front of the "d", it is assumed to be one if dloc == 0: count = "1" sides = die[1:] # if there is no number after the "d", the bot rejects it elif (dloc + 1) == len(die): await self.bot.say( "I don't see a number after 'd' in the argument '" + die + "'. I need to know a number of sides") return else: count = die[0:dloc] sides = die[dloc + 1:] try: sides = int(sides) except ValueError: await self.bot.say("I'm sorry, but '" + sides + "' isn't a parsable integer...") return try: count = int(count) except ValueError: await self.bot.say("I'm sorry, but '" + count + "' isn't a parsable integer...") return if count > 100000: await self.bot.say( str(count) + " dice is a *lot*. I think rolling that many would hurt " "my head :confounded:\nPlease don't make me do it." ) return dicesum = 0 for i in range(0, count): dicesum += random.randint(1, sides) total += dicesum message += str(count) + " d" + str( sides) + ": I rolled " + str(dicesum) + "\n" if len(dice) > 1: await self.bot.say(message + "Total: " + str(total)) else: await self.bot.say(message) return await self.bot.say( "I don't recognize the function `" + func + "`. Type `!rand help` for information on this command") except Exception as e: await utils.report(self.bot, str(e), source="Rand command", ctx=ctx)
async def handle_rss_feed(self, feed, ctx): """ The universal rss feed handler. Takes in an RSSFeed object then does all the magic :param feed: The RSSFeed :param ctx: The ctx object of the inciting message """ try: message = parse.stripcommand(ctx.message.content) # Update feed feed.refresh_if_stale() # Show most recent episode if message == "": episode = feed.items[0] await self.bot.say(embed=feed.get_embed(episode)) return # check for subcommand and parse it out subcommand = "" parameter = message if message[0] == "-": whitespace = utils.first_whitespace(message) if whitespace == -1: subcommand = message[1:] parameter = "" else: subcommand = message[1:whitespace] parameter = message[whitespace:].strip() # Teach the person how to use this thing if subcommand == "help": command_name = ctx.invoked_with title = f"!{command_name} - User Guide" description = f"Searches the RSS feed for {feed.title}" helpdict = { f"!{command_name}": "Post the most recent item in the feed", f"!{command_name} <word>": "Post the most recent item whose title contains the whole word <word>", f"!{command_name} -help": "Posts this message", f"!{command_name} -reg <regex>": "Perform a regular expression search for the most " "recent item title matching <regex>", f"!{command_name} -refresh": "Force the bot to refresh its cache"} thumbnail = feed.image if feed.image is not None else COMMAND_THUMBNAILS["rssfeed"] await self.bot.say(embed=utils.embedfromdict(helpdict, title=title, description=description, thumbnail_url=thumbnail)) return # Dump all the info about the feed if subcommand == "details": await self.bot.say(feed.to_string()) return # Test an embed for a given feed if subcommand == "embed": await self.bot.say(embed=feed.get_embed(feed.items[0])) return # Test an embed for a given feed if subcommand == "image": embed = Embed() embed.set_image(url=feed.image) await self.bot.say(embed=embed) return # If some nerd like Kris or Pat wants to do regex search if subcommand == "reg": episode = feed.search(parameter, regex=True) if episode: await self.bot.say(embed=feed.get_embed(episode)) return await self.bot.say(f"I couldn't find any results for the regex string `{parameter}`") # Force a refresh on the feed if subcommand == "refresh": feed.refresh() await self.bot.say(f"Alright, I have refreshed the feed `{feed.feed_id}`") return # Returns search results that the user can select if subcommand == "search": await self.bot.say("This has not been implemented yet :sad:") return # If there was a subcommand but it was unrecognized if subcommand != "": await self.bot.say(f"I'm sorry, I don't understand the subcommand `{subcommand}`. " + f"Please consult `-help` for more information") return # Search for the term episode = feed.search(parameter) if episode: await self.bot.say(embed=feed.get_embed(episode)) return await self.bot.say(f"I couldn't find any results in the {feed.title} feed " f"for the term `{parameter}` :worried:") except Exception as e: await utils.report(self.bot, str(e), source=f"handle_rss_feed() for '{feed.feed_id}'", ctx=ctx)