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 startup_greeting(self) -> None: """Announce presence to the devlog channel.""" embed = Embed(description="Connected!") embed.set_author( name="Gurkbot", url=constants.BOT_REPO_URL, icon_url=self.user.display_avatar.url, ) await self.get_channel(constants.Channels.devlog).send(embed=embed)
async def _send_paginated_embed(ctx: Context, lines: Iterable[str], title: str) -> None: """Send paginated embed.""" embed = Embed() embed.set_author(name=title) await LinePaginator.paginate( [f"{i}. {line}" for i, line in enumerate(lines, start=1)], ctx, embed, allow_empty_lines=True, )
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 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 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_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 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
async def handle_exception(exception_type, bot, exception, event=None, message=None, ctx=None, *args, **kwargs): if bot is not None: bot.errors = bot.errors + 1 with sentry_sdk.push_scope() as scope: embed = Embed(colour=Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp( time.time()).replace(tzinfo=datetime.timezone.utc)) # something went wrong and it might have been in on_command_error, make sure we log to the log file first lines = [ "\n===========================================EXCEPTION CAUGHT, DUMPING ALL AVAILABLE INFO===========================================", f"Type: {exception_type}" ] arg_info = "" for arg in list(args): arg_info += extract_info(arg) + "\n" if arg_info == "": arg_info = "No arguments" kwarg_info = "" for name, arg in kwargs.items(): kwarg_info += "{}: {}\n".format(name, extract_info(arg)) if kwarg_info == "": kwarg_info = "No keyword arguments" lines.append("======================Exception======================") lines.append(f"{str(exception)} ({type(exception)})") lines.append("======================ARG INFO======================") lines.append(arg_info) sentry_sdk.add_breadcrumb(category='arg info', message=arg_info, level='info') lines.append("======================KWARG INFO======================") lines.append(kwarg_info) sentry_sdk.add_breadcrumb(category='kwarg info', message=kwarg_info, level='info') lines.append("======================STACKTRACE======================") tb = "".join(traceback.format_tb(exception.__traceback__)) lines.append(tb) if message is None and event is not None and hasattr(event, "message"): message = event.message if message is None and ctx is not None: message = ctx.message if message is not None and hasattr(message, "content"): lines.append( "======================ORIGINAL MESSAGE======================") lines.append(message.content) if message.content is None or message.content == "": content = "<no content>" else: content = message.content scope.set_tag('message content', content) embed.add_field(name="Original message", value=Utils.trim_message(content, 1000), inline=False) lines.append( "======================ORIGINAL MESSAGE (DETAILED)======================" ) lines.append(extract_info(message)) if event is not None: lines.append( "======================EVENT NAME======================") lines.append(event) scope.set_tag('event name', event) embed.add_field(name="Event", value=event) if ctx is not None: lines.append( "======================COMMAND INFO======================") lines.append(f"Command: {ctx.command.name}") embed.add_field(name="Command", value=ctx.command.name) scope.set_tag('command', ctx.command.name) channel_name = 'Private Message' if isinstance( ctx.channel, PrivateChannel) else f"{ctx.channel.name} (`{ctx.channel.id}`)" lines.append(f"Channel: {channel_name}") embed.add_field(name="Channel", value=channel_name, inline=False) scope.set_tag('channel', channel_name) sender = f"{str(ctx.author)} (`{ctx.author.id}`)" scope.user = dict(id=ctx.author.id, username=str(ctx.author)) lines.append(f"Sender: {sender}") embed.add_field(name="Sender", value=sender, inline=False) lines.append( "===========================================DATA DUMP COMPLETE===========================================" ) GearbotLogging.error("\n".join(lines)) for t in [ConnectionClosed, ClientOSError, ServerDisconnectedError]: if isinstance(exception, t): return #nice embed for info on discord embed.set_author(name=exception_type) embed.add_field(name="Exception", value=f"{str(exception)} (`{type(exception)}`)", inline=False) parts = Pages.paginate(tb, max_chars=1024) num = 1 for part in parts: embed.add_field(name=f"Traceback {num}/{len(parts)}", value=part) num += 1 sentry_sdk.capture_exception(exception) # try logging to botlog, wrapped in an try catch as there is no higher lvl catching to prevent taking down the bot (and if we ended here it might have even been due to trying to log to botlog try: await GearbotLogging.bot_log(embed=embed) except Exception as ex: GearbotLogging.error( f"Failed to log to botlog, either Discord broke or something is seriously wrong!\n{ex}" ) GearbotLogging.error(traceback.format_exc())
self.state['nightly_macos'] = self.config[ 'artifact_service'].format(artifact['id']) elif 'win-x64' in artifact['name']: self.state['nightly_windows'] = self.config[ 'artifact_service'].format(artifact['id']) artifacts_entries.append( f'[{artifact["name"]}]({artifact["archive_download_url"]})') embed = Embed(title=f'Build {run["run_number"]} {build_result}', url=web_url, description='\n'.join(message), timestamp=finished, colour=colour) embed.set_author( name='GitHub Actions', icon_url='https://cdn.rodney.io/stuff/obsbot/github_actions.png') embed.add_field(name="Project", value=repo, inline=True) embed.add_field(name="Branch", value=branch, inline=True) # only attach artifact urls if build succeeded if build_success: embed.add_field(name='Artifacts', inline=False, value=', '.join(artifacts_entries)) return build_success, embed, (commit_hash, message[0], reaction_emote, web_url) async def get_with_retry(self, url, params=None,
async def updater(cog: BCVersionChecker): GearbotLogging.info("Started BC version checking background task") session: aiohttp.ClientSession = cog.bot.aiosession lastUpdate = 0 while cog.running: try: # check for a newer bc version async with session.get( 'https://www.mod-buildcraft.com/build_info_full/last_change.txt' ) as reply: stamp = await reply.text() stamp = int(stamp[:-1]) if stamp > lastUpdate: GearbotLogging.info("New BC version somewhere!") lastUpdate = stamp cog.BC_VERSION_LIST = await getList(session, "BuildCraft") cog.BCC_VERSION_LIST = await getList( session, "BuildCraftCompat") highestMC = VersionInfo.getLatest( cog.BC_VERSION_LIST.keys()) latestBC = VersionInfo.getLatest( cog.BC_VERSION_LIST[highestMC]) generalID = 309218657798455298 channel: disnake.TextChannel = cog.bot.get_channel( generalID) old_latest = Configuration.get_persistent_var( "latest_bc", "0.0.0") Configuration.set_persistent_var( "latest_bc", latestBC ) # save already so we don't get stuck and keep trying over and over if something goes wrong if channel is not None and latestBC != old_latest: GearbotLogging.info( f"New BuildCraft version found: {latestBC}") notify_channel = cog.bot.get_channel( 349517224320565258) await notify_channel.send( f"{Emoji.get_chat_emoji('WRENCH')} New BuildCraft version detected ({latestBC})" ) GearbotLogging.info( f"Fetching metadata for BuildCraft {latestBC}") message = await notify_channel.send( f"{Emoji.get_chat_emoji('REFRESH')} Fetching metadata..." ) info = await cog.getVersionDetails( "BuildCraft", latestBC) GearbotLogging.info(f"Metadata acquired: {info}") await message.edit( content= f"{Emoji.get_chat_emoji('YES')} Metadata acquired") if 'blog_entry' in info: message = await notify_channel.send( f"{Emoji.get_chat_emoji('REFRESH')} Updating general topic..." ) newTopic = f"General discussions about BuildCraft.\n" \ f"Latest version: {latestBC}\n" \ f"Full changelog and download: {info['blog_entry']}" await channel.edit(topic=newTopic) await message.edit( content= f"{Emoji.get_chat_emoji('YES')} Topic updated") else: notify_channel.send( f"{Emoji.get_chat_emoji('WARNING')} No blog post data found, notifying <@180057061353193472>" ) # message = await notify_channel.send(f"{Emoji.get_chat_emoji('REFRESH')} Uploading files to CurseForge...") # code, output, errors = await Utils.execute(f'cd BuildCraft/uploader && gradle curseforge -Pnew_version="{latestBC}"') # GearbotLogging.info(f"Upload to CF complete\n)------stdout------\n{output}\n------stderr------\n{errors}") # if code is 0: # content = f"{Emoji.get_chat_emoji('YES')} All archives successfully uploaded" # await message.edit(content=content) # else: # content = f"{Emoji.get_chat_emoji('NO')} Upload failed with code {code}, notifying <@106354106196570112>" # await notify_channel.send(content) # update FAQs if needed async with session.get( 'https://mod-buildcraft.com/website_src/faq.md') as reply: data = await reply.text() h = hashlib.md5(data.encode('utf-8')).hexdigest() old = Configuration.get_persistent_var("BCFAQ", "") channel = cog.bot.get_channel(361557801492938762) # FAQs if channel is not None and h != old: Configuration.set_persistent_var("BCFAQ", h) #clean the old stuff await channel.purge() #send banner with open("BuildCraft/FAQs.png", "rb") as file: await channel.send(file=File(file, filename="FAQs.png") ) #send content out = "" parts = [ d.strip("#").strip() for d in data.split("##")[1:] ] for part in parts: lines = part.splitlines() content = '\n'.join(lines[1:]) out += f"**```{lines[0].strip()}```**{content}\n" for page in Pages.paginate(out, max_chars=2048, max_lines=50): embed = Embed(description=page) await channel.send(embed=embed) pass except CancelledError: pass # bot shutdown except Exception as ex: cog.bot.errors = cog.bot.errors + 1 GearbotLogging.error( "Something went wrong in the BC version checker task") GearbotLogging.error(traceback.format_exc()) embed = disnake.Embed(colour=disnake.Colour(0xff0000), timestamp=datetime.datetime.utcfromtimestamp( time.time())) embed.set_author( name="Something went wrong in the BC version checker task:") embed.add_field(name="Exception", value=str(ex)) v = "" for line in traceback.format_exc().splitlines(): if len(v) + len(line) >= 1024: embed.add_field(name="Stacktrace", value=v) v = "" v = f"{v}\n{line}" if len(v) > 0: embed.add_field(name="Stacktrace", value=v) await GearbotLogging.bot_log(embed=embed) for i in range(1, 60): if cog.force or not cog.running: break await asyncio.sleep(10) GearbotLogging.info("BC version checking background task terminated")