@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-config"
|
||||
version = "1.2.5b1+1019"
|
||||
version = "1.2.6b1+1019"
|
||||
description = "Profile-based configuration compiler for JWeBot"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
|
||||
@@ -36,4 +36,4 @@ __all__ = [
|
||||
"write_default_profile",
|
||||
]
|
||||
|
||||
__version__ = "1.2.5b1+1019"
|
||||
__version__ = "1.2.6b1+1019"
|
||||
|
||||
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-extension-host"
|
||||
version = "1.2.5b1+1019"
|
||||
version = "1.2.6b1+1019"
|
||||
description = "Single-release, single-invocation process host for JWeBot extensions"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"jwebot-sdk==1.2.5b1+1019",
|
||||
"jwebot-kernel==1.2.5b1+1019",
|
||||
"jwebot-sdk==1.2.6b1+1019",
|
||||
"jwebot-kernel==1.2.6b1+1019",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@@ -16,4 +16,4 @@ __all__ = [
|
||||
"process_request",
|
||||
]
|
||||
|
||||
__version__ = "1.2.5b1+1019"
|
||||
__version__ = "1.2.6b1+1019"
|
||||
|
||||
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-kernel"
|
||||
version = "1.2.5b1+1019"
|
||||
version = "1.2.6b1+1019"
|
||||
description = "Stable routing and extension authority for JWeBot"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"jwebot-config==1.2.5b1+1019",
|
||||
"jwebot-sdk==1.2.5b1+1019",
|
||||
"jwebot-official-extensions==1.2.5b1+1019",
|
||||
"jwebot-config==1.2.6b1+1019",
|
||||
"jwebot-sdk==1.2.6b1+1019",
|
||||
"jwebot-official-extensions==1.2.6b1+1019",
|
||||
"tomli>=2; python_version < '3.11'",
|
||||
]
|
||||
|
||||
|
||||
@@ -107,4 +107,4 @@ __all__ = [
|
||||
"verify_installed_release_signature",
|
||||
]
|
||||
|
||||
__version__ = "1.2.5b1+1019"
|
||||
__version__ = "1.2.6b1+1019"
|
||||
|
||||
@@ -166,6 +166,44 @@ TRIVIA_HISTORY_KEY = "trivia_quiz_history"
|
||||
TRIVIA_ANSWER_WINDOW_MINUTES = 5
|
||||
TRIVIA_QUIZ_PROBABILITY_PERCENT = 75
|
||||
TRIVIA_RECENT_BASE_LIMIT = 24
|
||||
TRIVIA_TOPIC_NAMES = (
|
||||
"反恐精英",
|
||||
"顶点英雄",
|
||||
"守望先锋",
|
||||
"英雄联盟",
|
||||
"少女乐团派对",
|
||||
"韩流",
|
||||
"原神",
|
||||
)
|
||||
TRIVIA_TOPIC_ALIASES = {
|
||||
"反恐精英": "反恐精英",
|
||||
"反恐": "反恐精英",
|
||||
"cs": "反恐精英",
|
||||
"cs2": "反恐精英",
|
||||
"csgo": "反恐精英",
|
||||
"顶点英雄": "顶点英雄",
|
||||
"顶点": "顶点英雄",
|
||||
"apex": "顶点英雄",
|
||||
"守望先锋": "守望先锋",
|
||||
"守望": "守望先锋",
|
||||
"ow": "守望先锋",
|
||||
"overwatch": "守望先锋",
|
||||
"英雄联盟": "英雄联盟",
|
||||
"联盟": "英雄联盟",
|
||||
"lol": "英雄联盟",
|
||||
"leagueoflegends": "英雄联盟",
|
||||
"少女乐团派对": "少女乐团派对",
|
||||
"邦邦": "少女乐团派对",
|
||||
"gbp": "少女乐团派对",
|
||||
"girlsbandparty": "少女乐团派对",
|
||||
"bangdream": "少女乐团派对",
|
||||
"韩流": "韩流",
|
||||
"韩国流行音乐": "韩流",
|
||||
"kpop": "韩流",
|
||||
"原神": "原神",
|
||||
"genshin": "原神",
|
||||
"genshinimpact": "原神",
|
||||
}
|
||||
BEIJING_TIMEZONE = ZoneInfo("Asia/Shanghai")
|
||||
ACKNOWLEDGEMENT = "已收到,正在处理中喵"
|
||||
STEAM_ACKNOWLEDGEMENT = "Steam 查询 [■□□] 正在读取公开数据"
|
||||
@@ -360,6 +398,21 @@ TRIVIA_FACTS: tuple[tuple[str, str, str, str, tuple[str, str, str], str, int], .
|
||||
("lol-zac", "英雄联盟", "扎克名字常被解释为什么缩写?", "祖安无定形战斗体", ("祖安奥术核心", "时间装置自动体", "祖安酸液生物"), "扎克的名字常被当作祖安无定形战斗体的缩写梗。", 4),
|
||||
("lol-kindred", "英雄联盟", "永猎双子由哪两个化身组成?", "羊灵和狼灵", ("乌鸦和雄鹿", "狐狸和猫头鹰", "太阳和月亮"), "永猎双子是羊灵与狼灵两个死亡化身。", 2),
|
||||
("lol-ryze", "英雄联盟", "瑞兹长期收集并封存的危险物是什么?", "世界符文", ("暗裔武器", "海克斯核心", "虚空碎片"), "瑞兹的主线目标是阻止世界符文失控。", 3),
|
||||
("gbp-random-star", "少女乐团派对", "户山香澄使用的星形吉他昵称是什么?", "随机星", ("流星号", "闪耀心", "星光鸟"), "户山香澄的标志性星形吉他昵称是随机星。", 4),
|
||||
("gbp-hikawa-sisters", "少女乐团派对", "冰川双子里姐姐是谁?", "冰川纱夜", ("冰川日菜", "丸山彩", "今井莉莎"), "冰川纱夜是姐姐,冰川日菜是妹妹。", 3),
|
||||
("gbp-aya-idol", "少女乐团派对", "丸山彩所在乐队最核心的组合定位是?", "偶像乐队", ("幼驯染摇滚", "哥特交响", "欢乐马戏"), "丸山彩所在组合以偶像乐队定位为核心。", 3),
|
||||
("gbp-yukina-father", "少女乐团派对", "凑友希那坚持音乐的家庭原因主要和谁有关?", "父亲", ("姐姐", "祖母", "老师"), "凑友希那对音乐的执着,很大一部分和父亲的经历有关。", 4),
|
||||
("gbp-tsugumi-cafe", "少女乐团派对", "羽泽鸫家里经营的店更接近什么?", "咖啡店", ("唱片店", "花店", "乐器店"), "羽泽鸫家经营咖啡店,她也经常帮忙。", 3),
|
||||
("kpop-snsd-debut", "韩流", "少女时代的出道曲是哪首?", "再次重逢的世界", ("说出你的愿望", "哦", "狮心"), "少女时代的出道曲是再次重逢的世界。", 4),
|
||||
("kpop-redvelvet-leader", "韩流", "红贝贝的队长是谁?", "裴柱现", ("姜涩琪", "孙胜完", "朴秀荣"), "红贝贝的队长是裴柱现。", 3),
|
||||
("kpop-newjeans-fandom", "韩流", "新牛仔的官方粉丝名中文常译为什么?", "兔子", ("星星", "花园", "钥匙"), "新牛仔的官方粉丝名常译作兔子。", 4),
|
||||
("kpop-first-win", "韩流", "歌谣节目里常说的“一位”通常指什么?", "当期第一名", ("出道舞台", "安可失败", "投票资格"), "一位通常指某个歌谣节目当期排名第一。", 2),
|
||||
("kpop-lightstick", "韩流", "韩流演唱会里粉丝常用来应援的发光道具叫什么?", "应援棒", ("舞台牌", "投票券", "签售卡"), "应援棒是韩流现场应援里很常见的发光道具。", 2),
|
||||
("genshin-barbatos", "原神", "蒙德风神的真实魔神名是?", "巴巴托斯", ("摩拉克斯", "巴尔泽布", "布耶尔"), "蒙德风神温迪对应的魔神名是巴巴托斯。", 3),
|
||||
("genshin-morax", "原神", "璃月岩王帝君的魔神名是?", "摩拉克斯", ("巴巴托斯", "厄歌莉娅", "布耶尔"), "岩王帝君钟离对应的魔神名是摩拉克斯。", 3),
|
||||
("genshin-ei", "原神", "稻妻一心净土里的真正主人是谁?", "雷电影", ("雷电真", "八重神子", "九条裟罗"), "一心净土里的真正主人是雷电影。", 4),
|
||||
("genshin-nahida-title", "原神", "纳西妲作为神明对应的称号是?", "小吉祥草王", ("大慈树王", "尘世闲游", "正义之神"), "纳西妲在须弥被称为小吉祥草王。", 3),
|
||||
("genshin-neuvillette", "原神", "枫丹最高审判官是谁?", "那维莱特", ("莱欧斯利", "芙宁娜", "克洛琳德"), "那维莱特是枫丹的最高审判官。", 3),
|
||||
)
|
||||
TRIVIA_TEMPLATES = (
|
||||
"【{domain} 冷知识】{prompt}",
|
||||
@@ -378,12 +431,32 @@ def trivia_reward(rarity: int) -> int:
|
||||
return 20 + max(1, min(5, int(rarity))) * 30
|
||||
|
||||
|
||||
def trivia_topic_key(value: object) -> str:
|
||||
return re.sub(
|
||||
r"[\s'’`·・_\-::.。!!??]+",
|
||||
"",
|
||||
str(value or "").casefold(),
|
||||
)
|
||||
|
||||
|
||||
def canonical_trivia_topic(value: object) -> str | None:
|
||||
topic = trivia_topic_key(value)
|
||||
if not topic:
|
||||
return None
|
||||
return TRIVIA_TOPIC_ALIASES.get(topic)
|
||||
|
||||
|
||||
def supported_trivia_topics_message() -> str:
|
||||
return "支持主题:" + "、".join(TRIVIA_TOPIC_NAMES)
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def trivia_question_bank() -> tuple[dict[str, object], ...]:
|
||||
questions: list[dict[str, object]] = [
|
||||
{
|
||||
**question,
|
||||
"base_id": str(question["id"]),
|
||||
"topic": canonical_trivia_topic(question.get("domain")) or str(question["domain"]),
|
||||
"rarity": int(question.get("rarity", 4)),
|
||||
"reward": trivia_reward(int(question.get("rarity", 4))),
|
||||
}
|
||||
@@ -399,6 +472,7 @@ def trivia_question_bank() -> tuple[dict[str, object], ...]:
|
||||
"id": f"{base_id}-{variant + 1}",
|
||||
"base_id": base_id,
|
||||
"domain": domain,
|
||||
"topic": canonical_trivia_topic(domain) or domain,
|
||||
"question": template.format(domain=domain, prompt=prompt),
|
||||
"options": rotated,
|
||||
"answer": answer_label,
|
||||
@@ -6764,10 +6838,20 @@ class HourlyBroadcastEngine:
|
||||
*,
|
||||
group: str,
|
||||
local_now: datetime,
|
||||
topic: str | None = None,
|
||||
) -> dict[str, object]:
|
||||
bank = trivia_question_bank()
|
||||
bank = tuple(
|
||||
question
|
||||
for question in trivia_question_bank()
|
||||
if topic is None or str(question.get("topic")) == topic
|
||||
)
|
||||
if not bank:
|
||||
raise LocalCommandError("这个主题还没有题喵\n" + supported_trivia_topics_message())
|
||||
recent = set(self.trivia_recent_base_ids(group))
|
||||
seed = f"{group}|{local_now.date().isoformat()}|{local_now.hour:02d}|trivia"
|
||||
seed = (
|
||||
f"{group}|{local_now.date().isoformat()}|"
|
||||
f"{local_now.hour:02d}|trivia|{topic or 'all'}"
|
||||
)
|
||||
start = deterministic_int(seed, 0, len(bank) - 1)
|
||||
for offset in range(len(bank)):
|
||||
candidate = bank[(start + offset) % len(bank)]
|
||||
@@ -6783,8 +6867,15 @@ class HourlyBroadcastEngine:
|
||||
raise LocalCommandError("冷门问答题库选项无效")
|
||||
rarity = max(1, min(5, int(question.get("rarity", 3))))
|
||||
reward = max(1, min(500, int(question.get("reward", trivia_reward(rarity)))))
|
||||
topic = normalize_text(question.get("topic") or question.get("domain"))
|
||||
heading = f"冷门知识问答(冷门度 {rarity}/5,限时5分钟,答对 +{reward} 摩拉)"
|
||||
if topic:
|
||||
heading = (
|
||||
f"冷门知识问答(主题:{topic},冷门度 {rarity}/5,"
|
||||
f"限时5分钟,答对 +{reward} 摩拉)"
|
||||
)
|
||||
lines = [
|
||||
f"冷门知识问答(冷门度 {rarity}/5,限时5分钟,答对 +{reward} 摩拉)",
|
||||
heading,
|
||||
str(question.get("question", "")),
|
||||
]
|
||||
lines.extend(
|
||||
@@ -6799,8 +6890,13 @@ class HourlyBroadcastEngine:
|
||||
*,
|
||||
group: str,
|
||||
local_now: datetime,
|
||||
topic: str | None = None,
|
||||
) -> str:
|
||||
question = self.select_trivia_question(group=group, local_now=local_now)
|
||||
question = self.select_trivia_question(
|
||||
group=group,
|
||||
local_now=local_now,
|
||||
topic=topic,
|
||||
)
|
||||
message = self.render_trivia_message(question)
|
||||
expires_at = (
|
||||
local_now + timedelta(minutes=TRIVIA_ANSWER_WINDOW_MINUTES)
|
||||
@@ -6811,6 +6907,7 @@ class HourlyBroadcastEngine:
|
||||
{
|
||||
"question_id": str(question["id"]),
|
||||
"base_id": str(question.get("base_id") or question["id"]),
|
||||
"topic": str(question.get("topic") or question.get("domain") or ""),
|
||||
"question": str(question["question"]),
|
||||
"options": list(question["options"]),
|
||||
"answer": str(question["answer"]),
|
||||
@@ -6828,9 +6925,13 @@ class HourlyBroadcastEngine:
|
||||
self,
|
||||
*,
|
||||
group: str,
|
||||
topic_name: str = "",
|
||||
now: datetime | None = None,
|
||||
) -> str:
|
||||
local_now = self.in_beijing_timezone(now)
|
||||
topic = canonical_trivia_topic(topic_name)
|
||||
if normalize_text(topic_name) and topic is None:
|
||||
return supported_trivia_topics_message()
|
||||
active = self.ledger.local_game(group, TRIVIA_GAME_KEY)
|
||||
if active:
|
||||
try:
|
||||
@@ -6839,7 +6940,11 @@ class HourlyBroadcastEngine:
|
||||
except (TypeError, ValueError):
|
||||
return "冷门问答状态坏了喵"
|
||||
self.ledger.clear_local_game(group, TRIVIA_GAME_KEY)
|
||||
return self.start_trivia_question(group=group, local_now=local_now)
|
||||
return self.start_trivia_question(
|
||||
group=group,
|
||||
local_now=local_now,
|
||||
topic=topic,
|
||||
)
|
||||
|
||||
def due_trivia_broadcast(
|
||||
self,
|
||||
@@ -8107,10 +8212,11 @@ class EdgeApplication:
|
||||
}
|
||||
if decision.execution_path is ExecutionPath.LOCAL_COMMAND:
|
||||
try:
|
||||
command_kind = local_command_kind(str(row["question"]))
|
||||
command_kind, command_arguments = local_command_parts(str(row["question"]))
|
||||
reply = (
|
||||
self.clock.force_trivia_message(
|
||||
group=str(row["group_fingerprint"])
|
||||
group=str(row["group_fingerprint"]),
|
||||
topic_name=command_arguments,
|
||||
)
|
||||
if command_kind == "trivia_start"
|
||||
else
|
||||
|
||||
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-official-extensions"
|
||||
version = "1.2.5b1+1019"
|
||||
version = "1.2.6b1+1019"
|
||||
description = "Reviewed local command adapters and broker implementation for JWeBot"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = [
|
||||
"jwebot-sdk==1.2.5b1+1019",
|
||||
"jwebot-sdk==1.2.6b1+1019",
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
|
||||
@@ -90,4 +90,4 @@ __all__ = [
|
||||
"render_feature_list",
|
||||
]
|
||||
|
||||
__version__ = "1.2.5b1+1019"
|
||||
__version__ = "1.2.6b1+1019"
|
||||
|
||||
@@ -153,8 +153,8 @@ DIRECT_KERNEL_COMMANDS = (
|
||||
command_kinds=("deepseek_balance",),
|
||||
),
|
||||
KernelCommandDescriptor(
|
||||
description="手动触发冷门知识问答",
|
||||
help_entries=("/知识问答",),
|
||||
description="冷门问答",
|
||||
help_entries=("/知识问答 [主题]",),
|
||||
command_kinds=("trivia_start",),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jwebot-sdk"
|
||||
version = "1.2.5b1+1019"
|
||||
version = "1.2.6b1+1019"
|
||||
description = "Typed, capability-limited extension API for JWeBot plugins"
|
||||
requires-python = ">=3.9"
|
||||
dependencies = []
|
||||
|
||||
@@ -38,4 +38,4 @@ __all__ = [
|
||||
"SingleGroupContext",
|
||||
]
|
||||
|
||||
__version__ = "1.2.5b1+1019"
|
||||
__version__ = "1.2.6b1+1019"
|
||||
|
||||
Reference in New Issue
Block a user