awesome-botkit-external

0.1.17 • Public • Published

awesome-botkit-external 库

简单直接好用的机器人框架,支持 convoUI

  • 基于类型多态的有限状态机进行流程控制
  • 丰富的High-Level-API, 通过清晰的 DSL 描述任何复杂的业务流程状态转移图
  • 支持对 inbound-msg 的多种匹配方式:正文Body匹配,Action匹配,也可以直接用 Low-Level-API 进行完全灵活度的自定义匹配
  • 支持直接使用 Low-Level-API 在某个大状态下完成很多步精细交互操作(配合 async-await)
  • 支持在某一具体状态下,下挂多种匹配的表达式。包括动态长度的自定义匹配
  • 支持丰富简洁的控制流,goto,stay,repeat,stop. 对会话进行精准控制

1. 说明

1.1 Installation

npm install awesome-botkit-external

1.2 Demo

const botkit = require('awesome-botkit-external')
const cuw = botkit.ConvoUIWrapper;
 
class MyBotApp extends botkit.Bot {
    onJoinRoom(bot, roomId) {
        return new cuw.PureText(`亲,我是服务条款助手机器人【${bot.name}】.`);
    }
 
    onUserJoinRoom(bot, roomId){
        return new cuw.PureText(`亲,我是机器人【${bot.name}`);
    }
 
    onUserEnterRoom(bot, roomId, userId, displayName){
        return convo => {
            if (convo) {
                return new cuw.PureText('上一次会话还没结束');
            } else {
                return new cuw.PureText(`亲,我是服务条款助手机器人【${bot.name}】,我现在进入了房间`);
            }
        };
    }
 
    onTimeout(bot, roomId, userId, displayName) {
        return new cuw.PureText('会话结束');
    }
 
    // 主要逻辑都在这里
    describe(fsm) {
        const {startWith, when, goto, stay, stop} = botkit.DSL(fsm);
        const { Case, DefaultCase, match, BodyCase, ActionCase, CommandCase } = botkit.Matcher;
 
        startWith(MyStates.IDLE, {counter: 0});
 
        when(MyStates.IDLE)(async (sender, content, data) => {
            if(cuw.GetBody(content) === "step1") {
                return goto(MyStates.STEP1).withConvoMsg(new cuw.PureText("进入step1"))
            } else if (cuw.GetBody(content) === "step3") {
                return goto(MyStates.STEP3).withConvoMsg(new cuw.PureText("进入step3"))
            }
 
            return stay().withConvoMsg(new cuw.PureText("待在idle"))
        });
 
        when(MyStates.STEP1)((sender, content, data) => {
            if(cuw.GetBody(content) === "ui") {
                return goto(MyStates.UI).withConvoMsg(new cuw.Menu(
                    [
                        new cuw.MenuItem("step1", "s1"),
                        new cuw.MenuItem("step2", "s2"),
                        new cuw.MenuItem("step3", "s3"),
                        new cuw.MenuItem("command", "command"),
                        new cuw.MenuItem("stay", "stay"),
                    ],
                    "display"))
            }
 
            return stay().withConvoMsg(new cuw.PureText("待在step1"))
        });
 
        when(MyStates.UI)((sender, content, data) => {
            return match(content,
                ActionCase("s1")(() => goto(MyStates.STEP1).withConvoMsg(new cuw.PureText("进入step1"))),
                ActionCase("s2")(() => goto(MyStates.STEP2).withConvoMsg(new cuw.PureText("进入step2"))),
                ActionCase("s3")(() => goto(MyStates.STEP3).withConvoMsg(new cuw.PureText("进入step3"))),
                CommandCase("convo.ui.menu")((command) => stay().withConvoMsg(new cuw.PureText(command))),
 
                DefaultCase(() => stay().withConvoMsg(new cuw.PureText("待在UI")))
            );
        });
 
        when(MyStates.STEP2)((sender, content, data) => {
            if(cuw.GetBody(content) === "step3") {
                return goto(MyStates.STEP1).withConvoMsg(new cuw.PureText("进入step3"))
            }
 
            return stay().withConvoMsg(new cuw.PureText("待在step2"))
        });
 
        when(MyStates.STEP3)((sender, content, data) => {
            if(cuw.GetBody(content) === "step1") {
                return goto(MyStates.STEP1).withConvoMsg(new cuw.PureText("回到step1"))
            } else if (cuw.GetBody(content) === "done") {
                return goto(MyStates.DONE).withConvoMsg(new cuw.PureText("进入done"))
            }
 
            return stay().withConvoMsg(new cuw.PureText("待在step3"))
        });
 
        when(MyStates.DONE)((sender, content, data) => {
            return stop().withConvoMsg(new cuw.PureText("结束会话"))
        });
 
    }
}
 
