优化工单模块,增加企微模块

This commit is contained in:
mgw
2024-07-10 15:58:47 +08:00
parent e8512b23e4
commit 6b140fe6dd
134 changed files with 12830 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.enterprise.parser import parse_message # NOQA
from wechatpy.enterprise.replies import create_reply # NOQA
from wechatpy.enterprise.crypto import WeChatCrypto # NOQA
from wechatpy.enterprise.client import WeChatClient # NOQA

View File

@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.base import BaseWeChatClient
from wechatpy.enterprise.client import api
class WeChatClient(BaseWeChatClient):
API_BASE_URL = 'https://qyapi.weixin.qq.com/cgi-bin/'
user = api.WeChatUser()
department = api.WeChatDepartment()
menu = api.WeChatMenu()
message = api.WeChatMessage()
tag = api.WeChatTag()
media = api.WeChatMedia()
misc = api.WeChatMisc()
agent = api.WeChatAgent()
batch = api.WeChatBatch()
jsapi = api.WeChatJSAPI()
material = api.WeChatMaterial()
oauth = api.WeChatOAuth()
shakearound = api.WeChatShakeAround()
service = api.WeChatService()
chat = api.WeChatChat()
def __init__(self, corp_id, secret, access_token=None,
session=None, timeout=None, auto_retry=True):
super(WeChatClient, self).__init__(
corp_id, access_token, session, timeout, auto_retry
)
self.corp_id = corp_id
self.secret = secret
def fetch_access_token(self):
""" Fetch access token"""
return self._fetch_access_token(
url='https://qyapi.weixin.qq.com/cgi-bin/gettoken',
params={
'corpid': self.corp_id,
'corpsecret': self.secret
}
)

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.enterprise.client.api.department import WeChatDepartment # NOQA
from wechatpy.enterprise.client.api.media import WeChatMedia # NOQA
from wechatpy.enterprise.client.api.message import WeChatMessage # NOQA
from wechatpy.enterprise.client.api.menu import WeChatMenu # NOQA
from wechatpy.enterprise.client.api.tag import WeChatTag # NOQA
from wechatpy.enterprise.client.api.user import WeChatUser # NOQA
from wechatpy.enterprise.client.api.misc import WeChatMisc # NOQA
from wechatpy.enterprise.client.api.agent import WeChatAgent # NOQA
from wechatpy.enterprise.client.api.batch import WeChatBatch # NOQA
from wechatpy.enterprise.client.api.jsapi import WeChatJSAPI # NOQA
from wechatpy.enterprise.client.api.material import WeChatMaterial # NOQA
from wechatpy.enterprise.client.api.oauth import WeChatOAuth # NOQA
from wechatpy.enterprise.client.api.shakearound import WeChatShakeAround # NOQA
from wechatpy.enterprise.client.api.service import WeChatService # NOQA
from wechatpy.enterprise.client.api.chat import WeChatChat # NOQA

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatAgent(BaseWeChatAPI):
def get(self, agent_id):
"""
获取企业号应用
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=获取企业号应用
:param agent_id: 授权方应用 id
:return: 返回的 JSON 数据包
"""
return self._get(
'agent/get',
params={
'agentid': agent_id
}
)
def set(self,
agent_id,
name=None,
description=None,
redirect_domain=None,
logo_media_id=None,
report_location_flag=0,
is_report_user=True,
is_report_enter=True):
"""
设置企业号应用
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=设置企业号应用
:param agent_id: 企业应用的 id
:param name: 企业应用名称
:param description: 企业应用详情
:param redirect_domain: 企业应用可信域名
:param logo_media_id: 企业应用头像的mediaid通过多媒体接口上传图片获得mediaid
:param report_location_flag: 企业应用是否打开地理位置上报 0不上报1进入会话上报2持续上报
:param is_report_user: 是否接收用户变更通知
:param is_report_enter: 是否上报用户进入应用事件
:return: 返回的 JSON 数据包
"""
agent_data = optionaldict()
agent_data['agentid'] = agent_id
agent_data['name'] = name
agent_data['description'] = description
agent_data['redirect_domain'] = redirect_domain
agent_data['logo_mediaid'] = logo_media_id
agent_data['report_location_flag'] = report_location_flag
agent_data['isreportuser'] = 1 if is_report_user else 0
agent_data['isreportenter'] = 1 if is_report_enter else 0
return self._post(
'agent/set',
data=agent_data
)
def list(self):
"""
获取应用概况列表
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=获取应用概况列表
:return: 应用概况列表
"""
res = self._get('agent/list')
return res['agentlist']

View File

