async def countdown(self, ctx, *args): """Uses discord message time formatting to provide a countdown Usage: `~countdown September 22, 2021 9:00pm EDT` """ logging_utils.log_command("countdown", ctx.channel, ctx.author) if len(args) < 1: embed = discord_utils.create_no_argument_embed("time") await ctx.send(embed=embed) return user_time = time_utils.parse_date(" ".join(args)) if user_time is None: embed = discord_utils.create_embed() embed.add_field(name=f"Failed!", value=f"Is {' '.join(args)} a valid time?", inline=False) await ctx.send(embed=embed) return unix_time = int(datetime.timestamp(user_time)) embed = discord.Embed(title=f"{' '.join(args)}", description=f"<t:{unix_time}:f>\n" f"`<t:{unix_time}:R>` - <t:{unix_time}:R>\n\n" f"[Guide to format](https://discord.com/developers/docs/reference#message-formatting-formats)", color=constants.EMBED_COLOR) await ctx.send(embed=embed)
async def showweekcalendar(self, ctx, timezone: str = constants.UTC): """Show the calendar for the next 7 days ~showweekcalendar EDT""" logging_utils.log_command("showweekcalendar", ctx.channel, ctx.author) server_calendar_results = self.activity_calendar_sheet.findall( ctx.guild.name, in_column=activity_calendar_constants.SHEET_SERVER_COLUMN) description = "" current_date = time_utils.parse_date(datetime.utcnow().strftime( constants.SHEET_DATETIME_FORMAT), from_tz=constants.UTC, to_tz=timezone) for cell in server_calendar_results: row = self.activity_calendar_sheet.row_values(cell.row) date = time_utils.parse_date( row[activity_calendar_constants.SHEET_TIMESTAMP_COLUMN - 1], from_tz=constants.UTC, to_tz=timezone) # Only show activities that are 1 week out if date.day - current_date.day <= 7: description += f"\n\n{time_utils.replace_offset(date.strftime(constants.DISPLAY_DATETIME_FORMAT))}: " \ f"[{row[activity_calendar_constants.SHEET_ACTIVITY_COLUMN-1]}:]({row[activity_calendar_constants.SHEET_LINK_COLUMN-1]})" \ f" {row[activity_calendar_constants.SHEET_DESCRIPTION_COLUMN-1]}" embed = discord.Embed( title= f"Activity Calendar for Week of {current_date.strftime('%B %d')}", description=description, color=constants.EMBED_COLOR) await ctx.send(embed=embed)
async def deleteactivity(self, ctx, *args): """Remove all instances of activity from the calendar ~deleteactivity Dueling""" logging_utils.log_command("deleteactivity", ctx.channel, ctx.author) # In general, I've been adding activities with specifics e.g. "Dueling: Helga Game" # If you want to delete only that instance, ~dactivity "Dueling: Helga Game" # If you want to delete all dueling activities, ~dactivity Dueling activity = ' '.join(args) result_cells = self.activity_calendar_sheet.findall( ctx.guild.name, in_column=activity_calendar_constants.SHEET_SERVER_COLUMN) num_deletions = 0 # TODO: I think this whole num_deletions thing becomes a non-issue if we just sort the rows # in a descending order for cell in result_cells: row = self.activity_calendar_sheet.row_values(cell.row) if activity in row[ activity_calendar_constants.SHEET_ACTIVITY_COLUMN - 1]: self.activity_calendar_sheet.delete_row(cell.row - num_deletions) num_deletions += 1 embed = discord_utils.create_embed() embed.add_field( name="Success", value= f"Deleted {num_deletions} instances of {activity} from the calendar.", inline=False) await ctx.send(embed=embed)
async def endrace(self, ctx): """ DONT USE PLS Ends the race Usage: ~endrace """ logging_utils.log_command("endrace", ctx.channel, ctx.author) channel = ctx.channel.id if channel not in self.current_races: embed = discord_utils.create_embed() embed.add_field( name="No race!", value= "This channel doesn't have a race going on. You can't end something that hasn't started!", inline=False) embed.add_field( name="Start Race", value=f"To start a race, use {ctx.prefix}startrace", inline=False) await ctx.send(embed=embed) return self.current_races.pop(channel) embed = discord_utils.create_embed() embed.add_field( name="Race Stopped", value=f"To start a new race, use {ctx.prefix}startrace", inline=False) embed.add_field( name="Experimental", value= "ehm, this command is still in development. It actually probably didn't do anything, sorry!", inline=False) await ctx.send(embed=embed)
async def reload(self, ctx): """ Admin Command. Reload the Google Sheet so we can update our codes instantly. Usage: ~reload """ logging_utils.log_command("reload", ctx.channel, ctx.author) self.sheet_map = { cipher_race_constants.HP: google_utils.get_dataframe_from_gsheet( self.spreadsheet.worksheet( cipher_race_constants.HP_SHEET_TAB_NAME), cipher_race_constants.COLUMNS), cipher_race_constants.COMMON: google_utils.get_dataframe_from_gsheet( self.spreadsheet.worksheet( cipher_race_constants.COMMON_SHEET_TAB_NAME), cipher_race_constants.COLUMNS), cipher_race_constants.CHALLENGE: google_utils.get_dataframe_from_gsheet( self.spreadsheet.worksheet( cipher_race_constants.CHALLENGE_SHEET_TAB_NAME), cipher_race_constants.COLUMNS) } embed = discord_utils.create_embed() embed.add_field(name="Sheet Reloaded", value="Google sheet successfully reloaded", inline=False) await ctx.send(embed=embed)
async def time(self, ctx, *args): """Return the time in the specified location ~time Amsterdam""" logging_utils.log_command("time", ctx.channel, ctx.author) # No location provided if len(args) < 1: embed = discord_utils.create_no_argument_embed("location") await ctx.send(embed=embed) return # Allow long input (e.g. St. Louis, Missouri, USA) location = " ".join(args) timezone_dict = self.get_tz(location) # Unable to find the location in the geonames database if timezone_dict is None: embed = discord_utils.create_embed() embed.add_field(name="Error!", value=f"Cannot find {location}!", inline=False) await ctx.send(embed=embed) return names = ["Location (Timezone)", "Current Time"] # The DB provides a - (GMT) for west timezones but not a + for the east values = [f"{location.title()} ({time_utils.format_gmt_offset(timezone_dict)})", time_utils.format_time(timezone_dict['time'])] embed = discord_utils.populate_embed(names, values, inline=False) await ctx.send(embed=embed)
async def duelingcategory(self, ctx, *args): """Get Dueling Questions from a particular category! ~duelingcat""" logging_utils.log_command("duelingcategory", ctx.channel, ctx.author) categories = self.quarter_tab_get_column("B") user_cat = " ".join(args) # No supplied category -- give cats if len(args) < 1 or user_cat not in categories: embed = discord_utils.create_embed() embed.add_field(name="Available Categories", value="\n".join(categories)) await ctx.send(embed=embed) return # Find all questions of the given category # TODO: Hardcoded question = self.quarter_tab_get_question(user_cat, 2) embed = discord_utils.create_embed() embed.add_field( name=dueling_constants.THEME, value=question[0], ) #inline=False) embed.add_field( name=dueling_constants.CATEGORY, value=question[1], ) #inline=False) embed.add_field(name=dueling_constants.QUESTION, value=question[2], inline=False) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer(question[3]), inline=False) await ctx.send(embed=embed)
async def duelingtheme(self, ctx, *args): """Get Dueling Questions from a particular theme! ~duelingtheme""" logging_utils.log_command("duelingtheme", ctx.channel, ctx.author) themes = self.quarter_tab_get_column("A") user_theme = " ".join(args) # No supplied theme -- give themes if len(args) < 1 or user_theme not in themes: embed = discord_utils.create_embed() embed.add_field(name="Available Themes", value="\n".join(themes)) await ctx.send(embed=embed) return question = self.quarter_tab_get_question(user_theme, 1) embed = discord_utils.create_embed() embed.add_field( name=dueling_constants.THEME, value=question[0], ) #inline=False) embed.add_field( name=dueling_constants.CATEGORY, value=question[1], ) #inline=False) embed.add_field(name=dueling_constants.QUESTION, value=question[2], inline=False) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer(question[3]), inline=False) await ctx.send(embed=embed)
async def archive(self, ctx, *args): logging_utils.log_command("archive", ctx.channel, ctx.author) embed = nextcord.Embed( title="Error!", description= f"The command `{ctx.prefix}archive` does not exist! Did you mean `{ctx.prefix}archivechannel` instead?" ) await ctx.send(embed=embed)
async def search(self, ctx, *args): """ Command to search the interwebs! (google) Usage: ~search <target_site> <[query...]> """ logging_utils.log_command("search", ctx.channel, ctx.author) if len(args) < 2: await ctx.send(embed=discord_utils.create_no_argument_embed( "Target Site and Query")) return target_site = args[0].lower() if target_site in lookup_constants.REGISTERED_SITES: target_site = lookup_constants.REGISTERED_SITES[target_site] # If we do a google search, we want to return the 10 top results # Otherwise, we want to just send the most relevant result if target_site == lookup_constants.GOOGLE: is_google_search = True else: is_google_search = False original_query = ' '.join(args[1:]) # Don't add google to the query but add any other target site for easier access/SEO if not is_google_search: query = original_query + ' ' + target_site else: query = original_query # Dude this loop is going to be horrible wtf # If google: # Store all results as a list and print them out line by line in an embed # If not google: # Find the first result that matches the target site and return that # If we can't find it, return the google query I think embed = discord_utils.create_embed() results = [] for result in googlesearch.search(query, num=lookup_constants.QUERY_NUM, stop=lookup_constants.QUERY_NUM, pause=lookup_constants.PAUSE_TIME): if target_site in result: embed.add_field( name= f"{target_site.capitalize()} Result for {original_query}", value=result) await ctx.send(embed=embed) return results.append(result) if is_google_search: embed.add_field( name=f"{target_site.capitalize()} Result for {original_query}", value=f"{chr(10).join(results)}") else: embed.add_field( name=f"Search failed!", value= f"Sorry! We weren't able to find a {target_site.capitalize()}" f"link for {original_query}. However, here are the top 10 hits on Google:\n" f"{chr(10).join(results)}") await ctx.send(embed=embed)
async def google(self, ctx, *args): """Gets the top 10 google results for your query ~google The Leaning Tower of Piza""" logging_utils.log_command("google", ctx.channel, ctx.author) results = lookup_utils.search_query(' '.join(args)) embed = discord_utils.create_embed() embed.add_field(name=f"Google Result for {' '.join(args)}", value=f"{chr(10).join(results)}") await ctx.send(embed=embed)
async def archiveserver(self, ctx): """Command to archive every text channel in the server. WARNING: This command will take *very* long on any reasonably aged server Usage: `!archiveserver`""" logging_utils.log_command("archiveserver", ctx.channel, ctx.author) # If we don't have the lock, let the user know it may take a while. msg = None if self.lock.locked(): msg = await ctx.send(embed=archive_utils.get_delay_embed()) # LOCK EVERYTHING async with self.lock: start_embed = await self.get_start_embed(ctx.guild, ctx.guild.text_channels) if msg: await msg.delete() msg = None # SOMETIMES THE EMBED IS TOO LONG FOR DISCORD embeds = discord_utils.split_embed(start_embed) msgs = [] for embed in embeds: msgs.append(await ctx.send(embed=embed)) for text_channel in ctx.guild.text_channels: archive_utils.reset_archive_dir() try: zip_file, zip_file_size, textfile, textfile_size = await self.archive_one_channel( text_channel) file, embed = self.get_file_and_embed( text_channel, ctx.guild.filesize_limit, zip_file, zip_file_size, textfile, textfile_size) await ctx.send(file=file, embed=embed) except nextcord.errors.Forbidden: embed = discord_utils.create_embed() embed.add_field( name="ERROR: No access", value= f"Sorry! I don't have access to {text_channel.mention}. You'll need " f"to give me permission to view the channel if you want " f"to archive it", inline=False) await ctx.send(embed=embed) continue if msgs: for msg in msgs: await msg.delete() embed = discord_utils.create_embed() embed.add_field(name="All Done!", value=f"Successfully archived {ctx.guild}", inline=False) await ctx.send(embed=embed) # Clean up the archive dir archive_utils.reset_archive_dir()
async def errorlog(self, ctx, num_lines: int = 50): """Admin Command. Shows errors in reverse chronological order ~errorlog""" logging_utils.log_command("errorlog", ctx.channel, ctx.author) with open(error_constants.ERROR_LOGFILE, "r") as f: lines = f.readlines() last_n_lines = "".join(lines[-num_lines:]) # Trim the length of the log messages if len(last_n_lines) > 1990: last_n_lines = f"...\n{last_n_lines[-1990:]}" await ctx.send(f"```{last_n_lines}```")
async def submissions(self, ctx): """Count submissions for homework and extra credit ~submissions""" logging_utils.log_command("submissions", ctx.channel, ctx.author) # Get HW and EC if possible result_cells = self.activity_calendar_sheet.findall( ctx.guild.name, in_column=activity_calendar_constants.SHEET_SERVER_COLUMN) activity_ids = [] submission_counts = {} for cell in result_cells: row = self.activity_calendar_sheet.row_values(cell.row) if "Homework:" in row[ activity_calendar_constants.SHEET_ACTIVITY_COLUMN - 1] or "EC:" in row[ activity_calendar_constants.SHEET_ACTIVITY_COLUMN - 1]: # TODO: pls if row[activity_calendar_constants.SHEET_LINK_COLUMN - 1] not in [link[2] for link in activity_ids]: activity_ids.append(( row[activity_calendar_constants.SHEET_ACTIVITY_COLUMN - 1], row[activity_calendar_constants. SHEET_DESCRIPTION_COLUMN - 1], row[activity_calendar_constants.SHEET_LINK_COLUMN - 1])) HOUSES = ["Gryffindor", "Hufflepuff", "Ravenclaw", "Slytherin"] description = "" for activity in activity_ids: submission = asyncpraw.models.Submission(self.reddit_client, url=activity[2]) for comment in await submission.comments(): for house in HOUSES: if "submit" in comment.body.lower() and house.lower( ) in comment.body.lower(): if activity[0] not in submission_counts: submission_counts[activity[0]] = [0, 0, 0, 0] submission_counts[activity[0]][HOUSES.index( house)] = len(comment.replies) if activity[0] in submission_counts: description += f"\n\n**[{activity[0]}:]({activity[2]})** {activity[1]} \n" \ f"{chr(10).join([f'{house}: {submissions}' for house, submissions in zip(HOUSES, submission_counts[activity[0]])])}" embed = discord.Embed(title="Submission counts for HW and ECs", description=description, color=constants.EMBED_COLOR) await ctx.send(embed=embed)
async def duelingmultiplechoice(self, ctx): """Get A Multiple Choice Question ~duelingmc""" logging_utils.log_command("duelingmultiplechoice", ctx.channel, ctx.author) embed = discord_utils.create_embed() # Cut off header quarter_questions = self.quarter_tab.get_all_values()[1:] question = quarter_questions[np.random.randint(len(quarter_questions))] # TODO: Hacky way of saying there are not multiple options i = 0 while question[-1] == "": question = quarter_questions[np.random.randint( len(quarter_questions))] i += 1 if i >= 100: embed.add_field( name=f"{constants.FAILED}!", value= f"Sorry! I was unable to find a multiple choice question. Try again later?" ) await ctx.send(embed=embed) return multiple_choice = [question[3]] + question[5:] np.random.shuffle(multiple_choice) embed.add_field( name=dueling_constants.THEME, value=question[0], ) #inline=False) embed.add_field( name=dueling_constants.CATEGORY, value=question[1], ) #inline=False) embed.add_field(name=dueling_constants.QUESTION, value=question[2], inline=False) embed.add_field( name=dueling_constants.CHOICES, # TODO: woof that hardcoded formatting # Some of the answers are ints value="\n".join([ f"{letter}.) {str(answer)}" for letter, answer in zip( string.ascii_uppercase, multiple_choice) ])) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer(question[3], filler=20), inline=False) await ctx.send(embed=embed)
async def monthlychecklist(self, ctx): """ Prints out a list of things to do at the start of every month Usage: `~monthlychecklist` """ logging_utils.log_command("monthlychecklist", ctx.channel, ctx.author) checklist = self.get_checklist() embed = discord_utils.create_embed() embed.add_field(name="Monthly Checklist", value=checklist, inline=False) await ctx.send(embed=embed)
async def reset(self, ctx): """ Admin Command. Reset the bot as if it has just loaded up Usage: ~reset Note: Does not reload google sheet. Use ~reload for that """ logging_utils.log_command("reset", ctx.channel, ctx.author) self.current_races = {} embed = discord_utils.create_embed() embed.add_field( name="Success", value= "Bot has been reset, and all races have been forcibly ended. I feel brand new!", inline=False) await ctx.send(embed=embed)
async def christmas(self, ctx): """Print out a countdown to Christmas Usage: `~christmas`""" logging_utils.log_command("christmas", ctx.channel, ctx.author) now = datetime.now(tz=pytz.UTC) delta_time = date(now.year, 12, 25) - now.date() embed = discord.Embed( title=f"There are {delta_time.days} sleeps until {EMOJIS[':Santa_Claus:']} Christmas {EMOJIS[':Christmas_tree:']}", url="https://christmascountdown.live/", description=f"{EMOJIS[':snowflake:']}{EMOJIS[':cloud_with_snow:']}{EMOJIS[':deer:']}{EMOJIS[':bell:']}{EMOJIS[':cloud_with_snow:']}{EMOJIS[':snowflake:']}\n" f"Check out the countdown timer [here](https://christmascountdown.live/)", color=constants.EMBED_COLOR ) await ctx.send(embed=embed)
async def duelingrandom(self, ctx): """Give a random question ~duelingrandom""" logging_utils.log_command("duelingrandom", ctx.channel, ctx.author) # Cut off headers possible_questions = self.quarter_tab.get_all_values( )[1:] + self.quotes_tab.get_all_values()[1:] question = possible_questions[np.random.randint( len(possible_questions))] embed = discord_utils.create_embed() # TODO: hardcoded hacked if len(question) <= 5: # name the quote embed.add_field(name=dueling_constants.QUOTE_PROMPT, value=question[0], inline=False) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer( question[4]), inline=False) embed.add_field(name="HINT (Book)", value=dueling_utils.format_spoiler_answer( question[2], filler=10), inline=False) else: # MC question (do not give MC) embed.add_field( name=dueling_constants.THEME, value=question[0], ) # inline=False) embed.add_field( name=dueling_constants.CATEGORY, value=question[1], ) # inline=False) embed.add_field(name=dueling_constants.QUESTION, value=question[2], inline=False) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer( question[3]), inline=False) await ctx.send(embed=embed)
async def wikipedia(self, ctx, *args): """Gets the Wikipedia article most closely related to your search ~wiki Philadelphia""" logging_utils.log_command("wikipedia", ctx.channel, ctx.author) results = lookup_utils.search_query(' '.join(args), target_site=lookup_constants.WIKI) embed = discord_utils.create_embed() if len(results) > 1: embed.add_field( name=f"Search failed!", value=f"Sorry! We weren't able to find a Wikipedia" f"link for {' '.join(args)}. However, here are the top 10 hits on Google:\n" f"{chr(10).join(results)}") else: embed.add_field(name=f"Wikipedia Result for {' '.join(args)}", value=f"{chr(10).join(results)}") await ctx.send(embed=embed)
async def remindme(self, ctx, *args): """ Reminds you to do something later. Pick one of days (d), hours (h), minutes (m) Usage: `~remindme 24h Take out the trash` """ logging_utils.log_command("remindme", ctx.channel, ctx.author) utctime = datetime.datetime.now(tz=pytz.UTC) # TODO: I'm being REALLY loose on the arguments here if 'd' in args[0]: remind_time = utctime + datetime.timedelta(days=int(args[0][:-1])) elif 'h' in args[0]: remind_time = utctime + datetime.timedelta(hours=int(args[0][:-1])) elif 'm' in args[0]: remind_time = utctime + datetime.timedelta( minutes=int(args[0][:-1])) else: embed = discord_utils.create_embed() embed.add_field( name=f"{constants.FAILED}!", value="Must supply a unit of time! (e.g. 5d, 24h, 30m)", inline=False) await ctx.send(embed=embed) return self.reminder_tab.append_row([ ctx.guild.name, ctx.channel.name, str(ctx.channel.id), remind_time.strftime(constants.SHEET_DATETIME_FORMAT), ctx.author.name, str(ctx.author.id), ' '.join(args[1:]) ]) embed = discord_utils.create_embed() embed.add_field( name=f"{constants.SUCCESS}!", value= f"I will remind you to {' '.join(args[1:])} <t:{int(datetime.datetime.timestamp(remind_time))}:R>", inline=False) await ctx.send(embed=embed)
async def duelingquote(self, ctx): """Get a quote and answer the SPEAKER and BOOK ~duelingquote""" logging_utils.log_command("duelingquote", ctx.channel, ctx.author) # Cut off header quote_questions = self.quotes_tab.get_all_values()[1:] question = quote_questions[np.random.randint(len(quote_questions))] embed = discord_utils.create_embed() embed.add_field(name=dueling_constants.QUOTE_PROMPT, value=question[0], inline=False) embed.add_field(name=dueling_constants.ANSWER, value=dueling_utils.format_spoiler_answer(question[4]), inline=False) embed.add_field(name="HINT (Book)", value=dueling_utils.format_spoiler_answer(question[2], filler=10), inline=False) await ctx.send(embed=embed)
async def startrace(self, ctx, sheet: str = cipher_race_constants.HP): """ Start your race! You will have 60 seconds per level to solve the codes Usage: ~startrace <optional sheet> where sheet is {hp, challenge, common} """ logging_utils.log_command("startrace", ctx.channel, ctx.author) channel = ctx.channel.id if channel in self.current_races: print("startrace called from a channel that's already racing!!") embed = discord_utils.create_embed() embed.add_field( name="Already Racing!", value= f"Stop trying to start a new race while you have one going!", inline=False) await ctx.send(embed=embed) return # Create entry in current_races self.current_races[channel] = dict() self.current_races[channel][cipher_race_constants.LEVEL] = 1 # ~startrace challenge gives you 1000 random english word sheet # ~startrace hp gives you the harry potter sheet # ~startrace common gives you 1000 very common english words if sheet not in self.sheet_map: sheet = cipher_race_constants.HP self.current_races[channel][ cipher_race_constants.CODE] = self.sheet_map[sheet] embeds, self.current_races[channel][ cipher_race_constants. ANSWERS] = cipher_race_utils.create_code_embed( self.current_races[channel][cipher_race_constants.LEVEL], self.current_races[channel][cipher_race_constants.CODE], ctx.prefix) await ctx.send(embed=cipher_race_utils.get_opening_statement(sheet)) # In a short time, send the codes time = Timer(cipher_race_constants.BREAK_TIME, self.start_new_level, callback_args=(ctx, channel, embeds), callback_async=True)
async def resend(self, ctx): """Command to resend the last post again. ~resend""" # log command in console logging_utils.log_command("resend", ctx.channel, ctx.author) # respond to command await ctx.send("Resending last announcement!") # check for last submission in subreddit subreddit = await self.reddit.subreddit() async for submission in subreddit.new(limit=1): # process submission subreddit, title, author, message = RedditPost(self.bot, submission).process_post() embed = discord.Embed(title=title, description=f"By u/{author}", color=constants.EMBED_COLOR) embed.add_field(name=f"New Post in r/{subreddit}!", value=message, inline=False) channel = self.bot.get_channel(reddit_feed_constants.REDDIT_ANNOUNCEMENTS_CHANNEL_ID) await channel.send(embed=embed)
async def addactivity(self, ctx: commands.Context, *args): """Add an activity to the calendar ~addactivity "Dueling" "Harry Potter Trivia!" Tuesday, May 11, 2021 7pm EDT https://www.reddit.com/r/Dueling""" logging_utils.log_command("addactivity", ctx.channel, ctx.author) # Currently we require the args to be: Activity name, description, date, link # They have to use quotes if they want the name or description to be more than 1 word. # We pop off the name, description, and link, and what's left is the time. # Should we make them put the time in quotes too? Then it would just make things cleaner on the back end # I would prefer making it easier to use, though, I guess args = list(args) activity = args.pop(0) description = args.pop(0) link = args.pop() user_time = time_utils.parse_date(' '.join(args)) # We store all our times in UTC on the spreadsheet spreadsheet_time = time_utils.parse_date(user_time.strftime( constants.SHEET_DATETIME_FORMAT), to_tz=constants.UTC) embed = discord.Embed(title="Added To Activities Calendar", color=constants.EMBED_COLOR) embed.add_field(name="Time", value=user_time.strftime( constants.DISPLAY_DATETIME_FORMAT), inline=False) embed.add_field(name="Activity", value=f"{activity}", inline=False) embed.add_field(name="Description", value=f"{description}", inline=False) embed.add_field(name="Link", value=link, inline=False) await ctx.send(embed=embed) self.activity_calendar_sheet.append_row([ ctx.guild.name, spreadsheet_time.strftime(constants.SHEET_DATETIME_FORMAT), activity, description, link ]) self.activity_calendar_sheet.sort( (activity_calendar_constants.SHEET_TIMESTAMP_COLUMN, 'asc'))
async def showactivitycalendar(self, ctx, timezone: str = constants.UTC): """Displays the current activity calendar Can add a timezone to display the times in that timezone ~showactivitycalendar CEST""" logging_utils.log_command("showactivitycalendar", ctx.channel, ctx.author) server_calendar_results = self.activity_calendar_sheet.findall( ctx.guild.name, in_column=activity_calendar_constants.SHEET_SERVER_COLUMN) description = "" for cell in server_calendar_results: row = self.activity_calendar_sheet.row_values(cell.row) date = time_utils.parse_date( row[activity_calendar_constants.SHEET_TIMESTAMP_COLUMN - 1], from_tz=constants.UTC, to_tz=timezone) description += f"\n\n{time_utils.replace_offset(date.strftime(constants.DISPLAY_DATETIME_FORMAT))}: " \ f"[{row[activity_calendar_constants.SHEET_ACTIVITY_COLUMN-1]}]({row[activity_calendar_constants.SHEET_LINK_COLUMN-1]})" embed = discord.Embed(title="Current Activity Calendar", description=description, color=constants.EMBED_COLOR) await ctx.send(embed=embed)
async def quibbler(self, ctx): """ Generic Quibbler info with links to: 1.) r/TheQuibbler 2.) The r/TheQuibbler submission form 3.) The Issue profile page where all the quibbler's are stored 4.) The Google Doc which has prompts for inspiration ~quib """ logging_utils.log_command("quibbler", ctx.channel, ctx.author) description = f"The Quibbler is a light-hearted, quirky, facts-optional online magazine where creativity " \ f"and wit are prized over accuracy and factuality. We exist to put smiles on peoples' faces, " \ f"including our own! New editions are published quarterly on our [Issuu page]({quibbler_constants.ISSUU_LINK})" \ f"\n\nJoin [r/TheQuibbler]({quibbler_constants.SUBREDDIT_LINK})" \ f"\n\nAll submissions go to this [Google Form]({quibbler_constants.SUBMISSION_LINK})" \ f"\n\nCheck out our inspiration prompt [Google Doc]({quibbler_constants.PROMPT_LIST_LINK})" \ f"\n\nFor more information, take a look at r/TheQuibbler's [Wiki]({quibbler_constants.SUBREDDIT_WIKI_LINK})" embed = discord.Embed(title=f"r/TheQuibbler Information and Links", color=constants.EMBED_COLOR, description=description) await ctx.send(embed=embed)
async def unixtime(self, ctx, *args): """Return the time given (or current time if no argument) in Unix format (1626206635) Usage: `~unixtime Tuesday, September 27, 2021 9pm EDT""" logging_utils.log_command("time", ctx.channel, ctx.author) embed = discord_utils.create_embed() if len(args) < 1: curr_time = int(datetime.timestamp(datetime.now())) embed.add_field(name="Success!", value=f"Current time is `{curr_time}`", inline=False) else: user_time = time_utils.parse_date(" ".join(args)) if user_time is None: embed.add_field(name=f"{constants.FAILED}!", value=f"Is {' '.join(args)} a valid time?", inline=False) await ctx.send(embed=embed) return unix_time = int(datetime.timestamp(user_time)) embed.add_field(name=f"{constants.SUCCESS}!", value=f"The Unix Time at {' '.join(args)} is `{unix_time}`", inline=False) await ctx.send(embed=embed)
async def dueling(self, ctx): """Get info for dueling ~dueling""" logging_utils.log_command("dueling", ctx.channel, ctx.author) embed = discord.Embed( title="Welcome to Discord Dueling!", color=constants.EMBED_COLOR, description= f"Get your Harry Potter trivia fill during no-points month right here!\n" f"We have several commands to give you full control over what kind of question " f"you want!\n\n" f"**Multiple Choice**: `{ctx.prefix}duelingmc`\n" f"**Name the BOOK and SPEAKER**: `{ctx.prefix}duelingquote`\n" f"**Random Question**: `{ctx.prefix}duelingrandom`\n" f"**Question from specific CATEGORY**:`{ctx.prefix}duelingcat <category>` " f"(use `{ctx.prefix}duelingcat` for available categories)\n" f"**Question from specific THEME**:`{ctx.prefix}duelingtheme <theme>` " f"(use `{ctx.prefix}duelingtheme` for available themes)\n\n" f"For now, I will always include the answer at the end in spoiler text so you " f"can check your answer. Feel free to put your answer in here, but be sure to " f"cover it with spoiler text! To use spoiler text, surround your answer " f"with \|\| e.g. \|\|answer\|\|") await ctx.send(embed=embed)
async def answer(self, ctx, *args): """ Check your answer Usage: ~answer <your answer> """ channel = ctx.channel.id logging_utils.log_command("answer", ctx.channel, ctx.author) # if the team isn't puzzling then we need to instruct them to use startpuzzle command first. if channel not in self.current_races: embed = discord_utils.create_embed() embed.add_field( name="No race!", value= "This channel doesn't have a race going on. You can't answer anything!", inline=False) embed.add_field( name="Start Race", value=f"To start a race, use {ctx.prefix}startrace", inline=False) await ctx.send(embed=embed) return print( f"All current answers: {self.current_races[channel][cipher_race_constants.ANSWERS]}" ) # Remove the command and whitespace from the answer. user_answer = ''.join(args) result = cipher_race_utils.get_answer_result( user_answer, self.current_races[channel][cipher_race_constants.ANSWERS]) if result == cipher_race_constants.CORRECT: await ctx.message.add_reaction( EMOJIS[cipher_race_constants.CORRECT_EMOJI]) else: await ctx.message.add_reaction( EMOJIS[cipher_race_constants.INCORRECT_EMOJI]) # We pop off the correct answers as they are given, so at some point current_answers will be an empty list. # If there are more answers left, don't do any of that level complete nonsense. if len(self.current_races[channel][ cipher_race_constants.ANSWERS]) >= 1: return # If there are no answers left for the round, then the team has completed the level # Create the next level prep embed embed = cipher_race_utils.create_level_prep_embed( self.current_races[channel][cipher_race_constants.LEVEL]) # Proceed to next level. Perform computation ahead of time. self.current_races[channel][cipher_race_constants.LEVEL] += 1 # Creates all cipher_race embeds, updates used cipher_race IDS, and refreshes current answers for the next level. if cipher_race_constants.CODE in self.current_races[channel]: embeds, self.current_races[channel][ cipher_race_constants. ANSWERS] = cipher_race_utils.create_code_embed( self.current_races[channel][cipher_race_constants.LEVEL], self.current_races[channel][cipher_race_constants.CODE], ctx.prefix) else: embeds, self.current_races[channel][ cipher_race_constants. ANSWERS] = cipher_race_utils.create_code_embed( self.current_races[channel][cipher_race_constants.LEVEL], self.codes, ctx.prefix) await ctx.send(embed=embed) Timer(cipher_race_constants.BREAK_TIME, self.start_new_level, callback_args=(ctx, channel, embeds), callback_async=True)