const MyStates = {
    IDLE: "IDLE",
    STEP1: "STEP1",
    STEP2: "STEP2",
    STEP3: "step3",
    UI: "ui",
    DONE: "DONE"
};
 
new MyBotApp(require('./config.js')).run();

2 机器人应用代码结构

High-Level-API

状态机定义函数
  • describe 在 describe() 方法体里直接通过 DSL 描述状态机的状态转移图
状态机 DSL
  • startWith 描述状态机的初始状态和初始 data,只需调用一次
  • when 描述某个状态下,发生Event时,Bot业务执行与状态迁移的细节
  • goto 用于生成when()函数的返回值,返回 nextState
  • stay goto(CurrentState)的另一种形式,停留在本状态
  • stop goto(Done)的另一种形式,结束会话
matcher API
  • BodyCase 匹配普通聊天中的string, 支持变长 pattern(String or RegExp type)
  • ActionCase 匹配convoUI消息的action,支持变长 pattern(String or RegExp type)
  • CommandCase 匹配convoUI消息的command类型,支持变长pattern(String or RegExp type)
  • DefaultCase 模式匹配的Default分支
状态机data的共享

状态机describe()的方法体内可以使用如下两种方式共享变量(session scope)

  • 常规方式是在状态迁移时,将修改后的data对象传递给 using(). 这里建议通过 spread-rest 语法构造 immutable object 对象。后续会方便利用到状态跟踪,重演,TimeTravel Debugging 等很多玩法。
  • 还有一种可行的方式是,直接将变量挂在 fsm 上面

Low-Level-API

在回调函数内,可以不借助 matcher API,通过判断 content 或者 data 的具体细节来控制分支走向, 获得最大的灵活度:

when(MyStates.IDLE)(async (sender, content, data) => {
    if(convo_ui_wrapper.GetBody(content) === "step1") {
        return goto(MyStates.STEP1).withConvoMsg({body: "I goto step1!"});
    } else if (convo_ui_wrapper.GetBody(content) === "开始业务2") {
        return goto(MyStates.STEP2)
    }
 
    return stay().withConvoMsg({body: "Stay!"});
});

3. ConvoUI 消息及应答格式说明

3.1 convoUI 通用说明

3.1.1 渲染参数 disply

convoUI 生成参数中(详见下列控件),参数 display 用于控制ConvoUI消息(控件)的展示形式,目前思考,有以下几种:

不提供此参数:不作限制,由前端自行决定 fixed:控件“建议”显示在固定区域 popup:控件“建议”弹出显示 inplace:控件“建议”显示在聊天历史记录中 assist: 控件“建议”显示在文字输入助理区域(目前仅文字助手控件支持此显示方式)

3.1.2 展示过期参数

convo.SetDisplayExpires(value)参数用于指定在fixed、popup形式下的控件是否允许再次打开显示。如果未指定,则Web客户端上可以在历史消息中点开查看;若指定,则历史消息中不再提供打开按钮。

此值是一个过期时间(绝对时间,非相对时间)的长整数时间戳(epoch毫秒值)。

3.1.3 版本参数

convo.SetVersion(),该参数用于在iOS设备上兼容旧版本的ConvoUI

当前 convo.SetVersion("2.0")

不必手动设置版本

3.1.4 接收者参数

convo.SetReceiver()用于控制ConvoUI消息(控件)的接收对象。如果不指定,则表示不限定接收者。如果指定,这个数据是一个JSON数组,表示其中包含的userId的聊天界面上应该显示此ConvoUI消息,否则应隐藏。例如:

convo.SetReceiver(["@frank:finogeeks.club","@test:finogeeks.club"])

表示当前ConvoUI消息仅仅在frank和test两个用户的界面上显示,其他用户的界面上应该隐藏。

