-
-
Notifications
You must be signed in to change notification settings - Fork 798
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 新增微信客服接口(内测) * 新增微信小程序接口,修复参数错误 * 增加客服事件 * 格式化代码 * 修复缺少media_id错误 * 增加专属客服接口 * 格式化代码 * [微信客服] 新增获取客服帐号链接 * 修正客服账号链接url错误 * 增加发送事件响应消息 * msgid更新为选填
- Loading branch information
Showing
4 changed files
with
369 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
|
||
from wechatpy.client.api.base import BaseWeChatAPI | ||
|
||
|
||
class WeChatKF(BaseWeChatAPI): | ||
""" | ||
微信客服接口 | ||
https://work.weixin.qq.com/api/doc/90000/90135/94670 | ||
""" | ||
|
||
def sync_msg(self, token, cursor="", limit=1000): | ||
""" | ||
微信客户发送的消息、接待人员在企业微信回复的消息、发送消息接口发送失败事件(如被用户拒收) | ||
、客户点击菜单消息的回复消息,可以通过该接口获取具体的消息内容和事件。不支持读取通过发送消息接口发送的消息。 | ||
支持的消息类型:文本、图片、语音、视频、文件、位置、链接、名片、小程序、事件。 | ||
:param token: 回调事件返回的token字段,10分钟内有效;可不填,如果不填接口有严格的频率限制。不多于128字节 | ||
:param cursor: 上一次调用时返回的next_cursor,第一次拉取可以不填。不多于64字节 | ||
:param limit: 期望请求的数据量,默认值和最大值都为1000。 | ||
注意:可能会出现返回条数少于limit的情况,需结合返回的has_more字段判断是否继续请求。 | ||
:return: 接口调用结果 | ||
""" | ||
data = { | ||
"token": token, | ||
"cursor": cursor, | ||
"limit": limit, | ||
} | ||
return self._post("kf/sync_msg", data=data) | ||
|
||
def get_service_state(self, open_kfid, external_userid): | ||
""" | ||
获取会话状态 | ||
ID 状态 说明 | ||
0 未处理 新会话接入。可选择:1.直接用API自动回复消息。2.放进待接入池等待接待人员接待。3.指定接待人员进行接待 | ||
1 由智能助手接待 可使用API回复消息。可选择转入待接入池或者指定接待人员处理。 | ||
2 待接入池排队中 在待接入池中排队等待接待人员接入。可选择转为指定人员接待 | ||
3 由人工接待 人工接待中。可选择结束会话 | ||
4 已结束 会话已经结束。不允许变更会话状态,等待用户重新发起咨询 | ||
:param open_kfid: 客服帐号ID | ||
:param external_userid: 微信客户的external_userid | ||
:return: 接口调用结果 | ||
""" | ||
data = { | ||
"open_kfid": open_kfid, | ||
"external_userid": external_userid, | ||
} | ||
return self._post("kf/service_state/get", data=data) | ||
|
||
def trans_service_state(self, open_kfid, external_userid, service_state, servicer_userid=""): | ||
""" | ||
变更会话状态 | ||
:param open_kfid: 客服帐号ID | ||
:param external_userid: 微信客户的external_userid | ||
:param service_state: 当前的会话状态,状态定义参考概述中的表格 | ||
:return: 接口调用结果 | ||
""" | ||
data = { | ||
"open_kfid": open_kfid, | ||
"external_userid": external_userid, | ||
"service_state": service_state, | ||
} | ||
if servicer_userid: | ||
data["servicer_userid"] = servicer_userid | ||
return self._post("kf/service_state/trans", data=data) | ||
|
||
def get_servicer_list(self, open_kfid): | ||
""" | ||
获取接待人员列表 | ||
:param open_kfid: 客服帐号ID | ||
:return: 接口调用结果 | ||
""" | ||
data = { | ||
"open_kfid": open_kfid, | ||
} | ||
return self._get("kf/servicer/list", params=data) | ||
|
||
def add_servicer(self, open_kfid, userid_list): | ||
""" | ||
添加接待人员 | ||
添加指定客服帐号的接待人员。 | ||
:param open_kfid: 客服帐号ID | ||
:param userid_list: 接待人员userid列表 | ||
:return: 接口调用结果 | ||
""" | ||
if not isinstance(userid_list, list): | ||
userid_list = [userid_list] | ||
|
||
data = { | ||
"open_kfid": open_kfid, | ||
"userid_list": userid_list, | ||
} | ||
return self._post("kf/servicer/add", data=data) | ||
|
||
def del_servicer(self, open_kfid, userid_list): | ||
""" | ||
删除接待人员 | ||
从客服帐号删除接待人员 | ||
:param open_kfid: 客服帐号ID | ||
:param userid_list: 接待人员userid列表 | ||
:return: 接口调用结果 | ||
""" | ||
if not isinstance(userid_list, list): | ||
userid_list = [userid_list] | ||
|
||
data = { | ||
"open_kfid": open_kfid, | ||
"userid_list": userid_list, | ||
} | ||
return self._post("kf/servicer/del", data=data) | ||
|
||
def batchget_customer(self, external_userid_list): | ||
""" | ||
客户基本信息获取 | ||
:param external_userid_list: external_userid列表 | ||
:return: 接口调用结果 | ||
""" | ||
if not isinstance(external_userid_list, list): | ||
external_userid_list = [external_userid_list] | ||
|
||
data = { | ||
"external_userid_list": external_userid_list, | ||
} | ||
return self._post("kf/customer/batchget", data=data) | ||
|
||
def get_account_list(self): | ||
""" | ||
获取客服帐号列表 | ||
:return: 接口调用结果 | ||
""" | ||
return self._get("kf/account/list") | ||
|
||
def add_contact_way(self, open_kfid, scene): | ||
""" | ||
获取客服帐号链接 | ||
:param open_kfid: 客服帐号ID | ||
:param scene: 场景值,字符串类型,由开发者自定义。不多于32字节;字符串取值范围(正则表达式):[0-9a-zA-Z_-]* | ||
:return: 接口调用结果 | ||
""" | ||
data = {"open_kfid": open_kfid, "scene": scene} | ||
return self._post("kf/add_contact_way", data=data) | ||
|
||
def get_upgrade_service_config(self): | ||
""" | ||
获取配置的专员与客户群 | ||
:return: 接口调用结果 | ||
""" | ||
return self._get("kf/customer/get_upgrade_service_config") | ||
|
||
def upgrade_service(self, open_kfid, external_userid, service_type, member=None, groupchat=None): | ||
""" | ||
为客户升级为专员或客户群服务 | ||
:param open_kfid: 客服帐号ID | ||
:param external_userid: 微信客户的external_userid | ||
:param service_type: 表示是升级到专员服务还是客户群服务。1:专员服务。2:客户群服务 | ||
:param member: 推荐的服务专员,type等于1时有效 | ||
:param groupchat: 推荐的客户群,type等于2时有效 | ||
:return: 接口调用结果 | ||
""" | ||
|
||
data = { | ||
"open_kfid": open_kfid, | ||
"external_userid": external_userid, | ||
"type": service_type, | ||
} | ||
if service_type == 1: | ||
data["member"] = member | ||
else: | ||
data["groupchat"] = groupchat | ||
return self._post("kf/customer/upgrade_service", data=data) | ||
|
||
def cancel_upgrade_service(self, open_kfid, external_userid): | ||
""" | ||
为客户取消推荐 | ||
:param open_kfid: 客服帐号ID | ||
:param external_userid: 微信客户的external_userid | ||
:return: 接口调用结果 | ||
""" | ||
|
||
data = {"open_kfid": open_kfid, "external_userid": external_userid} | ||
return self._post("kf/customer/cancel_upgrade_service", data=data) | ||
|
||
def send_msg_on_event(self, code, msgtype, msg_content, msgid=None): | ||
""" | ||
当特定的事件回调消息包含code字段,可以此code为凭证,调用该接口给用户发送相应事件场景下的消息,如客服欢迎语。 | ||
支持发送消息类型:文本、菜单消息。 | ||
:param code: 事件响应消息对应的code。通过事件回调下发,仅可使用一次。 | ||
:param msgtype: 消息类型。对不同的msgtype,有相应的结构描述,详见消息类型 | ||
:param msg_content: 目前支持文本与菜单消息,具体查看文档 | ||
:param msgid: 消息ID。如果请求参数指定了msgid,则原样返回,否则系统自动生成并返回。不多于32字节; | ||
字符串取值范围(正则表达式):[0-9a-zA-Z_-]* | ||
:return: 接口调用结果 | ||
""" | ||
|
||
data = {"code": code, "msgtype": msgtype} | ||
if msgid: | ||
data["msgid"] = msgid | ||
data.update(msg_content) | ||
return self._post("kf/send_msg_on_event", data=data) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
|
||
from optionaldict import optionaldict | ||
|
||
from wechatpy.client.api.base import BaseWeChatAPI | ||
|
||
|
||
class WeChatKFMessage(BaseWeChatAPI): | ||
""" | ||
发送微信客服消息 | ||
https://work.weixin.qq.com/api/doc/90000/90135/94677 | ||
支持: | ||
* 文本消息 | ||
* 图片消息 | ||
* 语音消息 | ||
* 视频消息 | ||
* 文件消息 | ||
* 图文链接 | ||
* 小程序 | ||
* 菜单消息 | ||
* 地理位置 | ||
""" | ||
|
||
def send(self, user_id, open_kfid, msgid="", msg=None): | ||
""" | ||
当微信客户处于“新接入待处理”或“由智能助手接待”状态下,可调用该接口给用户发送消息。 | ||
注意仅当微信客户在主动发送消息给客服后的48小时内,企业可发送消息给客户,最多可发送5条消息;若用户继续发送消息,企业可再次下发消息。 | ||
支持发送消息类型:文本、图片、语音、视频、文件、图文、小程序、菜单消息、地理位置。 | ||
:param user_id: 指定接收消息的客户UserID | ||
:param open_kfid: 指定发送消息的客服帐号ID | ||
:param msgid: 指定消息ID | ||
:param tag_ids: 标签ID列表。 | ||
:param msg: 发送消息的 dict 对象 | ||
:type msg: dict | None | ||
:return: 接口调用结果 | ||
""" | ||
msg = msg or {} | ||
data = { | ||
"touser": user_id, | ||
"open_kfid": open_kfid, | ||
} | ||
if msgid: | ||
data["msgid"] = msgid | ||
data.update(msg) | ||
return self._post("kf/send_msg", data=data) | ||
|
||
def send_text(self, user_id, open_kfid, content, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "text", "text": {"content": content}}, | ||
) | ||
|
||
def send_image(self, user_id, open_kfid, media_id, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "image", "image": {"media_id": media_id}}, | ||
) | ||
|
||
def send_voice(self, user_id, open_kfid, media_id, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "voice", "voice": {"media_id": media_id}}, | ||
) | ||
|
||
def send_video(self, user_id, open_kfid, media_id, msgid=""): | ||
video_data = optionaldict() | ||
video_data["media_id"] = media_id | ||
|
||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "video", "video": dict(video_data)}, | ||
) | ||
|
||
def send_file(self, user_id, open_kfid, media_id, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "file", "file": {"media_id": media_id}}, | ||
) | ||
|
||
def send_articles_link(self, user_id, open_kfid, article, msgid=""): | ||
articles_data = { | ||
"title": article["title"], | ||
"desc": article["desc"], | ||
"url": article["url"], | ||
"thumb_media_id": article["thumb_media_id"], | ||
} | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={"msgtype": "news", "link": {"link": articles_data}}, | ||
) | ||
|
||
def send_msgmenu(self, user_id, open_kfid, head_content, menu_list, tail_content, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={ | ||
"msgtype": "msgmenu", | ||
"msgmenu": {"head_content": head_content, "list": menu_list, "tail_content": tail_content}, | ||
}, | ||
) | ||
|
||
def send_location(self, user_id, open_kfid, name, address, latitude, longitude, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={ | ||
"msgtype": "location", | ||
"msgmenu": {"name": name, "address": address, "latitude": latitude, "longitude": longitude}, | ||
}, | ||
) | ||
|
||
def send_miniprogram(self, user_id, open_kfid, appid, title, thumb_media_id, pagepath, msgid=""): | ||
return self.send( | ||
user_id, | ||
open_kfid, | ||
msgid, | ||
msg={ | ||
"msgtype": "miniprogram", | ||
"msgmenu": {"appid": appid, "title": title, "thumb_media_id": thumb_media_id, "pagepath": pagepath}, | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters