Compare commits

...

9 Commits

Author SHA1 Message Date
39c7c043ca Merge branch 'master' of ssh://gitea.service.jazzwhom.top:2221/mttu-developers/konabot
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-29 18:52:01 +08:00
39accb16e0 桂花说 2026-04-29 18:51:37 +08:00
ec1f9627f3 Merge pull request 'chores: manual fix and pbr pipeline' (#73) from bkbkzzzz/konabot:marchtoy_gl into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #73
2026-04-28 14:11:47 +08:00
c0590dacbc Merge branch 'master' into marchtoy_gl 2026-04-28 14:11:08 +08:00
d748e242db manual fix 2026-04-28 14:09:45 +08:00
e2fd0809a5 PBR 2026-04-28 01:09:17 +08:00
2144b1e0eb 补充解压 Typst 构建产物需要的依赖
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-28 00:39:37 +08:00
7d0d53bead 修复并调整构建产物的生命周期
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-28 00:35:03 +08:00
4bfcc9b41c Merge pull request '修复偶发的数据库连接失效问题' (#72) from fix/database-lock into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #72
2026-04-28 00:12:44 +08:00
6 changed files with 140 additions and 20 deletions

View File

@ -5,6 +5,8 @@ ENV VIRTUAL_ENV=/app/.venv \
PLAYWRIGHT_BROWSERS_PATH=/usr/lib/pw-browsers PLAYWRIGHT_BROWSERS_PATH=/usr/lib/pw-browsers
# 安装所有都需要的底层依赖 # 安装所有都需要的底层依赖
#
# xz-utils: 解压需要它
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libfontconfig1 libgl1 libegl1 libglvnd0 mesa-vulkan-drivers at-spi2-common fontconfig \ libfontconfig1 libgl1 libegl1 libglvnd0 mesa-vulkan-drivers at-spi2-common fontconfig \
@ -16,6 +18,7 @@ RUN apt-get update && \
libatk-bridge2.0-0t64 libatspi2.0-0t64 libxcomposite1 libxdamage1 libxfixes3 \ libatk-bridge2.0-0t64 libatspi2.0-0t64 libxcomposite1 libxdamage1 libxfixes3 \
libxkbcommon0 libasound2t64 libnss3 fonts-noto-cjk fonts-noto-cjk-extra \ libxkbcommon0 libasound2t64 libnss3 fonts-noto-cjk fonts-noto-cjk-extra \
fonts-noto-color-emoji \ fonts-noto-color-emoji \
xz-utils \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*

View File

@ -50,7 +50,14 @@ class ArtifactDepends:
tasks = set() tasks = set()
for f in self.callbacks: for f in self.callbacks:
tasks.add(f(downloaded)) tasks.add(f(downloaded))
return await asyncio.gather(*tasks, return_exceptions=True) result = await asyncio.gather(*tasks, return_exceptions=True)
for r in result:
if isinstance(r, BaseException):
logger.warning("完成了二进制文件的下载,但是有未捕捉的错误")
logger.exception(r)
return result
class Config(BaseModel): class Config(BaseModel):
@ -73,12 +80,7 @@ async def _():
async def _task(artifact: ArtifactDepends): async def _task(artifact: ArtifactDepends):
async with semaphore: async with semaphore:
downloaded = await ensure_artifact(artifact) await ensure_artifact(artifact)
result = await artifact._finished(downloaded)
for r in result:
if isinstance(r, BaseException):
logger.warning("完成了二进制文件的下载,但是有未捕捉的错误")
logger.exception(r)
tasks: set[asyncio.Task] = set() tasks: set[asyncio.Task] = set()
for a in artifact_list: for a in artifact_list:
@ -116,9 +118,16 @@ async def download_artifact(artifact: ArtifactDepends):
f"下载到的二进制的 sha256 与需求不同 TARGET={artifact.target} REQUESTED={artifact.sha256} ACTUAL={m.hexdigest()}" f"下载到的二进制的 sha256 与需求不同 TARGET={artifact.target} REQUESTED={artifact.sha256} ACTUAL={m.hexdigest()}"
) )
await artifact._finished(True)
async def ensure_artifact(artifact: ArtifactDepends) -> bool: async def ensure_artifact(artifact: ArtifactDepends) -> bool:
"""
确保所需的二进制存在。返回是否下载了这个二进制文件。
"""
if not artifact.is_corresponding_platform(): if not artifact.is_corresponding_platform():
logger.debug(f"所需求的平台不是当前平台,跳过二进制下载 artifact={artifact}")
return False return False
if not artifact.target.exists(): if not artifact.target.exists():
@ -136,6 +145,7 @@ async def ensure_artifact(artifact: ArtifactDepends) -> bool:
artifact.target.unlink() artifact.target.unlink()
await download_artifact(artifact) await download_artifact(artifact)
return True return True
await artifact._finished(False)
return False return False

