Beispiel #1
0
    async def run_create(self, context, opts):
        """
        Create an event on the server
        :param context:
        :param opts:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

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

        # Get the title out of the arguments
        title = " ".join(opts[0:])

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

        # Make sure they specified a title.
        if len(title) == 0 or len(title) > 255:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:title', user.get_guild()))

        # Create the event
        Event.create(guild=user.get_guild(),
                     channel=context.message.channel.id,
                     title=title)
        return await context.send(
            user.get_mention() + ', ' +
            lib.get_string('event:created', user.get_guild()).format(title))
Beispiel #2
0
    async def run_top(self, context):
        """
        Get the leaderboard of words written for this event
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

        # First try and get the event as if its running
        event = Event.get_by_guild(user.get_guild())

        # If that fails, get the last run one
        if event is None:
            event = Event.get_by_guild(user.get_guild(), include_ended=True)

        # If there is still not one, then just stop
        if event is None:
            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)

        return await context.send(
            embed=await event.get_leaderboard(Event.LEADERBOARD_LIMIT))
Beispiel #3
0
    async def run_rename(self, context, opts):
        """
        Rename the event
        :param context:
        :param opts:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

        # Do they have the permissions to rename 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()))

        # Get the title out of the arguments
        title = " ".join(opts[0:])

        # Make sure they specified a title.
        if len(title) == 0 or len(title) > 255:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('event:err:rename:title', user.get_guild()))

        # Create the event
        event.set_title(title)
        event.save()
        return await context.send(
            user.get_mention() + ', ' +
            lib.get_string('event:renamed', user.get_guild()).format(title))
Beispiel #4
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()))
Beispiel #5
0
    async def run_update(self, context, amount):
        """
        Update the user's word count on the event
        :param context:
        :param amount:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())

        amount = lib.is_number(amount[0])
        if amount is False or amount < 0:
            return await context.send(
                user.get_mention() + ', ' +
                lib.get_string('err:validamount', user.get_guild()))

        # Make sure the event is running
        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.update_wordcount(user.get_id(), amount)
        return await context.send(
            user.get_mention() + ', ' +
            lib.get_string('event:updated', user.get_guild()).format(
                event.get_title(), amount))
    async def run_set(self, context, type, opts):
        """
        Set the value of something for the event, such as description or image url
        :param context:
        :param type:
        :param opts:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

        # Do they have the permissions to set an event option?
        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()))

        value = " ".join(opts[0:])

        if type == 'description':
            event.set_description(value)
        elif type == 'image':
            if len(value) > 255:
                return await context.send(user.get_mention() + ', ' + lib.get_string('event:err:img', user.get_guild()))
            event.set_image(value)
        elif type == 'colour':
            event.set_colour(value)

        # Save the changes
        event.save()

        return await context.send(user.get_mention() + ', ' + lib.get_string('event:set', user.get_guild()).format(type, value))
    async def run_time(self, context):
        """
        Check how much time is left in the event
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())
        now = int(time.time())

        # Make sure the event exists
        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()))

        # Is the event scheduled to start, but has not yet started?
        if not event.is_running() and event.is_scheduled():
            left = lib.secs_to_days(event.get_start_time() - now)
            return await context.send(user.get_mention() + ', ' + lib.get_string('event:timetostart', user.get_guild()).format(left))

        # If the event is not running and it is NOT scheduled, then we don't know what time it will start.
        elif not event.is_running():
            return await context.send(user.get_mention() + ', ' + lib.get_string('event:err:noexists', user.get_guild()))

        # At this point, the event must be running. If it is scheduled, then we can get the time left from the end time. Otherwise, we don't know.
        elif event.is_scheduled():
            left = lib.secs_to_days(event.get_end_time() - now)
            return await context.send(user.get_mention() + ', ' + lib.get_string('event:timeleft', user.get_guild()).format(left))

        else:
            return await context.send(user.get_mention() + ', ' + lib.get_string('event:noendtime', user.get_guild()))
    async def run_me(self, context):
        """
        Check your own word count for the event so far
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())

        # Make sure the event is running
        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()))

        words = event.get_wordcount(user.get_id())
        return await context.send(user.get_mention() + ', ' + lib.get_string('event:wordcount', user.get_guild()).format(event.get_title(), words))
Beispiel #9
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)
    async def run_start(self, context):
        """
        Start the event now
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)

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

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

        event.set_context(context)
        return await event.start()
    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()
    async def complete(self, context=None, bot=None):
        """
        Finish the sprint, calculate all the WPM and XP and display results
        :return:
        """

        # Print the 'Results coming up shortly' message
        await self.say(lib.get_string('sprint:resultscomingsoon', self._guild),
                       context, bot)

        # Create array to use for storing the results
        results = []

        # If the sprint has already completed, stop.
        if self._completed != 0:
            return

        # Mark this sprint as complete so the cron doesn't pick it up and start processing it again
        self.set_complete()

        # Get all the users taking part
        users = self.get_users()

        # Loop through them and get their full sprint info
        for user_id in users:

            user = User(user_id,
                        self._guild,
                        context=context,
                        bot=bot,
                        channel=self.get_channel())
            user_sprint = self.get_user_sprint(user_id)

            # If it's a non-word count sprint, we don't need to do anything with word counts.
            if user_sprint['sprint_type'] == Sprint.SPRINT_TYPE_NO_WORDCOUNT:

                # Just give them the completed sprint stat and XP.
                await user.add_xp(Experience.XP_COMPLETE_SPRINT)
                user.add_stat('sprints_completed', 1)

                # Push user to results
                results.append({
                    'user': user,
                    'wordcount': 0,
                    'xp': Experience.XP_COMPLETE_SPRINT,
                    'type': user_sprint['sprint_type']
                })

            else:

                # If they didn't submit an ending word count, use their current one
                if user_sprint['ending_wc'] == 0:
                    user_sprint['ending_wc'] = user_sprint['current_wc']

                # Now we only process their result if they have declared something and it's different to their starting word count
                user_sprint['starting_wc'] = int(user_sprint['starting_wc'])
                user_sprint['current_wc'] = int(user_sprint['current_wc'])
                user_sprint['ending_wc'] = int(user_sprint['ending_wc'])
                user_sprint['timejoined'] = int(user_sprint['timejoined'])

                if user_sprint['ending_wc'] > 0 and user_sprint[
                        'ending_wc'] != user_sprint['starting_wc']:

                    wordcount = user_sprint['ending_wc'] - user_sprint[
                        'starting_wc']
                    time_sprinted = self._end_reference - user_sprint[
                        'timejoined']

                    # If for some reason the timejoined or sprint.end_reference are 0, then use the defined sprint length instead
                    if user_sprint[
                            'timejoined'] <= 0 or self._end_reference == 0:
                        time_sprinted = self._length

                    # Calculate the WPM from their time sprinted
                    wpm = Sprint.calculate_wpm(wordcount, time_sprinted)

                    # See if it's a new record for the user
                    user_record = user.get_record('wpm')
                    wpm_record = True if user_record is None or wpm > int(
                        user_record) else False

                    # If it is a record, update their record in the database
                    if wpm_record:
                        user.update_record('wpm', wpm)

                    # Give them XP for finishing the sprint
                    await user.add_xp(Experience.XP_COMPLETE_SPRINT)

                    # Increment their stats
                    user.add_stat('sprints_completed', 1)
                    user.add_stat('sprints_words_written', wordcount)
                    user.add_stat('total_words_written', wordcount)

                    # Increment their words towards their goal
                    await user.add_to_goals(wordcount)

                    # If they were writing in a Project, update its word count.
                    if user_sprint['project'] is not None:
                        project = Project(user_sprint['project'])
                        project.add_words(wordcount)

                    # is there an event running on this server?
                    event = Event.get_by_guild(self._guild)
                    if event and event.is_running():
                        event.add_words(user.get_id(), wordcount)

                    # Push user to results
                    results.append({
                        'user': user,
                        'wordcount': wordcount,
                        'wpm': wpm,
                        'wpm_record': wpm_record,
                        'xp': Experience.XP_COMPLETE_SPRINT,
                        'type': user_sprint['sprint_type']
                    })

        # Sort the results
        results = sorted(results, key=itemgetter('wordcount'), reverse=True)

        # Now loop through them again and apply extra XP, depending on their position in the results
        position = 1
        highest_word_count = 0

        for result in results:

            if result['wordcount'] > highest_word_count:
                highest_word_count = result['wordcount']
            # If the user finished in the top 5 and they weren't the only one sprinting, earn extra XP
            is_sprint_winner = result['wordcount'] == highest_word_count
            if position <= 5 and len(results) > 1:

                extra_xp = math.ceil(
                    Experience.XP_WIN_SPRINT /
                    (self.WINNING_POSITION if is_sprint_winner else position))
                result['xp'] += extra_xp
                await result['user'].add_xp(extra_xp)

            # If they actually won the sprint, increase their stat by 1
            # Since the results are in order, the highest word count will be set first
            # which means that any subsequent users with the same word count have tied for 1st place
            if position == 1 or result['wordcount'] == highest_word_count:
                result['user'].add_stat('sprints_won', 1)

            position += 1

        # Post the final message with the results
        if len(results) > 0:

            position = 1
            message = lib.get_string('sprint:results:header', self._guild)
            for result in results:

                if result['type'] == Sprint.SPRINT_TYPE_NO_WORDCOUNT:
                    message = message + lib.get_string(
                        'sprint:results:row:nowc', self._guild).format(
                            result['user'].get_mention(), result['xp'])
                else:

                    message = message + lib.get_string(
                        'sprint:results:row', self._guild).format(
                            position, result['user'].get_mention(),
                            result['wordcount'], result['wpm'], result['xp'])

                    # If it's a new PB, append that string as well
                    if result['wpm_record'] is True:
                        message = message + lib.get_string(
                            'sprint:results:pb', self._guild)

                message = message + '\n'
                position += 1

        else:
            message = lib.get_string('sprint:nowordcounts', self._guild)

        # Send the message, either via the context or directly to the channel
        await self.say(message, context, bot)
