async def issues_handler(self, data): issue = data['issue'] issue_num = issue['number'] action = data['action'] if issue['user']['login'] == 'taine-bot': return # we only really care about opened or closed if action == "closed": try: report = Report.from_github(issue_num) except ReportException: # report not found return # oh well await report.resolve(ContextProxy(self.bot), None, False) report.commit() elif action in ("opened", "reopened"): # is the issue new? try: report = Report.from_github(issue_num) except ReportException: # report not found report = Report.from_issue(issue) await GitHubClient.get_instance().add_issue_comment( issue['number'], f"Tracked as `{report.report_id}`.") await report.unresolve(ContextProxy(self.bot), None, False) report.commit()
async def report_opened(self, data): issue = data['issue'] issue_num = issue['number'] repo_name = data['repository']['full_name'] # is the issue new? try: report = Report.from_github(repo_name, issue_num) except ReportException: # report not found issue_labels = [lab['name'] for lab in issue['labels']] if EXEMPT_LABEL in issue_labels: return None report = Report.new_from_issue(repo_name, issue) if not issue['title'].startswith(report.report_id): formatted_title = f"{report.report_id} {report.title}" await GitHubClient.get_instance().rename_issue( repo_name, issue['number'], formatted_title) # await GitHubClient.get_instance().add_issue_to_project(report.github_issue, report.is_bug) await GitHubClient.get_instance().add_issue_comment( repo_name, issue['number'], f"Tracked as `{report.report_id}`.") await report.update_labels() await report.unresolve(ContextProxy(self.bot), open_github_issue=False) report.commit() return report
def test_create(): report = Report("1", "AVR-001", "test", 6, 0, [], None) assert report.reporter == "1" assert report.report_id == "AVR-001" assert report.title == "test" assert report.severity == 6 assert report.verification == 0 assert report.attachments == [] assert report.message is None report_dict = report.to_dict() new_report = Report.from_dict(report_dict) assert report.__dict__ == new_report.__dict__
async def update(self, ctx, build_id, *, msg=""): """Owner only - To be run after an update. Resolves all -P2 reports.""" if not ctx.message.author.id == constants.OWNER_ID: return changelog = DiscordEmbedTextPaginator() async for report_data in query( db.reports, Attr("pending").eq(True)): # find all pending=True reports report = Report.from_dict(report_data) await report.resolve(ctx, f"Patched in build {build_id}", ignore_closed=True) report.pending = False report.commit() action = "Fixed" if not report.is_bug: action = "Added" if report.get_issue_link(): changelog.add( f"- {action} [`{report.report_id}`]({report.get_issue_link()}) {report.title}" ) else: changelog.add( f"- {action} `{report.report_id}` {report.title}") changelog.add(msg) embed = discord.Embed(title=f"**Build {build_id}**", colour=0x87d37c) changelog.write_to(embed) await ctx.send(embed=embed) await ctx.message.delete()
async def pending_list(self, ctx): out = [] async for report_data in query(db.reports, Attr("pending").eq(True)): out.append(Report.from_dict(report_data).report_id) out = ', '.join(f"`{_id}`" for _id in out) await ctx.send(f"Pending reports: {out}")
def run(): with open("reports.json") as f: reports = json.load(f) for report_id, report in reports.items(): print(report_id) for attachment in report['attachments']: try: attachment['author'] = int(attachment['author']) except ValueError: pass attachment['message'] = attachment.pop('msg') new_report = Report.from_dict(report) try: new_report.reporter = int(new_report.reporter) except ValueError: pass if new_report.message: new_report.message = int(new_report.message) new_report.is_bug = new_report.report_id.startswith("AFR") new_report.subscribers = list(map(int, new_report.subscribers)) reports[report_id] = new_report.to_dict() with open("new-reports.json", 'w') as f: json.dump(reports, f)
async def unresolve(ctx, _id, *, msg=''): """Owner only - Unresolves a report.""" if not ctx.message.author.id == OWNER_ID: return report = Report.from_id(_id) await report.unresolve(ctx, msg) report.commit() await bot.say(f"Unresolved `{report.report_id}`: {report.title}.")
async def update(ctx, build_id: int, *, msg=""): """Owner only - To be run after an update. Resolves all -P2 reports.""" if not ctx.message.author.id == OWNER_ID: return changelog = "" for _id in bot.db.jget("pending-reports", []): report = Report.from_id(_id) await report.resolve(ctx, f"Patched in build {build_id}", ignore_closed=True) report.commit() action = "Fixed" if report.report_id.startswith("AFR"): action = "Added" if report.get_issue_link(): changelog += f"- {action} [`{report.report_id}`]({report.get_issue_link()}) {report.title}\n" else: changelog += f"- {action} `{report.report_id}` {report.title}\n" changelog += msg bot.db.jset("pending-reports", []) await bot.send_message(ctx.message.channel, embed=discord.Embed(title=f"**Build {build_id}**", description=changelog, colour=0x87d37c)) await bot.delete_message(ctx.message)
async def note(ctx, _id, *, msg=''): """Adds a note to a report.""" report = Report.from_id(_id) await report.addnote(ctx.message.author.id, msg) report.commit() await bot.say(f"Ok, I've added a note to `{report.report_id}` - {report.title}.") await report.update(ctx)
async def downvote(ctx, _id, *, msg=''): """Adds a downvote to the selected feature request.""" report = Report.from_id(_id) await report.downvote(ctx.message.author.id, msg) report.commit() await bot.say(f"Ok, I've added a note to `{report.report_id}` - {report.title}.") await report.update(ctx)
async def reidentify(self, ctx, report_id, identifier): """Owner only - Changes the identifier of a report.""" if not ctx.message.author.id == constants.OWNER_ID: return identifier = identifier.upper() id_num = get_next_report_num(identifier) report = Report.from_id(report_id) new_report = copy.copy(report) await report.resolve(ctx, f"Reassigned as `{identifier}-{id_num}`.", False) report.commit() new_report.report_id = f"{identifier}-{id_num}" msg = await self.bot.get_channel(constants.TRACKER_CHAN ).send(embed=new_report.get_embed()) new_report.message = msg.id if new_report.github_issue: await new_report.update_labels() await new_report.edit_title( f"{new_report.report_id} {new_report.title}") new_report.commit() await ctx.send( f"Reassigned {report.report_id} as {new_report.report_id}.")
async def resolve(self, ctx, _id, *, msg=''): """Owner only - Resolves a report.""" if not ctx.message.author.id == constants.OWNER_ID: return report = Report.from_id(_id) await report.resolve(ctx, msg) report.commit() await ctx.send(f"Resolved `{report.report_id}`: {report.title}.")
async def cannotrepro(ctx, _id, *, msg=''): """Adds nonreproduction to a report.""" report = Report.from_id(_id) await report.cannotrepro(ctx.message.author.id, msg, ctx) report.commit() await bot.say( f"Ok, I've added a note to `{report.report_id}` - {report.title}.") await report.update(ctx)
async def upvote(ctx, _id, *, msg=''): """Adds an upvote to the selected feature request.""" report = Report.from_id(_id) await report.upvote(ctx.message.author.id, msg, ctx) report.subscribe(ctx) await report.update(ctx) report.commit() await ctx.send( f"Ok, I've added a note to `{report.report_id}` - {report.title}.")
async def attach(ctx, report_id, message_id): """Attaches a recent message to a report.""" report = Report.from_id(report_id) try: msg = next(m for m in bot.messages if m.id == message_id) except StopIteration: return await bot.say("I cannot find that message.") await report.addnote(msg.author.id, msg.content) report.commit() await bot.say(f"Ok, I've added a note to `{report.report_id}` - {report.title}.") await report.update(ctx)
async def priority(ctx, _id, pri: int, *, msg=''): """Owner only - Changes the priority of a report.""" if not ctx.message.author.id == OWNER_ID: return report = Report.from_id(_id) report.severity = pri if msg: report.addnote(ctx.message.author.id, f"Priority changed to {pri} - {msg}") report.commit() await report.update(ctx) await bot.say(f"Changed priority of `{report.report_id}`: {report.title} to P{pri}.")
async def subscribe(ctx, report_id): """Subscribes to a report.""" report = Report.from_id(report_id) if ctx.message.author.id in report.subscribers: report.unsubscribe(ctx) await ctx.send( f"OK, unsubscribed from `{report.report_id}` - {report.title}.") else: report.subscribe(ctx) await ctx.send( f"OK, subscribed to `{report.report_id}` - {report.title}.") report.commit()
async def rename(self, ctx, report_id, *, name): """Owner only - Changes the title of a report.""" if not ctx.message.author.id == constants.OWNER_ID: return report = Report.from_id(report_id) report.title = name if report.github_issue: await report.edit_title(f"{report.report_id} {report.title}") await report.update(ctx) report.commit() await ctx.send(f"Renamed {report.report_id} as {report.title}.")
async def update(ctx, build_id: int): """Owner only - To be run after an update. Resolves all -P2 reports.""" if not ctx.message.author.id == OWNER_ID: return changelog = f"**Build {build_id}**\n" for _id, raw_report in bot.db.jget("reports", {}).items(): report = Report.from_dict(raw_report) if not report.severity == -2: continue await report.resolve(ctx, f"Patched in build {build_id}") report.commit() changelog += f"- `{report.report_id}` {report.title}\n" await bot.send_message(ctx.message.channel, changelog) await bot.delete_message(ctx.message)
async def issues_handler(self, data): issue = data['issue'] issue_num = issue['number'] action = data['action'] if data['sender']['login'] == 'taine-bot': return # we only really care about opened or closed if action == "closed": try: report = Report.from_github(issue_num) except ReportException: # report not found return # oh well await report.resolve(ContextProxy(self.bot), None, False, pend=True) report.commit() elif action in ("opened", "reopened"): # is the issue new? try: report = Report.from_github(issue_num) except ReportException: # report not found report = Report.from_issue(issue) if not issue['title'].startswith(report.report_id): formatted_title = re.sub(r'^([A-Z]{3}(-\d+)?\s)?', f"{report.report_id} ", issue['title']) await GitHubClient.get_instance().rename_issue( issue['number'], formatted_title) await GitHubClient.get_instance().add_issue_comment( issue['number'], f"Tracked as `{report.report_id}`.") await report.update_labels() await report.unresolve(ContextProxy(self.bot), None, False) report.commit()
async def report_closed(self, data): issue = data['issue'] issue_num = issue['number'] repo_name = data['repository']['full_name'] try: report = Report.from_github(repo_name, issue_num) except ReportException: # report not found return # oh well pend = data['sender']['login'] == constants.OWNER_GITHUB await report.resolve(ContextProxy(self.bot), close_github_issue=False, pend=pend) report.commit()
async def pending(ctx, *reports): """Owner only - Marks reports as pending for next patch.""" if not ctx.message.author.id == OWNER_ID: return not_found = 0 for _id in reports: try: report = Report.from_id(_id) except ReportException: not_found += 1 continue report.severity = -2 report.commit() await report.update(ctx) if not not_found: await bot.say(f"Marked {len(reports)} reports as patch pending.") else: await bot.say(f"Marked {len(reports)} reports as patch pending. {not_found} reports were not found.")
async def reidentify(ctx, report_id, identifier): """Owner only - Changes the identifier of a report.""" if not ctx.message.author.id == OWNER_ID: return identifier = identifier.upper() id_num = get_next_report_num(identifier) report = Report.from_id(report_id) new_report = copy.copy(report) await report.resolve(ctx, f"Reassigned as `{identifier}-{id_num}`.", False) report.commit() new_report.report_id = f"{identifier}-{id_num}" msg = await bot.send_message(bot.get_channel(TRACKER_CHAN), embed=new_report.get_embed()) new_report.message = msg.id new_report.commit() await bot.say(f"Reassigned {report.report_id} as {new_report.report_id}.")
async def priority(self, ctx, _id, pri: int, *, msg=''): """Owner only - Changes the priority of a report.""" if not ctx.message.author.id == constants.OWNER_ID: return report = Report.from_id(_id) report.severity = pri if msg: await report.addnote(ctx.message.author.id, f"Priority changed to {pri} - {msg}", ctx) if report.github_issue: await report.update_labels() await report.update(ctx) report.commit() await ctx.send( f"Changed priority of `{report.report_id}`: {report.title} to P{pri}." )
async def unpend(self, ctx, *reports): if not ctx.message.author.id == constants.OWNER_ID: return not_found = 0 for _id in reports: try: report = Report.from_id(_id) except ReportException: not_found += 1 continue report.unpend() await report.update(ctx) report.commit() if not not_found: await ctx.send(f"Unpended {len(reports)} reports.") else: await ctx.send( f"Unpended {len(reports) - not_found} reports. {not_found} reports were not found." )
async def issue_comment_handler(self, data): issue = data['issue'] issue_num = issue['number'] comment = data['comment'] action = data['action'] username = comment['user']['login'] if username == "taine-bot": return # don't infinitely add comments # only care about create if action == "created": try: report = Report.from_github(issue_num) except ReportException: return # oh well await report.addnote(f"GitHub - {username}", comment['body'], ContextProxy(self.bot), False) report.commit() await report.update(ContextProxy(self.bot))
async def handle_reaction(self, msg_id, member, emoji): if msg_id == README_MSG_ID: if emoji.id == BUG_HUNTER_REACTION_ID: return await self.toggle_role(member, id=BUG_HUNTER_ROLE_ID) elif emoji.id == ACCEPT_REACTION_ID: return await self.toggle_role(member, id=ACCEPT_ROLE_ID) if emoji.name not in (UPVOTE_REACTION, DOWNVOTE_REACTION): return try: report = Report.from_message_id(msg_id) except ReportException: return if report.is_bug: return if member.bot: return if member.id == constants.OWNER_ID: if emoji.name == UPVOTE_REACTION: await report.force_accept(ContextProxy(self.bot)) else: print(f"Force denying {report.title}") await report.force_deny(ContextProxy(self.bot)) report.commit() return else: try: if emoji.name == UPVOTE_REACTION: await report.upvote(member.id, '', ContextProxy(self.bot)) else: await report.downvote(member.id, '', ContextProxy(self.bot)) except ReportException as e: await member.send(str(e)) if member.id not in report.subscribers: report.subscribers.append(member.id) report.commit() await report.update(ContextProxy(self.bot))
async def report_labeled(self, data): await asyncio.sleep( 10) # prevent a race condition when an issue is newly created issue = data['issue'] issue_num = issue['number'] repo_name = data['repository']['full_name'] label_names = [l['name'] for l in issue['labels']] if len( [l for l in label_names if any(n in l for n in PRI_LABEL_NAMES)]) > 1: return # multiple priority labels if len([ l for l in label_names if l in (BUG_LABEL, FEATURE_LABEL, EXEMPT_LABEL) ]) > 1: return # multiple type labels try: report = Report.from_github(repo_name, issue_num) except ReportException: # report not found report = await self.report_opened(data) if report is None: # this only happens if we try to create a report off an enhancement label return # we don't want to track it anyway ctx = ContextProxy(self.bot) if EXEMPT_LABEL in label_names: # issue changed from bug/fr to enhancement await report.untrack(ctx) else: priority = report.severity for i, pri in enumerate(PRI_LABEL_NAMES): if any(pri in n for n in label_names): priority = i break report.severity = priority report.is_bug = FEATURE_LABEL not in label_names await report.update(ctx) report.commit()
async def viewreport(ctx, _id): """Gets the detailed status of a report.""" await ctx.send(embed=Report.from_id(_id).get_embed(True, ctx))