async def quote_add(self, ctx: commands.Context, user: str, *, message: str): """!kazhelp description: | Add a new quote manually. TIP: To automatically find and add a recent message, use {{!quote grab}}. parameters: - name: user type: "@user" description: > The user being quoted. Should be an @mention or a discord ID. - name: message type: string description: The quote text to add. examples: - command: .quote add @JaneDoe#0921 Ready for the mosh pit, shaka brah. """ if len(message) > Quote.MAX_MESSAGE_LEN: raise commands.UserInputError( "That quote is too long! Maximum length {:d} characters.". format(Quote.MAX_MESSAGE_LEN)) quote = c.store_quote(user=c.query_user(self.server, user), saved_by=c.query_user(self.server, ctx.message.author.id), channel_id=ctx.message.channel.id, message=message, timestamp=ctx.message.timestamp) message = "Added quote: {}".format(self.format_quote(quote)) logger.info(message) await self.bot.say( embed=self.make_single_embed(quote, title="Added quote.")) await self.send_output(message)
async def quote_add(self, ctx: commands.Context, user: str, *, message: str): """ Add a new quote manually. You can use `.quote grab` instead to automatically grab a recent message. Arguments: * user: Required. The user to find a quote for. See `.help quote` for valid formats. * message: Required. The quote text to add. Examples: .quote add @JaneDoe Ready for the mosh pit, shaka brah. """ logger.info("quote add: {}".format(message_log_str(ctx.message))) if len(message) > Quote.MAX_MESSAGE_LEN: raise ValueError( "That quote is too long! Maximum length {:d} characters.". format(Quote.MAX_MESSAGE_LEN)) quote = c.store_quote(user=c.query_user(self.server, user), saved_by=c.query_user(self.server, ctx.message.author.id), channel_id=ctx.message.channel.id, message=message, timestamp=ctx.message.timestamp) message = "Added quote: {}".format(self.format_quote(quote)) logger.info(message) await self.bot.say( embed=self.make_single_embed(quote, title="Added quote.")) await self.send_output(message)
async def quote_list(self, ctx: commands.Context, user: str, page: int = None): """!kazhelp description: Retrieve a list of quotes. Always PMed. parameters: - name: user type: "@user" description: > The user to find a quote for. Should be an @mention or a discord ID. - name: page type: number optional: true default: last page (most recent) description: The page number to show, if there are more than 1 page of quotes. examples: - command: .quote list @JaneDoe#0921 description: List all quotes by JaneDoe. - command: .quote list @JaneDoe#0921 4 description: List the 4th page of quotes by JaneDoe. """ db_user = c.query_user(self.server, user) if len(db_user.quotes) == 0: logger.warning("User has no quotes.") await self.bot.say("Sorry, {} has no quotes!".format(db_user.name)) return paginator = Pagination(db_user.quotes, self.QUOTES_PER_PAGE, align_end=True) if page is not None: paginator.page = max(0, min(paginator.total_pages - 1, page - 1)) await self.send_quotes_list(ctx.message.author, paginator, db_user)
async def quote_list(self, ctx: commands.Context, user: str, page: int = None): """ Retrieve a list of quotes. Reply is always PMed. Arguments: * user: Required. The user to find a quote for. See `.help quote` for valid formats. * page: Optional. The page number to access, if there are more than 1 pages of notes. Default: last page. Examples: .quote list @JaneDoe - List all quotes by JaneDoe (page 1 if multiple pages).. .quote list @JaneDoe 4 - List the 4th page of quotes by JaneDoe. """ logger.info("quote list: {}".format(message_log_str(ctx.message))) db_user = c.query_user(self.server, user) db_records = c.query_author_quotes(db_user) paginator = Pagination(db_records, self.QUOTES_PER_PAGE, align_end=True) if page is not None: paginator.page = max(0, min(paginator.total_pages - 1, page - 1)) await self.send_quotes_list(ctx.message.author, paginator, db_user, ctx.message.server)
async def quote_find(self, ctx: commands.Context, user: str, *, search: str = None): """ Find the most recent quote matching a user and/or text search. Arguments: * user: Required. The user to find a quote for, or part of their name or nickname to search, or "all". For exact user matches, see `.help quote` for valid formats. * search: Optional. Text to search in the quote. Examples: .quote find Jane - Find a quote for a user whose user/nickname contains "Jane". .quote find @JaneDoe flamingo - Find a quote containing "flamingo" by JaneDoe. .quote find Jane flamingo - Find a quote matching user "Jane" and containing "flamingo". """ logger.info("quote find: {}".format(message_log_str(ctx.message))) try: db_user = c.query_user(self.server, user) except ValueError: # not a valid user ID format if user != 'all': db_user = c.search_users(user) else: db_user = None db_records = c.search_quotes(search, db_user) em = self.make_single_embed(db_records[-1]) await self.bot.say(embed=em)
async def quote(self, ctx: commands.Context, user: str = None, *, search: str = None): """!kazhelp description: > Retrieve a quote matching a user and/or text search. Returns a random quote among all matching results. TIP: To search for a quote by index number, use {{!quote get}}. parameters: - name: user type: "@user or string or \\"all\\"" optional: true description: > The user to find a quote for. This can be an @mention, user ID, part of their name or nickname to search, or the special string "all" to find any user (i.e. search only by keyword). - name: search type: string optional: true description: The text to search. examples: - command: .quote description: Find a random quote. - command: .quote Jane description: Find a quote from any user whose name/nickname contains "Jane". - command: .quote @JaneDoe#0921 flamingo description: Find a quote by JaneDoe containing "flamingo". - command: .quote Jane flamingo description: Find a quote both matching user "Jane" and containing "flamingo". """ if user: try: db_user = c.query_user(self.server, user) except ValueError: # not a valid user ID format if user != 'all': db_user = c.search_users(user) else: db_user = None db_records = c.search_quotes(search, db_user) quote = db_records[random.randint(0, len(db_records) - 1)] logger.debug("Selected quote: {!r}".format(quote)) else: quote = c.random_quote() logger.info("Selected random quote id={:d} from all users".format( quote.quote_id)) number = quote.get_index() + 1 len_recs = len(quote.author.quotes) em = self.make_single_embed(quote, index=number, total=len_recs) await self.bot.say(embed=em)
async def quote_delete(self, ctx: commands.Context, user: str, number): """!kazhelp description: Delete one or all quotes attributed to a user. This is a moderative command; regular users should use {{!quote undo}} or {{!quote rem}}. parameters: - name: user type: "@user" description: The user whose quote to delete. Can be an @mention or discord ID. - name: number type: number or "all" description: The ID number of the quote to delete (starting from 1), or "all". examples: - command: .quote rem @JaneDoe#0921 4 description: Delete the 4th quote by JaneDoe. - command: .quote rem @JaneDoe#0921 all description: Remove all quotes by JaneDoe. """ db_user = c.query_user(self.server, user) len_recs = len(db_user.quotes) if number == 'all': logger.info("Removing all {} quotes for {!r}...".format( len_recs, db_user)) c.remove_quotes(db_user.quotes) await self.bot.say("Removed all {} quotes for {}.".format( len_recs, db_user.mention)) await self.send_output("Removed all {} quotes for {}.".format( len_recs, db_user.mention)) else: try: number = int(number) except ValueError: raise commands.BadArgument( "The number of the quote to delete is required.") if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return quote = db_user.quotes[number - 1] message_text = "Removed quote (mod): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, number, len_recs, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote_delete(self, ctx: commands.Context, user: str, number): """ [MOD ONLY] Delete one or all quotes attributed to a user. Arguments: * user: Required. The user to find a quote for. See `.help quote` for valid formats. * number: Required. The ID number of the quote to delete (starting from 1), or "all". Examples: .quote rem @JaneDoe 4 - Delete the 4th quote by JaneDoe. .quote rem @JaneDoe all - Remove all quotes by JaneDoe. """ logger.info("quote del: {}".format(message_log_str(ctx.message))) db_user = c.query_user(self.server, user) db_records = c.query_author_quotes(db_user) len_recs = len(db_records) if number == 'all': logger.info("Removing all {} quotes for {!r}...".format( len_recs, db_user)) c.remove_quotes(db_records) await self.bot.say("Removed all {} quotes for {}.".format( len_recs, db_user.mention)) await self.send_output("Removed all {} quotes for {}.".format( len_recs, db_user.mention)) else: try: number = int(number) except ValueError: raise commands.BadArgument("Cannot convert number to int") if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return quote = db_records[number - 1] message_text = "Removed quote (mod): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, number, len_recs, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote(self, ctx: commands.Context, user: str, number: int = None): """ Retrieve a quote. If a quote number isn't given, find a random quote. Arguments: * user: Required. The user to find a quote for. Example formats: * @mention of the user (make sure it actually links them) * User's name + discriminator: JaneDoe#0921 * Discord ID number: 123456789012345678 * number: Optional. The ID number of the quote to delete (starting from 1), as shown by the `.quote` or `.quote list` commands. Examples: .quote @JaneDoe - Find a random quote by JaneDoe. .quote @JaneDoe 4 - Find the 4th quote by JaneDoe. """ logger.info("quote: {}".format(message_log_str(ctx.message))) db_user = c.query_user(self.server, user) db_records = c.query_author_quotes(db_user) len_recs = len(db_records) if number is None: number = random.randint(1, len_recs) logger.info("Selected random quote {:d} by user {!r}...".format( number, db_user)) else: logger.info("Requested quote {:d} by user {!r}".format( number, db_user)) if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return em = self.make_single_embed(db_records[number - 1], number, len_recs) await self.bot.say(embed=em)
async def quote_remove(self, ctx: commands.Context, number: int): """!kazhelp description: | Remove one of your own quotes. WARNING: This command cannot be undone! IMPORTANT: If you are being harassed via quotes, or quote are otherwise being abused, please report this to the mods. TIP: To delete a quote you quoted (instead of a quote attributed to you), use {{!quote undo}} to remove the most recent one. For any other situation, contact the mods. parameters: - name: number type: number description: The ID number of the quote to delete (starting from 1), as shown by the {{!quote}}, {{!quote get}} and {{!quote list}} commands. examples: - command: .quote del 4 description: Delete the 4th quote attributed to you. """ db_user = c.query_user(self.server, ctx.message.author.id) len_recs = len(db_user.quotes) if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return quote = db_user.quotes[number - 1] message_text = "Removed quote (remove): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, number, len_recs, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote_get(self, ctx: commands.Context, user: str, number: int): """!kazhelp description: | Retrieve a quote by index. parameters: - name: user type: "@user" description: > The user to find a quote for. Should be an @mention or a discord ID. - name: number type: number optional: true description: > The ID number of the quote to find (starting from 1), as shown by the {{!quote}} and {{!quote list}} commands. examples: - command: .quote @JaneDoe#0921 description: Find a random quote by JaneDoe. - command: .quote @JaneDoe#0921 4 description: Find the 4th quote by JaneDoe. """ db_user = c.query_user(self.server, user) len_recs = len(db_user.quotes) # no quotes for this user if len_recs == 0: logger.warning("User has no quotes.") await self.bot.say("Sorry, {} has no quotes!".format(db_user.name)) return logger.info("Requested quote {:d} by user {!r}".format( number, db_user)) if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return quote = db_user.quotes[number - 1] em = self.make_single_embed(quote, number, len_recs) await self.bot.say(embed=em)
async def quote_undo(self, ctx: commands.Context): """!kazhelp description: | Remove the last quote you added. WARNING: This command cannot be undone! TIP: This command only undoes your own calls to {{!quote add}} or {{!quote grab}}. It does **not** undo {{!quote rem}}, and does not undo quote commands by other users. TIP: To delete quotes attributed to you, use {{!quote rem}}. """ db_user = c.query_user(self.server, ctx.message.author.id) quote = db_user.saved_quotes[-1] message_text = "Removed quote (undo): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote_undo(self, ctx: commands.Context): """ Remove the last quote you added. THIS COMMAND CANNOT BE UNDONE. This command only undoes `.quote add` or `.quote grab` actions. It does NOT undo `.quote rem` actions. """ logger.info("quote undo: {}".format(message_log_str(ctx.message))) db_user = c.query_user(self.server, ctx.message.author.id) db_records = c.query_saved_quotes(db_user) quote = db_records[-1] message_text = "Removed quote (undo): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote_remove(self, ctx: commands.Context, number: int): """ Remove one of your own quotes. THIS COMMAND CANNOT BE UNDONE. This command is limited to quotes attributed to you. For any other situations, please contact the moderators to delete quotes. Arguments: * number: Optional. The ID number of the quote to delete (starting from 1), as shown by the `.quote` or `.quote list` commands. Examples: .quote del 4 - Delete the 4th quote attributed to you. """ logger.info("quote rem: {}".format(message_log_str(ctx.message))) db_user = c.query_user(self.server, ctx.message.author.id) db_records = c.query_author_quotes(db_user) len_recs = len(db_records) if number < 1 or number > len_recs: logger.warning("Invalid index {:d}".format(number)) await self.bot.say( "Oops, I can't get quote {:d} for {}! Valid quotes are 1 to {:d}" .format(number, db_user.name, len_recs)) return quote = db_records[number - 1] message_text = "Removed quote (remove): {}".format( self.format_quote(quote)) em = self.make_single_embed(quote, number, len_recs, title="Quote deleted.") c.remove_quotes([quote]) await self.bot.say(embed=em) await self.send_output(message_text)
async def quote_grab(self, ctx: commands.Context, user: discord.Member, *, search: str = None): """!kazhelp description: | Find the most recent matching message and add it as a quote. This command searches the {{grab_search_max}} most recent messages in the channel. The most recent message matching both the user and (if specified) search text is added as a quote. TIP: To manually add a quote, use {{!quote add}}. parameters: - name: user type: "@user" description: > The user being quoted. Should be an @mention or a discord ID. - name: search type: string optional: true description: The quote text to find. examples: - command: .quote grab @JaneDoe#0921 description: Quote the most recent message from JaneDoe. - command: .quote grab @JaneDoe#0921 mosh pit description: Finds the most recent message from @JaneDoe containing "mosh pit". """ search_messages = self.bot.logs_from(ctx.message.channel, self.cog_config.grab_search_max) async for message in search_messages: \ # type: discord.Message # if requested author, and this message isn't the invoking one (in case of self-grab) if message.author == user and message.id != ctx.message.id: if not search or search.lower() in message.content.lower(): grabbed_message = message break else: # Nothing found if search: await self.bot.say( "No message from {} matching '{}' found in the last {:d} messages" .format(user.nick if user.nick else user.name, search, self.cog_config.grab_search_max)) else: await self.bot.say( "No message from {} found in the last {:d} messages". format(user.nick if user.nick else user.name, self.cog_config.grab_search_max)) return message_text = grabbed_message.content if grabbed_message.attachments: message_text = "{0}\n\n{1}".format( message_text, '\n'.join(a['url'] for a in ctx.message.attachments)) if len(message_text) > Quote.MAX_MESSAGE_LEN: raise commands.UserInputError( "That quote is too long! Maximum length {:d} characters.". format(Quote.MAX_MESSAGE_LEN)) quote = c.store_quote(user=c.query_user(self.server, grabbed_message.author.id), saved_by=c.query_user(self.server, ctx.message.author.id), channel_id=grabbed_message.channel.id, message=message_text, timestamp=grabbed_message.timestamp) message_text = "Added quote: {}".format(self.format_quote(quote)) logger.info(message_text) await self.bot.say( embed=self.make_single_embed(quote, title="Added quote.")) await self.send_output(message_text)
async def quote_grab(self, ctx: commands.Context, user: discord.Member, *, search: str = None): """ Find the most recent matching message and add it as a quote. This command searches the most recent messages (default 100 messages). The most recent message matching both the user and (if specified) search text is added as a quote. You can use `.quote add` instead to manually add the quote. Arguments: * user: Required. The user to find a quote for. See `.help quote` for valid formats. * search: Optional. The quote text to find among the user's recent messages. Examples: .quote grab @JaneDoe Quote the most recent message from @JaneDoe. .quote grab @JaneDoe mosh pit Finds the most recent message from @JaneDoe containing "mosh pit". """ logger.info("quote grab: {}".format(message_log_str(ctx.message))) async for message in self.bot.logs_from(ctx.message.channel, self.grab_max): \ # type: discord.Message # if requested author, and this message isn't the invoking one (in case of self-grab) if message.author == user and message.id != ctx.message.id: if not search or search.lower() in message.content.lower(): grabbed_message = message break else: # Nothing found if search: await self.bot.say(("No message from {} matching '{}' " "found in the last {:d} messages").format( user.nick if user.nick else user.name, search, self.grab_max)) else: await self.bot.say( "No message from {} found in the last {:d} messages". format(user.nick if user.nick else user.name, self.grab_max)) return message_text = grabbed_message.content if grabbed_message.attachments: message_text = "{0}\n\n{1}".format( message_text, '\n'.join(a['url'] for a in ctx.message.attachments)) if len(message_text) > Limits.EMBED_FIELD_VALUE: raise ValueError( "That quote is too long! Maximum length 1024 characters.") quote = c.store_quote(user=c.query_user(self.server, grabbed_message.author.id), saved_by=c.query_user(self.server, ctx.message.author.id), channel_id=grabbed_message.channel.id, message=message_text, timestamp=grabbed_message.timestamp) message_text = "Added quote: {}".format(self.format_quote(quote)) logger.info(message_text) await self.bot.say( embed=self.make_single_embed(quote, title="Added quote.")) await self.send_output(message_text)