npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hwbot

v0.1.5

Published

Robot SDK for Huawei Public Platform

Downloads

7

Readme

小爱开放平台语音技能SDK


小爱开放平台语音技能的非官方nodejs SDK,帮助你轻松对接小爱开放平台,快速构建起属于自己的语音技能。

使用前需要先在小爱开放平台注册开发者身份,申请语音技能,并确定服务器URL。具体参见小爱平台文档

安装

npm install aixbot

用法

AixBot和nodejs社区著名的koa框架用法基本一致,通过定义中间件和事件监听回调来完成任务。

快速启动

以下示例实现了一个简单的语音技能:

  • 支持进入和退出技能时的礼貌用语
  • 支持用户直接询问"你是谁"
  • 其它消息环回播放
const AixBot = require('aixbot');

const aixbot = new AixBot();

// define event handler
aixbot.onEvent('enterSkill', (ctx) => {
    ctx.speak('你好').wait();
});

// define text handler
aixbot.hears('你是谁', (ctx) => {
    ctx.speak(`我是Bowen`).wait();
});

// define regex handler, echo message
aixbot.hears(/\W+/, (ctx) => {
    ctx.speak(ctx.request.query);
});

// close session
aixbot.onEvent('quitSkill', (ctx) => {
    ctx.reply('再见').closeSession();
});

// run http server
aixbot.run(8080);

HTTPS启动

AixBot默认使用http协议。由于小爱开放平台需要开发者提供https,建议最好在nginx上配置好SSL证书,然后代理到内部aixbot的端口。

AixBot也支持直接以https启动,如下。

// config your ssl key and pem
let tlsOptions = {
    key: fs.readFileSync('./keys/1522555444697.key'),
    cert: fs.readFileSync('./keys/1522555444697.pem')
};

aixbot.run(8080, '0.0.0.0', tlsOptions);

定义中间件

AixBot支持像koa那样注册中间件。AixBot当前只支持中间件使用asyncawait的方式处理异步。

const AixBot = require('aixbot');

const aixbot = new AixBot();

// define middleware for response time
aixbot.use(async (ctx, next) => {
    console.log(`process request for '${ctx.request.query}' ...`);
    var start = new Date().getTime();
    await next();
    var execTime = new Date().getTime() - start;
    console.log(`... response in duration ${execTime}ms`);
});

// define middleware for DB
aixbot.use(async (ctx, next) => {
    ctx.db = {
        username : 'Bowen'
    };
    await next();
});

// define event handler
aixbot.onEvent('enterSkill', (ctx) => {
    ctx.speak('你好').wait();
});

// define text handler
aixbot.hears('你是谁', (ctx) => {
    ctx.speak(`我是${ctx.db.username}`).wait();
});

// define regex handler
aixbot.hears(/\W+/, (ctx) => {
    ctx.speak(ctx.request.query);
});

// close session
aixbot.onEvent('quitSkill', (ctx) => {
    ctx.reply('再见').closeSession();
});

// define error handler
aixbot.onError((err, ctx) => {
    logger.error(`error occurred: ${err}`);
    ctx.reply('内部错误,稍后再试').closeSession();
});

// run http server
aixbot.run(8080);

如上我们定义了两个中间件,一个打印消息的处理时间,一个为context添加访问DB的属性。 由于中间件或者消息处理过程中可能会抛出异常,所以我们为异常定义了处理方式aixbot.onError((err, ctx) => {...})

和KOA结合使用

大多数场景下我们只用像上面那样将AixBot独立启动就可以了,但是某些场景下我们需要在同一个程序里同时发布其它的web接口,这时可以将AixBot和koa结合使用。

const AixBot = require('aixbot');

const aixbot = new AixBot();

// define axibot middleware
aixbot.use(async (ctx, next) => {
    ctx.db = {
        username : 'Bowen'
    };
    await next();
});

// define event handler
aixbot.onEvent('enterSkill', (ctx) => {
    ctx.query('你好');
});

// define text handler
aixbot.hears('你是谁', (ctx) => {
    ctx.speak(`我是${ctx.db.username}`).wait();
});

// define regex handler
aixbot.hears(/\W+/, (ctx) => {
    ctx.speak(ctx.request.query);
});

// close session
aixbot.onEvent('quitSkill', (ctx) => {
    ctx.reply('再见').closeSession();
});

// define error handler
aixbot.onError((err, ctx) => {
    logger.error(`error occurred: ${err}`);
    ctx.reply('内部错误,稍后再试').closeSession();
});

