Exemplo n.º 1
0
    async def run_unschedule(self, context):
        """
        Unschedule the event
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())

        # Do they have the permissions to rename an event?
        self.check_permissions(context)

        # Make sure the event is running
        if event is None:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:noexists', user.get_guild()))

        # Unschedule the event
        event.set_startdate(None)
        event.set_enddate(None)
        event.save()

        # Remove any tasks we already had saved for this event.
        Task.cancel('event', event.get_id())

        return await context.send(user.get_mention() + ', ' + lib.get_string(
            'event:unscheduled', user.get_guild()).format(event.get_title()))
Exemplo n.º 2
0
    async def end(self, context=None, bot=None):
        """
        Mark the 'end' time of the sprint as 0 in the database and ask for final word counts
        :return:
        """

        # End the sprint in the database
        self.set_ended()

        if bot is None:
            bot = self.bot

        # Get the sprinting users to notify
        notify = self.get_notifications(self.get_users())

        # Check for a guild setting for the delay time, otherwise use the default
        guild = Guild.get_from_bot(bot, self._guild)
        delay = guild.get_setting('sprint_delay_end')
        if delay is None:
            delay = self.DEFAULT_POST_DELAY

        # Post the ending message
        message = lib.get_string('sprint:end', self._guild).format(delay)
        message = message + ', '.join(notify)
        await self.say(message, context, bot)

        # Convert the minutes to seconds
        delay = int(delay) * 60
        task_time = int(time.time()) + delay

        # Schedule the cron task
        Task.schedule(self.TASKS['complete'], task_time, 'sprint', self._id)
Exemplo n.º 3
0
    async def run_end(self, context):
        """
        Manually force the sprint to end (if the cron hasn't posted the message) and ask for final word counts
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        sprint = Sprint(user.get_guild())

        # If there is no active sprint, then just display an error
        if not sprint.exists():
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:noexists', user.get_guild()))

        # If they do not have permission to cancel this sprint, display an error
        if int(sprint.get_createdby()) != user.get_id() and context.message.author.permissions_in(context.message.channel).manage_messages is not True:
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:cannotend', user.get_guild()))

        # If the sprint hasn't started yet, it can't be ended.
        if not sprint.has_started():
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:notstarted', user.get_guild()))

        # Change the end reference to now, otherwise wpm calculations will be off, as it will use the time in the future when it was supposed to end.
        sprint.update_end_reference(int(time.time()))

        # Since we are forcing the end, we should cancel any pending tasks for this sprint
        Task.cancel('sprint', sprint.get_id())

        # We need to set the bot into the sprint object, as we will need it when trying to get the guild object
        sprint.set_bot(self.bot)
        return await sprint.end(context)
Exemplo n.º 4
0
    async def run_start(self, context, length=None, start=None):
        """
        Try to start a sprint on the server
        :param context
        :param length: Length of time (in minutes) the sprint should last
        :param start: Time in minutes from now, that the sprint should start
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        sprint = Sprint(user.get_guild())

        # Check if sprint is finished but not marked as completed, in which case we can mark it as complete
        if sprint.is_finished():
            # Mark the sprint as complete
            await sprint.complete()
            # Reload the sprint object, as now there shouldn't be a pending one
            sprint = Sprint(user.get_guild())

        # If a sprint is currently running, we cannot start a new one
        if sprint.exists():
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:alreadyexists', user.get_guild()))

        # Must be okay to continue #

        # If the length argument is not valid, use the default
        if length is None or lib.is_number(length) is False or lib.is_number(length) <= 0 or lib.is_number(length) > self.MAX_LENGTH:
            length = self.DEFAULT_LENGTH

        # Same goes for the start argument
        if start is None or lib.is_number(start) is False or lib.is_number(start) < 0 or lib.is_number(start) > self.MAX_DELAY:
            start = self.DEFAULT_DELAY

        # Make sure we are using ints and not floats passed through in the command
        length = int(length)
        start = int(start)

        # Calculate the start and end times based on the current timestamp
        now = int(time.time())
        start_time = now + (start * 60)
        end_time = start_time + (length * 60)

        # Create the sprint
        sprint = Sprint.create(guild=user.get_guild(), channel=context.message.channel.id, start=start_time, end=end_time, end_reference=end_time, length=length, createdby=user.get_id(), created=now)

        # Join the sprint
        sprint.join(user.get_id())

        # Increment the user's stat for sprints created
        user.add_stat('sprints_started', 1)

        # Are we starting immediately or after a delay?
        if start == 0:
            # Immediately. That means we need to schedule the end task.
            Task.schedule(sprint.TASKS['end'], end_time, 'sprint', sprint.get_id())
            return await sprint.post_start(context)
        else:
            # Delay. That means we need to schedule the start task, which will in turn schedule the end task once it's run.
            Task.schedule(sprint.TASKS['start'], start_time, 'sprint', sprint.get_id())
            return await sprint.post_delayed_start(context)
Exemplo n.º 5
0
    async def run_delete(self, context):
        """
        Delete the event on this server
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

        # Do they have the permissions to delete an event?
        self.check_permissions(context)

        # Check if there is an event running
        event = Event.get_by_guild(user.get_guild())
        if event is None or not event.is_valid():
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:noexists', user.get_guild()))

        # Make a fake prompt to wait for confirmation.
        argument = {
            'prompt': lib.get_string('event:deletesure', user.get_guild()),
            'check': lambda resp: resp.lower() in ('y', 'yes', 'n', 'no')
        }

        response = await self.prompt(context, argument, True)
        if not response:
            return

        response = response.content

        # If they confirm, then delete the event.
        if response.lower() in ('y', 'yes'):

            event.delete()

            # Remove any tasks we already had saved for this event.
            Task.cancel('event', event.get_id())

            output = lib.get_string('event:deleted',
                                    user.get_guild()).format(event.get_title())

        else:
            # Otherwise, just print 'OK'
            output = 'OK'

        await context.send(context.message.author.mention + ', ' + output)
Exemplo n.º 6
0
    def cancel(self, context):
        """
        Cancel the sprint and notify the users who were taking part
        :return:
        """

        # Load current user
        user = User(context.message.author.id, context.guild.id, context)

        # Delete sprints and sprint_users records
        self.__db.delete('sprint_users', {'sprint': self._id})
        self.__db.delete('sprints', {'id': self._id})

        # Delete pending scheduled tasks
        Task.cancel('sprint', self._id)

        # If the user created this, decrement their created stat
        if user.get_id() == self._createdby:
            user.add_stat('sprints_started', -1)
Exemplo n.º 7
0
    async def task_start(self, bot) -> bool:
        """
        Scheduled task to start the sprint
        :param task:
        :return: bool
        """
        # Run pre-checks
        if not self._task_prechecks(bot):
            return True

        now = int(time.time())

        # If the sprint has already finished, we don't need to do anything so we can return True and just have the task deleted.
        if self.is_finished() or self.is_complete():
            return True

        # Post the starting message.
        await self.post_start(bot=bot)

        # Schedule the end task.
        Task.schedule(self.TASKS['end'], self._end, 'sprint', self._id)
        return True
Exemplo n.º 8
0
    async def run_end(self, context):
        """
        End the event now
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

        # Do they have the permissions to end an event?
        self.check_permissions(context)

        event = Event.get_by_guild(user.get_guild())
        if event is None or not event.is_running():
            return await context.send(user.get_mention() + ', ' + lib.get_string('event:err:noexists', user.get_guild()))

        event.set_context(context)
        event.set_guild_object(context.guild)

        # Remove any tasks we already had saved for this event.
        Task.cancel('event', event.get_id())

        return await event.end()
Exemplo n.º 9
0
    def get_task(link: str, force_update=False) -> Task:
        """
        Get all information about a task from it's page.
        :param force_update: If it is False, the task will be downloaded from the site only if it doesn't exists in
        data.tasks. If it is True, the task will be downloaded and updated anyway.
        :param link:
        :return:
        """
        task = Task(task_id=INFOARENA_PREFIX + '-' + link.rsplit('/', 1)[-1],
                    link_name=link.rsplit('/', 1)[-1],
                    task_link=link)

        if not force_update:
            if task.task_id in data.tasks:
                return data.tasks.get(task.task_id)
        print("Update task: ", task.task_id)

        task_page = requests.get(link)
        parsed_task_page = BeautifulSoup(task_page.text, 'html.parser')

        parsed_task_page = parsed_task_page.find_all("div",
                                                     attrs={'id': 'main'})[0]
        parsed_task_page = parsed_task_page.find_all(
            "div", attrs={'class': 'wiki_text_block'})[0]

        info_table = parsed_task_page.find_all("table")[0]
        InfoarenaTasksCrawler._extract_info_from_table_task_page(
            task, info_table)

        info_table.decompose()
        task_name = parsed_task_page.find_all("h1")[0].text
        task.set_name(task_name)
        parsed_task_page.find_all("h1")[0].decompose()
        parsed_task_page.find_all("p")[0].decompose()
        parsed_task_page.find_all("p")[0].decompose()

        examples_table = parsed_task_page.find_all("table",
                                                   attrs={'class':
                                                          'example'})[-1]
        InfoarenaTasksCrawler._extract_examples_from_table(
            task, examples_table)

        for element in examples_table.fetchNextSiblings():
            element.decompose()
        examples_table.decompose()

        task.set_statement(parsed_task_page.text)

        data.tasks[task.task_id] = task
        return task
Exemplo n.º 10
0
    async def run_declare(self, context, amount=None):
        """
        Declare user's current word count for the sprint
        :param context:
        :param amount:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        sprint = Sprint(user.get_guild())

        # If there is no active sprint, then just display an error
        if not sprint.exists():
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:noexists', user.get_guild()))

        # If the user is not sprinting, then again, just display that error
        if not sprint.is_user_sprinting(user.get_id()):
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:notjoined', user.get_guild()))

        # If the sprint hasn't started yet, display error
        if not sprint.has_started():
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:notstarted', user.get_guild()))

        # Get the user's sprint info
        user_sprint = sprint.get_user_sprint(user.get_id())

        # If they joined without a word count, they can't add one.
        if user_sprint['sprint_type'] == Sprint.SPRINT_TYPE_NO_WORDCOUNT:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:nonwordcount', user.get_guild()))

        # Did they enter something for the amount?
        if amount is None:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:amount', user.get_guild()))

        # Are they trying to do a calculation instead of declaring a number?
        if amount[0] == '+' or amount[0] == '-':

            # Set the calculation variable to True so we know later on that it was not a proper declaration
            calculation = True

            # Convert the amount string to an int
            amount = int(amount)

            # Add that to the current word count, to get the new value
            new_amount = user_sprint['current_wc'] + amount

        else:
            calculation = False
            new_amount = amount

        # Make sure the amount is now a valid number
        if not lib.is_number(new_amount):
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:amount', user.get_guild()))

        # Just make sure the new_amount is defintely an int
        new_amount = int(new_amount)

        # If the declared value is less than they started with and it is not a calculation, then that is an error.
        if new_amount < int(user_sprint['starting_wc']) and not calculation:
            diff = user_sprint['current_wc'] - new_amount
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('sprint:err:wclessthanstart', user.get_guild(
                )).format(new_amount, user_sprint['starting_wc'], diff))

        # Is the sprint finished? If so this will be an ending_wc declaration, not a current_wc one.
        col = 'ending' if sprint.is_finished() else 'current'

        # Before we actually update it, if the WPM is huge and most likely an error, just check with them if they meant to put that many words.
        written = new_amount - int(user_sprint['starting_wc'])
        seconds = int(sprint.get_end_reference()) - user_sprint['timejoined']
        wpm = Sprint.calculate_wpm(written, seconds)

        # Does the user have a configured setting for max wpm to check?
        max_wpm = user.get_setting('maxwpm')
        if not max_wpm:
            max_wpm = self.WPM_CHECK

        if wpm > int(max_wpm):

            # Make a fake prompt to wait for confirmation.
            argument = {
                'prompt':
                lib.get_string('sprint:wpm:sure',
                               user.get_guild()).format(written, wpm),
                'check':
                lambda resp: resp.lower() in ('y', 'yes', 'n', 'no')
            }

            response = await self.adhoc_prompt(context, argument, True)

            # If they confirm, then delete the event.
            if response is False or response.content.lower() in ('n', 'no'):
                return await context.send(
                    user.get_mention() + ', ' +
                    lib.get_string('sprint:declareagain', user.get_guild()))

        # Update the user's sprint record
        arg = {col: new_amount}
        sprint.update_user(user.get_id(), **arg)

        # Reload the user sprint info
        user_sprint = sprint.get_user_sprint(user.get_id())

        # Which value are we displaying?
        wordcount = user_sprint['ending_wc'] if sprint.is_finished(
        ) else user_sprint['current_wc']
        written = int(wordcount) - int(user_sprint['starting_wc'])

        await context.send(user.get_mention() + ', ' + lib.get_string(
            'sprint:declared', user.get_guild()).format(wordcount, written))

        # Is the sprint now over and has everyone declared?
        if sprint.is_finished() and sprint.is_declaration_finished():
            Task.cancel('sprint', sprint.get_id())
            await sprint.complete(context)
