No description or website provided.
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
dueros support get exit reason by reqeust.get_event_data Nov 18, 2018
CHANGELOG.md
README.md
_config.yml Set theme jekyll-theme-slate Jul 24, 2018
requirements.txt
setup.py
start.sh

README.md

bot-sdk

SDK结构介绍

  • Bot.py为SDK的入口,用于接收DuerOS请求并返回结果
  • Nlu.py负责对请求关键信息的提取,如槽位、意图信息等
  • Request.py技能接收到DuerOS的数据全部交给Request进行处理,Request再委托Nlu、Session对数据做处理
  • Response.py技能数据处理完后交由Response封装结果返回DuerOS
  • Session.py处理会话信息
  • Certificate.py封装DuerOS和技能通信认证
  • card目录处理展示卡片相关
  • directive目录生成指令相关比如:浏览器指令、音频指令
  • tests 目录存放本地测试代码
  • samples 示例demo,其中包括guess_num、audio_play、personal_income_tax

安装、使用BOT SDK进行开发

1、 通过pip进行安装

pip install dueros-bot

2、 下载源码安装

  • 通过GitHub获取最新源码
git clone https://github.com/jokenwang/bot-sdk-python.git

下载bot-sdk代码后,可以使用如下命令安装:

python setup.py install

3、sh start.sh 运行,如出现问题请参考常见问题

4、运行tests目录下的

sh postData.sh json/xxx.json

将xxx.json文件内容发送模拟数据到服务器

5、开发教程

为了开始使用BOT SDK,你需要先新建一个python文件,比如文件名是Bot.py,该文件需要继承sdk/Bot.py。下一步,我们处理意图,Bot-sdk提供个函数来handle这些意图,例如继承sdk/Bot.py中的add_intent_handler函数,添加一个意图处理函数,比如,为新建闹钟,创建一个handler,在构造函数中添加:

self.add_intent_handler('remind', self.createRemind)
def createRemind(self):
    remindTime = self.get_slots('remindTime')
    if remindTime:
        card = new TextCard('创建中')
        return {
            'card': card,
        }

第一个参数代表意图名称,第二个参数代表意图命中后的回调函数,这里addHandler可以用来建立intent和handler的映射,第一个参数意图名称是条件,如果满足则执行对应的回调函数(第二个参数)。 其中回调函数中,self指向当前的Bot,getSlots继承自父类Bot,通过slot名字来获取对应的槽位值。回调函数返回值是一个字典,可以包含多个字段,比如:card、directives、outputSpeech、reprompt等,下面会一一给出示例。

设备相关(Bot方法)

  • 客户端是否支持屏幕展示
    bot.is_support_display()
  • 客户端是否支持音频播放
    bot.is_support_audio_player()
  • 客户端是否支持视频播放
    bot.is_support_audio_player()

card展示卡片

  • 文本卡片:TextCard
card = TextCard('content')
or 
card = TextCard()
//设置链接
card.set_anchor('http://www.baidu.com')
//设置cueWords
card.add_cue_words('hint1')
  • 标准卡片 StandardCard
card = StandardCard()
card.set_title('title')
card.set_content('content')
card.set_image('http://www...')
card.set_anchor('http://www.baidu.com')
  • 列表卡片ListCard
card = new ListCard()
item = new ListCardItem()
item.set_title('title')
item.set_content('content')
item.set_url('http://www')
item.set_image('http://www.png')
card.add_item(item)
  • 图片卡片ImageCard
card = ImageCard()
card.add_item('http://src.image', 'http://thumbnail.image');
  • 用户授权LinkAccountCard
card = LinkAccountCard()

文本展现模板

  • BodyTemplate1
bodyTemplate = BodyTemplate1()
bodyTemplate.set_token('token')
#设置模版背景图片
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
bodyTemplate.set_title('托尔斯泰的格言')
#设置模版plain类型的文本
bodyTemplate.set_plain_text_content('拖尔斯泰-理想的书籍是智慧的钥匙') 
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是BodyTemplate1模板'
}

上图下文模版

  • BodyTemplate2
bodyTemplate = BodyTemplate2()
#设置模版token
bodyTemplate.set_token('token')
#设置模版展示图片
bodyTemplate.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版背景图片
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
bodyTemplate.set_title('托尔斯泰的格言')
#设置模版plain类型的文本结构
bodyTemplate.set_plain_content('拖尔斯泰-理想的书籍是智慧的钥匙') 
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives':[directive],
    'outputSpeech': '这是BodyTemplate2模板'
}

