Example #1
0
 async def on_command_error(self, ctx, error):
     if isinstance(error, commands.CommandNotFound):
         pass
     elif isinstance(error, commands.MissingRequiredArgument):
         pass
     elif isinstance(error, commands.BadArgument):
         await ctx.send(error)
     elif isinstance(error, commands.NoPrivateMessage):
         await ctx.send(t("error.private_message"))
     elif isinstance(error, commands.MissingPermissions):
         await ctx.send(t("error.missing_permissions"))
     elif isinstance(error, commands.CheckFailure):
         pass
     elif isinstance(error, commands.CommandInvokeError):
         if isinstance(error.original, asyncio.TimeoutError):
             await ctx.channel.send(t("error.timeout"))
         elif isinstance(error.original, Forbidden):
             if error.original.text == "Cannot send messages to this user":
                 await ctx.send(t("error.cannot_private_message"))
             else:
                 raise error
         else:
             raise error
     else:
         raise error
Example #2
0
    async def channel(self, ctx):
        """Create a new event channel"""
        with self.bot.scoped_session() as session:
            guild = find_or_create_guild(session, ctx.guild.id)

        if not HavePermission(ctx.author, guild).channel():
            return await ctx.send(t("error.missing_permissions"))

        with self.bot.scoped_session() as session:
            channel_count = event_channel_count_for_guild(session, ctx.guild.id)

        if channel_count >= self.MAX_CHANNELS:
            return await ctx.send(t("channel.channel_limit"))

        channel = await self.bot.create_discord_event_channel(
            ctx.guild, ctx.channel.category
        )
        event_channel = EventChannel(id=channel.id, guild_id=ctx.guild.id)

        with self.bot.scoped_session() as session:
            session.add(event_channel)
            events = event_channel.events

        await self.list_events.call(events, channel)
        await ctx.send(t("channel.channel_created").format(channel.mention))
Example #3
0
 async def _get_title_from_user(self, ctx):
     """Retrieve the event title from the user"""
     await ctx.author.send(t("event.title_prompt"))
     while True:
         title = (await self.bot.get_next_pm(ctx.author)).content
         if len(title) <= self.MAX_TITLE_LENGTH:
             return title
         else:
             await ctx.author.send(
                 t("event.invalid_title").format(self.MAX_TITLE_LENGTH))
Example #4
0
    def call(self, prefix, commands):
        embed = discord.Embed(title=t("help.available_commands"))
        embed.color = EMBED_COLOR
        embed.description = t("help.server_invite").format(self.INVITE)

        for command in commands:
            signature = prefix + command.name
            help_text = self._get_command_help_text(command)
            embed.add_field(name=signature, value=help_text, inline=False)

        return embed
Example #5
0
    async def timezone(self, ctx):
        """Set your local time zone"""
        await ctx.send(embed=self.time_zone_embed.call())
        time_zone = await self.time_zone_input.call(ctx.author, ctx.channel)

        with self.bot.scoped_session() as session:
            user = find_or_create_user(session, ctx.author.id)
            user.time_zone = time_zone
            session.add(user)

        humanized_time_zone = t("time_zones.{}".format(time_zone.lower()))
        await ctx.send(t("time_zone.updated").format(humanized_time_zone))
Example #6
0
 async def _get_desc_from_user(self, ctx):
     """Retrieve the event description from the user"""
     await ctx.author.send(t("event.description_prompt"))
     while True:
         resp = (await self.bot.get_next_pm(ctx.author, timeout=240)).content
         if resp.upper() == "NONE":
             return None
         elif len(resp) <= self.MAX_DESC_LENGTH:
             return resp
         else:
             await ctx.author.send(
                 t("event.invalid_description").format(self.MAX_DESC_LENGTH)
             )
Example #7
0
    def call(self):
        embed = discord.Embed()
        embed.color = EMBED_COLOR
        embed.title = t("time_zone.title")

        embed.description = ""
        for i, iso_time_zone in enumerate(ISO_TIME_ZONES, 1):
            time_zone_name = t("time_zones.{}".format(iso_time_zone.lower()))
            embed.description += "**{}** {}\n".format(i, time_zone_name)
        embed.description += "\n"
        embed.description += t("time_zone.footer").format(self.INVITE)

        return embed
Example #8
0
 async def _get_capacity_from_user(self, ctx):
     """Retrieve the event capacity from the user"""
     await ctx.author.send(t("event.capacity_prompt"))
     while True:
         resp = (await self.bot.get_next_pm(ctx.author)).content
         if resp.upper() == "NONE":
             return None
         elif resp.isdigit() and int(resp) in range(1, self.MAX_CAPACITY + 1):
             return int(resp)
         else:
             await ctx.author.send(
                 t("event.invalid_capacity").format(self.MAX_CAPACITY)
             )
