async def update_setting(self, ctx, setting_key: str, setting_value: str): """*(admin) Update guild setting: !update_setting key value*""" guild_id = ctx.guild.id settings = GuildSettingsDb.get(guild_id) if hasattr(settings, setting_key): old_value = getattr(settings, setting_key) setattr(settings, setting_key, setting_value) GuildSettingsDb.commit(settings) await ctx.send(f":white_check_mark: Updated `{setting_key}={setting_value}` from old value: `{old_value}`") else: await ctx.send(f":exclamation: Unrecognized setting key: `{setting_key}`. Use `!show_settings` for more info.")
async def create_puzzle_spreadsheet(self, text_channel: discord.TextChannel, puzzle: PuzzleData): guild_id = text_channel.guild.id name = self.cap_name(puzzle.name) round_name = self.cap_name(puzzle.round_name) if name == "meta": # Distinguish metas between different rounds name = f"{name} ({round_name})" settings = GuildSettingsDb.get(guild_id) if not settings.drive_parent_id: return try: # create drive folder if needed round_folder = await get_or_create_folder( name=round_name, parent_id=settings.drive_parent_id) round_folder_id = round_folder["id"] spreadsheet = await create_spreadsheet(agcm=self.agcm, title=name, folder_id=round_folder_id) puzzle.google_folder_id = round_folder_id puzzle.google_sheet_id = spreadsheet.id PuzzleJsonDb.commit(puzzle) # inform spreadsheet creation puzzle_url = puzzle.hunt_url sheet_url = urls.spreadsheet_url(spreadsheet.id) emoji = GuildSettingsDb.get_cached(guild_id).discord_bot_emoji embed = discord.Embed( description= f"{emoji} I've created a spreadsheet for you at {sheet_url}. " f"Check out the `Quick Links` tab for more info! " # NOTE: This next sentence might be better elsewhere, for now easy enough to add message here. f"I've assumed the puzzle page is {puzzle_url}, use `!link` to update if needed." ) await text_channel.send(embed=embed) # add some helpful links await self.add_quick_links_worksheet(spreadsheet, puzzle, settings) except Exception as exc: logger.exception( f"Unable to create spreadsheet for {round_name}/{name}") await text_channel.send( f":exclamation: Unable to create spreadsheet for {round_name}/{name}: {exc}" ) return return spreadsheet
async def refresh_nexus(self): """Ref: https://discordpy.readthedocs.io/en/latest/ext/tasks/""" for guild in self.bot.guilds: settings = GuildSettingsDb.get_cached(guild.id) if settings.drive_nexus_sheet_id: puzzles = PuzzleJsonDb.get_all(guild.id) await update_nexus(agcm=self.agcm, file_id=settings.drive_nexus_sheet_id, puzzles=puzzles)
async def check_is_bot_channel(self, ctx) -> bool: """Check if command was sent to bot channel configured in settings""" settings = GuildSettingsDb.get_cached(ctx.guild.id) if not settings.discord_bot_channel: # If no channel is designated, then all channels are fine # to listen to commands. return True if ctx.channel.name == settings.discord_bot_channel: # Channel name matches setting (note, channel name might not be unique) return True await ctx.send(f":exclamation: Most bot commands should be sent to #{settings.discord_bot_channel}") return False
async def create_puzzle_channel(self, ctx, round_name: str, puzzle_name: str): """Create new text channel for puzzle, and optionally a voice channel Save puzzle metadata to data_dir, send initial messages to channel, and create corresponding Google Sheet if GoogleSheets cog is set up. """ guild = ctx.guild category_name = self.clean_name(round_name) category = discord.utils.get(guild.categories, name=category_name) if category is None: raise ValueError(f"Round {category_name} not found") channel_name = self.clean_name(puzzle_name) text_channel, created_text = await self.get_or_create_channel( guild=guild, category=category, channel_name=channel_name, channel_type="text", reason=self.PUZZLE_REASON ) settings = GuildSettingsDb.get_cached(guild.id) if created_text: puzzle_data = PuzzleData( name=channel_name, round_name=category_name, round_id=category.id, guild_name=guild.name, guild_id=guild.id, channel_mention=text_channel.mention, channel_id=text_channel.id, start_time=datetime.datetime.now(tz=pytz.UTC), ) if settings.hunt_url: # NOTE: this is a heuristic and may need to be updated! # This is based on last year's URLs, where the URL format was # https://<site>/puzzle/puzzle_name hunt_url_base = settings.hunt_url.rstrip("/") if channel_name == "meta": # Use the round name in the URL hunt_name = category_name.lower().replace("-", settings.hunt_url_sep) else: hunt_name = channel_name.replace("-", settings.hunt_url_sep) puzzle_data.hunt_url = f"{hunt_url_base}/{hunt_name}" PuzzleJsonDb.commit(puzzle_data) await self.send_initial_puzzle_channel_messages(text_channel) gsheet_cog = self.bot.get_cog("GoogleSheets") # print("google sheets cog:", gsheet_cog) if gsheet_cog is not None: # update google sheet ID await gsheet_cog.create_puzzle_spreadsheet(text_channel, puzzle_data) else: puzzle_data = self.get_puzzle_data_from_channel(text_channel) created_voice = False if settings.discord_use_voice_channels: voice_channel, created_voice = await self.get_or_create_channel( guild=guild, category=category, channel_name=channel_name, channel_type="voice", reason=self.PUZZLE_REASON ) if created_voice: puzzle_data.voice_channel_id = voice_channel.id PuzzleJsonDb.commit(puzzle_data) created = created_text or created_voice if created: if created_text and created_voice: created_desc = "text and voice" # I'm sure there's a more elegant way to do this .. elif created_text: created_desc = "text" elif created_voice: created_desc = "voice" await ctx.send( f":white_check_mark: I've created new puzzle {created_desc} channels for {category.mention}: {text_channel.mention}" ) else: await ctx.send( f"I've found an already existing puzzle channel for {category.mention}: {text_channel.mention}" ) return (text_channel, voice_channel, created)
async def show_settings(self, ctx): """*(admin) Show guild-level settings*""" guild_id = ctx.guild.id settings = GuildSettingsDb.get(guild_id) await ctx.channel.send(f"```json\n{settings.to_json()}```")
def get_guild_settings_from_ctx(cls, ctx, use_cached: bool = True) -> GuildSettings: guild_id = ctx.guild.id if use_cached: return GuildSettingsDb.get_cached(guild_id) else: return GuildSettingsDb.get(guild_id)