From b161a7528a42c7fa5a99b6678637aafea4ed5b54 Mon Sep 17 00:00:00 2001 From: annguyenNous Date: Tue, 23 Jun 2026 10:52:44 +0700 Subject: [PATCH] fix(plugins): add explicit encoding to read_text/write_text in plugins/ Fix 13 plugin files with unencoded read_text()/write_text() calls. Path.read_text() defaults to system locale (cp1252 on Windows). Ruff rule PLW1514. --- plugins/disk-cleanup/disk_cleanup.py | 6 +++--- plugins/google_meet/realtime/openai_client.py | 6 +++--- plugins/hermes-achievements/dashboard/plugin_api.py | 12 ++++++------ plugins/memory/hindsight/__init__.py | 8 ++++---- plugins/memory/honcho/__init__.py | 2 +- plugins/memory/mem0/__init__.py | 2 +- plugins/memory/mem0/_setup.py | 10 +++++----- plugins/platforms/discord/adapter.py | 2 +- plugins/platforms/feishu/adapter.py | 2 +- plugins/platforms/google_chat/adapter.py | 4 ++-- plugins/platforms/google_chat/oauth.py | 4 ++-- plugins/platforms/telegram/adapter.py | 2 +- plugins/platforms/whatsapp/adapter.py | 8 ++++---- 13 files changed, 34 insertions(+), 34 deletions(-) diff --git a/plugins/disk-cleanup/disk_cleanup.py b/plugins/disk-cleanup/disk_cleanup.py index 8f70631ea84f..e35114df74a1 100755 --- a/plugins/disk-cleanup/disk_cleanup.py +++ b/plugins/disk-cleanup/disk_cleanup.py @@ -110,12 +110,12 @@ def load_tracked() -> List[Dict[str, Any]]: return [] try: - return json.loads(tf.read_text()) + return json.loads(tf.read_text(encoding="utf-8")) except (json.JSONDecodeError, ValueError): bak = tf.with_suffix(".json.bak") if bak.exists(): try: - data = json.loads(bak.read_text()) + data = json.loads(bak.read_text(encoding="utf-8")) _log("WARN: tracked.json corrupted — restored from .bak") return data except Exception: @@ -129,7 +129,7 @@ def save_tracked(tracked: List[Dict[str, Any]]) -> None: tf = get_tracked_file() tf.parent.mkdir(parents=True, exist_ok=True) tmp = tf.with_suffix(".json.tmp") - tmp.write_text(json.dumps(tracked, indent=2)) + tmp.write_text(json.dumps(tracked, indent=2), encoding="utf-8") if tf.exists(): shutil.copy2(tf, tf.with_suffix(".json.bak")) tmp.replace(tf) diff --git a/plugins/google_meet/realtime/openai_client.py b/plugins/google_meet/realtime/openai_client.py index 24527603e524..467c4058b486 100644 --- a/plugins/google_meet/realtime/openai_client.py +++ b/plugins/google_meet/realtime/openai_client.py @@ -262,7 +262,7 @@ def _read_queue(self) -> list[dict]: if not self.queue_path.exists(): return [] out: list[dict] = [] - for line in self.queue_path.read_text().splitlines(): + for line in self.queue_path.read_text(encoding="utf-8").splitlines(): line = line.strip() if not line: continue @@ -281,11 +281,11 @@ def _rewrite_queue(self, remaining: list[dict]) -> None: if not remaining: # Keep the file but empty — consumers may be watching for # new writes via mtime, and delete-then-recreate is a race. - self.queue_path.write_text("") + self.queue_path.write_text("", encoding="utf-8") return self.queue_path.write_text( "\n".join(json.dumps(e) for e in remaining) + "\n" - ) + , encoding="utf-8") def _append_processed(self, entry: dict, result: dict) -> None: if self.processed_path is None: diff --git a/plugins/hermes-achievements/dashboard/plugin_api.py b/plugins/hermes-achievements/dashboard/plugin_api.py index b419efc6c27f..c2f69a22999b 100644 --- a/plugins/hermes-achievements/dashboard/plugin_api.py +++ b/plugins/hermes-achievements/dashboard/plugin_api.py @@ -159,7 +159,7 @@ def load_state() -> Dict[str, Any]: if not path.exists(): return {"unlocks": {}} try: - return json.loads(path.read_text()) + return json.loads(path.read_text(encoding="utf-8")) except Exception: return {"unlocks": {}} @@ -167,7 +167,7 @@ def load_state() -> Dict[str, Any]: def save_state(state: Dict[str, Any]) -> None: path = state_path() path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(state, indent=2, sort_keys=True)) + path.write_text(json.dumps(state, indent=2, sort_keys=True), encoding="utf-8") def _json_safe(value: Any) -> Any: @@ -185,7 +185,7 @@ def load_snapshot() -> Optional[Dict[str, Any]]: if not path.exists(): return None try: - data = json.loads(path.read_text()) + data = json.loads(path.read_text(encoding="utf-8")) if isinstance(data, dict): return data except Exception: @@ -196,7 +196,7 @@ def load_snapshot() -> Optional[Dict[str, Any]]: def save_snapshot(data: Dict[str, Any]) -> None: path = snapshot_path() path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(_json_safe(data), indent=2, sort_keys=True)) + path.write_text(json.dumps(_json_safe(data), indent=2, sort_keys=True), encoding="utf-8") def load_checkpoint() -> Dict[str, Any]: @@ -204,7 +204,7 @@ def load_checkpoint() -> Dict[str, Any]: if not path.exists(): return {"schema_version": 1, "generated_at": 0, "sessions": {}} try: - data = json.loads(path.read_text()) + data = json.loads(path.read_text(encoding="utf-8")) if isinstance(data, dict): data.setdefault("schema_version", 1) data.setdefault("generated_at", 0) @@ -219,7 +219,7 @@ def load_checkpoint() -> Dict[str, Any]: def save_checkpoint(data: Dict[str, Any]) -> None: path = checkpoint_path() path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(json.dumps(_json_safe(data), indent=2, sort_keys=True)) + path.write_text(json.dumps(_json_safe(data), indent=2, sort_keys=True), encoding="utf-8") def session_fingerprint(meta: Dict[str, Any]) -> Dict[str, Any]: diff --git a/plugins/memory/hindsight/__init__.py b/plugins/memory/hindsight/__init__.py index 9f5974b7b542..1c284f218b58 100644 --- a/plugins/memory/hindsight/__init__.py +++ b/plugins/memory/hindsight/__init__.py @@ -746,7 +746,7 @@ def save_config(self, values, hermes_home): existing = {} if config_path.exists(): try: - existing = json.loads(config_path.read_text()) + existing = json.loads(config_path.read_text(encoding="utf-8")) except Exception: pass existing.update(values) @@ -895,7 +895,7 @@ def post_setup(self, hermes_home: str, config: dict) -> None: env_path = Path(hermes_home) / ".env" existing_llm_key = "" if env_path.exists(): - for line in env_path.read_text().splitlines(): + for line in env_path.read_text(encoding="utf-8").splitlines(): if line.startswith("HINDSIGHT_LLM_API_KEY="): existing_llm_key = line.split("=", 1)[1] break @@ -925,7 +925,7 @@ def post_setup(self, hermes_home: str, config: dict) -> None: env_path.parent.mkdir(parents=True, exist_ok=True) existing_lines = [] if env_path.exists(): - existing_lines = env_path.read_text().splitlines() + existing_lines = env_path.read_text(encoding="utf-8").splitlines() updated_keys = set() new_lines = [] for line in existing_lines: @@ -938,7 +938,7 @@ def post_setup(self, hermes_home: str, config: dict) -> None: for k, v in env_writes.items(): if k not in updated_keys: new_lines.append(f"{k}={v}") - env_path.write_text("\n".join(new_lines) + "\n") + env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8") if mode == "local_embedded": materialized_config = dict(provider_config) diff --git a/plugins/memory/honcho/__init__.py b/plugins/memory/honcho/__init__.py index c9ddc41bc898..58d5a534f4c9 100644 --- a/plugins/memory/honcho/__init__.py +++ b/plugins/memory/honcho/__init__.py @@ -271,7 +271,7 @@ def save_config(self, values, hermes_home): existing = {} if config_path.exists(): try: - existing = json.loads(config_path.read_text()) + existing = json.loads(config_path.read_text(encoding="utf-8")) except Exception: pass existing.update(values) diff --git a/plugins/memory/mem0/__init__.py b/plugins/memory/mem0/__init__.py index eccf6ad53fe2..582e2bafb1ed 100644 --- a/plugins/memory/mem0/__init__.py +++ b/plugins/memory/mem0/__init__.py @@ -227,7 +227,7 @@ def save_config(self, values, hermes_home): existing = {} if config_path.exists(): try: - existing = json.loads(config_path.read_text()) + existing = json.loads(config_path.read_text(encoding="utf-8")) except Exception: pass existing.update(values) diff --git a/plugins/memory/mem0/_setup.py b/plugins/memory/mem0/_setup.py index 4fd9795b32d9..4474295ed0b0 100644 --- a/plugins/memory/mem0/_setup.py +++ b/plugins/memory/mem0/_setup.py @@ -189,7 +189,7 @@ def _write_env(env_path: Path, env_writes: dict[str, str]) -> None: env_path.parent.mkdir(parents=True, exist_ok=True) existing_lines: list[str] = [] if env_path.exists(): - existing_lines = env_path.read_text().splitlines() + existing_lines = env_path.read_text(encoding="utf-8").splitlines() updated_keys: set[str] = set() new_lines: list[str] = [] @@ -204,7 +204,7 @@ def _write_env(env_path: Path, env_writes: dict[str, str]) -> None: if k not in updated_keys: new_lines.append(f"{k}={v}") - env_path.write_text("\n".join(new_lines) + "\n") + env_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8") def _save_mem0_json(hermes_home: str, data: dict) -> None: @@ -217,7 +217,7 @@ def _save_mem0_json(hermes_home: str, data: dict) -> None: except Exception: pass existing.update(data) - config_path.write_text(json.dumps(existing, indent=2) + "\n") + config_path.write_text(json.dumps(existing, indent=2) + "\n", encoding="utf-8") def _setup_platform(hermes_home: str, config: dict, flags: dict[str, str]) -> None: @@ -237,7 +237,7 @@ def _setup_platform(hermes_home: str, config: dict, flags: dict[str, str]) -> No config_path = Path(hermes_home) / "mem0.json" if config_path.exists(): try: - existing_config = json.loads(config_path.read_text()) + existing_config = json.loads(config_path.read_text(encoding="utf-8")) except Exception: pass @@ -375,7 +375,7 @@ def _prompt_api_key(label: str, env_var: str, hermes_home: str) -> str: if not existing: env_path = Path(hermes_home) / ".env" if env_path.exists(): - for line in env_path.read_text().splitlines(): + for line in env_path.read_text(encoding="utf-8").splitlines(): if line.startswith(f"{env_var}="): existing = line.split("=", 1)[1].strip() break diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index 7d14adfcc706..05071f7de889 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -6053,7 +6053,7 @@ async def _respond( home = get_hermes_home() response_path = home / ".update_response" tmp = response_path.with_suffix(".tmp") - tmp.write_text(answer) + tmp.write_text(answer, encoding="utf-8") tmp.replace(response_path) logger.info( "Discord update prompt answered '%s' by %s", diff --git a/plugins/platforms/feishu/adapter.py b/plugins/platforms/feishu/adapter.py index bf3c49d3b867..78b30a60fceb 100644 --- a/plugins/platforms/feishu/adapter.py +++ b/plugins/platforms/feishu/adapter.py @@ -2040,7 +2040,7 @@ def _build_resolved_update_prompt_card(*, answer: str, user_name: str) -> Dict[s def _write_update_prompt_response(answer: str) -> None: response_path = get_hermes_home() / ".update_response" tmp_path = response_path.with_suffix(".tmp") - tmp_path.write_text(answer) + tmp_path.write_text(answer, encoding="utf-8") tmp_path.replace(response_path) async def send_voice( diff --git a/plugins/platforms/google_chat/adapter.py b/plugins/platforms/google_chat/adapter.py index 6f738488123c..7d5ebbfd21ab 100644 --- a/plugins/platforms/google_chat/adapter.py +++ b/plugins/platforms/google_chat/adapter.py @@ -362,7 +362,7 @@ def load(self) -> None: self._counts = {} return try: - raw = self._path.read_text() + raw = self._path.read_text(encoding="utf-8") data = json.loads(raw) if raw.strip() else {} except json.JSONDecodeError as exc: logger.warning( @@ -417,7 +417,7 @@ def _save(self) -> None: try: self._path.parent.mkdir(parents=True, exist_ok=True) tmp = self._path.with_suffix(self._path.suffix + ".tmp") - tmp.write_text(json.dumps(self._counts, separators=(",", ":"))) + tmp.write_text(json.dumps(self._counts, separators=(",", ":")), encoding="utf-8") os.replace(tmp, self._path) except OSError as exc: logger.warning( diff --git a/plugins/platforms/google_chat/oauth.py b/plugins/platforms/google_chat/oauth.py index 3d481b3ead7b..04d8f23b0aab 100644 --- a/plugins/platforms/google_chat/oauth.py +++ b/plugins/platforms/google_chat/oauth.py @@ -419,7 +419,7 @@ def store_client_secret(path: str) -> None: sys.exit(1) try: - data = json.loads(src.read_text()) + data = json.loads(src.read_text(encoding="utf-8")) except json.JSONDecodeError: print("ERROR: File is not valid JSON.") sys.exit(1) @@ -459,7 +459,7 @@ def _load_pending_auth(email: Optional[str] = None) -> dict: print("ERROR: No pending OAuth session found. Run --auth-url first.") sys.exit(1) try: - data = json.loads(pending.read_text()) + data = json.loads(pending.read_text(encoding="utf-8")) except Exception as exc: print(f"ERROR: Could not read pending OAuth session: {exc}") print("Run --auth-url again to start a fresh session.") diff --git a/plugins/platforms/telegram/adapter.py b/plugins/platforms/telegram/adapter.py index 2de169ee0926..d3adb7f2e432 100644 --- a/plugins/platforms/telegram/adapter.py +++ b/plugins/platforms/telegram/adapter.py @@ -4464,7 +4464,7 @@ async def _handle_callback_query( home = get_hermes_home() response_path = home / ".update_response" tmp = response_path.with_suffix(".tmp") - tmp.write_text(answer) + tmp.write_text(answer, encoding="utf-8") tmp.replace(response_path) logger.info("Telegram update prompt answered '%s' by user %s", answer, getattr(query.from_user, "id", "unknown")) diff --git a/plugins/platforms/whatsapp/adapter.py b/plugins/platforms/whatsapp/adapter.py index 5c3d6bbb8237..0710fdc6945d 100644 --- a/plugins/platforms/whatsapp/adapter.py +++ b/plugins/platforms/whatsapp/adapter.py @@ -158,7 +158,7 @@ def _kill_stale_bridge_by_pidfile(session_path: Path) -> None: try: # Format: line 1 = pid, optional line 2 = kernel start time. Legacy # files written before the guard existed have only the pid. - lines = pid_file.read_text().split("\n") + lines = pid_file.read_text(encoding="utf-8").split("\n") pid = int(lines[0].strip()) if len(lines) > 1 and lines[1].strip(): recorded_start = int(lines[1].strip()) @@ -199,7 +199,7 @@ def _write_bridge_pidfile(session_path: Path, pid: int) -> None: from gateway.status import get_process_start_time start = get_process_start_time(pid) text = str(pid) if start is None else "{}\n{}".format(pid, start) - (session_path / "bridge.pid").write_text(text) + (session_path / "bridge.pid").write_text(text, encoding="utf-8") except OSError: pass @@ -481,7 +481,7 @@ async def connect(self) -> bool: _deps_fresh = False if (bridge_dir / "node_modules").exists(): try: - _deps_fresh = (_dep_stamp.read_text().strip() == _pkg_hash) and bool(_pkg_hash) + _deps_fresh = (_dep_stamp.read_text(encoding="utf-8").strip() == _pkg_hash) and bool(_pkg_hash) except OSError: _deps_fresh = False if not _deps_fresh: @@ -507,7 +507,7 @@ async def connect(self) -> bool: print(f"[{self.name}] Dependencies installed") if _pkg_hash: try: - _dep_stamp.write_text(_pkg_hash) + _dep_stamp.write_text(_pkg_hash, encoding="utf-8") except OSError: pass # Stamp is an optimization; install still succeeded except Exception as e: