port overlort python ->Swift assed edition!

fukurou

the supreme coder
ADMIN
Swift:
class AlgorithmV2 {
    private var priority: Int
    private var alg: Any

    init(priority: Int, alg: Any) {
        self.priority = priority
        self.alg = alg
    }

    func getPriority() -> Int {
        return priority
    }

    func setPriority(_ priority: Int) {
        self.priority = priority
    }

    func getAlg() -> Any {
        return alg
    }

    func setAlg(_ alg: Any) {
        self.alg = alg
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class SkillHubAlgDispenser {
    private var _skills: [Skill] = []
    private var _activeSkill: Int = 0
    private var _tempN: Neuron = Neuron()
    private var _kokoro: Kokoro = Kokoro(AbsDictionaryDB())

    init(_ skillsParams: Skill...) {
        for skill in skillsParams {
            skill.setKokoro(_kokoro)
            _skills.append(skill)
        }
    }

    func setKokoro(_ kokoro: Kokoro) {
        _kokoro = kokoro
        for skill in _skills {
            skill.setKokoro(kokoro)
        }
    }

    @discardableResult
    func addSkill(_ skill: Skill) -> SkillHubAlgDispenser {
        skill.setKokoro(_kokoro)
        _skills.append(skill)
        return self
    }

    func dispenseAlgorithm(_ ear: String, _ skin: String, _ eye: String) -> AlgorithmV2? {
        guard !_skills.isEmpty else { return nil }
        _skills[_activeSkill].input(ear, skin, eye)
        _skills[_activeSkill].output(_tempN)
        for i in 1...5 {
            if let alg = _tempN.getAlg(i) {
                return AlgorithmV2(priority: i, alg: alg)
            }
        }
        return nil
    }

    func randomizeActiveSkill() {
        guard !_skills.isEmpty else { return }
        _activeSkill = Int.random(in: 0..<_skills.count)
    }

    func setActiveSkillWithMood(_ mood: Int) {
        if mood >= 0 && mood < _skills.count {
            _activeSkill = mood
        }
    }

    func cycleActiveSkill() {
        guard !_skills.isEmpty else { return }
        _activeSkill = (_activeSkill + 1) % _skills.count
    }

    func getSize() -> Int {
        return _skills.count
    }

    func activeSkillRef() -> Skill? {
        guard !_skills.isEmpty else { return nil }
        return _skills[_activeSkill]
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class UniqueRandomGenerator {
    private var n1: Int
    private var numbers: [Int] = []
    private var remainingNumbers: [Int] = []

    init(_ n1: Int) {
        self.n1 = n1
        self.numbers = Array(0..<n1)
        self.reset()
    }

    func reset() {
        remainingNumbers = numbers
        remainingNumbers.shuffle()
    }

    func getUniqueRandom() -> Int {
        if remainingNumbers.isEmpty {
            reset()
        }
        return remainingNumbers.removeLast()
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class UniqueResponder {
    private var responses: [String] = []
    private var urg: UniqueRandomGenerator

    init(_ replies: String...) {
        self.responses = replies
        self.urg = UniqueRandomGenerator(replies.count)
    }

    func getAResponse() -> String {
        guard !responses.isEmpty else { return "" }
        return responses[urg.getUniqueRandom()]
    }

    func responsesContainsStr(_ item: String) -> Bool {
        return responses.contains(item)
    }

    func strContainsResponse(_ item: String) -> Bool {
        for response in responses {
            if response.isEmpty { continue }
            if item.contains(response) {
                return true
            }
        }
        return false
    }

    func addResponse(_ s1: String) {
        if !responses.contains(s1) {
            responses.append(s1)
            urg = UniqueRandomGenerator(responses.count)
        }
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class AXSkillBundle {
    private var skills: [Skill] = []
    private var tempN = Neuron()
    private var kokoro = Kokoro(AbsDictionaryDB())

    init(_ skillsParams: Skill...) {
        for skill in skillsParams {
            skill.setKokoro(kokoro)
            skills.append(skill)
        }
    }

    func setKokoro(_ newKokoro: Kokoro) {
        kokoro = newKokoro
        for skill in skills {
            skill.setKokoro(kokoro)
        }
    }

    @discardableResult
    func addSkill(_ skill: Skill) -> AXSkillBundle {
        skill.setKokoro(kokoro)
        skills.append(skill)
        return self
    }

    func dispenseAlgorithm(_ ear: String, _ skin: String, _ eye: String) -> AlgorithmV2? {
        for skill in skills {
            skill.input(ear, skin, eye)
            skill.output(tempN)
            for j in 1...5 {
                if let alg = tempN.getAlg(j) {
                    return AlgorithmV2(priority: j, alg: alg)
                }
            }
        }
        return nil
    }

    func getSize() -> Int {
        return skills.count
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class AXGamification {
    private var _counter: Int = 0
    private var _max: Int = 0

    func getCounter() -> Int {
        return _counter
    }

    func getMax() -> Int {
        return _max
    }

    func resetCount() {
        _counter = 0
    }

    func resetAll() {
        _counter = 0
        _max = 0
    }

    func increment() {
        _counter += 1
        if _counter > _max {
            _max = _counter
        }
    }

    func incrementBy(_ n: Int) {
        _counter += n
        if _counter > _max {
            _max = _counter
        }
    }

    func reward(_ cost: Int) -> Bool {
        if cost < _counter {
            _counter -= cost
            return true
        }
        return false
    }

    func surplus(_ cost: Int) -> Bool {
        return cost < _counter
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class Responder {
    private var responses: [String] = []

    init(_ replies: String...) {
        self.responses = replies
    }

    func getAResponse() -> String {
        guard !responses.isEmpty else { return "" }
        return responses[Int.random(in: 0..<responses.count)]
    }

    func responsesContainsStr(_ item: String) -> Bool {
        return responses.contains(item)
    }

    func strContainsResponse(_ item: String) -> Bool {
        for response in responses {
            if response.isEmpty { continue }
            if item.contains(response) {
                return true
            }
        }
        return false
    }

    func addResponse(_ s1: String) {
        responses.append(s1)
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
// ╔════════════════════════════════════════════════════════════════════════╗
// ║                           SPEECH ENGINES                               ║
// ╚════════════════════════════════════════════════════════════════════════╝
 

fukurou

the supreme coder
ADMIN
Swift:
class ChatBot {
    private var sentences = RefreshQ(limit: 5)
    private var wordToList: [String: RefreshQ] = [:]
    private var allParamRef: [String: String] = [:]
    private var paramLim: Int = 5
    private var loggedParams = RefreshQ(limit: 5)
    private var conjuration = "is a"

    init(logParamLim: Int) {
        loggedParams.setLimit(logParamLim)
    }

    func setConjuration(_ conjuration: String) {
        self.conjuration = conjuration
    }

    func setSentencesLim(_ lim: Int) {
        sentences.setLimit(lim)
    }

    func setParamLim(_ paramLim: Int) {
        self.paramLim = paramLim
    }

    func getWordToList() -> [String: RefreshQ] {
        return wordToList
    }

    func talk() -> String {
        let result = sentences.getRNDElement()
        return clearRecursion(result)
    }

    private func clearRecursion(_ result: String) -> String {
        var result = result
        let params = RegexUtil.extractAllRegexes("(\\w+)(?= #)", result)
        for strI in params {
            if let temp = wordToList[strI] {
                let s1 = temp.getRNDElement()
                result = result.replacingOccurrences(of: "\(strI) #", with: s1)
            }
        }
        return result.contains("#") ? clearRecursion(result) : result
    }

    func addParam(_ category: String, _ value: String) {
        if wordToList[category] == nil {
            wordToList[category] = RefreshQ(limit: paramLim)
        }
        wordToList[category]?.insert(value)
        allParamRef[value] = category
    }

    func addKeyValueParam(_ kv: AXKeyValuePair) {
        if wordToList[kv.getKey()] == nil {
            wordToList[kv.getKey()] = RefreshQ(limit: paramLim)
        }
        wordToList[kv.getKey()]?.insert(kv.getValue())
        allParamRef[kv.getValue()] = kv.getKey()
    }

    func addSubject(_ category: String, _ value: String) {
        if wordToList[category] == nil {
            wordToList[category] = RefreshQ(limit: 1)
        }
        wordToList[category]?.insert(value)
        allParamRef[value] = category
    }

    func addSentence(_ sentence: String) {
        sentences.insert(sentence)
    }

    func learn(_ s1: String) {
        var line = " " + s1
        for key in wordToList.keys {
            line = line.replacingOccurrences(of: " \(key)", with: " \(key) #")
        }
        sentences.insert(line.trimmingCharacters(in: .whitespaces))
    }

    func learnV2(_ s1: String) -> Bool {
        let original = s1
        var line = " " + s1
        for key in allParamRef.keys {
            if let cat = allParamRef[key] {
                line = line.replacingOccurrences(of: " \(key)", with: " \(cat) #")
            }
        }
        line = line.trimmingCharacters(in: .whitespaces)
        if line != original {
            sentences.insert(line)
            return true
        }
        return false
    }

    func learnParam(_ s1: String) {
        guard s1.contains(conjuration) else { return }
        let category = RegexUtil.afterWord(conjuration, s1)
        guard let list = wordToList[category] else { return }
        let param = s1.replacingOccurrences(of: "\(conjuration) \(category)", with: "").trimmingCharacters(in: .whitespaces)
        list.insert(param)
        allParamRef[param] = category
        loggedParams.insert(s1)
    }

    func addParamFromAXPrompt(_ kv: AXKeyValuePair) {
        guard wordToList[kv.getKey()] != nil else { return }
        wordToList[kv.getKey()]?.insert(kv.getValue())
        allParamRef[kv.getValue()] = kv.getKey()
    }

    func addRefreshQ(_ category: String, _ q1: RefreshQ) {
        wordToList[category] = q1
    }

    func getALoggedParam() -> String {
        return loggedParams.getRNDElement()
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class PhraseMatcher {
    private let matcher: NSRegularExpression
    private let responses: [AXKeyValuePair]

    init(_ pattern: String, _ responses: [AXKeyValuePair]) {
        self.matcher = try! NSRegularExpression(pattern: pattern, options: [])
        self.responses = responses
    }

    func matches(_ str1: String) -> Bool {
        let range = NSRange(location: 0, length: str1.utf16.count)
        return matcher.firstMatch(in: str1, options: [], range: range) != nil
    }

    func respond(_ str1: String) -> [AXKeyValuePair] {
        let nsStr = str1 as NSString
        let match = matcher.firstMatch(in: str1, options: [], range: NSRange(location: 0, length: nsStr.length))
        var result: [AXKeyValuePair] = []

        if let m = match {
            let groupCount = m.numberOfRanges - 1
            for kv in responses {
                var key = kv.key
                var value = kv.value
                for i in 0..<groupCount {
                    let group = nsStr.substring(with: m.range(at: i + 1))
                    key = key.replacingOccurrences(of: "{\(i)}", with: group.lowercased())
                    value = value.replacingOccurrences(of: "{\(i)}", with: group.lowercased())
                }
                result.append(AXKeyValuePair(key: key, value: value))
            }
        }
        return result
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class ElizaDeducer {
    private var babble2: [PhraseMatcher] = []
    private var patternIndex: [String: [PhraseMatcher]] = [:]
    private var responseCache: [String: [AXKeyValuePair]] = [:]
    private var ec2: EventChatV2

    init(_ limit: Int) {
        ec2 = EventChatV2(limit)
    }

    func getEC2() -> EventChatV2 {
        return ec2
    }

    func learn(_ msg: String) {
        if let cached = responseCache[msg] {
            ec2.addKeyValues(cached)
        }

        for matcher in getPotentialMatchers(msg) {
            if matcher.matches(msg) {
                let response = matcher.respond(msg)
                responseCache[msg] = response
                ec2.addKeyValues(response)
            }
        }
    }

    func learnedBool(_ msg: String) -> Bool {
        var learned = false
        if let cached = responseCache[msg] {
            ec2.addKeyValues(cached)
            learned = true
        }

        for matcher in getPotentialMatchers(msg) {
            if matcher.matches(msg) {
                let response = matcher.respond(msg)
                responseCache[msg] = response
                ec2.addKeyValues(response)
                learned = true
            }
        }
        return learned
    }

    func respond(_ str1: String) -> String {
        return ec2.response(str1)
    }

    func respondLatest(_ str1: String) -> String {
        return ec2.responseLatest(str1)
    }

    func addPhraseMatcher(_ pattern: String, _ kvPairs: String...) {
        var kvs: [AXKeyValuePair] = []
        for i in stride(from: 0, to: kvPairs.count, by: 2) {
            kvs.append(AXKeyValuePair(key: kvPairs[i], value: kvPairs[i + 1]))
        }
        let matcher = PhraseMatcher(pattern, kvs)
        babble2.append(matcher)
        indexPattern(pattern, matcher)
    }

    private func getPotentialMatchers(_ msg: String) -> [PhraseMatcher] {
        var matchers: [PhraseMatcher] = []
        for key in patternIndex.keys where msg.contains(key) {
            matchers.append(contentsOf: patternIndex[key] ?? [])
        }
        return matchers
    }

    private func indexPattern(_ pattern: String, _ matcher: PhraseMatcher) {
        for word in pattern.split(separator: " ") {
            let key = String(word)
            patternIndex[key, default: []].append(matcher)
        }
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class ElizaDeducerInitializer: ElizaDeducer {
    override init(_ lim: Int) {
        super.init(lim)
        initializeBabble2()
    }

    private func initializeBabble2() {
        addPhraseMatcher(
            pattern: "(.*) is (.*)",
            "what is {0}", "{0} is {1}",
            "explain {0}", "{0} is {1}"
        )

        addPhraseMatcher(
            pattern: "if (.*) or (.*) than (.*)",
            "{0}", "{2}",
            "{1}", "{2}"
        )

        addPhraseMatcher(
            pattern: "if (.*) and (.*) than (.*)",
            "{0}", "{1}"
        )

        addPhraseMatcher(
            pattern: "(.*) because (.*)",
            "{1}", "i guess {0}"
        )
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
/// This function wrapper class adds save/load functionality to the ElizaDeducer object.
///
/// Usage:
/// let ed = ElizaDeducerInitializer(2)
/// ed.getEC2().addFromDB("test", "one_two_three")  // Manual load for testing
/// let kokoro = Kokoro(AbsDictionaryDB())          // Use skill's kokoro attribute
/// let ew = ElizaDBWrapper()
/// print(ew.respond("test", ed.getEC2(), kokoro))  // Load and respond
/// print(ew.respond("test", ed.getEC2(), kokoro))  // Uses cached version
/// ed.learn("a is b")                              // Learning happens post-response
/// ElizaDBWrapper.sleepNSave(ed.getEC2(), kokoro)  // Save when bot is idle
class ElizaDBWrapper {
    private var modifiedKeys: Set<String> = []

    /// Responds to input, loads from DB only once per input key
    func respond(_ input: String, _ ec: EventChatV2, _ kokoro: Kokoro) -> String {
        if modifiedKeys.contains(input) {
            return ec.response(input)
        }
        modifiedKeys.insert(input)
        ec.addFromDB(input, kokoro.grimoireMemento.load(input))
        return ec.response(input)
    }

    /// Responds with the most recent reply, loads from DB only once
    func respondLatest(_ input: String, _ ec: EventChatV2, _ kokoro: Kokoro) -> String {
        if modifiedKeys.contains(input) {
            return ec.responseLatest(input)
        }
        modifiedKeys.insert(input)
        ec.addFromDB(input, kokoro.grimoireMemento.load(input))
        return ec.responseLatest(input)
    }

    /// Use this to save once the bot is idle/asleep—not during every response
    static func sleepNSave(_ ecv2: EventChatV2, _ kokoro: Kokoro) {
        for key in ecv2.getModifiedKeys() {
            let saveStr = ecv2.getSaveStr(key)
            kokoro.grimoireMemento.save(key, saveStr)
        }
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class RailBot {
    private var ec = EventChatV2(5)
    private var context: String = "stand by"
    private var elizaWrapper: ElizaDBWrapper? = nil

    init(limit: Int = 5) {
        self.ec = EventChatV2(limit)
    }

    /// Enables database features. Must be called before any save/load operations.
    func enableDBWrapper() {
        if elizaWrapper == nil {
            elizaWrapper = ElizaDBWrapper()
        }
    }

    /// Disables database features.
    func disableDBWrapper() {
        elizaWrapper = nil
    }

    /// Sets the current context.
    func setContext(_ newContext: String?) {
        guard let newContext = newContext, !newContext.isEmpty else { return }
        context = newContext
    }

    /// Private helper for monolog response.
    private func respondMonolog(_ ear: String) -> String {
        guard !ear.isEmpty else { return "" }
        let temp = ec.response(ear)
        if !temp.isEmpty {
            context = temp
        }
        return temp
    }

    /// Learns a new response for the current context.
    func learn(_ ear: String) {
        guard !ear.isEmpty, ear != context else { return }
        ec.addKeyValue(context, ear)
        context = ear
    }

    /// Returns a monolog based on the current context.
    func monolog() -> String {
        return respondMonolog(context)
    }

    /// Responds to a dialog input.
    func respondDialog(_ ear: String) -> String {
        return ec.response(ear)
    }

    /// Responds to the latest input.
    func respondLatest(_ ear: String) -> String {
        return ec.responseLatest(ear)
    }

    /// Adds a new key-value pair to the memory.
    func learnKeyValue(_ context: String, _ reply: String) {
        ec.addKeyValue(context, reply)
    }

    /// Feeds a list of key-value pairs into the memory.
    func feedKeyValuePairs(_ kvList: [AXKeyValuePair]) {
        for kv in kvList {
            learnKeyValue(kv.getKey(), kv.getValue())
        }
    }

    /// Saves learned data using the provided Kokoro instance.
    func saveLearnedData(_ kokoro: Kokoro) {
        elizaWrapper?.sleepNSave(ec, kokoro)
    }

    /// Private helper for loadable monolog mechanics.
    private func loadableMonologMechanics(_ ear: String, _ kokoro: Kokoro) -> String {
        guard let eliza = elizaWrapper else { return "" }
        let temp = eliza.respond(ear, ec, kokoro)
        if !temp.isEmpty {
            context = temp
        }
        return temp
    }

    /// Returns a loadable monolog based on the current context.
    func loadableMonolog(_ kokoro: Kokoro) -> String {
        guard elizaWrapper != nil else { return monolog() }
        return loadableMonologMechanics(context, kokoro)
    }

    /// Returns a loadable dialog response.
    func loadableDialog(_ ear: String, _ kokoro: Kokoro) -> String {
        guard let eliza = elizaWrapper else { return respondDialog(ear) }
        return eliza.respond(ear, ec, kokoro)
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
/// Funnels input strings to a unique response bundle (via UniqueResponder)
class EventChat {
    private var dic: [String: UniqueResponder] = [:]

    /// Initialize with a responder and its associated keys
    init(_ ur: UniqueResponder, _ args: String...) {
        for key in args {
            dic[key] = ur
        }
    }

    /// Add a responder to multiple keys
    func addItems(_ ur: UniqueResponder, _ args: String...) {
        for key in args {
            dic[key] = ur
        }
    }

    /// Add or extend a key-response mapping
    func addKeyValue(_ key: String, _ value: String) {
        if let existingUR = dic[key] {
            existingUR.addResponse(value)
        } else {
            dic[key] = UniqueResponder(value)
        }
    }

    /// Get a randomized response associated with the given input
    func response(_ input: String) -> String {
        return dic[input]?.getAResponse() ?? ""
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
/// AXFunnelResponder maps string keys to `Responder` objects
/// and allows various forms of safe or fallback retrieval.
class AXFunnelResponder {
    private var dic: [String: Responder] = [:]

    /// Add a key-responder pair
    func addKV(_ key: String, _ value: Responder) {
        dic[key] = value
    }

    /// Add a key-responder pair and return self (builder pattern)
    @discardableResult
    func addKVBuilderPattern(_ key: String, _ value: Responder) -> AXFunnelResponder {
        dic[key] = value
        return self
    }

    /// Funnel returns responder output for key, or default = key
    func funnel(_ key: String) -> String {
        return dic[key]?.getAResponse() ?? key
    }

    /// Funnel returns responder output or default = ""
    func funnelOrNothing(_ key: String) -> String {
        return dic[key]?.getAResponse() ?? ""
    }

    /// Funnel returns responder output or nil
    func funnelWalrusOperator(_ key: String) -> String? {
        return dic[key]?.getAResponse()
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
/// Simulates a parrot chirp trigger mechanism.
/// This parrot chirps if it's been quiet for a while—unless you told it to hush.
/// It goes quiet at night, just like polite birds do.
class TrgParrot {
    private var tolerance: TrgTolerance
    private var silencer: Responder

    init(limit: Int) {
        // Default tolerance is 3 unless a positive limit is given
        let tempLim = limit > 0 ? limit : 3
        self.tolerance = TrgTolerance(tempLim)
        self.silencer = Responder("ok", "okay", "stop", "shut up", "quiet")
    }

    /// Relies on a standby signal (no recent interaction) and a recent input.
    /// If no recent input and in standby → chirp.
    /// If input includes silencing command → no chirp.
    /// If awake and active → chirps a few times before quieting.
    func trigger(standBy: Bool, ear: String) -> Bool {
        // Is it night? I will be quiet. 💤
        if TimeUtils.isNight() {
            return false
        }
        // You want the bird to shut up?
        if silencer.responsesContainsStr(ear) {
            tolerance.disable()
            return false
        }
        // No input/output for a while?
        if standBy {
            // I will chirp!
            tolerance.reset()
            return true
        }
        // Are we handshaking?
        if !ear.isEmpty {
            // I will reply-chirp until it grows old for me
            if tolerance.trigger() {
                return true
            }
        }
        return false
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
// ╔════════════════════════════════════════════════════════════════════════╗
// ║                        OUTPUT MANAGEMENT                               ║
// ╚════════════════════════════════════════════════════════════════════════╝
 

fukurou

the supreme coder
ADMIN
Swift:
class LimUniqueResponder {
    private var responses: [String] = []
    private var lim: Int
    private var urg: UniqueRandomGenerator

    init(_ lim: Int) {
        self.lim = lim
        self.urg = UniqueRandomGenerator(0)
    }

    func getAResponse() -> String {
        guard !responses.isEmpty else { return "" }
        return responses[urg.getUniqueRandom()]
    }

    func responsesContainsStr(_ item: String) -> Bool {
        return responses.contains(item)
    }

    func strContainsResponse(_ item: String) -> Bool {
        return responses.contains { !$0.isEmpty && item.contains($0) }
    }

    func addResponse(_ s1: String) {
        if let index = responses.firstIndex(of: s1) {
            responses.remove(at: index)
            responses.append(s1)
            return
        }
        if responses.count > lim - 1 {
            responses.removeFirst()
        } else {
            urg = UniqueRandomGenerator(responses.count + 1)
        }
        responses.append(s1)
    }

    func addResponses(_ replies: String...) {
        for reply in replies {
            addResponse(reply)
        }
    }

    func getSavableStr() -> String {
        return responses.joined(separator: "_")
    }

    func getLastItem() -> String {
        return responses.last ?? ""
    }

    func clone() -> LimUniqueResponder {
        let clone = LimUniqueResponder(self.lim)
        clone.responses = self.responses
        clone.urg = UniqueRandomGenerator(clone.responses.count)
        return clone
    }
}
 

fukurou

the supreme coder
ADMIN
Swift:
class EventChatV2 {
    private var dic: [String: LimUniqueResponder] = [:]
    private var modifiedKeys: Set<String> = []
    private var lim: Int

    init(_ lim: Int) {
        self.lim = lim
    }

    func getModifiedKeys() -> Set<String> {
        return modifiedKeys
    }

    func keyExists(_ key: String) -> Bool {
        // Return true if this key has been modified
        return modifiedKeys.contains(key)
    }

    /// Add items using a cloned responder for each input key
    func addItems(_ ur: LimUniqueResponder, _ args: String...) {
        for key in args {
            dic[key] = ur.clone()
        }
    }

    /// Load key-value list from DB save format (underscore-delimited)
    func addFromDB(_ key: String, _ value: String?) {
        guard let value = value, value.lowercased() != "null" else { return }
        let values = value.split(separator: "_").map(String.init)

        if dic[key] == nil {
            dic[key] = LimUniqueResponder(lim)
        }

        for item in values {
            dic[key]?.addResponse(item)
        }
    }

    /// Add a single key-value pair and mark the key as modified
    func addKeyValue(_ key: String, _ value: String) {
        modifiedKeys.insert(key)

        if dic[key] != nil {
            dic[key]?.addResponse(value)
        } else {
            let responder = LimUniqueResponder(lim)
            responder.addResponse(value)
            dic[key] = responder
        }
    }

    /// Inject a list of AXKeyValuePair-style objects
    func addKeyValues(_ elizaResults: [AXKeyValuePair]) {
        for pair in elizaResults {
            addKeyValue(pair.getKey(), pair.getValue())
        }
    }

    /// Return a randomized response for key
    func response(_ input: String) -> String {
        return dic[input]?.getAResponse() ?? ""
    }

    /// Return the most recently added response for key
    func responseLatest(_ input: String) -> String {
        return dic[input]?.getLastItem() ?? ""
    }

    /// Return a single savable string representation (underscore-separated)
    func getSaveStr(_ key: String) -> String {
        return dic[key]?.getSavableStr() ?? ""
    }
}
 
Top