예제 #1
0
파일: app.py 프로젝트: ThomasRoncen/OWAPI
async def jsonify(ctx, response: Response):
    """
    JSONify the response.
    """
    if not isinstance(response.response, dict):
        return response

    # json.dump the body.
    status_code = response.status_code
    if not any(response.response.values()):
        status_code = 404
    if ctx.request.args.get("format", "json") in ["json_pretty", "pretty"]:
        d = json.dumps(response.response, sort_keys=True, indent=4, separators=(',', ': '))
    else:
        d = json.dumps(response.response)
    response.set_data(d)
    response.headers["Content-Type"] = "application/json"

    # 261
    response.headers["Cache-Control"] = "public, max-age=300"
    expires = (datetime.datetime.utcnow() + datetime.timedelta(seconds=300))\
        .replace(tzinfo=datetime.timezone.utc)
    response.headers["Expires"] = format_datetime(expires, usegmt=True)
    response.status_code = status_code
    return response


api_bp.add_child(api_v3)

app.register_blueprint(api_bp)
예제 #2
0
파일: app.py 프로젝트: azah/OWAPI
# Create the api blueprint and add children
api_bp = Blueprint("api", prefix="/api")


@api_bp.after_request
async def jsonify(ctx, response: Response):
    """
    JSONify the response.
    """
    if not isinstance(response.response, dict):
        return response

    # json.dump the body.
    status_code = response.status_code
    if not any(response.response.values()):
        status_code = 404
    if ctx.request.args.get("format", "json") == "json_pretty":
        d = json.dumps(response.response, sort_keys=True, indent=4, separators=(',', ': '))
    else:
        d = json.dumps(response.response)
    response.set_data(d)
    response.headers["Content-Type"] = "application/json"
    response.status_code = status_code
    return response


api_bp.add_child(api_v2)
api_bp.add_child(api_v3)

