-
-
Notifications
You must be signed in to change notification settings - Fork 667
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create an admin task when votes are 2 weeks old #2323
Changes from 19 commits
4f235c8
b1845e5
99d9662
18e7cbe
71d6ebc
293dac8
f6f3e55
33828c8
ecc29bd
8bd6840
c4be56d
346be1a
ed728d4
506fbb2
0ba1ddf
9773aca
0da9f16
fef37fe
9259f84
161df87
4b0d49b
154c51d
802003b
5a916ac
68c625c
3ca37a0
1c8059a
da3c904
59319b4
33415da
babf906
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,8 +1,9 @@ | ||||||
import asyncio | ||||||
import textwrap | ||||||
from io import StringIO | ||||||
from typing import Optional, Union | ||||||
from typing import List, Optional, Tuple, Union | ||||||
|
||||||
import arrow | ||||||
import discord | ||||||
from async_rediscache import RedisCache | ||||||
from botcore.site_api import ResponseCodeError | ||||||
|
@@ -11,18 +12,23 @@ | |||||
from discord.ext.commands import BadArgument, Cog, Context, group, has_any_role | ||||||
|
||||||
from bot.bot import Bot | ||||||
from bot.constants import Bot as BotConfig, Channels, Emojis, Guild, MODERATION_ROLES, Roles, STAFF_ROLES | ||||||
from bot.constants import ( | ||||||
Bot as BotConfig, Channels, Emojis, GithubAdminRepo, Guild, MODERATION_ROLES, Roles, STAFF_ROLES | ||||||
) | ||||||
from bot.converters import MemberOrUser, UnambiguousMemberOrUser | ||||||
from bot.exts.recruitment.talentpool._review import Reviewer | ||||||
from bot.log import get_logger | ||||||
from bot.pagination import LinePaginator | ||||||
from bot.utils import time | ||||||
from bot.utils.channel import get_or_fetch_channel | ||||||
from bot.utils.members import get_or_fetch_member | ||||||
|
||||||
from ._api import Nomination, NominationAPI | ||||||
|
||||||
AUTOREVIEW_ENABLED_KEY = "autoreview_enabled" | ||||||
FLAG_EMOJI = "🎫" | ||||||
REASON_MAX_CHARS = 1000 | ||||||
OLD_NOMINATIONS_THRESHOLD_IN_DAYS = 14 | ||||||
|
||||||
log = get_logger(__name__) | ||||||
|
||||||
|
@@ -43,6 +49,7 @@ def __init__(self, bot: Bot) -> None: | |||||
|
||||||
async def cog_load(self) -> None: | ||||||
"""Start autoreview loop if enabled.""" | ||||||
self.track_forgotten_nominations.start() | ||||||
if await self.autoreview_enabled(): | ||||||
self.autoreview_loop.start() | ||||||
|
||||||
|
@@ -111,6 +118,81 @@ async def autoreview_status(self, ctx: Context) -> None: | |||||
else: | ||||||
await ctx.send("Autoreview is currently disabled.") | ||||||
|
||||||
@tasks.loop(hours=72) | ||||||
async def track_forgotten_nominations(self) -> None: | ||||||
"""Track active nominations who are more than 2 weeks old.""" | ||||||
old_nominations = await self._get_forgotten_nominations() | ||||||
nomination_details = await self._filter_out_tracked_nominations(old_nominations) | ||||||
for nomination, nomination_vote_message in nomination_details: | ||||||
issue_created = await self._track_vote_in_github(nomination) | ||||||
if issue_created: | ||||||
await nomination_vote_message.add_reaction(FLAG_EMOJI) | ||||||
|
||||||
async def _get_forgotten_nominations(self) -> List[Nomination]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This can be done since Python 3.9. There are more examples in this PR where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 68c625c There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't resolved. |
||||||
"""Get active nominations that are more than 2 weeks old.""" | ||||||
now = arrow.utcnow() | ||||||
nominations = [ | ||||||
nomination | ||||||
for nomination in await self.api.get_nominations(active=True) | ||||||
if (now - nomination.inserted_at).days >= OLD_NOMINATIONS_THRESHOLD_IN_DAYS | ||||||
shtlrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
] | ||||||
return nominations | ||||||
|
||||||
async def _filter_out_tracked_nominations( | ||||||
self, | ||||||
nominations: List[Nomination] | ||||||
) -> List[Tuple[Nomination, discord.Message]]: | ||||||
"""Filter the forgotten nominations that are still untracked in GitHub.""" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't just a filter, since it returns more data that it was given. This should be described in the docstring, and maybe change the function name to make it clear it isn't just a filter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alrighty, fixed in 1c8059a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't resolved. |
||||||
untracked_nominations = [] | ||||||
|
||||||
for nomination in nominations: | ||||||
# We avoid the scenario of this task run & nomination created at the same time | ||||||
if not nomination.thread_id: | ||||||
continue | ||||||
|
||||||
thread = await get_or_fetch_channel(nomination.thread_id) | ||||||
shtlrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
if not thread: | ||||||
# Thread was deleted | ||||||
continue | ||||||
|
||||||
starter_message = thread.starter_message | ||||||
if not starter_message: | ||||||
# Starter message will be null if it's not cached | ||||||
starter_message = await self.bot.get_channel(Channels.nomination_voting).fetch_message(thread.id) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. handle the case where this raises an error if the message doesn't exist There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in 5a916ac There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't resolved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Linking the commit here 5a916ac |
||||||
|
||||||
if not starter_message: | ||||||
# Starter message deleted | ||||||
continue | ||||||
|
||||||
if FLAG_EMOJI in [reaction.emoji for reaction in starter_message.reactions]: | ||||||
# Nomination has been already tracked in GitHub | ||||||
continue | ||||||
|
||||||
untracked_nominations.append((nomination, starter_message)) | ||||||
return untracked_nominations | ||||||
|
||||||
async def _track_vote_in_github(self, nomination: Nomination) -> bool: | ||||||
""" | ||||||
Adds an issue in GitHub to track dormant vote. | ||||||
|
||||||
Returns True when the issue has been created, False otherwise. | ||||||
""" | ||||||
if not GithubAdminRepo.token: | ||||||
log.warn(f"No token for the {GithubAdminRepo.name} repository was provided, skipping issue creation.") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
warn is a deprecated alias to warning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't resolved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 3ca37a0 |
||||||
return False | ||||||
|
||||||
url = f"https://api.github.com/repos/{GithubAdminRepo.owner}/{GithubAdminRepo.name}/issues" | ||||||
headers = { | ||||||
"Accept": "application/vnd.github.v3+json", | ||||||
"Authorization": f"Bearer {GithubAdminRepo.token}" | ||||||
} | ||||||
member = await get_or_fetch_member(self.bot.get_guild(Guild.id), nomination.user_id) | ||||||
ChrisLovering marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
data = {"title": f"Nomination review needed. Id: {nomination.id}. User: {member.name}"} | ||||||
ChrisLovering marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
async with self.bot.http_session.post(url=url, raise_for_status=True, headers=headers, json=data) as response: | ||||||
# REVIEW: Might be useful to add logs here ? | ||||||
shtlrs marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
return response.status == 201 | ||||||
|
||||||
@tasks.loop(hours=1) | ||||||
async def autoreview_loop(self) -> None: | ||||||
"""Send request to `reviewer` to send a nomination if ready.""" | ||||||
|
@@ -489,13 +571,22 @@ async def _nomination_to_string(self, nomination: Nomination) -> str: | |||||
entries_string = "\n\n".join(entries) | ||||||
|
||||||
start_date = time.discord_timestamp(nomination.inserted_at) | ||||||
|
||||||
thread = None | ||||||
|
||||||
if nomination.thread_id: | ||||||
thread = await get_or_fetch_channel(nomination.thread_id) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle the case where this raises an error if the thread doesn't exist. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's actually handled in the original PR that adds the thread id to the nominations table. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be visible in e68d756 |
||||||
|
||||||
thread_jump_url = f'[Jump to thread!]({thread.jump_url})' if thread else "*Not created*" | ||||||
|
||||||
if nomination.active: | ||||||
lines = textwrap.dedent( | ||||||
f""" | ||||||
=============== | ||||||
Status: **Active** | ||||||
Date: {start_date} | ||||||
Nomination ID: `{nomination.id}` | ||||||
Nomination vote thread: {thread_jump_url} | ||||||
|
||||||
{entries_string} | ||||||
=============== | ||||||
|
@@ -509,6 +600,7 @@ async def _nomination_to_string(self, nomination: Nomination) -> str: | |||||
Status: Inactive | ||||||
Date: {start_date} | ||||||
Nomination ID: `{nomination.id}` | ||||||
Nomination vote thread: {thread_jump_url} | ||||||
|
||||||
{entries_string} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
from bot.exts.recruitment.talentpool._api import Nomination, NominationAPI | ||
from bot.log import get_logger | ||
from bot.utils import time | ||
from bot.utils.channel import get_or_fetch_channel | ||
from bot.utils.members import get_or_fetch_member | ||
from bot.utils.messages import count_unique_users_reaction, pin_no_system_message | ||
|
||
|
@@ -180,7 +181,7 @@ async def post_review(self, nomination: Nomination) -> None: | |
) | ||
message = await thread.send(f"<@&{Roles.mod_team}> <@&{Roles.admins}>") | ||
|
||
await self.api.edit_nomination(nomination.id, reviewed=True) | ||
await self.api.edit_nomination(nomination.id, reviewed=True, thread_id=thread.id) | ||
|
||
bump_cog: ThreadBumper = self.bot.get_cog("ThreadBumper") | ||
if bump_cog: | ||
|
@@ -433,11 +434,20 @@ async def _previous_nominations_review(self, member: Member) -> Optional[str]: | |
|
||
nomination_times = f"{num_entries} times" if num_entries > 1 else "once" | ||
rejection_times = f"{len(history)} times" if len(history) > 1 else "once" | ||
threads = [await get_or_fetch_channel(nomination.thread_id) for nomination in history] | ||
|
||
nomination_vote_threads = ", ".join( | ||
[ | ||
f"[Thread-{thread_number}]({thread.jump_url})" if thread else '' | ||
for thread_number, thread in enumerate(threads, start=1) | ||
] | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change doesn't seem related to the issue, so might be worth splitting to another PR, since there's some more logic you'll need here.
In your There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That change is due to the fact that my other PR that hasn't been merged is needed, so I merged it here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think commit e68d756 should hold the answer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And now that my previously mentioned PR has been merged, these changes no longer exist |
||
end_time = time.format_relative(history[0].ended_at) | ||
|
||
review = ( | ||
f"They were nominated **{nomination_times}** before" | ||
f", but their nomination was called off **{rejection_times}**." | ||
f"\nList of all of their nomination threads: {nomination_vote_threads}" | ||
f"\nThe last one ended {end_time} with the reason: {history[0].end_reason}" | ||
) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make this configurable to run just like we do for autoreview ?