async def youtube_stream_task(self): """Check every 15 minutes if a YouTube channel is streaming live. If it is, send an announcement to the specified Discord channel.""" # A standard Google API key has 10.000 units per day # This task, with the minutes set to 10, costs approx. 14.832 units per day # This task, with the minutes set to 15, costs approx. 9.888 units per day try: discord_channel = self.bot.democraciv_guild_object.get_channel( config.YOUTUBE_ANNOUNCEMENT_CHANNEL) except AttributeError: print( f'[BOT] ERROR - I could not find the Democraciv Discord Server! Change "DEMOCRACIV_GUILD_ID" ' f'in the config to a server I am in or disable YouTube Stream announcements.' ) raise exceptions.GuildNotFoundError(config.DEMOCRACIV_GUILD_ID) if discord_channel is None: print( "[BOT] ERROR - The YOUTUBE_ANNOUNCEMENT_CHANNEL id in config.py is not a channel on the" " specified Democraciv guild.") raise exceptions.ChannelNotFoundError( config.YOUTUBE_ANNOUNCEMENT_CHANNEL) stream_data = await self.get_live_broadcast() if stream_data is None: return video_data = stream_data["items"][0] _title = video_data['snippet']['title'] _channel_title = video_data['snippet']['channelTitle'] _description = video_data['snippet']['description'] _thumbnail = video_data['snippet']['thumbnails']['high']['url'] _video_url = f'https://youtube.com/watch?v={video_data["id"]}' embed = self.bot.embeds.embed_builder( title= f"{config.YOUTUBE_LOGO_STREAM} {_channel_title} - Live on YouTube", description="", has_footer=False) embed.add_field(name="Title", value=f"[{_title}]({_video_url})", inline=False) embed.add_field(name="Description", value=self.reduce_youtube_description(_description), inline=False) if _thumbnail.startswith('https://'): embed.set_image(url=_thumbnail) if config.YOUTUBE_EVERYONE_PING_ON_STREAM: await discord_channel.send( f'@everyone {_channel_title} is live on YouTube!', embed=embed) else: await discord_channel.send(f'{_channel_title} is live on YouTube!', embed=embed)
def get_democraciv_channel( bot, channel: DemocracivChannel) -> typing.Optional[discord.TextChannel]: to_return = bot.democraciv_guild_object.get_channel(channel.value) if to_return is None: raise exceptions.ChannelNotFoundError(channel.name) return to_return
async def twitch_task(self): """Checks every 30 seconds if a stream is live on Twitch. If it is and has not been announced yet, send announcement and mod/executive reminders to specified Discord channel.""" try: channel = self.bot.democraciv_guild_object.get_channel(config.TWITCH_ANNOUNCEMENT_CHANNEL) except AttributeError: print(f'[BOT] ERROR - I could not find the Democraciv Discord Server! Change "DEMOCRACIV_GUILD_ID" ' f'in the config.py to a server I am in or disable Twitch announcements.') raise exceptions.GuildNotFoundError(config.DEMOCRACIV_GUILD_ID) if channel is None: print("[BOT] ERROR - The TWITCH_ANNOUNCEMENT_CHANNEL id in config.py is not a channel on the" " specified Democraciv guild.") raise exceptions.ChannelNotFoundError(config.TWITCH_ANNOUNCEMENT_CHANNEL) twitch_data = await self.check_twitch_livestream() if twitch_data == self.StreamStatus.OFFLINE: return elif twitch_data == self.StreamStatus.LIVE_AND_ANNOUNCED: return elif twitch_data[0] == self.StreamStatus.LIVE_AND_NOT_ANNOUNCED: embed = self.bot.embeds.embed_builder(title=f"{config.TWITCH_LOGO} {self.streamer} - " f"Live on Twitch", description="", has_footer=False, colour=0x984efc) embed.add_field(name="Title", value=twitch_data[2], inline=False) embed.add_field(name="Link", value=f"https://twitch.tv/{self.streamer}", inline=False) embed.set_image(url=twitch_data[3]) if config.TWITCH_EVERYONE_PING_ON_ANNOUNCEMENT: await channel.send(f'@everyone {self.streamer} is live on Twitch!', embed=embed) else: await channel.send(f'{self.streamer} is live on Twitch!', embed=embed) if config.TWITCH_ANNOUNCEMENT_TO_REDDIT: data = { "kind": "link", "nsfw": False, "sr": config.REDDIT_SUBREDDIT, "title": f"{self.streamer} is live on Twitch: {twitch_data[2]}", "spoiler": False, "resubmit": True, "url": f"https://twitch.tv/{self.streamer}" } await self.bot.reddit_api.post_to_reddit(data) # Send reminder about streaming rules to executive channel await self.streaming_rules_reminder() # Send reminder to moderation to export Twitch VOD await self.export_twitch_reminder()
async def streaming_rules_reminder(self): executive_channel = mk.get_democraciv_channel(self.bot, mk.DemocracivChannel.EXECUTIVE_CHANNEL) try: minister_role = mk.get_democraciv_role(self.bot, mk.DemocracivRole.MINISTER_ROLE) governor_role = mk.get_democraciv_role(self.bot, mk.DemocracivRole.GOVERNOR_ROLE) executive_proxy_role = mk.get_democraciv_role(self.bot, mk.DemocracivRole.EXECUTIVE_PROXY_ROLE) except exceptions.RoleNotFoundError: minister_role = governor_role = executive_proxy_role = None if executive_channel is None: raise exceptions.ChannelNotFoundError("executive") embed = self.bot.embeds.embed_builder(title="Streaming Guidelines", description="Looks like you're starting another game session. " "Remember these guidelines!") embed.add_field(name="Don't show the stream key", value="Never show the stream key or the DMs with the " "moderator that sent you the key on stream!", inline=False) embed.add_field(name="Introduce yourself", value="No one knows which voice belongs to whom! " "Introduce yourself with your name and position.", inline=False) embed.add_field(name="Keep it short", value="In the past, streams often were too long. Keep the stream " "short and don't waste time by starting the stream when not everyone " "is ready or the game is not even started yet!", inline=False) embed.add_field(name="Hand over the save-game", value="Remember to send the save-game to one of " "the moderators after the stream!", inline=False) embed.set_thumbnail(url="https://cdn.discordapp.com/attachments/423938725525979146/" "645394491133394966/01-twitch-logo.jpg") if minister_role is not None and governor_role is not None and executive_proxy_role is not None: await executive_channel.send(f"{minister_role.mention} {governor_role.mention} " f"{executive_proxy_role.mention}") await executive_channel.send(embed=embed)
async def welcome(self, ctx): """Add a welcome message that every new member will see once they join this server""" is_welcome_enabled = await self.bot.checks.is_welcome_message_enabled( ctx.guild.id) current_welcome_channel = await utils.get_welcome_channel( self.bot, ctx.guild) current_welcome_message = await self.bot.db.fetchval( "SELECT welcome_message FROM guilds WHERE id = $1", ctx.guild.id) if current_welcome_channel is None: current_welcome_channel = "-" else: current_welcome_channel = current_welcome_channel.mention if not current_welcome_message: current_welcome_message = "-" elif len(current_welcome_message) > 1024: current_welcome_message = "*The welcome message is too long to fit in here.*" embed = self.bot.embeds.embed_builder( title=f":wave: Welcome Messages on {ctx.guild.name}", description= f"React with the {config.GUILD_SETTINGS_GEAR} emoji to change" f" these settings.", has_footer=False) embed.add_field(name="Enabled", value=self.emojiy_settings(is_welcome_enabled)) embed.add_field(name="Welcome Channel", value=current_welcome_channel) embed.add_field(name="Welcome Message", value=current_welcome_message, inline=False) info_embed = await ctx.send(embed=embed) flow = Flow(self.bot, ctx) if await flow.gear_reaction_confirm(info_embed, 300): status_question = await ctx.send( "React with :white_check_mark: to enable welcome messages," " or with :x: to disable welcome messages.") reaction = await flow.get_yes_no_reaction_confirm( status_question, 240) if reaction is None: return if reaction: await self.bot.db.execute( "UPDATE guilds SET welcome = true WHERE id = $1", ctx.guild.id) await ctx.send(":white_check_mark: Enabled welcome messages.") # Get new welcome channel await ctx.send( ":information_source: Reply with the name of the welcome channel." ) channel_object = await flow.get_new_channel(240) if isinstance(channel_object, str): raise exceptions.ChannelNotFoundError(channel_object) status = await self.bot.db.execute( "UPDATE guilds SET welcome_channel = $2 WHERE id = $1", ctx.guild.id, channel_object.id) if status == "UPDATE 1": await ctx.send( f":white_check_mark: Set the welcome channel to {channel_object.mention}." ) # Get new welcome message await ctx.send( f":information_source: Reply with the message that should be sent to {channel_object.mention} " f"every time a new member joins.\n\nWrite `{{member}}` " f"to make the Bot mention the user.") welcome_message = await flow.get_text_input(300) if welcome_message: status = await self.bot.db.execute( "UPDATE guilds SET welcome_message = $2 WHERE id = $1", ctx.guild.id, welcome_message) if status == "UPDATE 1": await ctx.send( f":white_check_mark: Welcome message was set.") elif not reaction: await self.bot.db.execute( "UPDATE guilds SET welcome = false WHERE id = $1", ctx.guild.id) await ctx.send( ":white_check_mark: Welcome messages were disabled.") await self.bot.cache.update_guild_config_cache()
async def exclude(self, ctx, channel: str = None): """Exclude message edits & deletions in a channel from showing up in your server's log channel **Usage:** `-server exclude` to see all excluded channels `-server exclude <channel>` to add/remove a channel to/from the excluded channels list """ current_logging_channel = await utils.get_logging_channel( self.bot, ctx.guild) if current_logging_channel is None: return await ctx.send( ":x: This server currently has no logging channel." " Please set one with `-server logs`.") help_description = "Add or remove a channel to the excluded channels with:\n`-server exclude [channel_name]`\n" excluded_channels = await self.bot.db.fetchval( "SELECT logging_excluded FROM guilds WHERE id = $1", ctx.guild.id) if not channel: current_excluded_channels_by_name = [] if excluded_channels is None: return await ctx.send( "There are no from logging excluded channels on this server." ) for channel in excluded_channels: channel = self.bot.get_channel(channel) if channel is not None: current_excluded_channels_by_name.append(channel.mention) if not current_excluded_channels_by_name: current_excluded_channels_by_name = "There are no from logging excluded channels on this server." else: current_excluded_channels_by_name = '\n'.join( current_excluded_channels_by_name) embed = self.bot.embeds.embed_builder( title=f"Logging-Excluded Channels on {ctx.guild.name}", description=help_description, has_footer=False) embed.add_field(name="Excluded Channels", value=current_excluded_channels_by_name) return await ctx.send(embed=embed) else: try: channel_object = await commands.TextChannelConverter().convert( ctx, channel) except commands.BadArgument: raise exceptions.ChannelNotFoundError(channel) if not channel_object: raise exceptions.ChannelNotFoundError(channel) # Remove channel if channel_object.id in excluded_channels: remove_status = await self.bot.db.execute( "UPDATE guilds SET logging_excluded = array_remove(logging_excluded, $2 ) WHERE id = $1", ctx.guild.id, channel_object.id) if remove_status == "UPDATE 1": await self.bot.cache.update_guild_config_cache() return await ctx.send( f":white_check_mark: {channel_object.mention} is no longer excluded from" f" showing up in {current_logging_channel.mention}.") # Add channel add_status = await self.bot.db.execute( "UPDATE guilds SET logging_excluded = array_append(logging_excluded," " $2) WHERE id = $1", ctx.guild.id, channel_object.id) if add_status == "UPDATE 1": await ctx.send( f":white_check_mark: Excluded channel {channel_object.mention} from showing up in " f"{current_logging_channel.mention}.") await self.bot.cache.update_guild_config_cache()
async def logs(self, ctx): """Log important events like message edits & deletions and more to a specific channel""" is_logging_enabled = await self.bot.checks.is_logging_enabled( ctx.guild.id) current_logging_channel = await utils.get_logging_channel( self.bot, ctx.guild) if current_logging_channel is None: current_logging_channel = "-" else: current_logging_channel = current_logging_channel.mention embed = self.bot.embeds.embed_builder( title=f":spy: Event Logging on {ctx.guild.name}", description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji " f"to change these settings.", has_footer=False) embed.add_field(name="Enabled", value=self.emojiy_settings(is_logging_enabled)) embed.add_field(name="Log Channel", value=current_logging_channel) info_embed = await ctx.send(embed=embed) flow = Flow(self.bot, ctx) if await flow.gear_reaction_confirm(info_embed, 300): status_question = await ctx.send( "React with :white_check_mark: to enable logging, " "or with :x: to disable logging.") reaction = await flow.get_yes_no_reaction_confirm( status_question, 240) if reaction is None: return if reaction: await self.bot.db.execute( "UPDATE guilds SET logging = true WHERE id = $1", ctx.guild.id) await ctx.send(":white_check_mark: Event logging was enabled.") await ctx.send( ":information_source: Reply with the name of the channel" " that I should use to log all events to.") channel_object = await flow.get_new_channel(240) if isinstance(channel_object, str): raise exceptions.ChannelNotFoundError(channel_object) status = await self.bot.db.execute( "UPDATE guilds SET logging_channel = $2 WHERE id = $1", ctx.guild.id, channel_object.id) if status == "UPDATE 1": await ctx.send( f":white_check_mark: Set the logging channel to {channel_object.mention}." ) elif not reaction: await self.bot.db.execute( "UPDATE guilds SET logging = false WHERE id = $1", ctx.guild.id) await ctx.send(":white_check_mark: Event logging was disabled." ) await self.bot.cache.update_guild_config_cache()
async def youtube_upload_tasks(self): """Check every 10 minutes if the 3 last uploads of a YouTube channel are new. If at least one is, send an announcement to the specified Discord channel.""" # A standard Google API key has 10.000 units per day # This task, with the minutes set to 10, costs approx. 2160 units per day try: discord_channel = self.bot.democraciv_guild_object.get_channel( config.YOUTUBE_ANNOUNCEMENT_CHANNEL) except AttributeError: print( f'[BOT] ERROR - I could not find the Democraciv Discord Server! Change "DEMOCRACIV_GUILD_ID" ' f'in the config to a server I am in or disable YouTube Upload announcements.' ) raise exceptions.GuildNotFoundError(config.DEMOCRACIV_GUILD_ID) if discord_channel is None: print( "[BOT] ERROR - The YOUTUBE_ANNOUNCEMENT_CHANNEL id in config.py is not a channel on the" " specified Democraciv guild.") raise exceptions.ChannelNotFoundError( config.YOUTUBE_ANNOUNCEMENT_CHANNEL) youtube_data = await self.get_newest_upload() if youtube_data is None: return # Each check last 3 uploads in case we missed some in between for i in range(3): youtube_video = youtube_data["items"][i] _id = youtube_video["snippet"]["resourceId"]["videoId"] # Try to add post id to database status = await self.bot.db.execute( "INSERT INTO youtube_uploads (id) VALUES ($1) ON CONFLICT DO NOTHING", _id) # ID already in database -> post already seen if status == "INSERT 0 0": continue title = youtube_video['snippet']['title'] channel = youtube_video['snippet']['channelTitle'] description = youtube_video['snippet']['description'] thumbnail_url = youtube_video['snippet']['thumbnails']['high'][ 'url'] video_link = f"https://youtube.com/watch?v={_id}" if config.YOUTUBE_VIDEO_UPLOADS_TO_REDDIT: data = { "kind": "link", "nsfw": False, "sr": config.REDDIT_SUBREDDIT, "title": title, "spoiler": False, "url": video_link } await self.bot.reddit_api.post_to_reddit(data) embed = self.bot.embeds.embed_builder( title= f"{config.YOUTUBE_LOGO_UPLOAD} {channel} - New YouTube video uploaded", description="", has_footer=False, colour=0xff001b) embed.add_field(name="Title", value=f"[{title}]({video_link})", inline=False) embed.add_field(name="Description", value=self.reduce_youtube_description(description), inline=False) embed.set_image(url=thumbnail_url) await discord_channel.send(embed=embed)
async def reddit_task(self): """Checks every 60 seconds if the 5 newest reddit posts of a subreddit are new. If at least one is, send announcement to the specified Discord channel.""" try: channel = self.bot.democraciv_guild_object.get_channel( config.REDDIT_ANNOUNCEMENT_CHANNEL) except AttributeError: print( f'[BOT] ERROR - I could not find the Democraciv Discord Server! Change "DEMOCRACIV_GUILD_ID" ' f'in config.py to a server I am in or disable Reddit announcements.' ) raise exceptions.GuildNotFoundError(config.DEMOCRACIV_GUILD_ID) if channel is None: print( "[BOT] ERROR - The REDDIT_ANNOUNCEMENT_CHANNEL id in config.py is not a channel on the" " specified Democraciv guild.") raise exceptions.ChannelNotFoundError( config.REDDIT_ANNOUNCEMENT_CHANNEL) reddit_post_json = await self.get_newest_reddit_post() if reddit_post_json is None: return # Each check last 5 reddit posts in case we missed some in between for i in range(5): reddit_post = reddit_post_json["data"]["children"][i]["data"] _id = reddit_post['id'] # Try to add post id to database status = await self.bot.db.execute( "INSERT INTO reddit_posts (id) VALUES ($1) ON CONFLICT DO NOTHING", _id) # ID already in database -> post already seen if status == "INSERT 0 0": continue _title = reddit_post['title'] _author = f"u/{reddit_post['author']}" _comments_link = f"https://reddit.com{reddit_post['permalink']}" try: _thumbnail_url = reddit_post['preview']['images'][0]['source'][ 'url'] except KeyError: _thumbnail_url = reddit_post['thumbnail'] embed = self.bot.embeds.embed_builder( title=f"{config.REDDIT_LOGO} New post on r/{self.subreddit}", description="", has_footer=False, colour=0xff2d1c) embed.add_field(name="Thread", value=f"[{_title}]({_comments_link})", inline=False) embed.add_field(name="Author", value=f"{_author}", inline=False) if _thumbnail_url.startswith("https://"): _thumbnail_url = html.unescape(_thumbnail_url) embed.set_thumbnail(url=_thumbnail_url) await channel.send(embed=embed)