Exemplo n.º 1
0
async def scan_all_reminders(*, client):
    """Scans all reminders and posts the ones that should be posted.

    Args:
        client (discord.py client object): the discord.py client needed to post discord messages
    """
    # dirty solution since it only checks for the first author
    await daily_reminder_check(author=int(json.load(open('settings/config.json', 'r'))['bot']['owners'][0]), client=client)

    await log('Start scanning all reminders.', 'spamInfo', client=client)

    reminders = json.load(open('settings/database.json', 'r'))['reminders']

    current_time = ct.struct_to_ms(ct.get_current_time())

    reminders_to_delete = []

    for i in range(len(reminders)):
        if current_time > reminders[i]['date']:
            await post_reminder(reminders[i], client=client)
            reminders_to_delete.append(reminders[i]['id'])

    await delete_reminders(reminders_to_delete, client=client)

    await log(' Done scanning all reminders.', 'spamInfo', client=client)
    return
Exemplo n.º 2
0
async def daily_reminder_check(*, force=False, author, client):
    """Checks if there's reminders today and automatically posts it on discord.

    Args:
        force (Boolean): If True it forces to do the check. Otherwise it only checks if it hasn't already today or if it's disable in the config.
        author (Number): The author ID of the discord user to check.
        client (discord.py client object): the discord.py client needed to post discord messages
    """
    last_daily_check = ct.ms_to_struct(json.load(open('settings/database.json', 'r'))['general']['lastDailyRemCheck'])

    current_time = ct.get_current_time()

    if (json.load(open('settings/config.json', 'r'))['reminders']['dailyReminderCheck'] != True and force != True):
        await log('Skipped doing daily reminder check as it\'s been disabled in config.', 'spamInfo', client=client)
        return

    if current_time.tm_year == last_daily_check.tm_year and current_time.tm_mon == last_daily_check.tm_mon and current_time.tm_mday == last_daily_check.tm_mday and force != True:
        await log('Skipped doing daily reminder check as it\'s already been done.', 'spamInfo', client=client)
        return

    reminders = json.load(open('settings/database.json', 'r'))['reminders']

    todays_reminders = []

    for i in range(len(reminders)):
        reminder_date = ct.ms_to_struct(reminders[i]['date'])

        if reminders[i]['author'] == author and current_time.tm_year == reminder_date.tm_year and current_time.tm_mon == reminder_date.tm_mon and current_time.tm_mday == reminder_date.tm_mday:
            todays_reminders.append(reminders[i]['id'])

    if len(todays_reminders) == 0:
        await log('Skipped posting daily reminders as there\'s no reminders today.', 'spamInfo', client=client)
        return

    try:
        await log('Posting daily reminder check...', 'spamInfo', client=client)
        channel = client.get_channel(json.load(open('settings/config.json', 'r'))['reminders']['dailyReminderCheckChannel'])
        await channel.send(f"There are {len(todays_reminders)} reminder(s) today: `{'`, `'.join(todays_reminders)}`")
    except AttributeError:
        await log('Could not post daily reminder check, likely due to wrong channel ID inside config > reminders > dailyReminderCheckChannel.', 'warn', client=client)


    # update 'lastDailtRemCheck' in database
    with open('settings/database.json', 'r+') as f:
        database = json.load(f)
        database['general']['lastDailyRemCheck'] = ct.struct_to_ms(current_time)

        # reset file position to the beginning - stackoverflow copy, dont ask
        f.seek(0)
        json.dump(database, f, indent=4)
        f.truncate()
    return
Exemplo n.º 3
0
def already_checked(last_checked, interval,
                    current_time=ct.get_current_time()):
    """Checks if feed has already scanned in the last interval.

    Args:
        last_checked (number): the time it has last been checked in ms since epoch
        interval (number): the interval in seconds
        current_time (time.time_struct object): overwrite the current time
    """
    # multiply interval by 1000 to convert from seconds to ms
    # subtract 5s from interval to make sure that a scheduled scan doesn't hit it
    last_checked = last_checked + (interval * 1000 - 5000)
    last_checked = ct.ms_to_struct(last_checked)

    return last_checked > current_time
