async def timer_pause(self, ctx: commands.Context): """ Pauses the timer, if it's running. Keeps all settings and current period and time. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if len(interface.subbed) == 0: if interface.timer.stop(): send = "No subs detected, timer will instead stop soon." await self.bot.say(send, delete_after=interface.timer.step) else: await self.bot.remove_messages(channel) send = "No subs detected, timer has stopped instead." await self.bot.say(send, tts=interface.tts) log = ( "Attempted to pause the timer, but due to the lack of subs, " "it will be stopped instead") elif interface.timer.pause(): log = "Timer will be paused soon." await self.bot.say(log, delete_after=interface.timer.step) else: log = "Could not pause timer, stopped or already running." await self.bot.say("I cannot stop something that isn't moving.", delete_after=self.bot.ans_lifespan) lib.log(log, channel_id=channel.id)
async def timer_sub(self, ctx: commands.Context): """ Adds you to the list of people currently using the timer. If you're in this list, you will receive a private message if the timer's period changes or if it starts/pauses/stops. """ author = ctx.message.author channel = self.bot.spoof(author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if author not in interface.subbed: interface.add_sub(author, datetime.utcnow()) interface.restart_inactivity() log = (lib.get_author_name(ctx, True) + " has subscribed to this timer.") send = "You've successfully subscribed to this timer, {}!" \ .format(lib.get_author_name(ctx, True)) else: log = (lib.get_author_name(ctx, True) + " tried to subscribe to " + "this timer, but he was already added") send = ("You're already subscribed. " + "I'll let you know of any changes!") lib.log(log, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan)
async def attendance(self, ctx: commands.Context, name=None): """ Gives the last attendance (timer subscription) of the user to any timer, in the form of the UTC time in which it was registered. :param name: The username (Not the nick) of the user of whose attendance you want to know. Must use the name#discriminator format. :return: """ author = ctx.message.author if name is None: name = str(author) if name == "all": result = '\n'.join("{}: {}".format( record.name.split('#')[0], "None found." if record.last_seen is None else record.last_seen.strftime("%m-%d-%y %H:%M")) for record in db_manager.get_all_records()) else: record = db_manager.get_user_attendance(name) result = "None found." if record is None else record\ .strftime("%m-%d-%y %H:%M") log = "{} queried for {} attendance. Result was: {}"\ .format(lib.get_name(author, True), "their" if name == str(author) else (name + "'s"), result) lib.log(log, channel_id=lib.get_channel_id(ctx)) await self.bot.say("```\n{}\n```".format(result), delete_after=self.bot.ans_lifespan * 3)
async def admin_lock(self, ctx: commands.Context): """ Locks a channel's timer so no user can modify it. Unless they have permissions. This command either locks or unlocks, thus acting as a switch. Requires elevated permissions. """ channel = lib.get_channel(ctx) interface = self.bot.get_interface(channel) if interface.spoofed is not None: channel = interface.spoofed if interface.locked: interface.locked = False await self.bot.say("Channel unlocked.", delete_after=self.bot.ans_lifespan) lib.log(lib.get_author_name(ctx) + " unlocked the channel.", channel_id=channel.id) else: interface.locked = True await self.bot.say("Channel locked.", delete_after=self.bot.ans_lifespan) lib.log(lib.get_author_name(ctx) + " locked the channel.", channel_id=channel.id)
async def timer_unsub(self, ctx: commands.Context): """ Removes you from the list of people currently using the timer. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) author = ctx.message.author result = 0 interface = self.bot.get_interface(channel) if author in interface.subbed: result = interface.remove_sub(author) log = (lib.get_author_name(ctx, True) + " has un-subscribed to this timer.") send = "You've successfully un-subscribed to this timer, {}!" \ .format(lib.get_author_name(ctx, True)) else: log = (lib.get_author_name(ctx, True) + " tried to un-subscribe " + "to this timer, but he was not in the list") send = "You're not subscribed to this timer... " lib.log(log, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan) if result == -1: await self.bot.say("Timer now has no subs. Will stop after {} " "minutes unless someone subscribes!".format( self.bot.timer_inactivity_allowed), delete_after=self.bot.ans_lifespan) elif result == -2: await self.bot.remove_messages(channel) await self.bot.say("Timer has stopped since it was paused and " "everyone un-subscribed")
async def timer_time(self, ctx: commands.Context): """ Gives the user the current period and time of the timer. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) send = self.bot.get_interface(channel).timer.time(True) lib.log(send, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan * 2)
async def admin_reloadcfg(self): """ Reloads the configuration. Requires elevated permissions. """ self.bot.reload_config(config.get_config().reload()) await self.bot.say("Successfully reloaded configuration.", delete_after=self.bot.ans_lifespan) lib.log("Reloaded configuration.")
async def timer_status(self, ctx: commands.Context): """ Tells whether the timer is stopped, running or paused, if it's correctly set up and if it will soon stop or pause. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) send = self.bot.get_interface(channel).timer.show_status() lib.log(send, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan * 2)
def get_record(self, user: User): record = self._sql_session.query(TimerUser)\ .filter_by(discord_id=user.id).first() if record is None: lib.log("DB queried for non-existent user {}," " registry will be created.".format(str(user))) record = TimerUser(discord_id=user.id, name=str(user)) self._sql_session.add(record) self._sql_session.commit() return record
async def last(self, ctx: commands.Context): """ Shows you how long your last session lasted. """ time_str = printable_time( db_manager.get_user_last_session(ctx.message.author)) if time_str is None: time_str = "None found." lib.log("{} queried for their last session time. Result: {}".format( lib.get_author_name(ctx, True), time_str)) await self.bot.say("```{}```".format(time_str), delete_after=self.bot.ans_lifespan * 3)
async def on_command_error(self, error, ctx: commands.Context): log = lib.get_author_name(ctx) send = None if isinstance(error, commands.CheckFailure): if str(error) == "timer not found": send = "No timer found for this channel." log += " tried to interact with a nonexistent timer." elif str(error) == "timer locked": send = "You are not allowed to modify this timer." log += " tried to modify a locked timer without permissions." elif str(error) == "no permissions" or str(error) == "not admin": send = "You do not have permission to do this!" log += (" tried to execute a command and failed, " + "lacked permissions.") else: send = "Timers are not allowed in this channel." log += " tried to start a timer in a non-whitelisted channel." elif isinstance(error, commands.CommandNotFound): send = "Command not found: `" + ctx.invoked_with + "`." log += " tried to execute a nonexistent command: `{}`."\ .format(ctx.invoked_with) alt = None for name, command in self.bot.commands.items(): if ctx.invoked_with == name: alt = name elif isinstance(command, commands.GroupMixin): for sub_name, sub_command in command.commands.items(): if ctx.invoked_with == sub_name: alt = name + " " + sub_name if alt is not None: send += " Did you mean `" + alt + "`?" elif isinstance(error, commands.CommandInvokeError): lib.log_cmd_stacktrace(error) return else: log = str(error) lib.log(log, channel_id=lib.get_channel_id(ctx), level=logging.WARN) await self.bot.safe_send(lib.get_channel(ctx), send, delete_after=self.bot.ans_lifespan)
async def admin_sub(self, ctx: commands.Context, user_id: str, channel_id=None): """ Forcefully subscribes a member to a timer. :param user_id: The ID of the user to subscribe :type user_id: str :param channel_id: The ID of the channel to subscribe the user to. If it's None or not provided, defaults to the channel this command was sent to. :type channel_id: str """ if channel_id is None: channel_id = lib.get_channel(ctx).id server = lib.get_server(ctx) user = server.get_member(user_id) interface = self.bot.get_interface(server.get_channel(channel_id), False) author_name = lib.get_author_name(ctx, True) member_name = lib.get_name(user, True) if interface is None: lib.log("{} tried to subscribe {} to {}, " "but the channel was not found or had no interface" .format(author_name, member_name, "this channel" if channel_id is None else "channel with id=" + channel_id), channel_id=channel_id) await self.bot.say("Channel not found or invalid.", delete_after=self.bot.ans_lifespan) elif user is None: lib.log("{} tried to forcefully subscribe an invalid member" .format(author_name), channel_id=channel_id) await self.bot.say("User is invalid. Are you sure its his ID?", delete_after=self.bot.ans_lifespan) else: if user in interface.subbed: await self.bot.say("User is already subscribed!", delete_after=self.bot.ans_lifespan) lib.log("{} tried to subscribe {} to {}, but user is already " "subscribed".format(author_name, member_name, "this channel" if channel_id is None else "channel with id=" + channel_id ), channel_id=channel_id) return interface.add_sub(user, datetime.now()) lib.log("{} forcefully subscribed {} to {}." .format(author_name, member_name, "this channel" if channel_id is None else "channel with id=" + channel_id), channel_id=channel_id) await self.bot.say("Member subscribed!", delete_after=self.bot.ans_lifespan) await self.bot.say("{}, {} has subscribed you to this channel's " "timer!".format(member_name, author_name), delete_after=self.bot.ans_lifespan)
async def leaderboard(self, ctx: commands.Context): """ Shows the highest recorded times """ result = '\n' \ .join("{} - {}".format(record.name.split('#')[0], "None found." if record.total_recorded is None else printable_time(record.total_recorded)) for record in db_manager.get_leaderboard()) lib.log("{} queried for the leaderboard. Result: {}".format( lib.get_author_name(ctx, True), result)) await self.bot.say("```\n{}\n```".format(result), delete_after=self.bot.ans_lifespan * 3)
async def timer_resume(self, ctx: commands.Context): """ Resumes a paused timer. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) if self.bot.get_interface(channel).timer.resume(): await self.bot.run_timer(channel) else: lib.log("Unable to resume timer, stopped or already running.", channel_id=channel.id) await self.bot.say("**grumble grumble.** The timer is " + "stopped or already running, I can't " + "resume that!", delete_after=self.bot.ans_lifespan)
async def check_last(self, ctx: commands.Context, name): """ Shows you how long other users' last session lasted. :param name: The name (not the nick) of the person to check. Must use the name#discriminator format. """ time_str = printable_time(db_manager.get_user_last_session(name)) if time_str is None: time_str = "None found." lib.log("{} queried for {}'s last session time. Result: {}".format( lib.get_author_name(ctx, True), "their" if name == str(ctx.message.author) else (name + "'s"), time_str)) await self.bot.say("```{}```".format(time_str), delete_after=self.bot.ans_lifespan * 3)
async def admin_debug(self): """ Makes the logger show debug-level information on the log. This command is administrator-only. """ if lib.is_logger_debug(): lib.debug(False) level = "info" state = "off" else: lib.debug(True) level = "debug" state = "on" lib.log("Switching to {}-level logging".format(level)) await self.bot.say("Debug mode {}.".format(state), delete_after=self.bot.ans_lifespan)
async def _translate_keyword(self, keyword: str, server_id: str, channel_id: str): if keyword == "help": example_periods = ', '.join( str(period.time) for period in PomodoroTimer.parse_format(SAFE_DEFAULT_FMT)) await self.bot.say( ("**Example:**\n\t {}setup {}\n\t" "_This will give you a sequence of {}_").format( self.bot.command_prefix, SAFE_DEFAULT_FMT, example_periods), delete_after=self.bot.ans_lifespan * 2) return None if keyword == 'default': # fetch default setup string from config, # or fallback to "Safe Default" translation = config.get_config().get_str( 'timer.channel_whitelist.' + server_id + '.' + channel_id) if translation is None: lib.log("No setup configured for this channel. Using the " + "safe default option", channel_id=channel_id) translation = SAFE_DEFAULT_FMT return translation if keyword == 'blank': pass keys = keyword.split(':', 1) if len(keys) < 2: return keyword if keys[0] == 'typical': durations = keys[1].split(',', 2) return "(2xStudy/Work:{x},Break:{y}),Study/Work:{x},Long_Break:{z}"\ .format(x=durations[0], y=durations[1], z=durations[2]) if keys[0] == 'saved': translation = config.get_config().get_str('timer.saved_formats.' + keys[1]) return translation return keyword
async def timer_stop(self, ctx: commands.Context): """ Stops the timer, if it's running. Resets the current period and time, but keeps the setup. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if interface.timer.stop(): send = "Timer will stop soon." await self.bot.say(send, delete_after=interface.timer.step) else: await self.bot.remove_messages(channel) send = "Timer has stopped." await self.bot.say(send, tts=interface.tts) lib.log(send, channel_id=channel.id)
async def admin_spoof(self, ctx: commands.Context, spoofed_id=None): """ Enables spoof-mode on a channel. Spoof mode allows users with permissions to modify another specified channel's timer from the one in which this command was executed. For example, if channel #session_1 has ID '249719010319532064' and someone executes '!spoof 249719010319532064' from #admin_area, all timer-related commands (except for setup) executed from #admin_area by members with permissions will either affect or give information of the timer in #session_1 instead. :param spoofed_id: The ID of the channel that instructions will be sent to. :type spoofed_id: str """ channel = lib.get_channel(ctx) if channel.id == spoofed_id: await self.bot.say("How about no. " + spoofed_id, delete_after=self.bot.ans_lifespan) return spoofed_channel = lib.get_server(ctx).get_channel(spoofed_id) if spoofed_id is not None: self.bot.get_interface(channel).spoofed = spoofed_channel send = "Now acting in channel " + spoofed_channel.name log = "Now acting as if in " + spoofed_channel.name elif self.bot.get_interface(channel).spoofed is not None: self.bot.get_interface(channel).spoofed = None send = "Now acting in current channel" log = "Spoofing now off" else: raise commands.MissingRequiredArgument await self.bot.say(send, delete_after=self.bot.ans_lifespan) lib.log(log, channel_id=channel.id)
async def admin_shutdown(self, ctx: commands.Context): """ Exits the program. Administrator only! """ lib.log("Shutting down...") await self.bot.say("Hope I did well, bye!") for channel, timer in self.bot.valid_timers().items(): if timer.get_state() != State.STOPPED: timer.stop() if lib.get_channel(ctx) != channel: await self.bot.safe_send( channel, "I'm sorry, I have to go. See you later!" ) await self.bot.remove_messages(channel) self.bot.unsub_all() await self.bot.logout()
async def timer_superreset(self, ctx: commands.Context): """ Ignores all conditions and resets the channel's timer. Requires elevated permissions. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if interface.timer.get_state() == State.RUNNING: self.bot.timers_running -= 1 await self.bot.update_status() await self.bot.remove_messages(channel) interface.timer = None lib.log("Successfully forced a reset on this channel's timer.", channel_id=channel.id) await self.bot.say("Timer has been force-reset", delete_after=self.bot.ans_lifespan)
async def timer(self, ctx): """ Controls the channel's timer. Do '!help timer' for sub-commands. None of the sub-commands will really work without using `setup` first. """ if ctx.invoked_subcommand is None: sect = ctx.message.content.split(' ') if len(sect) < 2 or sect[1] is None: log = "{} invoked an incomplete timer command." send = "Timers are allowed here. Now what?" else: log = "{} invoked an invalid timer command." send = "Invalid timer sub-command." else: return lib.log(log.format(lib.get_author_name(ctx)), channel_id=lib.get_channel_id(ctx)) await self.bot.say(send, delete_after=self.bot.ans_lifespan)
async def total(self, ctx: commands.Context, name=None): """ Shows you the total time a user has used the timer for. :param name: The name (not the nick) of the person to check. Must use the name#discriminator format. If none is provided, it will check your own record. """ if name is None: name = ctx.message.author time_str = printable_time(db_manager.get_user_total(name)) if time_str is None: time_str = "None found." name = str(name) lib.log("{} queried for {}'s last session time. Result: {}".format( lib.get_author_name(ctx, True), "their" if name == str(ctx.message.author) else (name + "'s"), time_str)) await self.bot.say("```{}```".format(time_str), delete_after=self.bot.ans_lifespan * 3)
async def timer_goto(self, ctx: commands.Context, period_idx): """ Skips to the n-th period, assuming the periods' indexes go from 1 to the amount of them. :param period_idx: The index of the period to start from, from 1 to n. :type period_idx: 'next' or int such that 1 <= period_idx <= n, n being the amount of periods set. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if period_idx == "next": idx = interface.timer.get_period(True) + 1 else: try: idx = int(period_idx) except TypeError: raise commands.BadArgument label = interface.timer.goto(idx) if label is not None: log = send = "Moved to period number {!s} ({})".format(idx, label) if interface.timer.get_state() != State.STOPPED: await self.bot.edit_message(interface.list_message, interface.timer.list_periods()) if interface.timer.get_state() == State.PAUSED: await self.bot.edit_message(interface.time_message, interface.timer.time()) else: log = "Invalid period number entered when trying goto command." send = "Invalid period number." lib.log(log, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan)
async def timer_reset(self, ctx: commands.Context): """ Resets the timer setup. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) if interface.timer.get_state() == State.STOPPED: interface.timer.set_state(None) interface.timer = None interface.time_message = None interface.list_message = None log = lib.get_author_name(ctx) + " reset the timer." send = "Successfully reset session configuration." else: log = (lib.get_author_name(ctx) + " tried resetting a timer that " "was running or paused.") send = "Cannot do that while the timer is not stopped." lib.log(log, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan)
async def timer_tts(self, ctx: commands.Context, toggle: str = None): """ Sets the TTS option on or off for the channel. :param toggle: Whether to turn on or off the TTS option. If no option is provided, it will toggle it :type toggle: str """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) log = send = None if toggle is None: interface.tts = not interface.tts toggle = "ok" else: try: interface.tts = lib.to_boolean(toggle) except TypeError: toggle = None log = "TTS command failed, bad argument." send = ("I could not understand if you wanted to " + "turn TTS on or off.") if toggle is not None: status = ("on" if interface.tts else "off") log = "TTS now " + status + " for this channel." send = "Text-to-speech now " + status + " for this channel." lib.log(log, channel_id=channel.id, level=logging.WARN if toggle is None else logging.INFO) await self.bot.say(send, tts=interface.tts and toggle is not None, delete_after=self.bot.ans_lifespan)
async def timer_start(self, ctx: commands.Context, period_idx=1): """ Starts the timer with the recorded setup. The timer must be correctly set up and not running for it to work. :param period_idx: The index of the period to start from, from 1 to n. :type period_idx: int; 1 <= period_idx <= amount of periods """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) interface = self.bot.get_interface(channel) timer = interface.timer if timer.start(): if not 0 < period_idx <= len(timer.periods): period_idx = 1 try: if interface.restart_inactivity(): await self.bot.say( "Timer has no subs. Will stop after" " {} minutes unless someone subscribes!".format( self.bot.timer_inactivity_allowed), delete_after=self.bot.ans_lifespan) await self.bot.run_timer(channel, period_idx - 1) except discord.errors.HTTPException: await self.bot.say("@here\n" "Connection interrupted, please resume! (1)" ) timer.pause() else: lib.log(lib.get_author_name(ctx) + " tried to start a timer that was already running.", channel_id=channel.id) await self.bot.say("This channel's timer is already running", delete_after=self.bot.ans_lifespan)
async def on_ready(self): """ A listener for the event in which the bot is ready to work. """ lib.log("") lib.log("Using discord.py version: " + discord.__version__) lib.log("Logged in as :") lib.log("\t" + self.bot.user.name) lib.log("\t" + self.bot.user.id) lib.log("") await self.bot.update_status() message = "**[{}]** {}"\ .format(config.get_config().get_str('version'), config.get_config().get_str('startup_msg')) for server in self.bot.servers: await self.bot.send_message(server, message)
async def run_timer(self, channel: discord.Channel, start_idx=0): """ Makes a timer run. :param channel: The channel where the timer that is being ran is. :type channel: discord.Channel :param start_idx: The index of the period from which the timer should start from. Defaults to 0, or is 0 if it's outside the valid range. :type start_idx: int; 0 < start_idx <= len(timer.periods) """ interface = self.get_interface(channel) timer = interface.timer if timer is None: lib.log("Tried to start a timer, but none found.", channel_id=channel.id) return await self.wait_until_ready() self.timers_running += 1 await self.update_status() while not self.is_closed: iter_start = datetime.now() start_micro = iter_start.second * 1000000 + iter_start.microsecond if timer.get_state() == State.RUNNING and \ timer.curr_time >= timer.periods[timer.get_period()]\ .time * 60: say = "'{}' period over!" \ .format(timer.periods[timer.get_period()].name) timer.curr_time = 0 if timer.get_period() + 1 >= len(timer.periods) and \ not timer.repeat: say += "\nI have ran out of periods, and looping is off." lib.log(say, channel_id=channel.id) await self.safe_send(channel, say, tts=interface.tts) break timer.set_period((timer.get_period() + 1) % len(timer.periods)) if timer.action == Action.NONE: say += " '{}' period now starting ({})." \ .format(timer.periods[timer.get_period()].name, lib.pluralize( timer.periods[timer.get_period()].time, "minute", append="s")) lib.log(say, channel_id=channel.id) try: await self.safe_send(channel, say, tts=interface.tts) await self.edit_message(interface.list_message, timer.list_periods()) except d_err.HTTPException: lib.log("Skipped updating periods due to HTTPException", channel_id=channel.id, level=logging.WARN) if timer.action == Action.STOP: timer.action = Action.NONE lib.log("Timer has stopped.", channel_id=channel.id) await self.safe_send(channel, "Timer has stopped.") break elif timer.action == Action.PAUSE: timer.action = Action.NONE timer.set_state(State.PAUSED) lib.log("Timer has paused.", channel_id=channel.id) await self.safe_send(channel, "Timer has paused.") elif timer.action == Action.RUN: timer.action = Action.NONE prev_state = timer.get_state() timer.set_state(State.RUNNING) if prev_state == State.STOPPED: timer.set_period(start_idx) say_action = "Starting" else: say_action = "Resuming" if start_idx != 0: say_action += " (from period n." + str(start_idx + 1) + ")" lib.log(say_action, channel_id=channel.id) await self.safe_send(channel, say_action) if interface.time_message is None: try: await self._generate_messages(channel) except discord.Forbidden: lib.log("No permission to pin.", channel_id=channel.id) kitty = ("I tried to pin a message and failed." + " Can I haz permission to pin messages?" + " https://goo.gl/tYYD7s") await self.safe_send(channel, kitty) try: if interface.time_message is not None: await self.edit_message(interface.time_message, timer.time()) except d_err.NotFound: pass except d_err.HTTPException: lib.log( "Skipped editing the time message due to HTTPException", channel_id=channel.id, level=logging.WARN) if timer.get_state() == State.RUNNING: iter_end = datetime.now() end_micro = iter_end.second * 1000000 + iter_end.microsecond end_micro -= start_micro end_micro %= 1000000.0 sleep_time = ((timer.step * 1000000.0) - end_micro) await asyncio.sleep(sleep_time / 1000000.0) timer.curr_time += timer.step inactive = interface.check_inactivity( self.timer_inactivity_allowed, self.user_inactivity_allowed) if isinstance(inactive, bool) and inactive: send = "Timer will stop due to inactivity." lib.log(send, channel_id=channel.id, level=logging.INFO) await self.safe_send(channel, send, delete_after=self.ans_lifespan) elif isinstance(inactive, list): for user in inactive: notice = ("ou have been un-subscribed due to" " inactivity!") # Yes, there's a typo, but there's also a hacky solution await self.safe_send(channel, "{}, y{}".format( user.mention, notice), delete_after=self.ans_lifespan) await self.safe_send(user, "Y" + notice) if interface.restart_inactivity(): send = ("Timer has no subs. Will stop after {} " "minutes unless someone subscribes!")\ .format(self.timer_inactivity_allowed) await self.safe_send( channel, send, delete_after=self.ans_lifespan) interface.add_sub_time(timer.step) else: break if timer.get_state() != State.PAUSED: timer.curr_time = 0 timer.set_period(-1) timer.set_state(State.STOPPED) if len(timer.periods) == 0: timer.set_state(None) interface.timer = None await self.safe_send(channel, "Timer has been reset.", delete_after=self.bot.ans_lifespan) await self.remove_messages(channel) self.timers_running -= 1 await self.update_status()
async def setup(self, ctx: commands.Context, timer_format="default", repeat=None, count_back=None): """ Sets up a timer for the channel in which this command was executed. Only allows timers in white-listed channels (Specified in the configuration). :param timer_format: The string containing the periods and their names, in a format similar to that of a dictionary. Ex.: PeriodA:10,PeriodB:5,PeriodC:15 This will create 3 periods of 10, 5 and 15 minutes. It also accepts segments with the format (nxName1:t1,Name2:t2), which creates n iterations of Name1:t1,Name2:t2 periods (Where Name1 and Name2 are the period names and t1, t2 the respective times). Ex.: (3xPeriodA:10,PeriodB:5),PeriodC:15 This will create 7 periods of times 10,5,10,5,10,5 and 15. :type timer_format: str :param repeat: (boolean) Whether the timer should go back to period 1 after going through the complete list (True) or not (False). Defaults to True. :param count_back: (boolean) Whether the timer should show remaining (True) or elapsed (False) time. Defaults to True. """ channel = self.bot.spoof(ctx.message.author, lib.get_channel(ctx)) timer_format = await self._translate_keyword(timer_format, lib.get_server_id(ctx), channel.id) if timer_format is None: return # Parse the countdown and looping arguments with the custom function. try: loop = config.get_config().get_boolean('timer.looping_default') if \ repeat is None else lib.to_boolean(repeat) countdown = config.get_config() \ .get_boolean('timer.countdown_default') if count_back is None \ else lib.to_boolean(count_back) except TypeError: lib.log( "Could not parse boolean arguments '{!s}' and '{!s}'".format( repeat, count_back), channel_id=channel.id) await self.bot.say("Invalid arguments received, please try again.", delete_after=self.bot.ans_lifespan) return interface = self.bot.get_interface(channel) if interface.timer is None: interface.timer = PomodoroTimer(interface) times = interface.timer.setup(timer_format, loop, countdown) if times is not None: log = ("Correctly set up timer config: {}." "\nLooping is **{}**\nCountdown is **{}**") \ .format(times, "ON" if loop else "OFF", "ON" if countdown else "OFF") send = log else: interface.timer = None log = ("Could not set the periods correctly, " "command 'setup' failed.") send = ("I did not understand what you wanted, " "please try again!") else: # channel_id is in p_timers.keys() log = ("Rejecting setup command, there is a period set already " "established.") send = ("I'm already set and ready to go, please use the reset " "command if you want to change the timer configuration.") lib.log(log, channel_id=channel.id) await self.bot.say(send, delete_after=self.bot.ans_lifespan)