【ChatOps系列】ChatOps简介

要说 ChatOps 就不得不说 DevOps,DevOps 是来源于 Development 和 Operations 的一个组合词,顾名思义,是一系列过程、方法与系统的统称,旨在促进开发、测试和运维人员之间的沟通与协作。简单来说,是通过引入一系列的「工具」,通过三种不同角色的开发成员间的「协作」而实现的一种「自动化」的工作模式。这种工作方式带来的好处显而易见:

  • 实现持续快速交付
  • 能够降低人力成本

但很大程度上,DevOps 更多是指开发群体之间的一种协作模式(通常也在开发人员中实施),随着全行业的发展和人力成本的攀升,在团队所有角色间贯通的升级版「DevOps」逐渐登场,也就是我们将要重点介绍的 ChatOps。

devops

ChatOps介绍

ChatOps 的历史相对短暂,2013 年 GitHub 内部最早开始推行 ChatOps,希望能以聊天的方式更容易更快速的去完成 DevOps 承载的工作。

ChatOps 以聊天室,即沟通平台为中心,通过一系列的机器人去对接后台的各种服务,工作人员只需要在聊天窗口中与机器人对话,即可与后台服务进行交互,整个工作的展开就像是使唤一个智能助手那样简单自然。

chatops_devops

ChatOps 站在巨人的肩膀上发展,也为工作带来了显而易见的好处:

  • 公开透明。所有的工作消息都在同一个聊天平台中沉淀并公开给所有相关成员,消除沟通壁垒,工作历史有迹可循,团队合作更加顺畅。
  • 上下文共享。减少因工作台切换等对消息的截断,保证消息的完整性,让工作承接有序,各角色,各工具都成为完成工作流中的一环,打造真正流畅的工作体验。
  • 移动友好。只需要在前台与预设好的机器人对话即可完成与后台工具、系统的交互,在移动环境下无需再与众多复杂的工具直接对接,大大提升移动办公的可行性。
  • DevOps 文化打造。用与机器人对话这种简单的方式降低 DevOps 的接受门槛,让这种自动化办公的理念更容易的扩展到团队的每一个角落。

chatops

ChatOps 主要由三个部分构成:聊天室(控制中心)、机器人(连接中心)、基础设施,基础设施主要是支撑我们业务运行的各种服务与工具,在构建 ChatOps 时主要需要选择聊天室和机器人,国外早期的工作沟通工具 HipChat,新秀 Slack 都是作为 ChatOps 承载平台的好选择,在中文的环境下,则可以选择 BearyChat(倍洽)等等。

聊天室选择:

  • 功能完备
  • 操作交互友好
  • 多平台支持
  • API 丰富
  • 三方工具支持丰富
  • 机器人支持程度

机器人选择:

  • 功能完备
  • 易扩展(我们可能需要根据基础设施编写大量机器人扩展)
  • 编程语言熟悉(因为我们需要编写扩展)
  • 开源扩展丰富(可以极大减少开发工作量)

聊天室主要有:Slack、HipChat、BearyChat(倍洽)、Rocket.Chat、钉钉(机器人支持程度不够,不太支持)。机器人主要有:Hubot(javascript/CoffeeScript)、Lita(Ruby),Errbot(python)。

机器人我推荐使用 Hubot,后面所有的实验都使用 Hubot 展开。 GitHub 团队内部实现的 ChatOps 与一个叫做 Hubot 的机器人框架密切相关,Hubot 提供很多聊天机器人所需的基础设施,借助 Hubot 框架能比较方便的和自己编写的功能或自己的系统对接。目前,Hubot 已经发展出了较好的生态圈,有很多开源插件可以借用。

chatops

ChatOps实现

本系列专题主要讲两种实现,一种是基于 GitHub+Hubot+Slack 实现,一种基于开源体系的 GitLab + Hubot + Rocket.Chat,它们功能完善且有良好的扩展及丰富的API,只要爱倒腾一定会有意想不到的收获。

GitHub 体系:

  • slack 聊天室
  • Hubot 机器人
  • GitHub 代码管理
  • Travis CI 自动化测试
  • heaven 自动部署
  • hubot-deploy 连接 Hubot 与 heaven

GitLab 体系:

  • Recket.chat 聊天室
  • Hubot 机器人
  • GitLab 代码管理
  • GitLab CI 自动化测试
  • hubot-gitlab-deploy 自动化部署(计划重构中)
  • fabric-deploy-script 基于配置的 fabric 自动部署实现 (计划开源中)

本系列会介绍各种设计服务安装、配置,以及各种服务组件之间连接配置等,同时会涉及到项目配置管理、密钥管理等相关知识。

计划

本系列主要涉及 ChatOps 环境搭建、工具配置,以及项目的持续集成、持续部署的实现,持续部署过程会涉及到密钥管理、配置管理等等。

本系列计划一至两周一篇文章,每篇文章介绍一个点,两种体系都会讲到,大约20~30篇文章,优先讲解开源体系的 GitLab 篇。

在这期间会开发相关机器人脚本及相关服务组件,可以形成项目会发布在 GitHub 上,不能形成项目的会在文章中,最后所有实验相关代码均可在 GitHub 上 chatops_experiment 中获取。

测试中需要自动部署的项目会单独存储在一个账号/组织下面,具体详见每个章节。

salt-api使用

启用salt-api 服务

这里简单的说明下,SaltStack官方支持三种REST API,分别是rest_cherry; rest_tonado和rest_wsgi, 本文选择使用rest_cherry模块来实现SaltStack的HTTP API。

配置salt-API服务

yum -y install salt-api
useradd -M -s /sbin/nologin saltapi
echo "saltapi" | passwd saltapi --stdin
vim /etc/salt/master
external_auth:
  pam:
    saltapi:
      - .*
      - '@wheel'
      - '@runner'

rest_cherrypy:
  port: 8000
  disable_ssl: Ture
  
  systemctl restart salt-master 
  systemctl restart salt-api 

使用CURL测试salt-API接口

第一步登录获得token,后续的命令使用token直接执行。

curl  -sk http://192.168.56.11:8000/login -H 'Accept: application/x-yaml' -d username='saltapi' -d password='saltapi' -d eauth='pam'

return:
- eauth: pam
  expire: 1522883646.381435
  perms:
  - .*
  - '@wheel'
  - '@runner'
  start: 1522840446.381431
  token: 86e323effd30bed7b2cdbcf4e70744048bbc25ca
  user: saltapi

第二步执行如下命令:salt 'linux-node2' test.ping

curl -sk http://192.168.56.11:8000/ -H 'Accept: application/json' -H 'X-Auth-Token: 86e323effd30bed7b2cdbcf4e70744048bbc25ca' -d client='local' -d tgt='linux-node2' -d fun='test.ping'  | python -mjson.tool  
{
    "return": [
        {
            "linux-node2": true
        }
    ]
}

执行带参数的命令:salt 'linux-node2' cmd.run 'free -m'

curl -sk http://192.168.56.11:8000/ -H 'Accept: application/json' -H 'X-Auth-Token: 86e323effd30bed7b2cdbcf4e70744048bbc25ca' -d client='local' -d tgt='linux-node2' -d fun='cmd.run' -d arg='whoami' | python -mjson.tool
{
    "return": [
        {
            "linux-node2": "root"
        }
    ]
}

salt API /run 接口

运行绕过正常会话处理的命令除此之外,该URL与根URL(/)相同。

curl -sk http://192.168.56.11:8000/run \
    -H 'Accept: application/x-yaml' \
    -H 'Content-type: application/json' \
    -d '[{
        "client": "local",
        "tgt": "linux-node2",
        "fun": "test.ping",
        "username": "saltapi",
        "password": "saltapi",
        "eauth": "pam"
    }]' 
return:
- linux-node2: true

使用python 实现如下命令

salt 'linux-node2' cmd.run 'free -m'
import requests

url='http://192.168.56.11:8000/login'
username = 'saltapi'
password = 'saltapi'

data = {'username': username,'password': password,'eauth': 'pam'}
headers = {'Accept': 'application/json','Content-Type': 'application/x-www-form-urlencoded'}
login_req = requests.post(url=url, headers=headers, data=data)
js = login_req.json()
token = js['return'][0].get('token')
print(token)


