Browse Source

All settings decentralisation done. perm_roles and is_anal depreciated. New convert setting "hide" added.

tags/v2.0.0
Roxie Gibson 5 years ago
parent
commit
49b97efb24
19 changed files with 507 additions and 626 deletions
  1. +3
    -1
      CHANGELOG.md
  2. +1
    -20
      main.py
  3. +16
    -0
      roxbot/__init__.py
  4. +3
    -38
      roxbot/checks.py
  5. +40
    -42
      roxbot/cogs/admin.py
  6. +7
    -3
      roxbot/cogs/customcommands.py
  7. +2
    -2
      roxbot/cogs/fun.py
  8. +24
    -0
      roxbot/cogs/gss.py
  9. +60
    -0
      roxbot/cogs/joinleave.py
  10. +62
    -0
      roxbot/cogs/nsfw.py
  11. +42
    -1
      roxbot/cogs/selfassign.py
  12. +2
    -2
      roxbot/cogs/trivia.py
  13. +32
    -4
      roxbot/cogs/twitch.py
  14. +68
    -11
      roxbot/cogs/voice.py
  15. +12
    -6
      roxbot/guild_settings.py
  16. +37
    -1
      roxbot/logging.py
  17. +0
    -4
      roxbot/settings/servers/.gitignore
  18. +0
    -491
      roxbot/settings/settings.py
  19. +96
    -0
      roxbot/system.py

+ 3
- 1
CHANGELOG.md View File

- easy setup and get that wiki going - easy setup and get that wiki going


### Big Changes ### Big Changes
- All settings have been decentralised between servers and cogs. All changes to the settings have been moved to independant commands. This doesn't effect custom commands or warnings.
- All settings have been decentralised between servers and cogs. All changes to the settings have been moved to independant commands. This doesn't effect custom commands or warnings.
- is_anal setting is now depreciated.`;suck` and `;spank` now only work in channels marked NSFW.
- perm_roles setting is now depreciated. All commands will work of Discord's permission system.


### Regular Updates ### Regular Updates
#### New Features #### New Features

+ 1
- 20
main.py View File

# REMEMBER TO UNCOMMENT THE GSS LINE, ROXIE # REMEMBER TO UNCOMMENT THE GSS LINE, ROXIE
# DO NOT UNCOMMENT GSS IF YOU ARE NOT ROXIE # DO NOT UNCOMMENT GSS IF YOU ARE NOT ROXIE


cogs = [
"roxbot.cogs.admin",
"roxbot.cogs.customcommands",
"roxbot.cogs.fun",
"roxbot.cogs.image",
"roxbot.cogs.joinleave",
"roxbot.cogs.nsfw",
"roxbot.cogs.reddit",
"roxbot.cogs.selfassign",
"roxbot.cogs.trivia",
"roxbot.cogs.twitch",
"roxbot.cogs.util",
"roxbot.cogs.voice",
#"roxbot.cogs.gss"
]



# Sets up Logging that discord.py does on its own # Sets up Logging that discord.py does on its own
logger = logging.getLogger('discord') logger = logging.getLogger('discord')
bot.load_extension("roxbot.system") bot.load_extension("roxbot.system")
print("system.py Loaded") print("system.py Loaded")


bot.load_extension("roxbot.settings.settings")
print("settings.py Loaded")

bot.load_extension("roxbot.err_handle") bot.load_extension("roxbot.err_handle")
print("err_handle.py Loaded") print("err_handle.py Loaded")




# Load Extension Cogs # Load Extension Cogs
print("Cogs Loaded:") print("Cogs Loaded:")
for cog in cogs:
for cog in roxbot.cogs:
try: try:
bot.load_extension(cog) bot.load_extension(cog)
print(cog.split(".")[2]) print(cog.split(".")[2])

+ 16
- 0
roxbot/__init__.py View File

__version__ = "2.0.0a" __version__ = "2.0.0a"


datetime_formatting = "{:%a %Y/%m/%d %H:%M:%S} UTC" datetime_formatting = "{:%a %Y/%m/%d %H:%M:%S} UTC"

cogs = [
"roxbot.cogs.admin",
"roxbot.cogs.customcommands",
"roxbot.cogs.fun",
"roxbot.cogs.image",
"roxbot.cogs.joinleave",
"roxbot.cogs.nsfw",
"roxbot.cogs.reddit",
"roxbot.cogs.selfassign",
"roxbot.cogs.trivia",
"roxbot.cogs.twitch",
"roxbot.cogs.util",
"roxbot.cogs.voice",
#"roxbot.cogs.gss"
]

+ 3
- 38
roxbot/checks.py View File



# TODO: Clean up this file. # TODO: Clean up this file.


def is_owner_or_admin():

def has_permission_or_owner(**perms):
def predicate(ctx): def predicate(ctx):
if ctx.author.id == roxbot.owner: if ctx.author.id == roxbot.owner:
return True return True
elif isinstance(ctx.channel, discord.DMChannel):
return False
else:
for role in ctx.author.roles:
if role.id in gs.get(ctx.guild)["admin"]["admin_roles"]:
return True
return False
return commands.has_permissions(**perms)
return commands.check(predicate) return commands.check(predicate)




def _is_admin_or_mod(ctx):
if ctx.message.author.id == roxbot.owner:
return True
elif isinstance(ctx.channel, discord.DMChannel):
return False
else:
admin_roles = gs.get(ctx.guild)["admin"]["admin_roles"]
mod_roles = gs.get(ctx.guild)["admin"]["mod_roles"]
for role in ctx.author.roles:
if role.id in mod_roles or role.id in admin_roles:
return True
return False


def is_admin_or_mod():
return commands.check(_is_admin_or_mod)


def nsfw_predicate(ctx): def nsfw_predicate(ctx):
if isinstance(ctx.channel, discord.DMChannel): if isinstance(ctx.channel, discord.DMChannel):
return False return False


def is_nfsw_enabled(): def is_nfsw_enabled():
return commands.check(lambda ctx: nsfw_predicate(ctx)) return commands.check(lambda ctx: nsfw_predicate(ctx))


def isnt_anal():
def predicate(ctx):
if isinstance(ctx.channel, discord.DMChannel):
return False
anal = gs.get(ctx.guild)["admin"]["is_anal"]
if not anal or (nsfw_predicate(ctx) and gs.get(ctx.guild).is_anal["y/n"]):
return True
else:
return False
return commands.check(predicate)

+ 40
- 42
roxbot/cogs/admin.py View File

self.bot = bot_client self.bot = bot_client
self.settings = { self.settings = {
"admin": { "admin": {
"convert": {"admin_roles": "role", "mod_roles": "role"},
"convert": {"warnings": "hide"},
"admin_roles": [], "admin_roles": [],
"mod_roles": [], "mod_roles": [],
"is_anal": 0, "is_anal": 0,
"warnings": {}, "warnings": {},
},
"logging": {
"enabled": 0,
"convert": {"enabled": "bool", "channel": "channel"},
"channel": 0
} }
} }
self.slow_mode = False self.slow_mode = False
pass pass


