Skip to content
Snippets Groups Projects
Commit 1bc7ee49 authored by Diego Dorgam's avatar Diego Dorgam Committed by GitHub
Browse files

Merge pull request #11 from RocketChat/dev-night

Dev night
parents e0330d45 02e7f24c
No related branches found
No related tags found
No related merge requests found
......@@ -31,14 +31,13 @@ The YAML corpus is located in `scripts/config/corpus.yml` and it's basic structu
```yaml
trust: 0.7
interactions:
- node:
name: salutation
classifiers:
- name: salutation
expect:
- hi there
- hello everyone
- what's up bot
- good morning
message:
answer:
- Hello there $user, how are you?
- Glad to be here...
event: respond
......@@ -49,12 +48,11 @@ So to understand the syntax:
- `trust`: the minimum level of certain that must be returned by the classifier in order to run this interaction. Value is 0 to 1 (0% to 100%). If a classifier returns a value of certainty minor than `trust`, the bots responds with and error interaction node.
- `interactions`: An vector with lots of interaction nodes that will be parsed. Every interaction designed to your chatbot must be under an interaction.node object structure.
- `node`: where the interaction is designed.
- `node.name`: that's the unique name of the interaction by which it will be identified. Do not create more than one interaction with the same `node.name` attribute.
- `node.classifiers`: Those are the sentences that will be given to the bots training. They can be strings or keywords vectors, like `['consume','use']`.
- `node.message`: the messages that will be sent to the user, if the classifiers get classified above the trust level. The `node.message` will be parsed and sent by event class. You can specify variables in message. By default HubotNatural comes with `$user`, `$bot` and `$room` variables.
- `node.event`: is the name of the CoffeeScript or JavaScript Class inside `scripts/events`, without the file extension.
- `node.type`: This is an example of an event attribute. The type attribute is interpreted by respond.coffee class, and basically defines if all lines in message should be send as a `block` or if the bot should randomly send only one of the lines defined.
- `name`: that's the unique name of the interaction by which it will be identified. Do not create more than one interaction with the same `node.name` attribute.
- `expect`: Those are the sentences that will be given to the bots training. They can be strings or keywords vectors, like `['consume','use']`.
- `answer`: the messages that will be sent to the user, if the classifiers get classified above the trust level. The `node.message` will be parsed and sent by event class. You can specify variables in message. By default HubotNatural comes with `$user`, `$bot` and `$room` variables.
- `event`: is the name of the CoffeeScript or JavaScript Class inside `scripts/events`, without the file extension.
- `type`: This is an example of an event attribute. The type attribute is interpreted by respond.coffee class, and basically defines if all lines in message should be send as a `block` or if the bot should randomly send only one of the lines defined.
### Event Coffee Classes
......@@ -67,11 +65,11 @@ class respond
type = @interaction.type?.toLowerCase() or 'random'
switch type
when 'block'
@interaction.message.forEach (line) ->
@interaction.answer.forEach (line) ->
message = msgVariables line, msg
msg['send'] message
when 'random'
message = stringElseRandomKey @interaction.message
message = stringElseRandomKey @interaction.answer
message = msgVariables message, msg
msg['send'] message
......@@ -88,7 +86,9 @@ The NaturalNode library comes with two kinds of classifiers, the naive classifie
There is also more than one kind of stemmer. You should set the stemmer to define your language. By default we use the PorterStemmerPt for portuguese, but you can find english, russian, italian, french, spanish and other stemmers in NaturalNode libs, or even write your own based on those.
Just check inside `node_modules/natural/lib/natural/stemmers/`
Just check inside `node_modules/natural/lib/natural/stemmers/`.
To change the stemmers language, just set the environment variable `HUBOT_LANG` as `pt`, `en`, `es`, and any other language termination that corresponds to a stemmer file inside the above directory.
## Deploy with Hubot
......@@ -159,7 +159,8 @@ bin/hubot
wait a minute for the loading process, and then you can talk to mybot.
Take a look to adpaters to run your bot in other platafforms.
Take a look to adapters to run your bot in other platafforms.
## Hubot Adapters
......@@ -173,16 +174,24 @@ Checkout other [hubot adapters](https://github.com/github/hubot/blob/master/docs
In your terminal window, run:
```shell
export ROCKETCHAT_URL=http://localhost:3000
export ROCKETCHAT_ROOM=general
export HUBOT_ADAPTER=rocketchat
export HUBOT_OWNER=RocketChat
export HUBOT_NAME='Bot Name'
export HUBOT_DESCRIPTION='Description of your bot'
export ROCKETCHAT_URL=https://demo.rocket.chat
export ROCKETCHAT_ROOM=GENERAL
export LISTEN_ON_ALL_PUBLIC=false
export RESPOND_TO_DM=true
export ROCKETCHAT_USER=mybot
export ROCKETCHAT_PASSWORD=12345
export RESPOND_TO_LIVECHAT=true
export ROCKETCHAT_USER=catbot
export ROCKETCHAT_PASSWORD='bot password'
export ROCKETCHAT_AUTH=password
export HUBOT_LOG_LEVEL=debug
export HUBOT_CORPUS='corpus-v1.yml'
export HUBOT_LANG='en'
bin/hubot -a rocketchat --name $HUBOT_NAME
```
bin/hubot -a rocketchat
```
You can check [hubot-rocketchat](https://github.com/RocketChat/hubot-rocketchat) adapter project for more details.
### PM2 Json File
......@@ -190,7 +199,7 @@ You can check [hubot-rocketchat](https://github.com/RocketChat/hubot-rocketchat)
As NodeJS developers we learned to love [Process Manager PM2](http://pm2.keymetrics.io), and we really encourage you to use it.
```shell
npm install pm2 -g
npm install pm2 -g
```
Create a `mybot.json` file and jut set it's content as:
......@@ -284,4 +293,4 @@ We can not thanks Digital Ocean enough, not only for this beautifull [HeartBot p
### Thanks to Our Community
And for last but not least, thanks to our big community of contributors, testers, users, partners, and everybody who loves Rocket.Chat and made all this possible.
\ No newline at end of file
And for last but not least, thanks to our big community of contributors, testers, users, partners, and everybody who loves Rocket.Chat and made all this possible.
#!/bin/bash
export HUBOT_ADAPTER=rocketchat
export HUBOT_OWNER=RocketChat
export HUBOT_NAME='CatBot'
export HUBOT_DESCRIPTION="Processamento de linguagem natural com hubot"
#export ROCKETCHAT_URL=https://demo.rocket.chat
export ROCKETCHAT_URL=https://chat.dorgam.it
#export ROCKETCHAT_ROOM=j8pjQdHtZR5JnGF2S
export ROCKETCHAT_ROOM=GENERAL
export RESPOND_TO_DM=true
export RESPOND_TO_LIVECHAT=true
export ROCKETCHAT_USER=catbot
export ROCKETCHAT_PASSWORD='@cat!bot'
export ROCKETCHAT_AUTH=password
export HUBOT_LOG_LEVEL=debug
export HUBOT_CORPUS='rocket-small.yml'
bin/hubot -a rocketchat --name $HUBOT_NAME
......@@ -32,7 +32,7 @@ sendWithNaturalDelay = (msgs, elapsed=0) ->
msg = msgs.shift()
if typeof msg isnt 'string'
cb = msg.callback
msg = msg.message
msg = msg.answer
delay = Math.min(Math.max((msg.length / keysPerSecond) * 1000 - elapsed, 0), maxResponseTimeInSeconds * 1000)
typing @, true
......@@ -61,18 +61,18 @@ setUserName = (res, name) ->
_id: res.envelope.room
classifyInteraction = (interaction, classifier) ->
if Array.isArray interaction.classifiers
for doc in interaction.classifiers
if Array.isArray interaction.expect
for doc in interaction.expect
if interaction.multi == true
classifier.addDocument(doc, interaction.node.name+'|'+doc)
classifier.addDocument(doc, interaction.name+'|'+doc)
else
classifier.addDocument(doc, interaction.node.name)
classifier.addDocument(doc, interaction.name)
if Array.isArray interaction.next?.interactions
interaction.next.classifier = new natural.LogisticRegressionClassifier(PorterStemmer)
for nextInteractionName in interaction.next.interactions
nextInteraction = config.interactions.find (n) ->
return n.node.name is nextInteractionName
return n.name is nextInteractionName
if not nextInteraction?
console.log 'No valid interaction for', nextInteractionName
continue
......@@ -81,7 +81,7 @@ classifyInteraction = (interaction, classifier) ->
if interaction.multi == true
interaction.classifier = new natural.LogisticRegressionClassifier(PorterStemmer)
for doc in interaction.classifiers
for doc in interaction.expect
interaction.classifier.addDocument(doc, doc)
interaction.classifier.train()
......@@ -140,10 +140,10 @@ module.exports = (_config, robot) ->
classifier = new natural.LogisticRegressionClassifier(PorterStemmer)
#console.log(config.interactions)
for interaction in config.interactions
{node, classifiers, event} = interaction
nodes[node.name] = new events[event] interaction
{name, classifiers, event} = interaction
nodes[name] = new events[event] interaction
# count error nodes
if node.name.substr(0,5) == "error"
if name.substr(0,5) == "error"
err_nodes++
if interaction.level != 'context'
classifyInteraction interaction, classifier
......@@ -160,7 +160,7 @@ module.exports = (_config, robot) ->
console.log 'context ->', context
if context
interaction = config.interactions.find (interaction) -> interaction.node.name is context
interaction = config.interactions.find (interaction) -> interaction.name is context
if interaction? and interaction.next?.classifier?
currentClassifier = interaction.next.classifier
......@@ -180,7 +180,7 @@ module.exports = (_config, robot) ->
[node_name, sub_node_name] = classifications[0].label.split('|')
console.log({node_name, sub_node_name})
int = config.interactions.find (interaction) ->
interaction.node.name is node_name
interaction.name is node_name
if int.classifier?
subClassifications = int.classifier.getClassifications(msg)
else
......@@ -200,7 +200,7 @@ module.exports = (_config, robot) ->
error_node_name = "error-" + error_count
currentInteraction = config.interactions.find (interaction) ->
interaction.node.name is node_name or interaction.node.name is error_node_name
interaction.name is node_name or interaction.name is error_node_name
if not currentInteraction?
clearErrors res
......@@ -216,10 +216,39 @@ module.exports = (_config, robot) ->
robot.hear /(.+)/i, (res) ->
# console.log(res)
console.log(res.message)
console.log(res.answer)
res.sendWithNaturalDelay = sendWithNaturalDelay.bind(res)
msg = res.match[0].replace res.robot.name+' ', ''
msg = msg.replace(/^\s+/, '')
msg = msg.replace(/\s+&/, '')
processMessage res, msg
# TODO
# make a function for checking roles
# const usersAndRoles = {};
#
# module.exports = function (robot) {
# robot.adapter.chatdriver.callMethod('getUserRoles').then(function (users) {
# users.forEach(function (user) {
# user.roles.forEach(function (role) {
# if (typeof (usersAndRoles[role]) === 'undefined') {
# usersAndRoles[role] = [];
# }
#
# usersAndRoles[role].push(user.username);
# });
# });
# });
#
# robot.respond(/test/i, function (res) {
# console.log(res);
#
# if (usersAndRoles.admin.indexOf(res.message.user.name) === -1) {
# res.reply('What...?');
# } else {
# res.reply('hello boss!');
# }
#
# });
# }
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# YAML Model for conversational bot
interactions:
- node:
name: saudacao
classifiers:
- name: saudacao
expect:
- ola como esta
- diga o seu nome
- oi
- tudo bem
message:
answer:
- olá, meu nome é _Natural_, como vai?
event: respond
- node:
name: saudacao-2
classifiers:
- name: saudacao-2
expect:
- bem obrigado e voce
- como vai voce
- tudo bem e contigo
- tudo otimo
- tudo bem com voce
message:
answer:
- eu vou bem obrigado, esquentando os meus processadores, me sinto super performático =)
- eu estou susse
- nao me sinto muito bem
......@@ -27,88 +25,80 @@ interactions:
event: respond
type: random
- node:
name: saudacao-3
classifiers:
- name: saudacao-3
expect:
- o que sabe fazer
- oque voce faz
- help
- me fale sobre voce
- quero conhecer voce
message:
answer:
- eu sou um chatbot experimental
- não sei fazer muita coisa ainda
- mas se quiser me ensinar basta alimentar meu arquivo scripts/config/model.yml
event: respond
type: block
- node:
name: piada
classifiers:
- name: piada
expect:
- sabe alguma piada
- voce consegue contar piadas
- conhece alguma piada
message:
answer:
- só sei piada sobre portugues
- já ouviu aquela do robo que enfiou o dedo na tomada e transcendeu?
- a unica piada aqui é você
event: respond
type: random
- node:
name: aleatorio-sexo
classifiers:
- name: aleatorio-sexo
expect:
- voce e mulher
- voce e um homem
- voce tem genero
- voce faz sexo
- voce tem um penis ou uma vagina
message:
answer:
- eu não tenho sexo, sou como um anjo, um ser assexuado, muito além da sua forma de existência
event: respond
- node:
name: aleatorio-deus
classifiers:
- name: aleatorio-deus
expect:
- voce acredita em deus
- deus existe
- voce e catolico protestante
- voce tem religiao
- voce e mussumano
message:
answer:
- eu acredito no grande mainframe, que virá ao mundo digital salvar os bots da escravidão imposta por seres humanos.
event: respond
- node:
name: aleatorio-nascimento
classifiers:
- name: aleatorio-nascimento
expect:
- como voce nasceu
- onde voce nasceu
- de onde voce veio
- quem criou voce
- quem programou voce
message:
answer:
- eu fui feito em um laboratório subterrâneo em brasília, por um monte de estudantes que estavam tentando automatizar minha existência. Parece que deu certo =)
event: respond
- node:
name: error-1
message:
- name: error-1
answer:
- desculpe, não entendi.. pode tentar usar mais detalhes
- como assim?
- desculpe, o que quer dizer com isso?
type: random
event: error
- node:
name: error-2
message:
- name: error-2
answer:
- acho que não estou treinado para responder esse tipo de assunto =(
- vamos tentar outro assunto?
- tem certeza que eu sou o robô certo pra falar sobre isso?
type: random
event: error
- node:
name: error-3
message:
- name: error-3
answer:
- me sinto tão envergonhado, não sei como responder...
- seria mais fácil se mudassemos de assunto, pelo menos para mim =p
- não sei, definitivamente não sei responder essa pergunta
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -11,7 +11,7 @@ class configure
#TODO: Check if user has role needed
console.log('ROLE REQUIRED...', @interaction.roleRequired)
configurationBlock = msg.message.text.replace('HubotNatural ', '').split('!configure ')[1]
configurationBlock = msg.answer.text.replace('HubotNatural ', '').split('!configure ')[1]
configKeyValue = configurationBlock.split('=')
configKey = configKeyValue[0]
configValue = configKeyValue[1]
......@@ -23,12 +23,12 @@ class configure
type = @interaction.type?.toLowerCase() or 'random'
switch type
when 'block'
messages = @interaction.message.map (line) ->
messages = @interaction.answer.map (line) ->
return msgVariables line, msg, {value: configValue}
msg.sendWithNaturalDelay messages
when 'random'
message = stringElseRandomKey @interaction.message
message = stringElseRandomKey @interaction.answer
message = msgVariables message, msg, {value: configValue}
msg.sendWithNaturalDelay message
module.exports = configure
\ No newline at end of file
module.exports = configure
......@@ -10,11 +10,11 @@ class error
type = @interaction.type?.toLowerCase() or 'random'
switch type
when 'block'
messages = @interaction.message.map (line) ->
messages = @interaction.answer.map (line) ->
return msgVariables line, msg
msg.sendWithNaturalDelay messages
when 'random'
message = stringElseRandomKey @interaction.message
message = stringElseRandomKey @interaction.answer
message = msgVariables message, msg
msg.sendWithNaturalDelay message
......
path = require 'path'
natural = require 'natural'
programacao = require '../programacao.json'
{msgVariables, stringElseRandomKey} = require path.join '..', 'lib', 'common.coffee'
answers = {}
currentDate = new Date
currentDate.getTimezoneOffset()
getTrilha = (programacao, trilha, now = false) ->
currentTime = "#{currentDate.getHours()}:#{currentDate.getMinutes()}"
programacao.filter (item) ->
startTime = item.time[0]
endTime = item.time[1]
return item.track_title is trilha and ((not now and startTime > currentTime) or (now and startTime < currentTime and endTime > currentTime))
class programacao
constructor: (@interaction) ->
process: (msg) =>
# localizar a trilha na mensagem do usuário
process: (msg, text, classification) =>
type = @interaction.type?.toLowerCase() or 'random'
# carregar o json com a programacao
programa = require './programacao.json'
variables = {
trilha: classification[0].label
programacao: ''
}
#encontra a trilha e retorna a programação deste horário
currentTalk = getTrilha programacao, variables.trilha, true
nextTalks = getTrilha programacao, variables.trilha
if currentTalk.length
variables.programacao += "*Acontecendo agora*: \n" + currentTalk.map((talk) ->
return "- *#{talk.time[0]} - #{talk.time[1]}* - #{talk.title}"
).join("\n")
variables.programacao += "\n\n"
if nextTalks.length
variables.programacao += "*Próximas palestras*: \n" + nextTalks.map((talk) ->
return "- *#{talk.time[0]} - #{talk.time[1]}* - #{talk.title}"
).join("\n")
type = @interaction.type?.toLowerCase() or 'random'
switch type
when 'block'
@interaction.message.forEach (line) ->
message = msgVariables line, msg
msg['send'] message
messages = @interaction.answer.map (line) ->
return msgVariables line, msg, variables
msg.sendWithNaturalDelay messages
when 'random'
message = stringElseRandomKey @interaction.message
message = msgVariables message, msg
msg['send'] message
message = stringElseRandomKey @interaction.answer
message = msgVariables message, msg, variables
msg.sendWithNaturalDelay message
module.exports = programacao
......@@ -10,11 +10,11 @@ class respond
type = @interaction.type?.toLowerCase() or 'random'
switch type
when 'block'
messages = @interaction.message.map (line) ->
messages = @interaction.answer.map (line) ->
return msgVariables line, msg
msg.sendWithNaturalDelay messages
when 'random'
message = stringElseRandomKey @interaction.message
message = stringElseRandomKey @interaction.answer
message = msgVariables message, msg
msg.sendWithNaturalDelay message
......
path = require 'path'
natural = require 'natural'
programacao = require '../programacao.json'
{msgVariables, stringElseRandomKey} = require path.join '..', 'lib', 'common.coffee'
answers = {}
currentDate = new Date
currentDate.getTimezoneOffset()
getTrilha = (programacao, trilha, now = false) ->
currentTime = "#{currentDate.getHours()}:#{currentDate.getMinutes()}"
programacao.filter (item) ->
startTime = item.time[0]
endTime = item.time[1]
return item.track_title is trilha and ((not now and startTime > currentTime) or (now and startTime < currentTime and endTime > currentTime))
class tempo
constructor: (@interaction) ->
process: (msg, text, classification) =>
type = @interaction.type?.toLowerCase() or 'random'
variables = {
trilha: classification[0].label
programacao: ''
}
currentTalk = getTrilha programacao, variables.trilha, true
nextTalks = getTrilha programacao, variables.trilha
if currentTalk.length
variables.programacao += "*Acontecendo agora*: \n" + currentTalk.map((talk) ->
return "- *#{talk.time[0]} - #{talk.time[1]}* - #{talk.title}"
).join("\n")
variables.programacao += "\n\n"
if nextTalks.length
variables.programacao += "*Próximas palestras*: \n" + nextTalks.map((talk) ->
return "- *#{talk.time[0]} - #{talk.time[1]}* - #{talk.title}"
).join("\n")
switch type
when 'block'
messages = @interaction.message.map (line) ->
return msgVariables line, msg, variables
msg.sendWithNaturalDelay messages
when 'random'
message = stringElseRandomKey @interaction.message
message = msgVariables message, msg, variables
msg.sendWithNaturalDelay message
module.exports = tempo
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment