示例#1
0
    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}
示例#2
0
 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"}
示例#3
0
 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
示例#4
0
    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
示例#5
0
    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)
示例#6
0
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
示例#7
0
    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)
示例#8
0
    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,
            )
示例#9
0
    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")
示例#10
0
    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