@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
from wechatpy.utils import to_text
class WeChatBatch(BaseWeChatAPI):
def invite_user(self, url, token, encoding_aes_key, user_ids=None,
party_ids=None, tag_ids=None, invite_tips=None):
"""
邀请成员关注
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
:param url: 企业应用接收企业号推送请求的访问协议和地址支持http或https协议
:param token: 用于生成签名
:param encoding_aes_key: 用于消息体的加密是AES密钥的Base64编码
:param user_ids: 可选成员ID列表多个接收者用|分隔最多支持1000个。
:param party_ids: 可选部门ID列表多个接收者用|分隔最多支持100个。
:param tag_ids: 可选标签ID列表多个接收者用|’分隔。
:param invite_tips: 可选,推送到微信上的提示语
:return: 返回的 JSON 数据包
"""
data = optionaldict()
data['callback'] = {
'url': url,
'token': token,
'encodingaeskey': encoding_aes_key
}
if isinstance(user_ids, (tuple, list)):
user_ids = '|'.join(map(to_text, user_ids))
if isinstance(party_ids, (tuple, list)):
party_ids = '|'.join(map(to_text, party_ids))
if isinstance(tag_ids, (tuple, list)):
tag_ids = '|'.join(map(to_text, tag_ids))
data['touser'] = user_ids
data['toparty'] = party_ids
data['totag'] = tag_ids
data['invite_tips'] = invite_tips
return self._post(
'batch/inviteuser',
data=data
)
def sync_user(self, url, token, encoding_aes_key, media_id):
"""
增量更新成员
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
:param url: 企业应用接收企业号推送请求的访问协议和地址支持http或https协议
:param token: 用于生成签名
:param encoding_aes_key: 用于消息体的加密是AES密钥的Base64编码
:param media_id: 上传的csv文件的media_id
:return: 返回的 JSON 数据包
"""
return self._post(
'batch/syncuser',
data={
'media_id': media_id,
'callback': {
'url': url,
'token': token,
'encodingaeskey': encoding_aes_key
}
}
)
def replace_user(self, url, token, encoding_aes_key, media_id):
"""
全量覆盖成员
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
:param url: 企业应用接收企业号推送请求的访问协议和地址支持http或https协议
:param token: 用于生成签名
:param encoding_aes_key: 用于消息体的加密是AES密钥的Base64编码
:param media_id: 上传的csv文件的media_id
:return: 返回的 JSON 数据包
"""
return self._post(
'batch/replaceuser',
data={
'media_id': media_id,
'callback': {
'url': url,
'token': token,
'encodingaeskey': encoding_aes_key
}
}
)
def replace_party(self, url, token, encoding_aes_key, media_id):
"""
全量覆盖部门
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
:param url: 企业应用接收企业号推送请求的访问协议和地址支持http或https协议
:param token: 用于生成签名
:param encoding_aes_key: 用于消息体的加密是AES密钥的Base64编码
:param media_id: 上传的csv文件的media_id
:return: 返回的 JSON 数据包
"""
return self._post(
'batch/replaceparty',
data={
'media_id': media_id,
'callback': {
'url': url,
'token': token,
'encodingaeskey': encoding_aes_key
}
}
)
def get_result(self, job_id):
"""
获取异步任务结果
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
:param job_id: 异步任务id最大长度为64字符
:return: 返回的 JSON 数据包
"""
return self._get(
'batch/getresult',
params={
'jobid': job_id
}
)

View File