cmd_headers = {'Accept': 'application/json','Content-type': 'application/x-www-form-urlencoded', 'X-Auth-Token': token}
cmd_data = {'client': 'local', 'tgt': 'linux-node2', 'fun': 'cmd.run', 'arg': 'free -m '}
urls = 'http://192.168.56.11:8000'
req = requests.post(urls,headers=cmd_headers,data=cmd_data)
print(req.status_code)

if req.status_code == 200:
	print(req.json())

执行结果如下:

ab5d9ea0f428367c79f8eaf3dc62d39b1c45b4c9
200
{'return': [{'linux-node2': '              total        used        free      shared  buff/cache   available\nMem:            979         607          76          49         295         172\nSwap:             0           0           0'}]}

使用python编写过程中遇到的问题:

  • curl 请求时,默认会增加Content-type': 'application/x-www-form-urlencoded 的头部,所以在request请求头中也需要加入。

Jenkins教程 安装BlueOcean与Maven构建

前言

本文旨在使用BlueOcean实现构建可视化与使用Maven构建上一节Jenkins教程(三)添加凭据与流水线拉取Git代码拉下来的代码

什么是Blue Ocean

Blue Ocean 重新思考Jenkins的用户体验,从头开始设计Jenkins Pipeline, 但仍然与自由式作业兼容,Blue Ocean减少了混乱而且进一步明确了团队中每个成员 Blue Ocean 的主要特性包括:

  • 持续交付(CD)Pipeline的 复杂可视化 ,可以让您快速直观地理解管道状态。
  • Pipeline 编辑器 – 引导用户通过直观的、可视化的过程来创建Pipeline,从而使Pipeline的创建变得平易近人。
  • 个性化 以适应团队中每个成员不同角色的需求。
  • 在需要干预和/或出现问题时 精确定位 。 Blue Ocean 展示 Pipeline中需要关注的地方, 简化异常处理,提高生产力
  • 本地集成分支和合并请求, 在与GitHub 和 Bitbucket中的其他人协作编码时实现最大程度的开发人员生产力。

简言之:简化复杂可视化,提供更个性直观的界面,可以精准定位构建失败的位置

安装Blue Ocean

  • 准备条件:是管理员用户,或拥有安装插件的权限

Manage Jenkins > Manage Plugins

过滤输入Blue Ocean> 勾选第一个Blue Ocean > 直接安装 > 安装完成返回首页

这里提示有很多Blue Ocean的插件,我们是无需手动安装的,通过安装Blue Ocean会自动安装官方推荐的其它组件

此时我们可以使用 <host>:<port>/blue进行访问到Blue Ocean的页面

这里先不讲,配置好Maven后,我们再来使用它来构建一次

使用Maven构建工具

这里使用docker的maven镜像来进行构建工程,以减少多个构建工程使用同一构建工具时的冲突问题

另一方面是演示下agent的使用😆

准备条件:

  • 安装Docker
  • 添加Jenkins到Docker用户组sudo usermod -aG docker jenkins && newgrp docker
  • 重启Jenkins

创建自定义maven的配置文件/home/jenkins/mvnrepo/settings.xml,使用了阿里的镜像源,本地仓库这个不用改,这里会被容器映射到容器内/root/.m2

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
  <mirrors>
    <mirror>
        <id>nexus-aliyun</id>
        <mirrorOf>*</mirrorOf>
        <name>Nexus aliyun</name>
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
  </mirrors>
</settings>

在Github的仓库中我创建了chapter2分支,里面的Jenkinsfile如下

pipeline {
    agent { //这里使用docker镜像来启动maven,这样有个好处就是多个工程同时构建时不会出现冲突而失败
        docker {
            image 'maven:3.6-alpine' 
            args '-u root -v /home/jenkins/mvnrepo:/root/.m2'  //持载到本地,减少重复下载量,使用ali源
        }
    }
    stages {
        stage('Pull Git Demo') {
            steps{
                //拉取代码
            	git 'https://github.com/hellxz/springboot-demo1.git'
            }
        }
        stage('Build') { 
            steps {
                //执行构建命令
                sh 'mvn -B -DskipTests clean package' 
            }
        }
    }
}

持载的那个位置下的settings.xml会被maven容器使用,从而修改镜像源,另外在settings.xml中指不指定本地仓库都没区别,亲测

创建流水线

指定Jenkinsfile所在的版本控制与分支,这里我使用的是https://github.com/hellxz/JenkinsfileTutorials.git

分支是chapter2,检出到chapter2目录下,Jenkinsfile直接就在仓库的根目录内

启动测试

输入http://<host>:<port>/blue, 点击刚才创建的maven build demo项目

点击运行

构建成功,我们查看Build步骤,可以看到构建成功了

由于在公司里我使用的是本地配置的maven,所以查hub.docker.com,对Jenkinsfile中使用maven镜像的部分修改了几次,不然你会看到拉取的jar包都是从阿里源中拉出来的

查看构建目录

本文结束

后记

被maven-docker的文档小坑了一下,文档中提到的自定义settings.xml文件是持载到另一个目录下

还有就是maven容器内使用的用户是非root用户,如果按官方文档中不加-u root,那么容器内的/root/.m2目录是持载不出来的!

当前我们的构建属于脏构建,即上次的构建结果与代码拉取结果都在同一个工作目录中,这样是需要修改的,现在为了简单演示没有添加,可以在拉代码前加一个清理空间的步骤,使用cleanWs()命令

下篇准备写把微服务项目打成Docker镜像,静请期待

引文

saltstack中如何实现多个master来管理minion

背景:

公司有多个部门,有一些机器有本部门的业务,这些机器也有其他部门的业务,所以本部门需要一个master服务器来管理这批机器,其他部门也需要一个master服务器来管理这个机器,所以就需要多个master来进行管理。

实现:

实现方式就是所有的master服务器使用相同的private key,即我们只需要将其中一台已经配置好的master上的private key进行复制,然后拷贝到另一台master服务器上即可,然后在minion端配置好minion id即可,配置完成后需要重启下服务。

示例:

准备了三台机器

机器列表 状态
192.168.235.133 master1
192.168.235.134 minion
192.168.235.135 master2

1)将master2上的private key进行拷贝至master1上

复制代码
[root@localhost master]# ll
总用量 8
-r--------. 1 root root 1679 1月   2 21:19 master.pem    #private key
-rw-r--r--. 1 root root  451 1月   2 21:19 master.pub
drwxr-xr-x. 2 root root   15 1月   2 21:20 minions
drwxr-xr-x. 2 root root    6 1月   2 21:19 minions_autosign
drwxr-xr-x. 2 root root    6 1月   2 21:19 minions_denied
drwxr-xr-x. 2 root root    6 1月   2 21:20 minions_pre
drwxr-xr-x. 2 root root    6 1月   2 21:19 minions_rejected

 private key 所在目录:/etc/salt/pki/master

2)修改minion端的配置文件

 

书写格式一定要写对

3)然后在master1上进行密钥认证

[root@localhost master]# salt-key -L
Accepted Keys:
Denied Keys:
Unaccepted Keys:
m1
Rejected Keys:
[root@localhost master]# salt-key -A
The following keys are going to be accepted:
Unaccepted Keys:
m1
Proceed? [n/Y] Y
Key for minion m1 accepted.

4)重启服务,测试即可

需要注意的是:另一个master不能有其他节点服务器连接

以上操作便实现了多个master共同管理minion节点的需求了

Python 的 ChatOps 库:Opsdroid 和 Errbot

学习一下 Python 世界里最广泛使用的 ChatOps 库:每个都能做什么,如何使用。

ChatOps 是基于会话导向而进行的开发。其思路是你可以编写能够对聊天窗口中的某些输入进行回复的可执行代码。作为一个开发者,你能够用 ChatOps 从 Slack 合并拉取请求,自动从收到的 Facebook 消息中给某人分配支持工单,或者通过 IRC 检查开发状态。

在 Python 世界,最为广泛使用的 ChatOps 库是 Opsdroid 和 Errbot。在这个月的 Python 专栏,让我们一起聊聊使用它们是怎样的体验,它们各自适用于什么方面以及如何着手使用它们。