Exemplo n.º 4
0
    async def addreminder_command(self, ctx):
        if await misc.is_dm(ctx) == True:
            return

        author_ID = ctx.message.author.id
        user = ctx.message.author.name + '#' + ctx.message.author.discriminator
        prefix = json.load(open('settings/config.json',
                                'r'))['bot']['prefix'][0]
        # get actual message
        args = get_args(ctx)

        reminder_interval = json.load(open('settings/config.json',
                                           'r'))['reminders']['interval']
        current_time = ct.struct_to_datetime(ct.get_current_time())

        timezone = json.load(open('settings/config.json',
                                  'r'))['reminders']['timezone']

        public = None
        public_aliases = ['p', 'pc', 'pub', 'public', 'open', 'privatent']
        private_aliases = ['pv', 'priv', 'private', 'unlisted', 'publicnt']

        #aliases used to specify 'use this channel'
        this_channel_aliases = ['_']

        channel = None
        channel_arg = 0

        rem_type = None

        #everything in this try except is based around figuring out where the date & time string starts and ends as it doesn't follow any easy to follow rules
        #this is mainly because relative time inputs are allowed like "tomorrow" or "in 5 minutes", this is also the whole reason why channel has to be input
        #as the channel plays the biggest part in figuring out where the date & time string ends
        try:
            if args[0] in ['reminder']:
                rem_type = 'reminder'

            #check which arg is the channel
            for i in range(len(args)):
                if args[i].startswith('<#'):
                    if await misc.is_valid_channel(
                            ctx=ctx,
                            channel=self.bot.get_channel(int(args[i][2:-1])),
                            send_message=False) == True:
                        channel = self.bot.get_channel(int(args[i][2:-1]))
                        channel_arg = i
                        break

            #check if user simply wanted to use the current channel by entering _
            if channel == None:
                for i in range(len(args)):
                    if args[i] in this_channel_aliases:
                        channel = ctx.message.channel
                        channel_arg = i
                        break

            if channel == None:
                #throw error so try except catches it and sends the 'invalid arguments' message
                raise IndexError()
                return

            # private/public always has to be specified before channel so that's where were checking if it exists
            if args[channel_arg - 1] in public_aliases:
                public = True
            elif args[channel_arg - 1] in private_aliases:
                public = False

            #get the date string
            #this checks if type or public/private or both got specified in order to figure out where the date string starts and ends
            if rem_type != None:
                if public == None:
                    rem_date_input = ' '.join(args[1:channel_arg])
                else:
                    rem_date_input = ' '.join(args[1:(channel_arg - 1)])
            else:
                if public == None:
                    rem_date_input = ' '.join(args[0:channel_arg])
                else:
                    rem_date_input = ' '.join(args[0:(channel_arg - 1)])

            #print(f"Date string to parse: '{rem_date_input}'")

            #TO_TIMEZONE makes sure it converts the time to UTC which is what the bot uses
            if timezone == '':
                rem_date = parse(rem_date_input,
                                 settings={
                                     'TO_TIMEZONE': 'etc/UTC',
                                     'RETURN_AS_TIMEZONE_AWARE': False
                                 })
            else:
                rem_date = parse(rem_date_input,
                                 settings={
                                     'TO_TIMEZONE': 'etc/UTC',
                                     'RETURN_AS_TIMEZONE_AWARE': False,
                                     'TIMEZONE': timezone
                                 })

            print(rem_date)

            if rem_date == None:
                await ctx.send(
                    content=
                    f"<@!{author_ID}> Could not parse date `{rem_date_input}`. This might also be caused by using the command wrong (or the bot being broken), try using `{prefix}add {self.addreminder_command.usage}`"
                )
                await log(
                    f"Failed to create a reminder as the date '{rem_date_input}' could not be parsed (timezone: '{timezone}').",
                    'spamInfo',
                    client=self.bot)
                return

            if rem_date <= (current_time + dt.timedelta(minutes=1)):
                await ctx.send(
                    content=
                    f"<@!{author_ID}> Reminder has to be more than 1 minute in the future."
                )
                await log(
                    f"Failed to create a reminder on '{rem_date.strftime('%Y-%m-%d %H:%M')}' (current time: '{current_time.strftime('%Y-%m-%d %H:%M')}' | parsed string: '{rem_date_input}').",
                    'spamInfo',
                    client=self.bot)
                return

            #use defaults if not specified by the user
            if rem_type == None:
                rem_type = 'reminder'

            if public == None:
                public = False

            message = ' '.join(args[channel_arg + 1:])

        except IndexError:
            await ctx.send(
                content=
                f"<@!{author_ID}> Invalid arguments. Use `{prefix}add {self.addreminder_command.usage}`"
            )
            return

        # create actual reminder and get the ID of the created reminder
        reminder_id = mrem.add_reminder(rem_type='reminder',
                                        author=author_ID,
                                        dt=rem_date,
                                        channel=channel.id,
                                        message=message,
                                        public=public)

        if public != True:
            await ctx.message.delete()

        await log(
            f"Created a reminder on '{rem_date.strftime('%Y-%m-%d %H:%M')}' (string parsed: '{rem_date_input}' | timezone: '{timezone}') with the ID '{reminder_id} by user {user}",
            'spamInfo',
            client=self.bot)

        if type(reminder_id) == str:
            await ctx.send(
                content=
                f"<@!{author_ID}> A reminder has been created with the ID `{reminder_id}` and will be posted on `{rem_date.strftime('%Y-%m-%d %H:%M (%I:%M%p) UTC')}`."
            )
            return

        await ctx.send(
            content=
            f"<@!{author_ID}> Something has gone wrong while creating a reminder. The reminder might've still been created though, check `[insert command here]` to see if it exists."
        )
        return
