11
@@ -1,13 +1,11 @@
|
||||
# pyright: reportArgumentType=false
|
||||
import asyncio
|
||||
import json
|
||||
import aiofiles
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
|
||||
from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult, MessageChain
|
||||
from astrbot.api.star import Context, Star, register, StarTools
|
||||
from astrbot.api.event import MessageChain
|
||||
from astrbot.api.star import Context
|
||||
from astrbot.api import logger
|
||||
from astrbot.api import AstrBotConfig
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from pathlib import Path
|
||||
from datetime import datetime,date
|
||||
import aiofiles
|
||||
import base64
|
||||
import os
|
||||
|
||||
async def load_template(template_name: str) -> str:
|
||||
"""
|
||||
@@ -58,6 +60,7 @@ def week_to_num(week :str):
|
||||
}
|
||||
return week_map.get(week,None)
|
||||
|
||||
|
||||
def compare_date_str(date_str: str) -> str:
|
||||
"""
|
||||
date_str 格式:YYYY-MM-DD
|
||||
@@ -71,3 +74,40 @@ def compare_date_str(date_str: str) -> str:
|
||||
return "今天"
|
||||
else:
|
||||
return "将来"
|
||||
|
||||
|
||||
def load_as_base64(icons_dir: str) -> dict[str, str]:
|
||||
"""
|
||||
将指定目录下的图标文件转换为 base64 字符串。
|
||||
返回 {"文件名(不含扩展名)": "data:image/xxx;base64,xxx"} 的字典。
|
||||
"""
|
||||
supported_ext = {".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".ico"}
|
||||
icons = {}
|
||||
|
||||
for filename in os.listdir(icons_dir):
|
||||
name, ext = os.path.splitext(filename)
|
||||
if ext.lower() not in supported_ext:
|
||||
continue
|
||||
|
||||
# 用原始文件名(系统实际存储的)打开文件
|
||||
filepath = os.path.join(icons_dir, filename)
|
||||
with open(filepath, "rb") as f:
|
||||
data = base64.b64encode(f.read()).decode()
|
||||
|
||||
# 尝试修正显示用的 key(如果文件名是 GBK 乱码)
|
||||
display_name = name
|
||||
try:
|
||||
display_name = name.encode("latin-1").decode("gbk")
|
||||
except (UnicodeEncodeError, UnicodeDecodeError):
|
||||
pass # 本来就是正确的 UTF-8
|
||||
|
||||
if ext.lower() == ".svg":
|
||||
mime = "image/svg+xml"
|
||||
elif ext.lower() == ".jpg":
|
||||
mime = "image/jpeg"
|
||||
else:
|
||||
mime = f"image/{ext.lower().lstrip('.')}"
|
||||
|
||||
icons[display_name] = f"data:{mime};base64,{data}"
|
||||
|
||||
return icons
|
||||
@@ -51,7 +51,8 @@ class JX3Service:
|
||||
"code": 0,
|
||||
"msg": "功能函数未执行",
|
||||
"data": {},
|
||||
"temp": ""
|
||||
"temp": "",
|
||||
"icons": {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1063,7 +1064,7 @@ class JX3Service:
|
||||
|
||||
# 4. 加载模板
|
||||
try:
|
||||
return_data["temp"] = await load_template("juesheqiyu.html")
|
||||
return_data["temp"] = await load_template("test.html")
|
||||
except FileNotFoundError as e:
|
||||
logger.error(f"加载模板失败: {e}")
|
||||
return_data["msg"] = "系统错误:模板文件不存在"
|
||||
|
||||
@@ -13,11 +13,12 @@ from .bilei_data import BiLeidata
|
||||
|
||||
class MessageBuilder:
|
||||
"""回复消息构建"""
|
||||
def __init__(self, server: str, jx3api: JX3Service, bilei: BiLeidata, jx3at: AsyncTask):
|
||||
def __init__(self, server: str, jx3api: JX3Service, bilei: BiLeidata, jx3at: AsyncTask, icons: dict[str, dict[str, str]]):
|
||||
self.server = server
|
||||
self.jx3api = jx3api
|
||||
self.bilei = bilei
|
||||
self.jx3at = jx3at
|
||||
self.icons = icons
|
||||
|
||||
|
||||
async def html_render(
|
||||
@@ -68,6 +69,7 @@ class MessageBuilder:
|
||||
"omit_background": False,
|
||||
"type": "jpeg"
|
||||
}
|
||||
data["data"]["icons"] = self.icons
|
||||
url = await self.html_render(data["temp"], data["data"], options=options)
|
||||
await event.send(event.image_result(url))
|
||||
else:
|
||||
|
||||
@@ -11,6 +11,7 @@ from .core.jx3_data import JX3Service
|
||||
from .core.async_task import AsyncTask
|
||||
from .core.bilei_data import BiLeidata
|
||||
from .core.message import MessageBuilder
|
||||
from .core.fun_basic import load_as_base64
|
||||
|
||||
@register("astrbot_plugin_jx3",
|
||||
"fxdyz",
|
||||
@@ -37,9 +38,12 @@ class Jx3ApiPlugin(Star):
|
||||
|
||||
# 获取数据文件路径
|
||||
self.get_data_path()
|
||||
# 加载图片base64编码
|
||||
self.load_local_base64()
|
||||
# 构造所有类
|
||||
self.create_all()
|
||||
|
||||
|
||||
# 声明指令集
|
||||
self.command_map = {}
|
||||
|
||||
@@ -95,6 +99,7 @@ class Jx3ApiPlugin(Star):
|
||||
self.local_data_dir = StarTools.get_data_dir("astrbot_plugin_jx3")
|
||||
# 插件数据存储路径
|
||||
self.plugin_data_dir = Path(__file__).parent / "data"
|
||||
self.plugin_temp_dir = Path(__file__).parent /"templates"
|
||||
|
||||
# SQLite本地路径
|
||||
self.local_data_path = self.local_data_dir / "local_data.db"
|
||||
@@ -102,11 +107,31 @@ class Jx3ApiPlugin(Star):
|
||||
self.plugin_data_path = self.plugin_data_dir /"plugin_data.db"
|
||||
# API配置文件路径
|
||||
self.api_data_path = self.plugin_data_dir / "api_config.json"
|
||||
# 图片文件路径
|
||||
self.plugin_temp_img = self.plugin_temp_dir / "img"
|
||||
self.plugin_temp_sect = self.plugin_temp_dir / "sect"
|
||||
self.plugin_temp_serendipity = self.plugin_temp_dir / "serendipity"
|
||||
|
||||
# 数据路径打印
|
||||
logger.debug(f"本地数据路径: {self.local_data_path}")
|
||||
logger.debug(f"插件数据路径: {self.plugin_data_path}")
|
||||
logger.debug(f"API配置文件路径: {self.api_data_path}")
|
||||
logger.debug(f"图片文件路径: {self.plugin_temp_img}")
|
||||
logger.debug(f"图片文件路径: {self.plugin_temp_sect}")
|
||||
logger.debug(f"图片文件路径: {self.plugin_temp_serendipity}")
|
||||
|
||||
|
||||
def load_local_base64(self):
|
||||
"""加载图片文件的base64编码"""
|
||||
img = load_as_base64(str(self.plugin_temp_img))
|
||||
sect = load_as_base64(str(self.plugin_temp_sect))
|
||||
serendipity = load_as_base64(str(self.plugin_temp_serendipity))
|
||||
self.icons = {
|
||||
"img": img,
|
||||
"sect": sect,
|
||||
"serendipity": serendipity
|
||||
}
|
||||
logger.debug(f"图片base64编码加载完成: {self.icons}")
|
||||
|
||||
|
||||
def create_all(self):
|
||||
@@ -119,7 +144,7 @@ class Jx3ApiPlugin(Star):
|
||||
self.bilei = BiLeidata(self.local_sql_db)
|
||||
self.jx3api = JX3Service(str(self.api_data_path), self.conf, self.plugin_sql_db)
|
||||
self.jx3at = AsyncTask(self.context, self.conf, self.jx3api, self.local_sql_db)
|
||||
self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at)
|
||||
self.jx3cmd = MessageBuilder(self.server, self.jx3api, self.bilei, self.jx3at, self.icons)
|
||||
|
||||
|
||||
async def init_bilei_data(self):
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>角色奇遇一览</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 24px;
|
||||
background: #f5f7fb;
|
||||
color: #2c2c33;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 36px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16px;
|
||||
border-left: 4px solid #4f7cff;
|
||||
padding-left: 10px;
|
||||
color: #1f2a44;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 16px 12px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
min-height: 90px;
|
||||
}
|
||||
|
||||
.card img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.event-name {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
color: #2b3a67;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 22px;
|
||||
color: #7a7f99;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- 普通奇遇 -->
|
||||
<div class="section">
|
||||
<div class="section-title">普通奇遇({{ ptqy|length if ptqy else 0 }})</div>
|
||||
{% if ptqy %}
|
||||
<div class="card-container">
|
||||
{% for item in ptqy %}
|
||||
<div class="card">
|
||||
{% if icons.get(item.event) %}
|
||||
<img src="{{ icons.serendipity[item.event] }}" alt="{{ item.event }}">
|
||||
{% else %}
|
||||
<div class="event-name">{{ item.event }}</div>
|
||||
{% endif %}
|
||||
<div class="time">{{ item.time }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">暂无普通奇遇</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 绝世奇遇 -->
|
||||
<div class="section">
|
||||
<div class="section-title">绝世奇遇({{ jsqy|length if jsqy else 0 }})</div>
|
||||
{% if jsqy %}
|
||||
<div class="card-container">
|
||||
{% for item in jsqy %}
|
||||
<div class="card">
|
||||
{% if icons.get(item.event) %}
|
||||
<img src="{{ icons.serendipity[item.event] }}" alt="{{ item.event }}">
|
||||
{% else %}
|
||||
<div class="event-name">{{ item.event }}</div>
|
||||
{% endif %}
|
||||
<div class="time">{{ item.time }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">暂无绝世奇遇</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- 宠物奇遇 -->
|
||||
<div class="section">
|
||||
<div class="section-title">宠物奇遇({{ cwqy|length if cwqy else 0 }})</div>
|
||||
{% if cwqy %}
|
||||
<div class="card-container">
|
||||
{% for item in cwqy %}
|
||||
<div class="card">
|
||||
{% if icons.get(item.event) %}
|
||||
<img src="{{ icons.serendipity[item.event] }}" alt="{{ item.event }}">
|
||||
{% else %}
|
||||
<div class="event-name">{{ item.event }}</div>
|
||||
{% endif %}
|
||||
<div class="time">{{ item.time }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">暂无宠物奇遇</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||