Ejemplo n.º 1
0
    async def _delete_team(self,
                           ctx: SlashContext,
                           team_role: discord.Role,
                           confirmation: str = None):
        """Delete a team.
        After selecting a team, the user will be asked to repeat the command with its role ID
        to ensure they're aware of the action they're performing and of its consequences.
        """

        guild = data_manager.get_guild(ctx.guild)
        team = checks.is_role_tied_to_team(guild, team_role)

        if confirmation is not None and int(confirmation) == team_role.id:
            await guild.del_team(guild.get_team(team_role))

            embed = discord.Embed(
                title=
                f'{constants.Emojis.DELETE.value} __{team_role}__ and all its data'
                f' was deleted.',
                color=constants.Colors.ERROR.value)

        else:
            embed = discord.Embed(
                title=f'{constants.Emojis.WARNING.value} Are you sure'
                f' you want to delete __{team.role}__?',
                description=
                f'This action is irreversible and will erase all your data.'
                f'\nTo confirm, use `/delete_team {team_role} {team_role.id}`.',
                color=constants.Colors.ERROR.value)

        await ctx.send(embed=embed)
Ejemplo n.º 2
0
    async def _new_control_role(self, ctx: SlashContext, name: str):
        """Create a new control role."""
        role = await discord.Guild.create_role(ctx.guild,
                                               name=name,
                                               color=models.ControlRole.DEFAULT_COLOR)

        guild = data_manager.get_guild(ctx.guild)
        control_role = models.ControlRole(guild, role)
        control_role.perms = await Configuration.configure_perms(self.bot, ctx, name)
Ejemplo n.º 3
0
    async def _change_locale(self, ctx, locale: str):
        """Change the guild locale."""
        guild = data_manager.get_guild(ctx.guild)
        guild.locale = locale

        await ctx.send(embed=discord.Embed(
            title=f'{constants.Emojis.LOCALE.value} Server locale set: __{locale}__',
            description='Note: this will only change how date and time are formatted.',
            color=constants.Colors.DEFAULT.value))
Ejemplo n.º 4
0
    async def _set_channel(self, ctx, channel: discord.TextChannel):
        """Set the guild target channel."""
        if isinstance(channel, discord.CategoryChannel):
            return

        guild = data_manager.get_guild(ctx.guild)
        guild.target_channel = channel

        await ctx.send(embed=discord.Embed(
            title=f'{constants.Emojis.CONFIG.value} Channel set: __{guild.target_channel.name}__',
            color=constants.Colors.DEFAULT.value))
Ejemplo n.º 5
0
    async def _change_tz(self, ctx, offset: float):
        """Change the guild locale."""
        offset = float(offset)
        guild = data_manager.get_guild(ctx.guild)
        guild.tz = timezone(timedelta(hours=offset))

        await ctx.send(embed=discord.Embed(
            title=constants.Emojis.TIMEZONE.value + ' Server timezone set: __UTC {offset}__'
            .format(offset=f'+{offset}' if offset >= 0 else offset),
            description='Note: will not adapt to daylight savings and etc. automatically.',
            color=constants.Colors.DEFAULT.value))
Ejemplo n.º 6
0
    async def _do_receive_announcements(self, ctx: SlashContext, receive: bool):
        """Set whether the guild wants to receive official announcements or not."""
        guild = data_manager.get_guild(ctx.guild)
        guild.receive_announcements = receive

        title = (f'{constants.Emojis.CONFIG.value} This server will now'
                 + ('' if receive else ' not') + ' receive announcements.')

        embed = discord.Embed(
            title=title,
            color=constants.Colors.DEFAULT.value)

        await ctx.send(embed=embed)
Ejemplo n.º 7
0
    async def _new_task(self,
                        ctx: SlashContext,
                        content: str,
                        due_date: str,
                        due_time: str = None,
                        tags: str = None):
        """Create a new task."""
        guild = data_manager.get_guild(ctx.guild)
        checks.does_user_have_permission(guild, ctx.author,
                                         'create/edit tasks')

        if due_time:
            # Parse due time.
            due_time = datetime.strptime(due_time.upper(),
                                         TIME_FORMATS[guild.locale]).time()

        else:
            # Use default value.
            due_time = datetime.strptime(Tasks.DEFAULT_DUE_TIME,
                                         '%H:%M').time()

        due_date = dt_utils.string_to_date(due_date, guild.tz,
                                           DATE_FORMATS[guild.locale] + '/%Y')
        due_datetime = datetime.combine(due_date,
                                        due_time).replace(tzinfo=guild.tz)
        checks.has_datetime_passed(due_datetime, guild.tz)

        # Parse tags.
        team = await guild.get_user_team(self.bot, ctx)
        tags = team.parse_tags(tags) if tags is not None else []

        # Create the task and add it to the team.
        new_task = models.Task(content, tags, due_datetime)
        team.add_task(new_task)

        # Send confirmation.
        embed = discord.Embed(
            title=constants.Emojis.CREATE.value + ' New task due by'
            ' __{due_date}__ __{due_time}__ created:'.format(
                due_date=dt_utils.date_to_relative_name(
                    new_task.due_datetime.date(), guild.tz, guild.locale),
                due_time=new_task.due_datetime.strftime(
                    TIME_FORMATS[guild.locale])),
            description=f'{new_task.content}\n',
            color=team.role.color).set_footer(text=team.role.name.upper())

        if tags:
            embed.add_field(name=f'{constants.Emojis.TAGS.value} Tags:',
                            value=f'{iter_utils.format_iter(new_task.tags)}')

        await ctx.send(embed=embed)
Ejemplo n.º 8
0
    async def _control_roles(self, ctx: SlashContext):
        """Show all control roles in a guild and their permissions."""
        guild = data_manager.get_guild(ctx.guild)

        # Using helper functions, format a dictionary of roles and their formatted permissions.
        desc = iter_utils.format_dict({f'**{i.role}**': iter_utils.format_dict(i.perms) + '\n'
                                       for i in guild.control_roles})

        desc += ('\nGrant these roles to apply users their permissions.'
                 '\nYou can delete them through Server Settings like any other role.')

        await ctx.send(embed=discord.Embed(
            title=f'{constants.Emojis.CONFIG.value} Control roles in __{ctx.guild}__:',
            description=f'{desc}',
            color=constants.Colors.DEFAULT.value))
Ejemplo n.º 9
0
    async def _delete_tasks(self,
                            ctx: SlashContext,
                            date: str,
                            indexes: str = None):
        """Delete tasks by their due date and index."""
        # Get the necessary information and check it.
        guild = data_manager.get_guild(ctx.guild)
        checks.does_user_have_permission(guild, ctx.author, 'delete tasks')

        date = dt_utils.string_to_date(date, guild.tz,
                                       DATE_FORMATS[guild.locale] + '/%Y')
        checks.has_date_passed(date, guild.tz)

        team = await guild.get_user_team(self.bot, ctx)
        tasks = checks.are_there_tasks_due_on_date(team, date)

        if not indexes:
            # Show every task due on the date, sorted by index.
            embed = discord.Embed(
                title=
                f'{constants.Emojis.DELETE.value} Which tasks do you wish to delete?',
                description='Use `/delete_tasks {} [indexes]`.\n'.format(
                    dt_utils.format_date(date, guild.tz, guild.locale)),
                color=team.role.color)

            embed.description += models.Team.format_tasks_due_on_date(
                tasks, guild.locale)

        else:
            # Delete the tasks.
            indexes = [int(i) for i in indexes.split(';')]
            tasks_selected = []

            for index in indexes:
                tasks_selected.append(tasks[index - 1])
                team.del_task(tasks_selected[-1])

            embed = discord.Embed(
                title=constants.Emojis.DELETE.value + ' __{}__ task(s) due on'
                ' __{}__ was/were deleted:'.format(
                    len(tasks_selected),
                    dt_utils.format_date(date, guild.tz, guild.locale)),
                description=iter_utils.format_iter(
                    list(i.content for i in tasks_selected)),
                color=team.role.color)

        embed.set_footer(text=team.role.name.upper())
        await ctx.send(embed=embed)
Ejemplo n.º 10
0
    async def _teams(self, ctx: SlashContext):
        """Show every team in the guild."""
        guild = data_manager.get_guild(ctx.guild)
        sorted_teams = sorted(guild.teams, key=lambda x: x.role.name)
        desc = ''

        for team in sorted_teams:
            desc += '• ' + ('**(Joined)** ' if team.role in ctx.author.roles
                            else '') + f'{team.role}\n'

        desc += '\nJoin or leave a team with `/team`.'

        embed = discord.Embed(
            title=f'{constants.Emojis.TEAMS.value} Teams in __{ctx.guild}__:',
            description=desc,
            color=constants.Colors.DEFAULT.value)

        await ctx.send(embed=embed)