app.register_blueprint(api_bp)
예제 #3
0
파일: bot.py 프로젝트: SunDwarf/Chiru
class Chiru(Bot):
    """
    Bot class.
    """

    def __init__(self, *args, **kwargs):
        self.logger = logbook.Logger("Chiru")
        self.logger.level = logbook.INFO

        # We still have to do this
        logging.root.setLevel(logging.INFO)

        # Set SQLAlchemy's logger to INFO
        logging.getLogger("sqlalchemy").setLevel(logging.INFO)

        try:
            cfg = sys.argv[1]
        except IndexError:
            cfg = "config.yml"

        self.logger.info("Loading from `{}`.".format(cfg))

        if not os.path.exists(cfg):
            shutil.copy("config.example.yml", cfg)

        with open(cfg) as f:
            self.config = yaml.load(f)

        if self.config.get("use_uvloop", False):
            import uvloop
            self.logger.info("Switching to uvloop.")
            policy = uvloop.EventLoopPolicy()
            self.logger.info("Created event loop policy `{}`.".format(policy))
            asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
        else:
            self.logger.info("Using base selector event loop.")

        # Init now, so the loop is created here.
        super().__init__(*args, **kwargs)

        if self.config.get("self_bot"):
            self._skip_check = discord.User.__ne__

        self._redis = None

        # Create a new Kyoukai web server.
        self._webserver = Kyoukai("chiru")
        self._webserver_started = False

        self._webserver.debug = self.config.get("dev", False)

        self._webserver.before_request(self.before_request)
        root = self._webserver.root.wrap_route("/", self.root)
        self._webserver.root.add_route(root)

        self.start_time = time.time()

        self.app_id = ""
        self.owner_id = ""

        # Create the rotation background task.
        self.loop.create_task(self._rotate_game_text())

    async def _rotate_game_text(self):
        """
        Coroutine to rotate the game text.
        """
        await self.wait_until_ready()
        texts = self.config.get("game_texts", [])
        while True:
            await self.change_presence(game=discord.Game(name=random.choice(texts)))
            await asyncio.sleep(15)

    @property
    def is_self_bot(self):
        return self.config.get("self_bot", False)

    async def root(self, r: HTTPRequestContext):
        return "Chiru OK!", 200, {"X-Bot": "Chiru"}

    async def before_request(self, r: HTTPRequestContext):
        r.request.extra["bot"] = self
        return r

    def register_blueprint(self, blueprint: Blueprint):
        """
        Add a blueprint to the built-in webserver.
        """
        self._webserver.register_blueprint(blueprint)

    # region Redis

    async def _connect_redis(self):
        """
        Connect to redis.
        """
        host = self.config.get("redis")["host"]
        port = self.config.get("redis")["port"]
        password = self.config.get("redis", {}).get("password")
        db = self.config.get("redis", {}).get("db", 0)
        self.logger.info("Connecting to redis://{}:{}/{}...".format(host, port, db))
        try:
            redis_pool = await aioredis.create_pool(
                (host, port),
                db=db, password=password
            )
        except ConnectionRefusedError:
            self.logger.error("Could not connect to redis server.")
            self.logger.error("Exiting.")
            await self.logout()
            return
        else:
            self.logger.info("Established Redis connection.")
        self._redis = redis_pool
        self.logger.info("Connected to redis.")

        return self._redis

    async def get_redis(self) -> aioredis.RedisPool:
        if self._redis is None:
            await self._connect_redis()

        return self._redis

    async def get_set(self, server: discord.Server, key: str):
        """
        Gets a set from redis.
        """
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            x = await conn.smembers(built)

            m = []
            # Decode values, if we can.
            for _ in x:
                if isinstance(_, bytes):
                    m.append(_.decode())
                else:
                    m.append(_)

            return m

    async def add_to_set(self, server: discord.Server, key: str, item: str):
        """
        Add an item to a set.
        """
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            x = await conn.sadd(built, item.encode())

            return x

    async def remove_from_set(self, server: discord.Server, key: str, item: str):
        """
        Removes an item from a set.
        """
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            x = await conn.srem(built, item.encode())

            return x

    async def get_config(self, server: discord.Server, key: str):
        """
        Get a server config key.
        """
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            x = await conn.get(built)
            if isinstance(x, bytes):
                return x.decode()
            return x

    async def get_key(self, key: str):
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            x = await conn.get(key)
            if isinstance(x, bytes):
                return x.decode()
            return x

    async def set_config(self, server: discord.Server, key: str, value, **kwargs):
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            return await conn.set(built, value, **kwargs)

    async def delete_config(self, server: discord.Server, key: str):
        async with (await self.get_redis()).get() as conn:
            assert isinstance(conn, aioredis.Redis)
            built = "cfg:{}:{}".format(server.id, key)
            return await conn.delete(built)

    # endregion

    async def on_ready(self):
        self.logger.info("Loaded Chiru, logged in as `{}`.".format(self.user.name))
        try:
            app_info = await self.application_info()
            self.app_id = app_info.id
            self.owner_id = app_info.owner.id

            self.logger.info("Invite link: {}".format(discord.utils.oauth_url(self.app_id)))
        except discord.Forbidden:
            self.owner_id = self.user.id
        redis = await self.get_redis()
        if not redis:
            return

        extensions = initial_extensions + self.config.get("autoload", [])

        for cog in extensions:
            try:
                self.load_extension(cog)
            except Exception as e:
                self.logger.critical("Could not load extension `{}`".format(cog, e))
                self.logger.exception()
            else:
                self.logger.info("Loaded extension {}.".format(cog))

        if not self._webserver_started:
            try:
                self.logger.info("Starting built-in webserver.")
                component = KyoukaiComponent(self._webserver,
                                             self.config.get("webserver", {}).get("ip", "127.0.0.1"),
                                             self.config.get("port", {}).get("port", 5555),
                                             renderer="template_mako")
                self._webserver.component = component
                await self._webserver.start(component=component)
            except OSError as e:
                if e.errno == 98:
                    self.logger.warning("Cannot start built-in webserver; something is already listening.")
            else:
                self.logger.info("Started webserver successfully.")
                self._webserver_started = True

        new_time = time.time() - self.start_time

        self.logger.info("Ready in {} seconds.".format(new_time))
        self.logger.info("Bot has loaded and is ready for processing.")

    def __del__(self):
        self.loop.set_exception_handler(lambda *args, **kwargs: None)

    async def on_message(self, message):
        # Print logging output.

        if not isinstance(message.channel, discord.PrivateChannel):
            self.logger.info("Recieved message: {message.content} from {message.author.display_name}{bot}"
                             .format(message=message, bot=" [BOT]" if message.author.bot else ""))
            self.logger.info(" On channel: #{message.channel.name}".format(message=message))

        # Check for a valid server.
        if message.server is not None:
            self.logger.info(" On server: {} ({})".format(message.server.name, message.server.id))
        else:
            if self.config.get("self_bot"):
                return
            if not message.author.bot:
                # No DMs
                await self.send_message(message.channel, "I don't accept private messages.")
                return

        # Process commands
        try:
            await self.process_commands(message)
        except Exception as e:
            # Check the type of the error.
            if isinstance(e, (commands.errors.BadArgument, commands.errors.MissingRequiredArgument)):
                await self.send_message(message.channel, ":x: Bad argument: {}".format(' '.join(e.args)))
                return
            elif isinstance(e, commands.errors.CheckFailure):
                await self.send_message(message.channel, ":x: Check failed. You probably don't have permission to do "
                                                         "this.")
                return
            else:
                if isinstance(e, commands.errors.CommandInvokeError):
                    lines = traceback.format_exception(type(e), e.__cause__, e.__cause__.__traceback__)
                else:
                    lines = traceback.format_exception(type(e), e, e.__traceback__)
                await self.send_message(message.channel, ":no_entry: An error has occurred. This has been logged.")
                self.logger.error(''.join(lines))

    async def send_message(self, destination, content, *, tts=False):
        content = "\u200b{}".format(content)
        return await super().send_message(destination, content, tts=tts)

    async def process_commands(self, message):
        """
        Override of process_commands to use our own context.
        """
        _internal_channel = message.channel
        _internal_author = message.author

        view = StringView(message.content)
        if self._skip_check(message.author, self.user):
            return

        prefix = await self._get_prefix(message)
        invoked_prefix = prefix

        if not isinstance(prefix, (tuple, list)):
            if not view.skip_string(prefix):
                return
        else:
            invoked_prefix = discord.utils.find(view.skip_string, prefix)
            if invoked_prefix is None:
                return

        invoker = view.get_word()
        tmp = {
            'bot': self,
            'invoked_with': invoker,
            'message': message,
            'view': view,
            'prefix': invoked_prefix
        }
        ctx = Context(**tmp)
        del tmp

        if invoker in self.commands:
            command = self.commands[invoker]
            self.dispatch('command', command, ctx)
            await command.invoke(ctx)
            self.dispatch('command_completion', command, ctx)
        elif invoker:
            exc = CommandNotFound('Command "{}" is not found'.format(invoker))
            self.dispatch('command_error', exc, ctx)

    def main(self):
        self.run(self.config["oauth2_token"], bot=not self.config.get("self_bot", False))