指定所有人接收时,请明确设置receiver为null或空数组。这是因为,如果数据完全没有这个键的话,框架会自动添加当前用户为receiver,但如果这个键存在(即使为null或空数组),它就会知道是所有用户接收,然后框架会把这个无用的空键去掉

3.1.5 控制UI应答是否显示

在机器人端发出的ConvoUI消息,客户端呈现UI界面给用户时,用户可能会在UI界面上进行操作(例如按钮点击、菜单选择或者输入等),这些操作即ConvoUI应答的结果,是否需要在消息历史中显示,以参数convo.SetShowReply()来控制(默认值为false,即消息历史中隐藏ConvoUI应答),例如:

convo.SetShowReply(true)

例如在上述按钮出现时,用户点击了“同意条款”按钮,房间消息历史里面会显示应答消息(即应答消息的body文字部分)。这个body文字内容由客户端生成,例如上述按钮点击后文字消息如“点击按钮:同意条款”。依次类推,对于不同ConvoUI控件,这个文字格式有所不同。例如,若是菜单,则用户选择某项X后,显示文字消息为“菜单选择:X”,等等。

3.1.6 控制UI存根消息是否显示

对于display为popup,fixed,assist方式的UI消息而言,以下参数可以控制UI存根(stub,即承载UI的历史消息)是否在历史消息列表中隐藏掉。

convo.SetHideStub(true) // 默认为 false

3.1.7 控制会话控制是否显示

对于在某个与机器人会话的流程中,用户想做“结束会话”或“重置会话”等操作,这样的会话控制是否需要显示,以参数“convo_control”来控制,默认值为false,即不显示。

convo.SetConvoControl(false)

3.1.8 控制 FIX 控件是否静默处理指定操作,如是否弹起展示

convo.SetSilence(true) // 默认为 false

以上本章节参数对所有控件是通用的,convo 表示任何一种控件。在下文的格式描述中,将不再列出这些参数。

3.2 菜单(即按钮组) menu

所谓菜单,其实就是垂直或水平排列的逻辑按钮。

ConvoUI Message

const convo_ui_wrapper = require("awesome-botkit-external").ConvoUIWrapper

// title   : 按钮文字
// action  : 事件代码
item = new convo_ui_wrapper.MenuItem(title, action)

> `action`不仅可以是字符串,也可以是数字、对象、数组等任意JSON类型,客户端回送时保持原样就好(不要做解析及类型转换)

item.SetReply(true) // 默认为 true,可不设置

// type = "url",    点击则打开对应 value 的网页
// type = "userId", 点击则打开对应 value 用户的单聊页面
item.AddMeta(type, value)

// items   : menu 上的每个选项
// caption : 菜单标题
menu = new convo_ui_wrapper.Menu([item, ...], display, caption)
  • items中的replyfalse时,客户端可以不产生应答消息,默认为true。

menu 可以自由添加其他键,满足渲染的需要,例如给菜单增加一个标题(Caption),要求横向排列(horizontal),并在每个按钮上增加一个提示(tip):

menu.SetCaption("请选择一种收费方式")
menu.SetHorizontal(true)
menu.SetTip("想好了再按")

ConvoUI Reply

action = convo_ui_wrapper.GetAction(content)
result = convo_ui_wrapper.GetResult(content)

3.3 上传图片 image

ConvoUI Message

// prompt   : 上传提示文字
// max_size : 文件最大尺寸
image = new convo_ui_wrapper.Image(prompt, max_size, display)

image.SetTypes(["png", "jpeg", "gif"]) // 该数组描述可以接受的文件格式

ConvoUI Reply

// info: {
//   h: 398,
//   w: 394,
//   size: 31037,
//   mimetype: "image/jpeg"
// }
info = convo_ui_wrapper.GetInfo(content)
url = convo_ui_wrapper.GetUrl(content)

3.4 上传视频 video

ConvoUI Message

// prompt   : 上传提示文字
// max_size : 文件最大尺寸
video = new convo_ui_wrapper.Video(prompt, max_size, display)

video.SetTypes(["mp4","webm","ogg"])  // 该数组描述可以接受的文件格式

ConvoUI Reply