Exemplo n.º 11
0
    async def run_schedule(self, context):
        """
        Schedule the event to start and end at a specific datetime
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())

        # Do they have the permissions to rename an event?
        self.check_permissions(context)

        # Make sure the event is running
        if event is None:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:noexists', user.get_guild()))

        # Make sure the event is running
        if event.is_running():
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:alreadyrunning', user.get_guild()))

        # Do they have a timezone set in their user settings?
        user_timezone = user.get_setting('timezone')
        if user_timezone is None:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:timezonenotset', user.get_guild()))

        timezone = pytz.timezone(user_timezone)
        time = datetime.now(timezone).strftime('%H:%M:%S')
        offset = datetime.now(timezone).strftime('%z')

        # Print the pre-schedule information to check their timezone is correct.
        await context.send(user.get_mention() + ', ' +
                           lib.get_string('event:preschedule', user.get_guild(
                           )).format(user_timezone, time, offset))

        # We now have various stages to go through, so we loop through the stages, ask the question and store the user input as the answer.
        answers = []

        # Stage 1 - Start date
        answer = await self.run_stage_one(context)
        if not answer:
            return
        answers.append({'stage': 1, 'answer': answer})

        # Stage 2 - Start time
        answer = await self.run_stage_two(context)
        if not answer:
            return
        answers.append({'stage': 2, 'answer': answer})

        # Stage 3 - End date
        answer = await self.run_stage_three(context)
        if not answer:
            return
        answers.append({'stage': 3, 'answer': answer})

        # Stage 4 - End time
        answer = await self.run_stage_four(context)
        if not answer:
            return
        answers.append({'stage': 4, 'answer': answer})

        # Stage 5 - Confirmation
        answer = await self.run_stage_five(context, answers)
        if not answer:
            return
        answers.append({'stage': 5, 'answer': answer})

        # Now run the checks to make sure the end date is later than start date, etc...
        check = await self.check_answers(context, answers)
        if not check:
            return

        # Now convert those start and end times to UTC timestamps
        start = datetime.strptime(
            lib.find(answers, 'stage', 1)['answer'] + ' ' +
            lib.find(answers, 'stage', 2)['answer'], '%d-%m-%Y %H:%M')
        end = datetime.strptime(
            lib.find(answers, 'stage', 3)['answer'] + ' ' +
            lib.find(answers, 'stage', 4)['answer'], '%d-%m-%Y %H:%M')

        adjusted_start = int(timezone.localize(start).timestamp())
        adjusted_end = int(timezone.localize(end).timestamp())

        # Schedule the event with those timestamps
        event.set_startdate(adjusted_start)
        event.set_enddate(adjusted_end)
        event.set_channel(context.message.channel.id)
        event.save()

        # Remove any tasks we already had saved for this event.
        Task.cancel('event', event.get_id())

        # Schedule the tasks to run at those times.
        Task.schedule(Event.TASKS['start'], event.get_start_time(), 'event',
                      event.get_id())
        Task.schedule(Event.TASKS['end'], event.get_end_time(), 'event',
                      event.get_id())

        return await context.send(
            user.get_mention() + ', ' +
            lib.get_string('event:scheduled', user.get_guild()).format(
                event.get_title(), start, end))
Exemplo n.º 12
0
    async def run_declare(self, context, amount=None):
        """
        Declare user's current word count for the sprint
        :param context:
        :param amount:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        sprint = Sprint(user.get_guild())

        # If there is no active sprint, then just display an error
        if not sprint.exists():
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:noexists', user.get_guild()))

        # If the user is not sprinting, then again, just display that error
        if not sprint.is_user_sprinting(user.get_id()):
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:notjoined', user.get_guild()))

        # If the sprint hasn't started yet, display error
        if not sprint.has_started():
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:notstarted', user.get_guild()))

        # Did they enter something for the amount?
        if amount is None:
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:amount', user.get_guild()))

        # Get the user's sprint info
        user_sprint = sprint.get_user_sprint(user.get_id())

        # Are they trying to do a calculation instead of declaring a number?
        if amount[0] == '+' or amount[0] == '-':

            # Set the calculation variable to True so we know later on that it was not a proper declaration
            calculation = True

            # Convert the amount string to an int
            amount = int(amount)

            # Add that to the current word count, to get the new value
            new_amount = user_sprint['current_wc'] + amount

        else:
            calculation = False
            new_amount = amount

        # Make sure the amount is now a valid number
        if not lib.is_number(new_amount):
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:amount', user.get_guild()))

        # Just make sure the new_amount is defintely an int
        new_amount = int(new_amount)

        # If the declared value is less than they started with and it is not a calculation, then that is an error.
        if new_amount < int(user_sprint['starting_wc']) and not calculation:
            diff = user_sprint['current_wc'] - new_amount
            return await context.send(user.get_mention() + ', ' + lib.get_string('sprint:err:wclessthanstart', user.get_guild()).format(new_amount, user_sprint['starting_wc'], diff))

        # Is the sprint finished? If so this will be an ending_wc declaration, not a current_wc one.
        col = 'ending' if sprint.is_finished() else 'current'
        arg = {col: new_amount}

        # Update the user's sprint record
        sprint.update_user(user.get_id(), **arg)

        # Reload the user sprint info
        user_sprint = sprint.get_user_sprint(user.get_id())

        # Which value are we displaying?
        wordcount = user_sprint['ending_wc'] if sprint.is_finished() else user_sprint['current_wc']
        written = int(wordcount) - int(user_sprint['starting_wc'])

        await context.send(user.get_mention() + ', ' + lib.get_string('sprint:declared', user.get_guild()).format(wordcount, written))

        # Is the sprint now over and has everyone declared?
        if sprint.is_finished() and sprint.is_declaration_finished():
            Task.cancel('sprint', sprint.get_id())
            await sprint.complete(context)