Beispiel #13
0
    async def run(self, bot):
        """
        Run this task
        TODO: In the future, make this better. For now it will do.
        :return: bool
        """

        # If the task is already processing, don't go any further.
        if self.is_processing():
            return True

        # Mark the task as processing so other shards don't pick it up.
        self.start_processing(1)

        # Build a variable to store the method name to run
        method = 'task_' + str(self.type)

        # Start off with a False and see if we can successfully run and turn that to True
        result = False

        # Sprint tasks.
        if self.object == 'sprint':

            from structures.sprint import Sprint

            sprint = Sprint.get(self.object_id)
            if sprint.is_valid():
                result = await getattr(sprint, method)(bot)
            else:
                # If the sprint doesn't exist, then we can just delete this task.
                result = True

        elif self.object == 'goal':

            from structures.goal import Goal

            goal = Goal()
            result = await getattr(goal, method)(bot)

        elif self.object == 'event':

            from structures.event import Event

            event = Event(self.object_id)
            if event.is_valid():
                result = await getattr(event, method)(bot)
            else:
                # If the event doesn't exist, then we can just delete this task.
                return True

        else:
            # Invalid task object. May as well just delete this task.
            lib.out('Invalid task object: ' + self.object)
            result = True

        # If we finished the task, and it's not a recurring one, delete it.
        if result is True and not self.is_recurring():
            self.delete()
        else:
            self.start_processing(0)

        # If it's a recurring task, set its next run time.
        if self.is_recurring():
            self.set_recur()

        return result
    async def wrote(self, context, amount=None, shortname=None):
        """
        Adds to your total words written statistic.

        Examples:
            !wrote 250 - Adds 250 words to your total words written
            !wrote 200 sword - Adds 200 words to your Project with the shortname "sword". (See: Projects for more info).
        """

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

        # Check the arguments are valid
        args = await self.check_arguments(context,
                                          amount=amount,
                                          shortname=shortname)
        if not args:
            return

        amount = args['amount']
        shortname = args['shortname']
        message = None

        # If they were writing in a Project, update its word count.
        if shortname is not None:

            project = user.get_project(shortname.lower())

            # Make sure the project exists.
            if not project:
                return await context.send(
                    user.get_mention() +
                    ', ' + lib.get_string('project:err:noexists',
                                          user.get_guild()).format(shortname))

            project.add_words(amount)

            written_stat = user.get_stat('total_words_written')
            if written_stat is None:
                written_stat = 0
            total = int(written_stat) + int(amount)

            message = lib.get_string('wrote:addedtoproject',
                                     user.get_guild()).format(
                                         str(amount), project.get_title(),
                                         project.get_words(), total)

        # # Is there an Event running?
        event = Event.get_by_guild(user.get_guild())
        if event and event.is_running():
            event.add_words(user.get_id(), amount)

        # Increment their words written statistic
        user.add_stat('total_words_written', amount)

        # Update their words towards their daily goal
        await user.add_to_goal('daily', amount)

        # Output message
        if message is None:
            total = user.get_stat('total_words_written')
            message = lib.get_string('wrote:added', user.get_guild()).format(
                str(amount), str(total))

        await context.send(user.get_mention() + ', ' + message)