@commands.guild_only() @commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.has_permissions(manage_messages=True)
@commands.bot_has_permissions(manage_messages=True) @commands.bot_has_permissions(manage_messages=True)
@commands.command() @commands.command()
async def slowmode(self, ctx, seconds): async def slowmode(self, ctx, seconds):
"""Puts the current channel in slowmode.
"""Puts the current channel in slowmode. Requires the Manage Messages permission.
Usage: Usage:
;slowmode [time/"off"] ;slowmode [time/"off"]
seconds = number of seconds for the cooldown between messages a user has. seconds = number of seconds for the cooldown between messages a user has.
@commands.cooldown(1, 5) @commands.cooldown(1, 5)
@commands.command() @commands.command()
async def purge(self, ctx, limit=0, *, author: roxbot.converters.User=None): async def purge(self, ctx, limit=0, *, author: roxbot.converters.User=None):
"""Purges messages from the text channel.
"""Purges messages from the text channel. Requires the Manage Messages permission.
Limit = Limit of messages to be deleted Limit = Limit of messages to be deleted
Author (optional) = If given, roxbot will selectively only delete this user's messages.""" Author (optional) = If given, roxbot will selectively only delete this user's messages."""
# TODO: To sort out the limit == how many to delete for the author, and not just a limit. # TODO: To sort out the limit == how many to delete for the author, and not just a limit.
return await ctx.send(self.OK_PURGE_CONFIRMATION.format(len(messages))) return await ctx.send(self.OK_PURGE_CONFIRMATION.format(len(messages)))


@commands.guild_only() @commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.has_permissions(kick_members=True)
@commands.group(case_insensitive=True) @commands.group(case_insensitive=True)
async def warn(self, ctx): async def warn(self, ctx):
"""Group of commands handling warnings"""
"""Group of commands handling . Requires the Kick Members permission."""
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
return await ctx.send('Missing Argument') return await ctx.send('Missing Argument')


"""Adds a warning to a user.""" """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. # 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) settings = gs.get(ctx.guild)
warnings = settings["admin"]["warnings"]
warning_limit = 2 warning_limit = 2
warning_dict = { warning_dict = {
"warned-by": ctx.author.id, "warned-by": ctx.author.id,
} }
user_id = str(user.id) user_id = str(user.id)


if user_id not in settings.warnings:
settings.warnings[user_id] = []
if user_id not in warnings:
warnings[user_id] = []


warn_limit = 10 warn_limit = 10
if len(settings.warnings[user_id]) > warn_limit:
if len(warnings[user_id]) > warn_limit:
return await ctx.send(self.WARN_WARN_ADD_LIMIT_REACHED.format(warn_limit)) return await ctx.send(self.WARN_WARN_ADD_LIMIT_REACHED.format(warn_limit))


settings.warnings[user_id].append(warning_dict)
settings.update(settings.warnings, "warnings")
warnings[user_id].append(warning_dict)
settings["admin"]["warnings"] = warnings
settings.update(settings["admin"], "admin")


amount_warnings = len(settings.warnings[user_id])
amount_warnings = len(warnings[user_id])
if amount_warnings > warning_limit: if amount_warnings > warning_limit:
await ctx.author.send(self.OK_WARN_ADD_USER_LIMIT_DM.format(str(user), amount_warnings, warning_limit)) await ctx.author.send(self.OK_WARN_ADD_USER_LIMIT_DM.format(str(user), amount_warnings, warning_limit))


async def list(self, ctx, *, user: roxbot.converters.User=None): async def list(self, ctx, *, user: roxbot.converters.User=None):
"""Lists all or just the warnings for one user.""" """Lists all or just the warnings for one user."""
settings = gs.get(ctx.guild) settings = gs.get(ctx.guild)
warnings = settings["admin"]["warnings"]


if user is None: if user is None:
paginator = commands.Paginator() paginator = commands.Paginator()
for member in settings.warnings:
for member in warnings:
# Remove users with no warning here instead of remove cause im lazy # Remove users with no warning here instead of remove cause im lazy
if not settings.warnings[member]:
settings.warnings.pop(member)
if not warnings[member]:
warnings.pop(member)
else: else:
member_obj = discord.utils.get(ctx.guild.members, id=int(member)) member_obj = discord.utils.get(ctx.guild.members, id=int(member))
if member_obj: if member_obj:
paginator.add_line("{}: {} Warning(s)".format(str(member_obj), len(settings.warnings[member])))
paginator.add_line("{}: {} Warning(s)".format(str(member_obj), len(warnings[member])))
else: else:
paginator.add_line("{}: {} Warning(s)".format(member, len(settings.warnings[member])))
paginator.add_line("{}: {} Warning(s)".format(member, len(warnings[member])))
settings["admin"]["warnings"] = warnings
settings.update(settings["admin"], "admin")
if len(paginator.pages) <= 0: if len(paginator.pages) <= 0:
return await ctx.send(self.OK_WARN_LIST_NO_WARNINGS) return await ctx.send(self.OK_WARN_LIST_NO_WARNINGS)
for page in paginator.pages: for page in paginator.pages:
else: else:
user_id = str(user.id) user_id = str(user.id)


if not settings.warnings.get(user_id):
if not warnings.get(user_id):
return await ctx.send(self.OK_WARN_LIST_USER_NO_WARNINGS) return await ctx.send(self.OK_WARN_LIST_USER_NO_WARNINGS)


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 = discord.Embed(title="Warnings for {}".format(str(user)), colour=roxbot.EmbedColours.pink)
em.set_thumbnail(url=user.avatar_url) em.set_thumbnail(url=user.avatar_url)


x = 1 x = 1
userlist = settings.warnings[user_id]
userlist = warnings[user_id]
for warning in userlist: for warning in userlist:
try: try:
warned_by = str(await self.bot.get_user_info(warning["warned-by"])) warned_by = str(await self.bot.get_user_info(warning["warned-by"]))
"""Removes one or all of the warnings for a user.""" """Removes one or all of the warnings for a user."""
user_id = str(user.id) user_id = str(user.id)
settings = gs.get(ctx.guild) settings = gs.get(ctx.guild)
warnings = settings["admin"]["warnings"]


if index: if index:
try: try:
index = int(index) index = int(index)
index -= 1 index -= 1
settings.warnings[user_id].pop(index)
if not settings.warnings[user_id]:
settings.warnings.pop(user_id)
warnings[user_id].pop(index)
if not warnings[user_id]:
warnings.pop(user_id)


settings.update(settings.warnings, "warnings")
settings["admin"]["warnings"] = warnings
settings.update(settings["admin"], "admin")
return await ctx.send(self.OK_WARN_REMOVE_REMOVED_WARNING.format(index+1, str(user))) return await ctx.send(self.OK_WARN_REMOVE_REMOVED_WARNING.format(index+1, str(user)))