예제 #4
0
app = Kyoukai("owapi")


@app.route("/")
async def root(ctx: HTTPRequestContext):
    return Response.redirect(
        "https://github.com/SunDwarf/OWAPI/blob/master/api.md")


@app.root.errorhandler(500)
async def e500(ctx: HTTPRequestContext, exc: HTTPException):
    obb = {
        "error": 500,
        "msg": "please report this!",
        "exc": repr(exc.__cause__)
    }
    logger.error("Unhandled exception - Blizzard format probably changed!")
    traceback.print_exc()
    return json.dumps(obb), 500, {"Content-Type": "application/json"}


@app.root.errorhandler(404)
async def e404(ctx: HTTPRequestContext, exc: HTTPException):
    return json.dumps({"error": 404}), 404, {
        "Content-Type": "application/json"
    }


app.register_blueprint(routes.bp)
예제 #5
0
from kyoukai import Kyoukai
from .util import jsonify
from .models import Base
from .api_v1 import api_v1

app = Kyoukai("bashhub")
app.register_blueprint(api_v1)


@app.route("/")
async def index(ctx):
    return jsonify({"_": repr(ctx.dbsession)})


@app.route("/_create_all")
async def _create_all(ctx):
    Base.metadata.create_all(ctx.sql)
    return "OK"


@app.before_request
async def before_request(ctx):
    print(repr(ctx.request.path))
    print(repr(ctx.request.headers))
    print(repr(ctx.request.body))
    print("")
    return ctx