Skip to content

Commit 68df631

Browse files
committed
Add attendance counter to sideops
1 parent 6a7b423 commit 68df631

File tree

4 files changed

+83
-14
lines changed

4 files changed

+83
-14
lines changed

config.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,22 @@
7272
"j",
7373
]
7474

75-
# ATTENDANCE_EMOJI = "\N{HEAVY PLUS SIGN}"
76-
ATTENDANCE_EMOJI = None
75+
# By default, the number of attendees will only be displayed for sideops
76+
ALWAYS_DISPLAY_ATTENDANCE = False
7777

78-
EXTRA_EMOJIS: List[str] = [
78+
ATTENDANCE_EMOJI = "\N{HEAVY PLUS SIGN}"
79+
80+
# These are emojis that can be added to the event but are ignored when a user
81+
# reacts to them
82+
IGNORED_EMOJIS: List[str] = [
7983
# ATTENDANCE_EMOJI
8084
]
8185

86+
# The are emojis that are handled separately from regular sign-up emojis
87+
SPECIAL_EMOJIS: List[str] = [
88+
ATTENDANCE_EMOJI,
89+
]
90+
8291
PLATOON_SIZES = ['1PLT', '2PLT', 'sideop', 'WW2side']
8392

8493
# Dummy: an empty spacer. An embed can only have either one or three

errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ class RoleError(Exception):
2424

2525
class RoleTaken(Exception):
2626
pass
27+
28+
29+
class UnknownEmoji(Exception):
30+
pass

event.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ def __init__(self, id: int = None, display_name: str = None):
3232
self.id = id
3333
self.display_name = display_name
3434

35+
def __eq__(self, other: Union['User', discord.abc.User]):
36+
# This makes it so that User objects can be compared to
37+
# discord.abc.User by doing `user == discord.abc.User`. The comparison
38+
# will not work in the other direction because discord.abc.User checks
39+
# for the object type.
40+
return self.id == other.id
41+
3542

3643
class Event:
3744

@@ -49,6 +56,8 @@ def __init__(self, date: datetime.datetime, guildEmojis: Tuple[Emoji, ...],
4956
self.messageID = 0
5057
self.id = eventID
5158
self.sideop = sideop
59+
self.attendees: list[User] = []
60+
5261
if platoon_size is None:
5362
if sideop:
5463
self.platoon_size = "sideop"
@@ -254,7 +263,11 @@ def createEmbed(self) -> Embed:
254263
value="\N{ZERO WIDTH SPACE}",
255264
inline=group.isInline)
256265

257-
eventEmbed.set_footer(text="Event ID: " + str(self.id))
266+
if self.sideop or cfg.ALWAYS_DISPLAY_ATTENDANCE:
267+
attendees = f"Attendees: {len(self.attendees)}\n\n"
268+
else:
269+
attendees = ""
270+
eventEmbed.set_footer(text=f"{attendees}Event ID: {str(self.id)}")
258271

259272
return eventEmbed
260273

@@ -470,6 +483,21 @@ def findSignupRole(self, userID) -> Optional[Role]:
470483
# TODO: raise RoleNotFound instead of returning None?
471484
return None
472485

486+
def has_attendee(self, user: discord.abc.User) -> bool:
487+
"""Check if the given user has been marked as attending."""
488+
return user in self.attendees
489+
490+
def add_attendee(self, user: discord.abc.User) -> None:
491+
"""Add user to the attendance list"""
492+
if not self.has_attendee(user):
493+
self.attendees.append(
494+
User(id=user.id, display_name=user.display_name))
495+
496+
def remove_attendee(self, user: discord.abc.User) -> None:
497+
"""Remove user from the attendance list"""
498+
if self.has_attendee(user):
499+
self.attendees.remove(user)
500+
473501
def __str__(self):
474502
return f"{self.title} (ID {self.id}) at {self.date}"
475503

@@ -481,6 +509,10 @@ def toJson(self, brief_output=False) -> Dict[str, Any]:
481509
for groupName, roleGroup in self.roleGroups.items():
482510
roleGroupsData[groupName] = roleGroup.toJson(brief_output)
483511

512+
attendees_data = {}
513+
for user in self.attendees:
514+
attendees_data[user.id] = user.display_name
515+
484516
data: Dict[str, Any] = {}
485517
data["title"] = self.title
486518
data["date"] = self.date.strftime("%Y-%m-%d")
@@ -495,6 +527,7 @@ def toJson(self, brief_output=False) -> Dict[str, Any]:
495527
data["messageID"] = self.messageID
496528
data["platoon_size"] = self.platoon_size
497529
data["sideop"] = self.sideop
530+
data["attendees"] = attendees_data
498531
data["roleGroups"] = roleGroupsData
499532
return data
500533

@@ -513,6 +546,10 @@ def fromJson(self, eventID, data: dict, emojis, manual_load=False):
513546
self.messageID = int(data.get("messageID", 0))
514547
self.platoon_size = str(data.get("platoon_size", PLATOON_SIZE))
515548
self.sideop = bool(data.get("sideop", False))
549+
attendees_data = data.get("attendees", {})
550+
for userID, name in attendees_data.items():
551+
self.attendees.append(User(int(userID), name))
552+
516553
# TODO: Handle missing roleGroups
517554
groups: List[str] = []
518555
for groupName, roleGroupData in data["roleGroups"].items():

eventListener.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import config as cfg
1111
import messageFunctions as msgFnc
12-
from errors import EventNotFound, RoleNotFound, RoleTaken
12+
from errors import EventNotFound, RoleNotFound, RoleTaken, UnknownEmoji
1313
from event import Event
1414
from eventDatabase import EventDatabase
1515
from operationbot import OperationBot
@@ -50,7 +50,7 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent):
5050
# Bot's own reaction, or reaction outside of the event channel
5151
return
5252

53-
if payload.emoji.name in cfg.EXTRA_EMOJIS:
53+
if payload.emoji.name in cfg.IGNORED_EMOJIS:
5454
return
5555

5656
message: Message = await self.bot.eventchannel.fetch_message(
@@ -78,16 +78,20 @@ async def on_raw_reaction_add(self, payload: RawReactionActionEvent):
7878
f"{message.jump_url}")
7979
return
8080
else:
81-
await self._handle_signup(event, payload.emoji, user, message)
81+
if payload.emoji.is_custom_emoji():
82+
emoji: Union[PartialEmoji, str] = payload.emoji
83+
else:
84+
emoji = cast(str, payload.emoji.name)
8285

83-
async def _handle_signup(self, event: Event, partial_emoji: PartialEmoji,
84-
user: User, message: Message):
85-
# Get emoji string
86-
if partial_emoji.is_custom_emoji():
87-
emoji: Union[PartialEmoji, str] = partial_emoji
88-
else:
89-
emoji = cast(str, partial_emoji.name)
86+
if payload.emoji.name in cfg.SPECIAL_EMOJIS:
87+
await self._handle_special_emoji(event, emoji, user,
88+
message)
89+
else:
90+
await self._handle_signup(event, emoji, user, message)
9091

92+
async def _handle_signup(self, event: Event,
93+
emoji: Union[PartialEmoji, str], user: User,
94+
message: Message):
9195
# Find signup of user
9296
old_signup: Optional[Role] = event.findSignupRole(user.id)
9397

@@ -156,6 +160,21 @@ async def _handle_signup(self, event: Event, partial_emoji: PartialEmoji,
156160

157161
await self.bot.logchannel.send(text)
158162

163+
async def _handle_special_emoji(self, event: Event,
164+
emoji: Union[PartialEmoji, str],
165+
user: User, message: Message):
166+
167+
if emoji == cfg.ATTENDANCE_EMOJI:
168+
if event.has_attendee(user):
169+
event.remove_attendee(user)
170+
else:
171+
event.add_attendee(user)
172+
await msgFnc.updateMessageEmbed(message, event)
173+
EventDatabase.toJson()
174+
else:
175+
raise UnknownEmoji(f"Reaction to unknown special emoji {emoji} "
176+
f"in event {event} by user {user}")
177+
159178
@Cog.listener()
160179
async def on_message(self, message: Message):
161180
if message.author == self.bot.user:

0 commit comments

Comments
 (0)