Skip to content
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

action_items: add reactions #59

Merged
merged 6 commits into from
Jan 14, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 130 additions & 1 deletion src/extensions/action_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,20 @@ async def get_action_items(

# send each bullet point separately
for item in formatted_bullet_points:
await action_items.client.rest.create_message(
item = await action_items.client.rest.create_message(
CHANNEL_IDS["action-items"],
mentions_everyone=False,
user_mentions=True,
role_mentions=True,
content=item,
)

await action_items.client.rest.add_reaction(
channel=item.channel_id,
message=item.id,
emoji="✅",
)

# respond with success if it executes successfully
await ctx.respond(
"✅ Action Items sent successfully!",
Expand All @@ -111,6 +117,129 @@ async def get_action_items(
return


async def check_valid_reaction(
event: hikari.GuildReactionAddEvent | hikari.GuildReactionDeleteEvent,
message: hikari.PartialMessage,
) -> bool:
bot_user = action_items.client.app.get_me()
if not bot_user: # bot_user will always be available after the bot has started
return False

# ignore reactions by the bot, reactions that are not ✅
# and reactions not created in the #action-items channel
if (
event.user_id == bot_user.id
or event.emoji_name != "✅"
or event.channel_id != CHANNEL_IDS["action-items"]
):
return False

assert message.author # it will always be available

# ignore messages not sent by the bot and messages with no content
if message.author.id != bot_user.id or not message.content:
return False

return True


async def validate_user_reaction(
user_id: int, message_content: str, guild_id: int
) -> bool:
# extract user and role mentions from the message content
mention_regex = r"<@[!&]?(\d+)>"
mentions = re.findall(mention_regex, message_content)

# make a list of all mentions
mentioned_ids = [int(id_) for id_ in mentions]

if user_id in mentioned_ids:
return True

member = action_items.client.cache.get_member(
guild_id, user_id
) or await action_items.client.rest.fetch_member(guild_id, user_id)

if any(role_id in mentioned_ids for role_id in member.role_ids):
return True

return False


@action_items.listen()
async def reaction_add(event: hikari.GuildReactionAddEvent) -> None:
# retrieve the message that was reacted to
message = action_items.client.cache.get_message(
event.message_id
) or await action_items.client.rest.fetch_message(
event.channel_id, event.message_id
)

is_valid_reaction = await check_valid_reaction(event, message)
if not is_valid_reaction:
return

assert message.content # check_valid_reaction verifies the message content exists

is_valid_reaction = await validate_user_reaction(
event.user_id, message.content, event.guild_id
)
if not is_valid_reaction:
return

# cross out the action item, if it was not crossed out already
if not message.content.startswith("- ✅ ~~"):
# add strikethrough and checkmark
updated_content = f"- ✅ ~~{message.content[2:]}~~"
await action_items.client.rest.edit_message(
event.channel_id, event.message_id, content=updated_content
)


@action_items.listen()
async def reaction_remove(event: hikari.GuildReactionDeleteEvent) -> None:
# retrieve the message that was un-reacted to
# NOTE: cannot use cached message as the reaction count will be outdated
message = await action_items.client.rest.fetch_message(
event.channel_id, event.message_id
)

is_valid_reaction = await check_valid_reaction(event, message)
if not is_valid_reaction:
return

assert message.content # check_valid_reaction verifies the message content exists

checkmark_reactions = await event.app.rest.fetch_reactions_for_emoji(
event.channel_id,
event.message_id,
"✅",
)

reactions = [
await validate_user_reaction(user.id, message.content, event.guild_id)
for user in checkmark_reactions
]
valid_reaction_count = len(
list(
filter(
lambda r: r is True,
reactions,
)
)
)

assert message.content # check_valid_reaction verifies the message content exists
# remove the strikethrough on the item, provided all mentioned users/roles
# are not currently reacted to the message
if message.content.startswith("- ✅ ~~") and valid_reaction_count == 0:
# add strikethrough and checkmark
updated_content = f"- {message.content[6:-2]}"
await action_items.client.rest.edit_message(
event.channel_id, event.message_id, content=updated_content
)


@arc.loader
def loader(client: arc.GatewayClient) -> None:
client.add_plugin(action_items)
Loading