Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
lgc2333 committed Oct 13, 2023
1 parent f23c87d commit 94f6487
Show file tree
Hide file tree
Showing 10 changed files with 1,721 additions and 1,536 deletions.
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,14 @@ _✨ 基于 NoneBot2 的碧蓝档案 Wiki 插件 ✨_

## 💬 前言

诚邀各位帮忙更新插件数据源仓库!能帮这个小小插件贡献微薄之力,鄙人感激不尽!!

诚邀各位帮忙更新插件数据源仓库!能帮这个小小插件贡献微薄之力,鄙人感激不尽!!
[点击跳转 bawiki-data 查看详细贡献说明](https://github.com/lgc2333/bawiki-data)

### Tip

- 本插件并不自带 `balogo` 指令需要的字体,请自行下载并安装到系统:
[RoGSanSrfStd-Bd.otf](https://raw.githubusercontent.com/lgc-NB2Dev/readme/main/bawiki/RoGSanSrfStd-Bd.otf)[GlowSansSC-Normal-v0.93.zip](https://github.com/welai/glow-sans/releases/download/v0.93/GlowSansSC-Normal-v0.93.zip)

## 📖 介绍

一个碧蓝档案的 Wiki 插件,主要数据来源为 [GameKee](https://ba.gamekee.com/)[SchaleDB](https://lonqie.github.io/SchaleDB/)
Expand Down Expand Up @@ -155,6 +159,10 @@ Telegram:[@lgc2333](https://t.me/lgc2333)

- 插件数据源提供

### [nulla2011/Bluearchive-logo](https://github.com/nulla2011/Bluearchive-logo)

- 蔚蓝档案标题生成器

<!--
### [RainNight0](https://github.com/RainNight0)
Expand All @@ -179,14 +187,18 @@ Telegram:[@lgc2333](https://t.me/lgc2333)

## 📝 更新日志

### 0.9.5
### 0.9.6

- 修复由于 SchaleDB 数据结构变动导致的一些 Bug
- 抽卡总结图现在有半透明和圆角了
- 新增指令 `balogo`

<details>
<summary><strong>历史更新日志(点击展开)</strong></summary>

### 0.9.5

- 修复由于 SchaleDB 数据结构变动导致的一些 Bug
- 抽卡总结图现在有半透明和圆角了

### 0.9.4

- 修复了三星爆率过高的 bug ([#47](https://github.com/lgc-NB2Dev/nonebot-plugin-bawiki/pull/47))
Expand Down
2 changes: 1 addition & 1 deletion nonebot_plugin_bawiki/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .config import Cfg as Cfg # noqa: E402
from .help import extra, register_help_cmd, usage # noqa: E402

__version__ = "0.9.5"
__version__ = "0.9.6"
__plugin_meta__ = PluginMetadata(
name="BAWiki",
description="碧蓝档案Wiki插件",
Expand Down
99 changes: 99 additions & 0 deletions nonebot_plugin_bawiki/command/logo_generate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import mimetypes
import re
from typing import TYPE_CHECKING

import anyio
from nonebot import logger, on_shell_command
from nonebot.adapters.onebot.v11 import MessageSegment
from nonebot.exception import ParserExit
from nonebot.matcher import Matcher
from nonebot.params import ShellCommandArgs
from nonebot.rule import ArgumentParser, Namespace
from nonebot_plugin_htmlrender import get_new_page
from playwright.async_api import Request, Route
from yarl import URL

from ..help import FT_E, FT_S
from ..resource import RES_BA_LOGO_JS_PATH, RES_PATH

if TYPE_CHECKING:
from . import HelpList


help_list: "HelpList" = [
{
"func": "标题生成器",
"trigger_method": "指令",
"trigger_condition": "balogo",
"brief_des": "生成 BA Logo 样式的图片",
"detail_des": (
"生成 BA Logo 样式的图片\n"
"感谢 nulla2011/Bluearchive-logo 项目以 MIT 协议开源了图片绘制代码\n"
" \n"
"可以用这些指令触发:\n"
f"- {FT_S}balogo{FT_E}\n"
f"- {FT_S}baLogo{FT_E}\n"
f"- {FT_S}baLOGO{FT_E}\n"
f"- {FT_S}ba标题{FT_E}\n"
" \n"
"指令示例:\n"
f"- {FT_S}balogo Blue Archive{FT_E}\n"
f'- {FT_S}balogo "我是" "秦始皇"{FT_E}(包含空格的文本请使用引号包裹)'
),
},
]


RES_ROUTE_PREFIX = "https://bawiki.res/"


async def res_router(route: Route, request: Request):
res_name = URL(request.url).path[1:]
logger.debug(f"Requested resource `{res_name}`")

path = anyio.Path(RES_PATH / res_name)
if not await path.exists():
logger.debug(f"Resource `{res_name}` not found")
await route.abort()
return

mime = mimetypes.guess_type(path)[0]
logger.debug(f"Resource `{res_name}` mimetype: {mime}, path: {path}")
await route.fulfill(body=await path.read_bytes(), content_type=mime)


async def get_logo(text_l: str, text_r: str) -> str:
async with get_new_page() as page:
await page.route(re.compile(f"^{RES_ROUTE_PREFIX}(.*)$"), res_router)
await page.goto(f"{RES_ROUTE_PREFIX}web/empty.html")
return await page.evaluate(
RES_BA_LOGO_JS_PATH.read_text(encoding="u8"),
[text_l, text_r],
)


parser = ArgumentParser("balogo", add_help=False)
parser.add_argument("text_l")
parser.add_argument("text_r")

cmd_ba_logo = on_shell_command(
"balogo",
aliases={"baLogo", "baLOGO", "ba标题"},
parser=parser,
)


@cmd_ba_logo.handle()
async def _(matcher: Matcher, err: ParserExit = ShellCommandArgs()):
if err.message:
await matcher.finish(f"参数解析失败:{err.message}")


@cmd_ba_logo.handle()
async def _(matcher: Matcher, arg: Namespace = ShellCommandArgs()):
try:
b64_url = await get_logo(arg.text_l, arg.text_r)
except Exception:
logger.exception("Error when generating image")
await matcher.finish("遇到错误,请检查后台输出")
await matcher.finish(MessageSegment.image(b64_url))
2 changes: 2 additions & 0 deletions nonebot_plugin_bawiki/resource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
RES_GAMEKEE_UTIL_JS_PATH = RES_WEB_DIR / "gamekee_util.js"
RES_SCHALE_UTIL_JS_PATH = RES_WEB_DIR / "schale_util.js"
RES_SCHALE_UTIL_CSS_PATH = RES_WEB_DIR / "schale_util.css"
RES_BA_LOGO_JS_PATH = RES_WEB_DIR / "ba_logo.js"
RES_EMPTY_HTML_PATH = RES_WEB_DIR / "empty.html"

GAMEKEE_UTIL_JS = RES_GAMEKEE_UTIL_JS_PATH.read_text(encoding="u8")
SCHALE_UTIL_JS = RES_SCHALE_UTIL_JS_PATH.read_text(encoding="u8")
Expand Down
Binary file added nonebot_plugin_bawiki/resource/res/cross.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added nonebot_plugin_bawiki/resource/res/halo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
167 changes: 167 additions & 0 deletions nonebot_plugin_bawiki/resource/res/web/ba_logo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// https://github.com/nulla2011/bluearchive-logo/blob/master/src/canvas.ts
/** @param {[string, string]} */
async ([textL, textR]) => {
const transparentBg = true;
const fontSize = 84;
const canvasHeight = 250;
const canvasWidth = 50;
const textBaseLine = 0.68;
const horizontalTilt = -0.4;
const paddingX = 10;
const graphOffset = { X: -15, Y: 0 };
const hollowPath = [
[284, 136],
[321, 153],
[159, 410],
[148, 403],
];

const halo = document.createElement('img');
halo.id = 'halo';
halo.src = 'https://bawiki.res/halo.png';

const cross = document.createElement('img');
cross.id = 'cross';
cross.src = 'https://bawiki.res/cross.png';

// wait images loaded
await Promise.all(
[halo, cross].map(
(img) =>
new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
})
)
);

// create canvas
const canvas = document.createElement('canvas');
canvas.width = canvasWidth;
canvas.height = canvasHeight;
const c = canvas.getContext('2d');

// load font
const font = `900 ${fontSize}px 'Ro GSan Serif Std B', 'Glow Sans SC', sans-serif`;
await document.fonts.load(font, `${textL}${textR}`);
c.font = font;

// extend canvas
const textMetricsL = c.measureText(textL);
const textMetricsR = c.measureText(textR);

const textWidthL =
textMetricsL.width -
(textBaseLine * canvasHeight + textMetricsL.fontBoundingBoxDescent) *
horizontalTilt;
const textWidthR =
textMetricsR.width +
(textBaseLine * canvasHeight - textMetricsR.fontBoundingBoxAscent) *
horizontalTilt;

const canvasWidthL =
textWidthL + paddingX > canvasWidth / 2
? textWidthL + paddingX
: canvasWidth / 2;
const canvasWidthR =
textWidthR + paddingX > canvasWidth / 2
? textWidthR + paddingX
: canvasWidth / 2;

canvas.width = canvasWidthL + canvasWidthR;

// clear canvas
c.clearRect(0, 0, canvas.width, canvas.height);

// background
if (!transparentBg) {
c.fillStyle = '#fff';
c.fillRect(0, 0, canvas.width, canvas.height);
}

// left blue text
c.font = font;
c.fillStyle = '#128AFA';
c.textAlign = 'end';
c.setTransform(1, 0, horizontalTilt, 1, 0, 0);
c.fillText(textL, canvasWidthL, canvas.height * textBaseLine);
c.resetTransform(); // restore don't work

// halo
c.drawImage(
halo,
canvasWidthL - canvas.height / 2 + graphOffset.X,
graphOffset.Y,
canvasHeight,
canvasHeight
);

// right black text
c.fillStyle = '#2B2B2B';
c.textAlign = 'start';
if (transparentBg) c.globalCompositeOperation = 'destination-out';
c.strokeStyle = 'white';
c.lineWidth = 12;
c.setTransform(1, 0, horizontalTilt, 1, 0, 0);
c.strokeText(textR, canvasWidthL, canvas.height * textBaseLine);

c.globalCompositeOperation = 'source-over';
c.fillText(textR, canvasWidthL, canvas.height * textBaseLine);
c.resetTransform();

// cross stroke
const graph = {
X: canvasWidthL - canvas.height / 2 + graphOffset.X,
Y: graphOffset.Y,
};
c.beginPath();
hollowPath.forEach(([x, y], i) => {
const f = (i === 0 ? c.moveTo : c.lineTo).bind(c);
f(graph.X + x / 2, graph.Y + y / 2);
});
c.closePath();

if (transparentBg) c.globalCompositeOperation = 'destination-out';
c.fillStyle = 'white';
c.fill();
c.globalCompositeOperation = 'source-over';

// cross
c.drawImage(
cross,
canvasWidthL - canvas.height / 2 + graphOffset.X,
graphOffset.Y,
canvasHeight,
canvasHeight
);

// output
/** @type {HTMLCanvasElement} */
let outputCanvas;
if (
textWidthL + paddingX >= canvasWidth / 2 &&
textWidthR + paddingX >= canvasWidth / 2
) {
outputCanvas = canvas;
} else {
outputCanvas = document.createElement('canvas');
outputCanvas.width = textWidthL + textWidthR + paddingX * 2;
outputCanvas.height = canvas.height;

const ctx = outputCanvas.getContext('2d');
ctx.drawImage(
canvas,
canvasWidth / 2 - textWidthL - paddingX,
0,
textWidthL + textWidthR + paddingX * 2,
canvas.height,
0,
0,
textWidthL + textWidthR + paddingX * 2,
canvas.height
);
}

const b64 = outputCanvas.toDataURL().replace(/^data:image\/png;base64,/, '');
return `base64://${b64}`;
};
8 changes: 8 additions & 0 deletions nonebot_plugin_bawiki/resource/res/web/empty.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body></body>
</html>
Loading

0 comments on commit 94f6487

Please sign in to comment.