Release beta 1.1.2.1019
check / local-gate (push) Failing after 53s

This commit is contained in:
JWeBot Contributors
2026-10-19 16:56:59 +08:00
parent 522e349491
commit ddebe075d7
30 changed files with 1433 additions and 46 deletions
+349 -3
View File
@@ -8,6 +8,7 @@ import sqlite3
import sys
import tempfile
import threading
import time
import unittest
from pathlib import Path
from unittest import mock
@@ -924,9 +925,9 @@ class LedgerAndApplicationTests(unittest.TestCase):
self.assertEqual(version["route"], "local")
self.assertIn("JWeBot 版本", version["text"])
self.assertIn("kernel", version["text"])
self.assertIn("最新版本:beta 1.1.1.1016", version["text"])
self.assertIn("更新时间:2026-10-16 17:19:45", version["text"])
self.assertIn("更新摘要:修复扫码加权重复购买", version["text"])
self.assertIn("最新版本:beta 1.1.2.1019", version["text"])
self.assertIn("更新时间:2026-10-19 16:38:10", version["text"])
self.assertIn("更新摘要:新增低频主动插话、随机冷门知识问答", version["text"])
self.assertEqual(debug["route"], "local")
self.assertIn("调试埋点", debug["text"])
self.assertEqual(balance["route"], "local")
@@ -1132,6 +1133,223 @@ class LedgerAndApplicationTests(unittest.TestCase):
self.assertIn(ranking[0]["display_name"], daily_delivery["result_text"])
self.application.deepseek.complete.assert_not_called()
def test_low_frequency_ambient_chat_uses_recent_context(self):
ledger = self.application.ledger
observed_at = MODULE.datetime(
2026,
10,
13,
12,
42,
tzinfo=MODULE.BEIJING_TIMEZONE,
).isoformat()
for index, (sender, text) in enumerate(
(
("Alice", "今天这个加班安排是不是有点离谱"),
("Bob", "我也觉得还要继续修 bug"),
("Alice", "先把问题处理完再说"),
("Bob", "感觉咖啡已经救不了我了"),
),
start=1,
):
self.assertTrue(
ledger.record_person_observation(
event_id=f"ambient-observation-{index}",
group_fingerprint="group-a",
sender=sender,
text=text,
observed_at=observed_at,
source="test",
)
)
ledger.record_repeat_history(
event_id=f"ambient-context-{index}",
group_fingerprint="group-a",
sender=sender,
text=text,
observed_at=observed_at,
)
self.application.deliver = mock.Mock()
now = MODULE.datetime(
2026,
10,
13,
13,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
with mock.patch.object(MODULE, "TRIVIA_QUIZ_PROBABILITY_PERCENT", 0), \
mock.patch.object(MODULE, "AMBIENT_CHAT_PROBABILITY_PERCENT", 100):
self.assertTrue(
self.application.process_clock_once(
now=now,
group_fingerprint="group-a",
)
)
delivery = self.application.deliver.call_args.args[0]
self.assertIn("加班味", delivery["result_text"])
row = ledger.connection.execute(
"""
SELECT kind, state FROM clock_broadcasts
WHERE group_fingerprint = ? AND slot_key = ?
""",
("group-a", "hourly:2026-10-13T13"),
).fetchone()
self.assertEqual(row["kind"], "ambient_chat")
self.assertEqual(row["state"], "delivered")
self.assertFalse(
self.application.process_clock_once(
now=now,
group_fingerprint="group-a",
)
)
def test_trivia_quiz_broadcast_and_answer_rewards_mora(self):
self.assertGreater(len(MODULE.trivia_question_bank()), 200)
ledger = self.application.ledger
observed_at = MODULE.datetime(
2026,
10,
13,
12,
42,
tzinfo=MODULE.BEIJING_TIMEZONE,
).isoformat()
for sender in ("Alice", "Bob"):
ledger.record_person_observation(
event_id=f"trivia-member-{sender}",
group_fingerprint="group-a",
sender=sender,
text="冷门题准备一下",
observed_at=observed_at,
source="test",
)
self.application.deliver = mock.Mock()
now = MODULE.datetime(
2026,
10,
13,
13,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
with mock.patch.object(MODULE, "TRIVIA_QUIZ_PROBABILITY_PERCENT", 100):
self.assertTrue(
self.application.process_clock_once(
now=now,
group_fingerprint="group-a",
)
)
delivery = self.application.deliver.call_args.args[0]
self.assertIn("冷门知识问答", delivery["result_text"])
self.assertIn("限时5分钟", delivery["result_text"])
self.assertIn("/回答 A", delivery["result_text"])
state = ledger.local_game("group-a", MODULE.TRIVIA_GAME_KEY)
self.assertIsNotNone(state)
assert state is not None
state["expires_at"] = time.time() + 300
ledger.save_local_game("group-a", MODULE.TRIVIA_GAME_KEY, state)
answer = str(state["answer"])
reward = int(state["reward"])
result = self.application.accept(
event(
"trivia-answer",
f"@JWeBot /回答 {answer}",
sender="Alice",
)
)
self.assertEqual(result["route"], "local")
self.assertIn("答对了", result["text"])
self.assertIn(f"奖励摩拉 +{reward}", result["text"])
self.assertIsNone(ledger.local_game("group-a", MODULE.TRIVIA_GAME_KEY))
person = ledger.resolve_person("group-a", "Alice")
account = ledger._load_player_account("group-a", str(person["person_key"]))
self.assertEqual(account["balance"], 1000 + reward)
def test_ambient_chat_cooldown_keeps_next_hour_scan_broadcast(self):
ledger = self.application.ledger
observed_at = MODULE.datetime(
2026,
10,
13,
12,
45,
tzinfo=MODULE.BEIJING_TIMEZONE,
).isoformat()
for index, (sender, text) in enumerate(
(
("Alice", "今天加班真的有点顶不住"),
("Bob", "先把这个线上问题看完"),
("Alice", "咖啡续上"),
("Bob", "修完再开一把游戏"),
),
start=1,
):
ledger.record_person_observation(
event_id=f"ambient-cooldown-observation-{index}",
group_fingerprint="group-a",
sender=sender,
text=text,
observed_at=observed_at,
source="test",
)
ledger.record_repeat_history(
event_id=f"ambient-cooldown-context-{index}",
group_fingerprint="group-a",
sender=sender,
text=text,
observed_at=observed_at,
)
self.application.deliver = mock.Mock()
first = MODULE.datetime(
2026,
10,
13,
13,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
second = MODULE.datetime(
2026,
10,
13,
14,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
with mock.patch.object(MODULE, "TRIVIA_QUIZ_PROBABILITY_PERCENT", 0), \
mock.patch.object(MODULE, "AMBIENT_CHAT_PROBABILITY_PERCENT", 100):
self.assertTrue(
self.application.process_clock_once(
now=first,
group_fingerprint="group-a",
)
)
self.assertTrue(
self.application.process_clock_once(
now=second,
group_fingerprint="group-a",
)
)
second_delivery = self.application.deliver.call_args.args[0]
self.assertIn("扫码了", second_delivery["result_text"])
row = ledger.connection.execute(
"""
SELECT kind, state FROM clock_broadcasts
WHERE group_fingerprint = ? AND slot_key = ?
""",
("group-a", "hourly:2026-10-13T14"),
).fetchone()
self.assertEqual(row["kind"], "hourly")
self.assertEqual(row["state"], "delivered")
def test_failed_hourly_broadcast_does_not_increment_scan_ranking(self):
ledger = self.application.ledger
for event_id, sender in (("clock-c", "Alice"), ("clock-d", "Bob")):
@@ -1162,6 +1380,134 @@ class LedgerAndApplicationTests(unittest.TestCase):
self.assertEqual(ledger.clock_scan_ranking("group-a", "2026-10-13"), [])
self.assertEqual(ledger.status()["clock_broadcasts_pending"], 0)
def test_existing_pending_hourly_broadcast_is_delivered(self):
ledger = self.application.ledger
for event_id, sender in (("clock-pending-a", "Alice"), ("clock-pending-b", "Bob")):
ledger.record_person_observation(
event_id=event_id,
group_fingerprint="group-a",
sender=sender,
text="群内聊天记录",
observed_at=MODULE.utc_now(),
source="test",
)
person = ledger.resolve_person("group-a", "Bob")
self.assertTrue(
ledger.reserve_clock_broadcast(
group_fingerprint="group-a",
slot_key="hourly:2026-10-13T05",
local_date="2026-10-13",
kind="hourly",
message="现在是北京时间 5:00,bob 扫码了。",
scan_person_key=str(person["person_key"]),
scan_label="bob",
)
)
self.application.deliver = mock.Mock()
now = MODULE.datetime(
2026,
10,
13,
5,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
self.assertTrue(
self.application.process_clock_once(
now=now,
group_fingerprint="group-a",
)
)
delivery = self.application.deliver.call_args.args[0]
self.assertIn("bob 扫码了", delivery["result_text"])
self.assertEqual(
ledger.clock_scan_ranking("group-a", "2026-10-13")[0]["display_name"],
"bob",
)
def test_scan_boost_pity_forces_target_after_two_misses(self):
ledger = self.application.ledger
for event_id, sender in (("pity-member-a", "Alice"), ("pity-member-b", "Bob")):
ledger.record_person_observation(
event_id=event_id,
group_fingerprint="group-a",
sender=sender,
text="群内聊天记录",
observed_at=MODULE.utc_now(),
source="test",
)
alice = ledger.resolve_person("group-a", "Alice")
bob = ledger.resolve_person("group-a", "Bob")
carol = ledger.resolve_person("group-a", "Carol")
account = MODULE.normalize_player_account({})
account["scan_boosts"] = [
{
"target_person_key": str(bob["person_key"]),
"target_display_name": "bob",
"weight": 5,
"created_at": MODULE.datetime(
2026,
10,
13,
3,
30,
tzinfo=MODULE.BEIJING_TIMEZONE,
).timestamp(),
"expires_at": MODULE.datetime(
2099,
10,
14,
3,
30,
tzinfo=MODULE.BEIJING_TIMEZONE,
).timestamp(),
}
]
ledger._save_player_account(
"group-a",
str(alice["person_key"]),
account,
MODULE.utc_now(),
)
for hour, person, label in (
(4, alice, "alice"),
(5, carol, "carol"),
):
slot_key = f"hourly:2026-10-13T{hour:02d}"
self.assertTrue(
ledger.reserve_clock_broadcast(
group_fingerprint="group-a",
slot_key=slot_key,
local_date="2026-10-13",
kind="hourly",
message="test",
scan_person_key=str(person["person_key"]),
scan_label=label,
)
)
self.assertTrue(ledger.complete_clock_broadcast("group-a", slot_key))
now = MODULE.datetime(
2026,
10,
13,
6,
0,
tzinfo=MODULE.BEIJING_TIMEZONE,
)
status = ledger.scan_boost_pity_status("group-a", local_now=now)
self.assertTrue(status[0]["guaranteed"])
self.assertEqual(status[0]["display_name"], "bob")
broadcast = self.application.clock.due_broadcast(
now=now,
group_fingerprint="group-a",
)
self.assertIsNotNone(broadcast)
self.assertEqual(broadcast.scan_label, "bob")
def test_scan_ranking_command_uses_current_beijing_date(self):
ledger = self.application.ledger
local_date = MODULE.datetime.now(MODULE.BEIJING_TIMEZONE).date().isoformat()