async def summary(message: discord.Message, *options, phrase: Annotate.LowerContent=None): """ Perform a summary! """ # This dict stores all parsed options as keywords member, channel, num = [], None, None for value in options: num_match = valid_num.match(value) if num_match: assert not num num = int(num_match.group("num")) # Assign limits if num > max_summaries: num = max_summaries elif num < 1: num = 1 continue member_match = valid_member.match(value) if member_match: member.append(utils.find_member(message.server, member_match.group())) continue channel_match = valid_channel.match(value) if channel_match: assert not channel channel = utils.find_channel(message.server, channel_match.group()) assert channel.permissions_for(message.server.me).read_message_history, "**I can't see this channel.**" continue # Assign defaults if not num: num = 1 if not channel: channel = message.channel await client.send_typing(message.channel) await update_task.wait() await update_messages(channel) # Split the messages into content and filter member and phrase if member: message_content = [m.clean_content for m in stored_messages[channel.id] if m.author in member] else: message_content = [m.clean_content for m in stored_messages[channel.id]] if phrase: message_content = [s for s in message_content if phrase.lower() in s.lower()] # Clean up by removing all commands from the summaries if phrase is None or not phrase.startswith(config.command_prefix): message_content = [s for s in message_content if not s.startswith(config.command_prefix)] # Check if we even have any messages assert message_content, on_no_messages.format(message) # Generate the summary, or num summaries for i in range(num): sentence = markov_messages(message_content) await client.say(message, sentence or on_fail.format(message))
async def summary(message: discord.Message, *options, phrase: Annotate.Content = None): """ Run a markov chain through the past 5000 messages + up to another 5000 messages after first use. This command needs some time after the plugin reloads as it downloads the past 5000 messages in the given channel. """ # This dict stores all parsed options as keywords member, channel, num = [], None, None regex, case, tts, coherent, strict = False, False, False, False, False bots = not summary_options.data["no_bot"] for value in options: num_match = valid_num.match(value) if num_match: assert not num num = int(num_match.group("num")) continue member_match = valid_member.match(value) if member_match: member.append(message.server.get_member(member_match.group("id"))) continue member_match = valid_member_silent.match(value) if member_match: member.append( utils.find_member(message.server, member_match.group("name"))) continue role_match = valid_role.match(value) if role_match: role = discord.utils.get(message.server.roles, id=role_match.group("id")) member.extend(m for m in message.server.members if role in m.roles) continue channel_match = valid_channel.match(value) if channel_match: assert not channel channel = utils.find_channel(message.server, channel_match.group()) continue if value in valid_options: if value == "+re" or value == "+regex": regex = True if value == "+case": case = True if value == "+tts": tts = True if value == "+coherent": coherent = True if value == "+strict": strict = True bots = False if value == "+nobot" else True if value == "+bot" else bots
async def summary(message: discord.Message, *options, phrase: Annotate.Content=None): """ Perform a summary! """ # This dict stores all parsed options as keywords member, channel, num = [], None, None regex, case, tts = False, False, False bots = not summary_options.data["no_bot"] for value in options: num_match = valid_num.match(value) if num_match: assert not num num = int(num_match.group("num")) # Assign limits if num > max_summaries: num = max_summaries elif num < 1: num = 1 continue member_match = valid_member.match(value) if member_match: member.append(utils.find_member(message.server, member_match.group())) continue member_match = valid_member_silent.match(value) if member_match: member.append(utils.find_member(message.server, member_match.group("name"))) continue channel_match = valid_channel.match(value) if channel_match: assert not channel channel = utils.find_channel(message.server, channel_match.group()) assert channel.permissions_for(message.server.me).read_message_history, "**I can't see this channel.**" continue if value in valid_options: if value == "+re" or value == "+regex": regex = True if value == "+case": case = True if value == "+tts": assert message.author.permissions_in(message.channel).send_tts_messages, \ "**You don't have permissions to send tts messages in this channel.**" tts = True bots = False if value == "+nobot" else True if value == "+bot" else bots
def parse_annotation(param: inspect.Parameter, default, arg: str, index: int, message: discord.Message): """ Parse annotations and return the command to use. index is basically the arg's index in shelx.split(message.content) """ if default is param.empty: default = None if param.annotation is not param.empty: # Any annotation is a function or Annotation enum anno = param.annotation # Valid enum checks if isinstance(anno, utils.Annotate): content = lambda s: utils.split(s, maxsplit=index)[-1].strip("\" ") if anno is utils.Annotate.Content: # Split and get raw content from this point return content(message.content) or default elif anno is utils.Annotate.LowerContent: # Lowercase of above check return content(message.content).lower() or default elif anno is utils.Annotate.CleanContent: # Split and get clean raw content from this point return content(message.clean_content) or default elif anno is utils.Annotate.LowerCleanContent: # Lowercase of above check return content(message.clean_content).lower() or default elif anno is utils.Annotate.Member: # Checks member names or mentions return utils.find_member(message.server, arg) or default_self(anno, default, message) elif anno is utils.Annotate.Channel: # Checks channel names or mentions return utils.find_channel(message.server, arg) or default_self(anno, default, message) elif anno is utils.Annotate.Code: # Works like Content but extracts code return utils.get_formatted_code(utils.split(message.content, maxsplit=index)[-1]) or default try: # Try running as a method result = anno(arg) return result if result is not None else default except TypeError: raise TypeError("Command parameter annotation must be either pcbot.utils.Annotate or a callable") except: # On error, eg when annotation is int and given argument is str return None return str(arg) or default # Return str of arg if there was no annotation
async def parse_annotation(param: inspect.Parameter, default, arg: str, index: int, message: discord.Message): """ Parse annotations and return the command to use. index is basically the arg's index in shelx.split(message.content) """ if default is param.empty: default = None if param.annotation is not param.empty: # Any annotation is a function or Annotation enum anno = override_annotation(param.annotation) content = lambda s: utils.split(s, maxsplit=index)[-1].strip("\" ") # Valid enum checks if isinstance(anno, utils.Annotate): if anno is utils.Annotate.Content: # Split and get raw content from this point return content(message.content) or default elif anno is utils.Annotate.LowerContent: # Lowercase of above check return content(message.content).lower() or default elif anno is utils.Annotate.CleanContent: # Split and get clean raw content from this point return content(message.clean_content) or default elif anno is utils.Annotate.LowerCleanContent: # Lowercase of above check return content(message.clean_content).lower() or default elif anno is utils.Annotate.Member: # Checks member names or mentions return utils.find_member(message.server, arg) or default_self( anno, default, message) elif anno is utils.Annotate.Channel: # Checks text channel names or mentions return utils.find_channel(message.server, arg) or default_self( anno, default, message) elif anno is utils.Annotate.VoiceChannel: # Checks voice channel names or mentions return utils.find_channel(message.server, arg, channel_type="voice") elif anno is utils.Annotate.Code: # Works like Content but extracts code return utils.get_formatted_code( utils.split(message.content, maxsplit=index)[-1]) or default try: # Try running as a method if getattr(anno, "allow_spaces", False): arg = content(message.content) # Pass the message if the argument has this specified if getattr(anno, "pass_message", False): result = anno(message, arg) else: result = anno(arg) # The function can be a coroutine if inspect.isawaitable(result): result = await result return result if result is not None else default except TypeError: raise TypeError( "Command parameter annotation must be either pcbot.utils.Annotate, a callable or a coroutine" ) except AssertionError as e: # raise the error in order to catch it at a lower level raise AssertionError(e) except: # On error, eg when annotation is int and given argument is str return None return str(arg) or default # Return str of arg if there was no annotation
def members_and_channels(message: discord.Message, arg: str): """ Look for both members and channel mentions. """ if utils.channel_mention_pattern.match(arg): return utils.find_channel(message.server, arg) return utils.find_member(message.server, arg)
async def summary(message: discord.Message, *options, phrase: Annotate.Content = None): """ Run a markov chain through the past 5000 messages + up to another 5000 messages after first use. This command needs some time after the plugin reloads as it downloads the past 5000 messages in the given channel. """ # This dict stores all parsed options as keywords member, channel, num = [], None, None regex, case, tts, coherent, strict = False, False, False, False, True bots = not summary_options.data["no_bot"] async with message.channel.typing(): for value in options: num_match = valid_num.match(value) if num_match: assert not num num = int(num_match.group("num")) continue member_match = valid_member.match(value) if member_match: member.append( message.guild.get_member(int(member_match.group("id")))) continue member_match = valid_member_silent.match(value) if member_match: member.append( utils.find_member(message.guild, member_match.group("name"))) continue role_match = valid_role.match(value) if role_match: role = discord.utils.get(message.guild.roles, id=int(role_match.group("id"))) member.extend(m for m in message.guild.members if role in m.roles) continue channel_match = valid_channel.match(value) if channel_match: assert not channel channel = utils.find_channel(message.guild, channel_match.group()) continue if value in valid_options: if value == "+re" or value == "+regex": regex = True if value == "+case": case = True if value == "+tts": tts = True if value == "+coherent": coherent = True if value == "+loose": strict = False bots = False if value == "+nobot" else True if value == "+bot" else bots # Assign defaults and number of summaries limit is_privileged = message.author.permissions_in( message.channel).manage_messages if num is None or num < 1: num = 1 elif num > max_admin_summaries and is_privileged: num = max_admin_summaries elif num > max_summaries: num = max_summaries if not is_privileged else num if not channel: channel = message.channel # Check channel permissions after the given channel has been decided assert channel.permissions_for( message.guild.me ).read_message_history, "**I can't see this channel.**" assert not tts or message.author.permissions_in(message.channel).send_tts_messages, \ "**You don't have permissions to send tts messages in this channel.**" if str(channel.id) in summary_options.data["persistent_channels"]: messages = summary_data.data["channels"][str(channel.id)] else: await update_task.wait() await update_messages(channel) messages = stored_messages[str(channel.id)] message_content = filter_messages_by_arguments(messages, channel, member, bots) # Replace new lines with text to make them persist through splitting message_content = (s.replace("\n", NEW_LINE_IDENTIFIER) for s in message_content) # Filter looking for phrases if specified if phrase and not is_endswith(phrase): message_content = list( filter_messages(message_content, phrase, regex, case)) command_prefix = config.guild_command_prefix(message.guild) # Clean up by removing all commands from the summaries if phrase is None or not phrase.startswith(command_prefix): message_content = [ s for s in message_content if not s.startswith(command_prefix) ] # Check if we even have any messages assert message_content, on_no_messages.format(message) markovify_model = None if strict: try: markovify_model = markovify.Text(message_content) except NameError: logging.warning( "+strict was used but markovify is not imported") strict = False except KeyError: markovify_model = None # Generate the summary, or num summaries for i in range(num): if strict and markovify_model: if phrase and is_endswith(phrase): try: sentence = markovify_model.make_sentence_with_start( phrase[:-3]) except KeyError: sentence = markovify_model.make_sentence(tries=1000) else: sentence = markovify_model.make_sentence(tries=1000) else: sentence = markov_messages(message_content, coherent) if not sentence: sentence = markov_messages(message_content, coherent) assert sentence, on_fail.format(message) # Convert new line identifiers back to characters sentence = sentence.replace(NEW_LINE_IDENTIFIER.strip(" "), "\n") await client.send_message(message.channel, sentence, tts=tts)