def _is_admin_or_mod(ctx): if ctx.message.author.id == owner: return True else: admin_roles = gs.get(ctx.guild).perm_roles["admin"] mod_roles = gs.get(ctx.guild).perm_roles["mod"] for role in ctx.author.roles: if role.id in mod_roles or role.id in admin_roles: return True return False
async def iam(self, ctx, *, role: discord.Role = None): """ Self-assign yourself a role. Only one role at a time. Usage: {command_prefix}iam [role] Example: .iam OverwatchPing """ settings = gs.get(ctx.guild) if role is None: # Hacky way to get the error I want from inspect import Parameter raise commands.MissingRequiredArgument(Parameter("Role", False)) if not settings.self_assign["enabled"]: embed = discord.Embed( colour=roxbot.EmbedColours.pink, description= "SelfAssignable roles are not enabled on this server") return await ctx.send(embed=embed) member = ctx.author if role in member.roles: return await ctx.send("You already have that role.") if role.id in settings.self_assign["roles"]: await member.add_roles(role, reason="'iam' command triggered.") return await ctx.send("Yay {}! You now have the {} role!".format( member.mention, role.name)) else: return await ctx.send("That role is not self-assignable.")
async def iamn(self, ctx, *, role: discord.Role = None): """ Remove a self-assigned role Usage: {command_prefix}iamn [role] Example: .iamn OverwatchPing """ settings = gs.get(ctx.guild) if role is None: from inspect import Parameter raise commands.MissingRequiredArgument(Parameter("role", False)) if not settings.self_assign["enabled"]: embed = discord.Embed( colour=roxbot.EmbedColours.pink, description= "SelfAssignable roles are not enabled on this server") return await ctx.send(embed=embed) member = ctx.author if role in member.roles and role.id in settings.self_assign["roles"]: await member.remove_roles(role, reason="'iamn' command triggered.") return await ctx.send("{} has been successfully removed.".format( role.name)) elif role not in member.roles and role.id in settings.self_assign[ "roles"]: return await ctx.send("You do not have {}.".format(role.name)) else: return await ctx.send("That role is not self-assignable.")
async def add(self, ctx, user: discord.User = None, *, warning=""): """Adds a warning to a user.""" # Warning in the settings is a dictionary of user ids. The user ids are equal to a list of dictionaries. settings = gs.get(ctx.guild) warning_limit = 2 warning_dict = { "warned-by": ctx.author.id, "date": time.time(), "warning": warning } user_id = str(user.id) if user_id not in settings.warnings: settings.warnings[user_id] = [] settings.warnings[user_id].append(warning_dict) settings.update(settings.warnings, "warnings") amount_warnings = len(settings.warnings[user_id]) if amount_warnings > warning_limit: await ctx.author.send( "{} has been reported {} time(s). This is a reminder that this is over the set limit of {}." .format(str(user), amount_warnings, warning_limit)) return await ctx.send("Reported {}.".format(str(user)))
async def skip(self, ctx, option=""): """Skips or votes to skip the current video. Use option "--force" if your an admin and """ voice = guild_settings.get(ctx.guild).voice if ctx.voice_client.is_playing(): if voice["skip_voting"] and not (option == "--force" and roxbot.checks._is_admin_or_mod(ctx)): # Admin force skipping if ctx.author in self.skip_votes[ctx.guild.id]: return await ctx.send("You have already voted to skip the current track.") else: self.skip_votes[ctx.guild.id].append(ctx.author) # -1 due to the bot being counted in the members generator ratio = len(self.skip_votes[ctx.guild.id]) / (len(ctx.voice_client.channel.members) - 1) needed_users = ceil((len(ctx.voice_client.channel.members) - 1) * voice["skip_ratio"]) if ratio >= voice["skip_ratio"]: await ctx.send("{} voted the skip the video.".format(ctx.author)) await ctx.send("Votes to skip now playing has been met. Skipping video...") self.skip_votes[ctx.guild.id] = [] else: await ctx.send("{} voted the skip the song.".format(ctx.author)) return await ctx.send("{}/{} votes required to skip the video. To vote, use the command `{}skip`".format(len(self.skip_votes[ctx.guild.id]), needed_users, ctx.prefix)) else: await ctx.send("Skipped video") # This should be fine as the queue_logic function should handle moving to the next song and all that. self.now_playing[ctx.guild.id] = None ctx.voice_client.stop() else: await ctx.send("I'm not playing anything.")
async def printsettings(self, ctx, option=None): """OWNER OR ADMIN ONLY: Prints the servers settings file.""" # TODO: Use paginator to make the output here not break all the time. config = guild_settings.get(ctx.guild) settings = dict(config.settings.copy( )) # Make a copy of settings so we don't change the actual settings. em = discord.Embed(colour=EmbedColours.pink) em.set_author(name="{} settings for {}.".format( self.bot.user.name, ctx.message.guild.name), icon_url=self.bot.user.avatar_url) if option in settings: raw = bool(ctx.invoked_with == "printsettingsraw") settingcontent = self.parse_setting(ctx, settings[option], raw=raw) em.add_field(name=option, value=settingcontent, inline=False) return await ctx.send(embed=em) else: for setting in settings: if setting != "custom_commands" and setting != "warnings": raw = bool(ctx.invoked_with == "printsettingsraw") settingcontent = self.parse_setting(ctx, settings[setting], raw=raw) em.add_field(name=setting, value=settingcontent, inline=False) elif setting == "custom_commands": em.add_field( name="custom_commands", value= "For Custom Commands, use the custom list command.", inline=False) return await ctx.send(embed=em)
async def log(guild, channel, command_name, **kwargs): logging = guild_settings.get(guild).logging if logging["enabled"]: embed = discord.Embed(title="{} command logging".format(command_name), colour=EmbedColours.pink) for key, value in kwargs.items(): embed.add_field(name=key, value=value) return await channel.send(embed=embed)
def nsfw_predicate(ctx): nsfw = gs.get(ctx.guild).nsfw if not nsfw["channels"] and nsfw["enabled"]: return nsfw["enabled"] == 1 elif nsfw["enabled"] and nsfw["channels"]: return ctx.channel.id in nsfw["channels"] else: return False
def predicate(ctx): if ctx.author.id == owner: return True else: for role in ctx.author.roles: if role.id in gs.get(ctx.guild).perm_roles["admin"]: return True return False
def _is_admin_or_mod(message): if message.author.id == roxbot.owner: return True perm_roles = gs.get(message.channel.guild).perm_roles for role in message.author.roles: if role.id in perm_roles.get("admin") or role.id in perm_roles.get( "mod"): return True return False
async def on_member_remove(self, member): # TODO: Add some way of detecting whether a user left/was kicked or was banned. logging = guild_settings.get(member.guild).logging if logging["enabled"]: channel = self.bot.get_channel(logging["channel"]) embed = discord.Embed( description="{} left the server".format(member), colour=EmbedColours.pink) return await channel.send(embed=embed)
async def on_member_remove(self, member): """ The same but the opposite """ settings = guild_settings.get(member.guild) channel = settings.goodbyes["goodbye-channel"] if not settings.goodbyes["enabled"]: return else: channel = self.bot.get_channel(channel) return await channel.send(embed=discord.Embed( description="{}#{} has left or been beaned.".format(member.name, member.discriminator), colour=roxbot.EmbedColours.pink))
async def list(self, ctx, *, user: roxbot.converters.UserConverter = None): """Lists all or just the warnings for one user.""" settings = gs.get(ctx.guild) if user is None: output = "" for member in settings.warnings: # Remove users with no warning here instead of remove cause im lazy if not settings.warnings[member]: settings.warnings.pop(member) else: member_obj = discord.utils.get(ctx.guild.members, id=int(member)) if member_obj: output += "{}: {} Warning(s)\n".format( str(member_obj), len(settings.warnings[member])) else: output += "{}: {} Warning(s)\n".format( member, len(settings.warnings[member])) if not output: return await ctx.send("No warnings on record.") return await ctx.send(output) user_id = str(user.id) if not settings.warnings.get(user_id): return await ctx.send( "This user doesn't have any warning on record.") if not settings.warnings[user_id]: settings.warnings.pop(user_id) settings.update(settings.warnings, "warnings") em = discord.Embed(title="Warnings for {}".format(str(user)), colour=roxbot.EmbedColours.pink) em.set_thumbnail(url=user.avatar_url) x = 1 userlist = settings.warnings[user_id] for warning in userlist: try: warned_by = str(await self.bot.get_user_info(warning["warned-by"])) except discord.ext.commands.CommandInvokeError: warned_by = warning["warned-by"] date = datetime.datetime.fromtimestamp( warning["date"]).strftime('%c') warn_reason = warning["warning"] em.add_field(name="Warning %s" % x, value="Warned by: {}\nTime: {}\nReason: {}".format( warned_by, date, warn_reason)) x += 1 return await ctx.send(embed=em)
def predicate(ctx): gs = guild_settings.get(ctx.guild) if gs.voice["need_perms"]: # Had to copy the admin or mod code cause it wouldn't work ;-; if ctx.message.author.id == roxbot.owner: return True else: admin_roles = gs.perm_roles["admin"] mod_roles = gs.perm_roles["mod"] for role in ctx.author.roles: if role.id in mod_roles or role.id in admin_roles: return True return False else: return True
async def prune(self, ctx, dry_run=0): """Purges banned users from the warn list. Add a 1 at the end to do a dry run.""" settings = gs.get(ctx.guild) warnings = settings.warnings.copy() count = 0 for ban in await ctx.guild.bans(): for user in warnings: if int(user) == ban.user.id: if dry_run == 0: settings.warnings.pop(user) count += 1 settings.update(settings.warnings, "warnings") return await ctx.send( "Purged {} banned users from the warn list.".format(count))
async def on_member_join(self, member): logging = guild_settings.get(member.guild).logging if logging["enabled"]: channel = self.bot.get_channel(logging["channel"]) embed = discord.Embed(title="{} joined the server".format(member), colour=EmbedColours.pink) embed.add_field(name="ID", value=member.id) embed.add_field(name="Mention", value=member.mention) embed.add_field(name="Date Account Created", value="{:%a %Y/%m/%d %H:%M:%S} UTC".format( member.created_at)) embed.add_field(name="Date Joined", value="{:%a %Y/%m/%d %H:%M:%S} UTC".format( member.joined_at)) embed.set_thumbnail(url=member.avatar_url) return await channel.send(embed=embed)
async def on_member_join(self, member): """ Greets users when they join a server. """ settings = guild_settings.get(member.guild) if not settings.greets["enabled"]: return if settings.greets["custom-message"]: message = settings.greets["custom-message"] else: message = settings.greets["default-message"] em = discord.Embed(title="Welcome to {}!".format(member.guild), description='Hey {}! Welcome to **{}**! {}'.format( member.mention, member.guild, message), colour=roxbot.EmbedColours.pink) em.set_thumbnail(url=member.avatar_url) channel = self.bot.get_channel(settings.greets["welcome-channel"]) return await channel.send(embed=em)
async def remove(self, ctx, user: roxbot.converters.UserConverter = None, index=None): """Removes one or all of the warnings for a user.""" user_id = str(user.id) settings = gs.get(ctx.guild) if index: try: index = int(index) index -= 1 settings.warnings[user_id].pop(index) if not settings.warnings[user_id]: settings.warnings.pop(user_id) settings.update(settings.warnings, "warnings") return await ctx.send("Removed Warning {} from {}".format( index + 1, str(user))) except Exception as e: if isinstance(e, IndexError): return await ctx.send(":warning: Index Error.") elif isinstance(e, KeyError): return await ctx.send( "Could not find user in warning list.") elif isinstance(e, ValueError): return await ctx.send("Please enter a valid index number.") else: raise e else: try: settings.warnings.pop(user_id) settings.update(settings.warnings, "warnings") return await ctx.send("Removed all warnings for {}".format( str(user))) except KeyError: return await ctx.send("Could not find user in warning list.")
async def logging(self, ctx, selection=None, *, changes=None): """Edits the logging settings. Options: enable/disable: Enable/disables logging. channel: sets the channel. """ selection = selection.lower() settings = guild_settings.get(ctx.guild) if selection == "enable": settings.logging["enabled"] = 1 await ctx.send("'logging' was enabled!") elif selection == "disable": settings.logging["enabled"] = 0 await ctx.send("'logging' was disabled :cry:") elif selection == "channel": channel = self.get_channel(ctx, changes) settings.logging["channel"] = channel.id await ctx.send("{} has been set as the logging channel!".format(channel.mention)) else: return await ctx.send("No valid option given.") return self.guild_settings.update(settings.logging, "logging")
async def log(guild, channel, command_name, **kwargs): """Logs activity internally for Roxbot. Will only do anything if the server enables internal logging. This is mostly used for logging when certain commands are used that can be an issue for admins. Esp when Roxbot outputs something that could break the rules, then deletes their message. Params ======= guild: discord.Guild Used to check if the guild has logging enabled channel: discord.TextChannel command_name: str kwargs: dict All kwargs and two other params will be added to the logging embed as fields, allowing you to customise the output """ logging = guild_settings.get(guild).logging if logging["enabled"]: embed = discord.Embed(title="{} command logging".format(command_name), colour=EmbedColours.pink) for key, value in kwargs.items(): embed.add_field(name=key, value=value) return await channel.send(embed=embed)
async def listroles(self, ctx): """ List's all roles that can be self-assigned on this server. Usage: {command_prefix}listroles """ settings = gs.get(ctx.guild) if not settings.self_assign["enabled"]: embed = discord.Embed( colour=roxbot.EmbedColours.pink, description= "SelfAssignable roles are not enabled on this server") return await ctx.send(embed=embed) roles = [] for role in settings.self_assign["roles"]: for serverrole in ctx.guild.roles: if role == serverrole.id: roles.append("**" + serverrole.name + "**") roles = '\n'.join(roles) embed = discord.Embed( colour=roxbot.EmbedColours.pink, description="The self-assignable roles for this server are: \n" + roles) return await ctx.send(embed=embed)
async def play(self, ctx, *, url, stream=False, from_queue=False): """Plays from a url or search query (almost anything youtube_dl supports)""" voice = guild_settings.get(ctx.guild).voice guild = ctx.guild # Checks if invoker is in voice with the bot. Skips admins and mods and owner. if not roxbot.checks._is_admin_or_mod(ctx) or from_queue: if not ctx.author.voice: raise commands.CommandError("You're not in the same voice channel as Roxbot.") if ctx.author.voice.channel != ctx.voice_client.channel: raise commands.CommandError("You're not in the same voice channel as Roxbot.") # For internal speed. This should make the playlist management quicker when play is being invoked internally. if isinstance(url, dict): video = url url = video.get("webpage_url") else: video = ytdl.extract_info(url, download=False) # Playlist and search handling. if 'entries' in video and video.get("extractor_key") != "YoutubeSearch": await ctx.send("Looks like you have given me a playlist. I will que up all {} videos in the playlist.".format(len(video.get("entries")))) data = dict(video) video = data["entries"].pop(0) for entry in data["entries"]: self._queue_song(ctx, entry, stream) elif 'entries' in video and video.get("extractor_key") == "YoutubeSearch": video = video["entries"][0] # Duration limiter handling if video.get("duration", 1) > voice["max_length"] and not roxbot.checks._is_admin_or_mod(ctx): raise commands.CommandError("Cannot play video, duration is bigger than the max duration allowed.") # Actual playing stuff section. # If not playing and not queuing, and not paused, play the song. Otherwise queue it. if (not ctx.voice_client.is_playing() and self.am_queuing[guild.id] is False) and not ctx.voice_client.is_paused(): self.am_queuing[guild.id] = True async with ctx.typing(): player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=stream, volume=self._volume[ctx.guild.id]) player.stream = stream player.queued_by = ctx.author self.now_playing[guild.id] = player self.am_queuing[guild.id] = False ctx.voice_client.play(player, after=lambda e: print('Player error: %s' % e) if e else None) # Create task to deal with what to do when the video ends or is skipped and how to handle the queue self.queue_logic[ctx.guild.id] = self.bot.loop.create_task(self._queue_logic(ctx)) embed = self._generate_np_embed(ctx.guild, "Now Playing") await ctx.send(embed=embed) else: # Queue the song as there is already a song playing or paused. self._queue_song(ctx, video, stream) # Sleep because if not, queued up things will send first and probably freak out users or something while self.am_queuing[guild.id] is True: await asyncio.sleep(0.5) embed = discord.Embed(description='Added "{}" to queue'.format(video.get("title")), colour=roxbot.EmbedColours.pink) await ctx.send(embed=embed)
def tag_blacklist(guild): blacklist = "" for tag in gs.get(guild).nsfw["blacklist"]: blacklist += "-{} ".format(tag) return blacklist
def self_assign(cls, guild): name = "Self Assign" settings = guild_settings.get(guild).self_assign params = settings.keys() return cls(name, settings, *params)
def isnt_anal(): return commands.check(lambda ctx: gs.get(ctx.guild).is_anal[ "y/n"] and nsfw_predicate(ctx) or not gs.get(ctx.guild).is_anal["y/n"])
def twitch(cls, guild): name = "Twitch" settings = guild_settings.get(guild).twitch params = settings.keys() return cls(name, settings, *params)
def voice(cls, guild): name = "Voice" settings = guild_settings.get(guild).voice params = settings.keys() return cls(name, settings, *params)
def gss(cls, guild): name = "GaySoundsShitposts" settings = guild_settings.get(guild).gss params = settings.keys() return cls(name, settings, *params)
async def subreddit(self, ctx, subreddit): """ Grabs an image or video (jpg, png, gif, gifv, webm, mp4) from the subreddit inputted. Example: {command_prefix}subreddit pics """ subreddit = subreddit.lower() links = await subreddit_request(subreddit) title = "" choice = {} if not links or not links.get("after") or links["children"][0]["kind"] == "t5": # Determine if response is valid return await ctx.send("Error ;-; That subreddit probably doesn't exist. Please check your spelling") url = "" x = 0 # While loop here to make sure that we check if there is any image posts in the links we have. If so, just take the first one. # Choosing a while loop here because, for some reason, the for loop would never exit till the end. Leading to slow times. while not url and x <= 20: choice = random.choice(links["children"])["data"] url = await parse_url(choice["url"]) if url: x_old = int(x) # If the url or id are in the cache, continue the loop. If not, proceed with the post. for cache in self.post_cache[ctx.guild.id]: if url in cache or choice["id"] in cache: x += 1 break if x_old != x: # Has x been incremented url = "" # Restart search for new post if x > 20: break continue title = "**{}** \nby /u/{} from /r/{}\n".format(unescape(choice["title"]), unescape(choice["author"]), subreddit) break else: x += 1 # Check if post is NSFW, and if it is and this channel doesn't past the NSFW check, then return with the error message. if choice["over_18"] and not roxbot.checks.nsfw_predicate(ctx): return await ctx.send("This server/channel doesn't have my NSFW stuff enabled. This extends to posting NFSW content from Reddit.") if not url: # If no image posts could be found with the for loop. return await ctx.send("I couldn't find any images from that subreddit.") # Put the post ID and url in the cache. The url is used in case of two posts having the same link. Handy to avoid crossposts getting through the cache. cache_amount = 10 post = (choice["id"], url) self.post_cache[ctx.guild.id].append(post) if len(self.post_cache[ctx.guild.id]) >= cache_amount: self.post_cache[ctx.guild.id].pop(0) if url.split("/")[-2] == "a": text = "This is an album, click on the link to see more.\n" else: text = "" if ctx.invoked_with == "subreddit": # Only log the command when it is this command being used. Not the inbuilt commands. logging = guild_settings.get(ctx.guild).logging log_channel = self.bot.get_channel(logging["channel"]) await roxbot.log( ctx.guild, log_channel, "subreddit", User=ctx.author, Subreddit=subreddit, Returned="<{}>".format(url), Channel=ctx.channel, Channel_Mention=ctx.channel.mention, Time="{:%a %Y/%m/%d %H:%M:%S} UTC".format(ctx.message.created_at) ) # Not using a embed here because we can't use video in rich embeds but they work in embeds now :/ output = await ctx.send(title + text + url) await roxbot.utils.delete_option(self.bot, ctx, output, self.bot.get_emoji(444410658101002261) or "❌")
async def settings(self, ctx): self.guild_settings = guild_settings.get(ctx.guild)