Example #9
0
 def _organizer_name(self, event, guild):
     """Retrieve the guild specific display name of the organizer"""
     organizer = guild.get_member(event.organizer_id)
     if organizer:
         return organizer.display_name
     else:
         return t("event.unknown_user")
Example #10
0
    async def on_guild_join(self, guild):
        with self.bot.scoped_session() as session:
            find_or_create_guild(session, guild.id)

        # Add entry for guild prefix in the cache
        self.bot.cache.update_prefix(guild.id, None)

        await guild.owner.send(t("welcome_message").format(guild.name))
Example #11
0
    async def delete(self, ctx, *, role: discord.Role):
        """Change or view the minimum role required to delete events"""
        with self.bot.scoped_session() as session:
            guild = find_or_create_guild(session, ctx.guild.id)
            guild.delete_role_id = role.id
            session.add(guild)

        await ctx.send(t("role.delete_role_changed").format(role))
Example #12
0
    async def prefix(self, ctx, new_prefix):
        """Update the server's command prefix"""
        with self.bot.scoped_session() as session:
            guild = find_or_create_guild(session, ctx.guild.id)
            guild.prefix = new_prefix
            session.add(guild)

        self.bot.cache.update_prefix(ctx.guild.id, new_prefix)
        await ctx.send(t("prefix.changed").format(new_prefix))
Example #13
0
    async def delete_error(self, ctx, error):
        if isinstance(error, commands.MissingRequiredArgument):
            with self.bot.scoped_session() as session:
                guild = find_or_create_guild(session, ctx.guild.id)

            role = discord.utils.get(ctx.guild.roles, id=guild.delete_role_id)

            await ctx.send(
                t("role.delete_role_current").format(role, ctx.prefix))
Example #14
0
    async def _get_start_time(self, ctx, iso_time_zone):
        """Retrieve a datetime UTC object from the user"""
        await ctx.author.send(t("event.start_time_prompt"))
        while True:
            start_time_str = (await self.bot.get_next_pm(ctx.author)).content
            try:
                utc_start_time = (arrow.get(
                    start_time_str,
                    ["YYYY-MM-DD h:mm A", "YYYY-MM-DD HH:mm"],
                    tzinfo=iso_time_zone,
                ).to("utc").datetime)

                if utc_start_time < arrow.utcnow():
                    await ctx.author.send(t("event.start_time_in_the_past"))
                else:
                    return utc_start_time
            except:
                await ctx.author.send(t("event.invalid_start_time"))
Example #15
0
 async def _get_event_from_user(self, user, events_dict):
     while True:
         resp = (await self.bot.get_next_pm(user, timeout=60)).content
         if resp == "cancel":
             return -1
         if not resp.isdigit() or int(resp) not in list(events_dict.keys()):
             await user.send(t("event.invalid_selection_error"))
         else:
             event = events_dict[int(resp)]
             return event
Example #16
0
 async def _get_choice_from_user(self, user, channel, valid_range):
     while True:
         resp = (await self.bot.get_next_message(user, channel,
                                                 timeout=60)).content
         if resp.lower() == "cancel":
             return 0
         if not resp.isdigit() or not 0 < int(resp) <= valid_range:
             await user.send(t("event.invalid_selection_error"))
         else:
             return int(resp)
Example #17
0
    async def call(self, user, channel):
        while True:
            resp = (await self.bot.get_next_message(user, channel)).content

            if not self._valid_time_zone_input(resp):
                await channel.send(t("event.invalid_time_zone"))
                continue

            time_zone_index = int(resp) - 1
            return ISO_TIME_ZONES[time_zone_index]
Example #18
0
 async def _get_time_zone(self, ctx):
     """Retrieve a valid time zone string from the user"""
     await ctx.author.send(embed=TimeZoneEmbed().call())
     while True:
         resp = (await self.bot.get_next_pm(ctx.author)).content
         if self._valid_time_zone_input(resp):
             time_zone_index = int(resp) - 1
             if time_zone_index in range(len(ISO_TIME_ZONES)):
                 return ISO_TIME_ZONES[time_zone_index]
         else:
             await ctx.author.send(t("event.invalid_time_zone"))
Example #19
0
    def call(self):
        embed = discord.Embed()
        embed.color = EMBED_COLOR
        embed.title = t("time_zone.title")
        embed.description = t("time_zone.footer").format(self.INVITE)

        time_zone_index = 1

        for region, time_zones in ISO_TIME_ZONES.items():
            time_zone_list = ""

            for time_zone in time_zones:
                time_zone_name = t("time_zones.{}".format(time_zone.lower()))
                time_zone_list += "**{}** {}\n".format(time_zone_index,
                                                       time_zone_name)
                time_zone_index += 1

            embed.add_field(name=t(region), value=time_zone_list)

        return embed
