Skip to content

Commit 194afb1

Browse files
committed
revert: revert: refactor: migrate to Lagrange.Milky
revert 657f5d8
1 parent 657f5d8 commit 194afb1

File tree

9 files changed

+443
-572
lines changed

9 files changed

+443
-572
lines changed

.github/workflows/deploy-image.yml

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@ name: Create and publish a Docker image
22

33
on:
44
push:
5-
branches: ["master"]
5+
branches: [ master ]
66

77
jobs:
8-
build-and-push-image:
9-
runs-on: ${{ matrix.os }}
8+
build:
109
strategy:
10+
fail-fast: false
1111
matrix:
12-
os: [ubuntu-24.04, ubuntu-24.04-arm]
12+
include:
13+
- platform: linux/amd64
14+
runner: ubuntu-24.04
15+
- platform: linux/arm64
16+
runner: ubuntu-24.04-arm
17+
runs-on: ${{ matrix.runner }}
1318

1419
steps:
1520
- uses: actions/checkout@v6
1621

22+
- name: Prepare
23+
run: |
24+
platform=${{ matrix.platform }}
25+
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
26+
1727
- name: Login to Container Registry
1828
uses: docker/login-action@v3
1929
with:
@@ -30,11 +40,70 @@ jobs:
3040
- name: Set up Docker Buildx
3141
uses: docker/setup-buildx-action@v3
3242

