From a86389c9e88338a11b0ad629739a38386d599c17 Mon Sep 17 00:00:00 2001 From: Twentysix Date: Sat, 13 Feb 2016 09:46:25 +0100 Subject: [PATCH 1/3] Added ignore channels/servers capabilities Changed !help behaviour --- cogs/mod.py | 120 ++++++++++++++++++++++++++++++++++++++++++++++------ red.py | 9 +++- 2 files changed, 114 insertions(+), 15 deletions(-) diff --git a/cogs/mod.py b/cogs/mod.py index d406de517..a293ad175 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -5,16 +5,14 @@ from .utils.dataIO import fileIO import __main__ import os -main_path = os.path.dirname(os.path.realpath(__main__.__file__)) - - class Mod: """Moderation tools.""" def __init__(self, bot): self.bot = bot - self.whitelist_list = fileIO(main_path + "/data/mod/whitelist.json", "load") - self.blacklist_list = fileIO(main_path + "/data/mod/blacklist.json", "load") + self.whitelist_list = fileIO("data/mod/whitelist.json", "load") + self.blacklist_list = fileIO("data/mod/blacklist.json", "load") + self.ignore_list = fileIO("data/mod/ignorelist.json", "load") @commands.command(no_pm=True) @checks.admin_or_permissions(kick_members=True) @@ -130,7 +128,7 @@ class Mod: """Adds user to bot's blacklist""" if user.id not in self.blacklist_list: self.blacklist_list.append(user.id) - fileIO(main_path + "/data/mod/blacklist.json", "save", self.blacklist_list) + fileIO("data/mod/blacklist.json", "save", self.blacklist_list) await self.bot.say("User has been added to blacklist.") else: await self.bot.say("User is already blacklisted.") @@ -140,7 +138,7 @@ class Mod: """Removes user to bot's blacklist""" if user.id in self.blacklist_list: self.blacklist_list.remove(user.id) - fileIO(main_path + "/data/mod/blacklist.json", "save", self.blacklist_list) + fileIO("data/mod/blacklist.json", "save", self.blacklist_list) await self.bot.say("User has been removed from blacklist.") else: await self.bot.say("User is not in blacklist.") @@ -162,7 +160,7 @@ class Mod: else: msg = "" self.whitelist_list.append(user.id) - fileIO(main_path + "/data/mod/whitelist.json", "save", self.whitelist_list) + fileIO("data/mod/whitelist.json", "save", self.whitelist_list) await self.bot.say("User has been added to whitelist." + msg) else: await self.bot.say("User is already whitelisted.") @@ -172,11 +170,99 @@ class Mod: """Removes user to bot's whitelist""" if user.id in self.whitelist_list: self.whitelist_list.remove(user.id) - fileIO(main_path + "/data/mod/whitelist.json", "save", self.whitelist_list) + fileIO("data/mod/whitelist.json", "save", self.whitelist_list) await self.bot.say("User has been removed from whitelist.") else: await self.bot.say("User is not in whitelist.") + @commands.group(pass_context=True, no_pm=True) + @checks.admin_or_permissions(manage_channels=True) + async def ignore(self, ctx): + """Adds servers/channels to ignorelist""" + if ctx.invoked_subcommand is None: + + await self.bot.say(self.count_ignored() + "Type help ignore for info.") + + @ignore.command(name="channel", pass_context=True) + async def ignore_channel(self, ctx, channel : discord.Channel=None): + """Ignores channel + + Defaults to current one""" + current_ch = ctx.message.channel + if not channel: + if current_ch.id not in self.ignore_list["CHANNELS"]: + self.ignore_list["CHANNELS"].append(current_ch.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("Channel added to ignore list.") + else: + await self.bot.say("Channel already in ignore list.") + else: + if channel.id not in self.ignore_list["CHANNELS"]: + self.ignore_list["CHANNELS"].append(channel.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("Channel added to ignore list.") + else: + await self.bot.say("Channel already in ignore list.") + + + @ignore.command(name="server", pass_context=True) + async def ignore_server(self, ctx): + """Ignores current server""" + server = ctx.message.server + if server.id not in self.ignore_list["SERVERS"]: + self.ignore_list["SERVERS"].append(server.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("This server has been added to the ignore list.") + else: + await self.bot.say("This server is already being ignored.") + + @commands.group(pass_context=True, no_pm=True) + @checks.admin_or_permissions(manage_channels=True) + async def unignore(self, ctx): + """Removes servers/channels from ignorelist""" + if ctx.invoked_subcommand is None: + await self.bot.say(self.count_ignored() + "Type help unignore for info.") + + @unignore.command(name="channel", pass_context=True) + async def unignore_channel(self, ctx, channel : discord.Channel=None): + """Removes channel from ignore list + + Defaults to current one""" + current_ch = ctx.message.channel + if not channel: + if current_ch.id in self.ignore_list["CHANNELS"]: + self.ignore_list["CHANNELS"].remove(current_ch.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("This channel has been removed from the ignore list.") + else: + await self.bot.say("This channel is not in the ignore list.") + else: + if channel.id in self.ignore_list["CHANNELS"]: + self.ignore_list["CHANNELS"].remove(channel.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("Channel removed from ignore list.") + else: + await self.bot.say("That channel is not in the ignore list.") + + + @unignore.command(name="server", pass_context=True) + async def unignore_server(self, ctx): + """Removes current server from ignore list""" + server = ctx.message.server + if server.id in self.ignore_list["SERVERS"]: + self.ignore_list["SERVERS"].remove(server.id) + fileIO("data/mod/ignorelist.json", "save", self.ignore_list) + await self.bot.say("This server has been removed from the ignore list.") + else: + await self.bot.say("This server is not in the ignore list.") + + def count_ignored(self): + msg = "```Currently ignoring:\n" + msg += str(len(self.ignore_list["CHANNELS"])) + " channels\n" + msg += str(len(self.ignore_list["SERVERS"])) + " servers\n```\n" + return msg + + def check_folders(): folders = ("data", "data/mod/") for folder in folders: @@ -185,13 +271,19 @@ def check_folders(): os.makedirs(folder) def check_files(): - if not os.path.isfile(main_path + "/data/mod/blacklist.json"): - print("Creating empty blacklist.json...") - fileIO(main_path + "/data/mod/blacklist.json", "save", []) + ignore_list = {"SERVERS" : [], "CHANNELS" : []} - if not os.path.isfile(main_path + "/data/mod/whitelist.json"): + if not os.path.isfile("data/mod/blacklist.json"): + print("Creating empty blacklist.json...") + fileIO("data/mod/blacklist.json", "save", []) + + if not os.path.isfile("data/mod/whitelist.json"): print("Creating empty whitelist.json...") - fileIO(main_path + "/data/mod/whitelist.json", "save", []) + fileIO("data/mod/whitelist.json", "save", []) + + if not os.path.isfile("data/mod/ignorelist.json"): + print("Creating empty ignorelist.json...") + fileIO("data/mod/ignorelist.json", "save", ignore_list) def setup(bot): check_folders() diff --git a/red.py b/red.py index 4e6f64f74..b8368e1cb 100644 --- a/red.py +++ b/red.py @@ -26,7 +26,7 @@ Red - A multifunction Discord bot by Twentysix formatter = commands.HelpFormatter(show_check_failure=False) bot = commands.Bot(command_prefix=["_"], formatter=formatter, - description=description, pm_help=True) + description=description, pm_help=None) lock = False @@ -65,6 +65,13 @@ async def on_message(message): if author.id not in mod.whitelist_list: return + if not message.channel.is_private: + if message.server.id in mod.ignore_list["SERVERS"]: + return + + if message.channel.id in mod.ignore_list["CHANNELS"]: + return + await bot.process_commands(message) @bot.command() From dc0bf207b8c2b14c142b40ae73b920d99920b289 Mon Sep 17 00:00:00 2001 From: Twentysix Date: Sat, 13 Feb 2016 14:52:26 +0100 Subject: [PATCH 2/3] Added !server, !join and some general improvements Audio's task now properly checks if the audio module is still loaded Added !server to cogs.general for server info Added !join (owner only) to make Red join other servers settings.json now gets deleted if login fails --- cogs/audio.py | 6 +--- cogs/general.py | 44 +++++++++++++++++++++-------- cogs/mod.py | 1 - red.py | 75 +++++++++++++++++++++++++++++++++++-------------- 4 files changed, 87 insertions(+), 39 deletions(-) diff --git a/cogs/audio.py b/cogs/audio.py index 6c4a8ebc5..f1568d63a 100644 --- a/cogs/audio.py +++ b/cogs/audio.py @@ -41,7 +41,6 @@ class Audio: self.playlist = [] self.current = -1 #current track index in self.playlist self.downloader = {"DONE" : False, "TITLE" : False, "ID" : False, "URL" : False, "DURATION" : False, "DOWNLOADING" : False} - self.quit_manager = False self.skip_votes = [] self.sing = ["https://www.youtube.com/watch?v=zGTkAVsrfg8", "https://www.youtube.com/watch?v=cGMWL8cOeAU", @@ -422,7 +421,7 @@ class Audio: return False async def queue_manager(self): - while not self.quit_manager: + while "Audio" in self.bot.cogs: if self.queue and not self.music_player.is_playing(): new_link = self.queue[0] self.queue.pop(0) @@ -453,10 +452,7 @@ class Audio: async def incoming_messages(self, msg): # Workaround, need to fix if msg.author.id != self.bot.user.id: - cmds = ("unload cogs.audio", "reload cogs.audio") - if msg.content in cmds: - self.quit_manager = True if msg.channel.is_private and msg.attachments != []: await self.transfer_playlist(msg) if not msg.channel.is_private: diff --git a/cogs/general.py b/cogs/general.py index 58210f3b7..aef8a356b 100644 --- a/cogs/general.py +++ b/cogs/general.py @@ -133,30 +133,50 @@ class General: await self.bot.say("http://lmgtfy.com/?q=" + text) @commands.command(no_pm=True, hidden=True) - async def hug(self, member : discord.Member = None): + async def hug(self, user : discord.Member = None): """Because everyone likes hugs""" - await self.bot.say("(っ´▽`)っ" + " *" + member.name + "*") + await self.bot.say("(っ´▽`)っ" + " *" + user.name + "*") @commands.command(pass_context=True, no_pm=True) - async def info(self, ctx, member : discord.Member = None): - """Shows member's information""" + async def info(self, ctx, user : discord.Member = None): + """Shows users's informations""" author = ctx.message.author - if not member: - member = author + if not user: + user = author roles = [] - for m in member.roles: + for m in user.roles: if m.name != "@everyone": roles.append('"' + m.name + '"') #.replace("@", "@\u200b") if not roles: roles = ["None"] data = "```\n" - data += "Name: " + member.name + "\n" - data += "ID: " + member.id + "\n" - data += "Joined: " + str(member.joined_at) + "\n" + data += "Name: " + user.name + "\n" + data += "ID: " + user.id + "\n" + data += "Joined: " + str(user.joined_at) + "\n" data += "Roles: " + " ".join(roles) + "\n" - data += "Avatar: " + member.avatar_url + "\n" + data += "Avatar: " + user.avatar_url + "\n" data += "```" await self.bot.say(data) + @commands.command(pass_context=True, no_pm=True) + async def server(self, ctx): + """Shows server's informations""" + server = ctx.message.server + online = str(len([m.status for m in server.members if str(m.status) == "online" or str(m.status) == "idle"])) + total = str(len(server.members)) + + data = "```\n" + data += "Name: " + server.name + "\n" + data += "ID: " + server.id + "\n" + data += "Region: " + server.region.name + "\n" + data += "Users: " + online + "/" + total + "\n" + data += "Channels: " + str(len(server.channels)) + "\n" + data += "Roles: " + str(len(server.roles)) + "\n" + data += "Created: " + str(server.owner.joined_at) + "\n" + data += "Owner: " + server.owner.name + "\n" + data += "Icon: " + str(server.icon_url) + "\n" + data += "```" + await self.bot.say(data) + @commands.command() async def urban(self, *, search_terms : str): """Urban Dictionary search""" @@ -275,4 +295,4 @@ class NewPoll(): def setup(bot): n = General(bot) bot.add_listener(n.check_poll_votes, "on_message") - bot.add_cog(n) + bot.add_cog(n) \ No newline at end of file diff --git a/cogs/mod.py b/cogs/mod.py index a293ad175..cfd329c53 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -2,7 +2,6 @@ import discord from discord.ext import commands from .utils import checks from .utils.dataIO import fileIO -import __main__ import os class Mod: diff --git a/red.py b/red.py index b8368e1cb..5e2445362 100644 --- a/red.py +++ b/red.py @@ -9,6 +9,7 @@ import copy import glob import os import time +import sys # # Red, a Discord bot by Twentysix, based on discord.py and its command extension @@ -175,6 +176,18 @@ async def shutdown(): """Shuts down Red""" exit(1) +@bot.command() +@checks.is_owner() +async def join(invite_url : discord.Invite): + """Joins new server""" + try: + await bot.accept_invite(invite_url) + await bot.say("Server joined.") + except discord.NotFound: + await bot.say("The invite was invalid or expired.") + except discord.HTTPException: + await bot.say("I wasn't able to accept the invite. Try again.") + @bot.command() @checks.is_owner() async def setprefix(*text): @@ -244,9 +257,6 @@ def check_configs(): settings_path = "data/red/settings.json" settings = {"EMAIL" : "EmailHere", "PASSWORD" : "PasswordHere", "OWNER" : "id_here", "PREFIXES" : [], "ADMIN_ROLE" : "Transistor", "MOD_ROLE" : "Process"} if not os.path.isfile(settings_path): - print("Creating new settings.json...") - with open(settings_path, "w") as f: - f.write(json.dumps(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)") @@ -262,7 +272,7 @@ def check_configs(): print("\nChoose a prefix (or multiple ones, one at once) for the commands. Type exit when you're done. Example prefix: !") settings["PREFIXES"] = [] new_prefix = "" - while new_prefix.lower() != "exit": + while new_prefix.lower() != "exit" and settings["PREFIXES"] != []: new_prefix = input("Prefix> ") if new_prefix.lower() != "exit" and new_prefix != "": settings["PREFIXES"].append(new_prefix) @@ -310,6 +320,14 @@ def set_cog(cog, value): f.write(json.dumps(data)) def load_cogs(): + try: + if sys.argv[1] == "--no-prompt": + no_prompt = True + else: + no_prompt = False + except: + no_prompt = False + with open('data/red/cogs.json', "r") as f: data = json.load(f) register = tuple(data.keys()) #known cogs @@ -327,17 +345,18 @@ def load_cogs(): print(e) failed.append(extension) else: - print("\nNew extension: " + extension) - print("Load it?(y/n)") - if get_answer(): - data[extension] = True - try: - bot.load_extension(extension) - except Exception as e: - print(e) - failed.append(extension) - else: - data[extension] = False + if not no_prompt: + print("\nNew extension: " + extension) + print("Load it?(y/n)") + if get_answer(): + data[extension] = True + try: + bot.load_extension(extension) + except Exception as e: + print(e) + failed.append(extension) + else: + data[extension] = False if extensions: with open('data/red/cogs.json', "w") as f: @@ -349,12 +368,26 @@ def load_cogs(): print(m + " ", end="") print("\n") -check_folders() -check_configs() -settings = load_settings() - -if __name__ == '__main__': +def main(): + global settings + check_folders() + check_configs() + settings = load_settings() checks.owner = settings["OWNER"] load_cogs() bot.command_prefix = settings["PREFIXES"] - bot.run(settings['EMAIL'], settings['PASSWORD']) + yield from bot.login(settings["EMAIL"], settings["PASSWORD"]) + yield from bot.connect() + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + try: + loop.run_until_complete(main()) + except discord.LoginFailure: + print("Invalid login credentials. Restart Red and configure it properly.") + os.remove('data/red/settings.json') # Hopefully this won't backfire in case of discord servers' problems + except Exception as e: + print(e) + loop.run_until_complete(bot.logout()) + finally: + loop.close() From 8ef6853f281f59941799935dfa434f9d2a29d989 Mon Sep 17 00:00:00 2001 From: Twentysix Date: Sat, 13 Feb 2016 15:02:00 +0100 Subject: [PATCH 3/3] Hotfix Also, added --no-prompt command line option in last commit. When started with it, Red will ignore new modules. Useful when it's hosted on servers. --- red.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/red.py b/red.py index 5e2445362..72ae46126 100644 --- a/red.py +++ b/red.py @@ -272,7 +272,7 @@ def check_configs(): print("\nChoose a prefix (or multiple ones, one at once) for the commands. Type exit when you're done. Example prefix: !") settings["PREFIXES"] = [] new_prefix = "" - while new_prefix.lower() != "exit" and 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)