Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## [2.0.1] - 2026-05-18

### Added
- **提示消息全面可配置**:将 Setu 与 Fortune 的提示文案统一接入 `messages` 配置,支持 `enabled + text` 控制
- **占位符渲染能力**:新增统一消息解析逻辑,支持 `{count}`、`{max_count}`、`{user_id}`、`{error}`、`{tags_info}` 等占位符

### Changed
- **Setu 无结果提示改由命令层处理**:移除 use case 内硬编码提示,统一通过配置解析生成
- **Fortune 提示链路统一**:运势相关的配置未加载、仅群聊、刷新成功/失败、黑白名单操作反馈全部走统一消息配置

### Fixed
- **修复提示开关不生效**:补齐并生效 `send_failed` 开关,避免关闭后仍发送固定失败文案
- **修复部分提示无法关闭**:`fetching`、`found`、`send_failed` 及新增 fortune 提示项均可独立关闭
- **恢复今日运势卡片样式**:重新接回旧版模板、字体资源与渲染管线,避免截图卡片退化为简化样式
- **修复私聊运势重复触发**:`jrys/今日运势` 在多命令前缀场景下改为基于命令唤醒状态去重,避免 regex 与 command 同时响应

## [2.0.0] - 2026-05-17

### Changed
Expand Down
102 changes: 101 additions & 1 deletion _conf_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -395,14 +395,114 @@
"type": "object",
"hint": "图片最终发送失败时的提示消息。",
"items": {
"enabled": {
"type": "bool",
"description": "启用该提示",
"hint": "是否发送此提示消息。",
"default": true
},
"text": {
"type": "string",
"description": "提示文本",
"hint": "自动降级也失败后发送的文本。",
"default": "图片发送失败,请稍后再试。"
}
}
}
},
"rate_limited": {
"description": "并发限流提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "你有一个请求正在处理中,请稍后再试~"}
}
},
"config_not_loaded": {
"description": "配置未加载提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "配置未加载"}
}
},
"invalid_count": {
"description": "数量解析失败提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "hint": "{min_count}/{max_count} 占位符可用。", "default": "数量解析失败,图片数量必须在{min_count}-{max_count}之间"}
}
},
"max_count_exceeded": {
"description": "超出上限提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "hint": "{max_count} 占位符可用。", "default": "一次最多只能获取{max_count}张哦~"}
}
},
"count_out_of_range": {
"description": "数量越界提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "hint": "{min_count}/{max_count} 占位符可用。", "default": "图片数量必须在{min_count}-{max_count}之间哦~"}
}
},
"fetch_timeout": {
"description": "获取超时提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "获取图片超时,网络可能不稳定,请稍后再试。"}
}
},
"fetch_failed": {
"description": "获取失败提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "获取图片失败,请稍后再试"}
}
},
"no_result": {
"description": "无结果提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "hint": "{tags_info} 占位符可用。", "default": "未找到{tags_info}符合要求的图片~"}
}
},
"empty_payload": {
"description": "空负载提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "运气不好,一张图都没拿到..."}
}
},
"r18_docx_failed": {
"description": "R18 Docx 封装失败提示",
"type": "object",
"items": {
"enabled": {"type": "bool", "description": "启用该提示", "default": true},
"text": {"type": "string", "description": "提示文本", "default": "R18 Docx 封装失败,请稍后再试或联系管理员。"}
}
},
"fortune_group_only": {"description": "运势群聊限制提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "default": "此命令仅支持群聊"}}},
"fortune_missing_user_id": {"description": "运势用户ID缺失提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "default": "请指定用户ID"}}},
"fortune_get_failed": {"description": "运势获取失败提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{error} 占位符可用。", "default": "获取运势失败: {error}"}}},
"fortune_refresh_failed": {"description": "运势刷新失败提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{error} 占位符可用。", "default": "刷新运势失败: {error}"}}},
"fortune_refresh_group_failed": {"description": "群运势刷新失败提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{error} 占位符可用。", "default": "刷新群运势失败: {error}"}}},
"fortune_refresh_all_failed": {"description": "全局运势刷新失败提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{error} 占位符可用。", "default": "刷新全局运势失败: {error}"}}},
"fortune_refresh_group_done": {"description": "群运势刷新成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{count} 占位符可用。", "default": "已刷新本群 {count} 位用户的今日运势"}}},
"fortune_refresh_all_done": {"description": "全局运势刷新成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{count} 占位符可用。", "default": "已刷新全局 {count} 位用户的今日运势"}}},
"fortune_enabled_group_done": {"description": "开启运势成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "default": "运势功能已开启"}}},
"fortune_disabled_group_done": {"description": "关闭运势成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "default": "运势功能已关闭"}}},
"fortune_block_user_done": {"description": "运势黑名单添加成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{user_id} 占位符可用。", "default": "用户 {user_id} 已添加到运势黑名单"}}},
"fortune_unblock_user_done": {"description": "运势黑名单移除成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{user_id} 占位符可用。", "default": "用户 {user_id} 已从运势黑名单移除"}}},
"fortune_trust_user_done": {"description": "运势白名单添加成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{user_id} 占位符可用。", "default": "用户 {user_id} 已添加到运势白名单"}}},
"fortune_untrust_user_done": {"description": "运势白名单移除成功提示", "type": "object", "items": {"enabled": {"type": "bool", "description": "启用该提示", "default": true}, "text": {"type": "string", "description": "提示文本", "hint": "{user_id} 占位符可用。", "default": "用户 {user_id} 已从运势白名单移除"}}}
}
},
"safety": {
Expand Down
31 changes: 28 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from __future__ import annotations

from collections.abc import AsyncGenerator
import re
from typing import Any

from astrbot.api import logger
Expand Down Expand Up @@ -39,6 +40,7 @@

# Regex patterns for command triggers
SETU_REGEX_PATTERN = r"^/?(来\s*(.*?)(份|个|张|点))(.*?)(?:福利|色|瑟|涩|塞)?图$"
FORTUNE_REGEX_PATTERN = r"^(?!/)(今日运势|jrys)$"

# Module-level handler singletons
_setu_handler: SetuCommandHandler | None = None
Expand Down Expand Up @@ -103,15 +105,25 @@
"untrust": "untrust",
}

