Esempio n. 1
0
File: main.py Progetto: Korben85/bot
    async def nudge_channel(message, stripped, preferences):

        time_parser = TimeExtractor(stripped, preferences.timezone)

        try:
            t = time_parser.extract_displacement()

        except InvalidTime:
            await message.channel.send(embed=discord.Embed(
                description=preferences.language.get_string('nudge/invalid_time')))

        else:
            if 2 ** 15 > t > -2 ** 15:
                channel, _ = Channel.get_or_create(message.channel)

                channel.nudge = t

                session.commit()

                await message.channel.send(
                    embed=discord.Embed(description=preferences.language.get_string('nudge/success').format(t)))

            else:
                await message.channel.send(
                    embed=discord.Embed(description=preferences.language.get_string('nudge/invalid_time')))
Esempio n. 2
0
File: main.py Progetto: Korben85/bot
    async def blacklist(message, _, preferences):

        target_channel = message.channel_mentions[0] if len(message.channel_mentions) > 0 else message.channel

        channel, _ = Channel.get_or_create(target_channel)

        channel.blacklisted = not channel.blacklisted

        if channel.blacklisted:
            await message.channel.send(
                embed=discord.Embed(description=preferences.language.get_string('blacklist/added')))

        else:
            await message.channel.send(
                embed=discord.Embed(description=preferences.language.get_string('blacklist/removed')))

        session.commit()
Esempio n. 3
0
    async def on_message(self, message):
        member, _ = Member.get_or_create(
            discordID=message.author.id, defaults={"name": message.author.display_name}
        )

        name = (
            message.channel.name
            if isinstance(message.channel, discord.TextChannel)
            else "DM"
        )

        channel, _ = Channel.get_or_create(
            discordID=message.channel.id, defaults={"name": name}
        )

        message = Message.create(
            discordID=message.id,
            member=member,
            channel=channel,
            message=message.content,
            # embed = str(message.embeds),
        )
Esempio n. 4
0
File: main.py Progetto: Korben85/bot
    async def pause_channel(self, message, stripped, preferences):

        channel, _ = Channel.get_or_create(message.channel)

        if len(stripped) > 0:
            # argument provided for time
            time_parser = TimeExtractor(stripped, preferences.timezone)

            try:
                t = time_parser.extract_displacement()

            except InvalidTime:
                await message.channel.send(embed=discord.Embed(
                    description=preferences.language['pause/invalid_time']))

            else:
                channel.paused = True
                channel.paused_until = datetime.now() + timedelta(seconds=t)

                display = channel.paused_until \
                    .astimezone(pytz.timezone(preferences.timezone)) \
                    .strftime('%Y-%m-%d, %H:%M:%S')

                await message.channel.send(
                    embed=discord.Embed(description=preferences.language['pause/paused_until'].format(display)))

        else:
            # otherwise toggle the paused status and clear the time
            channel.paused = not channel.paused
            channel.paused_until = None

            if channel.paused:
                await message.channel.send(
                    embed=discord.Embed(description=preferences.language['pause/paused_indefinite']))

            else:
                await message.channel.send(
                    embed=discord.Embed(description=preferences.language['pause/unpaused']))
Esempio n. 5
0
File: main.py Progetto: Korben85/bot
    async def todo_command(message, stripped, preferences, command):
        if command == 'todos':
            location, _ = Channel.get_or_create(message.channel)
            name = 'Channel'
            todos = location.todo_list.all() + preferences.guild.todo_list.filter(Todo.channel_id.is_(None)).all()
            channel = location
            guild = preferences.guild

        else:
            location = preferences.user
            name = 'Your'
            todos = location.todo_list.filter(Todo.guild_id.is_(None)).all()
            channel = None
            guild = None

        splits = stripped.split(' ')

        if len(splits) == 1 and splits[0] == '':
            msg = ['\n{}: {}'.format(i, todo.value) for i, todo in enumerate(todos, start=1)]
            if len(msg) == 0:
                msg.append(preferences.language.get_string('todo/add').format(
                    prefix=preferences.prefix, command=command))

            s = ''
            for item in msg:
                if len(item) + len(s) < 2048:
                    s += item
                else:
                    await message.channel.send(embed=discord.Embed(title='{} TODO'.format(name), description=s))
                    s = ''

            if len(s) > 0:
                await message.channel.send(embed=discord.Embed(title='{} TODO'.format(name), description=s))

        elif len(splits) >= 2:
            if splits[0] == 'add':
                s = ' '.join(splits[1:])

                todo = Todo(value=s, guild=guild, user=preferences.user, channel=channel)
                session.add(todo)
                await message.channel.send(preferences.language.get_string('todo/added').format(name=s))

            elif splits[0] == 'remove':
                try:
                    todo = session.query(Todo).filter(Todo.id == todos[int(splits[1]) - 1].id).first()
                    session.query(Todo).filter(Todo.id == todos[int(splits[1]) - 1].id).delete(
                        synchronize_session='fetch')

                    await message.channel.send(preferences.language.get_string('todo/removed').format(todo.value))

                except ValueError:
                    await message.channel.send(
                        preferences.language.get_string('todo/error_value').format(
                            prefix=preferences.prefix, command=command))

                except IndexError:
                    await message.channel.send(preferences.language.get_string('todo/error_index'))

            else:
                await message.channel.send(
                    preferences.language.get_string('todo/help').format(prefix=preferences.prefix, command=command))

        else:
            if stripped == 'clear':
                todos.clear()
                await message.channel.send(preferences.language.get_string('todo/cleared'))

            else:
                await message.channel.send(
                    preferences.language.get_string('todo/help').format(prefix=preferences.prefix, command=command))

        session.commit()
Esempio n. 6
0
File: main.py Progetto: Korben85/bot
    async def create_reminder(self, message: discord.Message, location: int, text: str, time: int,
                              interval: typing.Optional[int] = None, method: str = 'natural') -> ReminderInformation:
        ut: float = unix_time()

        if time > ut + MAX_TIME:
            return ReminderInformation(CreateReminderResponse.LONG_TIME)

        elif time < ut:

            if (ut - time) < 10:
                time = int(ut)

            else:
                return ReminderInformation(CreateReminderResponse.PAST_TIME)

        channel: typing.Optional[Channel] = None
        user: typing.Optional[User] = None

        creator: User = User.from_discord(message.author)

        # noinspection PyUnusedLocal
        discord_channel: typing.Optional[typing.Union[discord.TextChannel, DMChannelId]] = None

        # command fired inside a guild
        if message.guild is not None:
            discord_channel = message.guild.get_channel(location)

            if discord_channel is not None:  # if not a DM reminder

                channel, _ = Channel.get_or_create(discord_channel)

                await channel.attach_webhook(discord_channel)

                time += channel.nudge

            else:
                user = await self.find_and_create_member(location, message.guild)

                if user is None:
                    return ReminderInformation(CreateReminderResponse.INVALID_TAG)

                discord_channel = DMChannelId(user.dm_channel, user.user)

        # command fired in a DM; only possible target is the DM itself
        else:
            user = User.from_discord(message.author)
            discord_channel = DMChannelId(user.dm_channel, message.author.id)

        if interval is not None:
            if MIN_INTERVAL > interval:
                return ReminderInformation(CreateReminderResponse.SHORT_INTERVAL)

            elif interval > MAX_TIME:
                return ReminderInformation(CreateReminderResponse.LONG_INTERVAL)

            else:
                # noinspection PyArgumentList
                reminder = Reminder(
                    message=Message(content=text),
                    channel=channel or user.channel,
                    time=time,
                    enabled=True,
                    method=method,
                    interval=interval,
                    set_by=creator.id)
                session.add(reminder)
                session.commit()

        else:
            # noinspection PyArgumentList
            reminder = Reminder(
                message=Message(content=text),
                channel=channel or user.channel,
                time=time,
                enabled=True,
                method=method,
                set_by=creator.id)
            session.add(reminder)
            session.commit()

        return ReminderInformation(CreateReminderResponse.OK, channel=discord_channel, time=time)