Opsdroid

Opsdroid 是一个相对年轻的(始于 2016)Python 开源聊天机器人库。它有着良好的开发文档,不错的教程,并且包含能够帮助你对接流行的聊天服务的插件。

它内置了什么

库本身并没有自带所有你需要上手的东西,但这是故意的。轻量级的框架鼓励你去运用它现有的连接器(Opsdroid 所谓的帮你接入聊天服务的插件)或者去编写你自己的,但是它并不会因自带你所不需要的连接器而自贬身价。你可以轻松使用现有的 Opsdroid 连接器来接入:

  • 命令行
  • Cisco Spark
  • Facebook
  • GitHub
  • Matrix
  • Slack
  • Telegram
  • Twitter
  • Websocket

Opsdroid 会调用使聊天机器人能够展现它们的“技能”的函数。这些技能其实是异步 Python 函数,并使用 Opsdroid 叫做“匹配器”的匹配装饰器。你可以设置你的 Opsdroid 项目,来使用同样从你设置文件所在的代码中的“技能”。你也可以从外面的公共或私人仓库调用这些“技能”。

你同样可以启用一些现存的 Opsdroid “技能”,包括 seen —— 它会告诉你聊天机器人上次是什么时候看到某个用户的,以及 weather —— 会将天气报告给用户。

最后,Opdroid 允许你使用现存的数据库模块设置数据库。现在 Opdroid 支持的数据库包括:

  • Mongo
  • Redis
  • SQLite

你可以在你的 Opdroid 项目中的 configuration.yaml 文件设置数据库、技能和连接器。

Opsdroid 的优势

Docker 支持:从一开始 Opsdroid 就打算在 Docker 中良好运行。在 Docker 中的指导是它 安装文档 中的一部分。使用 Opsdroid 和 Docker Compose 也很简单:将 Opsdroid 设置成一种服务,当你运行 docker-compose up 时,你的 Opsdroid 服务将会开启你的聊天机器人也将就绪。

version: "3"

services:
  opsdroid:
    container_name: opsdroid
    build:
      context: .
      dockerfile: Dockerfile

丰富的连接器: Opsdroid 支持九种像 Slack 和 Github 等从外部接入的服务连接器。你所要做的一切就是在你的设置文件中启用那些连接器,然后把必须的口令或者 API 密匙传过去。比如为了启用 Opsdroid 以在一个叫做 #updates 的 Slack 频道发帖,你需要将以下代码加入你设置文件的 connectors 部分:

- name: slack
    api-token: "this-is-my-token"
    default-room: "#updates"

在设置 Opsdroid 以接入 Slack 之前你需要添加一个机器人用户

如果你需要接入一个 Opsdroid 不支持的服务,在文档里有有添加你自己的连接器的教程。

相当不错的文档: 特别是对于一个在积极开发中的新兴库来说,Opsdroid 的文档十分有帮助。这些文档包括一篇带你创建几个不同的基本技能的教程。Opsdroid 在技能连接器数据库,以及匹配器方面的文档也十分清晰。

它所支持的技能和连接器的仓库为它的技能提供了富有帮助的示范代码。

自然语言处理: Opsdroid 的技能里面能使用正则表达式,但也同样提供了几个包括 Dialogflowluis.aiRecast.AI 以及 wit.ai 的 NLP API。

Opsdroid 可能的不足

Opsdroid 对它的一部分连接器还没有启用全部的特性。比如说,Slack API 允许你向你的消息添加颜色柱、图片以及其他的“附件”。Opsdroid Slack 连接器并没有启用“附件”特性,所以如果那些特性对你来说很重要的话,你需要编写一个自定义的 Slack 连接器。如果连接器缺少一个你需要的特性,Opsdroid 将欢迎你的贡献。文档中可以使用更多的例子,特别是对于预料到的使用场景。

示例用法

from opsdroid.matchers import match_regex
import random


@match_regex(r'hi|hello|hey|hallo')
async def hello(opsdroid, config, message):
    text = random.choice(["Hi {}", "Hello {}", "Hey {}"]).format(message.user)
    await message.respond(text)

hello/__init__.py

connectors:
  - name: websocket

skills:
  - name: hello
    repo: "https://github.com/<user_id>/hello-skill"

configuration.yaml

Errbot

Errbot 是一个功能齐全的开源聊天机器人。Errbot 发行于 2012 年,并且拥有人们从一个成熟的项目能期待的一切,包括良好的文档、优秀的教程以及许多帮你连入现有的流行聊天服务的插件。

它内置了什么

不像采用了较轻量级方式的 Opsdroid,Errbot 自带了你需要可靠地创建一个自定义机器人的一切东西。

Errbot 包括了对于本地 XMPP、IRC、Slack、Hipchat 以及 Telegram 服务的支持。它通过社区支持的后端列出了另外十种服务。

Errbot 的优势

良好的文档: Errbot 的文档成熟易读。

动态插件架构: Errbot 允许你通过和聊天机器人交谈安全地安装、卸载、更新、启用以及禁用插件。这使得开发和添加特性十分简便。感谢 Errbot 的颗粒性授权系统,出于安全意识这所有的一切都可以被锁闭。

当某个人输入 !help,Errbot 使用你的插件的文档字符串来为可获取的命令生成文档,这使得了解每行命令的作用更加简便。

内置的管理和安全特性: Errbot 允许你限制拥有管理员权限的用户列表,甚至细粒度访问控制。比如说你可以限制特定用户或聊天房间访问特定命令。

额外的插件框架: Errbot 支持钩子、回调、子命令、webhook、轮询以及其它更多特性。如果那些还不够,你甚至可以编写动态插件。当你需要基于在远程服务器上的可用命令来启用对应的聊天命令时,这个特性十分有用。

自带测试框架: Errbot 支持 pytest,同时也自带一些能使你简便测试插件的有用功能。它的“测试你的插件”的文档出于深思熟虑,并提供了足够的资料让你上手。

Errbot 可能的不足

以 “!” 开头: 默认情况下,Errbot 命令发出时以一个惊叹号打头(!help 以及 !hello)。一些人可能会喜欢这样,但是另一些人可能认为这让人烦恼。谢天谢地,这很容易关掉。

插件元数据 首先,Errbot 的 Hello World 插件示例看上去易于使用。然而我无法加载我的插件,直到我进一步阅读了教程并发现我还需要一个 .plug 文档,这是一个 Errbot 用来加载插件的文档。这可能比较吹毛求疵了,但是在我深挖文档之前,这对我来说都不是显而易见的。

示例用法

import random
from errbot import BotPlugin, botcmd

class Hello(BotPlugin):

    @botcmd
    def hello(self, msg, args):
        text = random.choice(["Hi {}", "Hello {}", "Hey {}"]).format(message.user)
        return text

hello.py

[Core]
Name = Hello
Module = hello

[Python]
Version = 2+

[Documentation]
Description = Example "Hello" plugin

hello.plug

你用过 Errbot 或 Opsdroid 吗?如果用过请留下关于你对于这些工具印象的留言。

开发Hubot聊天机器人

大家所熟知的智能聊天机器人,比如:微软小冰、Apple Siri、Google Now、IBM Watson等。微信自动回复、微信服务号里的客服Bot也都算是简单的实例。聊天机器人(Dialog System)主要实现方式有:基于人工模板(比如AIML)、基于搜索技术(比如ElasticSearch)、基于深度学习。从零实现这样的系统是很复杂的。

开源的ChatOps机器人:
1.Hubot:CoffeeScipt编写
2.Lita:Ruby编写
3.Errbot:Python编写

So, What is ChatOps? And How do I Get Started?
12+ Frameworks to Build ChatOps Bots

Hubot是由Github开发的开源聊天机器人,基于Node.js采用CoffeeScript编写。
Hubot https://hubot.github.com/
Hubot Scripts https://github.com/hubot-scripts
Hubot Control https://github.com/spajus/hubot-control

可以借助Hubot开发Chatbot来自动化的完成想要一切自动化任务,比如:
-运维自动化(编译部署代码、重启机器,监控服务器运行情况,自动修复Bug等)
-外部服务交互(管理Redmine、集成Jenkins、监视Zabbix等)
-定时获取天气预报
-随机订餐
-聊天机器人等等。

官方文档里有详细的使用说明,https://hubot.github.com/docs/,这里只做一个摘要。
Hubot Scripts里有大量的现成脚本可以用,也是自己编写脚本的最好sample。

(一)安装
运行Hubot需要以下软件支持:

  • Node.js
  • Redis 默认存贮数据
  • CoffeeScript
  • Yeoman
  • generator-hubot 生成Hubot骨架工程

 

引用
C:\Users\rensanning>node -v
v0.12.8
C:\Users\rensanning>npm -v
2.14.9
C:\Users\rensanning>npm install -g yo generator-hubot
C:\Users\rensanning>npm list -g generator-hubot yo

(二)创建自己的bot

引用
C:\Users\rensanning>cd d:/
D:\>mkdir hubotsample
D:\>cd hubotsample
D:\hubotsample>yo hubot
? Owner RenSanNing <rensanning@gmail.com>
? Bot name okbot
? Description A sample hubot
? Bot adapter campfire

默认提供了campfire和shell的adapter,其他的需要通过npm下载hubot-<AdapterName>。
也可以通过参数直接生成:

引用
yo hubot –owner=rensanning@gmail.com –name=foobot –description=”Foo Bot” –adapter=shell

项目结构:

  • bin/ 运行脚本
  • node_modules 应用的包文件
  • scripts 存放自定义脚本
  • external-scripts.json 应用的外部脚本
  • hubot-scripts.json *** 该文件已经无用,可以删除
  • package.json 项目全局配置信息

(三)运行bot

引用
D:\hubotsample>bin\hubot -v
2.19.0
D:\hubotsample>bin\hubot -h
Usage hubot [options]

Available options:
-a, –adapter ADAPTER   The Adapter to use
-c, –create PATH       Create a deployable hubot
-d, –disable-httpd     Disable the HTTP server
-h, –help              Display the help information
-l, –alias ALIAS       Enable replacing the robot’s name with alias
-n, –name NAME         The name of the robot in chat
-r, –require PATH      Alternative scripts path
-t, –config-check      Test hubot’s config to make sure it won’t fail at startup
-v, –version           Displays the version of hubot installed

-a 指定Adapter(默认是shell)
-d 关闭HTTP服务(默认是开启的)
-c deprecated 使用yeoman

采用默认的shell adapter

引用
D:\hubotsample>bin\hubot

指定adapter

引用
D:\hubotsample>bin\hubot -a shell

 

引用
okbot> help
usage:
history
exit, \q – close shell and exit
help, \? – print this usage
clear, \c – clear the terminal screen

 

引用
okbot> okbot help
okbot> Shell: okbot adapter – Reply with the adapter
okbot animate me <query> – The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead.
okbot echo <text> – Reply back with <text>
okbot help – Displays all of the help commands that Hubot knows about.
okbot help <query> – Displays all help commands that match <query>.
okbot image me <query> – The Original. Queries Google Images for <query> and returns a random top result.
okbot map me <query> – Returns a map view of the area returned by `query`.
okbot mustache me <url|query> – Adds a mustache to the specified URL or query result.
okbot ping – Reply with pong
okbot pug bomb N – get N pugs
okbot pug me – Receive a pug
okbot the rules – Make sure hubot still knows the rules.
okbot time – Reply with current time
okbot translate me <phrase> – Searches for a translation for the <phrase> and then prints that bad boy out.
okbot translate me from <source> into <target> <phrase> – Translates <phrase> from <source> into <target>. Both <source> and <target> are optional
ship it – Display a motivation squirrel

okbot ping
okbot> PONG

okbot echo 你好!
okbot> 你好!

okbot time
okbot> Server time is: Fri Sep 30 2016 11:05:24 GMT+0800 (中国 (标准时间))

***所有的输入都会被记录在.hubot_history文件里。

(四)编写脚本
编写的自定义脚本要放在scripts中,可以是.coffee或.js文件。

scripts/hello.coffee

Js代码  收藏代码
  1. # Description:
  2. #   This is a test.
  3. #
  4. # Commands:
  5. #   okbot helo – Reply with world!
  6. module.exports = (robot) ->
  7.   robot.respond /hello/i, (msg) ->
  8.     msg.send “world!”

 

引用
okbot hello
okbot> world!

因为Hubot要解析脚本文件,提供help帮助,所以脚本文件开头的注释是规范的,
第一行必须是注释“# Description:”(其他的可以没有),否则会有警告:

引用
hello.coffee is using deprecated documentation syntax

Hubot同时也支持js,比如:
scripts/hello2.js

Js代码  收藏代码
  1. // Description:
  2. //   This is a test2.
  3. // Commands:
  4. //   okbot helo – Reply with world!
  5. module.exports = function(robot) {
  6.     robot.respond(/hi/i, function(msg){
  7.         msg.send(“world2!”);
  8.     });
  9. }

 

引用
okbot hi
okbot> world2!

Respond vs Hear

  • respond只监听直接发送给机器人的消息,需要指定机器人名称
  • hear可以监听任何消息

 

引用
MYHUBOT xxx
myhubot xxx
@myhubot xxx
myhubot: xxx

Send vs Reply

  • send会将消息发送给所有人
  • reply会将消息回复给指定的人

Random
msg对象有一个random方法,可以从之后的数组对象中随机提取一个元素

引用
msg.send msg.random arrayObject

hubot Scripts Explained
http://theprogrammingbutler.com/blog/archives/2011/10/28/hubot-scripts-explained/

(五)安装脚本
Hubot 有一大堆现成的脚本,可以集成各种服务。

引用
D:\hubotsample>npm search hubot-scripts github
D:\hubotsample>npm install –save hubot-plusplus
hubot-plusplus@1.3.0 node_modules\hubot-plusplus
├── underscore@1.8.3
├── clark@0.0.6
└── coffee-script@1.6.3

将package-name添加到external-scripts.json

引用
“hubot-plusplus”

 

引用
okbot> ruby++
okbot> ruby has 1 point
okbot> java–
okbot> java has -1 points

(六)hubot-script实例

定时脚本
scripts/cron.coffee

Js代码  收藏代码
  1. cronJob = require(‘cron’).CronJob
  2. module.exports = (robot) ->
  3.   send = (room, msg) ->
  4.     response = new robot.Response(robot, {user : {id : -1, name : room}, text : “none”, done : false}, [])
  5.     response.send msg
  6.   new cronJob(‘0 * * * * *’, () ->
  7.     currentTime = new Date
  8.     send ‘#your-channel-name’“current time is #{currentTime.getHours()}:#{currentTime.getMinutes()}.”
  9.   ).start()

 

引用
D:\hubotsample>npm install cron –save
D:\hubotsample>bin\hubot -a shell

http请求
scripts/googleGEO.coffee