左图右文模版

  • BodyTemplate3
bodyTemplate = BodyTemplate3()
#设置模版token
bodyTemplate.set_token('token')
#设置模版展示图片
bodyTemplate.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版背景图片
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
bodyTemplate.set_title('托尔斯泰的格言')
#设置模版plain类型的文本结构
bodyTemplate.set_plain_content('拖尔斯泰-理想的书籍是智慧的钥匙')  
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives': [directive],
    'outputSpeech' : '这是BodyTemplate3模板'
}

右图左文

  • BodyTemplate4
bodyTemplate = BodyTemplate4()
bodyTemplate.set_token('token')
#设置模版展示图片
bodyTemplate.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版背景图片
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
bodyTemplate.set_title('托尔斯泰的格言')
#设置模版plain类型的文本结构
bodyTemplate.set_plain_content('拖尔斯泰-理想的书籍是智慧的钥匙') 
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是BodyTemplate4模板'
}

图片模板

  • BodyTemplate5
bodyTemplate = BodyTemplate5()
#设置模版token
bodyTemplate.set_token('token')
#模版图片数组添加一张图片
bodyTemplate.add_images('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版背景图片
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
bodyTemplate.set_title('托尔斯泰的格言')
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是BodyTemplate5模板'
}

上图下文模版类

bodyTemplate = BodyTemplate6()
bodyTemplate.set_token('token')
bodyTemplate.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
bodyTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
bodyTemplate.set_title('托尔斯泰的格言')
bodyTemplate.set_plain_content('拖尔斯泰-理想的书籍是智慧的钥匙')
#定义RenderTemplate指令
directive = RenderTemplate(bodyTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是BodyTemplate6模板'
}

横向列表模板

  • ListTemplate1
listTemplate = ListTemplate1()
#设置模板token
listTemplate.set_token('token')
#设置模板背景图
listTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
listTemplate.set_title('托尔斯泰的格言')

#设置模版列表数组listItems其中一项,即列表的一个元素
listTemplateItem = ListTemplateItem()
listTemplateItem.set_token('token')
listTemplateItem.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
listTemplateItem.set_plain_primary_text('一级标题')
listTemplateItem.set_plain_secondary_text('二级标题')

#把listTemplateItem添加到模版listItems
listTemplate.add_item(listTemplateItem)
#定义RenderTemplate指令
directive = RenderTemplate(listTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是ListTemplate1模板'
}

纵向列表模板

  • ListTemplate2
listTemplate = ListTemplate2()
#设置模板token
listTemplate.set_token('token')
#设置模板背景图
listTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
listTemplate.set_title('托尔斯泰的格言')

#设置模版列表数组listItems其中一项,即列表的一个元素
listTemplateItem = ListTemplateItem()
listTemplateItem.set_token('token')
listTemplateItem.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
listTemplateItem.set_plain_primary_text('一级标题')
listTemplateItem.set_plain_secondary_text('二级标题')

#把listTemplateItem添加到模版listItems
listTemplate.add_item(listTemplateItem)
#定义RenderTemplate指令
directive = RenderTemplate(listTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是ListTemplate2模板'
}

横向列表

*ListTemplate3

listTemplate = ListTemplate3()
#设置模板token
listTemplate.set_token('token')
#设置模板背景图
listTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
listTemplate.set_title('托尔斯泰的格言')

#设置模版列表数组listItems其中一项,即列表的一个元素
listTemplateItem = ListTemplateItem()
listTemplateItem.set_token('token')
listTemplateItem.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
listTemplateItem.set_plain_primary_text('一级标题')
listTemplateItem.set_plain_secondary_text('二级标题')

#把listTemplateItem添加到模版listItems
listTemplate.add_item(listTemplateItem)
#定义RenderTemplate指令
directive = RenderTemplate(listTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是ListTemplate3模板'
}

纵向列表

*ListTemplate4

listTemplate = ListTemplate4()
#设置模板token
listTemplate.set_token('token')
#设置模板背景图
listTemplate.set_background_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
#设置模版标题
listTemplate.set_title('托尔斯泰的格言')

#设置模版列表数组listItems其中一项,即列表的一个元素
listTemplateItem = ListTemplateItem()
listTemplateItem.set_token('token')
listTemplateItem.set_image('https://skillstore.cdn.bcebos.com/icon/100/c709eed1-c07a-be4a-b242-0b0d8b777041.jpg')
listTemplateItem.set_plain_primary_text('一级标题')
listTemplateItem.set_plain_secondary_text('二级标题')

