From ac630576c7ecb5d276a8e3cca37d96031e827bc1 Mon Sep 17 00:00:00 2001 From: JWeBot Contributors Date: Mon, 19 Oct 2026 17:40:46 +0800 Subject: [PATCH] Release beta 1.2.2.1019 --- CHANGELOG.md | 13 ++ README.md | 7 +- VERSION | 2 +- docs/command-catalog.md | 3 +- packages/jwebot-config/pyproject.toml | 2 +- .../src/jwebot_config/__init__.py | 2 +- packages/jwebot-extension-host/pyproject.toml | 6 +- .../src/jwebot_extension_host/__init__.py | 2 +- packages/jwebot-kernel/pyproject.toml | 8 +- .../src/jwebot_kernel/__init__.py | 2 +- .../src/jwebot_kernel/edge_runtime.py | 129 ++++++++++++++---- .../jwebot-official-extensions/pyproject.toml | 4 +- .../jwebot_official_extensions/__init__.py | 2 +- .../jwebot_official_extensions/descriptors.py | 6 + packages/jwebot-sdk/pyproject.toml | 2 +- .../jwebot-sdk/src/jwebot_sdk/__init__.py | 2 +- pyproject.toml | 2 +- tests/test_jwebot_edge.py | 66 ++++++++- tests/test_official_extension_inventory.py | 1 + tests/test_release_update.py | 2 +- wechat_adaptive_recorder.py | 10 ++ 21 files changed, 220 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 468144f..f613272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## beta 1.2.2.1019 - 2026-10-19 17:31:56 + +- 修复本地命令后的闲聊断上下文,新增 `/知识问答` 强制开题,并让多条回复按 0.5~1 秒随机间隔逐条发送。 +- Include non-tool local command results in the follow-up chat context so game + and account actions like `/原石祈愿` can be referenced naturally afterward. +- Exclude utility local commands such as `/version`, `/状态`, `/debug`, and + feature-list output from chat context to avoid noisy system details. +- Add randomized spacing between queued multi-part replies and immediate local + multi-reply sends. +- Add `/知识问答` as a Kernel-owned local command that starts a cold-knowledge + quiz immediately while reusing the active five-minute question when one is + already running. + ## beta 1.2.1.1019 - 2026-10-19 17:23:58 - 修复 Taffy 短句拆分只换行不分条的问题:多句闲聊现在会按多条消息逐条发送。 diff --git a/README.md b/README.md index 942a4e6..3086d4f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ JWeBot 是运行于 Linux 的单群微信自动回复系统。它通过本地 OC 观察选定的微信窗口,将消息写入持久化 Edge 账本,调用经过审核的命令插件, 并通过带视觉确认的 GUI 发送链路回复。 -当前版本:`beta 1.2.1.1019`,更新时间:2026-10-19 17:23:58。 +当前版本:`beta 1.2.2.1019`,更新时间:2026-10-19 17:31:56。 1.0 Beta 收口了当前稳定功能:本地命令插件、人物与上下文索引、 Steam 状态与时长查询、虚拟摩拉游戏、多人彩票、21 点、CS Major @@ -90,6 +90,7 @@ PATH="$PWD/.venv/bin:$PATH" ./tools/check-local.sh /21点 开始 [1~500];开局后 /21点 要牌|停牌|状态|结束 /21点 多人 启动;/21点 加入;/21点 多人 开始;/21点 放200 /摩拉(兼 /原石);/原石祈愿;/原石榜;/扫码加权 人名(24小时+5);/Taffy发话 内容 +/知识问答;/回答 A /游戏记录 [页码](/gamehistory) /游戏记录 [页码](/gamehistory) /游戏帮助(/games) @@ -269,10 +270,10 @@ visually confirmed GUI bridge. Current version: ```text -beta 1.2.1.1019 +beta 1.2.2.1019 ``` -Updated: 2026-10-19 17:23:58. +Updated: 2026-10-19 17:31:56. The 1.0 Beta baseline consolidates the stable local command set, person and context indexes, Steam presence/playtime queries, virtual coin games, diff --git a/VERSION b/VERSION index b28694a..a69435f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -beta 1.2.1.1019 +beta 1.2.2.1019 diff --git a/docs/command-catalog.md b/docs/command-catalog.md index afa4fab..8dfaf1a 100644 --- a/docs/command-catalog.md +++ b/docs/command-catalog.md @@ -1,6 +1,6 @@ # JWeBot Command Catalog -Current baseline: `beta 1.1.2.1019`. +Current baseline: `beta 1.2.2.1019`. This catalog records the reviewed commands shipped with the built-in local extensions. Runtime ownership is frozen at startup from the generated @@ -51,6 +51,7 @@ descriptor inventory; plugin code delegates to reviewed service profiles. | `/扫码加权 人名` | `/扫码概率 人名` | Spend a scan-boost ticket or 1 primogem to give that person +5 hourly scan weight for 24 hours. | | `/Taffy发话 内容` | `/taffysay content` | Spend Taffy speech credit; 1 primogem converts to 2 credits when needed. | | `/Taffy发话 定时 HH:MM 内容` | - | Schedule a Taffy speech through the guarded clock delivery queue. | +| `/知识问答` | - | Start a five-minute cold-knowledge quiz immediately, unless one is already active. | | `/回答 A` | `/answer A` | Answer the active five-minute cold-knowledge quiz for a rarity-based Mora reward. | | `/抽奖 [金额]` | `/draw` | Play a single-player prize draw. | | `/彩票 开始` | `/lottery start` | Open a multiplayer lottery round. | diff --git a/packages/jwebot-config/pyproject.toml b/packages/jwebot-config/pyproject.toml index 6079270..ca9e2d9 100644 --- a/packages/jwebot-config/pyproject.toml +++ b/packages/jwebot-config/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-config" -version = "1.2.1b1+1019" +version = "1.2.2b1+1019" description = "Profile-based configuration compiler for JWeBot" requires-python = ">=3.9" dependencies = [ diff --git a/packages/jwebot-config/src/jwebot_config/__init__.py b/packages/jwebot-config/src/jwebot_config/__init__.py index 8e0306d..c1c6a0a 100644 --- a/packages/jwebot-config/src/jwebot_config/__init__.py +++ b/packages/jwebot-config/src/jwebot_config/__init__.py @@ -36,4 +36,4 @@ __all__ = [ "write_default_profile", ] -__version__ = "1.2.1b1+1019" +__version__ = "1.2.2b1+1019" diff --git a/packages/jwebot-extension-host/pyproject.toml b/packages/jwebot-extension-host/pyproject.toml index 168fc27..fc22c55 100644 --- a/packages/jwebot-extension-host/pyproject.toml +++ b/packages/jwebot-extension-host/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-extension-host" -version = "1.2.1b1+1019" +version = "1.2.2b1+1019" description = "Single-release, single-invocation process host for JWeBot extensions" requires-python = ">=3.9" dependencies = [ - "jwebot-sdk==1.2.1b1+1019", - "jwebot-kernel==1.2.1b1+1019", + "jwebot-sdk==1.2.2b1+1019", + "jwebot-kernel==1.2.2b1+1019", ] [project.scripts] diff --git a/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py b/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py index b3f2b2c..dd14416 100644 --- a/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py +++ b/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py @@ -16,4 +16,4 @@ __all__ = [ "process_request", ] -__version__ = "1.2.1b1+1019" +__version__ = "1.2.2b1+1019" diff --git a/packages/jwebot-kernel/pyproject.toml b/packages/jwebot-kernel/pyproject.toml index 715ea3f..441df0c 100644 --- a/packages/jwebot-kernel/pyproject.toml +++ b/packages/jwebot-kernel/pyproject.toml @@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-kernel" -version = "1.2.1b1+1019" +version = "1.2.2b1+1019" description = "Stable routing and extension authority for JWeBot" requires-python = ">=3.9" dependencies = [ - "jwebot-config==1.2.1b1+1019", - "jwebot-sdk==1.2.1b1+1019", - "jwebot-official-extensions==1.2.1b1+1019", + "jwebot-config==1.2.2b1+1019", + "jwebot-sdk==1.2.2b1+1019", + "jwebot-official-extensions==1.2.2b1+1019", "tomli>=2; python_version < '3.11'", ] diff --git a/packages/jwebot-kernel/src/jwebot_kernel/__init__.py b/packages/jwebot-kernel/src/jwebot_kernel/__init__.py index 388b541..3446678 100644 --- a/packages/jwebot-kernel/src/jwebot_kernel/__init__.py +++ b/packages/jwebot-kernel/src/jwebot_kernel/__init__.py @@ -107,4 +107,4 @@ __all__ = [ "verify_installed_release_signature", ] -__version__ = "1.2.1b1+1019" +__version__ = "1.2.2b1+1019" diff --git a/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py b/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py index ece4918..359ce7d 100644 --- a/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py +++ b/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py @@ -8,6 +8,7 @@ import hashlib import json import logging import os +import random import re import shlex import signal @@ -104,6 +105,8 @@ EXTENSION_SOCKET_ROOT = Path("/run/jwebot-extension-host") MAX_EVENT_TEXT = 5_000 MAX_REPLY_CHARS = 900 MAX_CONTEXT_TURNS = 6 +MULTI_REPLY_DELAY_MIN_SECONDS = 0.5 +MULTI_REPLY_DELAY_MAX_SECONDS = 1.0 OCR_EVENT_SOURCES = frozenset({"ocr", "rapidocr-local"}) OCR_SEMANTIC_DUPLICATE_SECONDS = 8.0 OCR_RESOURCE_MUTATION_DUPLICATE_SECONDS = 75.0 @@ -117,6 +120,24 @@ OCR_RESOURCE_MUTATION_REASONS = frozenset( "local_transfer", } ) +LOCAL_CONTEXT_EXCLUDED_REASONS = frozenset( + { + "local_feature_list", + "local_context_summary", + "local_person_directory", + "local_person_info", + "local_person_memory", + "local_my_memory", + "local_forget_memory", + "local_memory_help", + "local_local_status", + "local_server_status", + "local_jwebot_version", + "local_telemetry_debug", + "local_deepseek_balance", + "local_unknown", + } +) MAX_OUTBOX_ATTEMPTS = 3 DIRECT_CHAT_GENERATION_WORKERS = 2 DIRECT_API_SOCKET_TIMEOUT_SECONDS = 18.0 @@ -1645,21 +1666,37 @@ class Ledger: with self.lock: rows = self.connection.execute( """ - SELECT question, response_text + SELECT question, response_text, route, route_reason FROM events WHERE group_fingerprint = ? AND event_id != ? - AND route = 'chat' - AND status IN ('chat_completed', 'chat_delivered') + AND ( + (route = 'chat' AND status IN ('chat_completed', 'chat_delivered')) + OR (route = 'local' AND status = 'local_completed') + ) AND response_text != '' ORDER BY created_at DESC LIMIT ? """, - (group_fingerprint, exclude_event_id, limit), + (group_fingerprint, exclude_event_id, limit * 3), ).fetchall() result: list[tuple[str, str]] = [] for row in reversed(rows): - result.append((str(row["question"]), str(row["response_text"]))) + if ( + row["route"] == "local" + and str(row["route_reason"]) in LOCAL_CONTEXT_EXCLUDED_REASONS + ): + continue + response_text = str(row["response_text"]) + parts = unpack_reply_parts(response_text) + result.append( + ( + str(row["question"]), + "\n".join(parts) if parts is not None else response_text, + ) + ) + if len(result) >= limit: + break return result def local_game( @@ -6749,6 +6786,53 @@ class HourlyBroadcastEngine: lines.append("回答格式:/回答 A") return "\n".join(lines) + def start_trivia_question( + self, + *, + group: str, + local_now: datetime, + ) -> str: + question = self.select_trivia_question(group=group, local_now=local_now) + message = self.render_trivia_message(question) + expires_at = ( + local_now + timedelta(minutes=TRIVIA_ANSWER_WINDOW_MINUTES) + ).timestamp() + self.ledger.save_local_game( + group, + TRIVIA_GAME_KEY, + { + "question_id": str(question["id"]), + "base_id": str(question.get("base_id") or question["id"]), + "question": str(question["question"]), + "options": list(question["options"]), + "answer": str(question["answer"]), + "explanation": str(question["explanation"]), + "rarity": int(question.get("rarity", 3)), + "reward": int(question.get("reward", 50)), + "created_at": local_now.timestamp(), + "expires_at": expires_at, + }, + ) + self.remember_trivia_question(group, question) + return message + + def force_trivia_message( + self, + *, + group: str, + now: datetime | None = None, + ) -> str: + local_now = self.in_beijing_timezone(now) + active = self.ledger.local_game(group, TRIVIA_GAME_KEY) + if active: + try: + if float(active.get("expires_at", 0)) > local_now.timestamp(): + return "已经有一题在进行中喵\n" + self.render_trivia_message(active) + except (TypeError, ValueError): + return "冷门问答状态坏了喵" + self.ledger.clear_local_game(group, TRIVIA_GAME_KEY) + return self.start_trivia_question(group=group, local_now=local_now) + def due_trivia_broadcast( self, *, @@ -6772,28 +6856,7 @@ class HourlyBroadcastEngine: seed = f"{group}|{local_date}|{local_now.hour:02d}|trivia-probability" if deterministic_int(seed, 0, 99) >= TRIVIA_QUIZ_PROBABILITY_PERCENT: return None - question = self.select_trivia_question(group=group, local_now=local_now) - message = self.render_trivia_message(question) - expires_at = ( - local_now + timedelta(minutes=TRIVIA_ANSWER_WINDOW_MINUTES) - ).timestamp() - self.ledger.save_local_game( - group, - TRIVIA_GAME_KEY, - { - "question_id": str(question["id"]), - "base_id": str(question.get("base_id") or question["id"]), - "question": str(question["question"]), - "options": list(question["options"]), - "answer": str(question["answer"]), - "explanation": str(question["explanation"]), - "rarity": int(question.get("rarity", 3)), - "reward": int(question.get("reward", 50)), - "created_at": local_now.timestamp(), - "expires_at": expires_at, - }, - ) - self.remember_trivia_question(group, question) + message = self.start_trivia_question(group=group, local_now=local_now) return ClockBroadcast( group_fingerprint=group, slot_key=slot_key, @@ -8038,6 +8101,11 @@ class EdgeApplication: try: command_kind = local_command_kind(str(row["question"])) reply = ( + self.clock.force_trivia_message( + group=str(row["group_fingerprint"]) + ) + if command_kind == "trivia_start" + else self._execute_local_extension(row, event) if ( self.config.local_extensions_enabled @@ -8705,6 +8773,13 @@ class EdgeApplication: if "missing expected group" in output: raise EdgeError("missing expected group") raise EdgeError("delivery helper failed") + if index < len(replies): + time.sleep( + random.uniform( + MULTI_REPLY_DELAY_MIN_SECONDS, + MULTI_REPLY_DELAY_MAX_SECONDS, + ) + ) self.telemetry.elapsed( "edge.delivery.process", started, diff --git a/packages/jwebot-official-extensions/pyproject.toml b/packages/jwebot-official-extensions/pyproject.toml index 5933bfb..5f52b0a 100644 --- a/packages/jwebot-official-extensions/pyproject.toml +++ b/packages/jwebot-official-extensions/pyproject.toml @@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-official-extensions" -version = "1.2.1b1+1019" +version = "1.2.2b1+1019" description = "Reviewed local command adapters and broker implementation for JWeBot" requires-python = ">=3.9" dependencies = [ - "jwebot-sdk==1.2.1b1+1019", + "jwebot-sdk==1.2.2b1+1019", ] [tool.setuptools.packages.find] diff --git a/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py b/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py index d5961b5..eea351d 100644 --- a/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py +++ b/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py @@ -90,4 +90,4 @@ __all__ = [ "render_feature_list", ] -__version__ = "1.2.1b1+1019" +__version__ = "1.2.2b1+1019" diff --git a/packages/jwebot-official-extensions/src/jwebot_official_extensions/descriptors.py b/packages/jwebot-official-extensions/src/jwebot_official_extensions/descriptors.py index e391d09..dd58ea1 100644 --- a/packages/jwebot-official-extensions/src/jwebot_official_extensions/descriptors.py +++ b/packages/jwebot-official-extensions/src/jwebot_official_extensions/descriptors.py @@ -63,6 +63,7 @@ LOCAL_COMMAND_ALIASES = { "扫码概率": "scan_boost", "Taffy发话": "taffy_say", "taffysay": "taffy_say", + "知识问答": "trivia_start", "回答": "trivia_answer", "answer": "trivia_answer", "转账": "transfer", @@ -151,6 +152,11 @@ DIRECT_KERNEL_COMMANDS = ( help_entries=("/余额",), command_kinds=("deepseek_balance",), ), + KernelCommandDescriptor( + description="手动触发冷门知识问答", + help_entries=("/知识问答",), + command_kinds=("trivia_start",), + ), ) DIRECT_KERNEL_COMMAND_KINDS = frozenset( kind diff --git a/packages/jwebot-sdk/pyproject.toml b/packages/jwebot-sdk/pyproject.toml index 6c6e7ab..d7957bb 100644 --- a/packages/jwebot-sdk/pyproject.toml +++ b/packages/jwebot-sdk/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-sdk" -version = "1.2.1b1+1019" +version = "1.2.2b1+1019" description = "Typed, capability-limited extension API for JWeBot plugins" requires-python = ">=3.9" dependencies = [] diff --git a/packages/jwebot-sdk/src/jwebot_sdk/__init__.py b/packages/jwebot-sdk/src/jwebot_sdk/__init__.py index 6d66356..2f41bf7 100644 --- a/packages/jwebot-sdk/src/jwebot_sdk/__init__.py +++ b/packages/jwebot-sdk/src/jwebot_sdk/__init__.py @@ -38,4 +38,4 @@ __all__ = [ "SingleGroupContext", ] -__version__ = "1.2.1b1+1019" +__version__ = "1.2.2b1+1019" diff --git a/pyproject.toml b/pyproject.toml index 2545954..f55f121 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.jwebot] -release = "beta 1.2.1.1019" +release = "beta 1.2.2.1019" production_deployments_enabled_by_default = false [tool.jwebot.workspace] diff --git a/tests/test_jwebot_edge.py b/tests/test_jwebot_edge.py index 1f33723..e3d7967 100644 --- a/tests/test_jwebot_edge.py +++ b/tests/test_jwebot_edge.py @@ -211,6 +211,10 @@ class RoutingTests(unittest.TestCase): MODULE.classify_question("/steam 在线"), ("local", "local_steam_friends"), ) + self.assertEqual( + MODULE.classify_question("/知识问答"), + ("local", "local_trivia_start"), + ) def test_reply_has_one_suffix_within_delivery_limit(self): self.assertEqual(MODULE.clean_reply("收到。"), "收到喵") @@ -308,6 +312,24 @@ class LedgerAndApplicationTests(unittest.TestCase): "第二条回复喵", ) + def test_local_game_command_result_is_available_to_followup_chat(self): + observed_context = [] + + def complete(**kwargs): + observed_context.extend(kwargs["context"]) + return "我看到了喵" + + self.application.deepseek.complete = complete + self.application.accept(event("version-context", "@JWeBot /version")) + local = self.application.accept(event("coin-context", "@JWeBot /翻硬币")) + self.application.accept(event("chat-context", "@JWeBot 刚才是什么结果")) + self.application.process_direct_chat_once() + + self.assertIn(("/翻硬币", local["text"]), observed_context) + self.assertTrue( + all(question != "/version" for question, _reply in observed_context) + ) + def test_route_and_ack_telemetry_is_privacy_safe(self): observed = [] with tempfile.TemporaryDirectory() as directory: @@ -450,6 +472,12 @@ class LedgerAndApplicationTests(unittest.TestCase): os.environ, {"JWEBOT_RUNTIME_PYTHON": str(runtime_python)}, ), \ + mock.patch.object( + MODULE.random, + "uniform", + return_value=0.75, + ) as uniform, \ + mock.patch.object(MODULE.time, "sleep") as sleep, \ mock.patch.object( MODULE.subprocess, "run", @@ -482,6 +510,11 @@ class LedgerAndApplicationTests(unittest.TestCase): }, ], ) + uniform.assert_called_once_with( + MODULE.MULTI_REPLY_DELAY_MIN_SECONDS, + MODULE.MULTI_REPLY_DELAY_MAX_SECONDS, + ) + sleep.assert_called_once_with(0.75) def test_two_direct_generations_run_in_parallel(self): entered = [] @@ -996,9 +1029,9 @@ class LedgerAndApplicationTests(unittest.TestCase): self.assertEqual(version["route"], "local") self.assertIn("JWeBot 版本", version["text"]) self.assertIn("kernel", version["text"]) - self.assertIn("最新版本:beta 1.2.1.1019", version["text"]) - self.assertIn("更新时间:2026-10-19 17:23:58", version["text"]) - self.assertIn("更新摘要:修复 Taffy 短句拆分只换行不分条", version["text"]) + self.assertIn("最新版本:beta 1.2.2.1019", version["text"]) + self.assertIn("更新时间:2026-10-19 17:31:56", version["text"]) + self.assertIn("更新摘要:修复本地命令后的闲聊断上下文", version["text"]) self.assertEqual(debug["route"], "local") self.assertIn("调试埋点", debug["text"]) self.assertEqual(balance["route"], "local") @@ -1342,6 +1375,33 @@ class LedgerAndApplicationTests(unittest.TestCase): account = ledger._load_player_account("group-a", str(person["person_key"])) self.assertEqual(account["balance"], 1000 + reward) + def test_trivia_quiz_can_be_started_by_local_command(self): + self.application.deepseek.complete = mock.Mock(side_effect=AssertionError) + + first = self.application.accept( + event("trivia-force", "@JWeBot /知识问答") + ) + second = self.application.accept( + event("trivia-force-existing", "@JWeBot /知识问答") + ) + + self.assertEqual(first["route"], "local") + self.assertEqual(first["execution_path"], "local-command") + self.assertIn("冷门知识问答", first["text"]) + self.assertIn("回答格式:/回答 A", first["text"]) + self.assertIn("已经有一题在进行中", second["text"]) + self.assertEqual( + self.application.ledger.row("trivia-force")["status"], + "local_completed", + ) + self.assertIsNotNone( + self.application.ledger.local_game( + "group-a", + MODULE.TRIVIA_GAME_KEY, + ) + ) + self.application.deepseek.complete.assert_not_called() + def test_ambient_chat_cooldown_keeps_next_hour_scan_broadcast(self): ledger = self.application.ledger observed_at = MODULE.datetime( diff --git a/tests/test_official_extension_inventory.py b/tests/test_official_extension_inventory.py index 66d5fdc..937cac3 100644 --- a/tests/test_official_extension_inventory.py +++ b/tests/test_official_extension_inventory.py @@ -152,6 +152,7 @@ class LocalExtensionInventoryTests(unittest.TestCase): { "feature_list", "deepseek_balance", + "trivia_start", } ), ) diff --git a/tests/test_release_update.py b/tests/test_release_update.py index 1a509a4..e0091f3 100644 --- a/tests/test_release_update.py +++ b/tests/test_release_update.py @@ -20,7 +20,7 @@ SPEC.loader.exec_module(MODULE) class ReleaseUpdateTests(unittest.TestCase): - release = "beta 1.2.1.1019" + release = "beta 1.2.2.1019" def setUp(self): self.temporary = tempfile.TemporaryDirectory() diff --git a/wechat_adaptive_recorder.py b/wechat_adaptive_recorder.py index 0475746..4e863f3 100755 --- a/wechat_adaptive_recorder.py +++ b/wechat_adaptive_recorder.py @@ -10,6 +10,7 @@ import hashlib import logging import os import queue +import random import socket import sys import threading @@ -43,6 +44,8 @@ GUI_SENDER_TIMEOUT_SECONDS = float( os.environ.get("JWEBOT_GUI_SENDER_TIMEOUT_SECONDS", "55") ) GUI_SENDER_MAX_PAYLOAD_BYTES = 65536 +MULTI_REPLY_DELAY_MIN_SECONDS = 0.5 +MULTI_REPLY_DELAY_MAX_SECONDS = 1.0 BASE_WIDTH = 1440 BASE_HEIGHT = 873 @@ -1052,6 +1055,13 @@ def run() -> None: event_id=event_id, source=f"local-multi-{index}", ) + if index < len(texts): + time.sleep( + random.uniform( + MULTI_REPLY_DELAY_MIN_SECONDS, + MULTI_REPLY_DELAY_MAX_SECONDS, + ) + ) except Exception as error: logging.warning( "multi reply GUI delivery failed event_hash=%s error_type=%s",