Example #20
0
 def call(self, user_count, event_count, guild_count):
     embed = discord.Embed(title=t("apollo"))
     embed.color = EMBED_COLOR
     embed.description = self._description()
     embed.add_field(name=t("about.users"), value=user_count)
     embed.add_field(name=t("about.servers"), value=guild_count)
     embed.add_field(name=t("about.events"), value=event_count)
     embed.add_field(name=t("about.memory"), value=self._memory_usage())
     embed.add_field(name=t("about.cpu"), value=self._cpu_usage())
     embed.add_field(name=t("about.uptime"), value=self._uptime())
     embed.set_footer(text=t("about.made_with"),
                      icon_url="http://i.imgur.com/5BFecvA.png")
     return embed
Example #21
0
    async def event(self, ctx):
        """Create a new event"""
        # Clean up event channels that may have been deleted
        # while the bot was offline.
        self.sync_event_channels.call(ctx.guild.id)

        with self.bot.scoped_session() as session:
            guild = find_or_create_guild(session, ctx.guild.id)

        if not Can(ctx.author, guild).event():
            return await ctx.send(t("error.missing_permissions"))

        with self.bot.scoped_session() as session:
            event_channels = (
                session.query(EventChannel).filter_by(guild_id=ctx.guild.id).all()
            )
            user = find_or_create_user(session, ctx.author.id)

        event = Event()
        event.title = await self._get_title_from_user(ctx)
        event.description = await self._get_desc_from_user(ctx)
        event.organizer = user
        event.capacity = await self._get_capacity_from_user(ctx)
        event.event_channel = await self._get_event_channel(ctx, event_channels)
        event.time_zone = await self._get_time_zone(ctx)
        event.start_time = await self._get_start_time(ctx, event.time_zone)

        channel = self.bot.get_channel(event.event_channel.id)
        await ctx.author.send(t("event.created").format(channel.mention))

        with self.bot.scoped_session() as session:
            session.add(event)

        with self.bot.scoped_session() as session:
            events = (
                session.query(Event)
                .filter_by(event_channel_id=event.event_channel_id)
                .all()
            )

        await self.list_events.call(events, channel)
Example #22
0
    async def call(self, user, channel, selection, title=None, footer=None):
        """
        Send a list of events to the user and ask them to pick one.
        Note only sends a list, and does not send the prompt before it.
        :param user: Member, e.g. context.author
        :param channel: Messageable
        :param selection: dict
        :param title: str or None
        :param footer: str or None
        :return: Event
        """
        if title is None:
            title = t("selection.generic")

        if footer is None:
            footer = t("selection.generic_cancel")

        await channel.send(
            embed=self.selection_embed.call(selection, title, footer))

        return await self._get_choice_from_user(user, channel, len(selection))
Example #23
0
    def call(self, event, responses, guild):
        """Create a Discord Embed to represent an event message"""
        embed = discord.Embed()
        embed.color = EMBED_COLOR
        embed.title = event.title

        if event.description:
            embed.description = event.description

        embed.set_footer(text=t("event.created_by").format(
            self._organizer_name(event, guild), emoji.SKULL))

        # Start time field
        embed.add_field(name=t("event.time"),
                        value=self._formatted_start_time(event),
                        inline=False)

        accepted_members = self._accepted_members(responses, guild,
                                                  event.capacity)
        embed.add_field(
            name=self._accepted_header(event.capacity, len(accepted_members)),
            value=self._format_members(accepted_members),
        )

        declined_members = self._declined_members(responses, guild)
        embed.add_field(name=self.DECLINED_HEADER,
                        value=self._format_members(declined_members))

        tentative_members = self._tentative_members(responses, guild)
        embed.add_field(name=self.TENTATIVE_HEADER,
                        value=self._format_members(tentative_members))

        standby_members = self._standby_members(responses, guild,
                                                event.capacity)
        if len(standby_members) > 0:
            embed.add_field(name=self.STANDBY_HEADER,
                            value=self._format_members(standby_members))

        return embed
Example #24
0
    async def call(self, events, discord_channel):
        # Mark messages for deletion so that we can ignore them
        # in OnRawMessageDelete.
        for event in events:
            self.bot.cache.mark_message_for_deletion(event.message_id)

        await discord_channel.purge()

        if len(events) == 0:
            return await discord_channel.send(t("channel.no_events"))

        for event in self.sort_events_by_start_time(events):
            with self.bot.scoped_session() as session:
                responses = responses_for_event(session, event.id)
            await self.list_event.call(event, responses, discord_channel)
