Browse Source
feat: add Bot/Hermes input commands (/steer, /queue, /cancel) with real-time streaming and multiline formatting
main
feat: add Bot/Hermes input commands (/steer, /queue, /cancel) with real-time streaming and multiline formatting
main
4 changed files with 251 additions and 23 deletions
-
83src/BotCommandProcessor.swift
-
160src/InputBoxManager.swift
-
27src/TextProcessor.swift
-
4src/VoiceCommandExecutor.swift
@ -0,0 +1,83 @@ |
|||||
|
import Foundation |
||||
|
|
||||
|
public struct BotCommandResult { |
||||
|
public let command: String // "/steer", "/queue", "/cancel" |
||||
|
public let body: String |
||||
|
|
||||
|
public var formattedText: String { |
||||
|
let cleanBody = body.trimmingCharacters(in: .whitespacesAndNewlines) |
||||
|
if cleanBody.isEmpty { |
||||
|
return command |
||||
|
} |
||||
|
return "\(command)\n\(cleanBody)" |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public final class BotCommandProcessor { |
||||
|
private static let leadingPunct = CharacterSet(charactersIn: " .,،؛:!؟?\\\"'«»()[]{}—-_") |
||||
|
|
||||
|
// Pattern 1: Steer (/steer) |
||||
|
// Matches: "اسلش اس", "اسلش استیر", "اسلش steer", "slash s", "slash steer", "/s", "/steer" |
||||
|
private static let steerRegex: NSRegularExpression = { |
||||
|
let p = "^(?:(?:اسلش|اصلش|slash|\\/)\\s*(?:استیر|steer|اس|s)|\\/(?:steer|s))(?=[.,،؛!؟?:\\s]|$)[.,،؛!؟?:\\s]*(.*)$" |
||||
|
return try! NSRegularExpression(pattern: p, options: [.caseInsensitive, .dotMatchesLineSeparators]) |
||||
|
}() |
||||
|
|
||||
|
// Pattern 2: Queue (/queue) |
||||
|
// Matches: "اسلش کیو", "اسلش کوئری", "اسلش queue", "slash q", "slash queue", "/q", "/queue" |
||||
|
private static let queueRegex: NSRegularExpression = { |
||||
|
let p = "^(?:(?:اسلش|اصلش|slash|\\/)\\s*(?:کوئری|کیو|queue|q)|\\/(?:queue|q))(?=[.,،؛!؟?:\\s]|$)[.,،؛!؟?:\\s]*(.*)$" |
||||
|
return try! NSRegularExpression(pattern: p, options: [.caseInsensitive, .dotMatchesLineSeparators]) |
||||
|
}() |
||||
|
|
||||
|
// Pattern 3: Cancel (/cancel) |
||||
|
// Matches: "اسلش سی", "اسلش کنسل", "اسلش کنسله", "slash c", "slash cancel", "/c", "/cancel" |
||||
|
private static let cancelRegex: NSRegularExpression = { |
||||
|
let p = "^(?:(?:اسلش|اصلش|slash|\\/)\\s*(?:کنسله|کنسل|cancel|سی|c)|\\/(?:cancel|c))(?=[.,،؛!؟?:\\s]|$)[.,،؛!؟?:\\s]*(.*)$" |
||||
|
return try! NSRegularExpression(pattern: p, options: [.caseInsensitive, .dotMatchesLineSeparators]) |
||||
|
}() |
||||
|
|
||||
|
public static func parse(text: String) -> BotCommandResult? { |
||||
|
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines) |
||||
|
guard !trimmed.isEmpty else { return nil } |
||||
|
|
||||
|
let range = NSRange(location: 0, length: (trimmed as NSString).length) |
||||
|
|
||||
|
// 1. Check Steer |
||||
|
if let match = steerRegex.firstMatch(in: trimmed, options: [], range: range) { |
||||
|
let body = extractGroup(from: trimmed, match: match, groupIndex: 1) |
||||
|
return BotCommandResult(command: "/steer", body: body) |
||||
|
} |
||||
|
|
||||
|
// 2. Check Queue |
||||
|
if let match = queueRegex.firstMatch(in: trimmed, options: [], range: range) { |
||||
|
let body = extractGroup(from: trimmed, match: match, groupIndex: 1) |
||||
|
return BotCommandResult(command: "/queue", body: body) |
||||
|
} |
||||
|
|
||||
|
// 3. Check Cancel |
||||
|
if let match = cancelRegex.firstMatch(in: trimmed, options: [], range: range) { |
||||
|
let body = extractGroup(from: trimmed, match: match, groupIndex: 1) |
||||
|
return BotCommandResult(command: "/cancel", body: body) |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
private static func extractGroup(from str: String, match: NSTextCheckingResult, groupIndex: Int) -> String { |
||||
|
guard groupIndex < match.numberOfRanges else { return "" } |
||||
|
let r = match.range(at: groupIndex) |
||||
|
guard r.location != NSNotFound else { return "" } |
||||
|
let rawGroup = (str as NSString).substring(with: r) |
||||
|
return rawGroup.trimmingCharacters(in: leadingPunct) |
||||
|
} |
||||
|
|
||||
|
public static func isBotCommand(text: String) -> Bool { |
||||
|
return parse(text: text) != nil |
||||
|
} |
||||
|
|
||||
|
public static func formatIfBotCommand(text: String) -> String? { |
||||
|
guard let res = parse(text: text) else { return nil } |
||||
|
return res.formattedText |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue