def post(self, row_id, **options): args = self.post_parser.parse_args() try: new_state = int(args["new_state"]) except (ValueError, KeyError): return {"error": "Invalid `new_state` parameter."}, 400 with DBManager.create_session_scope() as db_session: row = db_session.query(Module).filter_by(id=row_id).one_or_none() if not row: return {"error": "Module with this ID not found"}, 404 if validate_module(row_id) is False: return {"error": "cannot modify module"}, 400 row.enabled = True if new_state == 1 else False db_session.commit() payload = {"id": row.id, "new_state": row.enabled} AdminLogManager.post( "Module toggled", options["user"].discord_id, "Enabled" if row.enabled else "Disabled", row.id, ) SocketClientManager.send("module.update", payload) return {"success": "successful toggle", "new_state": new_state}
def get(self, timer_id, **options): with DBManager.create_session_scope() as db_session: timer = db_session.query(Timer).filter_by( id=timer_id).one_or_none() if timer is None: return {"error": "Invalid timer ID"}, 404 AdminLogManager.post("Timer removed", options["user"].discord_id, timer.name) db_session.delete(timer) SocketClientManager.send("timer.remove", {"id": timer.id}) return {"success": "good job"}
def get(self, banphrase_id, **options): with DBManager.create_session_scope() as db_session: banphrase = ( db_session.query(Banphrase).filter_by(id=banphrase_id).one_or_none() ) if banphrase is None: return {"error": "Invalid banphrase ID"}, 404 AdminLogManager.post( "Banphrase removed", options["user"].discord_id, banphrase.id, banphrase.phrase ) db_session.delete(banphrase) db_session.delete(banphrase.data) SocketClientManager.send("banphrase.remove", {"id": banphrase.id}) return {"success": "good job"}, 200
def get(self, command_id, **options): with DBManager.create_session_scope() as db_session: command = db_session.query(Command).filter_by( id=command_id).one_or_none() if command is None: return {"error": "Invalid command ID"}, 404 if command.level > options["user"].level or ( command.action.functions and options["user"].level < 1500): return {"error": "Unauthorized"}, 403 log_msg = f"The !{command.command.split('|')[0]} command has been removed" AdminLogManager.add_entry("Command removed", options["user"].discord_id, log_msg) db_session.delete(command.data) db_session.delete(command) if (SocketClientManager.send("command.remove", {"command_id": command_id}) is True): return {"success": "good job"}, 200 else: return {"error": "could not push update"}, 500
def timers_create(**options): session.pop("timer_created_id", None) session.pop("timer_edited_id", None) if request.method != "POST": return render_template("admin/create_timer.html") id = None try: if "id" in request.form: id = int(request.form["id"]) name = request.form["name"].strip() interval_online = int(request.form["interval_online"]) interval_offline = int(request.form["interval_offline"]) message_type = request.form["message_type"] message = request.form["message"].strip() except (KeyError, ValueError): abort(403) if interval_online < 0 or interval_offline < 0: abort(403) if message_type not in ["say", "me"]: abort(403) if not message: abort(403) user = options.get("user", None) if user is None: abort(403) options = { "name": name, "interval_online": interval_online, "interval_offline": interval_offline, } action = {"type": message_type, "message": message} options["action"] = action if id is None: timer = Timer(**options) with DBManager.create_session_scope(expire_on_commit=False) as db_session: if id is not None: timer = db_session.query(Timer).filter_by(id=id).one_or_none() if timer is None: return redirect("/admin/timers/", 303) old_message = "" new_message = "" try: old_message = timer.action.response new_message = action["message"] except: pass timer.set(**options) if old_message and old_message != new_message: log_msg = f'Timer "{timer.name}" has been updated from "{old_message}" to "{new_message}"' else: log_msg = f'Timer "{timer.name}" has been updated' AdminLogManager.add_entry( "Timer edited", user.discord_id, log_msg, data={"old_message": old_message, "new_message": new_message}, ) else: db_session.add(timer) AdminLogManager.post("Timer added", user.discord_id, timer.name) SocketClientManager.send("timer.update", {"id": timer.id}) if id is None: session["timer_created_id"] = timer.id else: session["timer_edited_id"] = timer.id return redirect("/admin/timers/", 303)
def init(args): import subprocess import sys from flask import request from flask import session from flask_scrypt import generate_random_salt import greenbot.utils import greenbot.web.common import greenbot.web.routes from greenbot.managers.db import DBManager from greenbot.managers.redis import RedisManager from greenbot.managers.schedule import ScheduleManager from greenbot.models.module import ModuleManager from greenbot.models.sock import SocketClientManager from greenbot.utils import load_config from greenbot.web.models import errors from greenbot.bothelper import BotHelper ScheduleManager.init() config = load_config(args.config) redis_options = {} if "redis" in config: redis_options = dict(config["redis"]) RedisManager.init(**redis_options) if "web" not in config: log.error("Missing [web] section in config.ini") sys.exit(1) if "secret_key" not in config["web"]: salt = generate_random_salt() config.set("web", "secret_key", salt.decode("utf-8")) with open(args.config, "w") as configfile: config.write(configfile) bot_name = config["main"]["bot_name"] BotHelper.set_bot_name(bot_name) SocketClientManager.init(bot_name) app.bot_modules = config["web"].get("modules", "").split() app.bot_commands_list = [] app.bot_config = config app.secret_key = config["web"]["secret_key"] app.config["DISCORD_CLIENT_ID"] = app.bot_config["discord"]["client_id"] app.config["DISCORD_CLIENT_SECRET"] = app.bot_config["discord"][ "client_secret"] app.config["DISCORD_REDIRECT_URI"] = app.bot_config["discord"][ "redirect_uri"] app.bot_dev = ("flags" in config and "dev" in config["flags"] and config["flags"]["dev"] == "1") DBManager.init(config["main"]["db"]) app.module_manager = ModuleManager(None).load() greenbot.web.routes.admin.init(app) greenbot.web.routes.api.init(app) greenbot.web.routes.base.init(app) greenbot.web.common.filters.init(app) greenbot.web.common.assets.init(app) greenbot.web.common.menu.init(app) errors.init(app, config) last_commit = None if app.bot_dev: try: last_commit = (subprocess.check_output( ["git", "log", "-1", "--format=%cd"]).decode("utf8").strip()) except: log.exception( "Failed to get last_commit, will not show last commit") default_variables = { "last_commit": last_commit, "version": "v1.0", "bot": { "name": config["main"]["bot_name"] }, "site": { "domain": config["web"]["domain"] }, "modules": app.bot_modules, "request": request, "session": session, "google_analytics": config["web"].get("google_analytics", None), } @app.context_processor def current_time(): current_time = {} current_time["current_time"] = greenbot.utils.now() return current_time @app.context_processor def inject_default_variables(): return default_variables
def commands_create(**options): session.pop("command_created_id", None) session.pop("command_edited_id", None) if request.method != "POST": return render_template("admin/create_command.html") if "aliases" not in request.form: abort(403) alias_str = request.form.get("aliases", "").replace("!", "").lower() delay_all = request.form.get("cd", Command.DEFAULT_CD_ALL) delay_user = request.form.get("usercd", Command.DEFAULT_CD_USER) level = request.form.get("level", Command.DEFAULT_LEVEL) cost = request.form.get("cost", 0) try: delay_all = int(delay_all) delay_user = int(delay_user) level = int(level) cost = int(cost) except ValueError: abort(403) if not alias_str: abort(403) if delay_all < 0 or delay_all > 9999: abort(403) if delay_user < 0 or delay_user > 9999: abort(403) if level < 0 or level > 2000: abort(403) if cost < 0 or cost > 9999999: abort(403) user = options.get("user", None) if user is None: abort(403) options = { "delay_all": delay_all, "delay_user": delay_user, "level": level, "cost": cost, "added_by": user.discord_id, } valid_action_types = ["reply", "privatemessage"] action_type = request.form.get("type").lower() if action_type not in valid_action_types: abort(403) response = request.form.get("response", "") log.info(user.level) functions = ( request.form.get("functions", "").split(" ") if user.level >= 1500 else [] ) log.info(functions) action = {"type": action_type, "message": response, "functions": functions} options["action"] = action command_manager = greenbot.managers.command.CommandManager( socket_manager=None, module_manager=ModuleManager(None).load(), bot=None ).load(enabled=None) command_aliases = [] for alias, command in command_manager.items(): command_aliases.append(alias) if command.command and len(command.command) > 0: command_aliases.extend(command.command.split("|")) command_aliases = set(command_aliases) alias_str = alias_str.replace(" ", "").replace("!", "").lower() alias_list = alias_str.split("|") alias_list = [alias for alias in alias_list if len(alias) > 0] if not alias_list: return render_template("admin/create_command_fail.html") for alias in alias_list: if alias in command_aliases: return render_template("admin/create_command_fail.html") alias_str = "|".join(alias_list) command = Command(command=alias_str, **options) command.data = CommandData(command.id, **options) log_msg = f"The !{command.command.split('|')[0]} command has been created" AdminLogManager.add_entry("Command created", user.discord_id, log_msg) with DBManager.create_session_scope(expire_on_commit=False) as db_session: db_session.add(command) db_session.add(command.data) db_session.commit() db_session.expunge(command) db_session.expunge(command.data) SocketClientManager.send("command.update", {"command_id": command.id}) session["command_created_id"] = command.id return redirect("/admin/commands/", 303)
def modules_edit(module_id, **options): module_manager = ModuleManager(None).load(do_reload=False) current_module = find(lambda m: m.ID == module_id, module_manager.all_modules) user = options["user"] if user.level < current_module.CONFIGURE_LEVEL: return ( render_template( "errors/403.html", extra_message= "You do not have permission to configure this module.", ), 403, ) if current_module is None: return render_template("admin/module_404.html"), 404 sub_modules = [] for module in module_manager.all_modules: module.db_module = None with DBManager.create_session_scope() as db_session: for db_module in db_session.query(Module): module = find(lambda m: m.ID == db_module.id, module_manager.all_modules) if module: module.db_module = db_module if module.PARENT_MODULE == current_module.__class__: sub_modules.append(module) if current_module.db_module is None: return render_template("admin/module_404.html"), 404 if request.method != "POST": settings = None try: settings = json.loads(current_module.db_module.settings) except (TypeError, ValueError): pass current_module.load(settings=settings) return render_template( "admin/configure_module.html", module=current_module, sub_modules=sub_modules, ) form_values = {key: value for key, value in request.form.items()} res = current_module.parse_settings(**form_values) if res is False: return render_template("admin/module_404.html"), 404 current_module.db_module.settings = json.dumps(res) db_session.commit() settings = None try: settings = json.loads(current_module.db_module.settings) except (TypeError, ValueError): pass current_module.load(settings=settings) payload = {"id": current_module.db_module.id} SocketClientManager.send("module.update", payload) AdminLogManager.post("Module edited", user.discord_id, current_module.NAME) return render_template( "admin/configure_module.html", module=current_module, sub_modules=sub_modules, )
def banphrases_create(**options): session.pop("banphrase_created_id", None) session.pop("banphrase_edited_id", None) if request.method == "POST": id = None try: if "id" in request.form: id = int(request.form["id"]) name = request.form["name"].strip() permanent = request.form.get("permanent", "off") warning = request.form.get("warning", "off") notify = request.form.get("notify", "off") case_sensitive = request.form.get("case_sensitive", "off") sub_immunity = request.form.get("sub_immunity", "off") remove_accents = request.form.get("remove_accents", "off") length = int(request.form["length"]) phrase = request.form["phrase"] operator = request.form["operator"].strip().lower() except (KeyError, ValueError): abort(403) permanent = permanent == "on" warning = warning == "on" notify = notify == "on" case_sensitive = case_sensitive == "on" sub_immunity = sub_immunity == "on" remove_accents = remove_accents == "on" if not name: abort(403) if not phrase: abort(403) if length < 0 or length > 1209600: abort(403) valid_operators = [ "contains", "startswith", "endswith", "exact", "regex" ] if operator not in valid_operators: abort(403) user = options.get("user", None) if user is None: abort(403) options = { "name": name, "phrase": phrase, "permanent": permanent, "warning": warning, "notify": notify, "case_sensitive": case_sensitive, "sub_immunity": sub_immunity, "remove_accents": remove_accents, "length": length, "added_by": user.discord_id, "edited_by": user.discord_id, "operator": operator, } if id is None: banphrase = Banphrase(**options) banphrase.data = BanphraseData(banphrase.id, added_by=options["added_by"]) with DBManager.create_session_scope( expire_on_commit=False) as db_session: if id is not None: banphrase = (db_session.query(Banphrase).options( joinedload( Banphrase.data)).filter_by(id=id).one_or_none()) if banphrase is None: return redirect("/admin/banphrases/", 303) banphrase.set(**options) banphrase.data.set(edited_by=options["edited_by"]) log.info( f"Updated banphrase ID {banphrase.id} by user ID {options['edited_by']}" ) AdminLogManager.post("Banphrase edited", user.discord_id, banphrase.id, banphrase.phrase) else: db_session.add(banphrase) db_session.add(banphrase.data) db_session.flush() log.info( f"Added a new banphrase by user ID {options['added_by']}" ) AdminLogManager.post("Banphrase added", user.discord_id, banphrase.id, banphrase.phrase) SocketClientManager.send("banphrase.update", {"id": banphrase.id}) if id is None: session["banphrase_created_id"] = banphrase.id else: session["banphrase_edited_id"] = banphrase.id return redirect("/admin/banphrases/", 303) else: return render_template("admin/create_banphrase.html")
def post(self, command_id, **extra_args): args = greenbot.utils.remove_none_values(self.post_parser.parse_args()) if len(args) == 0: return {"error": "Missing parameter to edit."}, 400 valid_names = [ "enabled", "level", "delay_all", "delay_user", "cost", "can_execute_with_whisper", "sub_only", ] valid_action_names = ["type", "message", "functions"] with DBManager.create_session_scope() as db_session: command = (db_session.query(Command).options( joinedload(Command.data).joinedload( CommandData.user)).filter_by(id=command_id).one_or_none()) if command is None: return {"error": "Invalid command ID"}, 404 if command.level > extra_args["user"].level: return {"error": "Unauthorized"}, 403 parsed_action = json.loads(command.action_json) options = {"edited_by": extra_args["user"].discord_id} for key in args: if key.startswith("data_"): name = key[5:] value = args[key] if name.startswith("action_"): name = name[7:] if (name in valid_action_names and name in parsed_action and command.action.type == "message"): value_type = type(parsed_action[name]) if value_type is bool: parsed_value = True if value == "1" else False elif value_type is int: try: parsed_value = int(value) except ValueError: continue else: parsed_value = value if name == "type": parsed_value = ( "privatemessage" if parsed_value == "Private Message" else "reply") if name == "functions": if extra_args["user"].level < 1500: continue parsed_value = parsed_value.split(" ") parsed_action[name] = parsed_value command.action_json = json.dumps(parsed_action) else: if name in valid_names: value_type = type(getattr(command, name)) if value_type is bool: parsed_value = True if value == "1" else False elif value_type is int: try: parsed_value = int(value) except ValueError: continue else: parsed_value = value options[name] = parsed_value aj = json.loads(command.action_json) old_message = "" new_message = "" try: old_message = command.action.response new_message = aj["message"] except: pass command.set(**options) command.data.set(**options) if len(old_message) > 0 and old_message != new_message: log_msg = f'The !{command.command.split("|")[0]} command has been updated from "{old_message}" to "{new_message}"' else: log_msg = ( f"The !{command.command.split('|')[0]} command has been updated" ) AdminLogManager.add_entry( "Command edited", extra_args["user"].discord_id, log_msg, data={ "old_message": old_message, "new_message": new_message }, ) if (SocketClientManager.send("command.update", {"command_id": command_id}) is True): return {"success": "good job"}, 200 else: return {"error": "could not push update"}, 500