diff --git a/cogs/alias.py b/cogs/alias.py index 209e22189..7d2889415 100644 --- a/cogs/alias.py +++ b/cogs/alias.py @@ -1,6 +1,5 @@ import discord from discord.ext import commands -from .utils import checks from .utils.chat_formatting import * from .utils.dataIO import fileIO from .utils import checks diff --git a/cogs/audio.py b/cogs/audio.py index c374c6f06..8e203721a 100644 --- a/cogs/audio.py +++ b/cogs/audio.py @@ -2,7 +2,6 @@ import discord from discord.ext import commands import asyncio import threading -import youtube_dl import os from random import choice as rndchoice from random import shuffle @@ -15,8 +14,16 @@ import aiohttp import json import time -if not discord.opus.is_loaded(): - discord.opus.load_opus('libopus-0.dll') +try: + import youtube_dl +except: + youtube_dl = None + +try: + if not discord.opus.is_loaded(): + discord.opus.load_opus('libopus-0.dll') +except: + opus = None youtube_dl_options = { 'format': 'bestaudio/best', @@ -775,6 +782,12 @@ def check_files(): def setup(bot): check_folders() check_files() + if youtube_dl is None: + raise RuntimeError("You need to run `pip3 install youtube_dl`") + return + if opus is None: + raise RuntimeError("You need to get the *.exe's and opus.dll from 26's github.") + return loop = asyncio.get_event_loop() n = Audio(bot) loop.create_task(n.queue_manager()) diff --git a/cogs/economy.py b/cogs/economy.py index b731e2eca..4a3d67e5d 100644 --- a/cogs/economy.py +++ b/cogs/economy.py @@ -1,9 +1,9 @@ import discord from discord.ext import commands from .utils.dataIO import fileIO -from .utils import checks from random import randint from copy import deepcopy +from .utils import checks from __main__ import send_cmd_help import os import time diff --git a/cogs/mod.py b/cogs/mod.py index c9c84f84a..2ebda50fc 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -1,7 +1,7 @@ import discord from discord.ext import commands -from .utils import checks from .utils.dataIO import fileIO +from .utils import checks from __main__ import send_cmd_help, settings import os import logging @@ -23,49 +23,31 @@ class Mod: # THEN # 2) Use checks.save_bot_settings(modified_bot_settings) # 3) Don't blame me (Will), blame the other guy (not 26) - @property - def bot_settings(self): - settings = {} - with open("data/red/settings.json", "r") as f: - settings = json.loads(f.read()) - if settings == {}: - raise RuntimeError("Settings not found.") - return settings @commands.group(pass_context=True) @checks.admin_or_permissions(manage_server=True) async def modset(self,ctx): """Manages server administration settings.""" if ctx.invoked_subcommand is None: - send_cmd_help(ctx) + await send_cmd_help(ctx) - @modset.command(name="adminrole",pass_context=True) + @modset.command(name="adminrole",pass_context=True,no_pm=True) async def _modset_adminrole(self,ctx,role_name : str): """Sets the admin role for this server, case insensitive.""" - sid = ctx.message.server.id - settings = self.bot_settings - if sid in settings: - settings[sid]["ADMIN_ROLE"] = role_name - else: - settings[sid] = {"ADMIN_ROLE":role_name,"MOD_ROLE":""} - settings[sid]["MOD_ROLE"] = settings["default"]["MOD_ROLE"] + server = ctx.message.server + if server.id not in settings.servers: await self.bot.say("Remember to set modrole too.") + settings.set_server_admin(server,role_name) await self.bot.say("Admin role set to '{}'".format(role_name)) - checks.save_bot_settings(settings) - @modset.command(name="modrole",pass_context=True) + @modset.command(name="modrole",pass_context=True,no_pm=True) async def _modset_modrole(self,ctx,role_name : str): """Sets the mod role for this server, case insensitive.""" - sid = ctx.message.server.id - settings = self.bot_settings - if sid in settings: - settings[sid]["MOD_ROLE"] = role_name - else: - settings[sid] = {"MOD_ROLE":role_name,"ADMIN_ROLE":""} - settings[sid]["ADMIN_ROLE"] = settings["default"]["ADMIN_ROLE"] + server = ctx.message.server + if server.id not in settings.servers: await self.bot.say("Remember to set adminrole too.") + settings.set_server_mod(server,role_name) await self.bot.say("Mod role set to '{}'".format(role_name)) - checks.save_bot_settings(settings) @commands.command(no_pm=True, pass_context=True) @checks.admin_or_permissions(kick_members=True) @@ -449,11 +431,15 @@ class Mod: def immune_from_filter(self, message): user = message.author - if user.id == checks.settings["OWNER"]: + server = message.server + admin_role = settings.get_server_admin(server) + mod_role = settings.get_server_mod(server) + + if user.id == settings.owner: return True - elif discord.utils.get(user.roles, name=checks.settings["ADMIN_ROLE"]): + elif discord.utils.get(user.roles, name=admin_role): return True - elif discord.utils.get(user.roles, name=checks.settings["MOD_ROLE"]): + elif discord.utils.get(user.roles, name=mod_role): return True else: return False diff --git a/cogs/utils/checks.py b/cogs/utils/checks.py index e62282e73..2f534c714 100644 --- a/cogs/utils/checks.py +++ b/cogs/utils/checks.py @@ -1,7 +1,8 @@ from discord.ext import commands import discord.utils -import os.path -import json +from cogs.utils.settings import Settings +from cogs.utils.dataIO import fileIO +from __main__ import settings # # This is a modified version of checks.py, originally made by Rapptz @@ -10,15 +11,8 @@ import json # https://github.com/Rapptz/RoboDanny/tree/async # -try: - with open("data/red/settings.json", "r") as f: - settings = json.loads(f.read()) -except Exception as e: - print(e) - settings = {"OWNER" : False, "default":{"ADMIN_ROLE" : False, "MOD_ROLE" : False}} - def is_owner_check(ctx): - return ctx.message.author.id == settings["OWNER"] + return ctx.message.author.id == settings.owner def is_owner(): return commands.check(is_owner_check) @@ -33,17 +27,6 @@ def is_owner(): # the permissions required for them. # Of course, the owner will always be able to execute commands. -def save_bot_settings(bot_settings=settings): - with open("data/red/settings.json", "w") as f: - f.write(json.dumps(bot_settings,sort_keys=True,indent=4,separators=(',',':'))) - -def update_old_settings(): - mod = settings["MOD_ROLE"] - admin = settings["ADMIN_ROLE"] - del settings["MOD_ROLE"] - del settings["ADMIN_ROLE"] - settings["default"] = {"MOD_ROLE":mod,"ADMIN_ROLE":admin} - def check_permissions(ctx, perms): if is_owner_check(ctx): return True @@ -67,30 +50,17 @@ def role_or_permissions(ctx, check, **perms): def mod_or_permissions(**perms): def predicate(ctx): - if "default" not in settings: - update_old_settings() - if admin_or_permissions(**perms): - return True - sid = ctx.message.server.id - if sid not in settings: - mod_role = settings["default"]["MOD_ROLE"].lower() - admin_role = settings["default"]["ADMIN_ROLE"].lower() - else: - mod_role = settings[sid]["MOD_ROLE"].lower() - admin_role = settings[sid]["ADMIN_ROLE"].lower() + server = ctx.message.server + mod_role = settings.get_server_mod(server) + admin_role = settings.get_server_admin(server) return role_or_permissions(ctx, lambda r: r.name.lower() in (mod_role,admin_role), **perms) return commands.check(predicate) def admin_or_permissions(**perms): def predicate(ctx): - if "default" not in settings: - update_old_settings() - sid = ctx.message.server.id - if sid not in settings: - admin_role = settings["default"]["ADMIN_ROLE"] - else: - admin_role = settings[sid]["ADMIN_ROLE"] + server = ctx.message.server + admin_role = settings.get_server_admin(server) return role_or_permissions(ctx, lambda r: r.name.lower() == admin_role.lower(), **perms) return commands.check(predicate) diff --git a/cogs/utils/dataIO.py b/cogs/utils/dataIO.py index f84312607..e2d1ece05 100644 --- a/cogs/utils/dataIO.py +++ b/cogs/utils/dataIO.py @@ -3,7 +3,7 @@ import json def fileIO(filename, IO, data=None): if IO == "save" and data != None: with open(filename, encoding='utf-8', mode="w") as f: - f.write(json.dumps(data)) + f.write(json.dumps(data,indent=4,sort_keys=True,separators=(',',' : '))) elif IO == "load" and data == None: with open(filename, encoding='utf-8', mode="r") as f: return json.loads(f.read()) diff --git a/cogs/utils/settings.py b/cogs/utils/settings.py new file mode 100644 index 000000000..cd513d51f --- /dev/null +++ b/cogs/utils/settings.py @@ -0,0 +1,125 @@ +from .dataIO import fileIO +import discord + +default_path = "data/red/settings.json" + +class Settings: + def __init__(self,path=default_path): + self.path = path + self.default_settings = {"EMAIL" : "EmailHere", "PASSWORD" : "PasswordHere", "OWNER" : "id_here", "PREFIXES" : [], "default":{"ADMIN_ROLE" : "Transistor", "MOD_ROLE" : "Process"}} + if not fileIO(self.path,"check"): + self.bot_settings = self.default_settings + self.save_settings() + else: + self.bot_settings = fileIO(self.path,"load") + if "default" not in self.bot_settings: + self.update_old_settings() + + def save_settings(self): + fileIO(self.path,"save",self.bot_settings) + + def update_old_settings(self): + mod = self.bot_settings["MOD_ROLE"] + admin = self.bot_settings["ADMIN_ROLE"] + del self.bot_settings["MOD_ROLE"] + del self.bot_settings["ADMIN_ROLE"] + self.bot_settings["default"] = {"MOD_ROLE":mod,"ADMIN_ROLE":admin} + self.save_settings() + + @property + def owner(self): + return self.bot_settings["OWNER"] + + @owner.setter + def owner(self,value): + self.bot_settings["OWNER"] = value + self.save_settings() + + @property + def email(self): + return self.bot_settings["EMAIL"] + + @property + def password(self): + return self.bot_settings["PASSWORD"] + + @property + def prefixes(self): + return self.bot_settings["PREFIXES"] + + @prefixes.setter + def prefixes(self,value): + self.bot_settings["PREFIXES"] = value + self.save_settings() + + @property + def default_admin(self): + if "default" not in self.bot_settings: + self.update_old_settings() + return self.bot_settings["default"].get("ADMIN_ROLE","") + + @default_admin.setter + def default_admin(self,value): + if "default" not in self.bot_settings: + self.update_old_settings() + self.bot_settings["default"]["ADMIN_ROLE"] = value + self.save_settings() + + @property + def default_mod(self): + if "default" not in self.bot_settings: + self.update_old_settings() + return self.bot_settings["default"].get("MOD_ROLE","") + + @default_mod.setter + def default_mod(self,value): + if "default" not in self.bot_settings: + self.update_old_settings() + self.bot_settings["default"]["MOD_ROLE"] = value + self.save_settings() + + @property + def servers(self): + ret = {} + server_ids = list(filter(lambda x: str(x).isdigit(),self.bot_settings)) + for server in server_ids: + ret.update({server:self.bot_settings[server]}) + return ret + + def get_server_admin(self,server): + assert isinstance(server,discord.Server) + if server is None: + return + if server.id not in self.bot_settings: + return self.default_admin + return self.bot_settings[server.id].get("ADMIN_ROLE","") + + def set_server_admin(self,server,value): + assert isinstance(server,discord.Server) + if server is None: + return + if server.id not in self.bot_settings: + self.add_server(server.id) + self.bot_settings[server.id]["ADMIN_ROLE"] = value + self.save_settings() + + def get_server_mod(self,server): + assert isinstance(server,discord.Server) + if server is None: + return + if server.id not in self.bot_settings: + return self.default_mod + return self.bot_settings[server.id].get("MOD_ROLE","") + + def set_server_mod(self,server,value): + assert isinstance(server,discord.Server) + if server is None: + return + if server.id not in self.bot_settings: + self.add_server(server.id) + self.bot_settings[server.id]["MOD_ROLE"] = value + self.save_settings() + + def add_server(self,sid): + self.bot_settings[sid] = self.bot_settings["default"].copy() + self.save_settings() diff --git a/red.py b/red.py index 197ce68a2..12c64eb5d 100644 --- a/red.py +++ b/red.py @@ -1,6 +1,6 @@ from discord.ext import commands import discord -from cogs.utils import checks +from cogs.utils.settings import Settings from random import choice as rndchoice import threading import datetime, re @@ -31,6 +31,10 @@ formatter = commands.HelpFormatter(show_check_failure=False) bot = commands.Bot(command_prefix=["_"], formatter=formatter, description=description, pm_help=None) +settings = Settings() + +from cogs.utils import checks + lock = False @bot.event @@ -150,7 +154,7 @@ async def debug(ctx, *, code : str): result = python.format(result) if not ctx.message.channel.is_private: - censor = (settings["EMAIL"], settings["PASSWORD"]) + censor = (settings.email, settings.password) r = "[EXPUNGED]" for w in censor: result = result.replace(w, r) @@ -169,8 +173,7 @@ async def owner(ctx): """Sets owner""" global lock msg = ctx.message - data = load_settings() - if data["OWNER"] != "id_here": + if settings.owner != "id_here": await bot.say("Owner ID has already been set.") return if lock: @@ -192,10 +195,7 @@ async def prefix(*prefixes): await bot.say("Example: setprefix [ ! ^ .") return bot.command_prefix = list(prefixes) - data = load_settings() - data["PREFIXES"] = list(prefixes) - with open("data/red/settings.json", "w") as f: - f.write(json.dumps(data)) + settings.prefixes = list(prefixes) if len(prefixes) > 1: await bot.say("Prefixes set") else: @@ -207,7 +207,7 @@ async def name(ctx, *name : str): """Sets Red's name""" if name == (): await send_cmd_help(ctx) - await bot.edit_profile(settings["PASSWORD"], username=" ".join(name)) + await bot.edit_profile(settings.password, username=" ".join(name)) await bot.say("Done.") @_set.command(pass_context=True) @@ -227,7 +227,7 @@ async def avatar(url : str): try: async with aiohttp.get(url) as r: data = await r.read() - await bot.edit_profile(settings["PASSWORD"], avatar=data) + await bot.edit_profile(settings.password, avatar=data) await bot.say("Done.") except: await bot.say("Error.") @@ -277,15 +277,11 @@ def user_allowed(message): mod = bot.get_cog('Mod') if mod is not None: - if checks.settings["OWNER"] == author.id: + if settings.owner == author.id: return True if not message.channel.is_private: - sid = message.server.id - if sid in checks.settings: - names = (checks.settings[sid]["ADMIN_ROLE"],checks.settings[sid]["MOD_ROLE"]) - else: - names = (checks.settings["default"]["ADMIN_ROLE"],checks.settings["default"]["MOD_ROLE"]) - + server = message.server + names = (settings.get_server_admin(server),settings.get_server_mod(server)) if None not in map(lambda name: discord.utils.get(author.roles,name=name),names): return True @@ -314,24 +310,13 @@ def wait_for_answer(author): while choice.lower() != "yes" and choice == "None": choice = input("> ") if choice == "yes": - data = load_settings() - data["OWNER"] = author.id - with open("data/red/settings.json", "w") as f: - f.write(json.dumps(data)) - checks.owner = data["OWNER"] - print(author.name + " has been set as owner. A restart is required.") + settings.owner = author.id + print(author.name + " has been set as owner. A restart is required, maybe?") lock = False else: print("setowner request has been ignored.") lock = False -def load_settings(): - try: - with open('data/red/settings.json', "r") as f: - return json.load(f) - except: - raise("Couldn't load credentials.") - def list_cogs(): cogs = glob.glob("cogs/*.py") clean = [] @@ -348,51 +333,46 @@ def check_folders(): os.makedirs(folder) def check_configs(): - settings_path = "data/red/settings.json" - settings = {"EMAIL" : "EmailHere", "PASSWORD" : "PasswordHere", "OWNER" : "id_here", "PREFIXES" : [], "default":{"ADMIN_ROLE" : "Transistor", "MOD_ROLE" : "Process"}} - if not os.path.isfile(settings_path): - + if settings.bot_settings == settings.default_settings: print("Red - First run configuration") print("If you don't have one, create a NEW ACCOUNT for Red. Do *not* use yours. (https://discordapp.com)") - settings["EMAIL"] = input("\nEmail> ") - settings["PASSWORD"] = input("\nPassword> ") + settings.email = input("\nEmail> ") + settings.password = input("\nPassword> ") - if not settings["EMAIL"] or not settings["PASSWORD"]: + if not settings.email or not settings.password: input("Email and password cannot be empty. Restart Red and repeat the configuration process.") exit(1) - if "@" not in settings["EMAIL"]: + if "@" not in settings.email: input("You didn't enter a valid email. Restart Red and repeat the configuration process.") exit(1) print("\nChoose a prefix (or multiple ones, one at once) for the commands. Type exit when you're done. Example prefix: !") - settings["PREFIXES"] = [] + settings.prefixes = [] new_prefix = "" - while new_prefix.lower() != "exit" or settings["PREFIXES"] == []: + while new_prefix.lower() != "exit" or settings.prefixes == []: new_prefix = input("Prefix> ") if new_prefix.lower() != "exit" and new_prefix != "": - settings["PREFIXES"].append(new_prefix) + settings.prefixes = settings.prefixes.append(new_prefix) + #Remember we're using property's here, oh well... print("\nInput *your own* ID. You can type \@Yourname in chat to see it (copy only the numbers).") print("If you want, you can also do it later with [prefix]set owner. Leave empty in that case.") - settings["OWNER"] = input("\nID> ") - if settings["OWNER"] == "": settings["OWNER"] = "id_here" - if not settings["OWNER"].isdigit() and settings["OWNER"] != "id_here": + settings.owner = input("\nID> ") + if settings.owner == "": settings["OWNER"] = "id_here" + if not settings.owner.isdigit() and settings["OWNER"] != "id_here": print("\nERROR: What you entered is not a valid ID. Set yourself as owner later with [prefix]set owner") - settings["OWNER"] = "id_here" + settings.owner = "id_here" print("\nInput the admin role's name. Anyone with this role will be able to use the bot's admin commands") print("Leave blank for default name (Transistor)") - settings["default"]["ADMIN_ROLE"] = input("\nAdmin role> ") - if settings["default"]["ADMIN_ROLE"] == "": settings["default"]["ADMIN_ROLE"] = "Transistor" + settings.default_admin = input("\nAdmin role> ") + if settings.default_admin == "": settings.default_admin = "Transistor" print("\nInput the moderator role's name. Anyone with this role will be able to use the bot's mod commands") print("Leave blank for default name (Process)") - settings["default"]["MOD_ROLE"] = input("\nAdmin role> ") - if settings["default"]["MOD_ROLE"] == "": settings["default"]["MOD_ROLE"] = "Process" - - with open(settings_path, "w") as f: - f.write(json.dumps(settings)) + settings.default_mod = input("\nModerator role> ") + if settings.default_mod == "": settings.default_mod = "Process" cogs_s_path = "data/red/cogs.json" cogs = {} @@ -477,15 +457,14 @@ def load_cogs(): def main(): global settings + global checks + check_folders() check_configs() set_logger() - settings = load_settings() - checks.settings["OWNER"] = settings["OWNER"] - checks.save_bot_settings(checks.settings) load_cogs() - bot.command_prefix = settings["PREFIXES"] - yield from bot.login(settings["EMAIL"], settings["PASSWORD"]) + bot.command_prefix = settings.prefixes + yield from bot.login(settings.email, settings.password) yield from bot.connect() if __name__ == '__main__':