const Koa = require('koa');
const koaBody = require('koa-body');
const Router = require('koa-router');

const router = new Router();
const app = new Koa();

// koa middleware
app.use(async (ctx, next) => {
    console.log(`process request for '${ctx.request.url}' ...`);
    var start = new Date().getTime();
    await next();
    var execTime = new Date().getTime() - start;
    console.log(`... response in duration ${execTime}ms`);
});

app.use(koaBody());
router.get('/', (ctx, next) => {
    ctx.response.body = 'welcome';
    ctx.response.status = 200;    
});

// register aixbot handler to koa router
router.post('/aixbot', aixbot.httpHandler());
  
app.use(router.routes());

app.listen(8080);
console.log('KOA server is runing...');

在上面的例子里,我们没有直接调用aixbot.run(),而是使用router.post('/aixbot', aixbot.httpHandler())将aixbot的处理绑定到koa router指定的/aixbot路由上。同时我们为AixBot和koa定义了各自的消息中间件。在运行时会先执行koa的中间件,然后再根据koa的路由规则进行消息分派。分派到/aixbot上的post消息先会执行AixBot的中间件,然后执行对应的已注册的AixBot消息回调。

对接NLU平台

AixBot支持对小爱发来的消息按照事件类型或者消息内容定义回调方法,并支持对消息内容以正则表达式的方式定义规则。但是如果需要完成复杂的语音技能,就必须对接功能完备的NLU处理平台。

对于NLU处理平台,最直接的是使用小爱开放平台的NLU配置界面进行配置,配置好后在收到的消息里就会携带NLU处理后得到的intent和slot信息。

AixBot可以监听指定的intent,在context中可以取出对应的slot信息。

// define intent handler
aixbot.onIntent('query-weather', (ctx) => {
    console.log(JSON.stringify(ctx.request.slotInfo));
});

如果需要完成更复杂的NLU处理,可以将AixBot对接其它更专业的NLU处理平台。遗憾的是DialogFlowwit.ai目前都在墙外,微软的LUIS当前还可以用。国内类似的开放平台也有,基本和小爱当前的NLU能力差不多。作为一名程序员,说实话我不是很喜欢这种通过网页配置的方式来构建对话,我更喜欢经过良好封装的能够以代码的形式来定义和处理对话的chatbot引擎库,这样可以更加灵活地完成复杂功能。如果你自己有类似的NLU处理能力,那就会很方便了。

AixBot和非小爱的NLU平台对接,无非是在AixBot的回调里面将小爱发来消息里的对话内容转发到对应的NLU平台,然后根据NLU平台的返回结果构造给小爱的回复。这里和具体的NLU平台相关,就不再详述了。

API

AixBot

AixBot API reference

const AixBot = require('aixbot')

Constructor

Initialize new AixBot bot.

const aixbot = new AixBot([appId])

| Param | Type | Description | | --- | --- | --- | | [appId] | String | app_id of skill |

在小爱开放平台上申请的每一个技能都有一个app_id。 如果需要对收到的每条消息的app_id进行严格校验,则在构造AixBot的实例时提供该值。

use

Registers a middleware.

aixbot.use(...middleware)

| Param | Type | Description | | --- | --- | --- | | middleware | function | Middleware function |

aixbot.use(async (ctx, next) => {
    ctx.db = {
        username : 'Bowen'
    };
    await next();
});

onEvent

Registers event handler.

aixbot.onEvent(eventType, handler)

| Param | Type | Description | | --- | --- | --- | | eventType | String | event type | | handler | function | handler function |

现在支持以下事件类型:

| Event Type | Description | | --- | --- | | enterSkill | 进入技能 | | quitSkill | 离开技能 | | inSkill | 技能进行中 | | noResponse | 音箱无响应 | | recordFinish | 录音完成 | | recordFail | 录音失败 | | playFinishing | 录音播放即将完成 |

aixbot.onEvent('enterSkill', (ctx) => {
    ctx.speak('你好').wait();
});

aixbot.onEvent('inSkill', (ctx) => {
    console.log(`received : ${ctx.request.query}`);
});

注意:inSkill事件的处理优先级是最低的,比随后介绍的onIntentonTextonRegExp都要低。可以用它来做一些默认处理。

onIntent

Registers intent handler.

aixbot.onIntent(intent, handler)

| Param | Type | Description | | --- | --- | --- | | intent | String | intent name | | handler | function | handler function |

aixbot.onIntent('query-weather', (ctx) => {
    console.log(JSON.stringify(ctx.request.slotInfo));
});

onText

Registers text handler.

aixbot.onText(text, handler)