View File

@ -1,16 +1,16 @@
# 指令介绍 # 指令介绍
简易 Raymarch 小玩具 简易 Raymarch 小玩具
用法march `<scene>` 用法march `[scene]`
march torus(1.0, 0.2).color(1.0, 0.2, 0.2) torus(1.0, 0.2).rot(90, 0, 0).color(0.2, 0.2, 1.0) camera(4.0).pos(4, 0, 0).lookat(0) march torus(1.0, 0.2).color(1.0, 0.2, 0.2) torus(1.0, 0.2).rot(90, 0, 0).color(0.2, 0.2, 1.0) camera(4.0).pos(4, 0, 0).lookat(0)
# 主要语法 # 主要语法
`<scene>` ::= `<scene>` "." `<op>` |`<obj>` `[scene]` ::= `[scene]` "." `[op]` |`[obj]`
`<obj>` ::= `<obj_ty>` | `<obj_ty>` "(" <args> ")" `[obj]` ::= `[obj_ty]` | `[obj_ty]` "(" [args] ")"
`<op>` ::= `<op_ty>` | `<op_ty>` "(" `<args>` ")" `[op]` ::= `[op_ty]` | `[op_ty]` "(" `[args]` ")"
`<args>` ::= `<args>` "," `<arg>` | `<arg>` `[args]` ::= `[args]` "," `[arg]` | `[arg]`
其中 `obj_ty`、`op_ty` 分别为物体类型(如 `cube`、`sphere`、`torus` 等)与变换类型(如 `pos`、`rot`)。 其中 `obj_ty`、`op_ty` 分别为物体类型(如 `cube`、`sphere`、`torus` 等)与变换类型(如 `pos`、`rot`)。
@ -37,4 +37,4 @@
`rounded`:圆角 `rounded`:圆角
# 特殊说明 # 特殊说明
`<op_ty>` 不包含 scale。非正交的变换会破坏 SDF 的性质。 `[op_ty]` 不包含 scale。非正交的变换会破坏 SDF 的性质。

View File

@ -0,0 +1,44 @@
import re
from typing import Any
from nonebot import on_message
from nonebot.adapters import Event
from nonebot_plugin_alconna import UniMessage, UniMsg
from playwright.async_api import Page
from konabot.common.nb import match_keyword
from konabot.common.web_render import WebRenderer, konaweb
async def render_image(message: str) -> UniMessage[Any]:
"""
渲染文本为图片
"""
async def page_function(page: Page):
await page.wait_for_function("typeof setContent === 'function'")
await page.evaluate(
"([ message ]) => { return setContent(message); }",
[ message ],
)
img_data = await WebRenderer.render(
url=konaweb("guihuasay"),
target="#main",
other_function=page_function,
)
return UniMessage.image(raw=img_data)
cmd = on_message(
rule=match_keyword.match_keyword(
re.compile(r"^(桂花[说想])\s.+", re.I),
),
)
@cmd.handle()
async def _(event: Event, msg: UniMsg):
text = msg.extract_plain_text().lstrip()
_, content = text.split(maxsplit=1)
msg = await render_image(content)
await msg.send(event)

View File

@ -2,6 +2,7 @@
const float EPS = 0.001; const float EPS = 0.001;
const int MAX_ITER = 128; const int MAX_ITER = 128;
const float INF = 1e10; const float INF = 1e10;
const float PI = 3.14159;
uniform vec2 u_resolution; uniform vec2 u_resolution;
out vec4 fragColor; out vec4 fragColor;
@ -68,17 +69,76 @@ vec3 nrm(vec3 p) {
)); ));
} }
float saturate(float x) {
return clamp(x, 0.0, 1.0);
}
float ggx_distribution(vec3 n, vec3 h, float roughness) {
float alpha = roughness * roughness;
float alpha2 = alpha * alpha;
float NdotH = saturate(dot(n, h));
float denom = NdotH * NdotH * (alpha2 - 1.0) + 1.0;
return alpha2 / max(PI * denom * denom, EPS);
}
float geometry_schlick_ggx(float NdotX, float roughness) {
float r = roughness + 1.0;
float k = r * r / 8.0;
return NdotX / max(NdotX * (1.0 - k) + k, EPS);
}
float geometry_smith(vec3 n, vec3 v, vec3 l, float roughness) {
float NdotV = saturate(dot(n, v));
float NdotL = saturate(dot(n, l));
return geometry_schlick_ggx(NdotV, roughness) * geometry_schlick_ggx(NdotL, roughness);
}
vec3 fresnel_schlick(vec3 f0, float cos_theta) {
return f0 + (1.0 - f0) * pow(1.0 - saturate(cos_theta), 5.0);
}
vec3 tonemap_aces(vec3 c) {
const float a = 2.51;
const float b = 0.03;
const float c1 = 2.43;
const float d = 0.59;
const float e = 0.14;
return clamp((c * (a * c + b)) / (c * (c1 * c + d) + e), 0.0, 1.0);
}
vec4 materialColor(int obj_id) { vec4 materialColor(int obj_id) {
<COLOR_BLOCK> <COLOR_BLOCK>
return vec4(1.0); return vec4(1.0);
} }
vec4 color(vec3 p, int obj_id) { vec4 color(vec3 p, vec3 r, int obj_id) {
vec3 normal = nrm(p); vec3 light_col = vec3(1.0);
vec3 light_dir = normalize(vec3(0.5, 0.8, -0.6)); vec4 albedo = materialColor(obj_id);
float light = 0.2 + 0.8 * max(dot(normal, light_dir), 0.0); vec3 N = nrm(p);
vec4 base = materialColor(obj_id); vec3 V = normalize(-r);
return vec4(base.rgb * light, base.a); vec3 L = normalize(vec3(0.5, 0.8, -0.6));
vec3 H = normalize(V + L);
float roughness = 0.45;
float metallic = 0.02;
float NdotL = saturate(dot(N, L));
float NdotV = saturate(dot(N, V));
float D = ggx_distribution(N, H, roughness);
float G = geometry_smith(N, V, L, roughness);
vec3 F0 = mix(vec3(0.04), albedo.rgb, metallic);
vec3 F = fresnel_schlick(F0, dot(V, H));
vec3 kD = (1.0 - F) * (1.0 - metallic);
vec3 diffuse = kD * albedo.rgb / PI;
vec3 specular = D * G * F / max(4.0 * NdotL * NdotV, EPS);
float hemi = N.y * 0.5 + 0.5;
vec3 sky_ambient = vec3(0.60, 0.72, 0.92);
vec3 ground_ambient = vec3(0.18, 0.16, 0.14);
vec3 ambient = mix(ground_ambient, sky_ambient, hemi) * (diffuse + 0.25 * F0) * 0.35;
vec3 col = ambient + (diffuse + specular) * light_col * NdotL;
col = tonemap_aces(col);
col = pow(col, vec3(1.0 / 2.2));
return vec4(col, 1.0);
} }
vec4 march(vec3 p, vec3 r) { vec4 march(vec3 p, vec3 r) {
@ -87,7 +147,7 @@ vec4 march(vec3 p, vec3 r) {
for(int i = 0; i < MAX_ITER; ++i) { for(int i = 0; i < MAX_ITER; ++i) {
qry = sd(p); qry = sd(p);
if(qry.value < EPS){ if(qry.value < EPS){
col = color(p, qry.obj_id); col = color(p, r, qry.obj_id);
break; break;
} }
p += qry.value * r; p += qry.value * r;

View File

@ -41,6 +41,7 @@ bin_path: Path | None = None
@arti_typst_linux.on_finished @arti_typst_linux.on_finished
async def _(downloaded: bool): async def _(downloaded: bool):
logger.debug("安装好了 Linux 版本的 Typst")
global bin_path global bin_path
tar_path = arti_typst_linux.target tar_path = arti_typst_linux.target
@ -71,6 +72,7 @@ async def _(downloaded: bool):
@arti_typst_windows.on_finished @arti_typst_windows.on_finished
async def _(downloaded: bool): async def _(downloaded: bool):
logger.debug("安装好了 Windows 版本的 Typst")
global bin_path global bin_path
zip_path = arti_typst_windows.target zip_path = arti_typst_windows.target
bin_path = BINARY_PATH / "typst.exe" bin_path = BINARY_PATH / "typst.exe"
@ -160,6 +162,7 @@ async def _(
# 对于本地机器,一般不会在应用启动时自动下载,这里再保证存在 # 对于本地机器,一般不会在应用启动时自动下载,这里再保证存在
await ensure_artifact(arti_typst_linux) await ensure_artifact(arti_typst_linux)
await ensure_artifact(arti_typst_windows) await ensure_artifact(arti_typst_windows)
if bin_path is None or not bin_path.exists(): if bin_path is None or not bin_path.exists():
logger.warning("当前环境不存在 Typst但仍然调用了") logger.warning("当前环境不存在 Typst但仍然调用了")
return return