#把listTemplateItem添加到模版listItems
listTemplate.add_item(listTemplateItem)
#定义RenderTemplate指令
directive = RenderTemplate(listTemplate)
return {
    'directives': [directive],
    'outputSpeech': '这是ListTemplate4模板'
}

Tag标签

*Tag 标签用在List模板的每个Item上(显示在每个item的右下角),比如:付费、免费、最新、VIP、限时、已购、最热以及自定义标签内容等 PayTag、FreeTag、NewTag、HotTag、VipTag、TimeTag、PurchasedTag、HotTag、CustomTag、AmountTag、AuditionTag

音频播放

  • 音频播放
directive = Play('http://www.baidu.com')
#设置音频格式
directive.set_stream_format('AUDIO_M3U8')
#上一首
previous = PreviousButton()
previous.set_selected(True)
# 创建暂停按钮
playpause = PlayPauseButton()
#下一首
next = NextButton()
#可以添加多个button  比如:收藏、喜欢、播放列表等
controls = [previous, playpause, NextButton()]
playerInfo = PlayerInfo('周杰伦 七里香', controls)
playerInfo.set_title('周杰伦')
playerInfo.set_title_subtext1('七里香')
playerInfo.set_art('http://adfadfa')
# 设置Play指令的PlayerInfo
directive.set_player_info(playerInfo)
return{
    'directives': [directive]
}
  • 视频播放
directive = VideoPlayer('video_url', PlayBehaviorEnum.REPLACE_ENQUEUED)
directive.set_offset_in_milliseconds(121321)
directive.set_expiry_time('123213223')
directive.set_expected_previous_token('asdsd-1233-dsew-39FG')
directive.set_report_delay_in_ms(1234.12212)
directive.set_report_interval_in_ms(123)
directive.set_token('AGDG-SAHSHD_ASDS_123')
directive.set_url('http://set-url.com')
return{
    'directives': [directive]
}

音频、视频播放列表

  • RenderAudioList 用于渲染音频播放列表。当在播放页面,点击播放列表按钮,可返回RenderAudioList用于渲染UI

  • RenderVideoList 用于渲染视频播放列表。当在播放页面,点击播放列表按钮,可返回RenderVideoList用于渲染UI

权限申请

1、AskForPermission 当技能需要获取用户权限:用户信息、位置信息等, 需要向用户进行权限申请。目前只支持用户权限的申请 2、 比如获取用户信息权限如下

directive = AskForPermissionsConsent()
directive.add_permission(PermissionEnum.PERMISSION_USER_INFO)

3、 添加事件回调处理

#用户允许授权 回调
self.add_permission_granted_event(func)
#表示用户拒绝授权
self.add_permission_rejected_event(func)
#表示用户同意授权,但是由于其他原因导致授权失败
self.add_permission_grant_failed_event(func)

4、获取用户信息, 如果用户允许获取权限那么可以在回调方法中去获取用户的信息

curl -X GET \
  https://xiaodu.baidu.com/saiya/v1/user/profile \
  -H 'authorization: bearer {apiAccessToken}'

或通过urlib发起GET请求发送数据, 将authorization字段放到请求的header中
注意:apiAccessToken从Launchrequest请求中获取, 通过下面方式可获取到

self.get_api_access_token()

如果返回成功,会获得用户的信息

{
	"status": 0,
	"msg": "ok",
	"data": {
		"nickname": "",
		"phone": "xxxx",
		"email": "xxx",
		"portrait": "xxx"
	},
	"logId": "xxxx"
}

status 字段详见Dueros文档

directive指令

  • 播放指令 AudioPlayer.Play
directives = []
directive = Play('http://www.music', PlayBehaviorEnum.REPLACE_ALL)
directives.append(directive)
return {
    'directives': directives,
    'outputSpeech': '正在为你播放歌曲',
}
  • 停止端上的播放音频 AudioPlayer.Stop
directives = []
directive = Stop()
directives.append(directive)
return {
    'directives': directives,
    'outputSpeech': '已停止播放',
}

设置好handler之后,就可以实例化刚刚定义的Bot,在webserver中接受DuerOS来的请求。例如samples中的文件。

返回speech

  • outputSpeech 上面例子,除了返回card之外,还可以返回outputSpeech,让客户端播报tts:
return {
    'outputSpeech': '请问你要干啥呢',
    'outputSpeech': '<speak>请问你要干啥呢</speak>'
}
  • reprompt 当客户端响应用户后,用户可能会一段时间不说话,如果你返回了reprompt,客户端会提示用户输入