Ejemplo n.º 11
0
    async def _team(self, ctx: SlashContext, team_role: discord.Role):
        """Join or leave a team."""
        guild = data_manager.get_guild(ctx.guild)
        checks.does_user_have_permission(guild, ctx.author, 'join/leave teams')
        team = checks.is_role_tied_to_team(guild, team_role)

        if team in guild.get_user_teams(ctx.author):
            await ctx.author.remove_roles(team.role)
            title = f'{constants.Emojis.LEAVE.value} __{ctx.author}__ has left __{team.role}__.'
            desc = f'Rejoin with `/team {team.role}`.'

        else:
            await ctx.author.add_roles(team.role)
            title = f'{constants.Emojis.JOIN.value} __{ctx.author}__ has joined __{team.role}__.'
            desc = f'Leave it with `/team {team.role}`.'

        embed = discord.Embed(title=title,
                              description=desc,
                              color=team.role.color)
        await ctx.send(embed=embed)
Ejemplo n.º 12
0
    async def _new_team(self, ctx: SlashContext, name: str):
        """Create a new team in the guild."""
        guild = data_manager.get_guild(ctx.guild)
        checks.does_user_have_permission(guild, ctx.author, 'create teams')

        role = await discord.Guild.create_role(
            ctx.guild, name=name, color=constants.Colors.DEFAULT.value)

        role.position = 0
        team = models.Team(role=role)
        guild.add_team(team)

        embed = discord.Embed(
            title=
            f'{constants.Emojis.TEAMS.value} New team created: \"__{role}__\"',
            description=f'Join the team with `/team`.'
            f'\n\nUsers who are granted the {role.mention} role'
            f' will automatically join it.',
            color=constants.Colors.DEFAULT.value)

        await ctx.send(embed=embed)
Ejemplo n.º 13
0
    async def _edit_notifications(self, ctx: SlashContext, batch: bool,
                                  early: bool, early_time: int, exact: bool):
        """Edit when the bot should send task notifications to a team."""
        guild = data_manager.get_guild(ctx.guild)
        team = await guild.get_user_team(self.bot, ctx)

        team.notify.update({
            'batch': batch,
            'early': early,
            'early_time': early_time,
            'exact': exact
        })

        embed = discord.Embed(
            title=f'{constants.Emojis.CONFIG.value} Notification settings'
            f' updated for __{team.role}__:',
            description=f'Note: this will not affect tasks already created.'
            f'\n{iter_utils.format_dict(team.notify)}',
            color=team.role.color).set_footer(text=team.role.name.upper())

        await ctx.send(embed=embed)
Ejemplo n.º 14
0
    async def on_guild_join(self, disc_guild_obj: discord.Guild):
        """Send greetings when entering a guild."""

        print('>> {time}: Ivone has entered {guild}'.format(
            time=datetime.strftime(datetime.now(), '%H:%M'),
            guild=disc_guild_obj))

        guild = data_manager.get_guild(disc_guild_obj)

        await guild.target_channel.send(embed=discord.Embed(
            title=f':grinning: Hello, __{guild.disc_guild_obj.name}__!',
            description=
            '• **Ivone** is a task management bot for teams using Discord.'
            ' Be it at school, work or anywhere else, Ivone keeps you organized.'
            '\n• To start, create a team with `/new_team`.'
            '\n• For more information, use `/help`.'
            '\n• Problems? Suggestions? Use `/feedback`!',
            color=constants.Colors.DEFAULT.value))

        # Warn the guild about their current (probably default) timezone and locale.
        example_date = dt.date(year=1970, month=12, day=1)
        example_time = dt.time(hour=12, minute=0)
        tz_offset = guild.tz.utcoffset(None).total_seconds() / 3600

        await guild.target_channel.send(embed=discord.Embed(
            title=
            f'{constants.Emojis.WARNING.value} Warning: check timezone and locale',
            description=
            f'Right now, this server\'s locale is set to {guild.locale}.'
            f' That means midday of december 1st will be formatted as'
            f' {example_date.strftime(DATE_FORMATS[guild.locale])}'
            f' {example_time.strftime(TIME_FORMATS[guild.locale])},'
            f' for example.'
            f'\nAlso, the timezone is set to'
            f' UTC {"+" if tz_offset > 0 else ""}{tz_offset}.'
            f'\nTo change these settings, use `/change_timezone`'
            f' and `/change_locale`.',
            color=constants.Colors.ERROR.value))