// info: {
//   h: 320,
//   w: 480,
//   size: 1563685,
//   duration: 2140786,
//   mimetype: "video/mp4",
//   thumbnail_url: "mxc://finogeeks.club/FHyPlCeYUSFFxlgbQYZmoEoe",
//   thumbnail_info: {
//     h: 300,
//     w: 300,
//     size: 46144,
//     mimetype: "image/jpeg"
//   }
// }
info = convo_ui_wrapper.GetInfo(content)
url = convo_ui_wrapper.GetUrl(content)

3.5 上传音频 audio

ConvoUI Message

// prompt   : 上传提示文字
// max_size : 文件最大尺寸
audio = new convo_ui_wrapper.Audio(prompt, max_size, display)

audio.SetTypes(["mp3","wav","ogg"])  // 该数组描述可以接受的文件格式

ConvoUI Reply

// info: {
//   size: 1563685,
//   duration: 2140786,
//   mimetype: "audio/mpeg"
// }
info = convo_ui_wrapper.GetInfo(content)
url = convo_ui_wrapper.GetUrl(content)

3.6 文字输入 input

ConvoUI Message

// prompt : 输入框上方提示文字,  string 或者 array,对应 GetInput(content)
// text   : 详细说明文字,例如关于输入内容的详细解释或说明
input = new convo_ui_wrapper.Input(prompt, text, display)

input.SetHtml(false)  //text中内容是否为HTML格式化的,可省,默认非HTML格式
input.SetType("text") //可省略,默认为text

prompt 为数组时,会渲染出对应个数的input框,reply GetInput 为相应的数组值。 prompt 为字符串时,会渲染出对应的一个input框,reply GetInput 为相应的字符串值。

其中type可以填入的值暂定为:

  • text:普通文本
  • username:按要求符合用户名格式的文字(按本系统用户名允许字符集)
  • password: 密码
  • pin:纯数字密码
  • number:数字(整数或带小数部分)
  • phone:手机或电话
  • cellphone:手机号
  • email:email地址
  • multiline:多行文本,类似HTML中TextArea标签的效果

ConvoUI Reply

input = convo_ui_wrapper.GetInput(content)

3.7 获取地理位置 location

ConvoUI Message

// 可对获取的地点范围等做规定,需要时扩展
location = new convo_ui_wrapper.Location(display)

ConvoUI Reply

geo_uri = convo_ui_wrapper.GetGeoUri(content)

3.8 选择 select

用途:显示一块文字,下方提供选择项。

例如:风险评估会话中的选择题 例如:一个服务协议,下方配同意还是拒绝的选择

ConvoUI Message

// text   : 选项文字
// action : 事件代码
item = new convo_ui_wrapper.SelectItem (text, action)

item.SetSelected(true) // 可选,表示默认选中

// text : 选择说明文字,例如服务协议内容或选择题题面
select = new convo_ui_wrapper.Select([item, ...], text, display)

select.SetMultiple(false) // 是否允许多选,可省,默认单选
select.SetHtml(false)     // text中内容是否为HTML格式化的,可省,默认非HTML格式

items中的action不仅可以是字符串,也可以是数字、对象、数组等任意JSON类型,客户端回送时保持原样就好(不要做解析及类型转换)

items中的SetSelected()参数是布尔型参数,表示是否预先选中某选项,对于menu.SetMultiple(true)的多选情况,items中可以有多个成员带有item.SetSelected(true)

ConvoUI Reply

selected = convo_ui_wrapper.GetSelected(content) // 得到事件代码,如果允许多选则可能有多个

3.9 短信验证码输入 verifycode

ConvoUI Message

// prompt  : 提示文字
// expires : 过期时间的长整数时间戳(epoch毫秒值)
verifycode = new convo_ui_wrapper.Verifycode(prompt, expires, display)

ConvoUI Reply

input = convo_ui_wrapper.GetInput(content) // 用户输入的验证码内容
expires = convo_ui_wrapper.GetExpired(content) // true/false 表示已超过过期时间

3.10 文字助手 text_assist

此控件的作用是机器人端发出一组文字,由客户端提供给用户作为类似自动完成的文字。具体展示形式由客户端决定(展示在固定区域的可点击文字、或者作为输入的自动完成选单等形式)。用户可以无视这种控件产生的提示。但当用户点击(若以可点击形式出现),则等同于用户发出一条文字消息。

ConvoUI Message

// items: ["文字一", "文字二", …… ]
text_assist = new convo_ui_wrapper.TextAssist(items, display)

