From de65004ddb326f7110486079efde18738f211d26 Mon Sep 17 00:00:00 2001 From: jecortez Date: Fri, 12 Jun 2020 16:07:52 -0700 Subject: [PATCH] Add option to generate models per-user this is still in progress --- src/config.coffee | 3 + src/default-listeners.coffee | 144 ++++++++++++++++++++--------------- 2 files changed, 84 insertions(+), 63 deletions(-) diff --git a/src/config.coffee b/src/config.coffee index 5151510..188544a 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -63,4 +63,7 @@ module.exports = (hash) -> ignoreMessageList: listSetting(hash, 'HUBOT_MARKOV_IGNORE_MESSAGE_LIST', []) learningListenMode: stringSetting(hash, 'HUBOT_MARKOV_LEARNING_LISTEN_MODE', 'catch-all') respondListenMode: stringSetting(hash, 'HUBOT_MARKOV_RESPOND_LISTEN_MODE', 'catch-all') + createUserModels: boolSetting(hash, 'HUBOT_MARKOV_CREATE_USER_MODELS', false) + userModelBlackList: listSetting(hash, 'HUBOT_MARKOV_USER_MODEL_BLACKLIST', []) + userModelWhiteList: listSetting(hash, 'HUBOT_MARKOV_USER_MODEL_WHITELIST', []) } diff --git a/src/default-listeners.coffee b/src/default-listeners.coffee index 591fcfa..56ce7a1 100644 --- a/src/default-listeners.coffee +++ b/src/default-listeners.coffee @@ -5,71 +5,96 @@ processors = require './processors' -module.exports = (robot, config) -> - activeModelNames = [] +reportErr = (msg, err) -> + msg.send ":boom:\n```#{err.stack}```" - reportErr = (msg, err) -> - msg.send ":boom:\n```#{err.stack}```" +setupModelResponder = (robot, pattern, markovGeneratorFn) -> + robot.respond pattern, (msg) -> + markovGeneratorFn msg.match[2] or '', (err, text) -> + return reportErr(err) if err? + msg.send text - if config.defaultModel - robot.markov.createModel 'default_forward', {} - activeModelNames.push 'default_forward' +getModelGenerationCallback = (robot, config, modelName) -> + return (seed, callback) -> + robot.markov.modelNamed modelName, (model) -> + model.generate seed, config.generateMax, callback - robot.markov.generateForward = (seed, callback) -> - robot.markov.modelNamed 'default_forward', (model) -> - model.generate seed, config.generateMax, callback +generateDefaultModel = (robot, config) -> + robot.markov.createModel 'default_forward', {} + robot.markov.generateForward = getModelGenerationCallback(robot, config, 'default_forward') + # Generate markov chains on demand, optionally seeded by some initial state. + setupModelResponder robot, /markov(\s+(.+))?$/i, robot.markov.generateForward - # Generate markov chains on demand, optionally seeded by some initial state. - robot.respond /markov(\s+(.+))?$/i, (msg) -> - robot.markov.generateForward msg.match[2] or '', (err, text) -> - return reportErr(err) if err? - msg.send text + return 'default_forward' - if config.reverseModel - robot.markov.createModel 'default_reverse', {}, (model) -> - model.processWith processors.reverseWords +generateReverseModel = (robot, config) -> + robot.markov.createModel 'default_reverse', {}, (model) -> + model.processWith processors.reverseWords + + robot.markov.generateReverse = getModelGenerationCallback(robot, config, 'default_reverse') - activeModelNames.push 'default_reverse' + # Generate reverse markov chains on demand, optionally seeded by some end state + setupModelResponder robot, /remarkov(\s+(.+))?$/i, robot.markov.generateReverse + + return 'default_reverse' + +generateMiddleModel = (robot, config) -> + robot.markov.generateMiddle = (seed, callback) -> + generateRight = getModelGenerationCallback(robot, config, 'default_forward') + + generateRest = (right, cb) -> + words = processors.words.pre(right) + rightSeed = words.shift() or '' - robot.markov.generateReverse = (seed, callback) -> robot.markov.modelNamed 'default_reverse', (model) -> - model.generate seed, config.generateMax, callback + model.generate rightSeed, config.generateMax, (err, left) -> + return cb(err) if err? + cb(null, [left, words...].join ' ') - # Generate reverse markov chains on demand, optionally seeded by some end state - robot.respond /remarkov(\s+(.+))?$/i, (msg) -> - robot.markov.generateReverse msg.match[2] or '', (err, text) -> - return reportErr(err) if err? - msg.send text + generateRight (err, right) -> + return callback(err) if err? + generateRest right, callback - if config.defaultModel and config.reverseModel + # Generate markov chains with the seed in the middle + setupModelResponder robot, /mmarkov(\s+(.+))?$/i, robot.markov.generateMiddle - robot.markov.generateMiddle = (seed, callback) -> - generateRight = (cb) -> - robot.markov.modelNamed 'default_forward', (model) -> - model.generate seed, config.generateMax, cb + return 'default_middle' - generateRest = (right, cb) -> - words = processors.words.pre(right) - rightSeed = words.shift() or '' +getUserModel = (username, config, robot)-> + userModelName = 'user_' + username - robot.markov.modelNamed 'default_reverse', (model) -> - model.generate rightSeed, config.generateMax, (err, left) -> - return cb(err) if err? - cb(null, [left, words...].join ' ') + unless robot.markov.byName[userModelName] + robot.markov.createModel userModelName, {} + markovGenerateUser = getModelGenerationCallback(robot, config, userModelName) - generateRight (err, right) -> - return callback(err) if err? - generateRest right, callback + # Generate user markov chains on demand, optionally seeded by some end state + umarkovUserPattern = "umarkov " + username + "(\s+(.+))?$" + setupModelResponder robot, RegExp(umarkovUserPattern), markovGenerateUser - # Generate markov chains with the seed in the middle - robot.respond /mmarkov(\s+(.+))?$/i, (msg) -> - robot.markov.generateMiddle msg.match[2] or '', (err, text) -> - return reportErr(err) if err? - msg.send text + return userModelName - if activeModelNames.length isnt 0 +attachRobotListener = (robot, listenMode, listener) -> + if listenMode == 'hear-all' + robot.hear /.*/i, listener + else if listenMode == 'catch-all' + robot.catchAll listener + else + robot.hear RegExp("" + listenMode), listener - learningListener = (msg) -> +module.exports = (robot, config) -> + activeModelNames = [] + + if config.defaultModel + activeModelNames.push generateDefaultModel(robot, config) + + if config.reverseModel + activeModelNames.push generateReverseModel(robot, config) + + if config.defaultModel and config.reverseModel + generateMiddleModel robot, config + + if activeModelNames.length isnt 0 or config.createUserModels? + attachRobotListener robot, config.learningListenMode, (msg) -> # Ignore empty messages return if !msg.message.text @@ -87,26 +112,19 @@ module.exports = (robot, config) -> for name in activeModelNames robot.markov.modelNamed name, (model) -> model.learn msg.message.text - if config.learningListenMode == 'hear-all' - robot.hear /.*/i, learningListener - else if config.learningListenMode == 'catch-all' - robot.catchAll learningListener - else - robot.hear RegExp("" + config.learningListenMode), learningListener + # add per-user models to the list as we see them + username = msg.envelope.user.name + if config.createUserModels and username not in config.userModelBlackList + if config.userModelWhiteList.length is 0 or config.userModelWhiteList[username] + userModelName = getUserModel username, config, robot + robot.markov.modelNamed userModelName, (model) -> model.learn msg.message.text if config.respondChance > 0 - respondListener = (msg) -> + attachRobotListener robot, config.respondListenMode, (msg) -> if Math.random() < config.respondChance - randomWord = msg.random(processors.words.pre(msg.message.text)) or '' + randomWord = msg.random(processors.words.pre msg.message.text) or '' if config.reverseModel robot.markov.generateMiddle randomWord, (text) -> msg.send text else robot.markov.generateForward randomWord, (text) -> msg.send text - - if config.respondListenMode == 'hear-all' - robot.hear /.*/i, respondListener - else if config.respondListenMode == 'catch-all' - robot.catchAll respondListener - else - robot.hear RegExp("" + config.respondListenMode), respondListener