Beispiel #15
0
    async def run_info(self, context):
        """
        Get the event information embedded message
        :param context:
        :return:
        """
        user = User(context.message.author.id, context.guild.id, context)
        event = Event.get_by_guild(user.get_guild())
        config = lib.get('./settings.json')

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

        # Work out which timezone to use when displaying the start and end dates.
        start_date = lib.get_string('na', user.get_guild())
        end_date = lib.get_string('na', user.get_guild())
        user_timezone = user.get_setting('timezone')
        if not user_timezone:
            user_timezone = 'UTC'

        timezone = pytz.timezone(user_timezone)

        # Is it scheduled with start and end dates?
        if event.is_scheduled():
            start = datetime.fromtimestamp(event.get_start_time())
            end = datetime.fromtimestamp(event.get_end_time())
            start_date = start.astimezone(timezone).strftime(
                '%d-%m-%Y %H:%M:%S') + ' (' + user_timezone + ')'
            end_date = end.astimezone(timezone).strftime(
                '%d-%m-%Y %H:%M:%S') + ' (' + user_timezone + ')'

        # Get the running status
        if event.is_running():
            status = lib.get_string('event:started', user.get_guild())
        else:
            status = lib.get_string('event:notyetstarted', user.get_guild())

        # Get the number of users in the event and how many words they have written in it so far
        writers = len(event.get_users())
        words = event.get_total_wordcount()

        # Get the description of the event and add to the end of the status, or just display the status if the description is empty
        description = event.get_description()
        if description and len(description) > 0:
            description = status + '\n\n' + description
        else:
            description = status

        # Get the thumbnail image to use
        image = event.get_image()
        if not image or len(image) == 0:
            image = config.avatar

        # Build the embedded message.
        embed = discord.Embed(title=event.get_title(),
                              color=event.get_colour(),
                              description=description)
        embed.set_thumbnail(url=image)
        embed.add_field(name=lib.get_string('event:startdate',
                                            user.get_guild()),
                        value=start_date,
                        inline=False)
        embed.add_field(name=lib.get_string('event:enddate', user.get_guild()),
                        value=end_date,
                        inline=False)
        embed.add_field(name=lib.get_string('event:numwriters',
                                            user.get_guild()),
                        value=str(writers),
                        inline=True)
        embed.add_field(name=lib.get_string('event:numwords',
                                            user.get_guild()),
                        value=str(words),
                        inline=True)

        # Send the message
        return await context.send(embed=embed)
Beispiel #16
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))