except Exception as e: except Exception as e:
if isinstance(e, IndexError): if isinstance(e, IndexError):
return await ctx.send(self.ERROR_WARN_REMOVE_INDEXERROR.format(len(settings.warnings[user_id])))
return await ctx.send(self.ERROR_WARN_REMOVE_INDEXERROR.format(len(settings["warnings"][user_id])))
elif isinstance(e, KeyError): elif isinstance(e, KeyError):
return await ctx.send(self.WARN_WARN_REMOVE_USER_NOT_FOUND.format(str(user))) return await ctx.send(self.WARN_WARN_REMOVE_USER_NOT_FOUND.format(str(user)))
elif isinstance(e, ValueError): elif isinstance(e, ValueError):
raise e raise e
else: else:
try: try:
settings.warnings.pop(user_id)
settings.update(settings.warnings, "warnings")
warnings.pop(user_id)
settings["admin"]["warnings"] = warnings
settings.update(settings["admin"], "admin")
return await ctx.send(self.OK_WARN_REMOVE_REMOVED_WARNINGS.format(str(user))) return await ctx.send(self.OK_WARN_REMOVE_REMOVED_WARNINGS.format(str(user)))
except KeyError: except KeyError:
return await ctx.send(self.WARN_WARN_REMOVE_USER_NOT_FOUND.format(str(user))) return await ctx.send(self.WARN_WARN_REMOVE_USER_NOT_FOUND.format(str(user)))
async def prune(self, ctx, dry_run=0): 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.""" """Purges banned users from the warn list. Add a 1 at the end to do a dry run."""
settings = gs.get(ctx.guild) settings = gs.get(ctx.guild)
warnings = settings.warnings.copy()
warnings = settings["admin"]["warnings"].copy()
count = 0 count = 0
for ban in await ctx.guild.bans(): for ban in await ctx.guild.bans():
for user in warnings: for user in warnings:
if int(user) == ban.user.id: if int(user) == ban.user.id:
if dry_run == 0: if dry_run == 0:
settings.warnings.pop(user)
settings["admin"]["warnings"].pop(user)
count += 1 count += 1
settings.update(settings.warnings, "warnings")
settings.update(settings["admin"], "admin")
return await ctx.send(self.OK_WARN_PRUNE_PRUNED.format(count)) return await ctx.send(self.OK_WARN_PRUNE_PRUNED.format(count))


@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(kick_members=True) @commands.bot_has_permissions(kick_members=True)
@commands.command() @commands.command()
async def kick(self, ctx, member: discord.Member, *, reason=""): async def kick(self, ctx, member: discord.Member, *, reason=""):
"""Kicks mentioned user. Allows you to give a reason."""
"""Kicks mentioned user. Allows you to give a reason. Requires the Kick Members permission."""
try: try:
await member.kick(reason=reason) await member.kick(reason=reason)
return await ctx.send(self.OK_MOD_ACTION.format("Kicked", member, reason)) return await ctx.send(self.OK_MOD_ACTION.format("Kicked", member, reason))
@commands.bot_has_permissions(ban_members=True) @commands.bot_has_permissions(ban_members=True)
@commands.command() @commands.command()
async def ban(self, ctx, member: discord.Member, *, reason=""): async def ban(self, ctx, member: discord.Member, *, reason=""):
"""Bans mentioned user. Allows you to give a reason."""
"""Bans mentioned user. Allows you to give a reason. Requires the Ban Members permission."""
try: try:
await member.ban(reason=reason, delete_message_days=0) await member.ban(reason=reason, delete_message_days=0)
return await ctx.send(self.OK_MOD_ACTION.format("Banned", member, reason)) return await ctx.send(self.OK_MOD_ACTION.format("Banned", member, reason))
@commands.bot_has_permissions(ban_members=True) @commands.bot_has_permissions(ban_members=True)
@commands.command() @commands.command()
async def unban(self, ctx, member: roxbot.converters.User, *, reason=""): async def unban(self, ctx, member: roxbot.converters.User, *, reason=""):
"""Unbans user with given ID. Allows you to give a reason."""
"""Unbans user with given ID. Allows you to give a reason. Requires the Ban Members permission."""
ban = await ctx.guild.get_ban(member) ban = await ctx.guild.get_ban(member)
mem = ban.user mem = ban.user
if mem is None: if mem is None:
# TODO: For issues like this, make BadArgument a negative response embed not an error
return await ctx.send(self.WARN_UNBAN_NOTFOUND) return await ctx.send(self.WARN_UNBAN_NOTFOUND)
try: try:
await ctx.guild.unban(mem, reason=reason) await ctx.guild.unban(mem, reason=reason)

+ 7
- 3
roxbot/cogs/customcommands.py View File

"custom_commands": { "custom_commands": {
"0": {}, "0": {},
"1": {}, "1": {},
"2": {}
"2": {},
"convert": {"0": "hide", "1": "hide", "2": "hide"}
} }
} }


command_output = self._get_output(settings["custom_commands"]["0"][command]) command_output = self._get_output(settings["custom_commands"]["0"][command])
return await channel.send(command_output) return await channel.send(command_output)


@commands.has_permissions(manage_messages=True)
@commands.guild_only() @commands.guild_only()
@commands.group(aliases=["cc"]) @commands.group(aliases=["cc"])
@roxbot.checks.is_owner_or_admin()
async def custom(self, ctx): async def custom(self, ctx):
""""A group of commands to manage custom commands for your server."""
""""
A group of commands to manage custom commands for your server.
Requires the Manage Messages permission.
"""
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
return await ctx.send('Missing Argument') return await ctx.send('Missing Argument')



+ 2
- 2
roxbot/cogs/fun.py View File

response += '\n' response += '\n'
return await ctx.send(response) return await ctx.send(response)


@roxbot.checks.isnt_anal()
@commands.is_nsfw()
@commands.command() @commands.command()
async def spank(self, ctx, *, user: discord.User = None): async def spank(self, ctx, *, user: discord.User = None):
""" """
return await ctx.send("You didn't mention someone for me to spank") return await ctx.send("You didn't mention someone for me to spank")
return await ctx.send(":peach: :wave: *{} spanks {}*".format(self.bot.user.name, user.name)) return await ctx.send(":peach: :wave: *{} spanks {}*".format(self.bot.user.name, user.name))


@roxbot.checks.isnt_anal()
@commands.is_nsfw()
@commands.command(aliases=["succ"]) @commands.command(aliases=["succ"])
async def suck(self, ctx, *, user: discord.User = None): async def suck(self, ctx, *, user: discord.User = None):
""" """

+ 24
- 0
roxbot/cogs/gss.py View File

return ctx.send("Error, message roxie thanks.") return ctx.send("Error, message roxie thanks.")
return await ctx.invoke(self.perms, role=arg) return await ctx.invoke(self.perms, role=arg)


@commands.command()
async def gss(self, ctx, selection=None, *, changes=None):
"""Custom Cog for the GaySoundsShitposts Discord Server."""
selection = selection.lower()
settings = roxbot.guild_settings.get(ctx.guild)
gss = settings["gss"]

if selection == "loggingchannel":
if ctx.message.channel_mentions:
channel = ctx.channel_mentions[0]
else:
channel = self.bot.get_channel(changes)
gss["log_channel"] = channel.id
await ctx.send("Logging Channel set to '{}'".format(channel.name))
elif selection == "requireddays":
gss["required_days"] = int(changes)
await ctx.send("Required days set to '{}'".format(str(changes)))
elif selection == "requiredscore":
gss["required_score"] = int(changes)
await ctx.send("Required score set to '{}'".format(str(changes)))
else:
return await ctx.send("No valid option given.")
return settings.update(gss, "gss")



def setup(bot_client): def setup(bot_client):
bot_client.add_cog(GaySoundsShitposts(bot_client)) bot_client.add_cog(GaySoundsShitposts(bot_client))

+ 60
- 0
roxbot/cogs/joinleave.py View File

""" """




import typing
import discord import discord
from discord.ext import commands

import roxbot import roxbot
from roxbot import guild_settings from roxbot import guild_settings


return await channel.send(embed=discord.Embed( return await channel.send(embed=discord.Embed(
description="{}#{} has left or been beaned.".format(member.name, member.discriminator), colour=roxbot.EmbedColours.pink)) description="{}#{} has left or been beaned.".format(member.name, member.discriminator), colour=roxbot.EmbedColours.pink))


@commands.has_permissions(manage_messages=True)
@commands.command()
async def greets(self, ctx, setting, channel: typing.Optional[discord.TextChannel] = None, *, text: str):
"""Edits settings for the Welcome Messages

Options:
enable/disable: Enable/disables parts of the cog. Needs to specify which part.
channel: Sets the channels for either option. Must be a ID or mention.
message: specifies a custom message for the greet messages.
"""
setting = setting.lower()
settings = guild_settings.get(ctx.guild)
greets = settings["greets"]
if setting == "enable":
greets["enabled"] = 1
await ctx.send("'greets' was enabled!")
elif setting == "disable":
greets["enabled"] = 0
await ctx.send("'greets' was disabled :cry:")
elif setting == "channel":
if channel is None:
channel = ctx.channel
greets["welcome-channel"] = channel.id
elif setting == "custommessage":
greets["custom-message"] = text
await ctx.send("Custom message set to '{}'".format(text))
else:
return await ctx.send("No valid option given.")
return settings.update(greets, "greets")


