async def checkin_exempt(self, ctx: commands.Context, user: MemberConverter2 = None, val: BooleanConverter = None): """!kazhelp description: | Check or set exemptions from check-ins. Users who are exempt from check-ins will not appear in a {{!checkin report}}. parameters: - name: user type: "@mention" optional: true description: The user to check (as an @mention or a Discord ID). - name: val type: '"yes" or "no"' optional: true description: "If not specified, check a user's exemption status. If specified, change that user's exemption status." examples: - command: .checkin exempt description: Get a list of exempt users. - command: .checkin exempt @JaneDoe description: Check if JaneDoe is exempt from check-ins. - command: .checkin exempt @JaneDoe yes description: Set JaneDoe as exempt from check-ins. """ self.c.cleanup_exempt(self.server) if user is None: exempt_users = self.c.get_exempt_users() if exempt_users: full_msg = "**Exempt from check-ins**\n{}"\ .format('\n'.join(user_mention(u.discord_id) for u in exempt_users)) else: full_msg = "**No users are exempt from check-ins.**" for msg in split_chunks_on(full_msg, Limits.MESSAGE): await self.bot.say(msg[:Limits.MESSAGE]) elif val is None: member_ = user # type: discord.Member # IDE type detection if self.c.get_user(member_).is_exempt: await self.bot.say("{} is **exempt** from check-ins.".format( member_.mention)) else: await self.bot.say( "{} is **not** exempt from check-ins.".format( member_.mention)) else: member_ = user # type: discord.Member # IDE type detection self.c.set_user_exempt(member_, val) await self.bot.say("{} has been set {} from check-ins.".format( member_.mention, "**exempt**" if val else "**not** exempt"))
async def send_embed_list(self, title: str, contents: str): contents_split = split_chunks_on(contents, Limits.EMBED_FIELD_VALUE) em = discord.Embed(color=0x80AAFF, title=title) sep = '-' num_fields = 0 max_fields = (Limits.EMBED_TOTAL - len(title) - 2) \ // (Limits.EMBED_FIELD_VALUE + len(sep)) for say_str in contents_split: if num_fields >= max_fields: await self.bot.say(embed=em) em = discord.Embed(color=theme.solarized.cyan, title=title) num_fields = 0 em.add_field(name=sep, value=say_str, inline=False) num_fields += 1 await self.bot.say(embed=em)
def _render(self, parsed: List[WikiStructure]) -> List[str]: items = [] msg_break = False for i in parsed: if isinstance(i, WikiMessageBreak): pass elif isinstance(i, WikiImage): items.append(i.url) elif isinstance(i, WikiSection): if i.heading: text = '**{}**\n\n{}'.format(i.heading, i.text.strip()) else: text = self._MESSAGE_SPACER + i.text.strip() split_text = split_chunks_on(text, Limits.MESSAGE) items.extend(split_text) else: items.append( "**[WARNING] UNKNOWN WIKI STRUCTURE DURING RENDERING") return items
async def _whois_search(self, ctx, user: str): logger.info("whois: searching for name match") members = [ m for m in ctx.message.server.members if (m.nick and user.lower() in m.nick.lower()) or user.lower() in m.name.lower() ] if members: member_list_str = ', '.join(str(m) for m in members) logger.debug("Found {:d} users: {}".format(len(members), member_list_str)) s = '**{:d} users found**\n'.format(len(members)) +\ '\n'.join("{0.mention} ID {0.id}".format(m) for m in members) for part in split_chunks_on(s, maxlen=Limits.MESSAGE): await self.bot.say(part) return True else: await self.bot.say("No matching user found.") return False
async def report(self, ctx: commands.Context, min_badges: int = 1): """!kazhelp description: "Show a report of member badge counts." parameters: - name: min_badges optional: true type: number description: | Minimum number of badges a user needs to have to be included in the report. """ if min_badges < 1: raise commands.BadArgument("`min` must be at least 1.") report_lines = [ "**Badge report (minimum {:d} badges)**".format(min_badges) ] for u, n in self.c.query_badge_report(min_badges): report_lines.append("{} - {:d} badges".format( user_mention(u.discord_id), n)) for msg in split_chunks_on('\n'.join(report_lines), maxlen=Limits.MESSAGE): await self.bot.say(msg)
def _render_embed( self, parsed: List[WikiStructure]) -> List[Union[str, EmbedSplitter]]: items = [] msg_break = False for i in parsed: if isinstance(i, WikiMessageBreak): msg_break = True elif isinstance(i, WikiImage): msg_break = False items.append(i.url) elif isinstance(i, WikiSection): heading = i.heading or EmbedSplitter.Empty desc = i.text.strip() if msg_break or not items or not isinstance( items[-1], EmbedSplitter): if len(desc) <= Limits.EMBED_DESC: items.append( EmbedSplitter(title=heading, description=desc)) else: desc_split = split_chunks_on(desc, Limits.EMBED_FIELD_VALUE) items.append( EmbedSplitter(title=heading, description=desc_split.pop(0))) for s in desc_split: items[-1].add_field(name=self._MESSAGE_SPACER, value=s, inline=False) else: items[-1].add_field(name=heading, value=desc, inline=False) msg_break = False else: items.append( "**[WARNING] UNKNOWN WIKI STRUCTURE DURING RENDERING") return items
async def send_message(self, destination, contents=None, *, tts=False, embed: Union[discord.Embed, EmbedSplitter] = None, auto_split=True, split='word') -> Sequence[discord.Message]: """ Send a message. This method wraps the :meth:`discord.Client.send_message` method and adds automatic message splitting if a message is too long for one line. No parsing of Markdown is done for message splitting; this behaviour may break intended formatting. For messages which may contain formatting, it is suggested you parse and split the message instead of relying on auto-splitting. See also :meth:`kaztron.utils.split_chunks_on` and :meth:`kaztron.utils.natural_split` for manual control of splitting behaviour. See also :meth:`kaztron.utils.split_code_chunks_on` for splitting Markdown code blocks manually. See also :cls:`kaztron.utils.embeds.EmbedSplitter` for similar functionality in splitting embeds. :param destination: he location to send the message (Channel, PrivateChannel, User, etc.) :param contents: The content of the message to send. If this is missing, then the ``embed`` parameter must be present. :param tts: Indicates if the message should be sent using text-to-speech. :param embed: The rich embed for the content. Also accepts EmbedSplitter instances, for automatic splitting - in this case, the EmbedSplitter will be finalized by this method. :param auto_split: Whether to auto-split messages that exceed the maximum message length. :param split: What to split on: 'word' or 'line'. 'Line' should only be used for messages known to contain many line breaks, as otherwise auto-splitting is likely to fail. """ # prepare text contents if not contents or not auto_split: content_chunks = (contents, ) else: if split == 'word': content_chunks = natural_split(contents, Limits.MESSAGE) elif split == 'line': content_chunks = split_chunks_on(contents, Limits.MESSAGE, split_char='\n') else: raise ValueError( '`split` argument must be \'word\' or \'line\'') # prepare embed try: embed_list = embed.finalize() except AttributeError: embed_list = (embed, ) # strategy: output all text chunks before starting to output embed chunks # so the last text chunk will have the first embed chunk attached # this is because non-split messages usually have the embed appear after the msg - # should be fairly rare for both msg and embed to be split msg_list = [] for content_chunk in content_chunks[:-1]: msg_list.append(await self.bot.send_message(destination, content_chunk, tts=tts)) msg_list.append(await self.bot.send_message(destination, content_chunks[-1], tts=tts, embed=embed_list[0])) for embed_chunk in embed_list[1:]: msg_list.append(await self.bot.send_message(destination, tts=tts, embed=embed_chunk)) return tuple(msg_list)