Skip to content

Commit

Permalink
✏️ GraiaX package
Browse files Browse the repository at this point in the history
  • Loading branch information
Redlnn committed Aug 14, 2023
1 parent 3638fcf commit 08541c6
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 75 deletions.
126 changes: 83 additions & 43 deletions docs/before/introduction/project/fastapi.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ Easy FastAPI Access for GraiaCommunity.
<project-info
name="graiax-fastapi"
license="MIT"
version="v0.2.1"
version="v0.4.0"
author="BlueGlassBlock、Red_lnn"
repoUser="GraiaCommunity"
repoName="graiax-fastapi"
/>

启动 Ariadne 时同时启动一个 Uvicorn 服务器并把 FastAPI 作为其
ASGIApplication,在退出时自动关闭 Uvicorn。
同时,可在 Saya 模块中便捷地注册 FastAPI 的路由。
你可以方便地使用 GraiaX FastAPI 配合 `graia.amnesia.builtins.asgi.UvicornASGIService`
轻松地在启动使用了 Graia Amnesia 的项目(如:Ariadne、Avilla)的同时启动一个
Uvicorn 服务器并在 Saya 模块中便捷地注册 FastAPI 的路由,且在 Launart 退出的时候自动关闭 Uvicorn。


## 安装

Expand Down Expand Up @@ -43,37 +44,79 @@ pip install graiax-fastapi

## 开始

以下教程以 Ariadne 配合 Launart 为例。
以 Avilla 为例。

如果你有使用 **Graia Saya** 作为模块管理工具,那么可以通过使用 **FastAPIBehaviour** 以在 Saya 模块中更方便地使用 FastAPI。
:::warning
自 v0.4.0 版本开始, GraiaX FastAPI 抛弃了旧版
Launart(`<0.7.0`)和旧版 Amnesia(`<0.8.0`)的支持,因此在
Ariadne 支持新的 Launart 以及 Amnesia 之前,请使用
`graiax-fastapi==0.3.0`
:::

但是 FastAPI 本身并**不自带**ASGI 服务器,因此你需要额外添加一个 **UvicornService**
### 配合 Launart 使用

### 在机器人入口文件中:

#### 机器人入口文件

如果你有使用 **Graia Saya** 作为模块管理工具,那么你可以使用 **FastAPIBehaviour**
以在 Saya 模块中更方便地使用 FastAPI。

FastAPI 本身并 **不自带** ASGI 服务器,因此你需要额外添加一个 **UvicornASGIService**

```python
import pkgutil

from avilla.console.protocol import ConsoleProtocol
from avilla.core import Avilla
from creart import create
from graia.ariadne.app import Ariadne
from graia.amnesia.builtins.uvicorn import UvicornService // [!code focus]
from graiax.fastapi import FastAPIBehaviour, FastAPIService // [!code focus]
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from graia.amnesia.builtins.asgi import UvicornASGIService
from graia.broadcast import Broadcast
from graia.saya import Saya
from launart import Launart

app = Ariadne(...)
broadcast = create(Broadcast)
saya = create(Saya)
fastapi = FastAPI() // [!code focus]
launart = create(Launart)
avilla = Avilla(broadcast=broadcast, launch_manager=launart, message_cache_size=0)
fastapi = FastAPI()

saya.install_behaviours(FastAPIBehaviour(fastapi)) // [!code focus]
avilla.apply_protocols(ConsoleProtocol())
saya.install_behaviours(FastAPIBehaviour(fastapi))
fastapi.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
launart.add_component(FastAPIService(fastapi))
launart.add_component(UvicornASGIService("127.0.0.1", 9000, {"": fastapi})) # type:ignore
# 上面这条命令会占据 Uvicorn 的所有入口点,详见下方的 Warning

# 可以不创建 FastAPI 实例, 交给 FastAPIService 自己创建 // [!code focus]
# app.launch_manager.add_service(FastAPIService()) // [!code focus]
# 这样的话就不能给 FastAPI 传参并自定义 FastAPI // [!code focus]
app.launch_manager.add_service(FastAPIService(fastapi)) // [!code focus]
app.launch_manager.add_service(UvicornService()) // [!code focus]
with saya.module_context():
for module in pkgutil.iter_modules(["modules"]):
if module.name[0] in ("#", ".", "_"):
continue
saya.require(f"modules.{module.name}")

Ariadne.launch_blocking()
launart.launch_blocking()
```

### 在 Saya 模块中:
:::warning
需要留意的是,在把我们的 FastAPI 实例添加到 `UvicornASGIService` 中间件时,我们通过
`{"": fastapi}` 指定了一个**入口点**(entrypoint)`""`,这代表着我们此时传进去的
FastAPI 实例将占据 `http://127.0.0.1:9000/` 下所有入口(例如我们可以通过 `http://127.0.0.1:9000/docs`
访问我们的 FastAPI 实例的 OpenAPI 文档),这样用起来很方便,但可能会影响其他也使用 `UvicornASGIService`
中间件的功能(例如 Avilla 的 ob11 协议)。

