import Foundation import UIKit import Contacts import EventKit import CoreLocation import MediaPlayer import AVFoundation /// 操作结果 public struct Result { let success: Bool let message: String public init(success: Bool, message: String) { self.success = success self.message = message } } /// 系统功能处理器 - 负责调用系统API执行各种功能以及注册系统功能 public class SystemFunctionHandler: NSObject { private let tag = "SystemFunctionHandler" // 位置管理器 private var locationManager: CLLocationManager? private var locationCompletion: ((Result) -> Void)? // 事件存储 private var eventStore: EKEventStore? public override init() { super.init() setupLocationManager() setupEventStore() } /// 设置位置管理器 private func setupLocationManager() { locationManager = CLLocationManager() locationManager?.delegate = self locationManager?.desiredAccuracy = kCLLocationAccuracyBest } /// 设置事件存储 private func setupEventStore() { eventStore = EKEventStore() } /// 在MCPClient中注册所有系统功能 /// - Parameter client: MCPClient实例 public func registerAllFunctions(client: MCPClient) { do { // 注册退出交互函数 registerExitInteractionFunction(client: client) // 注册翻译模式函数 registerTranslationModeFunction(client: client) // 注册发送短信函数 registerSendTextMessageFunction(client: client) // 注册拨打电话函数 registerMakePhoneCallFunction(client: client) // 注册设置提醒函数 registerSetReminderFunction(client: client) // 注册获取当前时间函数 registerGetCurrentTimeFunction(client: client) // 注册获取当前位置函数 registerGetCurrentLocationFunction(client: client) // 注册媒体控制函数 registerMediaPlayFunction(client: client) registerMediaPauseFunction(client: client) registerMediaPreviousFunction(client: client) registerMediaNextFunction(client: client) // 注册打开录音机功能 registerOpenRecorderFunction(client: client) print("\(tag) 所有系统功能注册完成") } catch { print("\(tag) 注册系统功能失败: \(error.localizedDescription)") } } // MARK: - 函数注册 /// 注册退出交互函数 private func registerExitInteractionFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = ExitInteractionHandler() _ = client.registerLocalFunction( name: "exit_interaction", description: "结束当前交互", parameters: parameters, handler: handler ) print("\(tag) 已注册退出交互功能") } /// 注册翻译模式函数 private func registerTranslationModeFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = TranslationModeHandler() _ = client.registerLocalFunction( name: "enter_translation_mode", description: "用户请求进入实时翻译模式时,启动实时翻译功能", parameters: parameters, handler: handler ) print("\(tag) 已注册翻译模式功能") } /// 注册发送短信函数 private func registerSendTextMessageFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [ "contact": [ "type": "string", "description": "联系人姓名或电话号码" ], "message": [ "type": "string", "description": "短信内容" ] ], "required": ["contact", "message"] ] let handler = SendTextMessageHandler() _ = client.registerLocalFunction( name: "send_text_message", description: "发送短信", parameters: parameters, handler: handler ) print("\(tag) 已注册发送短信功能") } /// 注册拨打电话函数 private func registerMakePhoneCallFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [ "contact": [ "type": "string", "description": "联系人姓名或电话号码" ] ], "required": ["contact"] ] let handler = MakePhoneCallHandler() _ = client.registerLocalFunction( name: "make_phone_call", description: "拨打电话", parameters: parameters, handler: handler ) print("\(tag) 已注册拨打电话功能") } /// 注册设置提醒函数 private func registerSetReminderFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [ "title": [ "type": "string", "description": "提醒标题" ], "content": [ "type": "string", "description": "提醒内容" ], "time": [ "type": "string", "description": "提醒时间,格式为'yyyy-MM-dd HH:mm',如'2023-12-31 14:30'" ] ], "required": ["title", "time"] ] let handler = SetReminderHandler(eventStore: eventStore) _ = client.registerLocalFunction( name: "set_reminder", description: "设置提醒事项", parameters: parameters, handler: handler ) print("\(tag) 已注册设置提醒功能") } /// 注册获取当前时间函数 private func registerGetCurrentTimeFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [ "format": [ "type": "string", "description": "时间格式,可选,默认为标准格式" ] ], "required": [] ] let handler = GetCurrentTimeHandler() _ = client.registerLocalFunction( name: "get_current_time", description: "获取当前日期和时间", parameters: parameters, handler: handler ) print("\(tag) 已注册获取当前时间功能") } /// 注册获取当前位置函数 private func registerGetCurrentLocationFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = GetCurrentLocationHandler(systemHandler: self) _ = client.registerLocalFunction( name: "get_current_location", description: "获取当前地理位置", parameters: parameters, handler: handler ) print("\(tag) 已注册获取当前位置功能") } /// 注册媒体播放功能 private func registerMediaPlayFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = MediaPlayHandler() _ = client.registerLocalFunction( name: "media_play", description: "播放媒体", parameters: parameters, handler: handler ) print("\(tag) 已注册媒体播放功能") } /// 注册媒体暂停功能 private func registerMediaPauseFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = MediaPauseHandler() _ = client.registerLocalFunction( name: "media_pause", description: "暂停媒体播放", parameters: parameters, handler: handler ) print("\(tag) 已注册媒体暂停功能") } /// 注册媒体上一首功能 private func registerMediaPreviousFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = MediaPreviousHandler() _ = client.registerLocalFunction( name: "media_previous", description: "播放上一首", parameters: parameters, handler: handler ) print("\(tag) 已注册媒体上一首功能") } /// 注册媒体下一首功能 private func registerMediaNextFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = MediaNextHandler() _ = client.registerLocalFunction( name: "media_next", description: "播放下一首", parameters: parameters, handler: handler ) print("\(tag) 已注册媒体下一首功能") } /// 注册打开录音机功能 private func registerOpenRecorderFunction(client: MCPClient) { let parameters: [String: Any] = [ "type": "object", "properties": [:], "required": [] ] let handler = OpenRecorderHandler() _ = client.registerLocalFunction( name: "open_recorder", description: "打开系统录音机并开始录音", parameters: parameters, handler: handler ) print("\(tag) 已注册打开录音机功能") } // MARK: - 内部方法 /// 获取当前位置 internal func getCurrentLocation() async -> Result { return await withCheckedContinuation { continuation in locationCompletion = { result in continuation.resume(returning: result) } guard let locationManager = locationManager else { continuation.resume(returning: Result(success: false, message: "位置管理器未初始化")) return } // 检查位置权限 let authStatus = locationManager.authorizationStatus switch authStatus { case .notDetermined: locationManager.requestWhenInUseAuthorization() return case .denied, .restricted: continuation.resume(returning: Result(success: false, message: "位置权限被拒绝")) return case .authorizedWhenInUse, .authorizedAlways: break @unknown default: continuation.resume(returning: Result(success: false, message: "未知的位置权限状态")) return } // 检查位置服务是否可用 guard CLLocationManager.locationServicesEnabled() else { continuation.resume(returning: Result(success: false, message: "位置服务未启用")) return } // 请求位置 locationManager.requestLocation() } } } // MARK: - CLLocationManagerDelegate extension SystemFunctionHandler: CLLocationManagerDelegate { public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { guard let location = locations.last else { locationCompletion?(Result(success: false, message: "无法获取位置信息")) return } let latitude = location.coordinate.latitude let longitude = location.coordinate.longitude let message = "当前位置:纬度 \(latitude),经度 \(longitude)" locationCompletion?(Result(success: true, message: message)) locationCompletion = nil } public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { locationCompletion?(Result(success: false, message: "获取位置失败:\(error.localizedDescription)")) locationCompletion = nil } public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { if status == .authorizedWhenInUse || status == .authorizedAlways { manager.requestLocation() } else if status == .denied || status == .restricted { locationCompletion?(Result(success: false, message: "位置权限被拒绝")) locationCompletion = nil } } } // MARK: - 功能处理器实现 /// 退出交互处理器 private class ExitInteractionHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { // 发送退出交互通知 NotificationCenter.default.post(name: NSNotification.Name("ExitInteraction"), object: nil) return "{\"result\": \"已结束当前交互\"}" } } /// 翻译模式处理器 private class TranslationModeHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { // 发送进入翻译模式通知 NotificationCenter.default.post(name: NSNotification.Name("EnterTranslationMode"), object: nil) return "{\"result\": \"已进入翻译模式\"}" } } /// 发送短信处理器 private class SendTextMessageHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { guard let contact = arguments["contact"] as? String, let message = arguments["message"] as? String else { throw OpenAIException("缺少必要参数") } let result = await sendTextMessage(contact: contact, message: message) return "{\"result\": \"\(result.message)\"}" } private func sendTextMessage(contact: String, message: String) async -> Result { // 在iOS中,我们只能打开短信应用,无法直接发送短信 guard let url = URL(string: "sms:\(contact)&body=\(message.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")") else { return Result(success: false, message: "无法创建短信URL") } return await withCheckedContinuation { continuation in DispatchQueue.main.async { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) { success in if success { continuation.resume(returning: Result(success: true, message: "已打开短信应用")) } else { continuation.resume(returning: Result(success: false, message: "打开短信应用失败")) } } } else { continuation.resume(returning: Result(success: false, message: "无法打开短信应用")) } } } } } /// 拨打电话处理器 private class MakePhoneCallHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { guard let contact = arguments["contact"] as? String else { throw OpenAIException("缺少联系人参数") } let result = await makePhoneCall(contact: contact) return "{\"result\": \"\(result.message)\"}" } private func makePhoneCall(contact: String) async -> Result { guard let url = URL(string: "tel:\(contact)") else { return Result(success: false, message: "无法创建电话URL") } return await withCheckedContinuation { continuation in DispatchQueue.main.async { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) { success in if success { continuation.resume(returning: Result(success: true, message: "已发起电话呼叫")) } else { continuation.resume(returning: Result(success: false, message: "发起电话呼叫失败")) } } } else { continuation.resume(returning: Result(success: false, message: "无法拨打电话")) } } } } } /// 设置提醒处理器 private class SetReminderHandler: FunctionHandler { private let eventStore: EKEventStore? init(eventStore: EKEventStore?) { self.eventStore = eventStore } func handle(arguments: [String: Any]) async throws -> String { guard let title = arguments["title"] as? String, let timeString = arguments["time"] as? String else { throw OpenAIException("缺少必要参数") } let content = arguments["content"] as? String ?? "" let result = await setReminder(title: title, content: content, time: timeString) return "{\"result\": \"\(result.message)\"}" } private func setReminder(title: String, content: String, time: String) async -> Result { guard let eventStore = eventStore else { return Result(success: false, message: "事件存储未初始化") } // 请求日历权限 let status = await requestCalendarAccess() guard status == .authorized else { return Result(success: false, message: "日历权限被拒绝") } // 解析时间 let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm" guard let date = formatter.date(from: time) else { return Result(success: false, message: "时间格式错误") } // 创建提醒 let reminder = EKReminder(eventStore: eventStore) reminder.title = title reminder.notes = content reminder.calendar = eventStore.defaultCalendarForNewReminders() let alarm = EKAlarm(absoluteDate: date) reminder.addAlarm(alarm) do { try eventStore.save(reminder, commit: true) return Result(success: true, message: "提醒设置成功") } catch { return Result(success: false, message: "设置提醒失败:\(error.localizedDescription)") } } private func requestCalendarAccess() async -> EKAuthorizationStatus { return await withCheckedContinuation { continuation in eventStore?.requestAccess(to: .reminder) { granted, error in let status = EKEventStore.authorizationStatus(for: .reminder) continuation.resume(returning: status) } } } } /// 获取当前时间处理器 private class GetCurrentTimeHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let format = arguments["format"] as? String let result = getCurrentTime(format: format) return "{\"result\": \"\(result.message)\", \"time\": \"\(result.message)\"}" } private func getCurrentTime(format: String?) -> Result { let formatter = DateFormatter() if let format = format, !format.isEmpty { formatter.dateFormat = format } else { formatter.dateStyle = .full formatter.timeStyle = .full formatter.locale = Locale(identifier: "zh_CN") } let currentTime = formatter.string(from: Date()) return Result(success: true, message: currentTime) } } /// 获取当前位置处理器 private class GetCurrentLocationHandler: FunctionHandler { private weak var systemHandler: SystemFunctionHandler? init(systemHandler: SystemFunctionHandler) { self.systemHandler = systemHandler } func handle(arguments: [String: Any]) async throws -> String { guard let systemHandler = systemHandler else { throw OpenAIException("系统处理器未初始化") } let result = await systemHandler.getCurrentLocation() return "{\"result\": \"\(result.message)\", \"success\": \(result.success)}" } } /// 媒体播放处理器 private class MediaPlayHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let result = controlMedia(action: "play") return "{\"result\": \"\(result.message)\"}" } private func controlMedia(action: String) -> Result { let commandCenter = MPRemoteCommandCenter.shared() switch action { case "play": // 发送播放命令通知 NotificationCenter.default.post(name: NSNotification.Name("MediaPlay"), object: nil) return Result(success: true, message: "已开始播放媒体") default: return Result(success: false, message: "未知的媒体操作") } } } /// 媒体暂停处理器 private class MediaPauseHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let result = controlMedia(action: "pause") return "{\"result\": \"\(result.message)\"}" } private func controlMedia(action: String) -> Result { switch action { case "pause": // 发送暂停命令通知 NotificationCenter.default.post(name: NSNotification.Name("MediaPause"), object: nil) return Result(success: true, message: "已暂停媒体播放") default: return Result(success: false, message: "未知的媒体操作") } } } /// 媒体上一首处理器 private class MediaPreviousHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let result = controlMedia(action: "previous") return "{\"result\": \"\(result.message)\"}" } private func controlMedia(action: String) -> Result { switch action { case "previous": // 发送上一首命令通知 NotificationCenter.default.post(name: NSNotification.Name("MediaPrevious"), object: nil) return Result(success: true, message: "已切换到上一首") default: return Result(success: false, message: "未知的媒体操作") } } } /// 媒体下一首处理器 private class MediaNextHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let result = controlMedia(action: "next") return "{\"result\": \"\(result.message)\"}" } private func controlMedia(action: String) -> Result { switch action { case "next": // 发送下一首命令通知 NotificationCenter.default.post(name: NSNotification.Name("MediaNext"), object: nil) return Result(success: true, message: "已切换到下一首") default: return Result(success: false, message: "未知的媒体操作") } } } /// 打开录音机处理器 private class OpenRecorderHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { let result = openRecorder() return "{\"result\": \"\(result.message)\"}" } private func openRecorder() -> Result { // 尝试打开语音备忘录应用 if let url = URL(string: "voicememos://") { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url) return Result(success: true, message: "已打开语音备忘录") } } return Result(success: false, message: "无法打开录音机应用") } }