text_assist.SetCaption("请选择") // caption 选项是可选的,可有可无

注意:对于此控件,如果提供了display参数,则会按前述三种展示方式来显示这一控件;如果没有提供这一参数,则采用一种特殊的展示方式,例如直接展示在文字输入框附近(或者自动完成的提示区域)。所以请明确你的用途:

  • 如果主要是输入辅助作用,请不要提供 display参数!

  • 如果是业务流程中一种菜单性质的选择(和menu控件的区别是产生文字反馈而不是ConvoUI Reply),则可能提供display参数比较好。

ConvoUI Reply

此消息没有特定响应。当用户点击(若以可点击形式出现)文字时,则等同于用户发出一条普通的Matrix文字消息。

3.11 网页嵌入 url_embedded

此控件作用是在显示区直接嵌入一个外部网址。在Web客户端上,最典型的实现方式是采用iframe标签来实现。

ConvoUI Message

// caption     : 必填,标题,类似微信公众号消息头文字(app端消息渲染用,及图文消息、豆腐块消息渲染用)
// url         : 必填,浏览器可以解析并渲染的url格式, 例如:http://... 或 https://...
// thumbnail   : 必填,类似微信公众号消息中右侧的预览图,此为预览图图片地址(app端消息渲染用,及图文消息、豆腐块消息渲染用)
// description : 必填,类似微信公众号消息细节文字(app端消息渲染用,及图文消息、豆腐块消息渲染用)
// display     : 可选,显示模式, 默认为inplace,可选值有inplace、popup、fixed
url_embedded = new convo_ui_wrapper.UrlEmbedded(caption, url, thumbnail, description, display)

url_embedded.SetType("webpage")  // 可选。消息UI类型,默认为网页引入格式,可选值包括webpage(网页)、graphtext(图文)、beancurd(豆腐块)
url_embedded.SetHeight(300)      // 可选。指定网页嵌入区域的高度;宽度不可指定,因为宽度受限于聊天视图的大小
url_embedded.SetScrolling(false) // 可选。true,表示任何时候总是有滚动条;false,一律禁止滚动条。不提供此参数,表示auto(需要时自动出)
url_embedded.SetFooter(true)     // 可选。默认值为true,(只针对图文消息)表示图文消息是否渲染出底部链接

graphtext(图文)、beancurd(豆腐块)只支持 inplace 显示模式,webpage(网页)支持三种。

ConvoUI Reply

此消息主要用于展示,没有特定响应。

3.12 富文本展示 richtext

此控件的作用根据指定的富文本源以及格式,在显示区渲染出带格式文本效果。目前支持 html 格式和 markdown 格式。

ConvoUI Message

// source : 富文本源码, 例如,带有html标签的文本;或者 format = markdown 时,markdown 文本源码
richtext = new convo_ui_wrapper.Richtext(source, display)

richtext.SetFormat("html")    // 默认 html,同时也支持 markdown
richtext.SetCaption("请浏览") // 可选,标题
richtext.SetThumbnail("")     // 可选,类似微信公众号消息中右侧的预览图,此为预览图图片地址
richtext.SetDescription("")   // 可选,类似微信公众号消息细节文字
richtext.SetType("richtext")  // 可选,消息UI类型,默认为richtext(富文本),可选值包括richtext(富文本)、graphtext(图文)、beancurd(豆腐块)、recruitment(招聘消息)
richtext.SetFooter(true)      // 可选,默认值为true,(只针对图文消息)表示图文消息是否渲染出底部链接
richtext.SetAfter("")         // 可选,招聘消息中头部偏右的文本

ConvoUI Reply

此消息主要用于展示,没有特定响应。

3.13 幻灯片显示 slideshow

此控件的作用是显示一个幻灯片效果。可以看成是url_embeddedrichtext控件的一个组合容器。其成员(每页幻灯片)可以是url_embedded,也可以是richtext。

ConvoUI Message

// items : [
//   new convo_ui_wrapper.UrlEmbedded(...), // 参数同 url_embedded
//   new convo_ui_wrapper.Richtext(...), // 参数同 richtext
//   ...
// ]
slideshow = new convo_ui_wrapper.Slideshow(items, display)