33-
- name: Build and push
43+
- name: Build and push by digest
44+
id: build
3445
uses: docker/build-push-action@v6
3546
with:
36-
push: true
37-
tags: ${{ steps.meta.outputs.tags }}
47+
platforms: ${{ matrix.platform }}
3848
labels: ${{ steps.meta.outputs.labels }}
39-
cache-from: type=gha
40-
cache-to: type=gha,mode=max
49+
tags: ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_IMAGE_NAME }}
50+
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
51+
cache-from: type=gha,scope=${{ env.PLATFORM_PAIR }}
52+
cache-to: type=gha,mode=max,scope=${{ env.PLATFORM_PAIR }}
53+
54+
- name: Export digest
55+
run: |
56+
mkdir -p ${{ runner.temp }}/digests
57+
digest="${{ steps.build.outputs.digest }}"
58+
touch "${{ runner.temp }}/digests/${digest#sha256:}"
59+
60+
- name: Upload digest
61+
uses: actions/upload-artifact@v4
62+
with:
63+
name: digests-${{ env.PLATFORM_PAIR }}
64+
path: ${{ runner.temp }}/digests/*
65+
if-no-files-found: error
66+
retention-days: 1
67+
68+
merge:
69+
runs-on: ubuntu-latest
70+
needs:
71+
- build
72+
steps:
73+
- name: Download digests
74+
uses: actions/download-artifact@v4
75+
with:
76+
path: ${{ runner.temp }}/digests
77+
pattern: digests-*
78+
merge-multiple: true
79+
80+
- name: Login to Container Registry
81+
uses: docker/login-action@v3
82+
with:
83+
registry: ${{ vars.DOCKER_REGISTRY }}
84+
username: ${{ secrets.DOCKER_USERNAME }}
85+
password: ${{ secrets.DOCKER_PASSWORD }}
86+
87+
- name: Set up Docker Buildx
88+
uses: docker/setup-buildx-action@v3
89+
90+
- name: Docker meta
91+
id: meta
92+
uses: docker/metadata-action@v5
93+
with:
94+
images: ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_IMAGE_NAME }}
95+
tags: |
96+
type=ref,event=branch
97+
type=ref,event=pr
98+
type=semver,pattern={{version}}
99+
type=semver,pattern={{major}}.{{minor}}
100+
101+
- name: Create manifest list and push
102+
working-directory: ${{ runner.temp }}/digests
103+
run: |
104+
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
105+
$(printf '${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_IMAGE_NAME }}@sha256:%s ' *)
106+
107+
- name: Inspect image
108+
run: |
109+
docker buildx imagetools inspect ${{ vars.DOCKER_REGISTRY }}/${{ vars.DOCKER_IMAGE_NAME }}:${{ steps.meta.outputs.version }}

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.13-alpine AS base
1+
FROM python:3.14-alpine AS base
22
WORKDIR /app
33

44

bot.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import nonebot
2+
from nonebot.adapters.milky import Adapter as NONEBOT_ADAPTER_MILKYAdapter
23
from nonebot.adapters.onebot.v11 import Adapter as ONEBOT_V11Adapter
34

45
nonebot.init()
56

67
driver = nonebot.get_driver()
8+
driver.register_adapter(NONEBOT_ADAPTER_MILKYAdapter)
79
driver.register_adapter(ONEBOT_V11Adapter)
810

911

pdm.lock

Lines changed: 291 additions & 527 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
[project]
2-
name = ""
2+
name = "Twelve"
33
version = "0.1.0"
4-
description = "Twelve"
5-
readme = "README.md"
6-
requires-python = "~=3.13"
4+
requires-python = "~=3.14"
75
dependencies = [
86
"backoff~=2.2",
97
"nonebot2[fastapi,httpx,websockets]~=2.4",
8+
"nonebot-adapter-milky @ https://github.com/nonebot/adapter-milky/archive/9afaa853697e1ee6f126b5127b1e7cbc701c1656.zip",
109
"nonebot-adapter-onebot~=2.4",
1110
"nonebot-plugin-alconna~=0.60",
1211
"nonebot-plugin-apscheduler~=0.5",
13-
"nonebot-plugin-htmlkit~=0.1.0rc4",
12+
"nonebot-plugin-htmlkit~=0.1.0rc5",
1413
"nonebot-plugin-htmlrender~=0.6",
1514
"nonebot-plugin-orm[sqlite]~=0.8",
1615
"nonebot-plugin-uninfo~=0.10",
1716
"playwright @ https://github.com/ProgramRipper/playwright-python/releases/download/v1.55.1/playwright-1.55.1-py3-none-any.whl",
1817
]
1918

2019
[dependency-groups]
21-
dev = ["black~=24.10", "isort~=5.13", "nb-cli~=1.4"]
20+
dev = ["black~=24.10", "isort~=5.13", "nb-cli~=1.7"]
21+
22+
[tool.pdm]
23+
distribution = false
2224

2325
[tool.nonebot]
24-
adapters = [
25-
{ name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11" },
26-
]
27-
plugins = [
28-
"nonebot_plugin_alconna",
29-
"nonebot_plugin_apscheduler",
30-
"nonebot_plugin_htmlkit",
31-
"nonebot_plugin_htmlrender",
32-
"nonebot_plugin_orm",
33-
"nonebot_plugin_uninfo",
34-
]
3526
plugin_dirs = ["src/plugins"]
36-
builtin_plugins = []
27+
28+
[tool.nonebot.adapters]
29+
nonebot-adapter-milky = [{name = "nonebot-adapter-milky", module_name = "nonebot.adapters.milky"}]
30+
nonebot-adapter-onebot = [{name = "OneBot V11", module_name = "nonebot.adapters.onebot.v11"}]
31+
32+
[tool.nonebot.plugins]
33+
nonebot-plugin-alconna = ["nonebot_plugin_alconna"]
34+
nonebot-plugin-apscheduler = ["nonebot_plugin_apscheduler"]
35+
nonebot-plugin-htmlkit = ["nonebot_plugin_htmlkit"]
36+
nonebot-plugin-htmlrender = ["nonebot_plugin_htmlrender"]
37+
nonebot-plugin-orm = ["nonebot_plugin_orm"]
38+
nonebot-plugin-uninfo = ["nonebot_plugin_uninfo"]
3739

3840
[tool.pyright]
3941
enableExperimentalFeatures = true
40-
pythonVersion = "3.13"
42+
pythonVersion = "3.14"
4143
pythonPlatform = "Linux"

src/plugins/bilibili/plugins/dynamic/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ async def broadcast(dynamics: list[Dynamic]):
239239
else render_screenshot(dynamic["id_str"])
240240
),
241241
get_share_click(
242-
dynamic["id_str"], "dynamic", "dt.dt-detail.0.0.pv"
242+
dynamic["id_str"], "dt.dt-detail.0.0.pv", "dynamic"
243243
),
244244
session.scalars(
245245
select(Subscription).where(
@@ -395,7 +395,7 @@ async def _(id_str: str):
395395
)
396396
else render_screenshot(dynamic["id_str"])
397397
),
398-
get_share_click(id_str, "dynamic", "dt.dt-detail.0.0.pv"),
398+
get_share_click(id_str, "dt.dt-detail.0.0.pv" ,"dynamic"),
399399
)
400400
await plugin_config.template.format(
401401
name=dynamic["modules"]["module_author"]["name"],

src/plugins/bilibili/plugins/live/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ async def broadcast(uids: list[int]) -> None:
7575
client.get(info["cover_from_user"] or info["face"]),
7676
get_share_click(
7777
info["room_id"],
78-
"vertical-three-point",
7978
"live.live-room-detail.0.0.pv",
79+
"vertical-three-point",
8080
),
8181
)
8282

@@ -186,7 +186,7 @@ async def _(uid: int):
186186

187187
url, cover = await gather(
188188
get_share_click(
189-
info["room_id"], "vertical-three-point", "live.live-room-detail.0.0.pv"
189+
info["room_id"], "live.live-room-detail.0.0.pv", "vertical-three-point"
190190
),
191191
client.get(info["cover_from_user"] or info["face"]),
192192
)

src/plugins/bilibili/utils.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import re
22
import string
3+
from hashlib import md5
34
from random import choices
5+
from time import time
46
from typing import Any, NoReturn, TypedDict
7+
from urllib.parse import urlencode
58

69
from arclet.alconna import Arg
710
from httpx import AsyncClient, Response
811
from nepattern import BasePattern, MatchMode
912
from nonebot import logger
1013
from nonebot_plugin_alconna import UniMessage
1114

15+
from ...utils import acache
16+
1217
UID_ARG = Arg(
1318
"uid",
1419
BasePattern(
@@ -52,8 +57,7 @@ def raise_for_status(resp: Response) -> Any:
5257
"user-agent": "bili-universal/75600100 CFNetwork/1.0 "
5358
"Darwin/23.2.0 os/ios model/iPhone 13 mini mobi_app/iphone "
5459
"build/75600100 osVer/17.2 network/1 channel/AppStore;tf:cm"
55-
},
56-
base_url="https://api.bilibili.com/x/share",
60+
}
5761
)
5862

5963
URL_PATTERN = re.compile(
@@ -63,14 +67,17 @@ def raise_for_status(resp: Response) -> Any:
6367
)
6468

6569

66-
async def get_share_click(oid: Any, origin: str, share_id: str) -> str:
70+
@acache
71+
async def get_share_click(
72+
oid: Any, share_id: str, share_origin: str | None = None
73+
) -> str:
6774
data = raise_for_status(
6875
await client.post(
69-
"/click",
76+
"https://api.bilibili.com/x/share/click",
7077
data={
7178
"oid": oid,
7279
"share_id": share_id,
73-
"share_origin": origin,
80+
"share_origin": share_origin,
7481
"platform": "ios",
7582
"share_channel": "COPY",
7683
"share_mode": 3,
@@ -88,10 +95,11 @@ class SharePlacard(TypedDict):
8895
link: str
8996

9097

98+
@acache
9199
async def get_share_placard(oid: Any, share_id: str) -> SharePlacard:
92100
return raise_for_status(
93101
await client.post(
94-
"/placard",
102+
"https://api.bilibili.com/x/share/placard",
95103
data={
96104
"oid": oid,
97105
"platform": "ios",
@@ -102,6 +110,25 @@ async def get_share_placard(oid: Any, share_id: str) -> SharePlacard:
102110
)
103111

104112

113+
APPKEY = "1d8b6e7d45233436"
114+
APPSEC = "560c52ccd288fed045859ed18bffd973"
115+
116+
117+
def sign(params: dict[str, Any]) -> str:
118+
query = urlencode(sorted((params | {"appkey": APPKEY}).items()))
119+
return query + f"&sign={md5((query + APPSEC).encode()).hexdigest()}"
120+
121+
122+
@acache
123+
async def get_space(uid: int):
124+
return raise_for_status(
125+
await client.get(
126+
"https://app.bilibili.com/x/v2/space",
127+
params=sign({"build": "76600100", "ts": int(time()), "vmid": uid}),
128+
)
129+
)
130+
131+
105132
async def handle_error(message: str) -> NoReturn:
106133
logger.error(message)
107134
await UniMessage(message).send()

src/utils.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from asyncio import Task, create_task
22
from collections.abc import Callable
33
from contextlib import suppress
4+
from functools import cache, wraps
45
from inspect import iscoroutine
5-
from typing import TYPE_CHECKING, Awaitable, TypeVar, cast
6+
from typing import TYPE_CHECKING, Awaitable, cast
67

78
from httpx import AsyncClient
89
from nonebot.exception import ActionFailed
@@ -15,17 +16,14 @@
1516
from nonebot.adapters.milky.model.api import MessageResponse
1617

1718

18-
T = TypeVar("T")
19-
20-
2119
tasks: set[Task] = set()
2220

2321

24-
async def await_(awaitable: Awaitable[T]) -> T:
22+
async def await_[T](awaitable: Awaitable[T]) -> T:
2523
return await awaitable
2624

2725

28-
def run_task(awaitable: Awaitable[T]) -> Task[T]:
26+
def run_task[T](awaitable: Awaitable[T]) -> Task[T]:
2927
if isinstance(awaitable, Task):
3028
task = awaitable
3129
elif iscoroutine(awaitable):
@@ -39,6 +37,15 @@ def run_task(awaitable: Awaitable[T]) -> Task[T]:
3937
return task
4038

4139

40+
def acache[T: Callable[..., Awaitable]](user_function: T, /) -> T:
41+
@cache
42+
@wraps(user_function)
43+
def wrapper(*args, **kwargs) -> Task[T]:
44+
return run_task(user_function(*args, **kwargs))
45+
46+
return cast(T, wrapper)
47+
48+
4249
async def send_message(scene_model: SceneModel, msg: UniMessage) -> Receipt:
4350
bot_model = await get_bot_model(scene_model.bot_persist_id)
4451

0 commit comments

Comments
 (0)