@@ -0,0 +1,273 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatChat(BaseWeChatAPI):
def create(self, chat_id, name, owner, user_list):
"""
创建会话
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param chat_id: 会话id。字符串类型最长32个字符。只允许字符0-9及字母a-zA-Z,
如果值内容为64bit无符号整型要求值范围在[1, 2^63)之间,
[2^63, 2^64)为系统分配会话id区间
:param name: 会话标题
:param owner: 管理员userid必须是该会话userlist的成员之一
:param user_list: 会话成员列表成员用userid来标识。会话成员必须在3人或以上1000人以下
:return: 返回的 JSON 数据包
"""
return self._post(
'chat/create',
data={
'chatid': chat_id,
'name': name,
'owner': owner,
'userlist': user_list,
}
)
def get(self, chat_id):
"""
获取会话
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param chat_id: 会话 ID
:return: 会话信息
"""
res = self._get('chat/get', params={'chatid': chat_id})
return res['chat_info']
def update(self, chat_id, op_user, name=None, owner=None,
add_user_list=None, del_user_list=None):
"""
修改会话
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param chat_id: 会话 ID
:param op_user: 操作人 userid
:param name: 会话标题
:param owner: 管理员userid必须是该会话userlist的成员之一
:param add_user_list: 会话新增成员列表成员用userid来标识
:param del_user_list: 会话退出成员列表成员用userid来标识
:return: 返回的 JSON 数据包
"""
data = optionaldict(
chatid=chat_id,
op_user=op_user,
name=name,
owner=owner,
add_user_list=add_user_list,
del_user_list=del_user_list,
)
return self._post('chat/update', data=data)
def quit(self, chat_id, op_user):
"""
退出会话
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param chat_id: 会话 ID
:param op_user: 操作人 userid
:return: 返回的 JSON 数据包
"""
return self._post(
'chat/quit',
data={
'chatid': chat_id,
'op_user': op_user,
}
)
def clear_notify(self, op_user, type, id):
"""
清除会话未读状态
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param op_user: 会话所有者的userid
:param type: 会话类型single|group分别表示单聊|群聊
:param id: 会话值为userid|chatid分别表示成员id|会话id
:return: 返回的 JSON 数据包
"""
return self._post(
'chat/clearnotify',
data={
'op_user': op_user,
'chat': {
'type': type,
'id': id,
}
}
)
def set_mute(self, user_mute_list):
"""
设置成员新消息免打扰
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param user_mute_list: 成员新消息免打扰参数数组最大支持10000个成员
:return: 返回的 JSON 数据包
"""
return self._post(
'chat/setmute',
data={'user_mute_list': user_mute_list}
)
def send_text(self, sender, receiver_type, receiver_id, content):
"""
发送文本消息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param sender: 发送人
:param receiver_type: 接收人类型single|group分别表示单聊|群聊
:param receiver_id: 接收人的值为userid|chatid分别表示成员id|会话id
:param content: 消息内容
:return: 返回的 JSON 数据包
"""
data = {
'receiver': {
'type': receiver_type,
'id': receiver_id,
},
'sender': sender,
'msgtype': 'text',
'text': {
'content': content,
}
}
return self._post('chat/send', data=data)
def send_single_text(self, sender, receiver, content):
"""
发送单聊文本消息
:param sender: 发送人
:param receiver: 接收人成员 ID
:param content: 消息内容
:return: 返回的 JSON 数据包
"""
return self.send_text(sender, 'single', receiver, content)
def send_group_text(self, sender, receiver, content):
"""
发送群聊文本消息
:param sender: 发送人
:param receiver: 会话 ID
:param content: 消息内容
:return: 返回的 JSON 数据包
"""
return self.send_text(sender, 'group', receiver, content)
def send_image(self, sender, receiver_type, receiver_id, media_id):
"""
发送图片消息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param sender: 发送人
:param receiver_type: 接收人类型single|group分别表示单聊|群聊
:param receiver_id: 接收人的值为userid|chatid分别表示成员id|会话id
:param media_id: 图片媒体文件id可以调用上传素材文件接口获取
:return: 返回的 JSON 数据包
"""
data = {
'receiver': {
'type': receiver_type,
'id': receiver_id,
},
'sender': sender,
'msgtype': 'image',
'image': {
'media_id': media_id,
}
}
return self._post('chat/send', data=data)
def send_single_image(self, sender, receiver, media_id):
"""
发送单聊图片消息
:param sender: 发送人
:param receiver: 接收人成员 ID
:param media_id: 图片媒体文件id可以调用上传素材文件接口获取
:return: 返回的 JSON 数据包
"""
return self.send_image(sender, 'single', receiver, media_id)
def send_group_image(self, sender, receiver, media_id):
"""
发送群聊图片消息
:param sender: 发送人
:param receiver: 会话 ID
:param media_id: 图片媒体文件id可以调用上传素材文件接口获取
:return: 返回的 JSON 数据包
"""
return self.send_image(sender, 'group', receiver, media_id)
def send_file(self, sender, receiver_type, receiver_id, media_id):
"""
发送文件消息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
:param sender: 发送人
:param receiver_type: 接收人类型single|group分别表示单聊|群聊
:param receiver_id: 接收人的值为userid|chatid分别表示成员id|会话id
:param media_id: 文件id可以调用上传素材文件接口获取, 文件须大于4字节
:return: 返回的 JSON 数据包
"""
data = {
'receiver': {
'type': receiver_type,
'id': receiver_id,
},
'sender': sender,
'msgtype': 'file',
'file': {
'media_id': media_id,
}
}
return self._post('chat/send', data=data)
def send_single_file(self, sender, receiver, media_id):
"""
发送单聊文件消息
:param sender: 发送人
:param receiver: 接收人成员 ID
:param media_id: 文件id可以调用上传素材文件接口获取, 文件须大于4字节
:return: 返回的 JSON 数据包
"""
return self.send_file(sender, 'single', receiver, media_id)
def send_group_file(self, sender, receiver, media_id):
"""
发送群聊文件消息
:param sender: 发送人
:param receiver: 会话 ID
:param media_id: 文件id可以调用上传素材文件接口获取, 文件须大于4字节
:return: 返回的 JSON 数据包
"""
return self.send_file(sender, 'group', receiver, media_id)

View File