_LEADING_COMMAND_PREFIX_PATTERN = re.compile(r"^[^\w\u4e00-\u9fff]+")


def _get_invoked_command(event: AstrMessageEvent) -> str:
raw_message = getattr(event, "message_str", None)
if not raw_message and hasattr(event, "get_message_str"):
raw_message = event.get_message_str()
text = str(raw_message or "").strip()
if text.startswith("/"):
text = text[1:].strip()
return text.split(maxsplit=1)[0] if text else ""
if not text:
return ""
first_token = text.split(maxsplit=1)[0]
return _LEADING_COMMAND_PREFIX_PATTERN.sub("", first_token).strip()


def _is_fortune_command_invocation(event: AstrMessageEvent) -> bool:
"""Return True when the message is already handled by fortune command routing."""
if not getattr(event, "is_at_or_wake_command", False):
return False
return _get_invoked_command(event) in {"今日运势", "jrys"}


def _resolve_fortune_refresh_target(event: AstrMessageEvent, args: str) -> str:
Expand Down Expand Up @@ -287,6 +299,19 @@ async def fortune_command(
async for result in _fortune_handler.fortune_command(event):
yield result

@filter.regex(FORTUNE_REGEX_PATTERN)
async def fortune_regex_command(
self, event: AstrMessageEvent
) -> AsyncGenerator[Any, None]:
"""纯文本今日运势/jrys入口(不带命令前缀)。"""
if _is_fortune_command_invocation(event):
return
if _fortune_handler is None:
yield event.plain_result("插件未初始化")
return
async for result in _fortune_handler.fortune_command(event):
yield result

@filter.command(
"运势刷新",
alias={
Expand Down
2 changes: 1 addition & 1 deletion metadata.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: astrbot_plugin_setu
display_name: 瑟瑟!
version: v2.0.0
version: v2.0.1
author: FlanChanXwO
desc: 随机福利图插件,支持标签与数量,以及图片分级控制,可以针对于特定平台配置概率绕过审核的发送方式。
repo: https://github.com/FlanChanXwO/astrbot_plugin_setu
5 changes: 1 addition & 4 deletions src/application/setu/get_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,5 @@ async def execute(self, count: int, tags: list[str], r18: bool) -> SetuImagesRes
)

if payload.is_empty:
tags_info = f"标签: {', '.join(tags)}" if tags else ""
return SetuImagesResult(
payload=None, notice=f"未找到{tags_info}符合要求的图片~"
)
return SetuImagesResult(payload=None)
return SetuImagesResult(payload=payload)
Loading