@commands.has_permissions(manage_messages=True)
@commands.command()
async def goodbyes(self, ctx, setting, *, channel: typing.Optional[discord.TextChannel] = None):
"""Edits settings for the Welcome Messages

Options:
enable/disable: Enable/disables parts of the cog. Needs to specify which part.
channel: Sets the channels for either option. Must be a ID or mention.
message: specifies a custom message for the greet messages.
"""
setting = setting.lower()
settings = guild_settings.get(ctx.guild)
goodbyes = settings["goodbyes"]
if setting == "enable":
goodbyes["enabled"] = 1
await ctx.send("'goodbyes' was enabled!")
elif setting == "disable":
goodbyes["enabled"] = 0
await ctx.send("'goodbyes' was disabled :cry:")
elif setting == "channel":
if channel is None:
channel = ctx.channel
goodbyes["goodbye-channel"] = channel.id
else:
return await ctx.send("No valid option given.")
return settings.update(goodbyes, "goodbyes")


def setup(Bot): def setup(Bot):
Bot.add_cog(JoinLeave(Bot)) Bot.add_cog(JoinLeave(Bot))

+ 62
- 0
roxbot/cogs/nsfw.py View File

""" """




import typing
import random import random
import discord import discord
from discord.ext import commands from discord.ext import commands
def __init__(self, bot_client): def __init__(self, bot_client):
self.bot = bot_client self.bot = bot_client
self.cache = {} self.cache = {}
self.settings = {
"nsfw": {
"enabled": 0,
"channels": [],
"convert": {"enabled": "bool", "channels": "channel"},
"blacklist": []
}
}


@roxbot.checks.is_nfsw_enabled() @roxbot.checks.is_nfsw_enabled()
@commands.command(hidden=True) @commands.command(hidden=True)
post_url = "https://simg3.gelbooru.com/images/" post_url = "https://simg3.gelbooru.com/images/"
return await ctx.invoke(self.gelbooru_clone, base_url=base_url, post_url=post_url, tags=tags) return await ctx.invoke(self.gelbooru_clone, base_url=base_url, post_url=post_url, tags=tags)


@commands.command()
async def nsfw(self, ctx, setting, channel: typing.Optional[discord.TextChannel] = None, *, changes=None):
"""Edits settings for the nsfw cog and other nsfw commands.
If nsfw is enabled and nsfw channels are added, the bot will only allow nsfw commands in the specified channels.

Options:
enable/disable: Enable/disables nsfw commands.
addchannel/removechannel: Adds/Removes a nsfw channel.
addbadtag/removebadtag: Add/Removes blacklisted tags so that you can avoid em with the commands.
Example:
;settings nsfw addchannel #nsfw_stuff
"""
setting = setting.lower()
settings = roxbot.guild_settings.get(ctx.guild)
nsfw = settings["nsfw"]

if setting == "enable":
nsfw["enabled"] = 1
await ctx.send("'nsfw' was enabled!")
elif setting == "disable":
nsfw["enabled"] = 0
await ctx.send("'nsfw' was disabled :cry:")
elif setting == "addchannel":
if not channel and not changes:
channel = ctx.channel
if channel.id not in nsfw["channels"]:
nsfw["channels"].append(channel.id)
await ctx.send("'{}' has been added to the nsfw channel list.".format(channel.name))
else:
return await ctx.send("'{}' is already in the list.".format(channel.name))
elif setting == "removechannel":
if not channel and not changes:
channel = ctx.channel
try:
nsfw["channels"].remove(channel.id)
await ctx.send("'{}' has been removed from the nsfw channel list.".format(channel.name))
except ValueError:
return await ctx.send("That role was not in the list.")
elif setting == "addbadtag":
if changes not in nsfw["blacklist"]:
nsfw["blacklist"].append(changes)
await ctx.send("'{}' has been added to the blacklisted tag list.".format(changes))
else:
return await ctx.send("'{}' is already in the list.".format(changes))
elif setting == "removebadtag":
try:
nsfw["blacklist"].remove(changes)
await ctx.send("'{}' has been removed from the blacklisted tag list.".format(changes))
except ValueError:
return await ctx.send("That tag was not in the blacklisted tag list.")
else:
return await ctx.send("No valid option given.")
return settings.update(nsfw, "nsfw")


def setup(bot_client): def setup(bot_client):
bot_client.add_cog(NFSW(bot_client)) bot_client.add_cog(NFSW(bot_client))

+ 42
- 1
roxbot/cogs/selfassign.py View File

SOFTWARE. SOFTWARE.
""" """



import discord import discord
from discord.ext import commands from discord.ext import commands


sa["roles"].remove(role.id) sa["roles"].remove(role.id)
return settings.update(sa, "self_assign") return settings.update(sa, "self_assign")


@commands.has_permissions(manage_roles=True)
@commands.guild_only()
@commands.command(aliases=["sa"])
async def selfassign(self, ctx, setting, *, role: discord.Role):
"""Edits settings for self assign cog. Requires Manage Roles permission.

Options:
enable/disable: Enable/disables the cog.
add/remove: adds or removes a role that can be self assigned in the server.
"""
settings = roxbot.guild_settings.get(ctx.guild)
self_assign = settings["self_assign"]
setting = setting.lower()


if setting == "enable":
self_assign["enabled"] = 1
await ctx.send("'self_assign' was enabled!")
elif setting == "disable":
self_assign["enabled"] = 0
await ctx.send("'self_assign' was disabled :cry:")
elif setting == "add":
try:
if role.id in self_assign["roles"]:
return await ctx.send("{} is already a self-assignable role.".format(role.name))
self_assign["roles"].append(role.id)
await ctx.send('Role "{}" added'.format(str(role)))
except AttributeError:
raise commands.BadArgument("Could not find that role.")
elif setting == "remove":
try:
if role.id in self_assign["roles"]:
self_assign["roles"].remove(role.id)
await ctx.send('"{}" has been removed from the self-assignable roles.'.format(str(role)))
else:
return await ctx.send("That role was not in the list.")
except AttributeError:
raise commands.BadArgument("Could not find that role.")
else:
return await ctx.send("No valid option given.")
return settings.update(self_assign, "self_assign")

@commands.guild_only() @commands.guild_only()
@commands.command(pass_context=True) @commands.command(pass_context=True)
async def listroles(self, ctx): async def listroles(self, ctx):

+ 2
- 2
roxbot/cogs/trivia.py View File

else: else:
await ctx.send(embed=discord.Embed(description="Game isn't being played here.", colour=self.error_colour)) await ctx.send(embed=discord.Embed(description="Game isn't being played here.", colour=self.error_colour))


@checks.is_admin_or_mod()
@commands.has_permissions(manage_channels=True)
@trivia.command() @trivia.command()
async def kick(self, ctx, user: discord.Member): async def kick(self, ctx, user: discord.Member):
"""Mod command to kick users out of the game. Useful if a user is AFK."""
"""Mod command to kick users out of the game. Useful if a user is AFK. Requires Manage Channels permission."""
channel = ctx.channel channel = ctx.channel
player = user player = user
if channel.id in self.games: if channel.id in self.games:

+ 32
- 4
roxbot/cogs/twitch.py View File

""" """




from discord import ActivityType
import typing
import discord
from discord.ext import commands from discord.ext import commands


import roxbot import roxbot
return return


if member_a.activitiy: if member_a.activitiy:
if member_a.activity.type == ActivityType.streaming and member_b.activity.type != ActivityType.streaming:
if member_a.activity.type == discord.ActivityType.streaming and member_b.activity.type != discord.ActivityType.streaming:
if not twitch["whitelist"]["enabled"] or member_a.id in twitch["whitelist"]["list"]: if not twitch["whitelist"]["enabled"] or member_a.id in twitch["whitelist"]["list"]:
channel = self.bot.get_channel(twitch["channel"]) channel = self.bot.get_channel(twitch["channel"])
return await channel.send(":video_game:** {} is live!** :video_game:\n{}\n{}".format( return await channel.send(":video_game:** {} is live!** :video_game:\n{}\n{}".format(
member_a.name, member_a.game.name, member_a.game.url)) member_a.name, member_a.game.name, member_a.game.url))


@commands.group()
@roxbot.checks.is_admin_or_mod()
@commands.group(aliases=["wl"])
@commands.has_permissions(manage_channels=True)
async def whitelist(self, ctx): async def whitelist(self, ctx):
"""Command group that handles the twitch cog's whitelist.""" """Command group that handles the twitch cog's whitelist."""
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
elif option == 'list': elif option == 'list':
return await ctx.send(settings["twitch"]["whitelist"]["list"]) return await ctx.send(settings["twitch"]["whitelist"]["list"])


@commands.has_permissions(manage_channels=True)
@commands.command()
async def twitch(self, ctx, setting, *, channel: discord.TextChannel = None):
"""Edits settings for self assign cog.

Options:
enable/disable: Enable/disables the cog.
channel: Sets the channel to shill in.
"""

setting = setting.lower()
settings = roxbot.guild_settings.get(ctx.guild)
twitch = settings["twitch"]

if setting == "enable":
twitch["enabled"] = 1
await ctx.send("'twitch' was enabled!")
elif setting == "disable":
twitch["enabled"] = 0
await ctx.send("'twitch' was disabled :cry:")
elif setting == "channel":
twitch["channel"] = channel.id
await ctx.send("{} has been set as the twitch shilling channel!".format(channel.mention))
else:
return await ctx.send("No valid option given.")
return settings.update(twitch, "twitch")



def setup(bot_client): def setup(bot_client):
bot_client.add_cog(Twitch(bot_client)) bot_client.add_cog(Twitch(bot_client))

+ 68
- 11
roxbot/cogs/voice.py View File

from discord.ext import commands from discord.ext import commands


import roxbot import roxbot
from roxbot import guild_settings




def _clear_cache(): def _clear_cache():


def volume_perms(): def volume_perms():
def predicate(ctx): def predicate(ctx):
gs = guild_settings.get(ctx.guild)
gs = roxbot.guild_settings.get(ctx.guild)
if gs["voice"]["need_perms"]: # Had to copy the admin or mod code cause it wouldn't work ;-; 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: if ctx.message.author.id == roxbot.owner:
return True return True
embed.set_footer(text="{}/{} | Volume: {}%".format(time_played, duration, int(self.now_playing[guild.id].volume*100))) embed.set_footer(text="{}/{} | Volume: {}%".format(time_played, duration, int(self.now_playing[guild.id].volume*100)))
return embed return embed


@roxbot.checks.is_admin_or_mod()
@roxbot.checks.has_permission_or_owner(manage_channels=True)
@commands.guild_only() @commands.guild_only()
@commands.command() @commands.command()
async def join(self, ctx, *, channel: discord.VoiceChannel = None): async def join(self, ctx, *, channel: discord.VoiceChannel = None):
async def play(self, ctx, *, url, stream=False, from_queue=False, queued_by=None): async def play(self, ctx, *, url, stream=False, from_queue=False, queued_by=None):
"""Plays from a url or search query (almost anything youtube_dl supports)""" """Plays from a url or search query (almost anything youtube_dl supports)"""
guild = ctx.guild guild = ctx.guild
voice = guild_settings.get(guild).get("voice")
voice = roxbot.guild_settings.get(guild).get("voice")


# Checks if invoker is in voice with the bot. Skips admins and mods and owner and if the song was queued previously. # Checks if invoker is in voice with the bot. Skips admins and mods and owner and if the song was queued previously.
if not (roxbot.checks._is_admin_or_mod(ctx) or from_queue): if not (roxbot.checks._is_admin_or_mod(ctx) or from_queue):
@commands.guild_only() @commands.guild_only()
@commands.command() @commands.command()
async def skip(self, ctx, option=""): 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"]
"""Skips or votes to skip the current video. Can use option "--force" if you have manage_channels permission. """
voice = roxbot.guild_settings.get(ctx.guild)["voice"]
if ctx.voice_client.is_playing(): 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 voice["skip_voting"] and not (option == "--force" and ctx.author.guild_permissions.manage_channels): # Admin force skipping
if ctx.author in self.skip_votes[ctx.guild.id]: if ctx.author in self.skip_votes[ctx.guild.id]:
return await ctx.send("You have already voted to skip the current track.") return await ctx.send("You have already voted to skip the current track.")
else: else:
await ctx.send(embed=page) await ctx.send(embed=page)


@commands.guild_only() @commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.has_permissions(manage_channels=True)
@commands.command() @commands.command()
async def remove(self, ctx, index): async def remove(self, ctx, index):
"""Removes a item from the queue with the given index. Can also input all to delete all queued items."""
"""Removes a item from the queue with the given index. Can also input all to delete all queued items. Requires the Manage Channels permission"""
# Try and convert index into an into. If not possible, just move forward # Try and convert index into an into. If not possible, just move forward
try: try:
index = int(index) index = int(index)
raise commands.CommandError("Valid Index not given.") raise commands.CommandError("Valid Index not given.")


@commands.guild_only() @commands.guild_only()
@roxbot.checks.is_admin_or_mod()
@commands.has_permissions(manage_channels=True)
@commands.command(alaises=["disconnect"]) @commands.command(alaises=["disconnect"])
async def stop(self, ctx): async def stop(self, ctx):
"""Stops and disconnects the bot from voice."""
"""Stops and disconnects the bot from voice. Requires the Manage Channels permission"""
if ctx.voice_client is None: if ctx.voice_client is None:
raise commands.CommandError("Roxbot is not in a voice channel.") raise commands.CommandError("Roxbot is not in a voice channel.")
else: else:
await ctx.voice_client.disconnect() await ctx.voice_client.disconnect()
return await ctx.send(":wave:") return await ctx.send(":wave:")


@commands.has_permissions(manage_channels=True)
@commands.command()
async def voice(self, ctx, setting=None, change=None):
"""Edits settings for the voice cog. Requires the Manage Channels permission
Options:
enable/disable: Enable/disables specified change.
skipratio: Specify what the ratio should be for skip voting if enabled. Example: 0.6 for 60%
maxlength/duration: Specify (in seconds) the max duration of a video that can be played. Ignored if staff of the server/bot owner.
Possible settings to enable/disable:
needperms: specifies whether volume controls and other bot functions need mod/admin perms.
skipvoting: specifies whether skipping should need over half of voice users to vote to skip. Bypassed by mods.
Example:
;settings voice enable skipvoting
"""
setting = setting.lower()
change = change.lower()
settings = roxbot.guild_settings.get(ctx.guild)
voice = settings["voice"]

if setting == "enable":
if change == "needperms":
voice["need_perms"] = 1
await ctx.send("'{}' has been enabled!".format(change))
elif change == "skipvoting":
voice["skip_voting"] = 1
await ctx.send("'{}' has been enabled!".format(change))
else:
return await ctx.send("Not a valid change.")
elif setting == "disable":
if change == "needperms":
voice["need_perms"] = 1
await ctx.send("'{}' was disabled :cry:".format(change))
elif change == "skipvoting":
voice["skip_voting"] = 1
await ctx.send("'{}' was disabled :cry:".format(change))
else:
return await ctx.send("Not a valid change.")
elif setting == "skipratio":
change = float(change)
if 1 > change > 0:
voice["skip_ratio"] = change
elif 0 < change <= 100:
change = change/10
voice["skip_ratio"] = change
else:
return await ctx.send("Valid ratio not given.")
await ctx.send("Skip Ratio was set to {}".format(change))
elif setting == "maxlength" or setting == "maxduration":
change = int(change)
if change >= 1:
voice["skip_ratio"] = change
else:
return await ctx.send("Valid max duration not given.")
await ctx.send("Max Duration was set to {}".format(change))
else:
return await ctx.send("Valid option not given.")
return settings.update(voice, "voice")



def setup(bot_client): def setup(bot_client):
bot_client.add_cog(Voice(bot_client)) bot_client.add_cog(Voice(bot_client))

+ 12
- 6
roxbot/guild_settings.py View File

for key, setting in settings.items(): for key, setting in settings.items():
if setting.get("convert"): if setting.get("convert"):
for x in setting["convert"].keys(): for x in setting["convert"].keys():
if setting["convert"][x] != "bool":
if setting["convert"][x] not in ("bool", "hide"):
if isinstance(setting[x], list): if isinstance(setting[x], list):
for y, value in enumerate(setting[x]): for y, value in enumerate(setting[x]):
if option == "str": if option == "str":
""" """
Get latest settings, and update them with a change. Get latest settings, and update them with a change.
:param changed_dict: :param changed_dict:
:param setting: Setting should be a str of the setting key. If nothing is passed, it is assumed changed_dict is the settings file for the whole server.
:param setting: Setting should be a str of the setting key.
If nothing is passed, it is assumed changed_dict is the settings file for the whole server.
THIS IS NOT RECOMMENED. Always try and just pass the cogs settings and not a whole settings file.
:return: :return:
""" """
self.settings = self.refresh() self.settings = self.refresh()
settings = self.settings.copy()
if setting is not None: if setting is not None:
self.settings[setting] = changed_dict
settings[setting] = changed_dict
elif isinstance(changed_dict, dict):
settings = changed_dict
elif isinstance(changed_dict, GuildSettings):
settings = changed_dict.settings
else: else:
self.settings = changed_dict
settings = self.settings.copy()
self._convert(settings, "str")
raise TypeError("changed_dict can only be a dict or GuildSettings object.")
settings = self._convert(settings, "str")
_write_changes(self.id, self.cogs, settings) _write_changes(self.id, self.cogs, settings)

+ 37
- 1
roxbot/logging.py View File

SOFTWARE. SOFTWARE.
""" """


import typing
import discord import discord
from discord.ext import commands


import roxbot import roxbot
from roxbot import guild_settings from roxbot import guild_settings
class Logging: class Logging:
def __init__(self, bot_client): def __init__(self, bot_client):
self.bot = bot_client self.bot = bot_client
self.settings = {
"logging": {
"enabled": 0,
"convert": {"enabled": "bool", "channel": "channel"},
"channel": 0
}
}


async def on_member_join(self, member): async def on_member_join(self, member):
logging = guild_settings.get(member.guild)["logging"] logging = guild_settings.get(member.guild)["logging"]
embed = discord.Embed(description="{} left the server".format(member), colour=roxbot.EmbedColours.pink) embed = discord.Embed(description="{} left the server".format(member), colour=roxbot.EmbedColours.pink)
return await channel.send(embed=embed) return await channel.send(embed=embed)


@commands.guild_only()
@commands.command(aliases=["log"])
async def logging(self, ctx, setting, *, channel: typing.Optional[discord.TextChannel] = None):
"""Edits the logging settings.

Options:
enable/disable: Enable/disables logging.
channel: sets the channel.
"""

setting = setting.lower()
settings = guild_settings.get(ctx.guild)

if setting == "enable":
settings["logging"]["enabled"] = 1
await ctx.send("'logging' was enabled!")
elif setting == "disable":
settings["logging"]["enabled"] = 0
await ctx.send("'logging' was disabled :cry:")
elif setting == "channel":
if not channel:
channel = ctx.channel
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 settings.update(settings["logging"], "logging")



def setup(bot_client): def setup(bot_client):
bot_client.add_cog(Logging(bot_client)) bot_client.add_cog(Logging(bot_client))

+ 0
- 4
roxbot/settings/servers/.gitignore View File

# Ignore everything in this directory
*
# Except this file
!.gitignore

+ 0
- 491
roxbot/settings/settings.py View File

# -*- coding: utf-8 -*-

"""
MIT License

Copyright (c) 2017-2018 Roxanne Gibson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import discord
from discord.ext import commands

from roxbot import checks, guild_settings, EmbedColours



class Settings:
"""
Settings is a mix of settings and admin stuff for the bot. OWNER OR ADMIN ONLY.
"""
def __init__(self, bot_client):
self.bot = bot_client

def get_channel(self, ctx, channel):
if ctx.message.channel_mentions:
return ctx.message.channel_mentions[0]
else:
return self.bot.get_channel(channel)

def parse_setting(self, ctx, settings_to_copy, raw=False):
settingcontent = ""
setting = settings_to_copy.copy()
convert = setting.get("convert", None)
if convert is not None and not raw:
for x in convert.keys():
if convert[x] == "bool":
if setting[x] == 0:
setting[x] = "False"
else:
setting[x] = "True"
elif convert[x] == "channel":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many channels to display."
else:
new_channels = []
for channel in setting[x]:
try:
new_channels.append(self.bot.get_channel(channel).mention)
except AttributeError:
new_channels.append(channel)
setting[x] = new_channels
else:
try:
setting[x] = self.bot.get_channel(setting[x]).mention
except AttributeError:
pass
elif convert[x] == "role":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many roles to display."
else:
new_roles = []
for role_id in setting[x]:
try:
new_roles.append(discord.utils.get(ctx.guild.roles, id=role_id).name)
except AttributeError:
new_roles.append(role_id)
setting[x] = new_roles
else:
try:
setting[x] = discord.utils.get(ctx.guild.roles, id=setting[x]).name
except AttributeError:
pass
elif convert[x] == "user":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many users to display."
else:
new_users = []
for user_id in setting[x]:

user = self.bot.get_user(user_id)
if user is None:
new_users.append(str(user_id))
else:
new_users.append(str(user))
setting[x] = new_users
else:
user = self.bot.get_user(setting[x])
if user is None:
setting[x] = str(setting[x])
else:
setting[x] = str(user)
elif convert[x] == "hide":
setting[x] = "This is hidden. Please use other commands to get this data."
for x in setting.items():
if x[0] != "convert":
settingcontent += str(x).strip("()") + "\n"
return settingcontent

@commands.command(aliases=["printsettingsraw"])
@checks.is_admin_or_mod()
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.
paginator = commands.Paginator(prefix="```md")
paginator.add_line("{} settings for {}.\n".format(self.bot.user.name, ctx.message.guild.name))
if option in settings:
raw = bool(ctx.invoked_with == "printsettingsraw")
settingcontent = self.parse_setting(ctx, settings[option], raw=raw)
paginator.add_line("**{}**".format(option))
paginator.add_line(settingcontent)
for page in paginator.pages:
await ctx.send(page)
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)
paginator.add_line("**{}**".format(setting))
paginator.add_line(settingcontent)
for page in paginator.pages:
await ctx.send(page)

@commands.group(case_insensitive=True)
@checks.is_admin_or_mod()
async def settings(self, ctx):
self.guild_settings = guild_settings.get(ctx.guild)

@settings.command(aliases=["log"])
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")

@settings.command(aliases=["sa"])
async def selfassign(self, ctx, selection=None, *, changes=None):
"""Edits settings for self assign cog.

Options:
enable/disable: Enable/disables the cog.
addrole/removerole: adds or removes a role that can be self assigned in the server.
"""

selection = selection.lower()
role = discord.utils.find(lambda u: u.name == changes, ctx.message.guild.roles)

self_assign = self.guild_settings.self_assign

if selection == "enable":
self_assign["enabled"] = 1
await ctx.send("'self_assign' was enabled!")
elif selection == "disable":
self_assign["enabled"] = 0
await ctx.send("'self_assign' was disabled :cry:")
elif selection == "addrole":
try:
if role.id in self_assign["roles"]:
return await ctx.send("{} is already a self-assignable role.".format(role.name))
self_assign["roles"].append(role.id)
await ctx.send('Role "{}" added'.format(str(role)))
except AttributeError:
return await ctx.send("Role param incorrect. Check you spelt it correctly")
elif selection == "removerole":
try:
if role.id in self_assign["roles"]:
self_assign["roles"].remove(role.id)
await ctx.send('"{}" has been removed from the self-assignable roles.'.format(str(role)))
else:
return await ctx.send("That role was not in the list.")
except AttributeError:
return await ctx.send("Role param incorrect. Check you spelt it correctly")
else:
return await ctx.send("No valid option given.")
return self.guild_settings.update(self_assign, "self_assign")

@settings.command(aliases=["jl"])
async def joinleave(self, ctx, selection=None, *, changes=None):
"""Edits settings for joinleave cog.

Options:
enable/disable: Enable/disables parts of the cog. Needs to specify which part.
Example:
;settings joinleave enable greets|goodbyes
greetschannel/goodbyeschannel: Sets the channels for either option. Must be a ID or mention.
custommessage: specifies a custom message for the greet messages.
"""

selection = selection.lower()
channel = self.get_channel(ctx, changes)
greets = self.guild_settings.greets
goodbyes = self.guild_settings.goodbyes

if selection == "greets":
if changes == "enable":
greets["enabled"] = 1
await ctx.send("'greets' was enabled!")
elif changes == "disable":
greets["enabled"] = 0
await ctx.send("'greets' was disabled :cry:")

elif selection == "goodbyes":
if changes == "enable":
goodbyes["enabled"] = 1
await ctx.send("'goodbyes' was enabled!")
elif changes == "disable":
goodbyes["enabled"] = 0
await ctx.send("'goodbyes' was disabled :cry:")

else:
if selection == "greetschannel":
greets["welcome-channel"] = channel.id
changes = "greets"
await ctx.send("{} has been set as the welcome channel!".format(channel.mention))
elif selection == "goodbyeschannel":
goodbyes["goodbye-channel"] = channel.id
changes = "goodbyes"
await ctx.send("{} has been set as the goodbye channel!".format(channel.mention))
elif selection == "custommessage":
greets["custom-message"] = changes
await ctx.send("Custom message set to '{}'".format(changes))
changes = "greets"
else:
return await ctx.send("No valid option given.")

if changes == "greets":
return self.guild_settings.update(greets, "greets")
elif changes == "goodbyes":
return self.guild_settings.update(goodbyes, "goodbyes")

@settings.command()
async def twitch(self, ctx, selection=None, *, changes=None):
"""Edits settings for self assign cog.

Options:
enable/disable: Enable/disables the cog.
channel: Sets the channel to shill in.
"""
# TODO: Menu also needs editing since I edited the twitch backend
selection = selection.lower()
twitch = self.guild_settings.twitch

if selection == "enable":
twitch["enabled"] = 1
await ctx.send("'twitch' was enabled!")
elif selection == "disable":
twitch["enabled"] = 0
await ctx.send("'twitch' was disabled :cry:")
elif selection == "channel":
channel = self.get_channel(ctx, changes)
twitch["channel"] = channel.id
await ctx.send("{} has been set as the twitch shilling channel!".format(channel.mention))
# Is lacking whitelist options. Might be added or might be depreciated.
# Turns out this is handled in the cog and I don't think it needs changing but may be confusing.
else:
return await ctx.send("No valid option given.")
return self.guild_settings.update(twitch, "twitch")

@settings.command(aliases=["perms"])
async def permrole(self, ctx, selection=None, *, changes=None):
"""Edits settings for permission roles.

Options:
addadmin/removeadmin: Adds/Removes admin role.
addmod/removemod: Adds/Removes mod role.
Example:
;settings permrole addadmin Admin
"""

selection = selection.lower()
role = discord.utils.find(lambda u: u.name == changes, ctx.message.guild.roles)
perm_roles = self.guild_settings.perm_roles

if selection == "addadmin":
if role.id not in perm_roles["admin"]:
perm_roles["admin"].append(role.id)
await ctx.send("'{}' has been added to the Admin role list.".format(role.name))
else:
return await ctx.send("'{}' is already in the list.".format(role.name))
elif selection == "addmod":
if role.id not in perm_roles["mod"]:
perm_roles["mod"].append(role.id)
await ctx.send("'{}' has been added to the Mod role list.".format(role.name))
else:
return await ctx.send("'{}' is already in the list.".format(role.name))
elif selection == "removeadmin":
try:
perm_roles["admin"].remove(role.id)
await ctx.send("'{}' has been removed from the Admin role list.".format(role.name))
except ValueError:
return await ctx.send("That role was not in the list.")
elif selection == "removemod":
try:
perm_roles["mod"].remove(role.id)
await ctx.send("'{}' has been removed from the Mod role list.".format(role.name))
except ValueError:
return await ctx.send("That role was not in the list.")

else:
return await ctx.send("No valid option given.")
return self.guild_settings.update(perm_roles, "perm_roles")

@settings.command()
async def gss(self, ctx, selection=None, *, changes=None):
"""Custom Cog for the GaySoundsShitposts Discord Server."""
# TODO: Menu
selection = selection.lower()
gss = self.guild_settings.gss

if selection == "loggingchannel":
channel = self.get_channel(ctx, changes)
gss["log_channel"] = channel.id
await ctx.send("Logging Channel set to '{}'".format(channel.name))
elif selection == "requireddays":
gss["required_days"] = int(changes)
await ctx.send("Required days set to '{}'".format(str(changes)))
elif selection == "requiredscore":
gss["required_score"] = int(changes)
await ctx.send("Required score set to '{}'".format(str(changes)))
else:
return await ctx.send("No valid option given.")
return self.guild_settings.update(gss, "gss")

@settings.command()
async def nsfw(self, ctx, selection=None, *, changes=None):
"""Edits settings for the nsfw cog and other nsfw commands.
If nsfw is enabled and nsfw channels are added, the bot will only allow nsfw commands in the specified channels.

Options:
enable/disable: Enable/disables nsfw commands.
addchannel/removechannel: Adds/Removes a nsfw channel.
addbadtag/removebadtag: Add/Removes blacklisted tags so that you can avoid em with the commands.
Example:
;settings nsfw addchannel #nsfw_stuff
"""
#menu = Menu.nsfw(ctx.guild)
#print(menu.content)
selection = selection.lower()
nsfw = self.guild_settings.nsfw

if selection == "enable":
nsfw["enabled"] = 1
await ctx.send("'nsfw' was enabled!")
elif selection == "disable":
nsfw["enabled"] = 0
await ctx.send("'nsfw' was disabled :cry:")
elif selection == "addchannel":
channel = self.get_channel(ctx, changes)
if channel.id not in nsfw["channels"]:
nsfw["channels"].append(channel.id)
await ctx.send("'{}' has been added to the nsfw channel list.".format(channel.name))
else:
return await ctx.send("'{}' is already in the list.".format(channel.name))
elif selection == "removechannel":
channel = self.get_channel(ctx, changes)
try:
nsfw["channels"].remove(channel.id)
await ctx.send("'{}' has been removed from the nsfw channel list.".format(channel.name))
except ValueError:
return await ctx.send("That role was not in the list.")
elif selection == "addbadtag":
if changes not in nsfw["blacklist"]:
nsfw["blacklist"].append(changes)
await ctx.send("'{}' has been added to the blacklisted tag list.".format(changes))
else:
return await ctx.send("'{}' is already in the list.".format(changes))
elif selection == "removebadtag":
try:
nsfw["blacklist"].remove(changes)
await ctx.send("'{}' has been removed from the blacklisted tag list.".format(changes))
except ValueError:
return await ctx.send("That tag was not in the blacklisted tag list.")
else:
return await ctx.send("No valid option given.")
return self.guild_settings.update(nsfw, "nsfw")

@settings.command()
async def voice(self, ctx, setting=None, change=None):
"""Edits settings for the voice cog.
Options:
enable/disable: Enable/disables specified change.
skipratio: Specify what the ratio should be for skip voting if enabled. Example: 0.6 for 60%
maxlength/duration: Specify (in seconds) the max duration of a video that can be played. Ignored if staff of the server/bot owner.
Possible settings to enable/disable:
needperms: specifies whether volume controls and other bot functions need mod/admin perms.
skipvoting: specifies whether skipping should need over half of voice users to vote to skip. Bypassed by mods.
Example:
;settings voice enable skipvoting
"""
setting = setting.lower()
change = change.lower()
voice = self.guild_settings.voice

if setting == "enable":
if change == "needperms":
voice["need_perms"] = 1
await ctx.send("'{}' has been enabled!".format(change))
elif change == "skipvoting":
voice["skip_voting"] = 1
await ctx.send("'{}' has been enabled!".format(change))
else:
return await ctx.send("Not a valid change.")
elif setting == "disable":
if change == "needperms":
voice["need_perms"] = 1
await ctx.send("'{}' was disabled :cry:".format(change))
elif change == "skipvoting":
voice["skip_voting"] = 1
await ctx.send("'{}' was disabled :cry:".format(change))
else:
return await ctx.send("Not a valid change.")
elif setting == "skipratio":
change = float(change)
if 1 > change > 0:
voice["skip_ratio"] = change
elif 0 < change <= 100:
change = change/10
voice["skip_ratio"] = change
else:
return await ctx.send("Valid ratio not given.")
await ctx.send("Skip Ratio was set to {}".format(change))
elif setting == "maxlength" or setting == "maxduration":
change = int(change)
if change >= 1:
voice["skip_ratio"] = change
else:
return await ctx.send("Valid max duration not given.")
await ctx.send("Max Duration was set to {}".format(change))
else:
return await ctx.send("Valid option not given.")
return self.guild_settings.update(voice, "voice")

@commands.guild_only()
@checks.is_admin_or_mod()
@commands.command()
async def serverisanal(self, ctx):
"""Tells the bot where the server is anal or not.
This only changes if roxbot can do the suck and spank commands outside of the specified nsfw channels."""
gs = guild_settings.get(ctx.guild)
is_anal = gs.is_anal
if is_anal["y/n"] == 0:
is_anal["y/n"] = 1
gs.update(is_anal, "is_anal")
await ctx.send("I now know this server is anal")
else:
is_anal["y/n"] = 0
gs.update(is_anal, "is_anal")
await ctx.send("I now know this server is NOT anal")


def setup(bot_client):
bot_client.add_cog(Settings(bot_client))

+ 96
- 0
roxbot/system.py View File

await self.bot.change_presence(status=discord_status) await self.bot.change_presence(status=discord_status)
await ctx.send("**:ok:** Status set to {}".format(discord_status)) await ctx.send("**:ok:** Status set to {}".format(discord_status))


def parse_setting(self, ctx, settings_to_copy, raw=False):
settingcontent = ""
setting = settings_to_copy.copy()
convert = setting.get("convert", None)
if convert is not None and not raw:
for x in convert.keys():
if convert[x] == "bool":
if setting[x] == 0:
setting[x] = "False"
else:
setting[x] = "True"
elif convert[x] == "channel":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many channels to display."
else:
new_channels = []
for channel in setting[x]:
try:
new_channels.append(self.bot.get_channel(channel).mention)
except AttributeError:
new_channels.append(channel)
setting[x] = new_channels
else:
try:
setting[x] = self.bot.get_channel(setting[x]).mention
except AttributeError:
pass
elif convert[x] == "role":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many roles to display."
else:
new_roles = []
for role_id in setting[x]:
try:
new_roles.append(discord.utils.get(ctx.guild.roles, id=role_id).name)
except AttributeError:
new_roles.append(role_id)
setting[x] = new_roles
else:
try:
setting[x] = discord.utils.get(ctx.guild.roles, id=setting[x]).name
except AttributeError:
pass
elif convert[x] == "user":
if isinstance(setting[x], list):
if len(setting[x]) >= 60:
setting[x] = "There is too many users to display."
else:
new_users = []
for user_id in setting[x]:

user = self.bot.get_user(user_id)
if user is None:
new_users.append(str(user_id))
else:
new_users.append(str(user))
setting[x] = new_users
else:
user = self.bot.get_user(setting[x])
if user is None:
setting[x] = str(setting[x])
else:
setting[x] = str(user)
elif convert[x] == "hide":
setting[x] = "This is hidden. Please use other commands to get this data."
for x in setting.items():
if x[0] != "convert":
settingcontent += str(x).strip("()") + "\n"
return settingcontent

@commands.command(aliases=["printsettingsraw"])
@commands.has_permissions(manage_guild=True)
async def printsettings(self, ctx, option=None):
"""OWNER OR ADMIN ONLY: Prints the servers settings file."""
config = roxbot.guild_settings.get(ctx.guild)
settings = dict(config.settings.copy()) # Make a copy of settings so we don't change the actual settings.
paginator = commands.Paginator(prefix="```md")
paginator.add_line("{} settings for {}.\n".format(self.bot.user.name, ctx.message.guild.name))
if option in settings:
raw = bool(ctx.invoked_with == "printsettingsraw")
settingcontent = self.parse_setting(ctx, settings[option], raw=raw)
paginator.add_line("**{}**".format(option))
paginator.add_line(settingcontent)
for page in paginator.pages:
await ctx.send(page)
else:
for setting in settings:
raw = bool(ctx.invoked_with == "printsettingsraw")
settingcontent = self.parse_setting(ctx, settings[setting], raw=raw)
paginator.add_line("**{}**".format(setting))
paginator.add_line(settingcontent)
for page in paginator.pages:
await ctx.send(page)

@commands.command() @commands.command()
@commands.is_owner() @commands.is_owner()
async def shutdown(self, ctx): async def shutdown(self, ctx):

Loading…
Cancel
Save