Ejemplo n.º 15
0
    async def _edit_task(self,
                         ctx: SlashContext,
                         date: str,
                         task_index: int = None,
                         attribute: str = None,
                         new_value: str = None):
        """Edit a single attribute in a task."""
        # Get the necessary information and check it.
        guild = data_manager.get_guild(ctx.guild)
        checks.does_user_have_permission(guild, ctx.author,
                                         'create/edit tasks')

        date = dt_utils.string_to_date(date, guild.tz,
                                       DATE_FORMATS[guild.locale] + '/%Y')
        checks.has_date_passed(date, guild.tz)

        team = await guild.get_user_team(self.bot, ctx)
        tasks = checks.are_there_tasks_due_on_date(team, date)

        if not task_index:
            # Show every task due on the selected date.
            embed = discord.Embed(
                title=
                f'{constants.Emojis.EDIT.value} Which task do you wish to edit?',
                description='Use `/edit_task {} [index]`.\n'.format(
                    dt_utils.format_date(date, guild.tz, guild.locale)),
                color=team.role.color)

            embed.description += models.Team.format_tasks_due_on_date(
                tasks, guild.locale)

        else:
            # Show every attribute of the selected task.
            task = tasks[int(task_index) - 1]

            if not attribute:
                embed = discord.Embed(
                    title=
                    f'{constants.Emojis.EDIT.value} Which attribute of this task'
                    f' do you wish to edit?',
                    description=
                    ('Use `/edit_task {date} {task_index} [attribute] [new value]`.'
                     '\n\n{task}').format(date=dt_utils.format_date(
                         date, guild.tz, guild.locale),
                                          task_index=task_index,
                                          task=task.to_formatted_string()),
                    color=team.role.color)

            else:
                # Edit the task.
                if attribute == 'content':
                    task.content = new_value

                elif attribute == 'due date':
                    new_due_date = dt_utils.string_to_date(
                        new_value, guild.tz,
                        DATE_FORMATS[guild.locale] + '/%Y')

                    new_due_datetime = task.due_datetime.replace(
                        day=new_due_date.day,
                        month=new_due_date.month,
                        year=new_due_date.year)

                    # In case the task was edited to be due today and had a
                    # due time now already in the past, change it to 23:59.
                    try:
                        checks.has_datetime_passed(new_due_datetime, guild.tz)

                    except checks.DateHasAlreadyPassedError:
                        new_due_datetime = new_due_datetime.replace(hour=23,
                                                                    minute=59)
                        checks.has_datetime_passed(new_due_datetime, guild.tz)

                    task.due_datetime = new_due_datetime

                elif attribute == 'due time':
                    new_due_time = datetime.strptime(
                        new_value, TIME_FORMATS[guild.locale])

                    new_due_datetime = task.due_datetime.replace(
                        hour=new_due_time.hour, minute=new_due_time.minute)

                    checks.has_datetime_passed(new_due_datetime, guild.tz)
                    task.due_datetime = new_due_datetime

                elif attribute == 'tags':
                    task.tags = team.parse_tags(new_value)

                embed = discord.Embed(
                    title=
                    f'{constants.Emojis.EDIT.value} Task edited successfully:',
                    description=f'{task.to_formatted_string()}',
                    color=team.role.color)

        embed.set_footer(text=team.role.name.upper())
        await ctx.send(embed=embed)
Ejemplo n.º 16
0
 async def devtest(self, ctx: Context):
     """Edit this whenever a command for testing something is needed."""
     guild = data_manager.get_guild(ctx.guild)
     await guild.announce('target_channel')
     await ctx.send(Development.CMD_EXECUTED)
Ejemplo n.º 17
0
    async def on_guild_role_delete(self, role: discord.Role):
        """Delete data that relied on a role that no longer exists."""
        guild = data_manager.get_guild(role.guild)

        if team := guild.get_team(role):
            guild.teams.remove(team)
Ejemplo n.º 18
0
 async def _edit_control_role(self, ctx: SlashContext, role: discord.Role):
     """Edit what users with a control role have permission to do."""
     guild = data_manager.get_guild(ctx.guild)
     control_role = checks.is_role_tied_to_control_role(guild, role)
     control_role.perms = await Configuration.configure_perms(self.bot, ctx, role.name)