假如我们使用 `{"/api": fastapi}` 指定 `/api` 为入口点,那么我们就需要通过 `http://127.0.0.1:9000/api/docs` 而不是
`http://127.0.0.1:9000/docs` 来访问我们的 FastAPI 实例的 OpenAPI 文档。
:::

#### Saya 模块中

```python
from graia.saya import Channel
Expand Down Expand Up @@ -124,52 +167,49 @@ async def ws(websocket: WebSocket):
break
```

:::details 其他方式
假如你不想在 Saya 模块中为 FastAPI 添加路由,那么你可以选择以下两种方式:
假如你不想在 Saya 模块中为 FastAPI 添加路由,那么你可以选择以下几种方式:

:::details 其他方式
**1. 在机器人入口文件中直接添加:**

```python
...
app = Ariadne(...)
saya = create(Saya)
fastapi = FastAPI()

...
fastapi.add_middleware(
CORSMiddleware,
allow_origins=['*'],
allow_origins=["*"],
allow_credentials=True,
allow_methods=['*'],
allow_headers=['*'],
allow_methods=["*"],
allow_headers=["*"],
)


@fastapi.get("/main")
async def main():
return "main"

...
Ariadne.launch_blocking()
launart.add_component(FastAPIService(fastapi))
launart.add_component(UvicornASGIService("127.0.0.1", 9000, {"": fastapi})) # type:ignore
...
```

**2. Ariadne 启动成功后在 Saya 模块中添加**
**2. 在 Avilla 启动成功后添加**

```python
from graia.amnesia.builtins.uvicorn import ASGIHandlerProvider
from graiax.shortcut.saya import listen

from fastapi.responses import PlainTextResponse
from avilla.standard.core.application.event import ApplicationReady
from graiax.fastapi.interface import FastAPIProvider

async def root(...):
...
async def interface_test():
return PlainTextResponse("I'm from interface!")


@listen(ApplicationLaunched)
async def function(app: Ariadne):
mgr = app.launch_manager
fastapi: FastAPI = mgr.get_interface(ASGIHandlerProvider).get_asgi_handler() # type: ignore
fastapi.add_api_route('/', endpoint=root, methods=['GET'])
fastapi.get('/main')(root)
fastapi.add_api_websocket_route('/ws', endpoint=websocket)
@listen(ApplicationReady)
async def function():
launart = Launart.current()
fastapi = launart.get_interface(FastAPIProvider)
fastapi.add_api_route("/interface", fastapi.get("/interface")(interface_test))
```

:::
91 changes: 73 additions & 18 deletions docs/before/introduction/project/playwright.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ Easy Playwright Access for GraiaCommunity.
<project-info
name="graiax-playwright"
license="MIT"
version="v0.2.1"
version="v0.3.0"
author="BlueGlassBlock、Red_lnn、I Love Study"
repoUser="GraiaCommunity"
repoName="graiax-playwright"
/>

