fixed # 适配AstrBot

This commit is contained in:
2026-10-23 19:12:16 +08:00
parent d0c1741940
commit 685cff9293
49 changed files with 1557 additions and 165 deletions
+28
View File
@@ -0,0 +1,28 @@
# 更新日志
## 2.0.2 — 待发布
### 新增
- 单张动态帮助图片,按当前平台的指令生成;支持「农场帮助 文字」。
- 种子与作物分类仓库,支持分页、作物编号及文字显示。
- 开垦与开垦条件查询,按下一实际地块编号校验等级和费用。
- 插件图标、市场短介绍、标签及独立更新日志。
### 调整与兼容
- 新账号初始6块土地;可可靠识别的旧三地账号一次性追加3块普通空地,
保留已开垦成果、原作物及资产。来源不明、地块异常或超出当前布局容量
时保留原状并提示管理员处理。
- 新播种及下一季的生虫间隔至少2小时;已排定的旧事件保留原计划一次。
- 全部农场图片遵循统一质量配置,仅「我的农场」允许追加「高清」。
- 提供本地独立存档、交易流水、重复消息保护和失败回滚。
- QQ/OneBot图片支持头像缓存;头像不可用时使用默认图像。
### 升级前请阅读
- 本条记录描述待发布代码,不代表已更新 AstrBot Cloud 或真实机器人。
- 新版没有签到、改名、土地升级、外部金币兑换及旧远程服务功能。
- 旧存档请先备份,再在副本中验证迁移;不要直接删除或覆盖真实玩家数据。
- 当前重点适配 QQ/OneBot(aiocqhttp),其他平台尚未完成真实环境验证。
- 本文件未编造旧1.x版本的发布日期和变更历史。
+13
View File
@@ -1,4 +1,17 @@
{
"resource_source": {
"description": "资源获取地址",
"type": "string",
"default": "Github",
"options": ["Github", "Gitea", "farm_service"],
"hint": "启动时校验本地素材SHA256,缺失或不符从所选地址下载;修改后重载。"
},
"farm_service_url": {
"description": "farm_service 地址",
"type": "string",
"default": "",
"hint": "始终独立配置,用于资源及后续活动服务;选择farm_service获取素材时必填HTTPS基础地址。"
},
"font_path": {
"description": "中文字体路径",
"type": "string",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

+6 -1
View File
@@ -11,7 +11,9 @@ from astrbot.api.message_components import At, Image
from astrbot.api.star import Context, Star, StarTools
from .avatars import AvatarCache
from .runtime import COMMANDS, Runtime
from .runtime import Runtime, command_map
COMMANDS = command_map('astrbot')
logger = logging.getLogger(__name__)
@@ -35,6 +37,9 @@ class FarmPlugin(Star):
image_format, font_setting='font_path',
image_quality=config.get('image_quality', 75),
image_quality_provider=lambda: self.config.get('image_quality', 75),
framework='astrbot',
resource_source=config.get('resource_source', 'Github'),
service_url=config.get('farm_service_url', ''),
)
async def initialize(self):
+1 -1
View File
@@ -2,7 +2,7 @@
发布目标:https://github.com/Shu-Ying/nonebot_plugin_farm.git
本目录负责命令、统一会话、消息、配置与启停,不导入真寻。
运行时在打包时复用 `integrations/zhenxun/runtime.py`,核心和渲染模块
运行时在打包时复用 `integrations/shared/runtime.py`,核心和渲染模块
按白名单私有化。此源码目录不能直接加载,需先构建生成仓库。
`python scripts/build_nonebot.py --output dist/nonebot-preview` 生成标准
+3 -2
View File
@@ -9,10 +9,11 @@ require("nonebot_plugin_localstore")
from ._version import __version__
from .config import Config
from .runtime import HELP
from .runtime import command_specs, help_text
__plugin_meta__ = PluginMetadata(
name="农场", description="快乐的农场时光", usage=HELP,
name="农场", description="快乐的农场时光",
usage=help_text(command_specs('nonebot')),
type="application", config=Config,
homepage="https://github.com/Shu-Ying/nonebot_plugin_farm",
supported_adapters=inherit_supported_adapters(
+6 -1
View File
@@ -12,7 +12,9 @@ from nonebot_plugin_uninfo import Uninfo
from .config import Config
from .data_paths import data_directory
from .runtime import COMMANDS, Runtime
from .runtime import Runtime, command_map
COMMANDS = command_map('nonebot')
logger = logging.getLogger(__name__)
config = get_plugin_config(Config)
@@ -22,6 +24,9 @@ runtime = Runtime(
config.farm_font, config.farm_deployment, config.farm_image_format,
font_setting="FARM_FONT",
image_quality=config.farm_image_quality,
framework='nonebot',
resource_source=config.farm_resource_source,
service_url=config.farm_service_url,
)
driver.on_startup(runtime.start)
driver.on_shutdown(runtime.close)
+6
View File
@@ -25,3 +25,9 @@ class Config(BaseModel):
75,
description="全部农场图片编码质量;仅我的农场高清时至少85",
)
farm_resource_source: Literal['Github', 'Gitea', 'farm_service'] = Field(
'Github', description='资源获取地址,修改后重载插件',
)
farm_service_url: str = Field(
'', description='farm_service 地址,独立供资源及活动服务使用',
)
@@ -1,6 +1,7 @@
"""真寻与 NoneBot 共用的运行编排;不导入宿主框架。"""
"""各平台适配器共用的运行编排;不导入宿主框架。"""
import hashlib
import asyncio
from dataclasses import replace
import json
import logging
@@ -9,12 +10,16 @@ import shlex
from ._vendor.farm_core import Catalogue, FarmService, Identity, SQLiteStore
from ._vendor.farm_core.domain import FarmError
from ._vendor.farm_core.commands import (
command_map, command_specs, help_text,
)
from ._vendor.farm_core.storage import SCHEMA_VERSION
from ._vendor.farm_core.presentation import (
plot_ids_text, receipt_text, snapshot_text,
)
from ._vendor.farm_render import FarmRenderer, RenderOptions, RenderService
from ._vendor.farm_render.seasons import season_for
from ._vendor.farm_render.bundle import ensure_assets, validate_options
from ._vendor.farm_render.shop import shop_page
from ._vendor.farm_render.details import detail_rows, details_text
from ._vendor.farm_render.warehouse import (
@@ -22,36 +27,8 @@ from ._vendor.farm_render.warehouse import (
)
logger = logging.getLogger(__name__)
COMMANDS = {
"开通农场":"register", "我的农场":"farm", "农场详述":"details",
"我的农场币":"inventory", "我的点券":"inventory",
"我的种子":"seeds", "我的作物":"crops",
"种子商店":"shop", "农场帮助":"help", "购买种子":"buy",
"播种":"sow", "收获":"harvest", "铲除":"eradicate",
"作物加锁":"lock", "作物解锁":"unlock", "出售作物":"sell",
"农场除虫":"clear_pests", "除虫":"clear_pests", "地块编号":"plot_ids",
"偷菜":"steal",
"开垦":"reclaim",
}
HELP = (
"开通农场 [名称]|我的农场 [高清]|农场详述 [页码]\n"
"地块编号|查看当前土地编号及开垦位置\n"
"偷菜 @目标|每日最多5次成功偷取\n"
"开垦|每次增加1块普通空地;开垦 条件|查看等级和费用\n"
"我的种子/我的作物 [页码] [文字],默认图片。\n"
"全部图片使用农场图片质量配置;仅我的农场支持高清。\n"
"种子商店 [页码]|购买种子 作物 数量|播种 作物 [数量]\n"
"收获|铲除|农场除虫|我的种子 [页码]|我的作物 [页码]\n"
"作物加锁 作物|作物解锁 作物|出售作物 [作物] [数量]\n"
"作物可填写名称或 #编号;同名作物请使用商店或仓库中的编号。\n"
"每成功播种1块地获得2经验;收获经验按作物结算。\n"
"每铲除1块枯萎地块获得2经验;每清除1只虫获得1经验。\n"
"新播种及下一季每半个基础生长周期生虫一次,间隔至少2小时。\n"
"每只虫使生长速度降低5%;成熟时每只虫使本季减产5%。\n"
"成熟后除虫不能恢复本季减产;生长期间可发送:农场除虫。\n"
"我的农场按北京时间自动切换春夏秋冬背景;高清使用相同季节。\n"
"旧版农场数据在首次使用时迁移,绑定当前机器人和平台。"
)
COMMANDS = command_map('zhenxun')
HELP = help_text(command_specs('zhenxun'))
ERRORS = {
'land_origin_unknown': '农场初始土地来源无法确认,未补地或扣币,'
'请联系管理员核查注册或迁移记录。',
@@ -100,8 +77,17 @@ class Runtime:
def __init__(self, data_dir, font_path=None, deployment="zhenxun-preview",
image_format="AUTO", *, font_dir=None,
font_setting="FARM_PREVIEW_FONT", image_quality=75,
image_quality_provider=None):
image_quality_provider=None, framework='zhenxun',
resource_source='Github', service_url='',
resource_options_provider=None):
RenderOptions(quality=image_quality)
validate_options(resource_source, service_url)
self.resource_source = resource_source
self.service_url = service_url
self.resource_options_provider = resource_options_provider
self.framework = framework
self.command_specs = command_specs(framework)
self.commands = command_map(framework)
self.image_quality = image_quality
self.image_quality_provider = image_quality_provider
self.data_dir = Path(data_dir).resolve()
@@ -147,11 +133,27 @@ class Runtime:
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
))
for candidate in candidates:
try:
if self.resource_options_provider is not None:
self.resource_source, self.service_url = (
self.resource_options_provider()
)
bundle_path = await asyncio.to_thread(
ensure_assets, self.data_dir, self.resource_source,
self.service_url,
)
except Exception:
# 不将下载URL、服务地址或可能带凭据的网络异常写入日志。
logger.error('farm assets unavailable; using text; '
'check resource source and service address')
bundle_path = None
for candidate in candidates if bundle_path is not None else ():
if not candidate.is_file():
continue
try:
self.rendering = RenderService(FarmRenderer(candidate))
self.rendering = RenderService(FarmRenderer(
candidate, bundle_path=bundle_path,
))
logger.info("farm preview font selected file=%s", candidate.name)
break
except (ValueError, OSError):
@@ -180,6 +182,10 @@ class Runtime:
if not self.started:
return "农场尚未初始化,请检查启动日志。"
try:
args = shlex.split(arguments)
# 帮助无需注册或迁移玩家数据,绘图失败可直接阅读文字。
if action == 'help':
return await self.help_menu(args)
who = Identity(self.deployment, str(bot_id), platform, str(user_id))
try:
await self.store.migrate_legacy(
@@ -212,7 +218,7 @@ class Runtime:
bot_id, platform, str(user_id), message_id, action,
)
if action == "help":
return HELP
return await self.help_menu(args)
if action == 'reclaim' and args == ['条件']:
conditions = await self.service.reclaim_conditions(who)
return (
@@ -371,7 +377,7 @@ class Runtime:
raise ValueError()
receipt = await getattr(self.service, action)(who, op)
else:
return HELP
return help_text(self.command_specs)
text = receipt_text(receipt)
if action == 'register':
if receipt.data['created']:
@@ -386,3 +392,27 @@ class Runtime:
return ERRORS.get(error.code, "操作未完成:" + error.code)
except ValueError:
return "参数有误,请发送:农场帮助"
async def help_menu(self, args):
if '高清' in args:
return '仅我的农场支持高清,请移除高清参数后重试。'
if args not in ([], ['文字']):
return '参数有误,请发送:农场帮助 或 农场帮助 文字'
fallback = help_text(self.command_specs)
if args or self.rendering is None:
return fallback
quality = self.image_quality
if self.image_quality_provider is not None:
quality = self.image_quality_provider()
try:
RenderOptions(quality=quality)
except (ValueError, TypeError):
logger.warning('farm help image quality config invalid')
return '农场图片质量配置无效,请设置为1~95的整数。'
try:
return await self.rendering.help_menu(
self.command_specs, self.framework, self.image_format, quality,
)
except (OSError, ValueError, RuntimeError):
logger.exception('farm help rendering failed; using text')
return fallback
+6
View File
@@ -19,6 +19,12 @@ __plugin_meta__ = PluginMetadata(
help="全部农场图片的编码质量,整数1~95,默认75;"
"数值越高通常越清晰、文件越大。仅我的农场支持高清,"
"高清质量不低于85。PNG为无损格式,不受此值影响。",
), RegisterConfig(
key='资源获取地址', value='Github', default_value='Github',
type=str, help='Github、Gitea或farm_service,默认Github;重载生效。',
), RegisterConfig(
key='farm_service 地址', value='', default_value='', type=str,
help='独立服务基础地址,用于资源及后续活动服务;HTTPS。',
)],
).to_dict(),
)
+9
View File
@@ -27,6 +27,14 @@ def image_quality():
)
def resource_options():
from zhenxun.configs.config import Config
config = Config.get(__package__.split('.')[-1])
return (config.get('资源获取地址', 'Github', build_model=False),
config.get('farm_service 地址', '', build_model=False))
logger = logging.getLogger(__name__)
driver = get_driver()
data_dir = data_directory(
@@ -39,6 +47,7 @@ runtime = Runtime(
str(getattr(driver.config, "farm_preview_image_format", "AUTO")).upper(),
font_dir=FONT_PATH,
image_quality_provider=image_quality,
resource_options_provider=resource_options,
)
async def start_runtime():
from zhenxun.configs.config import Config