| Param | Type | Description | | --- | --- | --- | | text | String | query content | | handler | function | handler function |

aixbot.onText('hi', (ctx) => {
    ctx.speak('hello');
});

onRegExp

Registers regex handler.

aixbot.onRegExp(regex, handler)

| Param | Type | Description | | --- | --- | --- | | regex | RegExp | regular expression | | handler | function | handler function |

aixbot.onRegExp(/\d+/, (ctx) => {
    ctx.speak(`收到数字:${ctx.request.query}`);
});

注意:所有regex handler的优先级低于text handler

hears

Wrapper of onText and onRegExp.

aixbot.hear(text, handler)

| Param | Type | Description | | --- | --- | --- | | text | String or RegExp | query or regular expression | | handler | function | handler function |

aixbot.hears('你是谁', (ctx) => {
    ctx.speak(`我是${ctx.db.username}`).wait();
});

aixbot.hears(/\W+/, (ctx) => {
    ctx.speak(ctx.request.query);
});

onError

Registers error handler.

aixbot.onError(handler)

| Param | Type | Description | | --- | --- | --- | | handler | function | handler function |

aixbot.onError((err, ctx) => {
    logger.error(`error occurred: ${err}`);
    ctx.reply('内部错误,稍后再试').closeSession();
});

run

Run http/https server.

aixbot.run(port, host, tlsOptions)

| Param | Type | Description | | --- | --- | --- | | port | number | port number | | host | String | host address | | tlsOptions | object | https options |

如果不提供tlsOptions,则启动http server,否则启动https server

let tlsOptions = {
    key: fs.readFileSync('./keys/1522555444697.key'),
    cert: fs.readFileSync('./keys/1522555444697.pem')
};

aixbot.run(8080, '0.0.0.0', tlsOptions);

httpHandler

get middleware for KOA.

aixbot.httpHandler()

const router = new Router();
const app = new Koa();
app.use(koaBody());
router.post('/aixbot', aixbot.httpHandler());  
app.use(router.routes());
app.listen(8080);

Context

Context API reference.

Context是每一个Aixbot中间件和消息回调的参数,通过它可以得到request和response,访问request和response的属性和方法。

aixbot.onEvent('enterSkill', (ctx) => {
    console.log(JSON.stringify(ctx.request.body)); // 打印接收消息体的所有内容
    console.log(ctx.request.query); // 打印接收到的消息文本;具体Request封装过的属性和接口参见Request的API介绍
    ctx.response.reply('欢迎!'); // 构造回复消息;具体Response封装过的属性和接口参见Response的API介绍
    console.log(JSON.stringify(ctx.response.body)); // 打印发送消息体的所有内容
});

另外,为了方便使用,Context代理了Response的一些主要接口,这些接口可以通过Context直接使用。例如:

aixbot.onEvent('enterSkill', (ctx) => {
    ctx.reply('欢迎!'); // 效果和 ctx.response.reply('欢迎!') 相同
    console.log(JSON.stringify(ctx.body)); // ctx.body 和 ctx.response.body 相同
});

由于Response支持连贯接口调用,所以Context上代理的Response接口也同样支持。

aixbot.hears('你是谁', (ctx) => {
    ctx.speak('我是Bowen,你是谁?').wait(); // wait()指示开启麦克风,用于直接的多轮对话
});

最后,Context的存在方便中间件为其添加其它的属性和方法:

aixbot.use(async (ctx, next) => {
    ctx.db = {
        username : 'Bowen'
    };
    await next();
});

aixbot.hears('你是谁', (ctx) => {
    ctx.speak(`我是${ctx.db.username}`).wait();
});

Request

Request API reference.

Request封装了从小爱收到的消息体。通过Context可以访问到Request实例:ctx.request

Request对接收消息体进行了封装,对常用字段提供了直接的读取属性。

| attribute | type | Description | | --- | --- | --- | | body | object | 消息体原始内容 | | query | String | message.request.query | | session | object | message.session | | appId | String | message.session.application.app_id | | user | object | message.session.user | | context | object | message.context | | slotInfo | object | message.request.slot_info | | intentName | String | message.request.slot_info.intent_name | | eventType | String | message.request.event_type | | eventProperty | object | message.request.event_property | | requestId | String | message.request.request_id | | requestType | number | message.request.type | | isEnterSkill | boolean | message.request.type == 0| | isInSkill | boolean | message.request.type == 1| | isQuitSkill | boolean | message.request.type == 2| | isNoResponse | boolean | message.request.no_response| | isRecordFinish | boolean | message.request.event_type == 'leavemsg.finished'| | isRecordFail | boolean | message.request.event_type == 'leavemsg.failed'| | isPlayFinishing | boolean | message.request.event_type == 'mediaplayer.playbacknearlyfinished'|

