Hubot 官方文档(译)
Hubot 官方文档
概览
Hubot 起步
您需要node.js和npm。安装它们后,我们就可以安装 hubot 生成器:
1 | npm install -g yo generator-hubot |
这条命令将给我们带来hubot yeoman生成器。现在我们就可以先创建一个的文件夹,然后在里面生成一个新的hubot实例。例如,如果我们想制造一个叫 myhubot的机器人:
1 | mkdir myhubot |
执行命令后,您将被问到一些关于谁在创建机器人和您将使用哪个适配器的问题。适配器是hubot与不同聊天提供商集成的方式。
如果您希望在没有交互提示配置的情况下自动化您的 hubot 构建,您可以将以下选项添加到 yo hubot
命令中:
Option | Description |
---|---|
--owner=”Bot Wrangler bw@example.com“ | Bot owner, e.g. “Bot Wrangler bw@example.com” |
--name=”Hubot” | bot 名, e.g. “Hubot” |
--description=”Delightfully aware robutt” | bot 描述, e.g. “Delightfully aware robutt” |
--adapter=campfire | bot 适配器, e.g. “campfire” |
--defaults | 所有声明都设置为默认值且无需提示 |
您现在就拥有了自己的一个多功能的 hubot!为方便起见,有一个bin/hubot
命令,用于处理安装 npm 依赖项、加载脚本,然后启动您的 hubot。
Hubot 需要 Redis 来持久化数据,因此在您可以在自己的计算机上启动 hubot 之前,您应该在本地主机上安装 Redis。如果只是想在没有 Redis 的情况下测试 Hubot,那么您可以从 external-scripts.json
中删除 hubot-redis-brain
。
1 | bin/hubot |
这将使用 shell 适配器启动 hubot,这对开发非常有用。记下 Hubot>
,这是您的 hubot 将用命令响应的名称。例如,列出可用命令:
1 | bin/hubot |
您基本上都会想要改变您的 hubot 的名字来添加角色。 bin/hubot 需要一个 --name
:
1 | bin/hubot --name myhubot |
您的 hubot 现在将响应 myhubot
。这是不区分大小写的,可以以 @
为前缀或以 :
为后缀。这些是等效的:
1 | MYHUBOT help |
脚本
Hubot 的力量来自于脚本。社区编写和维护着几百个脚本。通过使用 hubot-scripts <your-search-term>
查找 NPM registry 找到它们。例如:
1 | npm search hubot-scripts github |
要使用 NPM 包中的脚本:
- 运行
npm install --save <package-name>
去添加该包作为依赖并安装它。 - 添加该包到
external-scripts.json
。 - 运行
npm home <package-name>
以打开脚本主页的浏览器窗口,您可以在其中找到有关配置和安装脚本的更多信息。
您也可以将您自己的脚本放在scripts/
目录下。放在这里的所有脚本都将被 hubot 自动加载和使用。阅读有关通过编写自己的脚本自定义 hubot 的更多信息。
适配器
Hubot 使用适配器模式来支持多个聊天后端。这是可用适配器的列表,以及如何配置它们的详细信息。
部署
您可以将 hubot 部署到 Heroku,这是官方支持的方法。此外,您还可以将 hubot 部署到类似 UNIX 的系统或 Windows 中。请注意,部署到 Windows 的支持尚未得到官方支持。
模式
使用自定义脚本,您可以快速自定义 hubot,使他(她)成为最具生命力的机器人。当您教您的 hubot 新的技能,阅读docs/patterns.md 里的一些漂亮的技巧,可能会派上用场。
脚本
开箱即用的 hubot 不会做太多, 但它是一个可扩展的,可编脚本的机器人朋友。有数以百计的脚本由社区编写和维护,并且自己写起来也很容易。您可以在hubot的scripts
目录中创建自定义脚本,或创建一个脚本包,以便与社区共享!
脚本剖析
当您创建 hubot 时,生成器还创建了scripts
目录。如果您看了一下那里,您会看到一些脚本的例子。脚本要成为脚本,它需要:
- 在 hubot 脚本加载的目录里(默认为
src/scripts
和scripts
) - 是一个
.coffee
或者.js
文件 - 导出一个函数
导出一个函数,我们的意思是:
1 | module.exports = (robot) -> |
机器人参数是您的机器人朋友的实例。在这一点上,我们可以开始编写一些很棒的脚本。
聆听和响应
由于这是一个聊天机器人,最常见的交互是基于消息。Hubot 可以hear
在房间里说的消息,或直接response
专门传达给它的消息。这两种方法都以一个正则表达式和一个回调函数作为参数。例如:
1 | module.exports = (robot) -> |
每当一个消息的文本匹配时,robot.hear /badger/
的回调就会被调用。例如:
- Stop badgering the witness
- badger me
- what exactly is a badger anyways
robot.respond /open the pod bay doors/i
的回调仅当消息紧跟着机器人的名字或者别名被调用。如果机器人的名称是 HAL,别名是 /,则此回调将被以下触发:
- hal open the pod bay doors
- HAL: open the pod bay doors
- @HAL open the pod bay doors
- /open the pod bay doors
它不会被调用:
- HAL: please open the pod bay doors
- 因为它的响应与机器人名称后紧接着的文本绑定
- has anyone ever mentioned how lovely you are when you open the pod bay doors?
- 因为它确少机器人的名字
发送和回复
res
参数是Response
的实例(在历史上,这个参数是 msg
,您可能会看到其他脚本使用它)。有了它,您可以将消息send
回 res
来的房间,emote
一个消息到一个房间(如果给定的适配器支持它),或reply
发送消息的人。例如:
1 | module.exports = (robot) -> |
robot.hear /badgers/
的回调发送指定的消息,而不管向谁说: “Badgers? BADGERS? WE DON’T NEED NO STINKIN BADGERS”。
如果一个用户 Dave 说 “HAL: open the pod bay doors”, robot.respond /open the pod bay doors/i
的回调发送一个消息Dave: I’m afraid I can’t let you do that.
给房间或用户的消息
消息可以使用 messageRoom
功能发送给指定的房间或用户。
1 | module.exports = (robot) -> |
如果需要,可以明确指定用户名,或使用响应对象,可以向原始发件人发送私人消息。
1 | robot.respond /I don't like Sam-I-am/i, (res) -> |
捕获数据
到目前为止,我们的脚本已经有静态响应,这虽然有趣,在功能方面却很薄弱。res.match
的结果是将传入的消息与正则表达进行匹配。这只是一个 JavaScript thing, 它最终是一个数组, 索引0是与表达式匹配的全文。如果包括捕获组,这些组将填充 res.match
。例如,如果我们更新脚本:
1 | robot.respond /open the (.*) doors/i, (res) -> |
如果Dave说“HAL: open the pod bay doors”,res.match[0]
就成了open the pod bay doors
,res.match[1]
则为pod bay
。现在我们可以开始做更多动态的东西了:
1 | robot.respond /open the (.*) doors/i, (res) -> |
调用 HTTP
Hubot 可以为您进行 HTTP 请求,以集成和整合第三方 API 。这能够通过一个在robot.http
的node-scoped-http-client实例进行。最简单的例子如:
1 | robot.http("https://midnight-train") |
post 请求例如:
1 | data = JSON.stringify({ |
如果遇到错误,err
就是您所遇到的错误。您通常会想要检查这一点,并相应地处理:
1 | robot.http("https://midnight-train") |
res
是node的http.ServerResponse的一个实例。使用node-scoped-http-client的时候大多数方法不是特别关键, 但是有意思的是statusCode
和getHeader
。使用statusCode
去检查HTTP的状态码,当不是200状态码的时候意味着糟糕的事情发生了。使用getHeader
查看header,例如去检查服务限流(X-RateLimit-Limit:1 //每秒1次请求
):
1 | robot.http("https://midnight-train") |
body
是响应体字符串,应该是您最关心的东西。
1 | robot.http("https://midnight-train") |
JSON
与 API 交流,最简单的方法是 JSON,因为它不需要任何额外的依赖。当使用robot.http
请求,您通常应该设置Accept
头去给API一个希望得到什么的线索。一旦您得到了返回的body
,您就可以使用 JSON.parse
:
1 | robot.http("https://midnight-train") |
得到的返回也可能不是JSON,就像如果这API遇到一个错误并且它试图渲染一个正常的 HTML 错误,而不是 JSON。为了安全起见,您应该检查内容类型,并在解析时发现任何错误。
1 | robot.http("https://midnight-train") |
XML
XML API 比较难,因为没有捆绑的 XML 解析库。详细介绍此文档的范围超出了此范围,但下面有一些可以查看的库:
- xml2json (simplest to use, but has some limitations)
- jsdom (JavaScript implementation of the W3C DOM)
- xml2js
萤幕抓取
对于没有 API 的时代,总有可能进行萤幕抓取。详细介绍此文档的范围超出了此范围,但下面是一些可以查看的库:
高级 HTTP 和 HTTPS 设置
前面提到,hubot 使用node-scoped-http-client来提供一个简单的接口去发起HTTP和HTTPS请求。它封装了node的http和https库,但为常见的交互类型提供了一个简单的DSL(Domain Specific Language 领域专用语言)。
如果您需要直接控制http和https的选项,您传第二个参数给robot.http
,它将通过 node-scoped-http-client 传递给 http 或者 https:
1 | options = |
另外,如果node-scoped-http-client并不适合您,您可以直接使用http和https,或者其它的node库,例如:request
随机
一种常见的模式是聆听或响应命令,并发送随机有趣的图像或文本从一个包含可能性的数组。直接使用JavaScript和CoffeeScript来实现比较麻烦, 所以 hubot 包括一个方便的方法:
1 | lulz = ['lol', 'rofl', 'lmao'] |
主题
Hubot 可以响应一个房间的主题变化,假设适配器支持它:
1 | module.exports = (robot) -> |
加入和离开
Hubot 能够观察用户的加入和离开,假设适配器支持它。
1 | enterReplies = ['Hi', 'Target Acquired', 'Firing', 'Hello friend.', 'Gotcha', 'I see you'] |
自定义监听器
虽然上面的 helpers 涵盖了普通用户需要的大部分功能(聆听、响应、加入、离开、主题),有时您想为监听器赋予特定的匹配逻辑。如果是这样,您可以使用listen
来指定自定义匹配函数来替代正则表达。
如果监听器回调要被执行,匹配函数必须返回真实值。然后将匹配函数的真实返回值用response.match
传递给回调。
1 | module.exports = (robot) -> |
有关复杂的匹配器示例,请参阅设计模式文档。
环境变量
Hubot 可以使用process.env访问他运行的环境,就像任何其他node程序一样。这可用于配置脚本的运行方式,约定使用HUBOT_
前缀。
1 | answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING |
小心的去确保环境变量没有定义时脚本可以加载,给 hubot 开发者去定义该环境变量的提示,或者默认的一些东西。由脚本编写者来决定这是否应该是一个致命的错误(e.g. hubot 退出),也可以制作任何依赖于它的脚本,去表明它需要配置。在尽可能和有意义时,脚本运作不需要任何其它的配置更好。
在这里,我们可以默认的东西:
1 | answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING or 42 |
如果环境变量没有定义,在这里我们退出:
1 | answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING |
最后, 我们更新robot.repond
去检查它:
1 | answer = process.env.HUBOT_ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING |
依赖
Hubot 使用npm去管理它的依赖。要去添加额外的包,添加它们到package.json
文件的dependencies
中。例如,添加lolimadeupthispackage 1.2.3
,如:
1 | "dependencies": { |
如果您使用 hubot-scripts 中的脚本,记下要添加的脚本中的Dependencies
文档。它们以可以复制粘贴进package.json
的格式列出,只需确保根据需要添加逗号使其成为有效 JSON即可。
超时和间隔
译者注:这里普及JavaScript的
setTimeout
和setInterval
语法。
Hubot可以使用JavaScript内置的setTimeout来延迟运行代码。
1 | module.exports = (robot) -> |
另外,hubot可以用setInterval来间隔运行代码。它需要回调方法,以及调用之间的等待时间:
1 | module.exports = (robot) -> |
setTimeout
和setInterval
创建时,都返回超时或间隔的ID。这可用于clearTimeout
和clearInterval
。
1 | module.exports = (robot) -> |
HTTP 监听器
Hubot 包括支持express网络框架以满足HTTP请求。它监听EXPRESS_PORT
或PORT
环境变量指定的端口(按该顺序首选),默认值设置为8080。一个express应用的实例可在robot.router
中。可以通过指定EXPRESS_USER
和EXPRESS_PASSWORD
来保护用户名和密码。通过设置EXPRESS_STATIC
它可以自动为静态文件服务。
最常见的用途是为带有webhooks的服务提供HTTP端点去推送,并让这些出现在聊天中。
1 | module.exports = (robot) -> |
用curl测试它;另请参阅下面关于error handling的部分。
1 | // raw json, must specify Content-Type: application/json |
所有的端点URLs应该以/hubot
起头(无论您的机器人及叫什么名字)。这种一致性使得设置webhooks(可复制粘贴的URL)更加容易,并且保证URLs是有效的(并不是所有的bot命名都是URL安全的)。
事件
Hubot 还可以响应可用于在脚本之间传递数据的事件。这是通过使用robot.emit
和robot.on
封装node.js的EventEmitter实现的。
其中一个用例是,有一个脚本用于处理与服务的交互,然后在事件出现时发送。例如,我们有一个脚本从 GitHub post-commit
钩子接收数据,当 commits 来时发送 commits 事件,然后对这些提交进行另一个脚本行为。
1 | # src/scripts/github-commits.coffee |
1 | # src/scripts/heroku.coffee |
如果您提供一个事件,强烈建议在其数据中包括 hubot user或room对象。这将允许hubot 在聊天中通知用户或房间。
错误处理
没有完美的代码,并且错误和意外是意料之中的。以前,一个未捕获的异常会是您的hubot实例崩溃。Hubot现在包含了一个uncaughtException
处理程序,它为脚本提供钩子以此来进行错误处理。
1 | # src/scripts/does-not-compute.coffee |
您可以在这里做任何您想做的事,但您会想采取额外的预防措施来拯救和记录错误,特别是异步代码。否则,您可能会发现自己有递归的错误,却不知道发生了什么事情。
在头巾之下,‘error’事件发射出来,伴随着的是错误处理程序消费那个事件。uncaughtException
处理程序,从技术上讲, 使过程处于未知状态。。因此,无论何时您应该尽可能的拯救您自己的异常,并且自己发出它们。第一个参数是发出的错误,第二个参数是生成错误的可选消息。
使用前面的例子:
1 | robot.router.post '/hubot/chatsecrets/:room', (req, res) -> |
对于第二个例子,值得思考的是用户会看到哪些消息。如果您有回复用户的错误处理程序,您可能不需要添加自定义消息,并且可以发回提供给get()请求的错误消息,当然这取决于您想多么的公开您的错误报告。
脚本文档
Hubot 脚本可以用在其文件顶部的注释编写文档,例如:
1 | # Description: |
这最重要的和用户面向的是Commands
。在加载时间,Hubot 查看每个脚本的命令部分,并建立所有命令的列表。包含的help.coffee
允许用户在所有命令或搜索中寻求帮助。因此,为这些命令制作文档使用户更容易发现这些命令。
为命令制作文档的时候,这里有一些最佳实践:
- 保持在一行。帮助命令会被排序,因此第二行可能会被插到一个出乎意料的的地方,使得它没有意义。
- 将 Hubot 命名为 hubot,即使您的 hubot 被命名为其它。它将被自动替换为正确的名字。这使得无需更新文档即可轻松共享脚本。
- 对于
robot.respond
文档,总是以hubot
作为前缀。Hubot将自动用您的机器人名字或者机器人的别名替换。 - 查看 man 手册文档是怎样制作自己的文档的。特别是,括号表示可选项,”…”表示任意数量的参数,等。
文档的其它部分与bot的开发人员更相关,尤其是依赖、配置变量和说明。对hubot-scripts的所有贡献应包含与脚本启动和运行相关的所有这些部分。
持久化
Hubot暴露robot.brain
内存键值存储,可用于通过脚本存储和检索数据。
1 | robot.respond /have a soda/i, (res) -> |
如果脚本需要检索用户数据,robot.brain
上有些方法通过id,name,或者name的模糊匹配:userForName
, userForId
, userForFuzzyName
和usersForFuzzyName
去查找一个或者多个用户。
1 | module.exports = (robot) -> |
脚本加载
加载脚本有三个主要来源:
scripts/
目录下的所有脚本都与您的 hubot 绑定。hubot-scripts.json
中指定的社区脚本,并发布在hubot-scripts
npm 包。- 脚本从外部 npm 包加载, 并在
external-scripts.json
中指定。
从scripts/
目录中加载的脚本按字母顺序加载,因此您可以预期脚本的一致加载顺序。例如:
scripts/1-first.coffee
scripts/_second.coffee
scripts/third.coffee
分享脚本
一旦您构建了一些新的脚本,以扩大您的机器人朋友的能力,您应该考虑与世界分享他们!至少,您需要打包脚本并将其提交到 Node.js 程序包仓库
。您还应查看以下共享脚本的最佳实践。
查看脚本是否已存在
首先检查像您这样的脚本的 NPM 包是否已经存在。如果您没有看到可以贡献的现有包,则可以轻松地开始使用 hubot 脚本 yeoman 生成器。
创建一个脚本程序包
为 hubot 创建脚本包非常简单。首先安装yeoman生成器:
1 | npm install -g yo generator-hubot |
安装hubot生成器后,创建hubot脚本类似于创建一个新的hubot。您为您的hubot脚本创建目录,并在其中生成一个新的hubot:script
。例如,如果我们想创建一个 hubot 脚本叫“my-awesome-script”:
1 | mkdir hubot-my-awesome-script |
此时,您会被问到一些有关脚本作者的问题,脚本的名字(由目录名猜测),一个简短的描述,和去查找它的关键字(我们建议至少有hubot
,hubot-scripts
)。
如果您使用的是 git,生成的目录包含一个 .gitignore
,因此您可以初始化并添加所有内容:
1 | git init |
您现在有一个准备好去启动的hubot脚本库。轻松打开预创建的src/awesome-script.coffee
文件,并开始构建您的脚本!准备好后,您可以按照他们的文档将其发布到 npmjs!
您可能需要为新脚本编写一些单元测试。测试脚本样例写在test/awesome-script-test.coffee
,您可以用grunt
运行。有关测试的更多信息,请参阅测试Hubot脚本部分。
监听器元数据
除了正则表达和回调,hear
和respond
函数还接受一个可选的选项对象,它可以被用于附加任意元数据到生成的监听器对象。此元数据允许在不修改脚本包的情况下轻松扩展脚本的行为。
最重要和最常用的元数据键是id
。每个监听器应该被赋予一个唯一的名字(options.id,默认是null
)。名称应按模块划分(e.g. ‘my-module.my-listener’)。这些名称允许其他脚本直接针对单个监听器,并扩展它们的其他功能,如授权和速率限制。
其他扩展可以定义和处理其他元数据键。更多信息,请参阅Listener Middleware section部分。
回到到较早的示例:
1 | module.exports = (robot) -> |
这些范围标识符允许您外部指定新行为,如:
- 授权政策: “允许
annoyers
组中的每个人执行annoyance.*
命令” - 限速:“只允许30分钟执行一次
annoyance.start
”
中间件
有三种中间件: Receive, Listener and Response。
在监听器被查看之前,Receive 中间件执行一次。Listener 中间件在监听器匹配到信息后执行。Response 中间件在每次响应一个消息时运行。
执行过程和 API
类似于Express middleware,hubot 按定义顺序执行中间件。每个中间件可以继续链(通过调用next
)或中断链(通过调用done
)。如果所有中间件继续,监听器回调被执行,并且done
会被调用。中间件可以包装done
的回调,以便在过程的后半部分执行代码(在执行了监听器回调或更深的中间件中断之后)。
中间件被调用:
context
- 查看每个中间件类型的 API,查看上下文暴露的内容。
next
- 一个没有额外的属性的函数,它应该被调用从而继续到下一块中间件/执行监听器的回调
next
应该用一个单独的、可选的参数调用:要么提供done
函数,要么一个最终调用的时候叫done
的新函数。如果参数没有指定,那么将假定为done
。
done
- 一个没有附加属性的函数,用于在中断中间件执行时调用,并开始执行完成功能的链。
done
在调用时无需任何参数。
每个中间件接收相同的context
、next
和done
的API签名。不同种类的中间件在context
对象中可能接收到不同的信息。详细信息,请参阅每种类型的中间件的 API。
错误处理
对于同步中间件(从不产生事件循环),hubot 会自动捕获错误并发出error
事件,就像标准的监听器一样。Hubot 还将自动调用最近done
的回调以解除中间件堆栈。异步中间件应补捕获自己的异常,发出error
事件,并调用done
。任何未捕获的异常都会中断所有中间件完成回调的执行。
Listener 中间件
Listener 中间件在匹配消息的监听器和监听器执行之间插入逻辑。这允许您创建为每个匹配脚本运行的扩展。示例包括集中授权策略、限速、记录和指标。中间件像其他 hubot 脚本一样实现:中间件不是使用hear
和respond
方法,而是通过listenerMiddleware
注册。
Listener 中间件示例
在hubot-rate-limit中可以找到一个功能齐全的例子。
一个中间件logging命令执行的简单示例:
1 | module.exports = (robot) -> |
在此示例,将为与监听器匹配的每个聊天消息写入日志消息。
做出限速决策的更复杂示例:
1 | module.exports = (robot) -> |
在这个例子中,中间件检查监听器是否在过去1000ms中执行过。如果有,中间件立马调用done
,防止监听器回调被调用。如果监听器允许被执行,中间件附加一个done
处理器,以便它可以记录监听器完成执行的时间。
此示例还显示了如何利用特定于监听器的元数据创建非常强大的扩展:脚本开发人员只需添加中间件并设置监听器选项,即可使用速率限制中间件去以不同速率轻松率限制命令。
1 | module.exports = (robot) -> |
Listener 中间件 API
Listener 中间件回调接收三个参数,context
、next
、done
。有关next
和done
的描述,查看中间件API。Listener 中间件上下文包含这些字段:
listener
options
:定义监听器时设置的一个简单的包含选项的对象。查看监听器元数据。- 所有其他属性应视为内部属性。
response
- 标准response API 的所有部分都包含在中间件 API 中。查看发送和回复。
- 中间件可以用其他信息装饰(但不修改)响应对象(e.g. 例如将携带用户的 LDAP 组的属性添加到
response.message.user
) - 注意:文本消息(
response.message.text
)在监听器中间件中应该被认为是不可变的。
Receive 中间件
在任何监听器之行之前 Receive 中间件运行。它适用于尚未更新的黑名单命令以添加 ID、指标等。
Receive 中间件示例
这个简单的中间件禁止特定用户使用 hubot,包括hear
监听器。如果用户尝试明确地运行命令,它将返回错误消息。
1 | BLACKLISTED_USERS = [ |
Receive 中间件 API
Receive 中间件回调接收三个参数,context
、next
、done
。有关next
和done
的描述,请参阅中间件 API。Receive 中间件上下文包括这些字段:
response
- response 对象没有
match
对象,因为还没有监听器运行。 - 中间件可能通过附加信息(e.g. 添加一个携带用户的LDAP组的属性到
response.message.user
)来修饰response 对象。 - 中间件可能修改
response.message
对象。
- response 对象没有
Response 中间件
Response 中间件与 hubot 发送到聊天室的每个消息背道而驰。它有助于消息格式化、防止密码泄露、度量等。
Response 中间件示例
此简单示例将发送到聊天室的链接格式从markdown链接(如示例)更改为 Slack 支持的格式,https://example.com|示例。
1 | module.exports = (robot) -> |
Response 中间件 API
Response 中间件回调接收三个参数,context
、next
、done
。请参阅中间件 API 以了解next
和done
。Receive 中间件的上下文包括这些字段:
response
- 此响应对象可用于从中间件发送新消息。在这些新响应中,将调用中间件。小心不要创建无限循环。
strings
- 一个字符串数组被发送到聊天室适配器。您可以编辑这些,或者使用
context.strings = ["new strings"]
去替代它们。
- 一个字符串数组被发送到聊天室适配器。您可以编辑这些,或者使用
method
- 表示监听器发送的响应消息类型的字符串,例如
send
、reply
、emot
或者topic
。
- 表示监听器发送的响应消息类型的字符串,例如
plaintext
true
或者undefined
。这将被设置为true
,如果消息是正常的纯文本类型,例如send
和reply
。此属性应被视为仅读。
测试 hubot 脚本
hubot-test-helper是一个用来单元测试hubot脚本的好框架。(请注意,为了使用 hubot-test-helper,
,您需要一个支持Promises的最近Node版本。)
在 Hubot 实例中安装包:
1 | $ npm install hubot-test-helper --save-dev |
您还需要安装:
- 一个JavaScript测试框架,例如Mocha
- 一个断言库,例如chai或者expect.js
您或许还想安装:
- coffee-script(如果您用CoffeeScript写您的测试而不是JavaScript)
- 一个mocking库例如Sinon.js(如果您的脚本执行网络请求或其他异步操作)
下面是一个示例脚本,测试 Hubot 示例脚本中的前几个命令。这个脚本使用Mocha、chai、coffeescript,当然还有hubot-test-helper。
test/example-test.coffee
1 | Helper = require('hubot-test-helper') |
输出示例
1 | $ mocha --compilers "coffee:coffee-script/register" test/*.coffee |
模式
用共享模式处理常见hubot场景。
重命名hubot实例
当您重命名hubot时,他将不再回应他以前的名字。为了训练用户使用新名称,您可以选择在用户尝试说出旧名称时添加弃用通知。这里的模式逻辑是:
- 收听所有以旧名称开头的消息
- 回复用户,让他们知道新名称
设置这个非常简单:
- 在您的 hubot 实例的
scripts/
目录下创建一个捆绑脚本,叫rename-hubot.coffee
- 添加以下代码,根据您的需要修改:
1 | # Description: |
在上述模式中,修改 hubot 监听器和响应消息以满足您的需求。
此外,请务必注意,监听器应该基于 hubot 实际听到的内容,而不是在 hubot 适配器处理之前键入到聊天程序中的内容。例如,HipChat Adapter转换@hubot
为hubot:
在传递给hubot之前。
弃用或重命名监听器
如果您删除脚本或更改了脚本的命令,让用户知道更改可能会有用。一种方式是在聊天中告诉他们,或者让他们通过尝试使用不再存在的命令来发现变化。另一种方式是使 Hubot 让人们知道他们何时使用了不再工作的命令。
此模式类似于上述重命名 Hubot 实例模式:
- 收听与旧命令匹配的所有消息
- 回复用户,让他们知道,它已被弃用
设置如下:
- 在hubot实例的
scripts/
目录下创建一个叫deprecations.coffee
的捆绑脚本 - 复制一切旧的命令监听器,并将其添加到该文件中。例如,如果您出于某种糊涂的原因重新命名帮助命令:
1
2
3
4
5
6
7
8
9
10# Description:
# Tell users when they have used commands that are deprecated or renamed
#
# Commands:
# None
#
module.exports = (robot) ->
robot.respond /help\s*(.*)?$/i, (res) ->
res.reply "That means nothing to me anymore. Perhaps you meant `docs` instead?"
return
通过代理转发所有 HTTP 请求
在许多企业环境中,访问互联网和(或)受保护的资源需要网络代理。对于一次性的控制,可以指定一个Agent与robot.http
一起使用。但是,这需要修改您的机器人用于指向代理的每个脚本。相反,您可以在全局层次指定代理,并默认使所有 HTTP 请求使用代理。
由于 node.js 处理 HTTP 和 HTTPS 请求的方式,您需要为每一种协议指定一个不同的 Agent。ScopedHTTPClient 将自动的选择正确的 ProxyAgent 为每种请求。
- 安装 ProxyAgent。
npm install proxy-agent
- 在您的 hubot 实例的
scripts/
目录下创建一个捆绑脚本 - 添加后面的代码,并按需修改:
1 | proxy = require 'proxy-agent' |
消息的动态匹配
在某些情况下,您希望动态匹配不同的消息(e.g. factoids, JIRA projects)。而不是定义一个过于宽泛的总是匹配的正则表达,您可以告诉 hubot 只有在满足某些条件时才匹配。
在一个简单的机器人中,这与仅仅把条件放在监听器回调中没什么不同,但是在处理中间件时却有很大的不同:用基本的模型,正则表达式每次匹配中间件将执行。用这个动态匹配模型,中间件仅当动态条件匹配时执行。
例如,factoid lookup command可以被重新实现为:
1 | module.exports = (robot) -> |
限制命令访问
Hubot 的一个了不起的特色是它能够通过一条聊天消息改变生产环境。但是,并非每个访问您的聊天服务的人都能够触发生产环境改变。
有多种不同的模式来限制访问,您可以根据您的需求来选择:
- 两个访问 buckets:完整的和用黑白名单限制的
- 为每个命令定制访问规则(基于角色的权限控制)
- 特定房间的黑白名单命令
简单的每个监听器访问
在某些组织中,几乎所有员工都获得了相同级别的访问权限,只有少数员工需要受到限制(e.g. 新员工,外包员工等)。在这种模式下,您划分所有监听器集合,将“权力命令(power commands)”与“常规命令(normal commands)”分开。
一旦您将监听器分开了,您需要围绕黑白名单用户和监听器做出一些权衡。
选择用户白名单模式与用户黑名单模式的关键因素是:每个类别中的用户数量、任一类别的变化频率以及您的组织愿意接受的安全风险级别。
- 白名单用户模式(用户X、Y、Z有权访问权力命令;所有的其他用户只能访问常规命令)是一种更安全的访问方法(新用户没有默认的权限去执行权力命令),但有更高的维护成本(您需要将每个新用户添加到”审批”列表中)
- 黑名单用户模式(所有的用户都可以执行权力命令,除了用户X、Y、Z只能访问常规命令)是一种不太安全的方法(新的用户默认有权执行权力命令除非他们被加入了黑名单),却有较低的维护成本,在黑名单是小或者很少改变的情况下。
决定使用选择性的允许监听器和限制监听器的关键因素是:在每个分类中监听器的数量、内部和外部脚本的比例以及您的组织愿意接受的安全风险级别。
- 选择性的允许监听器(所有的监听器都是权力命令,除了监听器A、B、C被视为常规命令)是更安全的(新的监听器默认被限制),但维护成本却高得多(每个愚蠢/有趣的监听器需要被明确的降级到“常规”状态)。
- 选择性的限制监听器(监听器A、B、C是权力命令,其它的都是常规命令)是安全性更差的一种方式(新的监听器默认被放进常规分类,它能够被意想不到的访问;外部脚本在这里特别可怕),不过有更低的维护成本(无需修改/列举访问策略中的所有fun/culture脚本)。
作为额外的考虑,大多数脚本目前没有监听器 ID,因此您可能需要打开您使用的外部脚本的 PRs(或 fork)来添加监听器 ID。实际修改很容易,但与许多维护人员协调可能很费时。
一旦您决定遵循这四种模型的某一种,您需要构建适当的用户和监听器列表,以插入到您的授权中间件。
例如:用白名单用户模式去选择性的限制权力命令。
1 | POWER_COMMANDS = [ |
记住所有匹配给定消息的监听器(包括robot.hear /.+/
)执行的中间件,在对监听器进行分类时,请确保包含它们。
每个监听器特定的访问规则
对于较大的组织,访问的二元分类通常是不够的,需要更复杂的访问规则。
访问策略示例:
- 每个开发团队都有权编译处于某个状态的代码(cut releases)并部署其服务
- 运营组有权部署所有服务(但是不能 cut releases)
- 前台无法cut releases或部署服务
像这样的复杂政策目前最好直接在代码中实现,尽管这是正在进行中的工作为访问管理建立一个通用的框架。
每个房间特定的访问规则
拥有多个不同用途的聊天室的组织通常希望能够使用相同的 hubot 实例,但每个房间允许使用不同的命令集。
广泛的黑名单的解决方案正在进行中。白名单的解决方案可以采取类似的方法。
适配器
适配器是您希望 hubot 所运行的服务的界面。
Hubot 包含两个官方的适配器:
对于大多数聊天服务,有许多第三方适配器。这是其中最受欢迎的一些:
浏览所有的仓库通过在 Github 上的 hubot-adapter
topic 或者在 NPM 上搜索适配器。添加hubot-adapter
topic到您Github上的个人仓库,以将其包含在此列表中。
编写您自己的适配器
有兴趣添加自己的适配器?查看我们用于开发适配器的文档。
Campfire 适配器
Campfire是一个由37signals构建的基于 web 的聊天应用。Campfire 是 hubot 的一个原装适配器。
起步
您将需要一个Campfire帐户来开始,你可以免费注册。
下一步,你需要为你的 hubot 在 Campfire 账户上创建一个用户,然后给它访问权限,以便它可以加入您的房间。如果您尚未创建房间,则需要创建一个房间。
Hubot 默认使用它的 shell,用 Campfire 替代,你需要带上 -a campfire
运行 hubot:
1 | bin/hubot -a campfire |
如果你部署到 Heroku 或者使用 foreman,你需要确保在 Procfile
中携带-a campfire
被调用:
1 | web: bin/hubot -a campfire -n Hubot |
配置
适配器需要以下环境变量:
HUBOT_CAMPFIRE_ACCOUNT
HUBOT_CAMPFIRE_TOKEN
HUBOT_CAMPFIRE_ROOMS
Campfire API 令牌
这可以通过登录您的 hubot 的帐户,点击 My Info
链接来找到, 并记下 API 令牌。
Campfire 房间号
如果你加入了你希望你的hubot加入的房间,你可以看见在URL中的房间的数字 ID。记下你想你的 hubot 加入的每个房间 ID。
Campfire 账户
这只是您访问 Campfire 帐户的域名的第一部分。例如,如果您的 Campfire 是hubot.campfirenow.com
,你的二级域名是hubot
。记下这个二级域名。
在 Heroku 上配置环境变量
1 | heroku config:set HUBOT_CAMPFIRE_TOKEN="..." |
在 Unix 上配置环境变量
1 | export HUBOT_CAMPFIRE_TOKEN="..." |
在 Windows 上配置环境变量
使用 PowerShell:
1 | setx HUBOT_CAMPFIRE_TOKEN "..." /m |
Shell 适配器
Shell 适配器提供一个简单的读取打印循环,用于与本地 hubot 进行交互。它可用于在hubot 上使用脚本之前测试脚本。
起步
要使用外壳适配器,您可以在运行 hubot 时简单地省略 -a 选项,因为它默认会使用 Shell 适配器。
1 | bin/hubot |
配置
这个适配器不需要任何配置。
它支持两个环境变量以支持作为不同的用户测试脚本:
- HUBOT_SHELL_USER_ID: default is 1
- HUBOT_SHELL_USER_NAME: default is Shell
开发适配器
适配器基础
所有适配器继承自在 src/adapter.coffee
文件中的适配器类。有明确的你想去重写的方法。下面是扩展适配器类的基本桩代码:
1 | class Sample extends Adapter |
配置您的开发环境
- 为您的适配器
hubot-sample
创建一个新的文件夹。mkdir hubot-sample
- 切换您的工作路径到
hubot-sample
cd hubot-sample
- 运行
npm init
去创建您的 package.json- 确保入口是
src/sample.coffee
- 确保入口是
- 添加您的
.gitignore
去包含node_modules
。 - 编辑
src/sample.coffee
文件为您的适配器去包含上述的桩代码。 - 编辑
package.json
去为hubot添加 peer 依赖。1
2
3
4
5
6
7
8"dependencies": {
},
"peerDependencies": {
"hubot": ">=2.0"
},
"devDependencies": {
"coffee-script": ">=1.2.0"
} - 用
yo hubot
命令生成您的hubot。 - 切换工作目录到
hubot
你在第七步创建的。 - 现在执行
npm link
去添加您的适配器到hubot
npm link ../hubot-sample
- 运行
hubot -a sample
。
Gochas
在 node 社区有一个开放的议题围绕npm 链接的 peer 依赖不工作。要让我们的项目工作,您的代码需要做一些小的修改。
- 引入您的
hubot-sample
适配器,添加下面的代码:1
2
3
4
5try
{Robot,Adapter,TextMessage,User} = require 'hubot'
catch
prequire = require('parent-require')
{Robot,Adapter,TextMessage,User} = prequire 'hubot' - 在您的
hubot-sample
文件夹,修改package.json
去包含下面的依赖以至于这种自定义导入机制将起作用1
2
3"dependencies": {
"parent-require": "^1.0.0"
} - 现在尝试再运行
hubot -a sample
并看到引入正常加载了。 - 一旦这工作正常,您可以根据您认为合适的情况构建适配器的功能。看看其他一些适配器,获取一些实现想法。
- 一旦通过
npm
进行打包和部署,您就不再需要在hubot
中的依赖性了,因为 peer 依赖应该作为官方模块工作。
- 一旦通过
部署
部署到 Heroku
如果你一直跟着官方文档到了这里,是时候部署了,这样你就不仅在你的本地机器能使用它了。Heroku是部署hubot的最简单和支持的方式。
安装 Heroku Toolbelt 去开始,然后跟着他们的“Getting Started”操作指南,包括第一次登录:
1 | $ heroku login |
在新的 hubot 目录中,请确保您创建了 git 仓库,并且您的代码已经被 commit:
1 | git init |
创建一个 Heroku 应用:
1 | heroku create |
在您部署应用之前,您需要配置一些环境变量,供 hubot 使用。您需要的特定变量取决于您正在使用的适配器和脚本。对于 Campfire,没有其它脚本的话,你需要去设置以下环境变量:
1 | heroku config:set HUBOT_CAMPFIRE_ACCOUNT=yourcampfireaccount |
此时,您已准备好部署并开始聊天。使用 Heroku,就是执行 git push:
1 | git push heroku master |
您会看到一些文本飞过,最终是成功的。此时您应该可以在您配置的聊天室,看见您的机器人。如果没有,您可以看一看日志去调试:
1 | heroku logs |
如果您对您的 hubot 做了一些改变,在 push 它们之前,commit 一下就好:
1 | git commit -am "Awesome scripts OMG" |
一些脚本需要 redis 来运行,Heroku 提供一个叫 Redis Cloud 的附加装置,它是有免费计划的。使用它:
1 | heroku addons:create rediscloud |
在Heroku上的免费的dyno在不活跃30分钟后将休眠。那意味着您的hubot将离开聊天室并且仅重新加入当它获得流量。这是极度不方便的因为大多数交互都是通过聊天完成的,hubot 必须在线、在房间里回复消息。解决这个问题的一个临时方案是,你可以使用hubot-heroku-keepalive脚本,它可以让您免费的dyno活跃时间提升到18小时每天。如果你不想你的 hubot 休眠,你可能就需要升级到 Heroku 的 bobby 计划。
部署到 Unix
由于 Linux 的变种很多,以及 UNIX,因此 hubot 团队很难拥有规范文档,用于安装和部署到每个版本。因此,这试图概述部署所需的内容。
有3个主要的东西去部署和运行 hubot:
- node 和 npm
- 在服务器上更新源代码的方法
- 启动 hubot 的方法, 在崩溃时启动它, 并在代码更新时重新启动它
node 和 npm
首先,您的 UNIX 服务器将需要 node 和 npm。查看node.js 的 wiki:通过包管理器安装 Node.js,在 GNU/Linux 和 其它 UNIX 上编译。
在服务器上更新代码
更新 hubot 代码的最简单方法是对 hubot 的源代码进行 git checkout(您在阅读官方文档期间创建的源代码,而不是 github/hubot 仓库),只需 git pull 即可获取更改。这可能感觉不够优雅,但是刚开始的时候是有用的。
如果您有 Ruby 背景,使用 capistrano 会让您感到更舒服。
启动、停止和重启 hubot
每个hubot安装有一个bin/hubot
脚本来处理启动hubot。您可以在服务器上的 git checkout 处运行此命令,但是您可能会遭遇一些问题:
- 你断开终端连接,hubot 挂掉
- hubot 因为一些原因挂掉,不会自己重新启动
- 它不会开机自启
要处理断开连接,你可以在screen session中运行bin/hubot
,或者使用nohup。
要处理 hubot 挂掉,和自动重启,你可以想象运行bin/hubot
在一个bash while 循环。但真的,你可能想要一些进程监控工具, 如 monit、god、bluepill、upstart、runit、 systemd。
要处理开机自启,您可以创建适合 UNIX 分发的初始化脚本。如果您正在使用上面的进程监控工具之一,请确保它在启动时启动。有关配置示例,请参阅示例。
建议
本文档有意淡化强烈建议。不过在高层次上,强烈建议避免任何过于手动和不可复现的东西。尽可能使用操作系统的包和工具,并使用适当的部署工具来更新 hubot,以及进程管理以保持 hubot 的运行。
部署到 Windows
尚未完全测试 —— 你的历程可能有所不同(YMMV)
在 Windows 机器上,有四个主要的部署运行 hubot 的步骤:
- node 和 npm
- 在服务器上更新源代码的方法
- 为 hubot 设置环境变量
- 启动 hubot, 在崩溃时启动它,在代码更新时重新启动它的方法
node 和 npm
首先,您的 windows server 需要 node 和 npm。最佳方式是用 chocolately 使用 nodejs.install 包 。我发现在系统上的 path 变量没有设置正确;确保你能通过命令行运行 node/npm。如果你需要设置 path 变量,就用“setx PATH “%PATH%;C:\Program Files\nodejs””。
你也可以直接通过NodeJS 安装,这可以为你设置path 变量。
译者注:chocolately 和 scoop 都是 windows 下的包管理软件,译者使用的是 scoop。
更新服务器上的代码
在您的本地机器或直接在服务器上,去获取您服务器上的代码,您可以跟着在官方文档上的操作指南。如果您本地构建,推送您的hubot到Github并且克隆仓库到您的服务器上。不要克隆这标准的github/hubot repository,确保您用 Yo Generator 去构建您自己的 hubot。
设置环境变量
您会想设置您的 hubot 环境变量,在它运行的服务器上。您可以实现这个通过打开一个有管理员权限的 PowerShell 并且键入如下:
1 | [Environment]::SetEnvironmentVariable("HUBOT_ADAPTER", "Campfire", "Machine") |
这相当于去系统菜单 -> 选择高级系统设置 -> 环境变量并且添加一个新的系统变量叫 HUBOT_ADAPTER,值为 Campfire。
启动、停止和重启 hubot
每个hubot安装有一个 bin/hubot
脚本去处理hubot的启动。您可以在您的 hubot 的文件夹下直接执行以下命令:
1 | .\bin\hubot –adapter campfire |
不过,如果您手动调用,则存在一些问题。
- 你断开连接,hubot 也挂掉
- hubot 因为某些原因挂掉,不会重启
- hubot 不开机自启
解决这些,您可能想创建一个您将从您的hubot路径调用的 .ps1 文件,用您喜欢的名字。此examples
目录中有此文件的副本。它应包含以下内容:
1 | Write-Host "Starting Hubot Watcher" |
如果您使用 .ps1 文件来运行 hubot,记得允许本地未签名的 PowerShell 脚本。在一个管理员权限的 PowerShell 窗口运行下面的命令:
1 | Set-ExecutionPolicy RemoteSigned |
如果您想用一些其它的方式去启动您的进程,您可以设置这个 .ps1 文件为一个预启动项。
扩充文档
还不够完整,感谢您,帮助贡献提交一个 pull request?
部署到 Azure
如果你一直跟随着本文档到现在。是时候部署了,这样您就可以在本地机器之外使用它。Azure是部署 hubot 的一种方式,是 Heroku 的替代方案。
按照hubot的初始说明进行操作后,您将需要通过npm安装azure-cli。
1 | $ npm install -g azure-cli |
在新的 hubot 目录中,请确保您创建了 git 仓库,并且您代码已经 commit:
1 | git init |
然后,为您的 hubot 创建一个 Github repository。Azure 就可以从这个 git 仓库,而不是直接从您的开发机器拉取代码了。
1 | git remote add origin _your GitHub repo_ |
一旦您拥有了您的 Github 仓库,创建一个 Azure 网站链接到您的仓库。在 Azure,创建一个网站并且选择源码控制。当它询问“您的源码控制在哪里”,选择 Github 并且链接这个网站到您在上一步创建的 git 仓库。如果你下载过 Azure PowerShell 模块,你可以通过PowerShell 做这些。
1 | $ $creds = Get-Credential |
一旦您完成了这个,Azure 将在您任何时间推送代码到 Github 部署您的网站。不过您的 hubot 还不能正常运行。下一步,您需要您配置部署, 告诉 Azure 如何运行hubot。
首先,运行下面的命令去添加 deploy.cmd
到您的hubot路径。这是 Azure 用来知道如何部署 node 应用程序的文件。
1 | azure site deploymentscript --node |
然后,编辑此文件并查找为您提供步骤 1、2 和 3 的部分。您将添加第 4 步:
1 | :: 4. Create Hubot file with a coffee extension |
现在,在hubot的根路径下创建一个新文件叫server.js
,并添加下列两行:
1 | require('coffee-script/register'); |
最后,您需要添加环境变量到网站去确保它能够运行。下面的配置,您也可以通过图像界面来配置,或者用 Azure PowerShell 命令行,如下所示(示例显示:适配器为slack, 网站名称为mynewhubot)。
1 | $ $settings = New-Object Hashtable |
Commit 你的改变在git 仓库,并推送到Github,Azure 将自动获取改变并且部署到网站。
1 | $ git commit -m "Add Azure settings for hubot" |
Azure 提供了一个市场,您可以在其中使用 Redis Labs 提供的 Redis Cloud 使用默认的 heroku-redis-brain。或者,要添加 Azure 存储大脑,您需要创建 Azure 存储帐户和帐户密钥。然后您可以在您的 hubot 根目录中执行以下操作。
1 | npm install hubot-azure-scripts --save |
然后 external-scripts.json
中添加以下行以及其它外部脚本
1 | "hubot-azure-scripts/brain/storage-blob-brain" |
最后,在您的网站中再添加两个环境变量。您可以通过图形界面或以下PowerShell命令做到这一点。
1 | $ $settings = New-Object Hashtable |
现在任何需要大脑的脚本都可以运行。您应该查找其他脚本或通过查看文档编写自己的脚本。 Hubot 的所有普通脚本都与 Azure 上的托管 hubot 兼容。
故障排除提示和技巧
由于 Azure 基于 Windows,您可能会遇到路径长度问题。要解决此问题,您可以将环境变量 IN_PLACE_DEPLOYMENT
设置为 1,并使用自定义部署脚本来利用 NPM3 和 flat 模块安装。
如果使用 Azure 的免费 tier,还可以添加部署后步骤,通过使用诸如 startup.sh
之类的脚本(相对于 src 目录)设置环境变量 POST_DEPLOYMENT_ACTION
来在启动时 ping 服务器。
启动脚本示例:
1 | let retrys=0 |