Js代码  收藏代码
  1. module.exports = (robot) ->
  2.   robot.hear /location (.*)/, (msg) ->
  3.     request = robot.http(“https://maps.googleapis.com/maps/api/geocode/json”)
  4.                    .query(address: msg.match[1])
  5.                    .get()
  6.     request (err, res, body) ->
  7.       json = JSON.parse body
  8.       location = json[‘results’][0][‘geometry’][‘location’]
  9.       msg.send “#{location[‘lat’]}, #{location[‘lng’]}”

 

引用
okbot> location Beijing
okbot> 39.904211, 116.407395

抓取数据(request, cheerio)
scripts/title.coffee

Js代码  收藏代码
  1. request = require ‘request’
  2. cheerio = require ‘cheerio’
  3. module.exports = (robot) ->
  4.   robot.respond /title (.*)/i, (msg) ->
  5.     url = msg.match[1]
  6.     options =
  7.       url: url
  8.       timeout: 2000
  9.       headers: {‘user-agent’‘node title fetcher’}
  10.     request options, (error, response, body) ->
  11.       $ = cheerio.load body
  12.       title = $(‘title’).text().replace(/\n/g, )
  13.       msg.send(title)

 

引用
D:\hubotsample>npm install –save request
D:\hubotsample>npm install –save cheerio
D:\hubotsample>bin\hubot -a shell

okbot> okbot title http://github.com
okbot> How people build software · GitHub
okbot> okbot title http://www.google.com
okbot> Google

http应答(httpd)
scripts/version.coffee

Js代码  收藏代码
  1. module.exports = (robot) ->
  2.   robot.router.get “/version”, (req, res) ->
  3.     res.end robot.version

访问http://localhost:8080/version。
默认端口是8080,可以修改环境变量:export PORT=8080

Hubot大量依赖环境变量来配置脚本,所以一般都做一个启动脚本:

引用
#!/bin/sh
export HUBOT_ENV_TEST_VAR=””
bin/hubot -a twitter -n testbot

 

引用
@echo off
SET HUBOT_ENV_TEST_VAR=””
bin\hubot.cmd -a twitter -n testbot

脚本中的使用:

引用
TEST_VAR = process.env.HUBOT_ENV_TEST_VAR

捕获所有未处理信息
scripts/catchAll.coffee

Js代码  收藏代码
  1. module.exports = (robot) ->
  2.   robot.catchAll (res) ->
  3.     res.send “Nothing Found:#{res.message.text}”

hubotスクリプトの書き方とサンプル集
http://blog.fumiz.me/2012/08/05/hubot-irc-bot-script/
编写 Hubot Scripts
http://scarletsky.github.io/2016/05/02/write-your-own-hubot-scripts/

(七)自定义Adapter
hubot默认提供两种adapter:shell、campfile
shell用于开发调试,campfile以外的Chat Service也都有开源的实现。

Adapter基本构成

新建文件 \node_modules\hubot\src\adapters\SampleAdapter.coffee

Js代码  收藏代码
  1. class SampleAdapter extends Adapter
  2.   send: (envelope, strings…) ->
  3.     @robot.logger.info “Send”
  4.   run: ->
  5.     @robot.logger.info “Run”
  6. exports.use = (robot) ->
  7.   new SampleAdapter robot

修改文件 \node_modules\hubot\src\robot.coffee

引用
HUBOT_DEFAULT_ADAPTERS = [
‘campfire’
‘SampleAdapter’
‘shell’
]

启动bot

引用
D:\hubotsample>bin\hubot -a SampleAdapter

* 所有的Adapter必须继承自Adapter
* Adapter中最重要的两个方法是send和run,其他方法比如emote、reply、topic、play等在特定场景下才需要。
* run方法是bot启动时执行
* send方法是回复信息时执行
* 父类 adapter.coffee 里有详细的方法说明 https://github.com/github/hubot/blob/master/src/adapter.coffee
* Adapter名必须是:src\adapters里的文件名 & robot.coffee的HUBOT_DEFAULT_ADAPTERS里定义的名 或者和 hubot-#{adapter} 一致

Chat服务的Adapter

Js代码  收藏代码
  1. {Adapter, TextMessage} = require ‘hubot’
  2. {EventEmitter} = require ‘events’
  3. class MyChatAdapter extends Adapter
  4.   send: (envelope, strings…) ->
  5.     @bot.send str for str in strings
  6.   run: ->
  7.     options =
  8.       token:   process.env.HUBOT_CHAT_TOKEN
  9.       rooms:   process.env.HUBOT_CHAT_ROOMS
  10.       account: process.env.HUBOT_CHAT_ACCOUNT
  11.     bot = new MyChatStreaming options, @robot
  12.     bot.on ‘message’, (userId, userData, message) ->
  13.       user = @robot.brain.userForId userId, userData
  14.       @receive new TextMessage user, message
  15.     bot.listen()
  16. exports.use = (robot) ->
  17.   new MyChatAdapter robot
  18. class MyChatStreaming extends EventEmitter
  19.   constructor: (options, @robot) ->
  20.     @token         = options.token
  21.     @rooms         = options.rooms.split(“,”)
  22.     @account       = options.account
  23.   send: (message) ->
  24.     # Send data to your chat service
  25.   listen: ->
  26.     # Get messge data from chat service
  27.     # @emit ‘message’, user, message

具体可以参考: https://github.com/github/hubot/blob/master/src/adapters/campfire.coffee

扩展robot的方法:
\node_modules\hubot\src\adapters\incircle.coffee

Java代码  收藏代码
  1. class TestXXX extends Adapter
  2.   constructor: (robot) ->
  3.     super robot
  4.     robot.hearXXX = (options, callback) ->
  5.       robot.listeners.push new TextListener(robot, “@XXXmessage”, options, callback)

\scripts\testbot.coffee

Java代码  收藏代码
  1. module.exports = (robot) ->
  2.   robot.hearXXX (msg) ->
  3.     msg.send “#{JSON.stringify msg}”

(八)微信adapter
https://github.com/KasperDeng/Hubot-WeChat

主要机制是hack网页版微信协议,先用手机登录微信帐号,然后模拟网页版微信登录,这样就可以接受微信消息了。

  • npm install hubot-weixin –save
  • 手机端登录微信
  • 打开网页微信:web.weixin.qq.com
  • 手机扫描登录
  • 控制台查看如下信息 /node_modules/hubot-weixin/config.yaml
  • cookie: Uin: Sid: Skey: DeviceID:

  • bin\hubot.cmd -n bot -l / -a weixin

参考:
正在吃掉世界的Bot:它从哪里来,会到哪里去?
Hubot:来自GitHub的聊天机器人
GitHub社謹製! bot開発・実行フレームワーク「Hubot」
TDD Hubot scripts with gulp+mocha
在Skype中使用Hubot
http://qiita.com/bouzuya/items/c7d0ad80c357aab6b696

Errbot是一个聊天机器人,它是一个连接到您最喜欢的聊天服务的守护程序,并将您的工具和一些有趣的东西带到对话中。

Errbot

Errbot是一个聊天机器人。它允许你从聊天室以交互方式启动脚本,原因包括:随机幽默、聊天、启动构建、监控提交、触发警报。。。

它是用Python编写的,易于扩展。

Errbot作为open-source软件提供,并在gplv3许可下发布。

Features

聊天服务器支持

Built-in

With add-ons

  • 营火(见说明)
  • Cisco Spark(请参阅说明)
  • 不和谐(见说明)
  • Gitter支持(参见说明)
  • 矩阵(见说明)
  • 最重要(见说明)
  • RocketChat(见说明)
  • Skype(请参阅说明)
  • 毒物(见说明)
  • VK(见说明)
  • Zulip(见说明)

Administration

在初始安装和安全设置之后,可以通过与bot(chatops)聊天来管理Errbot。

  • 安装/卸载/更新/启用/禁用git上托管的私有或公共插件
  • 插件可以通过聊天配置
  • 引导机器人加入/离开多用户聊天室(MUC)
  • 安全性:ACL控制功能(每个命令的管理员/用户权限)
  • 后援:综合指挥部!备份创建持久数据的完整导出。
  • 日志:可以从聊天或流到哨兵。

Developer features

  • 在Python中很容易扩展!(见下文)
  • 每个插件(即self['foo'] = 'bar')的预置存储将保持该值。
  • 会话流跟踪用户的会话状态。
  • Webhook回调支持
  • 支持markdown附加格式,包括表格、嵌入图像、链接等。
  • 配置助手,允许通过聊天配置插件
  • 图形和文本开发/调试控制台
  • Self-documenting:你的docstring会自动变成帮助
  • 可以使用子命令和各种arg解析选项(re,命令行类型)
  • 轮询支持:你可以设置一个插件来定期做一些事情
  • 端到端测试后端
  • Slack和Hipchat下的卡片渲染。

社区和支持

如果您有:

  • 一个快速的问题,请随时加入我们的聊天室errbotio/errbot on Gitter。
  • 一个插件开发问题,请使用带有errbot和python标记的Stackoverflow。
  • 要报告的错误或功能请求,请使用我们的GitHub项目页面。

关于更多的一般性讨论和公告,您可以加入googleplus社区。你也可以在Twitter上用标签#errbotping我们。

Installation

Prerequisites

Errbot在Linux、Windows和Mac上的Python3.6+下运行。对于某些聊天系统,您需要一个密钥或登录名才能访问它。

Quickstart

我们建议设置一个virtualenv。

  1. 从pip安装errbot
  2. 在某处创建一个目录(这里称为errbot)来存放errbot的数据文件
  3. 初始化目录
  4. 在文本模式下试用Errbot
$ pip install errbot
$ mkdir errbot; cd errbot
$ errbot --init
$ errbot

它将显示一个提示>>>以便您可以直接与您的机器人对话!尝试!帮助开始。

添加对聊天系统的支持

对于built-ins,只需使用slack、hipchat、telegram、IRC、XMPP和pip中的一个,您仍然可以在初始安装之后添加缺失的支持

$ pip install "errbot[slack]"

对于外部的(Skype、Gitter、Discord等…),请按照各自的github页面进行操作。

Configuration

为了配置Errbot连接到其中一个聊天系统,您需要调整Errbot–init生成的config.py文件。

为了帮助您,我们在这里提供了一个文档模板:config-template.py。

注意:即使您从配置中更改了后端,您仍然可以使用errbot-T和errbot-G在本地测试实例(分别以文本和图形模式)。

将Errbot作为守护程序启动

如果所有这些都有效,现在可以使用-d(或–daemon)参数以分离模式运行它:

errbot --daemon

与机器人交互

启动Errbot之后,如果还没有,应该将bot添加到好友列表中。你也需要在一些聊天系统上明确邀请机器人进入聊天室。你现在可以直接向机器人发送命令了!

要获取所有可用命令的列表,可以发出:

!help

如果您只想了解有关特定命令的更多信息,可以发出:

!help command

Managing plugins

如果您的句柄在中的BOT_ADMINS列表中,则可以在one-on-one聊天中管理bot配置.py.

例如,要在公共插件回购中搜索关键字,您可以发布:

!repos search jira

要安装此列表中的插件,请发出:

!repos install <name of repo>

例如!repos安装errbotio/err-imagebot。

Writing plugins

编写自己的插件非常简单。errbot–init将在plugins子目录中安装一个名为err-example的插件,您可以使用它作为基础。

例如,创建一个“Hello,world!”Errbot插件:

from errbot import BotPlugin, botcmd

class Hello(BotPlugin):
    """Example 'Hello, world!' plugin for Errbot"""

    @botcmd
    def hello(self, msg, args):
        """Return the phrase "Hello, world!" to you"""
        return "Hello, world!"

Errbot 后端插件(Backend)开发 – 飞书版

日常工作当中使用最多的其实是聊天工具,那么将自动化的工作流程与聊天工具结合,那就能更加提高工作的效率在聊天的过程中就把事情做好了。另外可以使用聊天群交流,那么其他人是可以看到整个过程的,新来的人也可以通过查看聊天记录了解日常工作和执行过程,自然而然的工作流程的熟悉和达到积累组织知识的目的。

简介

DevOps

DevOpsDevelopment和Operations的组合词)是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。同时DevOps它更是一套项目部门之间沟通与协作问题的方法论与实践指导。

ChatOps

日常工作当中使用最多的其实是聊天工具,那么将自动化的工作流程与聊天工具结合,那就能更加提高工作的效率在聊天的过程中就把事情做好了。另外可以使用聊天群交流,那么其他人是可以看到整个过程的,新来的人也可以通过查看聊天记录了解日常工作和执行过程,自然而然的工作流程的熟悉和达到积累组织知识的目的。

Errbot

这是一款聊天聊天机器人,使用它可以快速将已经自动化后台工具集成到个人喜欢的聊天工具,如 slack等。更多的细节可以查阅官方文档。接着为了集成Errbot到飞书上面需要单独开发后端插件来实现,Errbot 的后端插件开发过程可以参考官方说明文档 。概括的来说这个插件的开发过程,更多的是对于 Errbot 提供的 基类 的继承和实现其中的抽象方法的过程。

飞书机器人

飞书是一款聊天工具,同时它提供了一个开发平台,提供强大的开放接口,开发者可以基于提供的接口能力快速开发出一个聊天机器人。其中如何快速开发机器人,它提供了一个简单的教程

实践 – 开发结合飞书开发Errbot后端(Backend)

首先项目源代码可以在此处找到:https://github.com/ouranoshong/errbot-backend-lark。另外README.md文档里面有个安装教程,可以按照上面描述的流程安装一遍体验一下整个过程。

下列是目录结构展示:

├── LICENSE
├── README.md
├── _lark
├── lark.plug
├── lark.py
├── requirements.txt
└── tests

开发流程说明

创建 lark.plug 文件,并在其中加入一下内容,规定后端插件的名称是 Lark :

[Core]
Name = Lark
Module = lark

[Documentation]
Description = This is the lark backend for ErrBot.

在项目的根目录下创建一个模块文件与 lark.plug  的  module 字段对应,文件名是 lark.py .  主要介绍这个文件的几个方法,具体的可以查看源文件

在文件中增加一个类,继承ErrBot基类和LarkServer。其中 ErrBot 类是由 ErrBot 提供的需要为其实现抽象方法。然后为了代码的方便管理使用模板方法设计模式单独编写了LarkServer的类,在这个类中是结合了高性能 python 框架 sanic实现的 HTTP 服务,用于接收聊天服务通知的聊天消息。

class LarkBackend(ErrBot, LarkServer)

serve_once 这个是机器人启动调用的入口,实例化 LarkClientLarkBot 以供其它方法调用

    def serve_once(self):
        self.lc = LarkClient(app_id=self.app_id, app_secret=self.app_secret)
        self.bot_identifier = LarkBot(self.lc)
        try:
            self.connect_callback()  # notify that the connection occured
            self.run()
        except KeyboardInterrupt:
            log.info("Interrupt received, shutting down..")
            return True
        except Exception:
            log.exception("Error running Lark server!")
        finally:
            log.debug("Triggering disconnect callback")
            self.disconnect_callback()

_message_receive_handler 这个方法是对 LarkServer 类中抽象方法的实现,主要是将接收到的飞书通知数据转换为 Message 对象,然后再调用 callback_message 方法,调用其它的消息处理逻辑。这里针对不同 chat_type 然后给 Message 对象的 frmto 初始化不同的对象,如 p2p ,即私聊消息通知事件则是转化为 LarkPersonLarkBot 对象,如果是 group 类型,即群聊的消息通知事件则转化为 LarkRoomOccupant  和 LarkRoom 对象。

	def _message_receive_handler(self, data: dict):
        """Callback event handler for the 'message' event"""
        log.debug("Saw an event: %s", pprint.pformat(data))

        event = data.get("event", {})
        message = event.get("message", {})
        msg_type = message.get("message_type", "")
        if msg_type != "text":
            log.warning("only support 'text' msg_type from now on, got:{msg_type}")
            return
        
        text = self._get_text_without_mentions(message)
        chat_type = message.get("chat_type", "").strip()
        sender = event.get("sender", {})
        sender_id = sender.get("sender_id", {})

        msg = Message(text, extras={"lark_event": data})
        if chat_type == 'p2p':
            msg.frm = LarkPerson(self.lc, sender_id.get("open_id"))
            msg.to = self.bot_identifier
        elif chat_type == "group":
            msg.frm = LarkRoomOccupant(self.lc, sender_id.get("open_id"), message.get("chat_id"), self)
            msg.to = LarkRoom(self.lc, message.get("chat_id"), bot=self)
        else:
            log.error(
                f"unknown chat_type:{chat_type} not in ['p2p', 'group']")

        self.callback_message(msg)

