fix(photon): add home channel env seed and simplify space resolution

This commit is contained in:
underthestars-zhy 2026-06-08 19:27:04 -07:00 committed by Teknium
parent 0d25cae041
commit 3b983e7791
4 changed files with 1841 additions and 42 deletions

View file

@ -133,11 +133,22 @@ def is_connected(cfg: PlatformConfig) -> bool:
def _env_enablement() -> Optional[dict]:
"""Seed PlatformConfig.extra from env so env-only setups appear in status."""
"""Seed PlatformConfig.extra from env so env-only setups appear in status.
The special ``home_channel`` key is handled by the core plugin hook and
becomes a proper ``HomeChannel`` on ``PlatformConfig``.
"""
project_id, project_secret = load_project_credentials()
if not (project_id and project_secret):
return None
return {"project_id": project_id, "project_secret": project_secret}
seed = {"project_id": project_id, "project_secret": project_secret}
home = os.getenv("PHOTON_HOME_CHANNEL", "").strip()
if home:
seed["home_channel"] = {
"chat_id": home,
"name": os.getenv("PHOTON_HOME_CHANNEL_NAME", "Home"),
}
return seed
# ---------------------------------------------------------------------------

View file

@ -354,50 +354,26 @@ async function resolveSpace(spaceId) {
if (phoneTarget && imessage) {
try {
const im = imessage(app);
if (typeof im.user === "function" && typeof im.space === "function") {
const user = await im.user(phoneTarget);
const space = await im.space(user);
rememberKnownSpace(spaceId, space);
rememberKnownSpace(phoneTarget, space);
rememberKnownSpace(space?.id, space);
return space;
}
const user = await im.user(phoneTarget);
const space = await im.space(user);
rememberKnownSpace(spaceId, space);
rememberKnownSpace(phoneTarget, space);
rememberKnownSpace(space?.id, space);
return space;
} catch (e) {
console.error(
"photon-sidecar: phone->DM resolution failed; falling back to " +
"id-based lookup: " +
"photon-sidecar: phone->DM resolution failed: " +
(e && e.stack ? e.stack : String(e))
);
}
}
// spectrum-ts exposes the same Space methods via `app.space(spaceId)` /
// narrowed helpers; we fall back through a few accessor shapes to
// tolerate small SDK API drift.
if (typeof app.space === "function") {
const space = await app.space(spaceId);
rememberKnownSpace(spaceId, space);
rememberKnownSpace(space?.id, space);
return space;
}
if (app.spaces && typeof app.spaces.get === "function") {
const space = await app.spaces.get(spaceId);
rememberKnownSpace(spaceId, space);
rememberKnownSpace(space?.id, space);
return space;
}
if (imessage) {
const im = imessage(app);
if (typeof im.space === "function") {
try {
const space = await im.space({ id: spaceId });
rememberKnownSpace(spaceId, space);
rememberKnownSpace(space?.id, space);
return space;
} catch {
/* fall through */
}
}
}
// No cache hit and not a phone/DM target. spectrum-ts exposes no API to
// rehydrate an arbitrary opaque space id: a Space is only obtained from the
// inbound `[space, message]` stream (cached above in `knownSpaces`) or
// reconstructed for a DM from its phone number. So a group space whose cache
// entry was lost — e.g. after a sidecar restart with no fresh inbound message
// in that group — cannot be resolved here; a new inbound message in the group
// re-warms the cache. DMs are unaffected (reconstructed from the phone).
throw new Error(`unable to resolve space id ${spaceId}`);
}
@ -430,7 +406,7 @@ const server = http.createServer(async (req, res) => {
}
const space = await resolveSpace(spaceId);
const result = await space.send(spectrumText(text));
return ok(res, { messageId: result?.id || result?.messageId || null });
return ok(res, { messageId: result?.id || null });
}
if (req.url === "/send-attachment") {
const { spaceId, path, name, mimeType, caption, kind } =
@ -465,7 +441,7 @@ const server = http.createServer(async (req, res) => {
);
}
}
return ok(res, { messageId: result?.id || result?.messageId || null });
return ok(res, { messageId: result?.id || null });
}
if (req.url === "/typing") {
const { spaceId, state = "start" } = body || {};

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@ from __future__ import annotations
import pytest
from hermes_cli.config import get_env_value, save_env_value
from plugins.platforms.photon.adapter import _env_enablement
from plugins.platforms.photon import cli
@ -36,3 +37,33 @@ def test_autoconfigure_access_preserves_existing_allowlist(
assert get_env_value("PHOTON_ALLOWED_USERS") == "+19998887777,+15551112222"
# The still-unset home channel is filled.
assert get_env_value("PHOTON_HOME_CHANNEL") == "+15551234567"
def test_env_enablement_seeds_home_channel(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PHOTON_PROJECT_ID", "project_123")
monkeypatch.setenv("PHOTON_PROJECT_SECRET", "secret_123")
monkeypatch.setenv("PHOTON_HOME_CHANNEL", "+15551234567")
monkeypatch.setenv("PHOTON_HOME_CHANNEL_NAME", "Primary DM")
seed = _env_enablement()
assert seed is not None
assert seed["home_channel"] == {
"chat_id": "+15551234567",
"name": "Primary DM",
}
def test_env_enablement_home_channel_defaults_name(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("PHOTON_PROJECT_ID", "project_123")
monkeypatch.setenv("PHOTON_PROJECT_SECRET", "secret_123")
monkeypatch.setenv("PHOTON_HOME_CHANNEL", "+15551234567")
monkeypatch.delenv("PHOTON_HOME_CHANNEL_NAME", raising=False)
seed = _env_enablement()
assert seed is not None
assert seed["home_channel"] == {
"chat_id": "+15551234567",
"name": "Home",
}