[Core] Refactor (#512)

This commit is contained in:
Caleb Johnson 2016-12-22 11:18:45 -06:00 committed by Twentysix
parent 1942f15576
commit 6f068191f1

190
red.py
View File

@ -64,7 +64,11 @@ class Bot(commands.Bot):
self._message_modifiers = [] self._message_modifiers = []
self.settings = Settings() self.settings = Settings()
self._intro_displayed = False self._intro_displayed = False
kwargs["self_bot"] = self.settings.self_bot self.logger = set_logger(self)
if 'self_bot' in kwargs:
self.settings.self_bot = kwargs['self_bot']
else:
kwargs['self_bot'] = self.settings.self_bot
super().__init__(*args, command_prefix=prefix_manager, **kwargs) super().__init__(*args, command_prefix=prefix_manager, **kwargs)
async def send_message(self, *args, **kwargs): async def send_message(self, *args, **kwargs):
@ -140,12 +144,12 @@ class Bot(commands.Bot):
mod = self.get_cog('Mod') mod = self.get_cog('Mod')
if mod is not None: if mod is not None:
if settings.owner == author.id: if self.settings.owner == author.id:
return True return True
if not message.channel.is_private: if not message.channel.is_private:
server = message.server server = message.server
names = (settings.get_server_admin( names = (self.settings.get_server_admin(
server), settings.get_server_mod(server)) server), self.settings.get_server_mod(server))
results = map( results = map(
lambda name: discord.utils.get(author.roles, name=name), lambda name: discord.utils.get(author.roles, name=name),
names) names)
@ -187,15 +191,52 @@ class Formatter(commands.HelpFormatter):
self._paginator.add_line(shortened) self._paginator.add_line(shortened)
formatter = Formatter(show_check_failure=False) def initialize(bot_class=Bot, formatter_class=Formatter):
formatter = formatter_class(show_check_failure=False)
bot = Bot(formatter=formatter, description=description, pm_help=None) bot = bot_class(formatter=formatter, description=description, pm_help=None)
send_cmd_help = bot.send_cmd_help # Backwards import __main__
user_allowed = bot.user_allowed # compatibility __main__.send_cmd_help = bot.send_cmd_help # Backwards
__main__.user_allowed = bot.user_allowed # compatibility
__main__.settings = bot.settings # sucks
settings = bot.settings async def get_oauth_url():
try:
data = await bot.application_info()
except Exception as e:
return "Couldn't retrieve invite link.Error: {}".format(e)
return discord.utils.oauth_url(data.id)
async def set_bot_owner():
if bot.settings.self_bot:
bot.settings.owner = bot.user.id
return "[Selfbot mode]"
if bot.settings.owner:
owner = discord.utils.get(bot.get_all_members(),
id=bot.settings.owner)
if not owner:
try:
owner = await bot.get_user_info(bot.settings.owner)
except:
owner = None
if not owner:
owner = bot.settings.owner # Just the ID then
return owner
how_to = "Do `[p]set owner` in chat to set it"
if bot.user.bot: # Can fetch owner
try:
data = await bot.application_info()
bot.settings.owner = data.owner.id
bot.settings.save_settings()
return data.owner
except:
return "Failed to fetch owner. " + how_to
else:
return "Yet to be set. " + how_to
@bot.event @bot.event
async def on_ready(): async def on_ready():
@ -224,14 +265,16 @@ async def on_ready():
print("{} servers".format(servers)) print("{} servers".format(servers))
print("{} channels".format(channels)) print("{} channels".format(channels))
print("{} users\n".format(users)) print("{} users\n".format(users))
prefix_label = "Prefixes:" if len(settings.prefixes) > 1 else "Prefix:" prefix_label = 'Prefix'
print("{} {}".format(prefix_label, " ".join(settings.prefixes))) if len(bot.settings.prefixes) > 1:
prefix_label += 'es'
print("{}: {}".format(prefix_label, " ".join(bot.settings.prefixes)))
print("Owner: " + str(owner)) print("Owner: " + str(owner))
print("{}/{} active cogs with {} commands".format( print("{}/{} active cogs with {} commands".format(
len(bot.cogs), total_cogs, len(bot.commands))) len(bot.cogs), total_cogs, len(bot.commands)))
print("-----------------") print("-----------------")
if settings.token and not settings.self_bot: if bot.settings.token and not bot.settings.self_bot:
print("\nUse this url to bring your bot to a server:") print("\nUse this url to bring your bot to a server:")
url = await get_oauth_url() url = await get_oauth_url()
bot.oauth_url = url bot.oauth_url = url
@ -249,35 +292,31 @@ async def on_ready():
await bot.get_cog('Owner').disable_commands() await bot.get_cog('Owner').disable_commands()
@bot.event @bot.event
async def on_resumed(): async def on_resumed():
bot.counter["session_resumed"] += 1 bot.counter["session_resumed"] += 1
@bot.event @bot.event
async def on_command(command, ctx): async def on_command(command, ctx):
bot.counter["processed_commands"] += 1 bot.counter["processed_commands"] += 1
@bot.event @bot.event
async def on_message(message): async def on_message(message):
bot.counter["messages_read"] += 1 bot.counter["messages_read"] += 1
if user_allowed(message): if bot.user_allowed(message):
await bot.process_commands(message) await bot.process_commands(message)
@bot.event @bot.event
async def on_command_error(error, ctx): async def on_command_error(error, ctx):
channel = ctx.message.channel channel = ctx.message.channel
if isinstance(error, commands.MissingRequiredArgument): if isinstance(error, commands.MissingRequiredArgument):
await send_cmd_help(ctx) await bot.send_cmd_help(ctx)
elif isinstance(error, commands.BadArgument): elif isinstance(error, commands.BadArgument):
await send_cmd_help(ctx) await bot.send_cmd_help(ctx)
elif isinstance(error, commands.DisabledCommand): elif isinstance(error, commands.DisabledCommand):
await bot.send_message(channel, "That command is disabled.") await bot.send_message(channel, "That command is disabled.")
elif isinstance(error, commands.CommandInvokeError): elif isinstance(error, commands.CommandInvokeError):
logger.exception("Exception in command '{}'".format( bot.logger.exception("Exception in command '{}'".format(
ctx.command.qualified_name), exc_info=error.original) ctx.command.qualified_name), exc_info=error.original)
oneliner = "Error in command '{}' - {}: {}".format( oneliner = "Error in command '{}' - {}: {}".format(
ctx.command.qualified_name, type(error.original).__name__, ctx.command.qualified_name, type(error.original).__name__,
@ -291,46 +330,9 @@ async def on_command_error(error, ctx):
await bot.send_message(channel, "That command is not " await bot.send_message(channel, "That command is not "
"available in DMs.") "available in DMs.")
else: else:
logger.exception(type(error).__name__, exc_info=error) bot.logger.exception(type(error).__name__, exc_info=error)
return bot
async def get_oauth_url():
try:
data = await bot.application_info()
except Exception as e:
return "Couldn't retrieve invite link.Error: {}".format(e)
return discord.utils.oauth_url(data.id)
async def set_bot_owner():
if settings.self_bot:
settings.owner = bot.user.id
return "[Selfbot mode]"
if bot.settings.owner:
owner = discord.utils.get(bot.get_all_members(),
id=bot.settings.owner)
if not owner:
try:
owner = await bot.get_user_info(bot.settings.owner)
except:
owner = None
if not owner:
owner = bot.settings.owner # Just the ID then
return owner
how_to = "Do `[p]set owner` in chat to set it"
if bot.user.bot: # Can fetch owner
try:
data = await bot.application_info()
settings.owner = data.owner.id
settings.save_settings()
return data.owner
except:
return "Failed to fetch owner. " + how_to
else:
return "Yet to be set. " + how_to
def check_folders(): def check_folders():
@ -341,7 +343,7 @@ def check_folders():
os.makedirs(folder) os.makedirs(folder)
def interactive_setup(): def interactive_setup(settings):
first_run = settings.bot_settings == settings.default_settings first_run = settings.bot_settings == settings.default_settings
if first_run: if first_run:
@ -406,9 +408,7 @@ def interactive_setup():
input("\n") input("\n")
def set_logger(): def set_logger(bot):
global logger
logger = logging.getLogger("red") logger = logging.getLogger("red")
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
@ -419,7 +419,7 @@ def set_logger():
stdout_handler = logging.StreamHandler(sys.stdout) stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(red_format) stdout_handler.setFormatter(red_format)
if settings.debug: if bot.settings.debug:
stdout_handler.setLevel(logging.DEBUG) stdout_handler.setLevel(logging.DEBUG)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
else: else:
@ -435,7 +435,7 @@ def set_logger():
logger.addHandler(stdout_handler) logger.addHandler(stdout_handler)
dpy_logger = logging.getLogger("discord") dpy_logger = logging.getLogger("discord")
if settings.debug: if bot.settings.debug:
dpy_logger.setLevel(logging.DEBUG) dpy_logger.setLevel(logging.DEBUG)
else: else:
dpy_logger.setLevel(logging.WARNING) dpy_logger.setLevel(logging.WARNING)
@ -447,6 +447,8 @@ def set_logger():
datefmt="[%d/%m/%Y %H:%M]")) datefmt="[%d/%m/%Y %H:%M]"))
dpy_logger.addHandler(handler) dpy_logger.addHandler(handler)
return logger
def ensure_reply(msg): def ensure_reply(msg):
choice = "" choice = ""
@ -466,13 +468,13 @@ def get_answer():
return False return False
def set_cog(cog, value): def set_cog(cog, value): # TODO: move this out of red.py
data = dataIO.load_json("data/red/cogs.json") data = dataIO.load_json("data/red/cogs.json")
data[cog] = value data[cog] = value
dataIO.save_json("data/red/cogs.json", data) dataIO.save_json("data/red/cogs.json", data)
def load_cogs(): def load_cogs(bot):
defaults = ("alias", "audio", "customcom", "downloader", "economy", defaults = ("alias", "audio", "customcom", "downloader", "economy",
"general", "image", "mod", "streams", "trivia") "general", "image", "mod", "streams", "trivia")
@ -488,8 +490,8 @@ def load_cogs():
"which Red cannot function. Reinstall.") "which Red cannot function. Reinstall.")
exit(1) exit(1)
if settings._no_cogs: if bot.settings._no_cogs:
logger.debug("Skipping initial cogs loading (--no-cogs)") bot.logger.debug("Skipping initial cogs loading (--no-cogs)")
if not os.path.isfile("data/red/cogs.json"): if not os.path.isfile("data/red/cogs.json"):
dataIO.save_json("data/red/cogs.json", {}) dataIO.save_json("data/red/cogs.json", {})
return return
@ -510,7 +512,7 @@ def load_cogs():
owner_cog._load_cog(extension) owner_cog._load_cog(extension)
except Exception as e: except Exception as e:
print("{}: {}".format(e.__class__.__name__, str(e))) print("{}: {}".format(e.__class__.__name__, str(e)))
logger.exception(e) bot.logger.exception(e)
failed.append(extension) failed.append(extension)
registry[extension] = False registry[extension] = False
@ -520,53 +522,55 @@ def load_cogs():
print("\nFailed to load: {}\n".format(" ".join(failed))) print("\nFailed to load: {}\n".format(" ".join(failed)))
def main(): def main(bot):
check_folders() check_folders()
if not settings.no_prompt: if not bot.settings.no_prompt:
interactive_setup() interactive_setup(bot.settings)
load_cogs() load_cogs(bot)
print("Logging into Discord...") print("Logging into Discord...")
bot.uptime = datetime.datetime.utcnow() bot.uptime = datetime.datetime.utcnow()
if settings.login_credentials: if bot.settings.login_credentials:
yield from bot.login(*settings.login_credentials, yield from bot.login(*bot.settings.login_credentials,
bot=not settings.self_bot) bot=not bot.settings.self_bot)
else: else:
print("No credentials available to login.") print("No credentials available to login.")
raise RuntimeError() raise RuntimeError()
yield from bot.connect() yield from bot.connect()
if __name__ == '__main__': if __name__ == '__main__':
sys.stdout = TextIOWrapper(sys.stdout.detach(), sys.stdout = TextIOWrapper(sys.stdout.detach(),
encoding=sys.stdout.encoding, encoding=sys.stdout.encoding,
errors="replace", errors="replace",
line_buffering=True) line_buffering=True)
set_logger() bot = initialize()
error = False error = False
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
try: try:
loop.run_until_complete(main()) loop.run_until_complete(main(bot))
except discord.LoginFailure: except discord.LoginFailure:
error = True error = True
logger.error(traceback.format_exc()) bot.logger.error(traceback.format_exc())
if not settings.no_prompt: if not bot.settings.no_prompt:
choice = input("Invalid login credentials. " choice = input("Invalid login credentials. If they worked before "
"If they worked before Discord might be having temporary " "Discord might be having temporary technical "
"technical issues.\nIn this case, press enter and " "issues.\nIn this case, press enter and try again "
"try again later.\nOtherwise you can type 'reset' to " "later.\nOtherwise you can type 'reset' to reset "
"reset the current credentials and set them " "the current credentials and set them again the "
"again the next start.\n> ") "next start.\n> ")
if choice.lower().strip() == "reset": if choice.lower().strip() == "reset":
settings.token = None bot.settings.token = None
settings.email = None bot.settings.email = None
settings.password = None bot.settings.password = None
settings.save_settings() bot.settings.save_settings()
except KeyboardInterrupt: except KeyboardInterrupt:
loop.run_until_complete(bot.logout()) loop.run_until_complete(bot.logout())
except: except Exception as e:
error = True error = True
logger.error(traceback.format_exc()) bot.logger.exception("Fatal exception, attempting graceful logout",
exc_info=e)
loop.run_until_complete(bot.logout()) loop.run_until_complete(bot.logout())
finally: finally:
loop.close() loop.close()