send_message 根据 msg 参数不同,构建不同的消息,最后通过调用飞书平台的接口完成消息回复。

    def send_message(self, msg: Message):
        super().send_message(msg)
        to_humanreadable = "<unknown>"
        receive_id = None
        receive_id_type = None
        memtion_id = None
        mention_name = None
        log.debug("message to: {}".format(msg.to))
        try:
            if isinstance(msg.to, RoomOccupant):
                memtion_id = msg.to.open_id
                memtion_name = msg.to.fullname
                receive_id = msg.to.room.chat_id
                to_humanreadable = msg.to.room.name
                receive_id_type = "chat_id"
            elif msg.is_group:
                receive_id = msg.to.chat_id
                to_humanreadable = msg.to.name
                receive_id_type = "chat_id"
            else:
                receive_id = msg.to.person
                memtion_id = receive_id
                to_humanreadable = msg.to.fullname
                memtion_name = to_humanreadable
                receive_id_type = "open_id"

            msgtype = "direct" if msg.is_direct else "channel"
            # chat_id > open_id
            log.debug('Sending %s message to %s (%s).', msgtype, to_humanreadable, receive_id)
            body = msg.body

            if memtion_id is not None:
                body = f'<at user_id="{memtion_id}">{memtion_name}</at>:\n{body}'

            log.debug('Message size: %d.', len(body))

            response = (self.lc.send_message(json.dumps({"text": body}), receive_id_type, receive_id, "text"))
            log.info('send message response %s', response)

build_identifier 根据 txt_rep 参数构建出不同唯一标识且可以被识别的对象,这个也是 ErrBot 中有所说明的方法之一。

    def build_identifier(self, txt_rep: str) -> Identifier:
        """
        txt_rep: {open_id}:{open_chat_id}:{chat_type}
        """
        log.debug("build idetifier txt: {}".format(txt_rep))

        # only open_id can be used to identify a unique person
        open_id, open_chat_id, chat_type = self._extract_identifiers_from_str(txt_rep)
        if open_id and chat_type == "p2p":
            return LarkPerson(lc=self.lc, open_id=open_id)
        if open_id and open_chat_id and chat_type == "group":
            return LarkRoomOccupant(lc=self.lc, open_id=open_id, chat_id=open_chat_id, bot=self)
        if open_chat_id:
            return LarkRoom(lc=self.lc, chat_id=open_chat_id, bot=self)

        raise Exception(
            "You found a bug. I expected at least one of open_id, open_chat_id, or chat_type "
            "to be resolved but none of them were. This shouldn't happen so, please file a bug."
        )

总结

  1. 了解并熟悉使用飞书开发平台接口快速开发一个聊天机器人,另外发现了飞书聊天工具中对 markdown 语法并不完善,使用时请注意只使用飞书已支持 markdown 语法,否则无法正常显示的风险;
  2. ErrBot 的插件化设计,其本身的有很强的扩展能力,快速集成到日常工作的聊天工具中,同时使用这个可以更快的开发需要的自动化流程,提高工作的效率。

Hubot的简单用法

简介

Hubot 是 Github 的开源聊天机器人,可以用来做一些自动化任务,如部署网站,翻译语言等等。
你可能会说,这些只要写个脚本就可以做到了吧?
确实,但你写完脚本之后还是需要手动运行那些脚本。
你有没想过其实你可以在常用的聊天软件上说 @xxx, 部署新版本的网站
,然后机器人就自动登录服务器,然后执行部署脚本,部署成功后告诉你 新版本的网站已经部署成功

是的,如果你的聊天软件上集成了 Hubot,你就可以轻松地用它来管理一些繁琐的事情啦!

安装

官方推荐我们用 yeoman + hubot 生成器来生成我们的聊天机器人,方法如下:

$ npm install -g yo generator-hubot
$ mkdir myhubot && cd myhubot
$ yo hubo

回答一些基本的问题后,我们的聊天机器人就生成好啦~

基本用法

我们的聊天机器人的执行文件是 bin/hubot
,我们先看看里面写什么:

$ cat ./bin/hubot
#!/bin/sh
set -e
npm install
export PATH="node_modules/.bin:node_modules/hubot/node_modules/.bin:$PATH"
exec node_modules/.bin/hubot --name "myhubot" "$@"

这份执行文件只是先执行 npm install
,然后设置环境变量,再执行 node_modules/.bin/hubot
而已,没什么神秘的。
我们试试运行一下这份可执行文件:

$ ./bin/hubot
myhubot>

我们看到了一个类似 shell 的东东!试试随便输入一些东西:

myhubot> hello
myhubot> world
myhubot> how are you?
myhubot> can you hear me?

我们发现无论我们输入什么,我们的机器人都没有反应,是不是坏掉了?其实并不是这样的,它没反应是因为我们没有对「输入」的处理,如果我们输入一些特定的「输入」,它就会有反应啦!

myhubot> myhubot ping
myhubot> PONG

myhubot> myhubot pug me
myhubot> http://28.media.tumblr.com/tumblr_locinzasB91qzj3syo1_500.jpg

myhubot> myhubot help
myhubot> myhubot adapter - Reply with the adapter
myhubot animate me <query> - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead.
myhubot echo <text> - Reply back with <text>
myhubot help - Displays all of the help commands that Hubot knows about.
myhubot help <query> - Displays all help commands that match <query>.
myhubot image me <query> - The Original. Queries Google Images for <query> and returns a random top result.
myhubot map me <query> - Returns a map view of the area returned by `query`.
myhubot mustache me <url|query> - Adds a mustache to the specified URL or query result.
myhubot ping - Reply with pong
myhubot pug bomb N - get N pugs
myhubot pug me - Receive a pug
myhubot the rules - Make sure hubot still knows the rules.
myhubot time - Reply with current time
myhubot translate me <phrase> - Searches for a translation for the <phrase> and then prints that bad boy out.
myhubot translate me from <source> into <target> <phrase> - Translates <phrase> from <source> into <target>. Both <source> and <target> are optional
ship it - Display a motivation squirrel

看到了吧!如果我们输入了特定的「输入」,机器人就会有反应啦!
当我们输入 myhubot help
的时候,返回的东东其实就是预定义的「输入」,这些预定义的「输入」只在 shell adapter 下有效哦!

Adapter

什么是 shell adapter ? 我们运行 ./bin/hubot
时默认的 adapter 就是 shell adapter。
什么是 adapter ? 所谓的 adapter 其实是一些让机器人接收输入的接口。
刚才提到,shell adapter 是默认情况下的 adapter,主要是用来测试 adapter 是否生效。说白了,其实就是没什么用!
觉得很坑爹是吧?说好的让我们的聊天软件整合我们的机器人呢?
实际上社区已经为我们提供了各种各样的 adapter,我们只要下载就可以用啦!具体请看看 https://hubot.github.com/docs/adapters/
那么我们如何指定用某个 adapter 呢?很简单啦,只要启动机器人的时候带上 -a
参数就好了。譬如如果我们想让机器人整合到 telegram,我们只要执行下面的命令就可以了:

$ npm install --save hubot-telegram
$ ./bin/hubot -a telegram

当然我们还需要设置一下,这些设置会根据不同的 adapter 而有所不同,具体请看对应的文档!
如果你所用的聊天软件并不在社区的支持列表中,又想把整合 Hubot 的话,可以自己写 adapter,文档在这里:https://hubot.github.com/docs/adapters/development/

Scripts

我们一直说 Hubot 是聊天机器人,机器人最基本的是根据不同的「输入」给出不同的「输出」。在 Hubot 应该怎么处理不同「输入」,给出不同的「输出」呢?答案就是用 Scripts 啦!
有没有发现我们机器人的目录下有个 scripts/
文件夹?我们可以在这个文件夹下添加各种脚本文件,根据不同的「输入」给出不同的「输出」。在我们启动 Hubot 的时候,它会加载 scripts/
文件夹下的脚本,赋予 Hubot 强大的交互能力!
需要注意的是,scripts/
下的脚本必须是 .coffee
或者 .js
格式的,而且必须暴露一个接受 robot 参数的函数!我们还是先打开 scripts/example.coffee
看看吧!

// coffee
module.exports = (robot) ->

// js
module.exports = function(robot) {}

在这个函数里面,我们可以利用 robot.hear、robot.response、robot.send、robot.reply
等 api 为不同的「输入」给出不同的「输出」!我们还可以用 robot.http(url).get()
等方法来发出 http 请求!这样我们的机器人就可以有更强大的交互能力了!
想知道更多 api 的用法的话,可以参考文档:https://hubot.github.com/docs/scripting/

写在最后

Hubot 真的是一个简单易用的聊天机器人,我们可以把它整合到我们的聊天软件中,让那些简单但繁琐的任务自动化起来,提高我们的工作效率!最后强烈推荐各位同学去读一下 Hubot 的源码,简单易懂,之后会对 Hubot 有更深刻的认识!

