mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-08 06:54:24 +00:00
iOS: harden share relay and extraction openclaw#19424 thanks @mbelinky
This commit is contained in:
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
||||
- iOS/Talk: add a `Background Listening` toggle that keeps Talk Mode active while the app is backgrounded (off by default for battery safety). Thanks @zeulewan.
|
||||
- iOS/Talk: harden barge-in behavior by disabling interrupt-on-speech when output route is built-in speaker/receiver, reducing false interruptions from local TTS bleed-through. Thanks @zeulewan.
|
||||
- iOS/Talk: add a `Voice Directive Hint` toggle for Talk Mode prompts so users can disable ElevenLabs voice-switching instructions to save tokens when not needed. (#18250) Thanks @zeulewan.
|
||||
- iOS/Share: add an iOS share extension that forwards shared URL/text/image content directly to gateway `agent.request`, with delivery-route fallback and optional receipt acknowledgements. (#19424) Thanks @mbelinky.
|
||||
- Telegram/Agents: add inline button `style` support (`primary|success|danger`) across message tool schema, Telegram action parsing, send pipeline, and runtime prompt guidance. (#18241) Thanks @obviyus.
|
||||
- Telegram: surface user message reactions as system events, with configurable `channels.telegram.reactionNotifications` scope. (#10075) Thanks @Glucksberg.
|
||||
- Mattermost: add emoji reaction actions plus reaction event notifications, including an explicit boolean `remove` flag to avoid accidental removals. (#18608) Thanks @echo931.
|
||||
|
||||
@@ -380,9 +380,6 @@ final class ShareViewController: UIViewController {
|
||||
unknownCount += 1
|
||||
}
|
||||
|
||||
if sharedURL != nil, sharedText != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,26 +400,16 @@ final class ShareViewController: UIViewController {
|
||||
}
|
||||
|
||||
let maxBytes = 5_000_000
|
||||
var data = rawData
|
||||
var mimeType: String
|
||||
var fileExt: String
|
||||
if let image = UIImage(data: rawData), let normalized = self.normalizedJPEGData(from: image, maxBytes: maxBytes) {
|
||||
data = normalized
|
||||
mimeType = "image/jpeg"
|
||||
fileExt = "jpg"
|
||||
} else {
|
||||
let utType = UTType(imageUTI)
|
||||
mimeType = utType?.preferredMIMEType ?? "application/octet-stream"
|
||||
fileExt = utType?.preferredFilenameExtension ?? "bin"
|
||||
if data.count > maxBytes {
|
||||
data = Data(data.prefix(maxBytes))
|
||||
}
|
||||
guard let image = UIImage(data: rawData),
|
||||
let data = self.normalizedJPEGData(from: image, maxBytes: maxBytes)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ShareAttachment(
|
||||
type: "image",
|
||||
mimeType: mimeType,
|
||||
fileName: "shared-image-\(index + 1).\(fileExt)",
|
||||
mimeType: "image/jpeg",
|
||||
fileName: "shared-image-\(index + 1).jpg",
|
||||
content: data.base64EncodedString())
|
||||
}
|
||||
|
||||
@@ -446,7 +433,7 @@ final class ShareViewController: UIViewController {
|
||||
}
|
||||
guard let fallback = image.jpegData(compressionQuality: 0.35) else { return nil }
|
||||
if fallback.count <= maxBytes { return fallback }
|
||||
return Data(fallback.prefix(maxBytes))
|
||||
return nil
|
||||
}
|
||||
|
||||
private func loadURL(from provider: NSItemProvider) async -> URL? {
|
||||
|
||||
@@ -17,20 +17,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>ai.openclaw.ios</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>openclaw</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.2.16</string>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20260216</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import Foundation
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
public struct ShareGatewayRelayConfig: Codable, Sendable, Equatable {
|
||||
public let gatewayURLString: String
|
||||
@@ -29,64 +26,37 @@ public struct ShareGatewayRelayConfig: Codable, Sendable, Equatable {
|
||||
}
|
||||
|
||||
public enum ShareGatewayRelaySettings {
|
||||
private static let relayPasteboardName = "ai.openclaw.share.gatewayRelay"
|
||||
private static let relayPasteboardType = "ai.openclaw.share.gatewayRelay.v1"
|
||||
private static let eventPasteboardName = "ai.openclaw.share.events"
|
||||
private static let lastEventType = "ai.openclaw.share.gatewayRelay.event.v1"
|
||||
private static let suiteName = "group.ai.openclaw.shared"
|
||||
private static let relayConfigKey = "share.gatewayRelay.config.v1"
|
||||
private static let lastEventKey = "share.gatewayRelay.event.v1"
|
||||
|
||||
private static var defaults: UserDefaults {
|
||||
UserDefaults(suiteName: self.suiteName) ?? .standard
|
||||
}
|
||||
|
||||
public static func loadConfig() -> ShareGatewayRelayConfig? {
|
||||
#if canImport(UIKit)
|
||||
guard let pasteboard = UIPasteboard(name: UIPasteboard.Name(self.relayPasteboardName), create: false) else {
|
||||
return nil
|
||||
}
|
||||
guard let data = pasteboard.data(forPasteboardType: self.relayPasteboardType) else { return nil }
|
||||
guard let data = self.defaults.data(forKey: self.relayConfigKey) else { return nil }
|
||||
return try? JSONDecoder().decode(ShareGatewayRelayConfig.self, from: data)
|
||||
#else
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
|
||||
public static func saveConfig(_ config: ShareGatewayRelayConfig) {
|
||||
#if canImport(UIKit)
|
||||
guard let data = try? JSONEncoder().encode(config) else { return }
|
||||
guard let pasteboard = UIPasteboard(name: UIPasteboard.Name(self.relayPasteboardName), create: true) else {
|
||||
return
|
||||
}
|
||||
pasteboard.setData(data, forPasteboardType: self.relayPasteboardType)
|
||||
#endif
|
||||
self.defaults.set(data, forKey: self.relayConfigKey)
|
||||
}
|
||||
|
||||
public static func clearConfig() {
|
||||
#if canImport(UIKit)
|
||||
guard let pasteboard = UIPasteboard(name: UIPasteboard.Name(self.relayPasteboardName), create: false) else {
|
||||
return
|
||||
}
|
||||
pasteboard.items = []
|
||||
#endif
|
||||
self.defaults.removeObject(forKey: self.relayConfigKey)
|
||||
}
|
||||
|
||||
public static func saveLastEvent(_ message: String) {
|
||||
#if canImport(UIKit)
|
||||
let timestamp = ISO8601DateFormatter().string(from: Date())
|
||||
let payload = "[\(timestamp)] \(message)"
|
||||
guard let data = payload.data(using: .utf8) else { return }
|
||||
guard let pasteboard = UIPasteboard(name: UIPasteboard.Name(self.eventPasteboardName), create: true) else {
|
||||
return
|
||||
}
|
||||
pasteboard.setData(data, forPasteboardType: self.lastEventType)
|
||||
#endif
|
||||
self.defaults.set(payload, forKey: self.lastEventKey)
|
||||
}
|
||||
|
||||
public static func loadLastEvent() -> String? {
|
||||
#if canImport(UIKit)
|
||||
guard let pasteboard = UIPasteboard(name: UIPasteboard.Name(self.eventPasteboardName), create: false) else {
|
||||
return nil
|
||||
}
|
||||
guard let data = pasteboard.data(forPasteboardType: self.lastEventType) else { return nil }
|
||||
let value = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
let value = self.defaults.string(forKey: self.lastEventKey)?
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return value.isEmpty ? nil : value
|
||||
#else
|
||||
return nil
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type { NodeEvent, NodeEventContext } from "./server-node-events-types.js";
|
||||
import { resolveSessionAgentId } from "../agents/agent-scope.js";
|
||||
import { normalizeChannelId } from "../channels/plugins/index.js";
|
||||
import { createOutboundSendDeps } from "../cli/outbound-send-deps.js";
|
||||
@@ -15,6 +14,7 @@ import { normalizeMainKey } from "../routing/session-key.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { parseMessageWithAttachments } from "./chat-attachments.js";
|
||||
import { normalizeRpcAttachmentsToChatAttachments } from "./server-methods/attachment-normalize.js";
|
||||
import type { NodeEvent, NodeEventContext } from "./server-node-events-types.js";
|
||||
import {
|
||||
loadSessionEntry,
|
||||
pruneLegacyStoreKeys,
|
||||
|
||||
Reference in New Issue
Block a user