async def _create_event(self, ctx: Context, date: datetime, batch=False, sideop=False, platoon_size=None, force=False, silent=False) -> Event: # TODO: Check for duplicate event dates? if date < datetime.today() and not force: raise BadArgument( "Requested date {} has already passed. " "Use the `force` argument to override".format(date)) # Create event and sort events, export event: Event = EventDatabase.createEvent(date, ctx.guild.emojis, sideop=sideop, platoon_size=platoon_size) await msgFnc.createEventMessage(event, self.bot.eventchannel) if not batch: await msgFnc.sortEventMessages(self.bot) EventDatabase.toJson() # Update JSON file if not silent: await ctx.send("Created event {}".format(event)) return event
async def changesizeall(self, ctx: Context, new_size: str): if new_size not in cfg.PLATOON_SIZES: ctx.send("Invalid new size {}".format(new_size)) return for event in EventDatabase.events.values(): print("converting", event) await self._change_size(ctx, event, new_size) await ctx.send("All events resized succesfully") EventDatabase.toJson()
async def _delete(self, event: Event, archived=False): # TODO: Move to a more appropriate location EventDatabase.removeEvent(event.id, archived=archived) try: eventMessage = await msgFnc.getEventMessage(event, self.bot, archived=archived) except MessageNotFound: # Message already deleted, nothing to be done pass else: await eventMessage.delete() EventDatabase.toJson(archive=archived)
async def settime(self, ctx: Context, event: EventEvent, time: EventTime): """ Set event time. Example: settime 1 18:30 """ # Change time event.setTime(time) # Update event and sort events, export await msgFnc.sortEventMessages(self.bot) EventDatabase.toJson() # Update JSON file await ctx.send("Time set for operation {}".format(event))
async def resizeall(self, ctx: Context): for event in EventDatabase.events.values(): print("reordering", event) ret = event.reorder() if ret is None: await ctx.send("{}: nothing to be done".format(event)) continue if ret.strip() != "": await ctx.send(ret) await self._update_event(event, export=False) await ctx.send("Event {} reordered succesfully".format(event)) await ctx.send("All events reordered succesfully") EventDatabase.toJson()
async def setdate(self, ctx: Context, event: EventEvent, date: EventDateTime): """ Set event date. Example: setdate 1 2019-01-01 """ # Change date event.setDate(date) # Update event and sort events, export await msgFnc.sortEventMessages(self.bot) EventDatabase.toJson() # Update JSON file await ctx.send("Date {} set for operation {} ID {}".format( event.date, event.title, event.id))
async def changesizeall(self, ctx: Context, new_size: str): if new_size not in cfg.PLATOON_SIZES: ctx.send("Invalid new size {}".format(new_size)) return for event in EventDatabase.events.values(): print("converting", event) ret = event.changeSize(new_size) if ret is None: await ctx.send("{}: nothing to be done".format(event)) continue if ret.strip() != "": await ctx.send(ret) await self._update_event(event, export=False) await ctx.send("Event {} resized succesfully".format(event)) await ctx.send("All events resized succesfully") EventDatabase.toJson()
async def on_ready(self): print("Waiting until ready") await self.bot.wait_until_ready() self.bot.fetch_data() commandchannel = self.bot.commandchannel await commandchannel.send("Connected") print("Ready, importing") await commandchannel.send("Importing events") # await EventDatabase.fromJson(self.bot) await self.bot.import_database() await commandchannel.send("Syncing") await msgFnc.syncMessages(EventDatabase.events, self.bot) await commandchannel.send("Synced") EventDatabase.toJson() msg = "{} events imported".format(len(EventDatabase.events)) print(msg) await commandchannel.send(msg) await self.bot.change_presence(activity=Game(name=cfg.GAME, type=2)) print('Logged in as', self.bot.user.name, self.bot.user.id)
async def _set_quick(self, ctx: Context, event: Event, terrain: str, faction: str, zeus: Member = None, time: EventTime = None, quiet=False): event.setTerrain(terrain) event.setFaction(faction) if zeus is not None: event.signup(event.findRoleWithName("ZEUS"), zeus) if time is not None: event.setTime(time) await msgFnc.sortEventMessages(self.bot) EventDatabase.toJson() # Update JSON file if not quiet: await ctx.send("Updated event {}".format(event))
async def removegroup(self, ctx: Context, eventMessage: EventMessage, *, groupName: str): """ Remove a role group from the event. Example: removegroup 1 Bravo """ event = EventDatabase.getEventByMessage(eventMessage.id) groupName = groupName.strip('"') if not event.hasRoleGroup(groupName): await ctx.send("No role group found with name {}".format(groupName) ) return # Remove reactions, remove role, update event, add reactions, export for reaction in event.getReactionsOfGroup(groupName): await eventMessage.remove_reaction(reaction, self.bot.user) event.removeRoleGroup(groupName) await msgFnc.updateMessageEmbed(eventMessage, event) EventDatabase.toJson() # Update JSON file await ctx.send("Group {} removed from {}".format(groupName, event))
async def _update_event(self, event: Event, import_db=False, reorder=True, export=True): # TODO: Move to a more appropriate location if import_db: await self.bot.import_database() # Event instance might have changed because of DB import, get again event = EventDatabase.getEventByMessage(event.messageID) try: message = await msgFnc.getEventMessage(event, self.bot) except MessageNotFound: message = await msgFnc.createEventMessage(event, self.bot.eventchannel) await msgFnc.updateMessageEmbed(eventMessage=message, updatedEvent=event) await msgFnc.updateReactions(event=event, message=message, reorder=reorder) if export: EventDatabase.toJson()
async def export(self, ctx: Context): """Export event database (manually).""" EventDatabase.toJson() await ctx.send("EventDatabase exported")
async def _multi_create(self, ctx: Context, start: date, end: date = None, force=None): if end is None: last_day = calendar.monthrange(start.year, start.month)[1] end = start.replace(day=last_day) delta = end - start days: List[date] = [] past_days: List[date] = [] weekend = [5, 6, 7] day: date for i in range(delta.days + 1): day = start + timedelta(days=i) if day.isoweekday() in weekend: if day < date.today() and not force: past_days.append(day) else: days.append(day) if len(past_days) > 0: strpastdays = " ".join([day.isoformat() for day in past_days]) strpast = ( "\nFollowing dates are in the past and will be skipped:\n" "```{}```".format(strpastdays)) else: strpast = "" if len(days) > 0: strdays = " ".join([day.isoformat() for day in days]) message = ("Creating events for following days:\n```{}``` " "{}" "Reply with `ok` or `cancel`.".format(strdays, strpast)) await ctx.send(message) else: message = ("No events to be created.{}" "Use the `force` argument to override. " "See `{}help multicreate`".format(strpast, CMD)) await ctx.send(message) return self.bot.awaiting_reply = True def pred(m): return m.author == ctx.message.author \ and m.channel == ctx.channel event_time = time(hour=18, minute=30) with_time = [datetime.combine(day, event_time) for day in days] try: while True: response = await self.bot.wait_for('message', check=pred) reply = response.content.lower() if reply == 'ok': await ctx.send("Creating events") for day in with_time: await self._create_event(ctx, day, batch=True) await msgFnc.sortEventMessages(self.bot) EventDatabase.toJson() await ctx.send("Done creating events") self.bot.awaiting_reply = False return elif reply == 'cancel': await ctx.send("Canceling") self.bot.awaiting_reply = False return else: await ctx.send("Please reply with `ok` or `cancel`.") except Exception: await ctx.send('```py\n{}\n```'.format(traceback.format_exc())) self.bot.awaiting_reply = False
async def syncmessages(self, ctx: Context): """Import database, sync messages with events and create missing messages.""" # NOQA await self.bot.import_database() await msgFnc.syncMessages(EventDatabase.events, self.bot) EventDatabase.toJson() await ctx.send("Event messages synced")
async def on_raw_reaction_add(self, payload: RawReactionActionEvent): if payload.member == self.bot.user or \ payload.channel_id != self.bot.eventchannel.id: # Bot's own reaction, or reaction outside of the event channel return if payload.emoji.name in cfg.EXTRA_EMOJIS: return # Remove the reaction message = await self.bot.eventchannel.fetch_message(payload.message_id) user = payload.member await message.remove_reaction(payload.emoji, user) # Get event from database with message ID try: event: Event = EventDatabase.getEventByMessage(message.id) except EventNotFound as e: print(e) await self.bot.logchannel.send( "NOTE: reaction to a non-existent event. " "msg: {} role: {} user: {} ({}#{})" .format(message.id, payload.emoji, user.display_name, user.name, user.discriminator)) return # Get emoji string if payload.emoji.is_custom_emoji(): emoji = payload.emoji else: emoji = payload.emoji.name # Find signup of user signup: Optional[Role] = event.findSignupRole(user.id) # Get role with the emoji try: role = event.findRoleWithEmoji(emoji) except RoleNotFound as e: raise RoleNotFound("{} in event {} by user {}#{}" .format(str(e), event, user.name, user.discriminator)) if role.name == "ZEUS": # somebody with Nitro added the ZEUS reaction by hand return late_signoff = False """ if user is not signed up and the role is free, sign up if user is not signed up and the role is not free, do nothing if user is signed up and they select the same role, sign off if user is signed up and they select a different role, do nothing """ if signup is None: # Sign up if role is free if role.userID is None: # signup event.signup(role, user) # Update event await msgFnc.updateMessageEmbed(message, event) EventDatabase.toJson() else: # Role is already taken, ignoring sign up attempt return message_action = "Signup" elif signup.emoji == emoji: # undo signup event.undoSignup(user) # Update event await msgFnc.updateMessageEmbed(message, event) EventDatabase.toJson() message_action = "Signoff" print("Signed off role name:", role.name) if role.name in cfg.SIGNOFF_NOTIFY_ROLES[event.platoon_size]: print("Signoff in to be notified") date = event.date print("Event date:", date) time_delta = date - datetime.today() if time_delta > timedelta(days=0): days_str = "" hours_str = "" minutes_str = "" days = time_delta.days hours = time_delta.seconds // (60 * 60) minutes = (time_delta.seconds - hours * 60 * 60) // 60 if days > 0: days_str = "{} days ".format(days) if hours > 0: hours_str = "{} hours ".format(hours) if minutes > 0: minutes_str = "{} minutes".format(minutes) timestring = "{}{}{}".format(days_str, hours_str, minutes_str) if time_delta < cfg.SIGNOFF_NOTIFY_TIME and \ self.bot.signoff_notify_user != user: print("Delta:", time_delta) print("Date delta smaller than notify period") late_signoff = True else: # user reacted to another role while signed up return if late_signoff and not event.sideop: message = "{}: User {} ({}#{}) signed off from {} role {} " \ "{} before the operation." \ .format(self.bot.signoff_notify_user.mention, user.display_name, user.name, user.discriminator, event, role.display_name, timestring) else: message = "{}: event: {} role: {} user: {} ({}#{})" \ .format(message_action, event, role.display_name, user.display_name, user.name, user.discriminator) await self.bot.logchannel.send(message)
async def on_raw_reaction_add(self, payload: RawReactionActionEvent): if payload.member == self.bot.user or \ payload.channel_id != self.bot.eventchannel.id: # Bot's own reaction, or reaction outside of the event channel return if payload.emoji.name in cfg.EXTRA_EMOJIS: return message: Message = await self.bot.eventchannel.fetch_message( payload.message_id) if message.author != self.bot.user: # We don't care about reactions to other messages than our own. # Makes it easier to test multiple bot instances on the same # channel return # Remove the reaction user: User = payload.member await message.remove_reaction(payload.emoji, user) # Get event from database with message ID try: event: Event = EventDatabase.getEventByMessage(message.id) except EventNotFound as e: print(e) await self.bot.logchannel.send( "NOTE: reaction to a non-existent event. " "msg: {} role: {} user: {} ({}#{})".format( message.id, payload.emoji, user.display_name, user.name, user.discriminator)) return # Get emoji string if payload.emoji.is_custom_emoji(): emoji = payload.emoji else: emoji = payload.emoji.name # Find signup of user old_signup: Optional[Role] = event.findSignupRole(user.id) # If a user is already signed up as Zeus they can't sign off or change # roles without the Event Moderator if old_signup and old_signup.name == cfg.EMOJI_ZEUS: return # Get role with the emoji # TODO: remove when converter exists try: role = event.findRoleWithEmoji(emoji) except RoleNotFound as e: raise RoleNotFound("{} in event {} by user {}#{}".format( str(e), event, user.name, user.discriminator)) if role.name == cfg.EMOJI_ZEUS: # Somebody with Nitro added the ZEUS reaction by hand, ignoring return late_signoff_delta = None old_role = "" """ if user is not signed up and the role is free, sign up if user is not signed up and the role is not free, do nothing if user is signed up and they select the same role, sign off if user is signed up and they select a different role, change to that role """ # NOQA if old_signup and emoji == old_signup.emoji: # User clicked a reaction of the current signed up role removed_role = event.undoSignup(user) message_action = "SIGNOFF" else: try: removed_role, _ = event.signup(role, user) except RoleTaken: # Users can't take priority with a reaction return if removed_role is None: # User wasn't signed up to any roles previously message_action = "SIGNUP" else: # User switched from a different role message_action = "CHANGE" old_role = "{} -> ".format(removed_role.display_name) # Update discord embed await msgFnc.updateMessageEmbed(message, event) EventDatabase.toJson() delta_message = "" if removed_role and not event.sideop: # User signed off or changed role, checking if there's a need to # ping late_signoff_delta = self._calculate_signoff_delta( event, removed_role, user) if late_signoff_delta is not None and not event.sideop: delta_message = "{}: {} before the operation:\n" \ .format(self.bot.signoff_notify_user.mention, late_signoff_delta) message = f"{delta_message}{message_action}: {event}, role: " \ f"{old_role}{role.display_name}, " \ f"user: {user.display_name} " \ f"({user.name}#{user.discriminator})" await self.bot.logchannel.send(message)