async def charinfo(self, ctx: Context, *, characters: str) -> None: """Shows you information about up to 50 unicode characters.""" match = re.match(r"<(a?):(\w+):(\d+)>", characters) if match: raise BadArgument( "Only unicode characters can be processed, but a custom Discord emoji " "was found. Please remove it and try again.", ) if len(characters) > 50: raise BadArgument(f"Too many characters ({len(characters)}/50)") def get_info(char: str) -> Tuple[str, str]: digit = f"{ord(char):x}" if len(digit) <= 4: u_code = f"\\u{digit:>04}" else: u_code = f"\\U{digit:>08}" url = f"https://www.compart.com/en/unicode/U+{digit:>04}" name = f"[{unicodedata.name(char, '')}]({url})" info = f"`{u_code.ljust(10)}`: {name} - {utils.escape_markdown(char)}" return info, u_code char_list, raw_list = zip(*(get_info(c) for c in characters)) embed = Embed(title="Character info", colour=Colours.green) if len(characters) > 1: # Maximum length possible is 502 out of 1024, so there's no need to truncate. embed.add_field( name="Full Raw Text", value=f"`{''.join(raw_list)}`", inline=False ) await LinePaginator.paginate( char_list, ctx, embed, max_lines=10, max_size=2000, empty=False )
def slow(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_chatroom_attrs(mod_action, embed) embed.add_field( name= f"Slow Amount (second{'' if int(info['args'][0]) == 1 else 's'})", value=f"`{info['args'][0]}`", inline=True) return embed
def followers(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_chatroom_attrs(mod_action, embed) embed.add_field( name= f"Time Needed to be Following (minute{'' if int(info['args'][0]) == 1 else 's'})", value=f"`{info['args'][0]}`", inline=True) return embed
def raid(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_chatroom_attrs(mod_action, embed) embed.add_field( name="Raided Channel", value= f"[{info['args'][0]}](<https://www.twitch.tv/{info['args'][0]}>)", inline=True) return embed
def set_appeals_attrs(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: self.set_user_attrs(streamer, info, mod_action, embed) embed.add_field( name="Moderator Reason", value= f"`{info['moderator_message'] if info['moderator_message'] != '' else 'None Provided'}`", inline=False) return embed
def delete(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_user_attrs(streamer, info, mod_action, embed) if "`" in info['args'][1]: embed.add_field(name="Message", value=f"```{info['args'][1]}```") else: embed.add_field(name="Message", value=f"`{info['args'][1]}`") # embed.add_field( # name="Message ID", value=f"`{info['args'][2]}`") return embed
def embed_helper(self, description: str, field: str) -> Embed: """Embed helper function.""" embed = Embed(title="Eval Results", colour=self.GREEN, description=description) embed.add_field( name="Output", value=field, ) return embed
async def get_commit_messages(self, event_body, brief=False): embed_commits = [] branch = event_body['ref'].split('/', 2)[2] project = event_body['repository']['full_name'] commits = event_body['commits'] if brief and len(commits) > self.config['commit_truncation_limit']: first_hash = commits[0]['id'] last_hash = commits[-2]['id'] compare_url = f'https://github.com/{project}/compare/{first_hash}^...{last_hash}' embed = Embed( title= f'Skipped {len(commits) - 1} commits... (click link for diff)', colour=Colour(self._skipped_commit_colour), url=compare_url) embed_commits.append((embed, None)) commits = commits[-1:] for commit in commits: author_username = commit['author'].get('username', None) author_name = commit['author'].get('name', None) timestamp = dateutil.parser.parse(commit['timestamp']) commit_message = commit['message'].split('\n') embed = Embed(title=commit_message[0], colour=Colour(self._commit_colour), url=commit['url'], timestamp=timestamp) if len(commit_message) > 2 and not brief: commit_body = '\n'.join(commit_message[2:]) embed.description = commit_body[:4096] author = await self.get_author_info(author_username) if author: if author['name'] and author['name'] != author['login']: author_name = f'{author["name"]} ({author["login"]})' else: author_name = author['login'] embed.set_author(name=author_name, url=author['html_url'], icon_url=author['avatar_url']) elif author_name: embed.set_author(name=author_name) else: embed.set_author(name='<No Name>') embed.set_footer(text='Commit') embed.add_field(name='Repository', value=project, inline=True) embed.add_field(name='Branch', value=branch, inline=True) embed_commits.append((embed, commit['id'])) return embed_commits
def set_user_attrs(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: user = info["target_user_login"] or info['args'][0] user_escaped = user.lower().replace('_', '\_') embed.title = f"Mod {mod_action.value.replace('_', ' ').title()} Action" #embed.description=f"[Review Viewercard for User](<https://www.twitch.tv/popout/{streamer.username}/viewercard/{user.lower()}>)" embed.color = self.colour.red embed.add_field( name="Flagged Account", value= f"[{user_escaped}](<https://www.twitch.tv/popout/{streamer.username}/viewercard/{user_escaped}>)", inline=True) return embed
def automod_caught_message(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: ignore_message = False user = info["message"]["sender"]["login"] user_escaped = user.lower().replace('_', '\_') embed.title = f"{mod_action.value.replace('_', ' ').title()}" embed.color = self.colour.red embed.add_field( name="Flagged Account", value= f"[{user_escaped}](<https://www.twitch.tv/popout/{streamer.username}/viewercard/{user_escaped}>)", inline=True) embed.add_field( name="Content Classification", value= f"{info['content_classification']['category'].title()} level {info['content_classification']['level']}", inline=True) text_fragments = [] topics = [] for fragment in info["message"]["content"]["fragments"]: if fragment != {}: for topic in fragment.get("automod", {}).get("topics", {}).keys(): if topic not in topics: topics.append(topic.replace("_", " ")) if fragment.get("text", None) is not None: text_fragments.append(fragment.get("text", None)) text_fragments = list( dict.fromkeys(text_fragments) ) #Remove duplicates from topics and text fragments, they're pointless topics = list(dict.fromkeys(topics)) embed.add_field( name="Text fragments", value= f"`{', '.join([f.strip(' ') for f in text_fragments]).strip(', ')}`" ) embed.add_field(name="Topics", value=f"`{', '.join(topics).strip(', ')}`") if info["status"] == "PENDING": embed.colour = self.colour.yellow if "automod_caught_message" not in streamer.action_whitelist and streamer.action_whitelist != []: ignore_message = True elif info["status"] == "ALLOWED": if "automod_allowed_message" not in streamer.action_whitelist and streamer.action_whitelist != []: ignore_message = True embed.colour = self.colour.green elif info["status"] == "DENIED": if "automod_denied_message" not in streamer.action_whitelist and streamer.action_whitelist != []: ignore_message = True embed.colour = self.colour.red else: if "automod_caught_message" not in streamer.action_whitelist and streamer.action_whitelist != []: ignore_message = True if info["status"] != "PENDING": embed.title = embed.title.replace("Caught", info["status"].title()) return ignore_message, embed
async def help(self, ctx: Context): # unfortunately the @check decorator doesn't really work for this use case. if not self.bot.is_admin(ctx.author): return is_private = self.bot.is_private(ctx.channel) embed = Embed(title='OBS Bot Help') for section, commands in self.help_sections.items(): if section in self.restricted and not is_private: continue longest = max(len(cmd) for cmd, _ in commands) content = '\n'.join(f'{cmd.ljust(longest)} - {helptext}' for cmd, helptext in commands) embed.add_field(name=section, value=f'```{content}```', inline=False) return await ctx.channel.send(embed=embed)
async def attempt_delivery(self, location, package): try: if location is None: return False tloc = None if isinstance(location, User) or isinstance( location, DMChannel) else location now = datetime.datetime.fromtimestamp(time.time()) send_time = datetime.datetime.fromtimestamp(package.send) desc = Translator.translate( 'reminder_delivery', tloc, date=send_time.strftime('%c'), timediff=server_info.time_difference( now, send_time, tloc)) + f"```\n{package.to_remind}\n```" desc = Utils.trim_message(desc, 2048) embed = Embed(color=16698189, title=Translator.translate('reminder_delivery_title', tloc), description=desc) if location.id == package.channel_id or package.guild_id == '@me': ref = MessageReference(guild_id=package.guild_id if package.guild_id != '@me' else None, channel_id=package.channel_id, message_id=package.message_id, fail_if_not_exists=False) else: ref = None embed.add_field( name=Translator.translate('jump_link', tloc), value= f'[Click me!]({assemble_jumplink(package.guild_id, package.channel_id, package.message_id)})' ) try: await location.send(embed=embed, reference=ref) except (Forbidden, NotFound): return False else: return True except Exception as ex: await TheRealGearBot.handle_exception("Reminder delivery", self.bot, ex, None, None, None, location, package) return False
async def on_button_click(self, interaction: MessageInteraction): if not interaction.data.custom_id.startswith('steamworks_'): return if not self.bot.is_contributor(interaction.author): return await interaction.response.send_message('You do not have permission to use this.', ephemeral=True) _, build_id, target_branch = interaction.data.custom_id.split('_') res = await self.set_build_live(build_id, target_branch, desc=f'Requested by {interaction.author}') if res['result'] != 1: embed = Embed(title='Build publishing failed.', description=f'Failed to push build to "{target_branch}":\n' f'```\n{res["message"]}\n```') return await interaction.response.send_message(embed=embed) embed = interaction.message.embeds[0] embed.add_field(name='Published', value='Yes') await interaction.response.edit_message(embed=embed, components=None) await interaction.followup.send(content='Build published successfully. Scheduling refresh...', ephemeral=True) # tell bot to fetch update self.bot.loop.create_task(self.build_update())
async def fider(self): items = [] async with self.bot.session.get( 'https://ideas.obsproject.com/api/v1/posts?view=recent') as r: if r.status == 200: feed = await r.json() for entry in feed: if entry['id'] <= self.bot.state['fider_last_id']: continue items.append(entry) else: logger.warning( f'Fetching fider posts failed with status code {r.status}') return for item in reversed(items): if item['id'] > self.bot.state['fider_last_id']: self.bot.state['fider_last_id'] = item['id'] url = f'https://ideas.obsproject.com/posts/{item["id"]}/' logger.info(f'Got new Fider post: {url}') description = item['description'] if len(description) > 180: description = description[:180] + ' [...]' embed = Embed(title=f'{item["id"]}: {item["title"]}', colour=Colour(self._fider_colour), url=url, description=description, timestamp=dateutil.parser.parse(item['createdAt'])) embed.set_author( name='Fider', url='https://ideas.obsproject.com/', icon_url='https://cdn.rodney.io/stuff/obsbot/fider.png') embed.set_footer(text='New Idea on Fider') name = 'Anonymous' if not item['user']['name'] else item['user'][ 'name'] embed.add_field(name='Created By', value=name, inline=True) await self.fider_channel.send(embed=embed)
async def listfilters(self, ctx: Context): if not self.bot.is_admin(ctx.author): return if not self.bot.is_private(ctx.channel): return _ban_filters = [] _kick_filters = [] _delete_filters = [] for name, regex in sorted(self.filters.items()): if name in self.bannable: _ban_filters.append(f'* "{name}" - `{regex.pattern}`') elif name in self.kickable: _kick_filters.append(f'* "{name}" - `{regex.pattern}`') else: _delete_filters.append(f'* "{name}" - `{regex.pattern}`') embed = Embed(title='Registered Message Filters') if _ban_filters: embed.add_field(name='Ban Filters', inline=False, value='```\n{}\n```'.format( '\n'.join(_ban_filters))) if _kick_filters: embed.add_field(name='Kick Filters', inline=False, value='```\n{}\n```'.format( '\n'.join(_kick_filters))) if _delete_filters: embed.add_field(name='Delete Filters', inline=False, value='```\n{}\n```'.format( '\n'.join(_delete_filters))) return await ctx.send(embed=embed)
def gen_emoji_page(self, guild: disnake.Guild, page): se = sorted(guild.emojis, key=lambda e: e.name) embed = Embed(color=0x2db1f3) embed.set_author(name=Translator.translate('emoji_server', guild, server=guild.name, page=page + 1, pages=len(guild.emojis) + 1), url=guild.icon.url) if page == 0: for chunk in Utils.chunks(se, 18): embed.add_field(name="\u200b", value=" ".join(str(e) for e in chunk)) animated = set() static = set() for e in guild.emojis: (animated if e.animated else static).add(str(e)) max_emoji = guild.emoji_limit embed.add_field(name=Translator.translate('static_emoji', guild), value=f"{len(static)} / {max_emoji}") embed.add_field(name=Translator.translate('animated_emoji', guild), value=f"{len(animated)} / {max_emoji}") else: self.add_emoji_info(guild, embed, se[page - 1]) return embed
async def status(self, ctx: Context): if not self.bot.is_admin(ctx.author): return embed = Embed(title='OBS Bot Status') embed.add_field( name='Core', inline=False, value=( f'Version: {__version__} - "{__codename__}" Edition\n' f'Uptime: {time.time() - self.bot.start_time:.0f} seconds\n')) mentions = ', '.join(u.mention for u in (self.bot.get_user(_id) for _id in self.bot.admins) if u) embed.add_field(name='Bot admins', inline=False, value=mentions) # get information from other Cogs if possible if fac := self.bot.get_cog('Factoids'): total_uses = sum(i['uses'] for i in fac.factoids.values()) embed.add_field( name='Factoid module', inline=False, value=(f'Factoids: {len(fac.factoids)}\n' f'Aliases: {len(fac.alias_map)}\n' f'Total uses: {total_uses} (since 2018-06-07)'))
async def get_issue_messages(self, event_body): issue_number = event_body['issue']['number'] title = event_body['issue']['title'] timestamp = dateutil.parser.parse(event_body['issue']['created_at']) embed = Embed(title=f'#{issue_number}: {title}', colour=Colour(self._issue_colour), url=event_body['issue']['html_url'], timestamp=timestamp) author_name = event_body['issue']['user']['login'] author = await self.get_author_info(author_name) if author and author['name'] and author['name'] != author['login']: author_name = f'{author["name"]} ({author["login"]})' embed.set_author(name=author_name, url=event_body['issue']['user']['html_url'], icon_url=event_body['issue']['user']['avatar_url']) embed.set_footer(text='Issue') embed.add_field(name='Repository', value=event_body['repository']['full_name'], inline=True) # create copy without description text for brief channel brief_embed = embed.copy() event_body['issue']['body'] = '\n'.join( l.strip() for l in event_body['issue']['body'].splitlines() if not l.startswith('<!-')) issue_text = event_body['issue']['body'] # strip double-newlines from issue forms if '\n\n' in issue_text: issue_text = issue_text.replace('\n\n', '\n') if len(issue_text) >= 2048: embed.description = issue_text[:2000] + ' [... message trimmed]' else: embed.description = issue_text return brief_embed, embed
async def get_pr_messages(self, event_body): pr_number = event_body['number'] title = event_body['pull_request']['title'] timestamp = dateutil.parser.parse( event_body['pull_request']['created_at']) embed = Embed(title=f'#{pr_number}: {title}', colour=Colour(self._pull_request_colour), url=event_body['pull_request']['html_url'], timestamp=timestamp) author_name = event_body['pull_request']['user']['login'] author = await self.get_author_info(author_name) if author and author['name'] and author['name'] != author['login']: author_name = f'{author["name"]} ({author["login"]})' embed.set_author( name=author_name, url=event_body['pull_request']['user']['html_url'], icon_url=event_body['pull_request']['user']['avatar_url']) embed.set_footer(text='Pull Request') embed.add_field(name='Repository', value=event_body['repository']['full_name'], inline=True) # create copy without description text for brief channel brief_embed = embed.copy() # filter out comments in template event_body['pull_request']['body'] = '\n'.join( l.strip() for l in event_body['pull_request']['body'].splitlines() if not l.startswith('<!-')) # trim message to discord limits if len(event_body['pull_request']['body']) >= 2048: embed.description = event_body['pull_request'][ 'body'][:2000] + ' [... message trimmed]' else: embed.description = event_body['pull_request']['body'] return brief_embed, embed
async def source_command(self, ctx: commands.Context, *, source_item: typing.Optional[str] = None) -> None: """Displays information about the bot's source code.""" if source_item is None: embed = Embed(title="Gurkbot's GitHub Repository") embed.add_field(name="Repository", value=f"[Go to GitHub]({BOT_REPO_URL})") embed.set_thumbnail(url=self.bot.user.display_avatar.url) await ctx.send(embed=embed) return elif not ctx.bot.get_command(source_item): raise commands.BadArgument( f"Unable to convert `{source_item}` to valid command or Cog.") github_source = _source.Source(self.bot.http_session, self.bot.user.display_avatar.url) embed = await github_source.inspect( cmd=ctx.bot.get_command(source_item)) await ctx.send(embed=embed)
async def stats(self, ctx: commands.Context) -> None: """Get the information and current uptime of the bot.""" embed = Embed( title="Bot Stats", color=Colours.green, ) embed.set_thumbnail(url=self.bot.user.display_avatar.url) uptime = humanize.precisedelta( datetime.utcnow().timestamp() - self.bot.launch_time ) fields = { "Python version": python_version(), "Disnake version": __version__, "Uptime": uptime, } for name, value in list(fields.items()): embed.add_field(name=name, value=value, inline=False) await ctx.send(content=ctx.author.mention, embed=embed)
async def get_discussion_messages(self, event_body): discussion_number = event_body['discussion']['number'] title = event_body['discussion']['title'] category = event_body['discussion']['category']['name'] timestamp = dateutil.parser.parse( event_body['discussion']['created_at']) embed = Embed(title=f'#{discussion_number}: {category} - {title}', colour=Colour(self._discussion_colour), timestamp=timestamp, url=event_body['discussion']['html_url']) author_name = event_body['discussion']['user']['login'] author = await self.get_author_info(author_name) if author and author['name'] and author['name'] != author['login']: author_name = f'{author["name"]} ({author["login"]})' embed.set_author( name=author_name, url=event_body['discussion']['user']['html_url'], icon_url=event_body['discussion']['user']['avatar_url']) embed.set_footer(text='Discussion') embed.add_field(name='Repository', value=event_body['repository']['full_name'], inline=True) # create copy without description text for brief channel brief_embed = embed.copy() event_body['discussion']['body'] = '\n'.join( l.strip() for l in event_body['discussion']['body'].splitlines() if not l.startswith('<!-')) if len(event_body['discussion']['body']) >= 1024: embed.description = event_body['discussion'][ 'body'][:1024] + ' [... message trimmed]' else: embed.description = event_body['discussion']['body'] return brief_embed, embed
async def get_wiki_message(self, event_body): embed = Embed(colour=Colour(self._wiki_colour)) embed.set_footer(text='GitHub Wiki Changes') # All edits in the response are from a single author author_name = event_body['sender']['login'] author = await self.get_author_info(author_name) if author and author['name'] and author['name'] != author['login']: author_name = f'{author["name"]} ({author["login"]})' embed.set_author(name=author_name, url=event_body['sender']['html_url'], icon_url=event_body['sender']['avatar_url']) embed.add_field(name='Repository', value=event_body['repository']['full_name']) body = [] for page in event_body['pages']: diff_url = f'{page["html_url"]}/_compare/{page["sha"]}^...{page["sha"]}' page_url = f'{page["html_url"]}/{page["sha"]}' body.append( f'**{page["action"]}:** [{page["title"]}]({page_url}) [[diff]({diff_url})]' ) embed.description = '\n'.join(body) return embed
async def tophardware(self, ctx: Context): embed = Embed(title='Top Hardware') cpus = [] for pos, cpu in enumerate(sorted(self.hardware_stats['cpu'].values(), key=lambda a: a['count'], reverse=True)[:10], start=1): cpus.append(f'{pos:2d}. - {cpu["name"]} ({cpu["count"]})') embed.add_field(name='CPUs', value='```{}```'.format('\n'.join(cpus)), inline=False) gpus = [] for pos, gpu in enumerate(sorted(self.hardware_stats['gpu'].values(), key=lambda a: a['count'], reverse=True)[:10], start=1): gpus.append(f'{pos:2d}. - {gpu["name"]} ({gpu["count"]})') embed.add_field(name='GPUs', value='```{}```'.format('\n'.join(gpus)), inline=False) return await ctx.send(embed=embed)
async def handle_unexpected_error(self, ctx: commands.Context, error: commands.CommandError) -> None: """Send a generic error message in `ctx` and log the exception as an error with exc_info.""" await ctx.send( f"Sorry, an unexpected error occurred. Please let us know!\n\n" f"```{error.__class__.__name__}: {error}```") push_alert = Embed( title="An unexpected error occurred", color=Colours.soft_red, ) push_alert.add_field( name="User", value=f"id: {ctx.author.id} | username: {ctx.author.mention}", inline=False, ) push_alert.add_field(name="Command", value=ctx.command.qualified_name, inline=False) push_alert.add_field( name="Message & Channel", value= f"Message: [{ctx.message.id}]({ctx.message.jump_url}) | Channel: <#{ctx.channel.id}>", inline=False, ) push_alert.add_field(name="Full Message", value=ctx.message.content, inline=False) dev_alerts = self.bot.get_channel(Channels.devalerts) if dev_alerts is None: logger.info( f"Fetching dev-alerts channel as it wasn't found in the cache (ID: {Channels.devalerts})" ) try: dev_alerts = await self.bot.fetch_channel(Channels.devalerts) except disnake.HTTPException as discord_exc: logger.exception("Fetch failed", exc_info=discord_exc) return # Trigger the logger before trying to use Discord in case that's the issue logger.error( f"Error executing command invoked by {ctx.message.author}: {ctx.message.content}", exc_info=error, ) await dev_alerts.send(embed=push_alert)
def delete_permitted_term(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_terms_attrs(mod_action, embed) embed.add_field(name="Removed by", value=f"{info['requester_login']}") if "`" in info["text"]: embed.add_field(name="Value", value=f"```{info['text']}```") else: embed.add_field(name="Value", value=f"`{info['text']}`") embed.remove_field(1) return embed
def ban(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_user_attrs(streamer, info, mod_action, embed) if info['args'][1] == "": embed.add_field(name="Flag Reason", value=f"`None Provided`") else: if "`" in info["args"][1]: embed.add_field(name="Flag Reason", value=f"```{info['args'][1]}```") else: embed.add_field(name="Flag Reason", value=f"`{info['args'][1]}`") return embed
def timeout(self, streamer: Streamer, info, mod_action: ModAction, embed: disnake.Embed) -> disnake.Embed: embed = self.set_user_attrs(streamer, info, mod_action, embed) if info['args'][2] == "": embed.add_field(name="Flag Reason", value=f"`None Provided`") else: if "`" in info["args"][2]: embed.add_field(name="Flag Reason", value=f"```{info['args'][2]}```") else: embed.add_field(name="Flag Reason", value=f"`{info['args'][2]}`") embed.add_field( name="Duration", value= f"{info['args'][1]} second{'' if int(info['args'][1]) == 1 else 's'}" ) #embed.add_field(name="\u200b", value="\u200b") return embed
async def info(self, ctx: Context, name: str.lower): _name = name if name in self.factoids else self.alias_map.get(name) if not _name or _name not in self.factoids: return await ctx.send( f'The specified factoid ("{name}") does not exist!') factoid = self.factoids[_name] message = factoid["message"].replace( '`', '\\`') if factoid["message"] else '<no message>' embed = Embed(title=f'Factoid information: {_name}', description=f'```{message}```') if factoid['aliases']: embed.add_field(name='Aliases', value=', '.join(factoid['aliases'])) embed.add_field(name='Uses (since 2018-06-07)', value=str(factoid['uses'])) embed.add_field(name='Is Embed', value=str(factoid['embed'])) if factoid['image_url']: embed.add_field(name='Image URL', value=factoid['image_url'], inline=False) return await ctx.send(embed=embed)
def append_command_doc_to_embed(self, command: commands.Command, embed: disnake.Embed): if not command.description: return embed.add_field( name="Description:", value=command.brief or "This command appears to not yet have a description.") def _eliminate_match_and_create_field(match: re.Match): # (?P<x>...) matches are returned in ~.groupdict() as {x: ...} instead of the usual # ~.groups() tuple. This can be used to quickly generate an embed field, which requires # 'name' and 'value' params embed.add_field( **match.groupdict(), inline=bool( match[2] ) # 2nd match stores field flags <n_>, atm only 'i' for inline ) # pattern substring that denotes how a name field should/can look, including flags name_flag = r"<n([i]?)>" remainder: str = re.sub( fr"({name_flag}(?P<name>.*?)<v>(?P<value>.*?))(?={name_flag}|\Z)", _eliminate_match_and_create_field, command.description, flags=re.M | re.S) if remainder and not remainder.isspace(): # More than whitespace remains after eliminating fields -> # some data wasn't found, probably unintentional logger.warning( f"Docstr for command '{command.qualified_name}' has remaining " f"content after processing: '{remainder}'. ") return embed