@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatDepartment(BaseWeChatAPI):
def create(self, name, parent_id=1, order=None, id=None):
"""
创建部门
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
:param name: 部门名称,长度限制为 1~64 个字符
:param parent_id: 父亲部门 id ,根部门 id 为 1
:return: 返回的 JSON 数据包
"""
department_data = optionaldict()
department_data['name'] = name
department_data['parentid'] = parent_id
department_data['order'] = order
department_data['id'] = id
return self._post(
'department/create',
data=dict(department_data)
)
def update(self, id, name=None, parent_id=None, order=None):
"""
更新部门
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
:param id: 部门 id
:param name: 部门名称
:param parent_id: 父亲部门 id
:param order: 在父部门中的次序,从 1 开始,数字越大排序越靠后
:return: 返回的 JSON 数据包
"""
department_data = optionaldict()
department_data['id'] = id
department_data['name'] = name
department_data['parentid'] = parent_id
department_data['order'] = order
return self._post(
'department/update',
data=dict(department_data)
)
def delete(self, id):
"""
删除部门
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
:param id: 部门 id
:return: 返回的 JSON 数据包
"""
return self._get(
'department/delete',
params={
'id': id
}
)
def get(self):
"""
获取部门列表
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
:return: 部门列表
"""
res = self._get('department/list')
return res['department']
def get_users(self, id, status=0, fetch_child=0):
"""
获取部门成员列表
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
:param id: 部门 id
:param status: 0 获取全部员工1 获取已关注成员列表,
2 获取禁用成员列表4 获取未关注成员列表。可叠加
:param fetch_child: 1/0是否递归获取子部门下面的成员
:return: 部门成员列表
"""
fetch_child = 1 if fetch_child else 0
res = self._get(
'user/simplelist',
params={
'department_id': id,
'status': status,
'fetch_child': fetch_child
}
)
return res['userlist']
def get_users_list(self, id, status=0, fetch_child=0):
"""
获取部门成员详情列表
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
:param id: 部门 id
:param status: 0 获取全部员工1 获取已关注成员列表,
2 获取禁用成员列表4 获取未关注成员列表。可叠加
:param fetch_child: 1/0是否递归获取子部门下面的成员
:return: 部门成员列表
"""
fetch_child = 1 if fetch_child else 0
res = self._get(
'user/list',
params={
'department_id': id,
'status': status,
'fetch_child': fetch_child
}
)
return res['userlist']

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import time
from wechatpy.client.api.base import BaseWeChatAPI
from wechatpy.utils import WeChatSigner
class WeChatJSAPI(BaseWeChatAPI):
def get_ticket(self):
"""
获取微信 JS-SDK ticket
http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3
:return: 返回的 JSON 数据包
"""
return self._get('get_jsapi_ticket')
def get_jsapi_ticket(self):
"""
获取微信 JS-SDK ticket
该方法会通过 session 对象自动缓存管理 ticket
:return: ticket
"""
ticket = self.session.get('jsapi_ticket')
expires_at = self.session.get('jsapi_ticket_expires_at', 0)
if not ticket or expires_at < int(time.time()):
jsapi_ticket = self.get_ticket()
ticket = jsapi_ticket['ticket']
expires_at = int(time.time()) + int(jsapi_ticket['expires_in'])
self.session.set('jsapi_ticket', ticket)
self.session.set('jsapi_ticket_expires_at', expires_at)
return ticket
def get_jsapi_signature(self, noncestr, ticket, timestamp, url):
data = [
'noncestr={noncestr}'.format(noncestr=noncestr),
'jsapi_ticket={ticket}'.format(ticket=ticket),
'timestamp={timestamp}'.format(timestamp=timestamp),
'url={url}'.format(url=url),
]
signer = WeChatSigner(delimiter=b'&')
signer.add_data(*data)
return signer.signature

View File