GraiaX Playwright 使用 launart 作为启动管理器, 适用于 Ariadne 及 Avilla。
Graiax Playwright 使用 [launart](https://github.com/GraiaProject/launart) 作为启动管理器,
适用于 [Ariadne](https://github.com/GraiaProject/Ariadne)[Avilla](https://github.com/GraiaProject/Avilla)

用于在 Ariadne 启动的时候同时通过 Playwright 启动一个浏览器内核,且可以在
Ariadne 运行过程中调用,并在其退出的时候自动关闭 Playwright。
通过 GraiaX Playwright 你可以轻松地在 Ariadne / Avilla 启动的时候同时启动一个
Playwright,并在其退出的时候自动关闭 Playwright。

> 需要注意的是,Playwright 将会在运行期间保持浏览器后台常驻,
> 但一般情况下由于未开启任何页面,其内存占用量不是非常大(但也是可观的)。
Expand Down Expand Up @@ -47,37 +48,39 @@ pip install graiax-playwright

## 开始

以下教程以 Ariadne 配合 Launart 为例。

### 在机器人入口文件中:
以下教程以配合 Launart 使用为例。

```python
from creart import create
from launart import Launart
from graia.ariadne.app import Ariadne
from graiax.playwright import PlaywrightService // [!code focus]
from graiax.playwright import PlaywrightService

app = Ariadne(...)
app.launch_manager.add_service(PlaywrightService("chromium")) # 默认值为 chromium // [!code focus]
app.launch_manager.add_service(PlaywrightService(user_data_dir="./browser_data")) # 与上一行二选一,使用 Persistent Context // [!code focus]
launart = create(Launart)
launart.add_component(PlaywrightService("chromium")) # 默认值为 chromium
launart.add_component(PlaywrightService("chromium"user_data_dir="./browser_data")) # 与上一行二选一,该方式使用 Persistent Context
...

Ariadne.launch_blocking()
launart.launch_blocking()
```

### Saya 模块中:
### 配合 Graia Saya 使用

```python
from graia.ariadne.app import Ariadne
from creart import create
from launart import Launart
from graia.ariadne.util.saya import listen
from graiax.playwright import PlaywrightBrowser
from graiax.shortcut.saya import listen

# 此处代码为没有使用 Persistent Context 的示例
# 若使用 Persistent Context 请使用 `context = app.launch_manager.get_interface(PlaywrightContext)`
# 若使用 Persistent Context 请使用 `context = launart.get_interface(PlaywrightContext)`
# 该方法获得的对象与 playwright.async_api.BrowserContext 兼容


@listen(...)
async def function(app: Ariadne):
browser = app.launch_manager.get_interface(PlaywrightBrowser)
launart = create(Launart)
browser = launart.get_interface(PlaywrightBrowser)
# 此处的 browser 之用法与 playwright.async_api.Browser 无异,但要注意的是下方代码的返回值为 False。
# `isinstance(browser, playwright.async_api.Browser)`
async with browser.page( # 此 API 启用了自动上下文管理
Expand All @@ -86,7 +89,59 @@ async def function(app: Ariadne):
) as page:
await page.set_content("Hello World!")
img = await page.screenshot(type="jpeg", quality=80, full_page=True, scale="device")
...
```

### 高级用法之一

上面配合 Saya 使用的例子展示了创建一个页面的例子,但假如我们需要一个与其他页面**隔离**的新页面(例如 cookie
等),那么我们可以使用 `browser.page(context=True)` 在创建页面时使用一个新的上下文,如下所示:

:::tip
该种用法不支持持久性上下文(Persistent Context)

更多信息详见:<https://playwright.dev/python/docs/browser-contexts>
:::

```python
@listen(...)
async def function(app: Ariadne):
launart = create(Launart)
browser = launart.get_interface(PlaywrightBrowser)
async with browser.page(new_context=True) as page: # 此 API 启用了自动上下文管理
await page.set_content("Hello World!")
img = await page.screenshot(type="jpeg", quality=80, full_page=True, scale="device")
...
```

await app.sendMessage(..., MessageChain(Image(data_bytes=img)))
### 高级用法之二

上面配合 Saya 使用的例子展示了为**单个页面**设置 viewport 的功能,自 GraiaX Playwright `v0.3.1`
版本起,可以在创建 PlaywrightService 时为全局的 Browser Context 指定 viewport,然后在截图时使用全局
Browser Context 截图,如下所示:

:::tip
该种用法不支持持久性上下文(Persistent Context)
:::

**机器人入口文件:**

```python
launart.add_service(PlaywrightService("chromium"))
```

**Saya 模块中:**

```python
from graiax.playwright import PlaywrightContext


@listen(...)
async def function(app: Ariadne):
launart = create(Launart)
context = launart.get_interface(PlaywrightContext)
async with context.page() as page: # 此 API 启用了自动上下文管理
await page.set_content("Hello World!")
img = await page.screenshot(type="jpeg", quality=80, full_page=True, scale="device")
...
```
2 changes: 1 addition & 1 deletion docs/before/introduction/project/shortcut.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Utilities for Graia Framework Community.
version="v0.2.1"
author="BlueGlassBlock、RF-Tar-Railt"
repoUser="GraiaCommunity"
repoName="graiax-shortcut"
repoName="Shortcut"
/>

:::tip
Expand Down
2 changes: 1 addition & 1 deletion docs/before/introduction/project/silkcoder.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A fxxxing simple silkv3 converter.
<project-info
name="graiax-silkcoder"
license="MIT"
version="v0.3.5"
version="v0.3.6"
author="I-love-study"
repoUser="I-love-study"
repoName="graiax-silkcoder"
Expand Down
17 changes: 6 additions & 11 deletions docs/before/introduction/project/t2i/playwright.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
repoName="graiax-text2img-playwright"
/>

一个基于 GraiaX Playwright 的文转图工具, 其可以将纯文本、Markdown 文本、HTML 代码通过 Playwright 转换为图片。
GraiaX TextToImage (Playwright) 是一个基于 [GraiaX Playwright](https://github.com/GraiaCommunity/graiax-playwright) 的文转图工具,
其可以将纯文本、Markdown 文本、HTML 代码通过 Playwright 转换为图片。

## 安装

Expand Down Expand Up @@ -39,9 +40,11 @@ pip install graiax-text2img-playwright
:::
::::

## 在 Saya 模块中使用
## 开始使用

### 将 Markdown 转换为图片
以下示例以 Ariadne 为例。

### 配合 Graia Saya 使用

```python
from graiax.shortcut.saya import listen
Expand Down Expand Up @@ -79,11 +82,3 @@ async def function(app: Ariadne, friend: Friend):
:::details 预览图
![preview](https://raw.githubusercontent.com/GraiaCommunity/graiax-text2img-playwright/master/preview.jpg)
:::

### 将 HTML 转换为图片

<loading/>

### 将纯文本转换为图片

<loading/>
2 changes: 1 addition & 1 deletion docs/guide/async_exec.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ async def drawing(group: Group):

## 应该什么时候用?

先说结论
先说结论

- 如果你的函数造成的延迟你**几乎感觉不到**,那你就直接用
- 如果你的函数是做 **I/O 密集型** 的工作或者是在运行途中可以 **释放 GIL 锁**,那你就用 `io_bound`
Expand Down

0 comments on commit 08541c6

Please sign in to comment.