async def run_leave(self, context): """ Leave the sprint :param context: :return: """ user = User(context.message.author.id, context.guild.id, context) sprint = Sprint(user.get_guild()) # If there is no active sprint or the user is not joined to it, display an error if not sprint.exists() or not sprint.is_user_sprinting(user.get_id()): return await context.send( user.get_mention() + ', ' + lib.get_string('sprint:err:notjoined', user.get_guild())) # Remove the user from the sprint sprint.leave(user.get_id()) await context.send(user.get_mention() + ', ' + lib.get_string('sprint:leave', user.get_guild())) # If there are now no users left, cancel the whole sprint if len(sprint.get_users()) == 0: # Cancel the sprint sprint.cancel(context) # Decrement sprints_started stat for whoever started this one creator = User(sprint.get_createdby(), sprint.get_guild()) creator.add_stat('sprints_started', -1) # Display a message letting users know return await context.send( lib.get_string('sprint:leave:cancelled', user.get_guild()))
async def run_complete(self, context): user = User(context.message.author.id, context.guild.id, context) # Do they have an active challenge to mark as complete? challenge = user.get_challenge() if challenge: # Update the challenge with the time completed user.complete_challenge(challenge['id']) # Add the XP await user.add_xp(challenge['xp']) # Increment the challenges_completed stat user.add_stat('challenges_completed', 1) output = lib.get_string('challenge:completed', user.get_guild( )) + ' **' + challenge['challenge'] + '** +' + str( challenge['xp']) + 'xp' else: output = lib.get_string('challenge:noactive', user.get_guild()) await context.send(f'{context.message.author.mention}, {output}')
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)
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)
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)
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)