@@ -0,0 +1,201 @@
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
import requests
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMaterial(BaseWeChatAPI):
def add_articles(self, articles):
"""
新增永久图文素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param articles: 图文素材数组
:return: 返回的 JSON 数据包
"""
articles_data = []
for article in articles:
articles_data.append({
'thumb_media_id': article['thumb_media_id'],
'title': article['title'],
'content': article['content'],
'author': article.get('author', ''),
'content_source_url': article.get('content_source_url', ''),
'digest': article.get('digest', ''),
'show_cover_pic': article.get('show_cover_pic', '0')
})
return self._post(
'material/add_mpnews',
data={
"mpnews": {
"articles": articles_data
}
}
)
def add(self, agent_id, media_type, media_file):
"""
新增其它类型永久素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param agent_id: 企业应用的id
:param media_type: 媒体文件类型分别有图片image、语音voice、视频video普通文件file
:param media_file: 要上传的文件,一个 File-object
:return: 返回的 JSON 数据包
"""
params = {
'agentid': agent_id,
'type': media_type,
}
return self._post(
url='material/add_material',
params=params,
files={
'media': media_file
}
)
def get_url(self, agent_id, media_id):
"""
获取永久素材下载地址
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param agent_id: 企业应用的id
:param media_id: 媒体文件 ID
:return: 临时素材下载地址
"""
parts = (
'https://qyapi.weixin.qq.com/cgi-bin/material/get',
'?access_token=',
self.access_token,
'&media_id=',
media_id,
'&agentid=',
agent_id,
)
return ''.join(parts)
def get(self, agent_id, media_id):
"""
获取永久素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param agent_id: 企业应用的id
:param media_id: 媒体文件 ID
:return: requests 的 Response 实例
"""
res = requests.get(self.get_url(agent_id, media_id))
return res
def get_articles(self, agent_id, media_id):
"""
获取永久素材:图文消息素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param agent_id: 企业应用的id
:param media_id: 媒体文件 ID
:return: 返回的 JSON 数据包
"""
return self._get(
'material/get',
params={
'agentid': agent_id,
'media_id': media_id,
}
)
def delete(self, agent_id, media_id):
"""
删除永久素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%A0%E9%99%A4%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
:param agent_id: 企业应用的id
:param media_id: 媒体文件 ID
:return: 返回的 JSON 数据包
"""
return self._get(
'material/del',
params={
'agentid': agent_id,
'media_id': media_id,
}
)
def update_articles(self, agent_id, media_id, articles):
"""
修改永久图文素材
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BF%AE%E6%94%B9%E6%B0%B8%E4%B9%85%E5%9B%BE%E6%96%87%E7%B4%A0%E6%9D%90
:param media_id: 要修改的图文消息的 id
:param index: 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义),第一篇为 0
:param articles: 图文素材数组
:return: 返回的 JSON 数据包
"""
articles_data = []
for article in articles:
articles_data.append({
'thumb_media_id': article['thumb_media_id'],
'title': article['title'],
'content': article['content'],
'author': article.get('author', ''),
'content_source_url': article.get('content_source_url', ''),
'digest': article.get('digest', ''),
'show_cover_pic': article.get('show_cover_pic', '0')
})
return self._post(
'material/update_news',
data={
'agentid': agent_id,
'media_id': media_id,
'articles': articles_data
}
)
def get_count(self, agent_id):
"""
获取素材总数
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%B4%A0%E6%9D%90%E6%80%BB%E6%95%B0
:param agent_id: 企业应用的id
:return: 返回的 JSON 数据包
"""
return self._get(
'material/get_count',
params={
'agent_id': agent_id,
}
)
def batchget(self, agent_id, media_type, offset=0, count=20):
"""
批量获取永久素材列表
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%B4%A0%E6%9D%90%E5%88%97%E8%A1%A8
:param agent_id: 企业应用的id
:param media_type: 媒体文件类型分别有图文mpnews、图片image
语音voice、视频video和文件file
:param offset: 从全部素材的该偏移位置开始返回0 表示从第一个素材返回
:param count: 返回素材的数量取值在1到20之间
:return: 返回的 JSON 数据包
"""
return self._post(
'material/batchget',
data={
'agent_id': agent_id,
'type': media_type,
'offset': offset,
'count': count
}
)

View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import requests
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMedia(BaseWeChatAPI):
def upload(self, media_type, media_file):
"""
上传临时素材文件
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
:param media_type: 媒体文件类型分别有图片image、语音voice、视频video和普通文件file
:param media_file: 要上传的文件,一个 File-object
:return: 返回的 JSON 数据包
"""
return self._post(
'media/upload',
params={
'type': media_type
},
files={
'media': media_file
}
)
def upload_image(self, media_file):
"""
上传卡券logo
:param media_file: 要上传的文件
:return:
"""
return self._post(
'media/uploadimg',
params={
'type': 'card_logo'
},
files={
'media': media_file
}
)
def download(self, media_id):
"""
获取临时素材文件
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
:param media_id: 媒体文件 ID
:return: requests 的 Response 实例
"""
return requests.get(self.get_url(media_id))
def get_url(self, media_id):
"""
获取临时素材下载地址
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
:param media_id: 媒体文件 ID
:return: 临时素材下载地址
"""
parts = (
'https://qyapi.weixin.qq.com/cgi-bin/media/get',
'?access_token=',
self.access_token,
'&media_id=',
media_id
)
return ''.join(parts)

View File

@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
from wechatpy.exceptions import WeChatClientException
class WeChatMenu(BaseWeChatAPI):
def create(self, agent_id, menu_data):
return self._post(
'menu/create',
params={
'agentid': agent_id
},
data=menu_data
)
def get(self, agent_id):
try:
return self._get(
'menu/get',
params={
'agentid': agent_id
}
)
except WeChatClientException as e:
if e.errcode == 46003:
# menu not exist
return None
else:
raise e
def delete(self, agent_id):
return self._get(
'menu/delete',
params={
'agentid': agent_id
}
)
def update(self, agent_id, menu_data):
self.delete(agent_id)
return self.create(agent_id, menu_data)

View File