参考资料

https://hubot.github.com/

 

ConfigMap使用方法

ConfigMap概览

  • ConfigMap API资源用来保存key-value pair配置数据,这个数据可以在pods里使用,或者被用来为像controller一样的系统组件存储配置数据。虽然ConfigMap跟Secrets类似,但是ConfigMap更方便的处理不含敏感信息的字符串。 注意:ConfigMaps不是属性配置文件的替代品。ConfigMaps只是作为多个properties文件的引用。你可以把它理解为Linux系统中的/etc目录,专门用来存储配置文件的目录。
  • 下面举个例子,使用ConfigMap配置来创建Kuberntes Volumes,ConfigMap中的每个data项都会成为一个新文件。

kind: ConfigMap
apiVersion: v1
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: example-config
  namespace: default
data:
  example.property.1: hello
  example.property.2: world
  example.property.file: |-
    property.1=value-1
    property.2=value-2
    property.3=value-3
  • data一栏包括了配置数据,ConfigMap可以被用来保存单个属性,也可以用来保存一个配置文件。
  • 配置数据可以通过很多种方式在Pods里被使用。
  • ConfigMaps可以被用来:

设置环境变量的值
在容器里设置命令行参数
在数据卷里面创建config文件
用户和系统组件两者都可以在ConfigMap里面存储配置数据。
  • 其实不用看下面的文章,直接从kubectl create configmap -h的帮助信息中就可以对ConfigMap究竟如何创建略知一二了。

Examples:
  # Create a new configmap named my-config based on folder bar
  kubectl create configmap my-config --from-file=path/to/bar

  # Create a new configmap named my-config with specified keys instead of file basenames on disk
  kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt

  # Create a new configmap named my-config with key1=config1 and key2=config2
  kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2

创建ConfigMaps

  • 可以使用该命令,用给定值、文件或目录来创建ConfigMap。

使用目录创建

  • 比如我们已经有个了包含一些配置文件,其中包含了我们想要设置的ConfigMap的值:

$ ls docs/user-guide/configmap/kubectl/
game.properties
ui.properties

$ cat docs/user-guide/configmap/kubectl/game.properties
enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

$ cat docs/user-guide/configmap/kubectl/ui.properties
color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice
  • 使用下面的命令可以创建一个包含目录中所有文件的ConfigMap。

$ kubectl create configmap game-config --from-file=docs/user-guide/configmap/kubectl
  • —from-file指定在目录下的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件的内容。
  • 让我们来看一下这个命令创建的ConfigMap:

$ kubectl describe configmaps game-config
Name:           game-config
Namespace:      default
Labels:         <none>
Annotations:    <none>

Data
====
game.properties:        158 bytes
ui.properties:          83 bytes
  • 我们可以看到那两个key是从kubectl指定的目录中的文件名。这些key的内容可能会很大,所以在kubectl describe的输出中,只能够看到键的名字和他们的大小。
  • 如果想要看到键的值的话,可以使用kubectl get:

$ kubectl get configmaps game-config -o yaml
  • 我们以yaml格式输出配置。

apiVersion: v1
data:
  game.properties: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
  ui.properties: |
    color.good=purple
    color.bad=yellow
    allow.textmode=true
    how.nice.to.look=fairlyNice
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:34:05Z
  name: game-config
  namespace: default
  resourceVersion: "407"
  selfLink: /api/v1/namespaces/default/configmaps/game-config
  uid: 30944725-d66e-11e5-8cd0-68f728db1985

使用文件创建

  • 刚才使用目录创建的时候我们—from-file指定的是一个目录,只要指定为一个文件就可以从单个文件中创建ConfigMap。

$ kubectl create configmap game-config-2 --from-file=docs/user-guide/configmap/kubectl/game.properties 

$ kubectl get configmaps game-config-2 -o yaml
apiVersion: v1
data:
  game-special-key: |
    enemies=aliens
    lives=3
    enemies.cheat=true
    enemies.cheat.level=noGoodRotten
    secret.code.passphrase=UUDDLRLRBABAS
    secret.code.allowed=true
    secret.code.lives=30
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T18:54:22Z
  name: game-config-3
  namespace: default
  resourceVersion: "530"
  selfLink: /api/v1/namespaces/default/configmaps/game-config-3
  uid: 05f8da22-d671-11e5-8cd0-68f728db1985
  • —from-file这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的。

使用字面值创建

  • 使用文字值创建,利用—from-literal参数传递配置信息,该参数可以使用多次,格式如下;

$ kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

$ kubectl get configmaps special-config -o yaml
apiVersion: v1
data:
  special.how: very
  special.type: charm
kind: ConfigMap
metadata:
  creationTimestamp: 2016-02-18T19:14:38Z
  name: special-config
  namespace: default
  resourceVersion: "651"
  selfLink: /api/v1/namespaces/default/configmaps/special-config
  uid: dadce046-d673-11e5-8cd0-68f728db1985

Pod中使用ConfigMap

  • 使用ConfigMap来替代环境变量
  • ConfigMap可以被用来填入环境变量。看下下面的ConfigMap。

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
  • 我们可以在Pod中这样使用ConfigMap:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
      envFrom:
        - configMapRef:
            name: env-config
  restartPolicy: Never
  • 这个Pod运行后会输出如下几行:

SPECIAL_LEVEL_KEY=very
SPECIAL_TYPE_KEY=charm
log_level=INFO

用ConfigMap设置命令行参数

  • ConfigMap也可以被使用来设置容器中的命令或者参数值。
  • 它使用的是Kubernetes的$(VAR_NAME)替换语法。
  • 我们看下下面这个ConfigMap。

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm
  • 为了将ConfigMap中的值注入到命令行的参数里面,我们还要像前面那个例子一样使用环境变量替换语法${VAR_NAME)。
  • (其实这个东西就是给Docker容器设置环境变量,以前我创建镜像的时候经常这么玩,通过docker run的时候指定-e参数修改镜像里的环境变量,然后docker的CMD命令再利用该$(VAR_NAME)通过sed来修改配置文件或者作为命令行启动参数。)

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
  restartPolicy: Never
  • 运行这个Pod后会输出:

very charm

通过数据卷插件使用ConfigMap

  • ConfigMap也可以在数据卷里面被使用。还是这个ConfigMap。

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm
  • 在数据卷里面使用这个ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容:

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ "/bin/sh", "-c", "cat /etc/config/special.how" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: special-config
  restartPolicy: Never

*运行这个Pod的输出是very。

  • 我们也可以在ConfigMap值被映射的数据卷里控制路径。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ "/bin/sh","-c","cat /etc/config/path/to/special-key" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: special-config
        items:
        - key: special.how
          path: path/to/special-key
  restartPolicy: Never
  • 运行这个Pod后的结果是very。

这里有几点需要注意。

挂载目录下的文件名称,即为cm定义里的key值。
挂载目录下的文件的内容,即为cm定义里的value值。value可以多行定义,这在一些稍微复杂的场景下特别有用,比如 my.cnf。
如果挂载目录下原来有文件,挂载后将不可见(AUFS)。
有的时候,我们希望将文件挂载到某个目录,但希望只是挂载该文件,不要影响挂载目录下的其他文件。有办法吗?

可以用subPath: Path within the volume from which the container’s volume should be mounted。Volume中待挂载的子目录。

subPath 的目的是为了在单一Pod中多次使用同一个volume而设计的。

例如,像下面的LAMP,可以将同一个volume下的 mysql 和 html目录,挂载到不同的挂载点上,这样就不需要为 mysql 和 html 单独创建volume了。

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      env:
      - name: MYSQL_ROOT_PASSWORD
        value: "rootpasswd" 
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php:7.0-apache
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

那怎么解决我们的问题呢?很简单,如果subPath不是目录,而是一个文件,那么就可以达到单独挂载的目的了。

containers:
- volumeMounts:
  - name: demo-config
    mountPath: /etc/special.type
    subPath: special.type
volumes:
- name: demo-config
  configMap:
    name: special-config

作者:Rick_Ji
链接:https://www.jianshu.com/p/9d55b4a4eeab
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。