Merge pull request #78 from tekulvw/multiserver-roles

Server based admin/mod roles, added cog alias.py, improved settings management
This commit is contained in:
Twentysix 2016-02-29 13:46:41 +01:00
commit e0aa5eb08a
12 changed files with 439 additions and 102 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ __pycache__
data data
!data/trivia/* !data/trivia/*
!data/audio/playlists/* !data/audio/playlists/*
*.exe
*.dll

121
cogs/alias.py Normal file
View File

@ -0,0 +1,121 @@
import discord
from discord.ext import commands
from .utils.chat_formatting import *
from .utils.dataIO import fileIO
from .utils import checks
from __main__ import send_cmd_help
import os
class Alias:
def __init__(self,bot):
self.bot = bot
self.aliases = fileIO("data/alias/aliases.json","load")
@commands.group(pass_context=True)
@checks.mod_or_permissions(manage_server=True)
async def alias(self,ctx):
"""Manage per-server aliases for commands"""
if ctx.invoked_subcommand is None:
await send_cmd_help(ctx)
@alias.command(name="add",pass_context=True)
async def _add_alias(self,ctx,command : str,*,to_execute):
"""Add an alias for a command
Example: !alias add test penis @TwentySix"""
server = ctx.message.server
if self.get_prefix(to_execute) == False:
to_execute = self.bot.command_prefix[0] + to_execute
if server.id not in self.aliases:
self.aliases[server.id] = {}
#curr_aliases = self.aliases[server.id]
if command not in self.bot.commands:
self.aliases[server.id][command] = to_execute
fileIO("data/alias/aliases.json","save",self.aliases)
await self.bot.say("Alias '{}' added.".format(command))
else:
await self.bot.say("Cannot add '{}' because it's a real bot command.".format(command))
@alias.command(name="help",pass_context=True)
async def _help_alias(self,ctx,command):
"""Tries to execute help for the base command of the alias"""
server = ctx.message.server
if server.id in self.aliases:
server_aliases = self.aliases[server.id]
if command in server_aliases:
help_cmd = server_aliases[command].split(" ")[0]
new_content = self.bot.command_prefix[0]
new_content += "help "
new_content += help_cmd[len(self.get_prefix(help_cmd)):]
message = ctx.message
message.content = new_content
await self.bot.process_commands(message)
else:
await self.bot.say("That alias doesn't exist.")
@alias.command(name="show",pass_context=True)
async def _show_alias(self,ctx,command):
"""Shows what command the alias executes."""
server = ctx.message.server
if server.id in self.aliases:
server_aliases = self.aliases[server.id]
if command in server_aliases:
await self.bot.say(box(server_aliases[command]))
else:
await self.bot.say("That alias doesn't exist.")
@alias.command(name="del",pass_context=True)
async def _del_alias(self,ctx,command : str):
"""Deletes an alias"""
server = ctx.message.server
if server.id in self.aliases:
self.aliases[server.id].pop(command,None)
fileIO("data/alias/aliases.json","save",self.aliases)
await self.bot.say("Alias '{}' deleted.".format(command))
async def check_aliases(self,message):
if message.author.id == self.bot.user.id or len(message.content) < 2 or message.channel.is_private:
return
msg = message.content
server = message.server
prefix = self.get_prefix(msg)
if prefix and server.id in self.aliases:
aliaslist = self.aliases[server.id]
alias = msg[len(prefix):].split(" ")[0]
args = msg[len(self.first_word(message.content)):]
if alias in aliaslist.keys():
content = aliaslist[alias] + args
new_message = message
new_message.content = content
await self.bot.process_commands(new_message)
def first_word(self,msg):
return msg.split(" ")[0]
def get_prefix(self, msg):
for p in self.bot.command_prefix:
if msg.startswith(p):
return p
return False
def check_folder():
if not os.path.exists("data/alias"):
print("Creating data/alias folder...")
os.makedirs("data/alias")
def check_file():
aliases = {}
f = "data/alias/aliases.json"
if not fileIO(f, "check"):
print("Creating default alias's aliases.json...")
fileIO(f, "save", aliases)
def setup(bot):
check_folder()
check_file()
n = Alias(bot)
bot.add_listener(n.check_aliases, "on_message")
bot.add_cog(n)

View File

@ -2,21 +2,31 @@ import discord
from discord.ext import commands from discord.ext import commands
import asyncio import asyncio
import threading import threading
import youtube_dl
import os import os
from random import choice as rndchoice from random import choice as rndchoice
from random import shuffle from random import shuffle
from .utils.dataIO import fileIO from .utils.dataIO import fileIO
from .utils import checks from .utils import checks
from __main__ import send_cmd_help from __main__ import send_cmd_help
from __main__ import settings as bot_settings
import glob import glob
import re import re
import aiohttp import aiohttp
import json import json
import time import time
if not discord.opus.is_loaded(): try:
import youtube_dl
except:
youtube_dl = None
try:
if not discord.opus.is_loaded():
discord.opus.load_opus('libopus-0.dll') discord.opus.load_opus('libopus-0.dll')
except:
opus = None
else:
opus = True
youtube_dl_options = { youtube_dl_options = {
'format': 'bestaudio/best', 'format': 'bestaudio/best',
@ -60,7 +70,7 @@ class Audio:
msg = ctx.message msg = ctx.message
if await self.check_voice(msg.author, msg): if await self.check_voice(msg.author, msg):
if self.is_playlist_valid([link]): # reusing a function if self.is_playlist_valid([link]): # reusing a function
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
self.queue = [] self.queue = []
self.current = -1 self.current = -1
self.playlist = [] self.playlist = []
@ -119,7 +129,7 @@ class Audio:
""" """
msg = ctx.message msg = ctx.message
if self.music_player.is_playing(): if self.music_player.is_playing():
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
self.music_player.paused = False self.music_player.paused = False
self.music_player.stop() self.music_player.stop()
else: else:
@ -168,7 +178,7 @@ class Audio:
files.extend(glob.glob("data/audio/localtracks/" + name + "/*.mp3")) files.extend(glob.glob("data/audio/localtracks/" + name + "/*.mp3"))
if glob.glob("data/audio/localtracks/" + name + "/*.flac"): if glob.glob("data/audio/localtracks/" + name + "/*.flac"):
files.extend(glob.glob("data/audio/localtracks/" + name + "/*.flac")) files.extend(glob.glob("data/audio/localtracks/" + name + "/*.flac"))
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
if await self.check_voice(msg.author, ctx.message): if await self.check_voice(msg.author, ctx.message):
self.queue = [] self.queue = []
self.current = -1 self.current = -1
@ -188,7 +198,7 @@ class Audio:
""" """
msg = ctx.message msg = ctx.message
if self.music_player.is_playing(): if self.music_player.is_playing():
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
self.current = -1 self.current = -1
self.playlist = [self.downloader["URL"]] self.playlist = [self.downloader["URL"]]
await self.bot.say("I will play this song on repeat.") await self.bot.say("I will play this song on repeat.")
@ -201,7 +211,7 @@ class Audio:
""" """
msg = ctx.message msg = ctx.message
if self.music_player.is_playing(): if self.music_player.is_playing():
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
if self.playlist: if self.playlist:
shuffle(self.playlist) shuffle(self.playlist)
await self.bot.say("The order of this playlist has been mixed") await self.bot.say("The order of this playlist has been mixed")
@ -214,7 +224,7 @@ class Audio:
""" """
msg = ctx.message msg = ctx.message
if self.music_player.is_playing() and self.playlist: if self.music_player.is_playing() and self.playlist:
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
self.current -= 2 self.current -= 2
if self.current == -1: if self.current == -1:
self.current = len(self.playlist) -3 self.current = len(self.playlist) -3
@ -231,7 +241,7 @@ class Audio:
""" """
msg = ctx.message msg = ctx.message
if self.music_player.is_playing(): if self.music_player.is_playing():
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
await self.close_audio() await self.close_audio()
else: else:
await self.bot.say("You can't stop music when there are other people in the channel! Vote to skip instead.") await self.bot.say("You can't stop music when there are other people in the channel! Vote to skip instead.")
@ -273,14 +283,16 @@ class Audio:
else: else:
await self.bot.say("That link is now allowed.") await self.bot.say("That link is now allowed.")
async def is_alone_or_admin(self, author): #Direct control. fix everything async def is_alone_or_admin(self, message): #Direct control. fix everything
author = message.author
server = message.server
if not self.settings["QUEUE_MODE"]: if not self.settings["QUEUE_MODE"]:
return True return True
elif author.id == checks.settings["OWNER"]: elif author.id == bot_settings.owner:
return True return True
elif discord.utils.get(author.roles, name=checks.settings["ADMIN_ROLE"]) is not None: elif discord.utils.get(author.roles, name=bot_settings.get_server_admin(server)) is not None:
return True return True
elif discord.utils.get(author.roles, name=checks.settings["MOD_ROLE"]) is not None: elif discord.utils.get(author.roles, name=bot_settings.get_server_mod(server)) is not None:
return True return True
elif len(author.voice_channel.voice_members) in (1, 2): elif len(author.voice_channel.voice_members) in (1, 2):
return True return True
@ -299,7 +311,7 @@ class Audio:
self.queue = [] self.queue = []
await self.play_video(rndchoice(self.sing)) await self.play_video(rndchoice(self.sing))
else: else:
if await self.is_alone_or_admin(msg.author): if await self.is_alone_or_admin(msg):
self.queue = [] self.queue = []
await self.play_video(rndchoice(self.sing)) await self.play_video(rndchoice(self.sing))
else: else:
@ -775,6 +787,12 @@ def check_files():
def setup(bot): def setup(bot):
check_folders() check_folders()
check_files() 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() loop = asyncio.get_event_loop()
n = Audio(bot) n = Audio(bot)
loop.create_task(n.queue_manager()) loop.create_task(n.queue_manager())

View File

@ -24,8 +24,9 @@ class CustomCommands:
await send_cmd_help(ctx) await send_cmd_help(ctx)
return return
server = ctx.message.server server = ctx.message.server
to_replace = ctx.prefix + "addcom " + command + " " to_replace = ctx.message.content.find(text[0])
text = ctx.message.content.replace(to_replace, "") text = ctx.message.content[to_replace:]
command = command.lower()
if not server.id in self.c_commands: if not server.id in self.c_commands:
self.c_commands[server.id] = {} self.c_commands[server.id] = {}
cmdlist = self.c_commands[server.id] cmdlist = self.c_commands[server.id]
@ -49,8 +50,9 @@ class CustomCommands:
await send_cmd_help(ctx) await send_cmd_help(ctx)
return return
server = ctx.message.server server = ctx.message.server
to_replace = ctx.prefix + "editcom " + command + " " to_replace = ctx.message.content.find(text[0])
text = ctx.message.content.replace(to_replace, "") text = ctx.message.content[to_replace:]
command = command.lower()
if server.id in self.c_commands: if server.id in self.c_commands:
cmdlist = self.c_commands[server.id] cmdlist = self.c_commands[server.id]
if command in cmdlist: if command in cmdlist:
@ -71,6 +73,7 @@ class CustomCommands:
Example: Example:
!delcom yourcommand""" !delcom yourcommand"""
server = ctx.message.server server = ctx.message.server
command = command.lower()
if server.id in self.c_commands: if server.id in self.c_commands:
cmdlist = self.c_commands[server.id] cmdlist = self.c_commands[server.id]
if command in cmdlist: if command in cmdlist:

View File

@ -1,9 +1,9 @@
import discord import discord
from discord.ext import commands from discord.ext import commands
from .utils.dataIO import fileIO from .utils.dataIO import fileIO
from .utils import checks
from random import randint from random import randint
from copy import deepcopy from copy import deepcopy
from .utils import checks
from __main__ import send_cmd_help from __main__ import send_cmd_help
import os import os
import time import time

View File

@ -21,6 +21,11 @@ class General:
"Don't count on it", "My reply is no", "My sources say no", "Outlook not so good", "Very doubtful"] "Don't count on it", "My reply is no", "My sources say no", "Outlook not so good", "Very doubtful"]
self.poll_sessions = [] self.poll_sessions = []
@commands.command()
async def ping(self):
"""Pong."""
self.bot.say("Pong.")
@commands.command() @commands.command()
async def choose(self, *choices): async def choose(self, *choices):
"""Chooses between multiple choices. """Chooses between multiple choices.

View File

@ -1,10 +1,11 @@
import discord import discord
from discord.ext import commands from discord.ext import commands
from .utils import checks
from .utils.dataIO import fileIO from .utils.dataIO import fileIO
from __main__ import send_cmd_help from .utils import checks
from __main__ import send_cmd_help, settings
import os import os
import logging import logging
import json
class Mod: class Mod:
"""Moderation tools.""" """Moderation tools."""
@ -16,6 +17,36 @@ class Mod:
self.ignore_list = fileIO("data/mod/ignorelist.json", "load") self.ignore_list = fileIO("data/mod/ignorelist.json", "load")
self.filter = fileIO("data/mod/filter.json", "load") self.filter = fileIO("data/mod/filter.json", "load")
@commands.group(pass_context=True,no_pm=True)
@checks.serverowner_or_permissions(manage_server=True)
async def modset(self,ctx):
"""Manages server administration settings."""
if ctx.invoked_subcommand is None:
await send_cmd_help(ctx)
msg = "```"
for k, v in settings.get_server(ctx.message.server).items():
msg += str(k) + ": " + str(v) + "\n"
msg += "```"
await self.bot.say(msg)
@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."""
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))
@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."""
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))
@commands.command(no_pm=True, pass_context=True) @commands.command(no_pm=True, pass_context=True)
@checks.admin_or_permissions(kick_members=True) @checks.admin_or_permissions(kick_members=True)
async def kick(self, ctx, user : discord.Member): async def kick(self, ctx, user : discord.Member):
@ -32,17 +63,17 @@ class Mod:
@commands.command(no_pm=True, pass_context=True) @commands.command(no_pm=True, pass_context=True)
@checks.admin_or_permissions(ban_members=True) @checks.admin_or_permissions(ban_members=True)
async def ban(self, ctx, user : discord.Member, purge_msg : int=0): async def ban(self, ctx, user : discord.Member, days : int=0):
"""Bans user and deletes last X days worth of messages. """Bans user and deletes last X days worth of messages.
Minimum 0 days, maximum 7. Defaults to 0.""" Minimum 0 days, maximum 7. Defaults to 0."""
author = ctx.message.author author = ctx.message.author
if purge_msg < 0 or purge_msg > 7: if days < 0 or days > 7:
await self.bot.say("Invalid days. Must be between 0 and 7.") await self.bot.say("Invalid days. Must be between 0 and 7.")
return return
try: try:
await self.bot.ban(user, days) await self.bot.ban(user, days)
logger.info("{}({}) banned {}({}), deleting {} days worth of messages".format(author.name, author.id, user.name, user.id, str(purge_msg))) logger.info("{}({}) banned {}({}), deleting {} days worth of messages".format(author.name, author.id, user.name, user.id, str(days)))
await self.bot.say("Done. It was about time.") await self.bot.say("Done. It was about time.")
except discord.errors.Forbidden: except discord.errors.Forbidden:
await self.bot.say("I'm not allowed to do that.") await self.bot.say("I'm not allowed to do that.")
@ -398,11 +429,15 @@ class Mod:
def immune_from_filter(self, message): def immune_from_filter(self, message):
user = message.author 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 return True
elif discord.utils.get(user.roles, name=checks.settings["ADMIN_ROLE"]): elif discord.utils.get(user.roles, name=admin_role):
return True return True
elif discord.utils.get(user.roles, name=checks.settings["MOD_ROLE"]): elif discord.utils.get(user.roles, name=mod_role):
return True return True
else: else:
return False return False

View File

@ -0,0 +1,17 @@
def bold(text):
return "**"+str(text)+"**"
def italics(text):
return "*"+str(text)+"*"
def strikethrough(text):
return "~~"+str(text)+"~~"
def underline(text):
return "__"+str(text)+"__"
def box(text):
return "```"+str(text)+"```"
def inline(text):
return "`"+str(text)+"`"

View File

@ -1,7 +1,8 @@
from discord.ext import commands from discord.ext import commands
import discord.utils import discord.utils
import os.path from cogs.utils.settings import Settings
import json from cogs.utils.dataIO import fileIO
from __main__ import settings
# #
# This is a modified version of checks.py, originally made by Rapptz # This is a modified version of checks.py, originally made by Rapptz
@ -10,14 +11,8 @@ import json
# https://github.com/Rapptz/RoboDanny/tree/async # https://github.com/Rapptz/RoboDanny/tree/async
# #
try:
with open("data/red/settings.json", "r") as f:
settings = json.loads(f.read())
except:
settings = {"OWNER" : False, "ADMIN_ROLE" : False, "MOD_ROLE" : False}
def is_owner_check(ctx): def is_owner_check(ctx):
return ctx.message.author.id == settings["OWNER"] return ctx.message.author.id == settings.owner
def is_owner(): def is_owner():
return commands.check(is_owner_check) return commands.check(is_owner_check)
@ -55,12 +50,28 @@ def role_or_permissions(ctx, check, **perms):
def mod_or_permissions(**perms): def mod_or_permissions(**perms):
def predicate(ctx): def predicate(ctx):
return role_or_permissions(ctx, lambda r: r.name in (settings["MOD_ROLE"], settings["ADMIN_ROLE"]), **perms) server = ctx.message.server
mod_role = settings.get_server_mod(server).lower()
admin_role = settings.get_server_admin(server).lower()
return role_or_permissions(ctx, lambda r: r.name.lower() in (mod_role,admin_role), **perms)
return commands.check(predicate) return commands.check(predicate)
def admin_or_permissions(**perms): def admin_or_permissions(**perms):
def predicate(ctx): def predicate(ctx):
return role_or_permissions(ctx, lambda r: r.name == settings["ADMIN_ROLE"], **perms) 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) return commands.check(predicate)
def serverowner_or_permissions(**perms):
def predicate(ctx):
server = ctx.message.server
owner = server.owner
if ctx.message.author.id == owner.id:
return True
return check_permissions(ctx,perms)
return commands.check(predicate)

View File

@ -3,7 +3,7 @@ import json
def fileIO(filename, IO, data=None): def fileIO(filename, IO, data=None):
if IO == "save" and data != None: if IO == "save" and data != None:
with open(filename, encoding='utf-8', mode="w") as f: 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: elif IO == "load" and data == None:
with open(filename, encoding='utf-8', mode="r") as f: with open(filename, encoding='utf-8', mode="r") as f:
return json.loads(f.read()) return json.loads(f.read())

149
cogs/utils/settings.py Normal file
View File

@ -0,0 +1,149 @@
from .dataIO import fileIO
import discord
import os
default_path = "data/red/settings.json"
class Settings:
def __init__(self,path=default_path):
self.path = path
self.check_folders()
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 check_folders(self):
folders = ("data", os.path.dirname(self.path), "cogs", "cogs/utils")
for folder in folders:
if not os.path.exists(folder):
print("Creating " + folder + " folder...")
os.makedirs(folder)
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"]
@email.setter
def email(self,value):
self.bot_settings["EMAIL"] = value
self.save_settings()
@property
def password(self):
return self.bot_settings["PASSWORD"]
@password.setter
def password(self,value):
self.bot_settings["PASSWORD"] = value
self.save_settings()
@property
def prefixes(self):
return self.bot_settings["PREFIXES"]
@prefixes.setter
def prefixes(self,value):
assert isinstance(value,list)
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(self,server):
assert isinstance(server,discord.Server)
return self.bot_settings.get(server.id,self.bot_settings["default"]).copy()
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()

100
red.py
View File

@ -1,6 +1,6 @@
from discord.ext import commands from discord.ext import commands
import discord import discord
from cogs.utils import checks from cogs.utils.settings import Settings
from random import choice as rndchoice from random import choice as rndchoice
import threading import threading
import datetime, re import datetime, re
@ -31,6 +31,10 @@ formatter = commands.HelpFormatter(show_check_failure=False)
bot = commands.Bot(command_prefix=["_"], formatter=formatter, bot = commands.Bot(command_prefix=["_"], formatter=formatter,
description=description, pm_help=None) description=description, pm_help=None)
settings = Settings()
from cogs.utils import checks
lock = False lock = False
@bot.event @bot.event
@ -150,7 +154,7 @@ async def debug(ctx, *, code : str):
result = python.format(result) result = python.format(result)
if not ctx.message.channel.is_private: if not ctx.message.channel.is_private:
censor = (settings["EMAIL"], settings["PASSWORD"]) censor = (settings.email, settings.password)
r = "[EXPUNGED]" r = "[EXPUNGED]"
for w in censor: for w in censor:
result = result.replace(w, r) result = result.replace(w, r)
@ -169,8 +173,7 @@ async def owner(ctx):
"""Sets owner""" """Sets owner"""
global lock global lock
msg = ctx.message msg = ctx.message
data = load_settings() if settings.owner != "id_here":
if data["OWNER"] != "id_here":
await bot.say("Owner ID has already been set.") await bot.say("Owner ID has already been set.")
return return
if lock: if lock:
@ -192,10 +195,7 @@ async def prefix(*prefixes):
await bot.say("Example: setprefix [ ! ^ .") await bot.say("Example: setprefix [ ! ^ .")
return return
bot.command_prefix = list(prefixes) bot.command_prefix = list(prefixes)
data = load_settings() settings.prefixes = list(prefixes)
data["PREFIXES"] = list(prefixes)
with open("data/red/settings.json", "w") as f:
f.write(json.dumps(data))
if len(prefixes) > 1: if len(prefixes) > 1:
await bot.say("Prefixes set") await bot.say("Prefixes set")
else: else:
@ -207,7 +207,7 @@ async def name(ctx, *name : str):
"""Sets Red's name""" """Sets Red's name"""
if name == (): if name == ():
await send_cmd_help(ctx) 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.") await bot.say("Done.")
@_set.command(pass_context=True) @_set.command(pass_context=True)
@ -227,7 +227,7 @@ async def avatar(url : str):
try: try:
async with aiohttp.get(url) as r: async with aiohttp.get(url) as r:
data = await r.read() data = await r.read()
await bot.edit_profile(settings["PASSWORD"], avatar=data) await bot.edit_profile(settings.password, avatar=data)
await bot.say("Done.") await bot.say("Done.")
except: except:
await bot.say("Error.") await bot.say("Error.")
@ -277,12 +277,12 @@ def user_allowed(message):
mod = bot.get_cog('Mod') mod = bot.get_cog('Mod')
if mod is not None: if mod is not None:
if checks.settings["OWNER"] == author.id: if settings.owner == author.id:
return True return True
if not message.channel.is_private: if not message.channel.is_private:
if discord.utils.get(author.roles, name=checks.settings["ADMIN_ROLE"]) is not None: server = message.server
return True names = (settings.get_server_admin(server),settings.get_server_mod(server))
if discord.utils.get(author.roles, name=checks.settings["MOD_ROLE"]) is not None: if None not in map(lambda name: discord.utils.get(author.roles,name=name),names):
return True return True
if author.id in mod.blacklist_list: if author.id in mod.blacklist_list:
@ -310,24 +310,13 @@ def wait_for_answer(author):
while choice.lower() != "yes" and choice == "None": while choice.lower() != "yes" and choice == "None":
choice = input("> ") choice = input("> ")
if choice == "yes": if choice == "yes":
data = load_settings() settings.owner = author.id
data["OWNER"] = author.id print(author.name + " has been set as owner. A restart is required, maybe?")
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.")
lock = False lock = False
else: else:
print("setowner request has been ignored.") print("setowner request has been ignored.")
lock = False 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(): def list_cogs():
cogs = glob.glob("cogs/*.py") cogs = glob.glob("cogs/*.py")
clean = [] clean = []
@ -344,60 +333,47 @@ def check_folders():
os.makedirs(folder) os.makedirs(folder)
def check_configs(): def check_configs():
settings_path = "data/red/settings.json" if settings.bot_settings == settings.default_settings:
settings = {"EMAIL" : "EmailHere", "PASSWORD" : "PasswordHere", "OWNER" : "id_here", "PREFIXES" : [], "ADMIN_ROLE" : "Transistor", "MOD_ROLE" : "Process"}
if not os.path.isfile(settings_path):
print("Red - First run configuration") 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)") 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.email = input("\nEmail> ")
settings["PASSWORD"] = input("\nPassword> ") 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.") input("Email and password cannot be empty. Restart Red and repeat the configuration process.")
exit(1) 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.") input("You didn't enter a valid email. Restart Red and repeat the configuration process.")
exit(1) exit(1)
print("\nChoose a prefix (or multiple ones, one at once) for the commands. Type exit when you're done. Example prefix: !") print("\nChoose a prefix (or multiple ones, one at once) for the commands. Type exit when you're done. Example prefix: !")
settings["PREFIXES"] = [] prefixes = []
new_prefix = "" new_prefix = ""
while new_prefix.lower() != "exit" or settings["PREFIXES"] == []: while new_prefix.lower() != "exit" or prefixes == []:
new_prefix = input("Prefix> ") new_prefix = input("Prefix> ")
if new_prefix.lower() != "exit" and new_prefix != "": if new_prefix.lower() != "exit" and new_prefix != "":
settings["PREFIXES"].append(new_prefix) prefixes.append(new_prefix)
#Remember we're using property's here, oh well...
settings.prefixes = prefixes
print("\nInput *your own* ID. You can type \@Yourname in chat to see it (copy only the numbers).") 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.") print("If you want, you can also do it later with [prefix]set owner. Leave empty in that case.")
settings["OWNER"] = input("\nID> ") settings.owner = input("\nID> ")
if settings["OWNER"] == "": settings["OWNER"] = "id_here" if settings.owner == "": settings["OWNER"] = "id_here"
if not settings["OWNER"].isdigit() and 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") 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("\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)") print("Leave blank for default name (Transistor)")
settings["ADMIN_ROLE"] = input("\nAdmin role> ") settings.default_admin = input("\nAdmin role> ")
if settings["ADMIN_ROLE"] == "": settings["ADMIN_ROLE"] = "Transistor" 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("\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)") print("Leave blank for default name (Process)")
settings["MOD_ROLE"] = input("\nAdmin role> ") settings.default_mod = input("\nModerator role> ")
if settings["MOD_ROLE"] == "": settings["MOD_ROLE"] = "Process" if settings.default_mod == "": settings.default_mod = "Process"
with open(settings_path, "w") as f:
f.write(json.dumps(settings))
else: # Undoing the changes made by the broken pull request
data = load_settings()
if "default" in data:
data["ADMIN_ROLE"] = data["default"]["ADMIN_ROLE"]
data["MOD_ROLE"] = data["default"]["MOD_ROLE"]
del data["default"]
print("Rolling back settings to previous format...")
with open(settings_path, "w") as f:
f.write(json.dumps(data, indent=4,sort_keys=True,separators=(',',' : ')))
cogs_s_path = "data/red/cogs.json" cogs_s_path = "data/red/cogs.json"
cogs = {} cogs = {}
@ -482,14 +458,14 @@ def load_cogs():
def main(): def main():
global settings global settings
global checks
check_folders() check_folders()
check_configs() check_configs()
set_logger() set_logger()
settings = load_settings()
checks.settings["OWNER"] = settings["OWNER"]
load_cogs() load_cogs()
bot.command_prefix = settings["PREFIXES"] bot.command_prefix = settings.prefixes
yield from bot.login(settings["EMAIL"], settings["PASSWORD"]) yield from bot.login(settings.email, settings.password)
yield from bot.connect() yield from bot.connect()
if __name__ == '__main__': if __name__ == '__main__':