@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMessage(BaseWeChatAPI):
def _send_message(self, agent_id, user_ids, party_ids='',
tag_ids='', msg=None):
msg = msg or {}
if isinstance(user_ids, (tuple, list)):
user_ids = '|'.join(user_ids)
if isinstance(party_ids, (tuple, list)):
party_ids = '|'.join(party_ids)
if isinstance(tag_ids, (tuple, list)):
tag_ids = '|'.join(tag_ids)
data = {
'touser': user_ids,
'toparty': party_ids,
'totag': tag_ids,
'agentid': agent_id
}
data.update(msg)
return self._post(
'message/send',
data=data
)
def send_text(self, agent_id, user_ids, content,
party_ids='', tag_ids='', safe=0):
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'text',
'text': {'content': content},
'safe': safe
}
)
def send_image(self, agent_id, user_ids, media_id,
party_ids='', tag_ids='', safe=0):
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'image',
'image': {
'media_id': media_id
},
'safe': safe
}
)
def send_voice(self, agent_id, user_ids, media_id,
party_ids='', tag_ids='', safe=0):
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'voice',
'voice': {
'media_id': media_id
},
'safe': safe
}
)
def send_video(self, agent_id, user_ids, media_id, title=None,
description=None, party_ids='', tag_ids='', safe=0):
video_data = optionaldict()
video_data['media_id'] = media_id
video_data['title'] = title
video_data['description'] = description
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'video',
'video': dict(video_data),
'safe': safe
}
)
def send_file(self, agent_id, user_ids, media_id,
party_ids='', tag_ids='', safe=0):
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'file',
'file': {
'media_id': media_id
},
'safe': safe
}
)
def send_articles(self, agent_id, user_ids, articles,
party_ids='', tag_ids=''):
articles_data = []
for article in articles:
articles_data.append({
'title': article['title'],
'description': article['description'],
'url': article['url'],
'picurl': article['image']
})
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'news',
'news': {
'articles': articles_data
}
}
)
def send_mp_articles(self, agent_id, user_ids, articles,
party_ids='', tag_ids='', safe=0):
articles_data = []
for article in articles:
articles_data.append({
'thumb_media_id': article['thumb_media_id'],
'author': article['author'],
'title': article['title'],
'content': article['content'],
'content_source_url': article['content_source_url'],
'digest': article['digest'],
'show_cover_pic': article['show_cover_pic']
})
return self._send_message(
agent_id,
user_ids,
party_ids,
tag_ids,
msg={
'msgtype': 'mpnews',
'mpnews': {
'articles': articles_data
},
'safe': safe
}
)

View File

@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMisc(BaseWeChatAPI):
def get_wechat_ips(self):
"""
获取微信服务器 IP 列表
:return: IP 地址列表
"""
res = self._get('getcallbackip')
return res['ip_list']

View File

@@ -0,0 +1,51 @@
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
import six
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatOAuth(BaseWeChatAPI):
OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize'
def authorize_url(self, redirect_uri, state=None):
"""
获取授权地址
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=OAuth%E9%AA%8C%E8%AF%81%E6%8E%A5%E5%8F%A3
:param redirect_url: 授权后重定向的回调链接地址
:param state: 重定向后会带上 state 参数
:return: 返回的 JSON 数据包
"""
redirect_uri = six.moves.urllib.parse.quote(redirect_uri)
url_list = [
self.OAUTH_BASE_URL,
'?appid=',
self._client.corp_id,
'&redirect_uri=',
redirect_uri,
'&response_type=code&scope=snsapi_base',
]
if state:
url_list.extend(['&state=', state])
url_list.append('#wechat_redirect')
return ''.join(url_list)
def get_user_info(self, code):
"""
根据 code 获取用户信息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=OAuth%E9%AA%8C%E8%AF%81%E6%8E%A5%E5%8F%A3
:param code: 通过成员授权获取到的code
:return: 返回的 JSON 数据包
"""
return self._get(
'user/getuserinfo',
params={
'code': code,
}
)

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatService(BaseWeChatAPI):
def get_provider_token(self, provider_secret):
"""
获取应用提供商凭证
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=获取应用提供商凭证
:param provider_secret: 提供商的secret在提供商管理页面可见
:return: 返回的 JSON 数据包
"""
return self._post(
'service/get_provider_token',
data={
'corpid': self._client.corp_id,
'provider_secret': provider_secret,
}
)
def get_login_info(self, provider_access_token, auth_code):
"""
获取企业号管理员登录信息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=获取企业号管理员登录信息
:param provider_access_token: 服务提供商的 accesstoken
:param auth_code: OAuth 2.0 授权企业号管理员登录产生的 code
:return: 返回的 JSON 数据包
"""
return self._post(
'service/get_login_info',
params={
'provider_access_token': provider_access_token,
},
data={
'auth_code': auth_code,
}
)

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatShakeAround(BaseWeChatAPI):
def get_shake_info(self, ticket):
"""
获取摇周边的设备及用户信息
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=获取设备及用户信息
:param ticket: 摇周边业务的ticket可在摇到的 URL 中得到ticket 生效时间为30分钟
:return: 设备及用户信息
"""
res = self._post(
'shakearound/getshakeinfo',
data={
'ticket': ticket
}
)
return res['data']

View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatTag(BaseWeChatAPI):
def create(self, name):
return self._post(
'tag/create',
data={
'tagname': name
}
)
def update(self, tag_id, name):
return self._post(
'tag/update',
data={
'tagid': tag_id,
'tagname': name
}
)
def delete(self, tag_id):
return self._get(
'tag/delete',
params={
'tagid': tag_id
}
)
def get_users(self, tag_id):
return self._get(
'tag/get',
params={
'tagid': tag_id
}
)
def add_users(self, tag_id, user_ids):
return self._post(
'tag/addtagusers',
data={
'tagid': tag_id,
'userlist': user_ids
}
)
def delete_users(self, tag_id, user_ids):
return self._post(
'tag/deltagusers',
data={
'tagid': tag_id,
'userlist': user_ids
}
)
def list(self):
res = self._get('tag/list')
return res['taglist']