slideshow.SetCaption("演示幻灯片") // 可选,如提供则在幻灯片显示区之上显示标题
slideshow.SetThumbnail("")         // 可选,类似微信公众号消息中右侧的预览图,此为预览图图片地址
slideshow.SetDescription("")       // 可选,类似微信公众号消息细节文字
slideshow.SetAutoplay(true)        // 可选,默认为true;如果为false,则幻灯片不会自动滚动,需手工切换

ConvoUI Reply

此消息主要用于展示,没有特定响应。

3.14 基本豆腐块 beancurd

豆腐块控件(类似微信分享)。

ConvoUI Message

// title   : 按钮文字
// link    : 链接地址
// content : 最多三行显示的文本内容
// icon    : 图片
beancurd = new convo_ui_wrapper.Beancurd(title, link, content, icon, display)

ConvoUI Reply

此消息主要用于展示,没有特定响应。

3.15 分页组件 pager

分页 + 按钮组(链接🔗)组件

ConvoUI Message

// title : 按钮元素的文本
// type  : 类型,"url" 或 "action"
// href  : 根据 type,按钮链接地址或action
// after : 按钮元素偏右的文本, 选填
item = new convo_ui_wrapper.PagerItem(title, type, href, after)

// size : 每页展示数据数
pager = new convo_ui_wrapper.Pager([item, ...], size, display)

pager.SetCaption("标题文字或标签文字") // 选填,组件的标题或标签说明文字
pager.SetCurrent(1)                    // 选填,number,分页初始化显示页面,默认为1
  • items是一个数组格式的数据,每个数组元素都是一个对象,包含title、href、after、after 属性

ConvoUI Reply

action = convo_ui_wrapper.GetAction(content)
result = convo_ui_wrapper.GetResult(content)

3.16 通知显示 notice

此控件作用是轻量化的通知一个状态、消息、内容。可以看成是url_embeddedrichtext及'menu'控件的一个组合容器。

ConvoUI Message

// key   : 文本
// value : 文本, KV 展示上类似一个 table
item = new convo_ui_wrapper.NoticeItem(key, value)

// caption : 标题
notice = new convo_ui_wrapper.Notice(caption, [item, ...], display)


// text      : 可选,头部内容,为空则不显示header
// font_size : 可选,默认14
// color     : 可选,默认纯黑 #000000 16 进制 RGB 色值
notice.SetHeader(text, font_size, color)

// text      : 可选,底部内容,为空则不显示footer
// font_size : 可选,默认14
// color     : 可选,默认纯黑 #000000 16 进制 RGB 色值
notice.SetFooter(text, font_size, color)

notice.SetUrl("")          // 可选 有链接则可以跳转,没有则不能跳转
notice.SetThumbnail("url") // 可选,一张小图片
notice.SetDescription("")  // 可选,类似微信公众号消息细节文字

// 可选,底部按钮,移动端建议不要超过3个
notice.SetMenu([new convo_ui_wrapper.MenuItem(title, action), new convo_ui_wrapper.MenuItem(title, action)]

ConvoUI Reply

action = convo_ui_wrapper.GetAction(content)
result = convo_ui_wrapper.GetResult(content)

4. 目前各端支持的消息的类型

ConvoUI 类型 Web iOS Android
convo.ui.button all + assist,不推荐 none none
convo.ui.menu all + assist inplace / fixed inplace / fixed
convo.ui.image all inplace / fixed
convo.ui.video none inplace / fixed
convo.ui.audio none inplace / fixed
convo.ui.input all inplace / fixed
convo.ui.location none inplace / fixed
convo.ui.select all inplace / fixed inplace / fixed
convo.ui.verifycode all inplace / fixed
convo.ui.text_assist assist assist assist
convo.ui.url_embedded all inplace
convo.ui.richtext all inplace
convo.ui.slideshow all inplace
convo.ui.beancurd inplace inplace inplace
convo.ui.graphtext inplace inplace inplace
convo.ui.pager inplace inplace inplace
convo.ui.notice inplace inplace inplace
  • all : 指inplace、fixed、popup三种形式都支持
  • none:该端不支持本控件
  • / :暂未实现,即将支持
  • 留空:尚未填写
  • 不推荐:支持但不推荐使用

Readme

Keywords

none

Package Sidebar

Install

npm i awesome-botkit-external

Weekly Downloads

1

Version

0.1.17

License

ISC

Last publish

Collaborators

  • gordanyang