koishi-plugin-jrys-prpr
Version:
[<ruby>**jrys-prpr**<rp>(</rp><rt>点我查看预览图</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/article/ae33f1b2e9dbc3fe89363a40fbf040703493298333289018.png)😽QQ官方json按钮支持,20个群即可发按钮!支持 monetary!很好看的字体! 支持自动清理记录内容。
234 lines (225 loc) • 5.7 kB
text/typescript
import fs from 'node:fs'
import { URL, pathToFileURL, fileURLToPath } from 'node:url'
import type { Context, Session } from 'koishi'
import type { Config, JrysData } from '../types'
import { getFontDataUrl, getFontFormatFromDataUrl } from './font'
import { getFormattedDate } from './jrys'
/**
* 生成运势卡片 HTML
*/
export async function generateFortuneHTML(
ctx: Context,
session: Session,
config: Config,
dJson: JrysData,
BackgroundURL_base64: string,
logInfo: (...args: any[]) => void
): Promise<string> {
// 获取字体 Data URL
const { fontDataUrl, selectedFont } = await getFontDataUrl(ctx, config, logInfo)
let insertHTMLuseravatar = session.event.user.avatar
let luckyStarHTML = `
.lucky-star {
font-size: 60px;
margin-bottom: 10px;
}
`
if (config.HTML_setting.luckyStarGradientColor) {
luckyStarHTML = `
.lucky-star {
font-size: 60px;
margin-bottom: 10px;
background: linear-gradient(to right,
#fcb5b5,
#fcd6ae,
#fde8a6,
#c3f7b1,
#aed6fa,
#c4aff5,
#f1afcc);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
`
}
const formattedDate = await getFormattedDate(logInfo)
let HTMLsource = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>运势卡片</title>
<style>
${fontDataUrl ? `@font-face {
font-family: "${selectedFont}";
src: url('${fontDataUrl}') format('${getFontFormatFromDataUrl(fontDataUrl)}');
}` : ''}
body, html {
height: 100%;
margin: 0;
overflow: hidden;
font-family: ${fontDataUrl ? `"${selectedFont}"` : 'Arial, sans-serif'};
}
.background {
background-image: url('${BackgroundURL_base64}');
background-size: cover;
background-position: center;
position: relative;
width: 1080px;
height: 1920px;
}
.overlay {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
min-height: 1%;
background-color: ${config.HTML_setting.MaskColor};
backdrop-filter: blur(${config.HTML_setting.Maskblurs}px);
border-radius: 20px 20px 0 0;
overflow: visible;
}
.user-info {
display: flex;
align-items: center;
padding: 10px 20px;
position: relative;
}
.user-avatar {
width: 120px;
height: 120px;
border-radius: 60px;
background-image: url('${insertHTMLuseravatar}');
background-size: cover;
background-position: center;
margin-left: 20px;
position: absolute;
top: 40px;
}
.username {
margin-left: 10px;
color: ${config.HTML_setting.UserNameColor};
font-size: 50px;
padding-top: 28px;
}
.fortune-info1 {
display: flex;
color: ${config.HTML_setting.HoroscopeTextColor};
flex-direction: column;
align-items: center;
position: relative;
width: 100%;
justify-content: center; /* 居中 */
margin-top: 0px; /* 上边距 */
}
.fortune-info1 > * {
margin: 10px; /* 元素之间的间距 */
}
.fortune-info2 {
color: ${config.HTML_setting.HoroscopeDescriptionTextColor};
padding: 0 20px;
margin-top: 40px;
}
.lucky-star, .sign-text, .unsign-text {
margin-bottom: 12px;
font-size: 42px;
}
.fortune-summary {
font-size: 60px;
}
${luckyStarHTML}
.sign-text, .unsign-text {
font-size: 32px;
line-height: 1.6;
padding: 10px;
border: ${config.HTML_setting.DashedboxThickn}px dashed ${config.HTML_setting.Dashedboxcolor};
border-radius: 15px;
margin-top: 10px;
}
.today-text {
font-size: 45px;
margin-bottom: 10px;
background: linear-gradient(to right,
#fcb5b5,
#fcd6ae,
#fde8a6,
#c3f7b1,
#aed6fa,
#c4aff5,
#f1afcc);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
</style>
</head>
<body>
<div class="background">
<div class="overlay">
<div class="user-info">
<div class="user-avatar"></div>
<!--span class="username">上学大人</span-->
</div>
<div class="fortune-info1">
<div class="today-text">${formattedDate}</div>
<div class="fortune-summary">${dJson.fortuneSummary}</div>
<div class="lucky-star">${dJson.luckyStar}</div>
</div>
<div class="fortune-info2">
<div class="sign-text">${dJson.signText}</div>
<div class="unsign-text">
${dJson.unsignText}
</div>
<!-- 不要迷信哦 -->
<div style="text-align: center; font-size: 24px; margin-bottom: 15px;">
仅供娱乐 | 相信科学 | 请勿迷信
</div>
</div>
</div>
</div>
</body>
</html>
`
logInfo(`触发用户: ${session.event.user?.id}`)
logInfo(`使用的格式化时间: ${formattedDate}`)
if (session.platform === 'qq') {
logInfo(`QQ官方:bot: ${session.bot.config.id}`)
logInfo(`QQ官方:用户头像: http://q.qlogo.cn/qqapp/${session.bot.config.id}/${session.event.user?.id}/640`)
}
logInfo(`蒙版颜色: ${config.HTML_setting.MaskColor}`)
logInfo(`虚线框粗细: ${config.HTML_setting.DashedboxThickn}`)
logInfo(`虚线框颜色: ${config.HTML_setting.Dashedboxcolor}`)
return HTMLsource
}
/**
* 获取图片 Buffer(用于 raw_jrys 模式)
*/
export async function getImageBuffer(ctx: Context, rawUrl: string): Promise<Buffer> {
// 首先检查是否为 data URL
if (rawUrl.startsWith('data:image/')) {
const base64Data = rawUrl.split(',')[1]
return Buffer.from(base64Data, 'base64')
}
// 检查是否为网络 URL
if (rawUrl.startsWith('http://') || rawUrl.startsWith('https://')) {
const response = await ctx.http.get(rawUrl, { responseType: 'arraybuffer' })
return Buffer.from(response)
}
// 否则,视为本地路径(可能是 file:/// URL 或普通文件系统路径)
let localPath: string;
if (rawUrl.startsWith('file:///')) {
try {
localPath = fileURLToPath(rawUrl)
} catch (error) {
throw new Error(`无效的 file URL: ${rawUrl}`)
}
} else {
localPath = rawUrl
}
if (fs.existsSync(localPath)) {
return fs.readFileSync(localPath)
}
throw new Error(`不支持的背景图格式或路径不存在: ${rawUrl}`)
}