View File

@@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatUser(BaseWeChatAPI):
def create(self, user_id, name, department=None, position=None,
mobile=None, gender=0, tel=None, email=None,
weixin_id=None, extattr=None, avatar_mediaid=None, to_invite=True):
"""
创建成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
user_data = optionaldict()
user_data['userid'] = user_id
user_data['name'] = name
user_data['gender'] = gender
user_data['department'] = department
user_data['position'] = position
user_data['mobile'] = mobile
user_data['tel'] = tel
user_data['email'] = email
user_data['weixinid'] = weixin_id
user_data['extattr'] = extattr
user_data['avatar_mediaid'] = avatar_mediaid
user_data['to_invite'] = to_invite
return self._post(
'user/create',
data=user_data
)
def update(self, user_id, name=None, department=None, position=None,
mobile=None, gender=None, tel=None, email=None,
weixin_id=None, enable=None, extattr=None, avatar_mediaid=None):
"""
更新成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
user_data = optionaldict()
user_data['userid'] = user_id
user_data['name'] = name
user_data['gender'] = gender
user_data['department'] = department
user_data['position'] = position
user_data['mobile'] = mobile
user_data['tel'] = tel
user_data['email'] = email
user_data['weixinid'] = weixin_id
user_data['extattr'] = extattr
user_data['enable'] = enable
user_data['avatar_mediaid'] = avatar_mediaid
return self._post(
'user/update',
data=user_data
)
def delete(self, user_id):
"""
删除成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
return self._get(
'user/delete',
params={
'userid': user_id
}
)
def get(self, user_id):
"""
获取成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
return self._get(
'user/get',
params={
'userid': user_id
}
)
def verify(self, user_id):
return self._get(
'user/authsucc',
params={
'userid': user_id
}
)
def get_info(self, agent_id, code):
return self._get(
'user/getuserinfo',
params={
'agentid': agent_id,
'code': code
}
)
def batch_delete(self, user_ids):
"""
批量删除成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
return self._post(
'user/batchdelete',
data={
'useridlist': user_ids
}
)
def list(self, department_id, fetch_child=False, status=0):
"""
批量获取部门成员
详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
"""
res = self._get(
'user/list',
params={
'department_id': department_id,
'fetch_child': 1 if fetch_child else 0,
'status': status
}
)
return res['userlist']
def convert_to_openid(self, user_id, agent_id=None):
"""
user_id 转成 openid
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=Userid%E4%B8%8Eopenid%E4%BA%92%E6%8D%A2%E6%8E%A5%E5%8F%A3
:param user_id: 企业号内的成员 ID
:param agent_id: 可选需要发送红包的应用ID若只是使用微信支付和企业转账则无需该参数
:return: 返回的 JSON 数据包
"""
data = optionaldict()
data['userid'] = user_id
data['agentid'] = agent_id
return self._post('user/convert_to_openid', data=data)
def convert_to_user_id(self, openid):
"""
openid 转成 user_id
详情请参考
http://qydev.weixin.qq.com/wiki/index.php?title=Userid%E4%B8%8Eopenid%E4%BA%92%E6%8D%A2%E6%8E%A5%E5%8F%A3
:param openid: 在使用微信支付、微信红包和企业转账之后返回结果的openid
:return: 该 openid 在企业号中对应的成员 user_id
"""
res = self._post(
'user/convert_to_userid',
data={'openid': openid}
)
return res['userid']

View File

@@ -0,0 +1,46 @@
from __future__ import absolute_import, unicode_literals
from wechatpy.crypto import BasePrpCrypto, BaseWeChatCrypto
from wechatpy.enterprise.exceptions import InvalidCorpIdException
class PrpCrypto(BasePrpCrypto):
def encrypt(self, text, corp_id):
return self._encrypt(text, corp_id)
def decrypt(self, text, corp_id):
return self._decrypt(text, corp_id, InvalidCorpIdException)
class WeChatCrypto(BaseWeChatCrypto):
def __init__(self, token, encoding_aes_key, corp_id):
super(WeChatCrypto, self).__init__(token, encoding_aes_key, corp_id)
self.corp_id = corp_id
def check_signature(self, signature, timestamp, nonce, echo_str):
return self._check_signature(
signature,
timestamp,
nonce,
echo_str,
PrpCrypto
)
def encrypt_message(self, msg, nonce, timestamp=None):
return self._encrypt_message(
msg,
nonce,
timestamp,
PrpCrypto
)
def decrypt_message(self, msg, signature, timestamp, nonce):
return self._decrypt_message(
msg,
signature,
timestamp,
nonce,
PrpCrypto
)

View File

@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.fields import IntegerField, BaseField
from wechatpy import events
EVENT_TYPES = {}
def register_event(event_type):
def register(cls):
EVENT_TYPES[event_type] = cls
return cls
return register
@register_event('subscribe')
class SubscribeEvent(events.SubscribeEvent):
"""
成员关注事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('unsubscribe')
class UnsubscribeEvent(events.UnsubscribeEvent):
"""
成员取消关注事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('click')
class ClickEvent(events.ClickEvent):
"""
点击菜单拉取消息事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('xml')
class ViewEvent(events.ViewEvent):
"""
点击菜单跳转链接事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('location')
class LocationEvent(events.LocationEvent):
"""
上报地理位置事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('scancode_push')
class ScanCodePushEvent(events.ScanCodePushEvent):
"""
扫码推事件的事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('scancode_waitmsg')
class ScanCodeWaitMsgEvent(events.ScanCodeWaitMsgEvent):
"""
扫码推事件且弹出“消息接收中”提示框的事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('pic_sysphoto')
class PicSysPhotoEvent(events.PicSysPhotoEvent):
"""
弹出系统拍照发图事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('pic_photo_or_album')
class PicPhotoOrAlbumEvent(events.PicPhotoOrAlbumEvent):
"""
弹出拍照或相册发图事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('pic_weixin')
class PicWeChatEvent(events.PicWeChatEvent):
"""
弹出微信相册发图器事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('location_select')
class LocationSelectEvent(events.LocationSelectEvent):
"""
弹出地理位置选择器事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
@register_event('enter_agent')
class EnterAgentEvent(events.BaseEvent):
"""
用户进入应用的事件推送
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
agent = IntegerField('AgentID', 0)
event = 'enter_agent'
@register_event('batch_job_result')
class BatchJobResultEvent(events.BaseEvent):
"""
异步任务完成事件
详情请参阅
http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
"""
event = 'batch_job_result'
batch_job = BaseField('BatchJob')
@property
def job_id(self):
return self.batch_job['JobId']
@property
def job_type(self):
return self.batch_job['JobType']
@property
def err_code(self):
return self.batch_job['ErrCode']
@property
def err_msg(self):
return self.batch_job['ErrMsg']

View File

@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.exceptions import WeChatException
class InvalidCorpIdException(WeChatException):
def __init__(self, errcode=-40005, errmsg='Invalid corp_id'):
super(InvalidCorpIdException, self).__init__(errcode, errmsg)

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.fields import IntegerField, StringField
from wechatpy import messages
MESSAGE_TYPES = {}
def register_message(msg_type):
def register(cls):
MESSAGE_TYPES[msg_type] = cls
return cls
return register
@register_message('text')
class TextMessage(messages.TextMessage):
agent = IntegerField('AgentID', 0)
@register_message('image')
class ImageMessage(messages.ImageMessage):
agent = IntegerField('AgentID', 0)
@register_message('voice')
class VoiceMessage(messages.VoiceMessage):
agent = IntegerField('AgentID', 0)
@register_message('shortvideo')
class ShortVideoMessage(messages.ShortVideoMessage):
agent = IntegerField('AgentID', 0)
@register_message('video')
class VideoMessage(messages.VideoMessage):
agent = IntegerField('AgentID', 0)
@register_message('location')
class LocationMessage(messages.LocationMessage):
agent = IntegerField('AgentID', 0)
@register_message('link')
class LinkMessage(messages.LinkMessage):
agent = IntegerField('AgentID', 0)
pic_url = StringField('PicUrl')

View File

@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import xmltodict
from wechatpy.messages import UnknownMessage
from wechatpy.utils import to_text
from wechatpy.enterprise.messages import MESSAGE_TYPES
from wechatpy.enterprise.events import EVENT_TYPES
def parse_message(xml):
if not xml:
return
message = xmltodict.parse(to_text(xml))['xml']
message_type = message['MsgType'].lower()
if message_type == 'event':
event_type = message['Event'].lower()
message_class = EVENT_TYPES.get(event_type, UnknownMessage)
else:
message_class = MESSAGE_TYPES.get(message_type, UnknownMessage)
return message_class(message)

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import six
from wechatpy import replies
from wechatpy.fields import IntegerField
REPLY_TYPES = {}
def register_reply(reply_type):
def register(cls):
REPLY_TYPES[reply_type] = cls
return cls
return register
@register_reply('text')
class TextReply(replies.TextReply):
agent = IntegerField('AgentID', 0)
@register_reply('image')
class ImageReply(replies.ImageReply):
agent = IntegerField('AgentID', 0)
@register_reply('voice')
class VoiceReply(replies.VoiceReply):
agent = IntegerField('AgentID', 0)
@register_reply('video')
class VideoReply(replies.VideoReply):
agent = IntegerField('AgentID', 0)
@register_reply('news')
class ArticlesReply(replies.ArticlesReply):
agent = IntegerField('AgentID', 0)
def create_reply(reply, message=None, render=False):
r = None
if isinstance(reply, replies.BaseReply):
r = reply
if message:
r.source = message.target
r.target = message.source
r.agent = message.agent
elif isinstance(reply, six.string_types):
r = TextReply(
message=message,
content=reply
)
elif isinstance(reply, (tuple, list)):
if len(reply) > 10:
raise AttributeError("Can't add more than 10 articles"
" in an ArticlesReply")
r = ArticlesReply(
message=message,
articles=reply
)
if r and render:
return r.render()
return r