From 92cc7a841c77f2205d6b56c399d150094f7a2e22 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 9 Jan 2026 22:38:12 +0100 Subject: [PATCH] refactor: centralize main session key normalization in apps --- CHANGELOG.md | 1 + .../app/src/main/java/com/clawdbot/android/NodeRuntime.kt | 4 ++-- .../app/src/main/java/com/clawdbot/android/SessionKey.kt | 6 ++++++ .../java/com/clawdbot/android/voice/TalkModeManager.kt | 3 ++- apps/ios/Sources/Model/NodeAppModel.swift | 3 +-- apps/ios/Sources/SessionKey.swift | 8 ++++++++ apps/ios/Sources/Voice/TalkModeManager.swift | 3 +-- 7 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt create mode 100644 apps/ios/Sources/SessionKey.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index edab390ef..ff77bb300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ - Doctor: run legacy state migrations in non-interactive mode without prompts. - Cron: parse Telegram topic targets for isolated delivery. (#478) — thanks @nachoiacovino - Cron: enqueue main-session system events under the resolved main session key. (#510) +- Mobile: centralize main session key normalization for iOS/Android runtime helpers. — thanks @steipete - Outbound: default Telegram account selection for config-only tokens; remove heartbeat-specific accountId handling. (follow-up #516) — thanks @YuriNachos - Cron: allow Telegram delivery targets with topic/thread IDs (e.g. `-100…:topic:123`). (#474) — thanks @mitschabaude-bot - Heartbeat: resolve Telegram account IDs from config-only tokens; cron tool accepts canonical `jobId` and legacy `id` for job actions. (#516) — thanks @YuriNachos diff --git a/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt b/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt index c0c1b7b58..8e753ff95 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/NodeRuntime.kt @@ -700,8 +700,8 @@ class NodeRuntime(context: Context) { val ui = config?.get("ui").asObjectOrNull() val raw = ui?.get("seamColor").asStringOrNull()?.trim() val sessionCfg = config?.get("session").asObjectOrNull() - val rawMainKey = sessionCfg?.get("mainKey").asStringOrNull()?.trim() - _mainSessionKey.value = rawMainKey?.takeIf { it.isNotEmpty() } ?: "main" + val mainKey = normalizeMainKey(sessionCfg?.get("mainKey").asStringOrNull()) + _mainSessionKey.value = mainKey val parsed = parseHexColorArgb(raw) _seamColorArgb.value = parsed ?: DEFAULT_SEAM_COLOR_ARGB diff --git a/apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt b/apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt new file mode 100644 index 000000000..f1897e7b4 --- /dev/null +++ b/apps/android/app/src/main/java/com/clawdbot/android/SessionKey.kt @@ -0,0 +1,6 @@ +package com.clawdbot.android + +internal fun normalizeMainKey(raw: String?): String { + val trimmed = raw?.trim() + return if (!trimmed.isNullOrEmpty()) trimmed else "main" +} diff --git a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt b/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt index c9ccf1e2e..18a4b5965 100644 --- a/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt +++ b/apps/android/app/src/main/java/com/clawdbot/android/voice/TalkModeManager.kt @@ -21,6 +21,7 @@ import android.speech.tts.UtteranceProgressListener import android.util.Log import androidx.core.content.ContextCompat import com.clawdbot.android.bridge.BridgeSession +import com.clawdbot.android.normalizeMainKey import java.net.HttpURLConnection import java.net.URL import java.util.UUID @@ -814,7 +815,7 @@ class TalkModeManager( val config = root?.get("config").asObjectOrNull() val talk = config?.get("talk").asObjectOrNull() val sessionCfg = config?.get("session").asObjectOrNull() - val mainKey = sessionCfg?.get("mainKey").asStringOrNull()?.trim()?.takeIf { it.isNotEmpty() } ?: "main" + val mainKey = normalizeMainKey(sessionCfg?.get("mainKey").asStringOrNull()) val voice = talk?.get("voiceId")?.asStringOrNull()?.trim()?.takeIf { it.isNotEmpty() } val aliases = talk?.get("voiceAliases").asObjectOrNull()?.entries?.mapNotNull { (key, value) -> diff --git a/apps/ios/Sources/Model/NodeAppModel.swift b/apps/ios/Sources/Model/NodeAppModel.swift index 92d173816..080b10f16 100644 --- a/apps/ios/Sources/Model/NodeAppModel.swift +++ b/apps/ios/Sources/Model/NodeAppModel.swift @@ -330,8 +330,7 @@ final class NodeAppModel { let ui = config["ui"] as? [String: Any] let raw = (ui?["seamColor"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" let session = config["session"] as? [String: Any] - let rawMainKey = (session?["mainKey"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - let mainKey = rawMainKey.isEmpty ? "main" : rawMainKey + let mainKey = SessionKey.normalizeMainKey(session?["mainKey"] as? String) await MainActor.run { self.seamColorHex = raw.isEmpty ? nil : raw self.mainSessionKey = mainKey diff --git a/apps/ios/Sources/SessionKey.swift b/apps/ios/Sources/SessionKey.swift new file mode 100644 index 000000000..16a57d5d5 --- /dev/null +++ b/apps/ios/Sources/SessionKey.swift @@ -0,0 +1,8 @@ +import Foundation + +enum SessionKey { + static func normalizeMainKey(_ raw: String?) -> String { + let trimmed = (raw ?? "").trimmingCharacters(in: .whitespacesAndNewlines) + return trimmed.isEmpty ? "main" : trimmed + } +} diff --git a/apps/ios/Sources/Voice/TalkModeManager.swift b/apps/ios/Sources/Voice/TalkModeManager.swift index 21ab74111..ce992290d 100644 --- a/apps/ios/Sources/Voice/TalkModeManager.swift +++ b/apps/ios/Sources/Voice/TalkModeManager.swift @@ -651,8 +651,7 @@ final class TalkModeManager: NSObject { guard let config = json["config"] as? [String: Any] else { return } let talk = config["talk"] as? [String: Any] let session = config["session"] as? [String: Any] - let rawMainKey = (session?["mainKey"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" - self.mainSessionKey = rawMainKey.isEmpty ? "main" : rawMainKey + self.mainSessionKey = SessionKey.normalizeMainKey(session?["mainKey"] as? String) self.defaultVoiceId = (talk?["voiceId"] as? String)?.trimmingCharacters(in: .whitespacesAndNewlines) if let aliases = talk?["voiceAliases"] as? [String: Any] { var resolved: [String: String] = [:]