Exemplo n.º 5
0
async def scan_all_feeds(*, client):
    """Scans all RSS feeds

    Args:
        client: the discord.py client object required for logging and posting new items
    """

    with open('settings/database.json', 'r+') as f:
        database = json.load(f)
        feeds = database['feeds']
        config = json.load(open('settings/config.json', 'r'))

        combine_posts = config['rss']['combinePosts']
        oldest_posts_first = config['rss']['oldestFirst']
        interval = config['rss']['interval'] * 60

        if interval < 600:
            interval = 600

        # convert both to seconds
        scan_delay = config['rss']['scanDelay'] / 1000
        post_delay = config['rss']['postDelay'] / 1000

        if scan_delay < 0:
            scan_delay = 0

        if post_delay < 0:
            post_delay = 0

        current_time = ct.get_current_time()

        last_checked = database['general']['lastChecked']

        # check if already scanned during the last interval
        if already_checked(last_checked, interval, current_time):
            await log(
                f" Skip scanning all feeds ({int(interval / 60)}min interval)..",
                'info',
                client=client)
            return

        await log('Start scanning all feeds.', 'info', client=client)

        # go through all feeds that have to be scanned
        # use range() because the original feeds variable has to be edited
        for i in range(len(feeds)):
            time.sleep(scan_delay)

            last_checked = feeds[i]['lastChecked']

            # check if already scanned during the last interval
            if already_checked(last_checked, interval, current_time):
                await log(
                    f" Skip scanning {feeds[i]['name']} ({int(interval / 60)}min interval).",
                    'spamInfo',
                    client=client)
                continue

            await log(f"Start scanning {feeds[i]['name']}.",
                      'spamInfo',
                      client=client)

            try:
                feed = scan_feed(feeds[i]['url'])
                results = get_new_items_from_feed(
                    feed=feed,
                    url=feeds[i]['url'],
                    last_checked=feeds[i]['lastChecked'])
            except:
                await log(f"Failed to scan {feeds[i]['url']}.",
                          'warn',
                          client=client)
                continue

            # if feed is unavailable, mark it as such with the current time
            if results == False:
                await log(
                    f"{feeds[i]['name']} is not available (url: '{feeds[i]['url']}'.",
                    'spamInfo',
                    client=client)
                # only mark it with the current time if it wasn't already marked as unavailable before
                if feeds[i]['unavailable'] == False:
                    feeds[i]['unavailable'] = ct.struct_to_ms(current_time)

                    for c in feeds[i]['channels']:
                        time.sleep(post_delay)

                        channel = client.get_channel(c['channelID'])
                        try:
                            await channel.send(
                                f"**{c['feedName']}** is not available (this message won't be posted again until it's available again). Feed URL: {feeds[i]['url']}"
                            )
                        except:
                            await log(
                                f"Could not send {feeds[i]['name']} as the channel is likely deleted.",
                                'warn',
                                client=client)

                continue

            # mark feeds as available
            feeds[i]['unavailable'] = False

            # check if posts should be combined
            if len(results) > combine_posts and combine_posts != 0:
                if oldest_posts_first == True:
                    first_item = results[len(results) - 1]
                else:
                    first_item = results[0]

                for c in feeds[i]['channels']:
                    time.sleep(post_delay)

                    embed = get_embed_from_item(item=first_item,
                                                db_item=feeds[i],
                                                feed=feed,
                                                channel_item=c,
                                                client=client)

                    channel = client.get_channel(c['channelID'])
                    try:
                        await channel.send(embed=embed)
                        await channel.send(
                            f"...and {len(results) - combine_posts} more from {c['feedName']}."
                        )
                    except:
                        await log(
                            f"Could not send {feeds[i]['name']} as the channel is likely deleted.",
                            'warn',
                            client=client)

            # post all items
            else:

                # reverse the results list so the oldest items come first
                if oldest_posts_first == True:
                    results = results[::-1]

                for c in feeds[i]['channels']:
                    time.sleep(post_delay)

                    channel = client.get_channel(c['channelID'])
                    for r in results:
                        embed = get_embed_from_item(item=r,
                                                    db_item=feeds[i],
                                                    feed=feed,
                                                    channel_item=c,
                                                    client=client)
                        try:
                            await channel.send(embed=embed)
                        except:
                            await log(
                                f"Could not send {feeds[i]['name']} as the channel is likely deleted.",
                                'warn',
                                client=client)

            feeds[i]['lastChecked'] = ct.struct_to_ms(ct.get_current_time())
            await log(f" Done scanning {feeds[i]['name']}.",
                      'spamInfo',
                      client=client)

        database['feeds'] = feeds
        database['general']['lastChecked'] = ct.struct_to_ms(
            ct.get_current_time())
        # reset file position to the beginning - stackoverflow copy, dont ask
        f.seek(0)
        json.dump(database, f, indent=4)
        f.truncate()
        await log('All feeds scanned.', 'info', client=client)
        return