return {
    'reprompt': '请问你要干啥呢',
    #或者ssml
    'reprompt': '<speak>请问你要干啥呢</speak>'
}

Lanuch & SessionEnd

  • bot开始服务 当bot被@(通过bot唤醒名打开时),DuerOS会发送LanuchRequest给bot,此时,bot可以返回欢迎语或者操作提示:
def launchRequest(self):
    return {
        'outputSpeech': r'欢迎进入'
    }

self.add_launch_handler(self.launchRequest)
  • bot 结束服务 当用户表达退出bot时,DuerOS会发送SessionEndedRequest:
def endRequest(self):
    ```
    清空状态,结束会话
    ```
self.add_session_ended_handler(self.endRequest)

使多轮对话管理更加简单

往往用户一次表达的需求,信息不一定完整,比如:'给我创建一个闹钟',由于query中没有提醒的时间,一个好的bot实现会问用户:'我应该什么时候提醒你呢?',这时用户说明天上午8点,这样bot就能获取设置时间,可以为用户创建一个闹钟。比如,你可以这样来实现:

def getRemindSlot(self):
    remindTime = self.getSlots('remind_time');
    if remindTime:
        return 返回设置闹钟指令
    self.nlu.ask('remind_time')
    return {
        'outputSpeech': r'要几点的闹钟呢?'
    }
self.add_launch_handler(self.getRemindSlot)

事件Events

  • Display.ElementSelected 展示列表的item被选中会触发此事件,端点会上送此事件

  • Display.ButtonClicked 展示类型的Button被点击,端点会上送此事件

  • Form.ButtonClicked 音频或视频播放页面按钮被点击(上一个、下一个、重复、收藏、收藏列表、播放列表),端点会上送此事件

  • AudioPlayer 、VideoPlayer 播放也有对应事件,详看官方文档

监听Events

def dealAlertEvent(self, event):
    card = TextCard('闹钟创建成功')
    return {
        'card': card,
    }
self.add_event_listener('Alerts.SetAlertSucceeded', self.dealAlertEvent)

event就是上送给技能的事件,里面包含事件类型、token等信息,可以通过event数据来做对应的业务。

Bot-sdk会根据通过add_event_listener添加的event handler来匹配对应的事件类型。

Bot-sdk会根据通过add_intent_handler添加handler的顺序来遍历所有的检查条件,寻找条件满足的handler来执行回调,并且当回调函数返回值不是None时结束遍历,将这个不为None的值返回。

NLU会维护slot的值,merge每次对话解析出的slot,你可以不用自己来处理,DuerOS每次请求Bot时会将merge的slot都下发。session内的数据完全由你来维护,你可以用来存储一些状态,比如打车Bot会用来存储当前的订单状态。你可以通过如下接口来使用slot和session:

get_slot('slot name')
set_slot('slot name', 'slot value'); #如果没有找到对应的slot,会自动新增一个slot
#session
get_session_attribute('key')
set_session_attribute('key', 'value')
#or
set_session_attribute('key.key1', 'value')
get_session_attribute('key.key1')
#清空session
clear()

你的Bot可以订阅端上触发的事件,通过接口add_event_listener实现,比如端上设置闹钟成功后,会下发SetAlertSucceeded的事件,Bot通过注册事件处理函数,进行相关的操作。

NLU交互协议

在DuerOS Bot Platform平台,可以通过nlu工具,添加了针对槽位询问的配置,包括: 1、是否必选,对应询问的默认话术 2、是否需要用户确认槽位内容,以及对应的话术 3、是否需要用户在执行动作前,对所有的槽位确认一遍,以及对应的话术 针对填槽多轮,Bot发起对用户收集、确认槽位(如果针对特定槽位有设置确认选项,就进行确认)、确认意图(如果有设置确认选项)的询问,bot-sdk提供了方便的快捷函数支持: 注意:一次返回的对话directive,只有一个,如果多次设置,只有最后一次的生效

  • ask 多轮对话的bot,会通过询问用户来收集完成任务所需要的槽位信息,询问用户的特点总结为3点,ask:问一个特定的槽位。比如,打车服务收到用户的打车意图的时候,发现没有提供目的地,就可以ask destination(目的地的槽位名):
#命中打车意图rent_car.book,但是没有提供目的地
def RentCar(self):
    destination = self.get_slots('destination')
    if not destination:
        self.nlu.ask('destination')
        card = TextCard('打车去哪呢')
        return {
            'card': card,
        }