Esempio n. 7
0
File: main.py Progetto: Korben85/bot
    async def on_message(self, message):

        def _check_self_permissions(_channel):
            p = _channel.permissions_for(message.guild.me)

            return p.send_messages and p.embed_links

        async def _get_user(_message):
            _user = session.query(User).filter(User.user == message.author.id).first()
            if _user is None:
                dm_channel_id = (await message.author.create_dm()).id

                c = session.query(Channel).filter(Channel.channel == dm_channel_id).first()

                if c is None:
                    c = Channel(channel=dm_channel_id)
                    session.add(c)
                    session.flush()

                    _user = User(user=_message.author.id, dm_channel=c.id, name='{}#{}'.format(
                        _message.author.name, _message.author.discriminator))
                    session.add(_user)
                    session.flush()

            return _user

        if (message.author.bot and self.config.ignore_bots) or \
                message.content is None or \
                message.tts or \
                len(message.attachments) > 0 or \
                self.match_string is None:

            # either a bot or cannot be a command
            return

        elif message.guild is None:
            # command has been DMed. dont check for prefix :)
            split = message.content.split(' ')

            command_word = split[0].lower()
            if len(command_word) > 0:
                if command_word[0] == '$':
                    command_word = command_word[1:]

                args = ' '.join(split[1:]).strip()

                if command_word in self.command_names:
                    command = self.commands[command_word]

                    if command.allowed_dm:
                        # get user
                        user = await _get_user(message)

                        await command.func(message, args, Preferences(None, user))

        elif _check_self_permissions(message.channel):
            # command sent in guild. check for prefix & call
            match = re.match(
                self.match_string,
                message.content,
                re.MULTILINE | re.DOTALL | re.IGNORECASE
            )

            if match is not None:
                # matched command structure; now query for guild to compare prefix
                guild = session.query(Guild).filter(Guild.guild == message.guild.id).first()
                if guild is None:
                    guild = Guild(guild=message.guild.id)

                    session.add(guild)
                    session.flush()

                # if none, suggests mention has been provided instead since pattern still matched
                if (prefix := match.group('prefix')) in (guild.prefix, None):
                    # prefix matched, might as well get the user now since this is a very small subset of messages
                    user = await _get_user(message)

                    if guild not in user.guilds:
                        guild.users.append(user)

                    # create the nice info manager
                    info = Preferences(guild, user)

                    command_word = match.group('cmd').lower()
                    stripped = match.group('args') or ''
                    command = self.commands[command_word]

                    # some commands dont get blacklisted e.g help, blacklist
                    if command.blacklists:
                        channel, just_created = Channel.get_or_create(message.channel)

                        if channel.guild_id is None:
                            channel.guild_id = guild.id

                        await channel.attach_webhook(message.channel)

                        if channel.blacklisted:
                            await message.channel.send(
                                embed=discord.Embed(description=info.language.get_string('blacklisted')))
                            return

                    # blacklist checked; now do command permissions
                    if command.check_permissions(message.author, guild):
                        if message.guild.me.guild_permissions.manage_webhooks:
                            await command.func(message, stripped, info)
                            session.commit()

                        else:
                            await message.channel.send(info.language.get_string('no_perms_webhook'))

                    else:
                        await message.channel.send(
                            info.language.get_string(
                                str(command.permission_level)).format(prefix=prefix))
Esempio n. 8
0
File: main.py Progetto: Korben85/bot
    async def look(message, stripped, preferences):

        def relative_time(t):
            days, seconds = divmod(int(t - unix_time()), 86400)
            hours, seconds = divmod(seconds, 3600)
            minutes, seconds = divmod(seconds, 60)

            sections = []

            for var, name in zip((days, hours, minutes, seconds), ('days', 'hours', 'minutes', 'seconds')):
                if var > 0:
                    sections.append('{} {}'.format(var, name))

            return ', '.join(sections)

        def absolute_time(t):
            return datetime.fromtimestamp(t, pytz.timezone(preferences.timezone)).strftime('%Y-%m-%d %H:%M:%S')

        r = re.search(r'(\d+)', stripped)

        limit: typing.Optional[int] = None
        if r is not None:
            limit = int(r.groups()[0])

        if 'enabled' in stripped:
            show_disabled = False
        else:
            show_disabled = True

        if 'time' in stripped:
            time_func = absolute_time

        else:
            time_func = relative_time

        if message.guild is None:
            channel = preferences.user.channel
            new = False

        else:
            discord_channel = message.channel_mentions[0] if len(message.channel_mentions) > 0 else message.channel

            channel, new = Channel.get_or_create(discord_channel)

        if new:
            await message.channel.send(preferences.language.get_string('look/no_reminders'))

        else:
            reminder_query = channel.reminders.order_by(Reminder.time)

            if not show_disabled:
                reminder_query = reminder_query.filter(Reminder.enabled)

            if limit is not None:
                reminder_query = reminder_query.limit(limit)

            if reminder_query.count() > 0:
                if limit is not None:
                    await message.channel.send(preferences.language.get_string('look/listing_limited').format(
                        reminder_query.count()))

                else:
                    await message.channel.send(preferences.language.get_string('look/listing'))

                s = ''
                for reminder in reminder_query:
                    string = '\'{}\' *{}* **{}** {}\n'.format(
                        reminder.message_content(),
                        preferences.language.get_string('look/inter'),
                        time_func(reminder.time),
                        '' if reminder.enabled else '`disabled`')

                    if len(s) + len(string) > 2000:
                        await message.channel.send(s, allowed_mentions=NoMention)
                        s = string
                    else:
                        s += string

                await message.channel.send(s, allowed_mentions=NoMention)

            else:
                await message.channel.send(preferences.language.get_string('look/no_reminders'))
Esempio n. 9
0
    async def look(message, stripped, preferences):

        r = re.search(r'(\d+)', stripped)

        limit: typing.Optional[int] = None
        if r is not None:
            limit = int(r.groups()[0])

        if 'enabled' in stripped:
            show_disabled = False
        else:
            show_disabled = True

        if message.guild is None:
            channel = preferences.user.channel
            new = False

        else:
            discord_channel = message.channel_mentions[0] if len(message.channel_mentions) > 0 else message.channel

            channel, new = Channel.get_or_create(discord_channel)

        if new:
            await message.channel.send(preferences.language.get_string('look/no_reminders'))

        else:
            reminder_query = channel.reminders.order_by(Reminder.time)

            if not show_disabled:
                reminder_query = reminder_query.filter(Reminder.enabled)

            if limit is not None:
                reminder_query = reminder_query.limit(limit)

            if reminder_query.count() > 0:
                if limit is not None:
                    await message.channel.send(preferences.language.get_string('look/listing_limited').format(
                        reminder_query.count()))

                else:
                    await message.channel.send(preferences.language.get_string('look/listing'))

                s = ''
                for reminder in reminder_query:
                    string = '\'{}\' *{}* **{}** {}\n'.format(
                        reminder.message_content(),
                        preferences.language.get_string('look/inter'),
                        datetime.fromtimestamp(reminder.time, pytz.timezone(preferences.timezone)).strftime(
                            '%Y-%m-%d %H:%M:%S'),
                        '' if reminder.enabled else '`disabled`')

                    if len(s) + len(string) > 2000:
                        await message.channel.send(s, allowed_mentions=NoMention)
                        s = string
                    else:
                        s += string

                await message.channel.send(s, allowed_mentions=NoMention)

            else:
                await message.channel.send(preferences.language.get_string('look/no_reminders'))
Esempio n. 10
0
class BotClient(discord.AutoShardedClient):
    def __init__(self, *args, **kwargs):
        self.start_time: float = unix_time()

        self.commands: typing.Dict[str, Command] = {

            'help': Command('help', self.help, blacklists=False),
            'info': Command('info', self.info),
            'donate': Command('donate', self.donate),

            'prefix': Command('prefix', self.change_prefix, False, PermissionLevels.RESTRICTED),
            'blacklist': Command('blacklist', self.blacklist, False, PermissionLevels.RESTRICTED, blacklists=False),
            # TODO: remodel restriction table with FKs for role table
            'restrict': Command('restrict', self.restrict, False, PermissionLevels.RESTRICTED),

            'timezone': Command('timezone', self.set_timezone),
            'lang': Command('lang', self.set_language),
            'clock': Command('clock', self.clock),

            'offset': Command('offset', self.offset_reminders, True, PermissionLevels.RESTRICTED),
            'nudge': Command('nudge', self.nudge_channel, True, PermissionLevels.RESTRICTED),

            'natural': Command('natural', self.natural, True, PermissionLevels.MANAGED),
            'n': Command('natural', self.natural, True, PermissionLevels.MANAGED),
            'remind': Command('remind', self.remind_cmd, True, PermissionLevels.MANAGED),
            'r': Command('remind', self.remind_cmd, True, PermissionLevels.MANAGED),
            'interval': Command('interval', self.interval_cmd, True, PermissionLevels.MANAGED),
            # TODO: remodel timer table with FKs for guild table
            'timer': Command('timer', self.timer, False, PermissionLevels.MANAGED),
            'del': Command('del', self.delete, True, PermissionLevels.MANAGED),
            # TODO: allow looking at reminder attributes in full by name
            'look': Command('look', self.look, True, PermissionLevels.MANAGED),

            'todos': Command('todos', self.todo, False, PermissionLevels.MANAGED),
            'todo': Command('todo', self.todo),

            'ping': Command('ping', self.time_stats)
        }

        self.match_string = None

        self.command_names = set(self.commands.keys())
        self.joined_names = '|'.join(self.command_names)

        # used in restrict command for filtration
        self.max_command_length = max(len(x) for x in self.command_names)

        self.config: Config = Config(filename='config.ini')

        self.executor: concurrent.futures.ThreadPoolExecutor = concurrent.futures.ThreadPoolExecutor()
        self.c_session: typing.Optional[aiohttp.ClientSession] = None

        super(BotClient, self).__init__(*args, **kwargs)

    async def do_blocking(self, method):
        # perform a long running process within a threadpool
        a, _ = await asyncio.wait([self.loop.run_in_executor(self.executor, method)])
        return [x.result() for x in a][0]

    async def find_and_create_member(self, member_id: int, context_guild: typing.Optional[discord.Guild]) \
            -> typing.Optional[User]:
        u: User = session.query(User).filter(User.user == member_id).first()

        if u is None and context_guild is not None:
            m = context_guild.get_member(member_id) or self.get_user(member_id)

            if m is not None:
                c = Channel(channel=(await m.create_dm()).id)
                session.add(c)
                session.flush()

                u = User(user=m.id, name='{}'.format(m), dm_channel=c.id)

                session.add(u)
                session.commit()

        return u

    async def is_patron(self, member_id) -> bool:
        if self.config.patreon_enabled:

            url = 'https://discordapp.com/api/v6/guilds/{}/members/{}'.format(self.config.patreon_server, member_id)

            head = {
                'authorization': 'Bot {}'.format(self.config.token),
                'content-type': 'application/json'
            }

            async with self.c_session.get(url, headers=head) as resp:

                if resp.status == 200:
                    member = await resp.json()
                    roles = [int(x) for x in member['roles']]

                else:
                    return False

            return self.config.patreon_role in roles

        else:
            return True

    @staticmethod
    async def welcome(guild, *_):

        for channel in guild.text_channels:
            if channel.permissions_for(guild.me).send_messages and not channel.is_nsfw():
                await channel.send('Thank you for adding reminder-bot! To begin, type `$help`!')
                break

            else:
                continue

    async def on_error(self, *a, **k):
        session.rollback()
        raise

    async def on_ready(self):

        print('Logged in as')
        print(self.user.name)
        print(self.user.id)

        self.match_string = \
            r'(?:(?:<@ID>\s+)|(?:<@!ID>\s+)|(?P<prefix>\S{1,5}?))(?P<cmd>COMMANDS)(?:$|\s+(?P<args>.*))' \
            .replace('ID', str(self.user.id)).replace('COMMANDS', self.joined_names)

        self.c_session: aiohttp.client.ClientSession = aiohttp.ClientSession()

        if self.config.patreon_enabled:
            print('Patreon is enabled. Will look for servers {}'.format(self.config.patreon_server))

        print('Local timezone set to *{}*'.format(self.config.local_timezone))

    async def on_guild_join(self, guild):
        await self.send()

        await self.welcome(guild)

    # noinspection PyMethodMayBeStatic
    async def on_guild_channel_delete(self, channel):
        session.query(Channel).filter(Channel.channel == channel.id).delete(synchronize_session='fetch')

    async def send(self):
        if self.config.dbl_token:
            guild_count = len(self.guilds)

            dump = json_dump({
                'server_count': guild_count
            })

            head = {
                'authorization': self.config.dbl_token,
                'content-type': 'application/json'
            }

            url = 'https://discordbots.org/api/bots/stats'
            async with self.c_session.post(url, data=dump, headers=head) as resp:
                print('returned {0.status} for {1}'.format(resp, dump))

    # noinspection PyBroadException
    async def on_message(self, message):

        def _check_self_permissions(_channel):
            p = _channel.permissions_for(message.guild.me)

            return p.send_messages and p.embed_links

        async def _get_user(_message):
            _user = session.query(User).filter(User.user == message.author.id).first()
            if _user is None:
                dm_channel_id = (await message.author.create_dm()).id

                c = session.query(Channel).filter(Channel.channel == dm_channel_id).first()

                if c is None:
                    c = Channel(channel=dm_channel_id)
                    session.add(c)
                    session.flush()

                    _user = User(user=_message.author.id, dm_channel=c.id, name='{}#{}'.format(
                        _message.author.name, _message.author.discriminator))
                    session.add(_user)
                    session.flush()

            return _user

        elif message.guild is None:
            # command has been DMed. dont check for prefix :)
            split = message.content.split(' ')

            command_word = split[0].lower()
            if command_word[0] == '$':
                command_word = command_word[1:]

            args = ' '.join(split[1:]).strip()

            if command_word in self.command_names:
                command = self.commands[command_word]

                if command.allowed_dm:
                    # get user
                    user = await _get_user(message)

                    await command.func(message, args, Preferences(None, user))
                    session.commit()

        elif _check_self_permissions(message.channel):
            # command sent in guild. check for prefix & call
            match = re.match(
                self.match_string,
                message.content,
                re.MULTILINE | re.DOTALL | re.IGNORECASE
            )

            if match is not None:
                # matched command structure; now query for guild to compare prefix
                guild = session.query(Guild).filter(Guild.guild == message.guild.id).first()
                if guild is None:
                    guild = Guild(guild=message.guild.id)

                    session.add(guild)
                    session.flush()

                # if none, suggests mention has been provided instead since pattern still matched
                if (prefix := match.group('prefix')) in (guild.prefix, None):
                    # prefix matched, might as well get the user now since this is a very small subset of messages
                    user = await _get_user(message)

                    if guild not in user.guilds:
                        guild.users.append(user)

                    # create the nice info manager
                    info = Preferences(guild, user)

                    command_word = match.group('cmd').lower()
                    stripped = match.group('args') or ''
                    command = self.commands[command_word]

                    # some commands dont get blacklisted e.g help, blacklist
                    if command.blacklists:
                        channel, just_created = Channel.get_or_create(message.channel)

                        if channel.guild_id is None:
                            channel.guild_id = guild.id

                        if channel.blacklisted:
                            await message.channel.send(
                                embed=discord.Embed(description=info.language.get_string('blacklisted')))
                            return

                    # blacklist checked; now do command permissions
                    if command.check_permissions(message.author, guild):
                        if message.guild.me.guild_permissions.manage_webhooks:
                            await command.func(message, stripped, info)
                            session.commit()

                        else:
                            await message.channel.send(info.language.get_string('no_perms_webhook'))

                    else:
                        await message.channel.send(
                            info.language.get_string(
                                str(command.permission_level)).format(prefix=prefix))