Skip to content
Open
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
2 changes: 1 addition & 1 deletion agent/copilot_acp_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ def _handle_server_message(
f"Write denied: '{path}' is a protected system/credential file."
)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(str(params.get("content") or ""))
path.write_text(str(params.get("content") or ""), encoding="utf-8")
response = {
"jsonrpc": "2.0",
"id": message_id,
Expand Down
2 changes: 1 addition & 1 deletion gateway/dead_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def _flush_locked(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._dead, indent=2))
tmp.write_text(json.dumps(self._dead, indent=2), encoding="utf-8")
tmp.replace(self._path)
except OSError as exc:
# Best-effort: keep the in-memory state, don't break delivery.
Expand Down
4 changes: 2 additions & 2 deletions gateway/delivery.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ def _deliver_local(
lines.append("")
lines.append(content)

output_path.write_text("\n".join(lines))
output_path.write_text("\n".join(lines), encoding="utf-8")

return {
"path": str(output_path),
Expand All @@ -370,7 +370,7 @@ def _save_full_output(self, content: str, job_id: str) -> Path:
out_dir = get_hermes_home() / "cron" / "output"
out_dir.mkdir(parents=True, exist_ok=True)
path = out_dir / f"{job_id}_{timestamp}.txt"
path.write_text(content)
path.write_text(content, encoding="utf-8")
return path

def _filter_silence_narration_enabled(self) -> bool:
Expand Down
43 changes: 34 additions & 9 deletions gateway/platforms/qqbot/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
app_id: "your-app-id" # or QQ_APP_ID env var
client_secret: "your-secret" # or QQ_CLIENT_SECRET env var
markdown_support: true # enable QQ markdown (msg_type 2)
dm_policy: "open" # open | allowlist | disabled
dm_policy: "pairing" # open | allowlist | disabled | pairing
allow_from: ["openid_1"]
group_policy: "open" # open | allowlist | disabled
group_policy: "pairing" # open | allowlist | disabled | pairing
group_allow_from: ["group_openid_1"]
stt: # Voice-to-text config (optional)
provider: "zai" # zai (GLM-ASR), openai (Whisper), etc.
Expand Down Expand Up @@ -208,11 +208,11 @@ def __init__(self, config: PlatformConfig):
self._markdown_support = bool(extra.get("markdown_support", True))

# Auth/ACL policies
self._dm_policy = str(extra.get("dm_policy", "open")).strip().lower()
self._dm_policy = str(extra.get("dm_policy", "pairing")).strip().lower()
self._allow_from = _coerce_list(
extra.get("allow_from") or extra.get("allowFrom")
)
self._group_policy = str(extra.get("group_policy", "open")).strip().lower()
self._group_policy = str(extra.get("group_policy", "pairing")).strip().lower()
self._group_allow_from = _coerce_list(
extra.get("group_allow_from") or extra.get("groupAllowFrom")
)
Expand Down Expand Up @@ -1193,7 +1193,7 @@ def _write_update_response(answer: str, operator: str = "") -> None:
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(
"QQ update prompt answered %r by %s",
Expand All @@ -1214,7 +1214,7 @@ async def _handle_c2c_message(
user_openid = str(author.get("user_openid", ""))
if not user_openid:
return
if not self._is_dm_allowed(user_openid):
if not self._is_dm_intake_allowed(user_openid):
return

text = content
Expand Down Expand Up @@ -1454,7 +1454,7 @@ async def _handle_dm_message(
# Without this check any member of any guild the bot is in could
# bypass the configured allowlist via direct messages.
author_id = str(author.get("id", ""))
if not self._is_dm_allowed(author_id):
if not self._is_dm_intake_allowed(author_id):
logger.debug(
"[%s] Guild DM blocked by ACL: guild=%s user=%s",
self._log_tag, guild_id, author_id,
Expand Down Expand Up @@ -3142,19 +3142,44 @@ def _strip_at_mention(content: str) -> str:
stripped = re.sub(r"^@\S+\s*", "", content.strip())
return stripped

def _open_dm_opted_in(self) -> bool:
if os.getenv("GATEWAY_ALLOW_ALL_USERS", "").lower() in {"true", "1", "yes"}:
return True
return os.getenv("QQ_ALLOW_ALL_USERS", "").lower() in {"true", "1", "yes"}

def _is_dm_allowed(self, user_id: str) -> bool:
if self._dm_policy == "disabled":
return False
if self._dm_policy == "allowlist":
return self._entry_matches(self._allow_from, user_id)
return True
if self._dm_policy == "open":
return self._open_dm_opted_in()
return False

def _is_dm_intake_allowed(self, user_id: str) -> bool:
principal = str(user_id or "").strip()
if not principal:
return False
if self._dm_policy == "disabled":
return False
if self._dm_policy == "allowlist":
return self._entry_matches(self._allow_from, principal)
if self._dm_policy == "pairing":
return True
if self._dm_policy == "open":
return self._open_dm_opted_in()
return False

def _is_group_allowed(self, group_id: str, user_id: str) -> bool:
if self._group_policy == "disabled":
return False
if self._group_policy == "allowlist":
return self._entry_matches(self._group_allow_from, group_id)
return True
if self._group_policy == "pairing":
return False
if self._group_policy == "open":
return True
return False

@staticmethod
def _entry_matches(entries: List[str], target: str) -> bool:
Expand Down
Loading
Loading