self.add_intent_handler('rent_car.book', self.RentCar)
  • delegate 将处理交给DuerOS的对话管理模块DM(Dialog Management),按事先配置的顺序,包括对缺失槽位的询问,槽位值的确认(如果设置了槽位需要确认,以及确认的话术),整个意图的确认(如果设置了意图需要确认,以及确认的话术。比如可以将收集的槽位依次列出,等待用户确认)
return self.nlu.set_delegate()
  • confirm slot 主动发起对一个槽位的确认,此时还需同时返回询问的outputSpeach。主动发起的确认,DM不会使用默认配置的话术。
self.nlu.set_confirm_slot('money')
return {
    'outputSpeech': '你确认充话费:10000000000',
    }
  • confirm intent 主动发起对一个意图的确认,此时还需同时返回询问的outputSpeach。主动发起的确认,DM不会使用默认配置的话术。一般当槽位填槽完毕,在进行下一步操作之前,一次性的询问各个槽位,是否符合用户预期。
money = self.get_slots('money')
phone = self.get_slots('phone')
if money and phone:
    self.nlu.set_confirm_intent()
    return {
        'outputSpeech': '你确认充话费:' + money + ',充值手机:' + phone,
    }

第三方授权(有屏设备)

当需要第三方资源,比如访问新浪微博资源时,需要用户授权才能访问第三方资源,此时就需要 用户授权此应用可以访问自己在新浪微博上的资源。 注意注意:debug模式下,在第三方配置的回调地址要写成:https://xiaodu-dbp.baidu.com/saiya/auth/xxxx

card = LinkAccountCard()
return {
    'card': card
}

返回card后会在屏幕展示一张二维码,通过手机扫码即可完成授权。有屏设备再完成授权后会发送授权事件:Connections.Response 数据格式如下:

{
    'dialogRequestId': '',
    'name': 'LinkAccountSucceeded', 
    'timestamp': 'xxxx',
    'token': 'xxxx', //第三方授权成功返回的token
    'requestId': 'xxxx',
    'type': 'Connections.Response'
}

插件

可以使用如下命令安装:你还可以写插件(拦截器Intercept),干预对话流程、干预返回结果。比如,用户没有通过百度帐号登录,bot直接让用户去登录,不响应意图,可以使用LoginIntercept:

loginIntercept = LoginIntercept()
self.add_intercept(loginIntercept)

开发自己的拦截器,继承Intercept,通过重载preprocess,能够在处理通过addHandler、addEventListener添加的回调之前,定义一些逻辑。通过重载postprocess能够对回调函数的返回值,进行统一的处理:

class YourIntercept(Intercept):
    def preprocess(self, bot):
        '''
        bot: 你的bot实例化对象
        '''

    def postprocess(self, bot, result):
        '''
        maybe format result 
        '''
        return result

intercept可以定义多个,执行顺序,以调用addIntercept的顺序来执行

技能数据验证

Bot技能支持数据验证(默认数据验证是关闭的),确保数据的来源的可靠性。(确保已经在技能平台配置了公钥)

  • 初始化数据校验
self.init_certificate(environ, private_key='')
  • 开启数据验证
self.enable_verify_request_sign()

详见文档通信认证

技能数据统计

Bot默认未开启技能数据统计功能,需要手动开启。(确保已经在技能平台配置了公钥)

self.set_monitor_enabled(True)

之后设置自己的私钥和环境(0:Debug模式, 1:online模式)

self.set_environment_info(private_key, environment=0)

常见问题

  • 运行sh start.sh 出现 ImportError: No module named OpenSSL 执行下面命令
sudo pip install pyOpenSSL
  • ImportError: No module named Crypto.PublicKey 执行下面命令
sudo pip install pycrypto
  • ImportError: No module named requests 执行下面命令
sudo pip install requests

或者在根目录执行下面命令解决全部问题

pip install -r requirements.txt

技能调试

为了避免每次调试都要部署到服务器可以使用ngrok将请求数据转发到自己的机器上(注意:ngrok访问比较慢,有时会链接超时)

变更记录

  • 版本变更详见变更记录 CHANGELOG.md

Known Users

按照登记顺序排序,更多接入技能,欢迎在 https://github.com/jokenwang/bot-sdk-python/issues/16 登记(仅供用户参考)

鸣谢

@gongqingliang821

免责声明

  • 此SDK非官网提供,纯属个人学习研究,如因使用此SDK导致的任何损失,本人概不负责。