aixbot.hears(/\W+/, (ctx) => {
    console.log(ctx.request.appId);
    console.log(ctx.request.query);
    if (ctx.request.isNoResponse) {
        console.log('received no response');
    }
    // ...
})

Response

Response API reference.

Response封装了发送给小爱的消息,通过ctx.response可以获取到Response的实例。

Response对发送消息体进行了封装,提供了更具有语义性的操作接口。

speak

Reply a text.

ctx.response.speak(text)

| Param | Type | Description | | --- | --- | --- | | text | String | 返回的消息文本 |

speak默认是关闭麦克风的,如果想要打开麦克风则需要和后面的wait接口一起使用。

wait

Open mic.

ctx.response.speak(text).wait()

wait接口不能单独使用,必须跟在其它有内容回复的接口后面。

query

response.speak(text).wait()的语法糖,可以直接写 response.query(text)

reply

response.speak(text)等价,可以直接写 response.reply(text)

directiveAudio

Reply a audio directive.

directiveAudio(url, token, offsetMs)

| Param | Type | Description | | --- | --- | --- | | url | String | 资源url | | token | String | 获取资源的token | | offsetMs | Long | 偏移时间 |

directiveTts

Reply a tts directive.

directiveTts(text)

| Param | Type | Description | | --- | --- | --- | | text | String | 语音合成文本 |

directiveRecord

Reply a record directive.

directiveRecord(fileId)

| Param | Type | Description | | --- | --- | --- | | fileId | String | 录音文件ID |

display

Reply a display.

display(type, url, text, template)

| Param | Type | Description | | --- | --- | --- | | type | Int | 1:html,2:native ui,3:widgets | | url | String | html address | | text | String | display text | | template | UlTemplate | 参见 UlTemplate |

setSession

Add paramter in session.

为当前对话上下文的session中添加变量,小爱会在随后的消息中携带该session参数。

setSession(obj)

| Param | Type | Description | | --- | --- | --- | | obj | Any | parameter store in session |

playMsgs

Reply to play record msgs.

指示播放列表中所有的录音文件。

playMsgs(fileIdList)

| Param | Type | Description | | --- | --- | --- | | fileIdList | Array | file_id array |

ctx.response.speak('请收听录音').playMsgs(['4747c167f000400f15f4d42x'])

registerPlayFinishing

指示播放录音即将完成后发送回调消息,具体参见小爱相关文档

ctx.response.speak('请收听录音').playMsgs(['4747c167f000400f15f4d42x']).registerPlayFinishing();

launchQuickApp

启动特定路径的快应用。 快应用语音技能的注册及配置见小爱文档

launchQuickApp(path)

| Param | Type | Description | | --- | --- | --- | | path | String | path of quick app |

ctx.response.launchQuickApp('/')

launchApp

启动APP。 启动APP的语音技能的注册及配置见小爱文档

launchApp(type, uri, permission)

| Param | Type | Description | | --- | --- | --- | | type | String | 启动APP的intent的类型;支持的类型 1 activity; 2 service; 3 broadcast | | uri | String | 启动APP的路径 | | permission | String | 权限信息;非必须参数 |

ctx.response.launchApp('activity', 'xxxxxxx')

record

指示开始录音,跟在回复后面使用。

ctx.response.speak('start record').record()

closeSession

指示结束回话,跟在回复后面使用。

ctx.response.speak('bye').closeSession()

notUnderstand

指示未理解的对话,跟在回复后面使用。

ctx.response.speak('what').notUnderstand()

body

获取消息体内容

ctx.response.speak('hello');
console.log(JSON.stringify(ctx.response.body));

context delegates

为了方便使用,Context对Response的下列属性和方法进行了代理:

  • speak
  • reply
  • query
  • directiveAudio
  • directiveTts
  • directiveRecord
  • display
  • playMsgs
  • launchQuickApp
  • launchApp
  • body
ctx.speak('hi').wait(); // same as : ctx.response.speak('hi').wait()

其它

源码在github,有问题请提issue。

使用 npm test可以对源码进行测试。

如果运行时想打开AixBot的debug打印,可以在启动时加上 DEBUG=aixbot:*,例如DEBUG=aixbot:* node index.js

本人使用的是 node 8.11.1版本,其它更低版本的不支持class,const,let,async,await等特性的node版本请绕路。

作者