Example #25
0
    async def call(self, user, channel, iso_time_zone):
        """
        Retrieve a datetime UTC object from the user
        :param user: Member, e.g. context.author
        :param channel: Messageable, e.g. context.author.dmchannel
        :param iso_time_zone: str, Option in Apollo.time_zones.ISO_TIME_ZONES
        :return: Arrow object
        """
        while True:
            start_time_str = (await self.bot.get_next_message(
                user, channel)).content.upper()
            try:
                utc_start_time = (arrow.get(
                    start_time_str,
                    ["YYYY-MM-DD h:mm A", "YYYY-MM-DD HH:mm"],
                    tzinfo=iso_time_zone,
                ).to("utc").datetime)

                if utc_start_time < arrow.utcnow():
                    await user.send(t("event.start_time_in_the_past"))
                else:
                    return utc_start_time
            except:
                await user.send(t("event.invalid_start_time"))
Example #26
0
    async def call(self, user, channel):
        """
        Get title from user
        :param user: Member, e.g. context.author
        :param channel: Messageable, e.g. context.author.dmchannel
        :return: str
        """

        while True:
            title = (await self.bot.get_next_message(user, channel)).content
            if len(title) <= MAX_TITLE_LENGTH:
                return title
            else:
                await user.send(
                    t("event.invalid_title").format(MAX_TITLE_LENGTH))
Example #27
0
    async def call(self, user, channel):
        """
        Get description from user
        :param user: Member, e.g. context.author
        :param channel: Messageable, e.g. context.author.dmchannel
        :return: str
        """

        while True:
            resp = (await self.bot.get_next_message(user, channel, timeout=240)).content
            if resp == "None":
                return None
            elif len(resp) <= MAX_DESC_LENGTH:
                return resp
            else:
                await user.send(t("event.invalid_description").format(MAX_DESC_LENGTH))
    async def call(self, event, payload):
        channel = self.bot.get_channel(payload.channel_id)

        if payload.emoji.name == emoji.CLOCK:
            # We generally clear the reaction later on, but as we could we
            # waiting on the user to input a time zone, we don't want this
            # reaction to hang around for too long.
            await self.bot.remove_reaction(payload)

            discord_user = self.bot.get_user(payload.user_id)

            with self.bot.scoped_session() as session:
                apollo_user = find_or_create_user(session, payload.user_id)

            await self.request_local_start_time.call(apollo_user, discord_user, event)

        if payload.emoji.name == emoji.SKULL:
            member = self.bot.find_guild_member(payload.guild_id, payload.user_id)

            with self.bot.scoped_session() as session:
                guild = find_or_create_guild(session, payload.guild_id)

            if event.organizer_id != member.id and not HavePermission(member, guild).delete():
                return await member.send("You don't have permission to do that.")

            with self.bot.scoped_session() as session:
                session.delete(event)

            await channel.delete_messages([discord.Object(id=event.message_id)])

            with self.bot.scoped_session() as session:
                event_count = event_count_for_event_channel(session, channel.id)

            if event_count == 0:
                await channel.send(t("channel.no_events"))

            return

        rsvp_status = EMOJI_STATUSES.get(payload.emoji.name)
        if rsvp_status:
            await self.update_response.call(event.id, payload.user_id, rsvp_status)

            with self.bot.scoped_session() as session:
                responses = responses_for_event(session, event.id)

            await self.update_event.call(event, responses, channel)
Example #29
0
    async def call(self, user, channel):
        """
        Retrieve the event capacity from the user
        :param user: Member, e.g. context.author
        :param channel: Messageable, e.g. context.author.dmchannel
        :return: int or None
        """

        while True:
            resp = (await self.bot.get_next_message(user, channel)).content
            if resp.upper() == "NONE":
                return None
            elif resp.isdigit() and int(resp) in range(1, MAX_CAPACITY + 1):
                return int(resp)
            else:
                await channel.send(
                    t("event.invalid_capacity").format(MAX_CAPACITY))
Example #30
0
    async def on_raw_message_delete(self, payload):
        # If the message is marked for deletion, it was deleted by the bot
        # as part of clearing the event channel. Unmark it, and return.
        if self.bot.cache.message_marked_for_deletion(payload.message_id):
            return self.bot.cache.unmark_message_for_deletion(payload.message_id)

        with self.bot.scoped_session() as session:
            event = find_event_from_message(session, payload.message_id)

            if not event:
                return

            session.delete(event)

            event_channel = event.event_channel

            if len(event_channel.events) == 0:
                discord_event_channel = self.bot.get_channel(event_channel.id)
                await discord_event_channel.send(t("channel.no_events"))