Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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()
Exemplo n.º 3
0
    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))
Exemplo n.º 4
0
 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)
Exemplo n.º 5
0
    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()
Exemplo n.º 6
0
async def sortEventMessages(bot: OperationBot):
    """Sort events in event database.

    Raises MessageNotFound if messages are missing."""
    from eventDatabase import EventDatabase
    EventDatabase.sortEvents()

    event: Event
    for event in EventDatabase.events.values():
        try:
            message = await getEventMessage(event, bot)
        except MessageNotFound as e:
            raise MessageNotFound("sortEventMessages: {}".format(e))
        await updateMessageEmbed(message, event)
        await updateReactions(event, message=message)
Exemplo n.º 7
0
    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))
Exemplo n.º 8
0
    async def _convert(self, arg: str, archived=False) -> Event:
        try:
            eventID = int(arg)
        except ValueError:
            raise BadArgument("Invalid message ID {}, needs to be an "
                              "integer".format(arg))

        try:
            if not archived:
                event = EventDatabase.getEventByID(eventID)
            else:
                event = EventDatabase.getArchivedEventByID(eventID)
        except EventNotFound as e:
            raise BadArgument(str(e))

        return event
Exemplo n.º 9
0
    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()
Exemplo n.º 10
0
    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))
Exemplo n.º 11
0
 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)
Exemplo n.º 12
0
    async def archive(self, ctx: Context, event: EventEvent):
        """
        Archive event.

        Example: archive 1
        """

        # Archive event and export
        EventDatabase.archiveEvent(event)
        try:
            eventMessage = await msgFnc.getEventMessage(event, self.bot)
        except MessageNotFound:
            await ctx.send(
                "Internal error: event {} without a message found".format(
                    event))
        else:
            await eventMessage.delete()

        # Create new message
        await msgFnc.createEventMessage(event, self.bot.eventarchivechannel)

        await ctx.send("Event {} archived".format(event))
Exemplo n.º 13
0
    async def convert(self, ctx: Context, arg: str) -> Event:
        try:
            eventID = int(arg)
        except ValueError:
            raise BadArgument("Invalid message ID {}, needs to be an "
                              "integer".format(arg))

        try:
            event = EventDatabase.getEventByID(eventID)
        except EventNotFound as e:
            raise BadArgument(str(e))

        return event
Exemplo n.º 14
0
    async def convert(self, ctx: Context, arg: str) -> Message:
        try:
            eventID = int(arg)
        except ValueError:
            raise BadArgument("Invalid message ID {}, needs to be an "
                              "integer".format(arg))
        try:
            event = EventDatabase.getEventByID(eventID)
            message = await msgFnc.getEventMessage(event, ctx.bot)
        except (EventNotFound, MessageNotFound) as e:
            raise BadArgument(str(e))

        return message
Exemplo n.º 15
0
    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))
Exemplo n.º 16
0
    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()
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
 async def import_database(self):
     try:
         EventDatabase.loadDatabase(self.commandchannel.guild.emojis)
     except ValueError as e:
         await self.commandchannel.send(e)
         await self.logout()
Exemplo n.º 20
0
 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")
Exemplo n.º 21
0
 async def export(self, ctx: Context):
     """Export event database (manually)."""
     EventDatabase.toJson()
     await ctx.send("EventDatabase exported")
Exemplo n.º 22
0
    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