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

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,23 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.menu import WeChatMenu # NOQA
from wechatpy.client.api.user import WeChatUser # NOQA
from wechatpy.client.api.card import WeChatCard # NOQA
from wechatpy.client.api.group import WeChatGroup # NOQA
from wechatpy.client.api.media import WeChatMedia # NOQA
from wechatpy.client.api.message import WeChatMessage # NOQA
from wechatpy.client.api.qrcode import WeChatQRCode # NOQA
from wechatpy.client.api.misc import WeChatMisc # NOQA
from wechatpy.client.api.merchant import WeChatMerchant # NOQA
from wechatpy.client.api.customservice import WeChatCustomService # NOQA
from wechatpy.client.api.datacube import WeChatDataCube # NOQA
from wechatpy.client.api.jsapi import WeChatJSAPI # NOQA
from wechatpy.client.api.material import WeChatMaterial # NOQA
from wechatpy.client.api.semantic import WeChatSemantic # NOQA
from wechatpy.client.api.shakearound import WeChatShakeAround # NOQA
from wechatpy.client.api.device import WeChatDevice # NOQA
from wechatpy.client.api.template import WeChatTemplate # NOQA
from wechatpy.client.api.poi import WeChatPoi # NOQA
from wechatpy.client.api.wifi import WeChatWiFi # NOQA
from wechatpy.client.api.scan import WeChatScan # NOQA

View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
class BaseWeChatAPI(object):
API_BASE_URL = ''
""" WeChat API base class """
def __init__(self, client=None):
self._client = client
def _get(self, url, **kwargs):
if getattr(self, 'API_BASE_URL', None):
kwargs['api_base_url'] = self.API_BASE_URL
return self._client.get(url, **kwargs)
def _post(self, url, **kwargs):
if getattr(self, 'API_BASE_URL', None):
kwargs['api_base_url'] = self.API_BASE_URL
return self._client.post(url, **kwargs)
@property
def access_token(self):
return self._client.access_token
@property
def session(self):
return self._client.session

View File

@@ -0,0 +1,431 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatCard(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/'
def create(self, card_data):
"""
创建卡券
:param card_data: 卡券信息
:return: 创建的卡券 ID
"""
result = self._post(
'card/create',
data=card_data,
result_processor=lambda x: x['card_id']
)
return result
def batch_add_locations(self, location_data):
"""
批量导入门店信息
:param location_data: 门店信息
:return: 门店 ID 列表,插入失败的门店元素值为 -1
"""
result = self._post(
'card/location/batchadd',
data=location_data,
result_processor=lambda x: x['location_id_list']
)
return result
def batch_get_locations(self, offset=0, count=0):
"""
批量获取门店信息
"""
return self._post(
'card/location/batchget',
data={
'offset': offset,
'count': count
}
)
def get_colors(self):
"""
获得卡券的最新颜色列表,用于创建卡券
:return: 颜色列表
"""
result = self._get(
'card/getcolors',
result_processor=lambda x: x['colors']
)
return result
def create_qrcode(self, qrcode_data):
"""
创建卡券二维码
:param qrcode_data: 二维码信息
:return: 二维码 ticket可使用 :func:show_qrcode 换取二维码文件
"""
result = self._post(
'card/qrcode/create',
data=qrcode_data,
result_processor=lambda x: x['ticket']
)
return result
def create_landingpage(self, buffer_data):
"""
创建货架
"""
result = self._post(
'card/landingpage/create',
data=buffer_data
)
return result
def get_html(self, card_id):
"""
图文消息群发卡券
"""
result = self._post(
'card/mpnews/gethtml',
data={
'card_id': card_id
},
result_processor=lambda x: x['content']
)
return result
def consume_code(self, code, card_id=None):
"""
消耗 code
"""
card_data = {
'code': code
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/code/consume',
data=card_data
)
def decrypt_code(self, encrypt_code):
"""
解码加密的 code
"""
result = self._post(
'card/code/decrypt',
data={
'encrypt_code': encrypt_code
},
result_processor=lambda x: x['code']
)
return result
def delete(self, card_id):
"""
删除卡券
"""
return self._post(
'card/delete',
data={
'card_id': card_id
}
)
def get_code(self, code, card_id=None, check_consume=True):
"""
查询 code 信息
"""
card_data = {
'code': code
}
if card_id:
card_data['card_id'] = card_id
if not check_consume:
card_data['check_consume'] = check_consume
return self._post(
'card/code/get',
data=card_data
)
def get_card_list(self, openid, card_id=None):
"""
用于获取用户卡包里的属于该appid下的卡券。
"""
card_data = {
'openid': openid
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/user/getcardlist',
data=card_data
)
def batch_get(self, offset=0, count=50, status_list=None):
"""
批量查询卡券信息
"""
card_data = {
'offset': offset,
'count': count
}
if status_list:
card_data['status_list'] = status_list
return self._post(
'card/batchget',
data=card_data
)
def get(self, card_id):
"""
查询卡券详情
"""
result = self._post(
'card/get',
data={
'card_id': card_id
},
result_processor=lambda x: x['card']
)
return result
def update_code(self, card_id, old_code, new_code):
"""
更新卡券 code
"""
return self._post(
'card/code/update',
data={
'card_id': card_id,
'code': old_code,
'new_code': new_code
}
)
def invalid_code(self, code, card_id=None):
"""
设置卡券失效
"""
card_data = {
'code': code
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/code/unavailable',
data=card_data
)
def update(self, card_data):
"""
更新卡券信息
"""
return self._post(
'card/update',
data=card_data
)
def set_paycell(self, card_id, is_open):
"""
更新卡券信息
"""
return self._post(
'card/paycell/set',
data={
'card_id': card_id,
'is_open': is_open
}
)
def set_test_whitelist(self, openids=None, usernames=None):
"""
设置卡券测试用户白名单
"""
openids = openids or []
usernames = usernames or []
return self._post(
'card/testwhitelist/set',
data={
'openid': openids,
'username': usernames
}
)
def activate_membercard(self, membership_number, code, init_bonus=0,
init_balance=0, card_id=None):
"""
激活/绑定会员卡
"""
card_data = {
'membership_number': membership_number,
'code': code,
'init_bonus': init_bonus,
'init_balance': init_balance
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/membercard/activate',
data=card_data
)
def update_membercard(self, code, add_bonus=0, record_bonus='',
add_balance=0, record_balance='', card_id=None):
"""
会员卡交易更新信息
"""
card_data = {
'code': code,
'add_bonus': add_bonus,
'add_balance': add_balance,
'record_bonus': record_bonus,
'record_balance': record_balance
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/membercard/updateuser',
data=card_data
)
def update_movie_ticket(self, code, ticket_class, show_time, duration,
screening_room, seat_number, card_id=None):
"""
更新电影票
"""
ticket = {
'code': code,
'ticket_class': ticket_class,
'show_time': show_time,
'duration': duration,
'screening_room': screening_room,
'seat_number': seat_number
}
if card_id:
ticket['card_id'] = card_id
return self._post(
'card/movieticket/updateuser',
data=ticket
)
def checkin_boardingpass(self, code, passenger_name, seat_class,
etkt_bnr, seat='', gate='', boarding_time=None,
is_cancel=False, qrcode_data=None, card_id=None):
"""
飞机票接口
"""
data = {
'code': code,
'passenger_name': passenger_name,
'class': seat_class,
'etkt_bnr': etkt_bnr,
'seat': seat,
'gate': gate,
'is_cancel': is_cancel
}
if boarding_time:
data['boarding_time'] = boarding_time
if qrcode_data:
data['qrcode_data'] = qrcode_data
if card_id:
data['card_id'] = card_id
return self._post(
'card/boardingpass/checkin',
data=data
)
def update_luckymoney_balance(self, code, balance, card_id=None):
"""
更新红包余额
"""
card_data = {
'code': code,
'balance': balance
}
if card_id:
card_data['card_id'] = card_id
return self._post(
'card/luckymoney/updateuserbalance',
data=card_data
)
def get_redirect_url(self, url, encrypt_code, card_id):
"""
获取卡券跳转外链
"""
from wechatpy.utils import WeChatSigner
code = self.decrypt_code(encrypt_code)
signer = WeChatSigner()
signer.add_data(self.secret)
signer.add_data(code)
signer.add_data(card_id)
signature = signer.signature
r = '{url}?encrypt_code={code}&card_id={card_id}&signature={signature}'
return r.format(
url=url,
code=encrypt_code,
card_id=card_id,
signature=signature
)
def deposit_code(self, card_id, codes):
"""
导入code
"""
card_data = {
'card_id': card_id,
'code': codes
}
return self._post(
'card/code/deposit',
data=card_data
)
def get_deposit_count(self, card_id):
"""
查询导入code数目
"""
card_data = {
'card_id': card_id,
}
return self._post(
'card/code/getdepositcount',
data=card_data
)
def check_code(self, card_id, codes):
"""
核查code
"""
card_data = {
'card_id': card_id,
'code': codes
}
return self._post(
'card/code/checkcode',
data=card_data
)
def modify_stock(self, card_id, n):
"""
修改库存
"""
if n == 0:
return
card_data = {
'card_id': card_id,
}
if n > 0:
card_data['increase_stock_value'] = n
elif n < 0:
card_data['reduce_stock_value'] = -n
return self._post(
'card/modifystock',
data=card_data
)

View File

@@ -0,0 +1,242 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import hashlib
import time
import datetime
from six.moves.urllib.parse import quote
from optionaldict import optionaldict
from wechatpy.utils import to_binary
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatCustomService(BaseWeChatAPI):
def add_account(self, account, nickname, password):
"""
添加客服账号
详情请参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:param account: 完整客服账号,格式为:账号前缀@公众号微信号
:param nickname: 客服昵称最长6个汉字或12个英文字符
:param password: 客服账号登录密码
:return: 返回的 JSON 数据包
"""
password = to_binary(password)
password = hashlib.md5(password).hexdigest()
return self._post(
'https://api.weixin.qq.com/customservice/kfaccount/add',
data={
'kf_account': account,
'nickname': nickname,
'password': password
}
)
def update_account(self, account, nickname, password):
"""
更新客服账号
详情请参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:param account: 完整客服账号,格式为:账号前缀@公众号微信号
:param nickname: 客服昵称最长6个汉字或12个英文字符
:param password: 客服账号登录密码
:return: 返回的 JSON 数据包
"""
password = to_binary(password)
password = hashlib.md5(password).hexdigest()
return self._post(
'https://api.weixin.qq.com/customservice/kfaccount/update',
data={
'kf_account': account,
'nickname': nickname,
'password': password
}
)
def delete_account(self, account):
"""
删除客服账号
详情请参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:param account: 完整客服账号,格式为:账号前缀@公众号微信号
:return: 返回的 JSON 数据包
"""
params_data = [
'access_token={0}'.format(quote(self.access_token)),
'kf_account={0}'.format(quote(to_binary(account), safe=b'/@')),
]
params = '&'.join(params_data)
return self._get(
'https://api.weixin.qq.com/customservice/kfaccount/del',
params=params
)
def get_accounts(self):
"""
获取客服账号列表
详情请参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:return: 客服账号列表
"""
res = self._get(
'customservice/getkflist',
result_processor=lambda x: x['kf_list']
)
return res
def upload_headimg(self, account, media_file):
"""
上传客服账号头像
详情请参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:param account: 完整客服账号
:param media_file: 要上传的头像文件,一个 File-Object
:return: 返回的 JSON 数据包
"""
return self._post(
'https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg',
params={
'kf_account': account
},
files={
'media': media_file
}
)
def get_online_accounts(self):
"""
获取在线客服接待信息
详情请参考
http://mp.weixin.qq.com/wiki/9/6fff6f191ef92c126b043ada035cc935.html
:return: 客服接待信息列表
"""
res = self._get(
'customservice/getonlinekflist',
result_processor=lambda x: x['kf_online_list']
)
return res
def create_session(self, openid, account, text=None):
"""
多客服创建会话
详情请参考
http://mp.weixin.qq.com/wiki/2/6c20f3e323bdf5986cfcb33cbd3b829a.html
:param openid: 客户 openid
:param account: 完整客服账号
:param text: 附加信息,可选
:return: 返回的 JSON 数据包
"""
data = optionaldict(
openid=openid,
kf_account=account,
text=text
)
return self._post(
'https://api.weixin.qq.com/customservice/kfsession/create',
data=data
)
def close_session(self, openid, account, text=None):
"""
多客服关闭会话
详情请参考
http://mp.weixin.qq.com/wiki/2/6c20f3e323bdf5986cfcb33cbd3b829a.html
:param openid: 客户 openid
:param account: 完整客服账号
:param text: 附加信息,可选
:return: 返回的 JSON 数据包
"""
data = optionaldict(
openid=openid,
kf_account=account,
text=text
)
return self._post(
'https://api.weixin.qq.com/customservice/kfsession/close',
data=data
)
def get_session(self, openid):
"""
获取客户的会话状态
详情请参考
http://mp.weixin.qq.com/wiki/2/6c20f3e323bdf5986cfcb33cbd3b829a.html
:param openid: 客户 openid
:return: 返回的 JSON 数据包
"""
return self._get(
'https://api.weixin.qq.com/customservice/kfsession/getsession',
params={'openid': openid}
)
def get_session_list(self, account):
"""
获取客服的会话列表
详情请参考
http://mp.weixin.qq.com/wiki/2/6c20f3e323bdf5986cfcb33cbd3b829a.html
:param account: 完整客服账号
:return: 客服的会话列表
"""
res = self._get(
'https://api.weixin.qq.com/customservice/kfsession/getsessionlist',
params={'kf_account': account},
result_processor=lambda x: x['sessionlist']
)
return res
def get_wait_case(self):
"""
获取未接入会话列表
详情请参考
http://mp.weixin.qq.com/wiki/2/6c20f3e323bdf5986cfcb33cbd3b829a.html
:return: 返回的 JSON 数据包
"""
return self._get(
'https://api.weixin.qq.com/customservice/kfsession/getwaitcase'
)
def get_records(self, start_time, end_time, page_index,
page_size=10, user_id=None):
"""
获取客服聊天记录
详情请参考
http://mp.weixin.qq.com/wiki/19/7c129ec71ddfa60923ea9334557e8b23.html
:param start_time: 查询开始时间UNIX 时间戳
:param end_time: 查询结束时间UNIX 时间戳,每次查询不能跨日查询
:param page_index: 查询第几页,从 1 开始
:param page_size: 每页大小,每页最多拉取 1000 条
:param user_id: 普通用户的标识,对当前公众号唯一
:return: 返回的 JSON 数据包
"""
if isinstance(start_time, datetime.datetime):
start_time = time.mktime(start_time.timetuple())
if isinstance(end_time, datetime.datetime):
end_time = time.mktime(end_time.timetuple())
record_data = {
'starttime': int(start_time),
'endtime': int(end_time),
'pageindex': page_index,
'pagesize': page_size
}
if user_id:
record_data['openid'] = user_id
res = self._post(
'https://api.weixin.qq.com/customservice/msgrecord/getrecord',
data=record_data,
result_processor=lambda x: x['recordlist']
)
return res

View File

@@ -0,0 +1,360 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import datetime
import six
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatDataCube(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/datacube/'
@classmethod
def _to_date_str(cls, date):
if isinstance(date, (datetime.datetime, datetime.date)):
return date.strftime('%Y-%m-%d')
elif isinstance(date, six.string_types):
return date
else:
raise ValueError('Can not convert %s type to str', type(date))
def get_user_summary(self, begin_date, end_date):
"""
获取用户增减数据
详情请参考
http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getusersummary',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
}
)
return res['list']
def get_user_cumulate(self, begin_date, end_date):
"""
获取累计用户数据
详情请参考
http://mp.weixin.qq.com/wiki/3/ecfed6e1a0a03b5f35e5efac98e864b7.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getusercumulate',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_interface_summary(self, begin_date, end_date):
"""
获取接口分析数据
详情请参考
http://mp.weixin.qq.com/wiki/8/30ed81ae38cf4f977194bf1a5db73668.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getinterfacesummary',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_interface_summary_hour(self, begin_date, end_date):
"""
获取接口分析分时数据
详情请参考
http://mp.weixin.qq.com/wiki/8/30ed81ae38cf4f977194bf1a5db73668.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getinterfacesummaryhour',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_article_summary(self, begin_date, end_date):
"""
获取图文群发每日数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getarticlesummary',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_article_total(self, begin_date, end_date):
"""
获取图文群发总数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getarticletotal',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_user_read(self, begin_date, end_date):
"""
获取图文统计数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getuserread',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_user_read_hour(self, begin_date, end_date):
"""
获取图文分时统计数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getuserreadhour',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_user_share(self, begin_date, end_date):
"""
获取图文分享转发数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getusershare',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_user_share_hour(self, begin_date, end_date):
"""
获取图文分享转发分时数据
详情请参考
http://mp.weixin.qq.com/wiki/8/c0453610fb5131d1fcb17b4e87c82050.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getusersharehour',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg(self, begin_date, end_date):
"""
获取消息发送概况数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsg',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_hour(self, begin_date, end_date):
"""
获取消息发送分时数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsghour',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_week(self, begin_date, end_date):
"""
获取消息发送周数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsgweek',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_month(self, begin_date, end_date):
"""
获取消息发送月数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsgmonth',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_dist(self, begin_date, end_date):
"""
获取消息发送分布数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsgdist',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_dist_week(self, begin_date, end_date):
"""
获取消息发送分布数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsgdistweek',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res
def get_upstream_msg_dist_month(self, begin_date, end_date):
"""
获取消息发送分布数据
详情请参考
http://mp.weixin.qq.com/wiki/12/32d42ad542f2e4fc8a8aa60e1bce9838.html
:param begin_date: 起始日期
:param end_date: 结束日期
:return: 统计数据列表
"""
res = self._post(
'getupstreammsgdistmonth',
data={
'begin_date': self._to_date_str(begin_date),
'end_date': self._to_date_str(end_date)
},
result_processor=lambda x: x['list']
)
return res

View File

@@ -0,0 +1,284 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import base64
import urllib
from wechatpy.utils import to_text, to_binary
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatDevice(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/device/'
def send_message(self, device_type, device_id, user_id, content):
"""
主动发送消息给设备
详情请参考
http://iot.weixin.qq.com/document-2_3.html
:param device_type: 设备类型目前为“公众账号原始ID”
:param device_id: 设备ID
:param user_id: 微信用户账号的openid
:param content: 消息内容BASE64编码
:return: 返回的 JSON 数据包
"""
content = to_text(base64.b64encode(to_binary(content)))
return self._post(
'transmsg',
data={
'device_type': device_type,
'device_id': device_id,
'openid': user_id,
'content': content
}
)
def create_qrcode(self, device_ids):
"""
获取设备二维码
详情请参考
http://iot.weixin.qq.com/document-2_5.html
:param device_ids: 设备id的列表
:return: 返回的 JSON 数据包
"""
return self._post(
'create_qrcode',
data={
'device_num': len(device_ids),
'device_id_list': device_ids
}
)
def get_qrcode_url(self, ticket, data=None):
"""
通过 ticket 换取二维码地址
详情请参考
http://iot.weixin.qq.com/document-2_5.html
:param ticket: 二维码 ticket
:param data: 额外数据
:return: 二维码地址
"""
url = 'http://we.qq.com/d/{ticket}'.format(ticket=ticket)
if data:
if isinstance(data, (dict, tuple, list)):
data = urllib.urlencode(data)
data = to_text(base64.b64encode(to_binary(data)))
url = '{base}#{data}'.format(base=url, data=data)
return url
def bind(self, ticket, device_id, user_id):
"""
绑定设备
详情请参考
http://iot.weixin.qq.com/document-2_12.html
:param ticket: 绑定操作合法性的凭证由微信后台生成第三方H5通过客户端jsapi获得
:param device_id: 设备id
:param user_id: 用户对应的openid
:return: 返回的 JSON 数据包
"""
return self._post(
'bind',
data={
'ticket': ticket,
'device_id': device_id,
'openid': user_id
}
)
def unbind(self, ticket, device_id, user_id):
"""
解绑设备
详情请参考
http://iot.weixin.qq.com/document-2_12.html
:param ticket: 绑定操作合法性的凭证由微信后台生成第三方H5通过客户端jsapi获得
:param device_id: 设备id
:param user_id: 用户对应的openid
:return: 返回的 JSON 数据包
"""
return self._post(
'unbind',
data={
'ticket': ticket,
'device_id': device_id,
'openid': user_id
}
)
def compel_bind(self, device_id, user_id):
"""
强制绑定用户和设备
详情请参考
http://iot.weixin.qq.com/document-2_12.html
:param device_id: 设备id
:param user_id: 用户对应的openid
:return: 返回的 JSON 数据包
"""
return self._post(
'compel_bind',
data={
'device_id': device_id,
'openid': user_id
}
)
force_bind = compel_bind
def compel_unbind(self, device_id, user_id):
"""
强制解绑用户和设备
详情请参考
http://iot.weixin.qq.com/document-2_12.html
:param device_id: 设备id
:param user_id: 用户对应的openid
:return: 返回的 JSON 数据包
"""
return self._post(
'compel_unbind',
data={
'device_id': device_id,
'openid': user_id
}
)
force_unbind = compel_unbind
def get_stat(self, device_id):
"""
设备状态查询
详情请参考
http://iot.weixin.qq.com/document-2_7.html
:param device_id: 设备id
:return: 返回的 JSON 数据包
"""
return self._post(
'get_stat',
data={'device_id': device_id}
)
def verify_qrcode(self, ticket):
"""
验证二维码
详情请参考
http://iot.weixin.qq.com/document-2_9.html
:param ticket: 设备二维码的ticket
:return: 返回的 JSON 数据包
"""
return self._post(
'verify_qrcode',
data={'ticket': ticket}
)
def get_user_id(self, device_type, device_id):
"""
获取设备绑定openID
详情请参考
http://iot.weixin.qq.com/document-2_4.html
:param device_type: 设备类型目前为“公众账号原始ID”
:param device_id: 设备id
:return: 返回的 JSON 数据包
"""
return self._post(
'get_openid',
data={
'device_type': device_type,
'device_id': device_id
}
)
get_open_id = get_user_id
def get_binded_devices(self, user_id):
"""
通过openid获取用户在当前devicetype下绑定的deviceid列表
详情请参考
http://iot.weixin.qq.com/document-2_13.html
:param user_id: 要查询的用户的openid
:return: 返回的 JSON 数据包
"""
return self._post(
'get_bind_device',
data={'openid': user_id}
)
get_bind_device = get_binded_devices
def send_status_message(self, device_type, device_id, user_id, status):
"""
主动发送设备状态消息给微信终端
详情请参考
http://iot.weixin.qq.com/document-2_10.html
:param device_type: 设备类型目前为“公众账号原始ID”
:param device_id: 设备ID
:param user_id: 微信用户账号的openid
:param status: 设备状态0--未连接, 1--已连接
:return: 返回的 JSON 数据包
"""
return self._post(
'transmsg',
data={
'device_type': device_type,
'device_id': device_id,
'open_id': user_id,
'device_status': status
}
)
def authorize(self, devices, op_type=0):
"""
设备授权
详情请参考
http://iot.weixin.qq.com/document-2_6.html
:param devices: 设备信息的列表
:param op_type: 请求操作的类型限定取值为0设备授权 1设备更新
:return: 返回的 JSON 数据包
"""
return self._post(
'authorize',
data={
'device_num': len(devices),
'device_list': devices,
'op_type': op_type
}
)
def get_qrcode(self):
"""
获取deviceid和二维码
详情请参考
http://iot.weixin.qq.com/document-2_11.html
:return: 返回的 JSON 数据包
"""
return self._get('getqrcode')
def authorize_device(self, devices, op_type=1):
"""
设备授权
详情请参考
http://iot.weixin.qq.com/document-2_6.html
:param devices: 设备信息的列表
:param op_type: 请求操作的类型限定取值为0设备授权 1设备更新
:return: 返回的 JSON 数据包
"""
return self._post(
'authorize_device',
data={
'device_num': len(devices),
'device_list': devices,
'op_type': op_type
}
)

View File

@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.utils import to_text
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatGroup(BaseWeChatAPI):
def create(self, name):
"""
创建分组
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param name: 分组名字30个字符以内
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.group.create('New Group')
"""
name = to_text(name)
return self._post(
'groups/create',
data={'group': {'name': name}}
)
def get(self, user_id=None):
"""
查询所有分组或查询用户所在分组 ID
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param user_id: 用户 ID提供时查询该用户所在分组否则查询所有分组
:return: 所有分组列表或用户所在分组 ID
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
group = client.group.get('openid')
"""
if user_id is None:
res = self._get(
'groups/get',
result_processor=lambda x: x['groups']
)
else:
res = self._post(
'groups/getid',
data={'openid': user_id},
result_processor=lambda x: x['groupid']
)
return res
def update(self, group_id, name):
"""
修改分组名
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param group_id: 分组id由微信分配
:param name: 分组名字30个字符以内
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.group.update(1234, 'New Name')
"""
name = to_text(name)
return self._post(
'groups/update',
data={
'group': {
'id': int(group_id),
'name': name
}
}
)
def move_user(self, user_id, group_id):
"""
移动用户分组
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param user_id: 用户 ID, 可以是单个或者列表,为列表时为批量移动用户分组
:param group_id: 分组 ID
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.group.move_user('openid', 1234)
"""
data = {'to_groupid': group_id}
if isinstance(user_id, (tuple, list)):
endpoint = 'groups/members/batchupdate'
data['openid_list'] = user_id
else:
endpoint = 'groups/members/update'
data['openid'] = user_id
return self._post(endpoint, data=data)
def delete(self, group_id):
"""
删除分组
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param group_id: 分组 ID
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.group.delete(1234)
"""
return self._post(
'groups/delete',
data={
'group': {
'id': group_id
}
}
)

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
"""
wechatpy.client.jsapi
~~~~~~~~~~~~~~~~~~~~
This module provides some APIs for JS SDK
:copyright: (c) 2014 by messense.
:license: MIT, see LICENSE for more details.
"""
from __future__ import absolute_import, unicode_literals
import time
from wechatpy.utils import WeChatSigner
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatJSAPI(BaseWeChatAPI):
def get_ticket(self, type='jsapi'):
"""
获取微信 JS-SDK ticket
:return: 返回的 JSON 数据包
"""
return self._get(
'ticket/getticket',
params={'type': type}
)
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('jsapi')
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,167 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.utils import json
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMaterial(BaseWeChatAPI):
def add_articles(self, articles):
"""
新增永久图文素材
详情请参考
http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
: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_news',
data={
'articles': articles_data
}
)
def add(self, media_type, media_file, title=None, introduction=None):
"""
新增其它类型永久素材
详情请参考
http://mp.weixin.qq.com/wiki/14/7e6c03263063f4813141c3e17dd4350a.html
:param media_type: 媒体文件类型分别有图片image、语音voice、视频video和缩略图thumb
:param media_file: 要上传的文件,一个 File-object
:param title: 视频素材标题,仅上传视频素材时需要
:param introduction: 视频素材简介,仅上传视频素材时需要
:return: 返回的 JSON 数据包
"""
params = {
'access_token': self.access_token,
'type': media_type
}
if media_type == 'video':
assert title, 'Video title must be set'
assert introduction, 'Video introduction must be set'
description = {
'title': title,
'introduction': introduction
}
params['description'] = json.dumps(description)
return self._post(
'material/add_material',
params=params,
files={
'media': media_file
}
)
def get(self, media_id):
"""
获取永久素材
详情请参考
http://mp.weixin.qq.com/wiki/4/b3546879f07623cb30df9ca0e420a5d0.html
:param media_id: 素材的 media_id
:return: 图文素材返回图文列表,其它类型为素材的内容
"""
def _processor(res):
if isinstance(res, dict):
# 图文素材
return res.get('news_item', [])
return res
res = self._post(
'material/get_material',
data={
'media_id': media_id
},
result_processor=_processor
)
return res
def delete(self, media_id):
"""
删除永久素材
详情请参考
http://mp.weixin.qq.com/wiki/5/e66f61c303db51a6c0f90f46b15af5f5.html
:param media_id: 素材的 media_id
:return: 返回的 JSON 数据包
"""
return self._post(
'material/del_material',
data={
'media_id': media_id
}
)
def update_articles(self, media_id, index, articles):
"""
修改永久图文素材
详情请参考
http://mp.weixin.qq.com/wiki/4/19a59cba020d506e767360ca1be29450.html
: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={
'media_id': media_id,
'index': index,
'articles': articles_data
}
)
def batchget(self, media_type, offset=0, count=20):
"""
批量获取永久素材列表
详情请参考
http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html
:param media_type: 媒体文件类型分别有图片image、语音voice、视频video和缩略图news
:param offset: 从全部素材的该偏移位置开始返回0 表示从第一个素材返回
:param count: 返回素材的数量取值在1到20之间
:return: 返回的 JSON 数据包
"""
return self._post(
'material/batchget_material',
data={
'type': media_type,
'offset': offset,
'count': count
}
)
def get_count(self):
"""
获取素材总数
详情请参考
http://mp.weixin.qq.com/wiki/16/8cc64f8c189674b421bee3ed403993b8.html
:return: 返回的 JSON 数据包
"""
return self._get('material/get_materialcount')

View File

@@ -0,0 +1,126 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMedia(BaseWeChatAPI):
def upload(self, media_type, media_file):
"""
上传临时素材
详情请参考
http://mp.weixin.qq.com/wiki/5/963fc70b80dc75483a271298a76a8d59.html
:param media_type: 媒体文件类型分别有图片image、语音voice、视频video和缩略图thumb
:param media_file: 要上传的文件,一个 File-object
:return: 返回的 JSON 数据包
"""
return self._post(
url='http://file.api.weixin.qq.com/cgi-bin/media/upload',
params={
'type': media_type
},
files={
'media': media_file
}
)
def download(self, media_id):
"""
获取临时素材
详情请参考
http://mp.weixin.qq.com/wiki/10/78b15308b053286e2a66b33f0f0f5fb6.html
:param media_id: 媒体文件 ID
:return: requests 的 Response 实例
"""
return self._get(
'http://file.api.weixin.qq.com/cgi-bin/media/get',
params={
'media_id': media_id
}
)
def get_url(self, media_id):
"""
获取临时素材下载地址
:param media_id: 媒体文件 ID
:return: 临时素材下载地址
"""
parts = (
'http://file.api.weixin.qq.com/cgi-bin/media/get',
'?access_token=',
self.access_token,
'&media_id=',
media_id
)
return ''.join(parts)
def upload_video(self, media_id, title, description):
"""
群发视频消息时获取视频 media_id
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param media_id: 需通过基础支持中的上传下载多媒体文件 :func:`upload` 来得到
:param title: 视频标题
:param description: 视频描述
:return: 返回的 JSON 数据包
"""
return self._post(
url='https://file.api.weixin.qq.com/cgi-bin/media/uploadvideo',
data={
'media_id': media_id,
'title': title,
'description': description
}
)
def upload_articles(self, articles):
"""
上传图文消息素材
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
: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(
'media/uploadnews',
data={
'articles': articles_data
}
)
def upload_mass_image(self, media_file):
"""
上传群发消息内的图片
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param media_file: 要上传的文件,一个 File-object
:return: 上传成功时返回图片 URL
"""
res = self._post(
url='https://api.weixin.qq.com/cgi-bin/media/uploadimg',
files={
'media': media_file
},
result_processor=lambda x: x['url']
)
return res

View File

@@ -0,0 +1,235 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.exceptions import WeChatClientException
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMenu(BaseWeChatAPI):
def get(self):
"""
查询自定义菜单。
详情请参考
http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
menu = client.menu.get()
"""
try:
return self._get('menu/get')
except WeChatClientException as e:
if e.errcode == 46003:
# menu not exist
return None
else:
raise e
def create(self, menu_data):
"""
创建自定义菜单 ::
from wechatpy import WeChatClient
client = WeChatClient("appid", "secret")
client.menu.create({
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"type":"click",
"name":"歌手简介",
"key":"V1001_TODAY_SINGER"
},
{
"name":"菜单",
"sub_button":[
{
"type":"xml",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"xml",
"name":"视频",
"url":"http://v.qq.com/"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}
]
}
]
})
详情请参考
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013
:param menu_data: Python 字典
:return: 返回的 JSON 数据包
"""
return self._post(
'menu/create',
data=menu_data
)
update = create
def delete(self):
"""
删除自定义菜单。
详情请参考
http://mp.weixin.qq.com/wiki/16/8ed41ba931e4845844ad6d1eeb8060c8.html
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.menu.delete()
"""
return self._get('menu/delete')
def get_menu_info(self):
"""
获取自定义菜单配置
详情请参考
http://mp.weixin.qq.com/wiki/17/4dc4b0514fdad7a5fbbd477aa9aab5ed.html
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
menu_info = client.menu.get_menu_info()
"""
return self._get('get_current_selfmenu_info')
def add_conditional(self, menu_data):
"""
创建个性化菜单 ::
from wechatpy import WeChatClient
client = WeChatClient("appid", "secret")
client.menu.add_conditional({
"button":[
{
"type":"click",
"name":"今日歌曲",
"key":"V1001_TODAY_MUSIC"
},
{
"type":"click",
"name":"歌手简介",
"key":"V1001_TODAY_SINGER"
},
{
"name":"菜单",
"sub_button":[
{
"type":"xml",
"name":"搜索",
"url":"http://www.soso.com/"
},
{
"type":"xml",
"name":"视频",
"url":"http://v.qq.com/"
},
{
"type":"click",
"name":"赞一下我们",
"key":"V1001_GOOD"
}
]
}
],
"matchrule":{
"group_id":"2",
"sex":"1",
"country":"中国",
"province":"广东",
"city":"广州",
"client_platform_type":"2"
}
})
详情请参考
http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
:param menu_data: Python 字典
:return: 返回的 JSON 数据包
"""
return self._post(
'menu/addconditional',
data=menu_data
)
def del_conditional(self, menu_id):
"""
删除个性化菜单
详情请参考
http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
:param menu_id: 菜单ID
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.menu.del_conditional('menu_id')
"""
return self._post(
'menu/delconditional',
data={'menuid': menu_id}
)
def try_match(self, user_id):
"""
测试个性化菜单匹配结果
详情请参考
http://mp.weixin.qq.com/wiki/0/c48ccd12b69ae023159b4bfaa7c39c20.html
:param user_id: 可以是粉丝的OpenID也可以是粉丝的微信号。
:return: 该接口将返回菜单配置
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.menu.try_match('openid')
"""
return self._post(
'menu/trymatch',
data={'user_id': user_id}
)

View File

@@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
from wechatpy.client.api.merchant.category import MerchantCategory
from wechatpy.client.api.merchant.stock import MerchantStock
from wechatpy.client.api.merchant.express import MerchantExpress
from wechatpy.client.api.merchant.group import MerchantGroup
from wechatpy.client.api.merchant.shelf import MerchantShelf
from wechatpy.client.api.merchant.order import MerchantOrder
from wechatpy.client.api.merchant.common import MerchantCommon
class WeChatMerchant(BaseWeChatAPI):
def __init__(self, *args, **kwargs):
super(WeChatMerchant, self).__init__(*args, **kwargs)
# sub APIs
self.category = MerchantCategory(self._client)
self.stock = MerchantStock(self._client)
self.express = MerchantExpress(self._client)
self.group = MerchantGroup(self._client)
self.shelf = MerchantShelf(self._client)
self.order = MerchantOrder(self._client)
self.common = MerchantCommon(self._client)
def create(self, product_data):
return self._post(
'merchant/create',
data=product_data
)
def delete(self, product_id):
return self._post(
'merchant/del',
data={
'product_id': product_id
}
)
def update(self, product_id, product_data):
product_data['product_id'] = product_id
return self._post(
'merchant/update',
data=product_data
)
def get(self, product_id):
return self._post(
'merchant/get',
data={
'product_id': product_id
}
)
def get_by_status(self, status):
return self._post(
'merchant/getbystatus',
data={
'status': status
}
)
def update_product_status(self, product_id, status):
return self._post(
'merchant/modproductstatus',
data={
'product_id': product_id,
'status': status
}
)

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantCategory(BaseWeChatAPI):
def get_sub_categories(self, cate_id):
res = self._post(
'merchant/category/getsub',
data={'cate_id': cate_id},
result_processor=lambda x: x['cate_list']
)
return res
def get_sku_list(self, cate_id):
res = self._post(
'merchant/category/getsku',
data={'cate_id': cate_id},
result_processor=lambda x: x['sku_table']
)
return res
def get_properties(self, cate_id):
res = self._post(
'merchant/category/getproperty',
data={'cate_id': cate_id},
result_processor=lambda x: x['properties']
)
return res

View File

@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantCommon(BaseWeChatAPI):
def upload_image(self, filename, image_data):
res = self._post(
'merchant/common/upload_img',
params={
'filename': filename
},
data=image_data,
result_processor=lambda x: x['image_url']
)
return res

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantExpress(BaseWeChatAPI):
def add(self, delivery_template):
return self._post(
'merchant/express/add',
data={
'delivery_template': delivery_template
}
)
def delete(self, template_id):
return self._post(
'merchant/express/del',
data={
'template_id': template_id
}
)
def update(self, template_id, delivery_template):
return self._post(
'merchant/express/update',
data={
'template_id': template_id,
'delivery_template': delivery_template
}
)
def get(self, template_id):
res = self._post(
'merchant/express/getbyid',
data={
'template_id': template_id
},
result_processor=lambda x: x['template_info']
)
return res
def get_all(self):
res = self._get(
'merchant/express/getall',
result_processor=lambda x: x['template_info']
)
return res

View File

@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantGroup(BaseWeChatAPI):
def add(self, name, product_list):
return self._post(
'merchant/group/add',
data={
'group_detail': {
'group_name': name,
'product_list': product_list
}
}
)
def delete(self, group_id):
return self._post(
'merchant/group/del',
data={
'group_id': group_id
}
)
def update(self, group_id, name):
return self._post(
'merchant/group/propertymod',
data={
'group_id': group_id,
'group_name': name
}
)
def update_product(self, group_id, product):
return self._post(
'merchant/group/productmod',
data={
'group_id': group_id,
'product': product
}
)
def get_all(self):
res = self._get(
'merchant/group/getall',
result_processor=lambda x: x['group_detail']
)
return res
def get(self, group_id):
res = self._post(
'merchant/group/getbyid',
data={
'group_id': group_id
},
result_processor=lambda x: x['group_detail']
)
return res

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantOrder(BaseWeChatAPI):
def get(self, order_id):
res = self._post(
'merchant/order/getbyid',
data={
'order_id': order_id
},
result_processor=lambda x: x['order']
)
return res
def get_by_filter(self, status=None, begin_time=None, end_time=None):
filter_dict = optionaldict(
status=status,
begintime=begin_time,
endtime=end_time
)
res = self._post(
'merchant/order/getbyfilter',
data=dict(filter_dict),
result_processor=lambda x: x['order_list']
)
return res
def set_delivery(self, order_id, company, track_no,
need_delivery=1, is_others=0):
return self._post(
'merchant/order/setdelivery',
data={
'order_id': order_id,
'delivery_company': company,
'delivery_track_no': track_no,
'need_delivery': need_delivery,
'is_others': is_others
}
)
def close(self, order_id):
return self._post(
'merchant/order/close',
data={
'order_id': order_id
}
)

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantShelf(BaseWeChatAPI):
def add(self, name, banner, shelf_data):
return self._post(
'merchant/shelf/add',
data={
'shelf_name': name,
'shelf_banner': banner,
'shelf_data': shelf_data
}
)
def delete(self, shelf_id):
return self._post(
'merchant/shelf/del',
data={
'shelf_id': shelf_id
}
)
def update(self, shelf_id, name, banner, shelf_data):
return self._post(
'merchant/shelf/add',
data={
'shelf_id': shelf_id,
'shelf_name': name,
'shelf_banner': banner,
'shelf_data': shelf_data
}
)
def get_all(self):
res = self._get(
'merchant/shelf/getall',
result_processor=lambda x: x['shelves']
)
return res
def get(self, shelf_id):
return self._post(
'merchant/shelf/getbyid',
data={
'shelf_id': shelf_id
}
)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class MerchantStock(BaseWeChatAPI):
def add(self, product_id, quantity, sku_info=''):
return self._post(
'merchant/stock/add',
data={
'product_id': product_id,
'quantity': quantity,
'sku_info': sku_info
}
)
def reduce(self, product_id, quantity, sku_info=''):
return self._post(
'merchant/stock/reduce',
data={
'product_id': product_id,
'quantity': quantity,
'sku_info': sku_info
}
)

View File

@@ -0,0 +1,572 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import re
import six
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMessage(BaseWeChatAPI):
OPENID_RE = re.compile(r'^[\w\-]{28}$', re.I)
def _send_custom_message(self, data, account=None):
data = data or {}
if account:
data['customservice'] = {'kf_account': account}
return self._post(
'message/custom/send',
data=data
)
def send_text(self, user_id, content, account=None):
"""
发送文本消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param content: 消息正文
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.send_text('openid', 'text')
"""
data = {
'touser': user_id,
'msgtype': 'text',
'text': {'content': content}
}
return self._send_custom_message(data, account=account)
def send_image(self, user_id, media_id, account=None):
"""
发送图片消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param media_id: 图片的媒体ID。 可以通过 :func:`upload_media` 上传。
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.send_image('openid', 'media_id')
"""
data = {
'touser': user_id,
'msgtype': 'image',
'image': {
'media_id': media_id
}
}
return self._send_custom_message(data, account=account)
def send_voice(self, user_id, media_id, account=None):
"""
发送语音消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param media_id: 发送的语音的媒体ID。 可以通过 :func:`upload_media` 上传。
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.send_voice('openid', 'media_id')
"""
data = {
'touser': user_id,
'msgtype': 'voice',
'voice': {
'media_id': media_id
}
}
return self._send_custom_message(data, account=account)
def send_video(self, user_id, media_id, title=None,
description=None, account=None):
"""
发送视频消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param media_id: 发送的视频的媒体ID。 可以通过 :func:`upload_media` 上传。
:param title: 视频消息的标题
:param description: 视频消息的描述
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.send_video('openid', 'media_id', 'title', 'description')
"""
video_data = {
'media_id': media_id,
}
if title:
video_data['title'] = title
if description:
video_data['description'] = description
data = {
'touser': user_id,
'msgtype': 'video',
'video': video_data
}
return self._send_custom_message(data, account=account)
def send_music(self, user_id, url, hq_url, thumb_media_id,
title=None, description=None, account=None):
"""
发送音乐消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param url: 音乐链接
:param hq_url: 高品质音乐链接wifi环境优先使用该链接播放音乐
:param thumb_media_id: 缩略图的媒体ID。 可以通过 :func:`upload_media` 上传。
:param title: 音乐标题
:param description: 音乐描述
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
"""
music_data = {
'musicurl': url,
'hqmusicurl': hq_url,
'thumb_media_id': thumb_media_id
}
if title:
music_data['title'] = title
if description:
music_data['description'] = description
data = {
'touser': user_id,
'msgtype': 'music',
'music': music_data
}
return self._send_custom_message(data, account=account)
def send_articles(self, user_id, articles, account=None):
"""
发送图文消息
详情请参考
http://mp.weixin.qq.com/wiki/7/12a5a320ae96fecdf0e15cb06123de9f.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param articles: 一个包含至多10个图文的数组, 或者微信图文消息素材 media_id
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
"""
if isinstance(articles, (tuple, list)):
articles_data = []
for article in articles:
articles_data.append({
'title': article['title'],
'description': article['description'],
'url': article['url'],
'picurl': article.get('image', article.get('picurl')),
})
data = {
'touser': user_id,
'msgtype': 'news',
'news': {
'articles': articles_data
}
}
else:
data = {
'touser': user_id,
'msgtype': 'mpnews',
'mpnews': {
'media_id': articles,
}
}
return self._send_custom_message(data, account=account)
def send_card(self, user_id, card_id, card_ext, account=None):
"""
发送卡券消息
详情请参参考
http://mp.weixin.qq.com/wiki/1/70a29afed17f56d537c833f89be979c9.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param card_id: 卡券 ID
:param card_ext: 卡券扩展信息
:param account: 可选,客服账号
:return: 返回的 JSON 数据包
"""
data = {
'touser': user_id,
'msgtype': 'wxcard',
'wxcard': {
'card_id': card_id,
'card_ext': card_ext
}
}
return self._send_custom_message(data, account=account)
def delete_mass(self, msg_id):
"""
删除群发消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param msg_id: 要删除的群发消息 ID
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.delete_mass('message id')
"""
return self._post(
'message/mass/delete',
data={
'msg_id': msg_id
}
)
def _send_mass_message(self, group_or_users, msg_type, msg,
is_to_all=False, preview=False):
data = {
'msgtype': msg_type
}
if not preview:
if isinstance(group_or_users, (tuple, list)):
# send by user ids
data['touser'] = group_or_users
endpoint = 'message/mass/send'
else:
# send by group id
data['filter'] = {
'group_id': group_or_users,
'is_to_all': is_to_all,
}
endpoint = 'message/mass/sendall'
else:
if not isinstance(group_or_users, six.string_types):
raise ValueError('group_or_users should be string types')
# 预览接口
if self.OPENID_RE.match(group_or_users):
# 按照 openid 预览群发
data['touser'] = group_or_users
else:
# 按照微信号预览群发
data['towxname'] = group_or_users
endpoint = 'message/mass/preview'
data.update(msg)
return self._post(
endpoint,
data=data
)
def send_mass_text(self, group_or_users, content,
is_to_all=False, preview=False):
"""
群发文本消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param content: 消息正文
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
return self._send_mass_message(
group_or_users,
'text',
{
'text': {
'content': content
}
},
is_to_all,
preview
)
def send_mass_image(self, group_or_users, media_id,
is_to_all=False, preview=False):
"""
群发图片消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param media_id: 图片的媒体 ID。 可以通过 :func:`upload_media` 上传。
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
return self._send_mass_message(
group_or_users,
'image',
{
'image': {
'media_id': media_id
}
},
is_to_all,
preview
)
def send_mass_voice(self, group_or_users, media_id,
is_to_all=False, preview=False):
"""
群发语音消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param media_id: 语音的媒体 ID。可以通过 :func:`upload_media` 上传。
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
return self._send_mass_message(
group_or_users,
'voice',
{
'voice': {
'media_id': media_id
}
},
is_to_all,
preview
)
def send_mass_video(self, group_or_users, media_id, title=None,
description=None, is_to_all=False, preview=False):
"""
群发视频消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param media_id: 视频的媒体 ID。可以通过 :func:`upload_video` 上传。
:param title: 视频标题
:param description: 视频描述
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
video_data = {
'media_id': media_id
}
if title:
video_data['title'] = title
if description:
video_data['description'] = description
return self._send_mass_message(
group_or_users,
'mpvideo',
{
'mpvideo': video_data
},
is_to_all,
preview
)
def send_mass_article(self, group_or_users, media_id,
is_to_all=False, preview=False):
"""
群发图文消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param media_id: 图文的媒体 ID。可以通过 :func:`upload_articles` 上传。
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
return self._send_mass_message(
group_or_users,
'mpnews',
{
'mpnews': {
'media_id': media_id
}
},
is_to_all,
preview
)
def get_mass(self, msg_id):
"""
查询群发消息发送状态
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param msg_id: 群发消息后返回的消息id
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.message.get_mass('mass message id')
"""
return self._post(
'message/mass/get',
data={
'msg_id': msg_id
}
)
def send_template(self, user_id, template_id, url, top_color, data):
"""
发送模板消息
详情请参考
http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param template_id: 模板 ID。在公众平台线上模板库中选用模板获得
:param url: 链接地址
:param top_color: 消息顶部颜色
:param data: 模板消息数据
:return: 返回的 JSON 数据包
"""
return self._post(
'message/template/send',
data={
'touser': user_id,
'template_id': template_id,
'url': url,
'topcolor': top_color,
'data': data
}
)
def send_template_applet(self, user_id, template_id, appid, pagepath, data):
"""
发送模板消息,点击跳转到小程序
详情请参考
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param template_id: 模板 ID。在公众平台线上模板库中选用模板获得
:param appid: 所需跳转到的小程序appid该小程序appid必须与发模板消息的公众号是绑定关联关系暂不支持小游戏
:param pagepath: 所需跳转到小程序的具体页面路径,支持带参数,示例index?foo=bar要求该小程序已发布暂不支持小游戏 不必填
:param data: 模板消息数据
:return: 返回的 JSON 数据包
"""
data_all = {
'touser': user_id,
'template_id': template_id,
"miniprogram": {
"appid": appid,
# "pagepath": pagepath
},
'data': data
}
if pagepath:
data_all['miniprogram']['pagepath'] = pagepath
return self._post(
'message/template/send',
data=data_all
)
def get_autoreply_info(self):
"""
获取自动回复规则
详情请参考
http://mp.weixin.qq.com/wiki/7/7b5789bb1262fb866d01b4b40b0efecb.html
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
info = client.message.get_autoreply_info()
"""
return self._get('get_current_autoreply_info')
def send_mass_card(self, group_or_users, card_id,
is_to_all=False, preview=False):
"""
群发卡券消息
详情请参考
http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
:param group_or_users: 值为整型数字时为按分组群发,值为列表/元组时为按 OpenID 列表群发
:param card_id: 卡券 ID
:param is_to_all: 用于设定是否向全部用户发送值为true或false选择true该消息群发给所有用户
选择false可根据group_id发送给指定群组的用户
:param preview: 是否发送预览,此时 group_or_users 参数应为一个openid字符串
:return: 返回的 JSON 数据包
"""
return self._send_mass_message(
group_or_users,
'wxcard',
{
'wxcard': {
'card_id': card_id
}
},
is_to_all,
preview
)

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatMisc(BaseWeChatAPI):
def short_url(self, long_url):
"""
将一条长链接转成短链接
详情请参考
http://mp.weixin.qq.com/wiki/10/165c9b15eddcfbd8699ac12b0bd89ae6.html
:param long_url: 长链接地址
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.misc.short_url('http://www.qq.com')
"""
return self._post(
'shorturl',
data={
'action': 'long2short',
'long_url': long_url
}
)
def get_wechat_ips(self):
"""
获取微信服务器 IP 地址列表
:return: IP 地址列表
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
ips = client.misc.get_wechat_ips()
"""
res = self._get(
'getcallbackip',
result_processor=lambda x: x['ip_list']
)
return res

View File

@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatPoi(BaseWeChatAPI):
def add_picture(self, access_token, buffer):
"""
上传图片接口
:param access_token: 接口凭证
:param buffer: buffer
:return:
详情请参考: https://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
"""
def add(self, poi_data):
"""
创建门店
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:param poi_data: 门店信息字典
:return: 返回的 JSON 数据包
"""
return self._post('poi/addpoi', data=poi_data)
def get(self, poi_id):
"""
查询门店信息
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:param poi_id: 门店 ID
:return: 返回的 JSON 数据包
"""
return self._post('poi/getpoi', data={'poi_id': poi_id})
def list(self, begin=0, limit=20):
"""
查询门店列表
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:param begin: 开始位置0 即为从第一条开始查询
:param limit: 返回数据条数最大允许50默认为20
:return: 返回的 JSON 数据包
"""
return self._post(
'poi/getpoilist',
data={
'begin': begin,
'limit': limit,
}
)
def update(self, poi_data):
"""
修改门店
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:param poi_data: 门店信息字典
:return: 返回的 JSON 数据包
"""
return self._post('poi/updatepoi', data=poi_data)
def delete(self, poi_id):
"""
删除门店
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:param poi_id: 门店 ID
:return: 返回的 JSON 数据包
"""
return self._post('poi/delpoi', data={'poi_id': poi_id})
def get_categories(self):
"""
获取微信门店类目表
详情请参考
http://mp.weixin.qq.com/wiki/16/8f182af4d8dcea02c56506306bdb2f4c.html
:return: 门店类目表
"""
res = self._get(
'api_getwxcategory',
result_processor=lambda x: x['category_list']
)
return res

View File

@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import requests
import six
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatQRCode(BaseWeChatAPI):
def create(self, qrcode_data):
"""
创建二维码
详情请参考
http://mp.weixin.qq.com/wiki/18/28fc21e7ed87bec960651f0ce873ef8a.html
:param data: 你要发送的参数 dict
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.qrcode.create({
'expire_seconds': 1800,
'action_name': 'QR_SCENE',
'action_info': {
'scene': {'scene_id': 123},
}
})
"""
return self._post(
'qrcode/create',
data=qrcode_data
)
def show(self, ticket):
"""
通过ticket换取二维码
详情请参考
http://mp.weixin.qq.com/wiki/18/28fc21e7ed87bec960651f0ce873ef8a.html
:param ticket: 二维码 ticket 。可以通过 :func:`create` 获取到
:return: 返回的 Request 对象
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.qrcode.show('ticket data')
"""
if isinstance(ticket, dict):
ticket = ticket['ticket']
return requests.get(
url='https://mp.weixin.qq.com/cgi-bin/showqrcode',
params={
'ticket': ticket
}
)
@classmethod
def get_url(cls, ticket):
"""
通过ticket换取二维码地址
详情请参考
http://mp.weixin.qq.com/wiki/18/28fc21e7ed87bec960651f0ce873ef8a.html
:param ticket: 二维码 ticket 。可以通过 :func:`create` 获取到
:return: 返回的二维码地址
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
url = client.qrcode.get_url('ticket data')
"""
url = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={ticket}'
if isinstance(ticket, dict):
ticket = ticket['ticket']
ticket = six.moves.urllib.parse.quote(ticket)
return url.format(ticket=ticket)

View File

@@ -0,0 +1,171 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatScan(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/scan/'
def get_merchant_info(self):
"""
获取商户信息
详情请参考
http://mp.weixin.qq.com/wiki/6/c61604ff6890d386d6227945ad4a68d2.html
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
info = client.scan.get_merchant_info()
"""
return self._get('merchantinfo/get')
def create_product(self, product_data):
"""
创建商品
详情请参考
http://mp.weixin.qq.com/wiki/6/c61604ff6890d386d6227945ad4a68d2.html
:return: 返回的 JSON 数据包
"""
return self._post('product/create', data=product_data)
def modify_product_status(self, standard, key, status):
"""
提交审核/取消发布商品
详情请参考
http://mp.weixin.qq.com/wiki/15/1007691d0f1c10a0588c6517f12ed70f.html
:param standard: 商品编码标准
:param key: 商品编码内容
:param status: 设置发布状态。on 为提交审核off 为取消发布
:return: 返回的 JSON 数据包
"""
data = {
'keystandard': standard,
'keystr': key,
'status': status,
}
return self._post('product/modstatus', data=data)
def publish_product(self, standard, key):
"""
提交审核商品 shortcut 接口
等同于调用 ``modify_product_status(standard, key, 'on')``
"""
return self.modify_product_status(standard, key, 'on')
def unpublish_product(self, standard, key):
"""
取消发布商品 shortcut 接口
等同于调用 ``modify_product_status(standard, key, 'off')``
"""
return self.modify_product_status(standard, key, 'off')
def set_test_whitelist(self, userids=None, usernames=None):
"""
设置测试人员白名单
注意每次设置均被视为一次重置而非增量设置。openid、微信号合计最多设置10个。
详情请参考
http://mp.weixin.qq.com/wiki/15/1007691d0f1c10a0588c6517f12ed70f.html
:param userids: 可选,测试人员的 openid 列表
:param usernames: 可选,测试人员的微信号列表
:return: 返回的 JSON 数据包
"""
data = optionaldict(
openid=userids,
username=usernames
)
return self._post('testwhitelist/set', data=data)
def get_product(self, standard, key):
"""
查询商品信息
详情请参考
http://mp.weixin.qq.com/wiki/15/7fa787701295b884410b5163e13313af.html
:param standard: 商品编码标准
:param key: 商品编码内容
:return: 返回的 JSON 数据包
"""
data = {
'keystandard': standard,
'keystr': key,
}
return self._post('product/get', data=data)
def list_product(self, offset=0, limit=10, status=None, key=None):
"""
批量查询商品信息
详情请参考
http://mp.weixin.qq.com/wiki/15/7fa787701295b884410b5163e13313af.html
:param offset: 可选,批量查询的起始位置,从 0 开始,包含该起始位置
:param limit: 可选,批量查询的数量,默认为 10
:param status: 可选支持按状态拉取。on为发布状态off为未发布状态
check为审核中状态reject为审核未通过状态all为所有状态
:param key: 支持按部分编码内容拉取。填写该参数后,可将编码内容中包含所传参数的商品信息拉出
:return: 返回的 JSON 数据包
"""
data = optionaldict(
offset=offset,
limit=limit,
status=status,
keystr=key,
)
return self._post('product/getlist', data=data)
def update_product(self, product_data):
"""
更新商品信息
详情请参考
http://mp.weixin.qq.com/wiki/15/7fa787701295b884410b5163e13313af.html
:return: 返回的 JSON 数据包
"""
return self._post('product/update', data=product_data)
def clear_product(self, standard, key):
"""
清除商品信息
详情请参考
http://mp.weixin.qq.com/wiki/15/7fa787701295b884410b5163e13313af.html
:param standard: 商品编码标准
:param key: 商品编码内容
:return: 返回的 JSON 数据包
"""
data = {
'keystandard': standard,
'keystr': key,
}
return self._post('product/clear', data=data)
def check_ticket(self, ticket):
"""
检查 wxticket 参数有效性
详情请参考
http://mp.weixin.qq.com/wiki/15/7fa787701295b884410b5163e13313af.html
:param ticket: 请求 URL 中带上的 wxticket 参数
:return: 返回的 JSON 数据包
"""
return self._post('scanticket/check', data={'ticket': ticket})

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatSemantic(BaseWeChatAPI):
def search(self,
query,
category,
uid=None,
latitude=None,
longitude=None,
city=None,
region=None):
"""
发送语义理解请求
详情请参考
http://mp.weixin.qq.com/wiki/0/0ce78b3c9524811fee34aba3e33f3448.html
:param query: 输入文本串
:param category: 需要使用的服务类型,多个可传入列表
:param uid: 可选用户唯一id非开发者id用户区分公众号下的不同用户建议填入用户openid
:param latitude: 可选,纬度坐标,与经度同时传入;与城市二选一传入
:param longitude: 可选,经度坐标,与纬度同时传入;与城市二选一传入
:param city: 可选,城市名称,与经纬度二选一传入
:param region: 可选,区域名称,在城市存在的情况下可省;与经纬度二选一传入
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
res = client.semantic.search(
'查一下明天从北京到上海的南航机票',
'flight,hotel',
city='北京'
)
"""
if isinstance(category, (tuple, list)):
category = ','.join(category)
data = optionaldict()
data['query'] = query
data['category'] = category
data['uid'] = uid
data['latitude'] = latitude
data['longitude'] = longitude
data['city'] = city
data['region'] = region
data['appid'] = self._client.appid
return self._post(
url='https://api.weixin.qq.com/semantic/semproxy/search',
data=data
)

View File

@@ -0,0 +1,376 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import time
from datetime import datetime
import six
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatShakeAround(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/'
@classmethod
def _to_timestamp(cls, date):
if isinstance(date, six.string_types):
date = datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
if isinstance(date, datetime):
timestamp = int(time.mktime(date.timetuple()))
return timestamp
return int(date)
def apply_device_id(self, quantity, reason, poi_id=None, comment=None):
"""
申请设备ID
详情请参考
http://mp.weixin.qq.com/wiki/15/b9e012f917e3484b7ed02771156411f3.html
:param quantity: 申请的设备ID的数量单次新增设备超过500个需走人工审核流程
:param reason: 申请理由不超过100个字
:param poi_id: 可选设备关联的门店ID
:param comment: 可选备注不超过15个汉字或30个英文字母
:return: 申请的设备信息
"""
data = optionaldict()
data['quantity'] = quantity
data['apply_reason'] = reason
data['poi_id'] = poi_id
data['comment'] = comment
res = self._post(
'shakearound/device/applyid',
data=data,
result_processor=lambda x: x['data']
)
return res
def update_device(self, device_id=None, uuid=None, major=None,
minor=None, comment=None):
"""
更新设备信息
详情请参考
http://mp.weixin.qq.com/wiki/15/b9e012f917e3484b7ed02771156411f3.html
:param device_id: 设备编号若填了UUID、major、minor则可不填设备编号若二者都填则以设备编号为优先
:param uuid: UUID
:param major: major
:param minor: minor
:param comment: 设备的备注信息不超过15个汉字或30个英文字母。
:return: 返回的 JSON 数据包
"""
data = optionaldict()
data['comment'] = comment
data['device_identifier'] = {
'device_id': device_id,
'uuid': uuid,
'major': major,
'minor': minor
}
return self._post(
'shakearound/device/update',
data=data
)
def bind_device_location(self, poi_id, device_id=None, uuid=None,
major=None, minor=None):
"""
配置设备与门店的关联关系
详情请参考
http://mp.weixin.qq.com/wiki/15/b9e012f917e3484b7ed02771156411f3.html
:param poi_id: 待关联的门店ID
:param device_id: 设备编号若填了UUID、major、minor则可不填设备编号若二者都填则以设备编号为优先
:param uuid: UUID
:param major: major
:param minor: minor
:return: 返回的 JSON 数据包
"""
data = optionaldict()
data['poi_id'] = poi_id
data['device_identifier'] = {
'device_id': device_id,
'uuid': uuid,
'major': major,
'minor': minor
}
return self._post(
'shakearound/device/bindlocation',
data=data
)
def search_device(self, identifiers=None, apply_id=None,
begin=0, count=10):
"""
查询设备列表
详情请参考
http://mp.weixin.qq.com/wiki/15/b9e012f917e3484b7ed02771156411f3.html
:param identifiers: 设备 ID 信息列表
:param apply_id: 批次ID申请设备ID超出500个时所返回批次ID
:param begin: 设备列表的起始索引值
:param count: 待查询的设备个数
:return: 设备列表
"""
data = optionaldict()
data['begin'] = begin
data['count'] = count
data['apply_id'] = apply_id
if identifiers:
data['device_identifiers'] = identifiers
res = self._post(
'shakearound/device/search',
data=data,
result_processor=lambda x: x['data']
)
return res
def add_page(self, title, description, icon_url, page_url, comment=None):
"""
新增页面
详情请参考
http://mp.weixin.qq.com/wiki/5/6626199ea8757c752046d8e46cf13251.html
:param title: 在摇一摇页面展示的主标题不超过6个字
:param description: 在摇一摇页面展示的副标题不超过7个字
:param icon_url: 在摇一摇页面展示的图片。图片需先上传至微信侧服务器,
用“素材管理-上传图片素材”接口上传图片返回的图片URL再配置在此处
:param page_url: 跳转链接
:param comment: 可选页面的备注信息不超过15个字
:return: 页面信息
"""
data = optionaldict()
data['title'] = title
data['description'] = description
data['icon_url'] = icon_url
data['page_url'] = page_url
data['comment'] = comment
res = self._post(
'shakearound/page/add',
data=data,
result_processor=lambda x: x['data']
)
return res
def update_page(self, page_id, title, description,
icon_url, page_url, comment=None):
"""
编辑页面信息
详情请参考
http://mp.weixin.qq.com/wiki/5/6626199ea8757c752046d8e46cf13251.html
:param page_id: 摇周边页面唯一ID
:param title: 在摇一摇页面展示的主标题不超过6个字
:param description: 在摇一摇页面展示的副标题不超过7个字
:param icon_url: 在摇一摇页面展示的图片。图片需先上传至微信侧服务器,
用“素材管理-上传图片素材”接口上传图片返回的图片URL再配置在此处
:param page_url: 跳转链接
:param comment: 可选页面的备注信息不超过15个字
:return: 页面信息
"""
data = optionaldict()
data['page_id'] = page_id
data['title'] = title
data['description'] = description
data['icon_url'] = icon_url
data['page_url'] = page_url
data['comment'] = comment
res = self._post(
'shakearound/page/update',
data=data,
result_processor=lambda x: x['data']
)
return res
def search_pages(self, page_ids=None, begin=0, count=10):
"""
查询页面列表
详情请参考
http://mp.weixin.qq.com/wiki/5/6626199ea8757c752046d8e46cf13251.html
:param page_ids: 指定页面的id列表
:param begin: 页面列表的起始索引值
:param count: 待查询的页面个数
:return: 页面查询结果信息
"""
if not page_ids:
data = {
'type': 2,
'begin': begin,
'count': count
}
else:
if not isinstance(page_ids, (tuple, list)):
page_ids = [page_ids]
data = {
'type': 1,
'page_ids': page_ids
}
res = self._post(
'shakearound/page/search',
data=data,
result_processor=lambda x: x['data']
)
return res
def delete_page(self, page_id):
"""
删除页面
详情请参考
http://mp.weixin.qq.com/wiki/5/6626199ea8757c752046d8e46cf13251.html
:param page_id: 指定页面的id列表
:return: 返回的 JSON 数据包
"""
return self._post(
'shakearound/page/delete',
data={
'page_id': page_id
}
)
def add_material(self, media_file, media_type='icon'):
"""
上传图片素材
详情请参考
http://mp.weixin.qq.com/wiki/5/e997428269ff189d8f9a4b9e177be2d9.html
:param media_file: 要上传的文件,一个 File-object
:param media_type: 摇一摇素材类型, 取值为 icon或者 license, 默认 icon.
:return: 上传的素材信息
"""
res = self._post(
'shakearound/material/add',
files={
'media': media_file
},
params={
'type': media_type
},
result_processor=lambda x: x['data']
)
return res
def bind_device_pages(self, page_ids, bind, append, device_id=None,
uuid=None, major=None, minor=None):
"""
配置设备与页面的关联关系
详情请参考
http://mp.weixin.qq.com/wiki/12/c8120214ec0ba08af5dfcc0da1a11400.html
:param page_ids: 待关联的页面列表
:param bind: 关联操作标志位, 0为解除关联关系1为建立关联关系
:param append: 新增操作标志位, 0为覆盖1为新增
:param device_id: 设备编号若填了UUID、major、minor则可不填设备编号若二者都填则以设备编号为优先
:param uuid: UUID
:param major: major
:param minor: minor
:return: 返回的 JSON 数据包
"""
if not isinstance(page_ids, (tuple, list)):
page_ids = [page_ids]
data = {
'page_ids': page_ids,
'bind': int(bind),
'append': int(append),
'device_identifier': {
'device_id': device_id,
'uuid': uuid,
'major': major,
'minor': minor
}
}
return self._post(
'shakearound/device/bindpage',
data=data
)
def get_shake_info(self, ticket):
"""
获取摇周边的设备及用户信息
详情请参考
http://mp.weixin.qq.com/wiki/3/34904a5db3d0ec7bb5306335b8da1faf.html
:param ticket: 摇周边业务的ticket可在摇到的URL中得到ticket生效时间为30分钟
:return: 设备及用户信息
"""
res = self._post(
'shakearound/user/getshakeinfo',
data={
'ticket': ticket
},
result_processor=lambda x: x['data']
)
return res
def get_device_statistics(self, begin_date, end_date, device_id=None,
uuid=None, major=None, minor=None):
"""
以设备为维度的数据统计接口
http://mp.weixin.qq.com/wiki/0/8a24bcacad40fe7ee98d1573cb8a6764.html
:param begin_date: 起始时间最长时间跨度为30天
:param end_date: 结束时间最长时间跨度为30天
:param device_id: 设备编号若填了UUID、major、minor则可不填设备编号若二者都填则以设备编号为优先
:param uuid: UUID
:param major: major
:param minor: minor
"""
data = {
'device_identifier': {
'device_id': device_id,
'uuid': uuid,
'major': major,
'minor': minor
},
'begin_date': self._to_timestamp(begin_date),
'end_date': self._to_timestamp(end_date)
}
res = self._post(
'shakearound/statistics/device',
data=data,
result_processor=lambda x: x['data']
)
return res
def get_page_statistics(self, page_id, begin_date, end_date):
"""
以页面为维度的数据统计接口
详情请参考
http://mp.weixin.qq.com/wiki/0/8a24bcacad40fe7ee98d1573cb8a6764.html
:param page_id: 页面 ID
:param begin_date: 起始时间最长时间跨度为30天
:param end_date: 结束时间最长时间跨度为30天
:return: 统计数据
"""
res = self._post(
'shakearound/statistics/page',
data={
'page_id': page_id,
'begin_date': self._to_timestamp(begin_date),
'end_date': self._to_timestamp(end_date),
},
result_processor=lambda x: x['data']
)
return res
def get_apply_status(self, apply_id):
"""
查询设备ID申请审核状态
详情请参考
http://mp.weixin.qq.com/wiki/15/b9e012f917e3484b7ed02771156411f3.html
:param apply_id: 批次ID申请设备ID时所返回的批次ID
:return: 批次状态信息
"""
res = self._post(
'shakearound/device/applystatus',
data={
'apply_id': apply_id,
},
result_processor=lambda x: x['data']
)
return res

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatTemplate(BaseWeChatAPI):
def set_industry(self, industry_id1, industry_id2):
"""
设置所属行业
详情请参考
http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
:param industry_id1: 公众号模板消息所属行业编号
:param industry_id2: 公众号模板消息所属行业编号
:return: 返回的 JSON 数据包
"""
return self._post(
'template/api_set_industry',
data={
'industry_id1': industry_id1,
'industry_id2': industry_id2
}
)
def get(self, template_id_short):
"""
获得模板ID
详情请参考
http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
:param template_id_short: 模板库中模板的编号有“TM**”和“OPENTMTM**”等形式
:return: 模板 ID
"""
res = self._post(
'template/api_add_template',
data={
'template_id_short': template_id_short
},
result_processor=lambda x: x['template_id']
)
return res
add = get

View File

@@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
import six
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatUser(BaseWeChatAPI):
def get(self, user_id, lang='zh_CN'):
"""
获取用户基本信息
详情请参考
http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param lang: 返回国家地区语言版本zh_CN 简体zh_TW 繁体en 英语
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
user = client.user.get('openid')
"""
assert lang in ('zh_CN', 'zh_TW', 'en'), 'lang can only be one of \
zh_CN, zh_TW, en language codes'
return self._get(
'user/info',
params={
'openid': user_id,
'lang': lang
}
)
def get_followers(self, first_user_id=None):
"""
获取关注者列表
详情请参考
http://mp.weixin.qq.com/wiki/3/17e6919a39c1c53555185907acf70093.html
:param first_user_id: 可选。第一个拉取的 OPENID不填默认从头开始拉取
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
followers = client.user.get_followers()
"""
params = {}
if first_user_id:
params['next_openid'] = first_user_id
return self._get(
'user/get',
params=params
)
def update_remark(self, user_id, remark):
"""
设置用户备注名
详情请参考
http://mp.weixin.qq.com/wiki/10/bf8f4e3074e1cf91eb6518b6d08d223e.html
:param user_id: 用户 ID 。 就是你收到的 `Message` 的 source
:param remark: 备注名
:return: 返回的 JSON 数据包
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
client.user.update_remark('openid', 'Remark')
"""
return self._post(
'user/info/updateremark',
data={
'openid': user_id,
'remark': remark
}
)
def get_group_id(self, user_id):
"""
获取用户所在分组 ID
详情请参考
http://mp.weixin.qq.com/wiki/0/56d992c605a97245eb7e617854b169fc.html
:param user_id: 用户 ID
:return: 用户所在分组 ID
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
group_id = client.user.get_group_id('openid')
"""
res = self._post(
'groups/getid',
data={'openid': user_id},
result_processor=lambda x: x['groupid']
)
return res
def get_batch(self, user_list):
"""
批量获取用户基本信息
详情请参考
http://mp.weixin.qq.com/wiki/14/bb5031008f1494a59c6f71fa0f319c66.html#.E6.89.B9.E9.87.8F.E8.8E.B7.E5.8F.96.E7.94.A8.E6.88.B7.E5.9F.BA.E6.9C.AC.E4.BF.A1.E6.81.AF
:param user_id: user_list
:return: 用户信息的 list
使用示例::
from wechatpy import WeChatClient
client = WeChatClient('appid', 'secret')
users = client.user.get_batch(['openid1', 'openid2'])
users = client.user.get_batch([
{'openid': 'openid1', 'lang': 'zh-CN'},
{'openid': 'openid2', 'lang': 'en'},
])
"""
if all((isinstance(x, six.string_types) for x in user_list)):
user_list = [{'openid': oid} for oid in user_list]
res = self._post(
'user/info/batchget',
data={'user_list': user_list},
result_processor=lambda x: x['user_info_list']
)
return res

View File

@@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from datetime import datetime, date
from optionaldict import optionaldict
from wechatpy.client.api.base import BaseWeChatAPI
class WeChatWiFi(BaseWeChatAPI):
API_BASE_URL = 'https://api.weixin.qq.com/bizwifi/'
def list_shops(self, page_index=1, page_size=20):
"""
获取门店列表
详情请参考
http://mp.weixin.qq.com/wiki/15/bcfb5d4578ea818b89913472cf2bbf8f.html
:param page_index: 可选分页下标默认从1开始
:param page_size: 可选每页的个数默认20个最大20个
:return: 返回的 JSON 数据包
"""
res = self._post(
'shop/list',
data={
'pageindex': page_index,
'pagesize': page_size,
},
result_processor=lambda x: x['data']
)
return res
def get_shop(self, shop_id=0):
"""
查询门店的WiFi信息
http://mp.weixin.qq.com/wiki/15/bcfb5d4578ea818b89913472cf2bbf8f.html
:param shop_id: 门店 ID
:return: 返回的 JSON 数据包
"""
res = self._post(
'shop/get',
data={
'shop_id': shop_id,
},
result_processor=lambda x: x['data']
)
return res
def add_device(self, shop_id, ssid, password, bssid):
"""
添加设备
详情请参考
http://mp.weixin.qq.com/wiki/10/6232005bdc497f7cf8e19d4e843c70d2.html
:param shop_id: 门店 ID
:param ssid: 无线网络设备的ssid。非认证公众号添加的ssid必需是“WX”开头(“WX”为大写字母)
认证公众号和第三方平台无此限制所有ssid均不能包含中文字符
:param password: 无线网络设备的密码大于8个字符不能包含中文字符
:param bssid: 无线网络设备无线mac地址格式冒号分隔字符长度17个并且字母小写
:return: 返回的 JSON 数据包
"""
return self._post(
'device/add',
data={
'shop_id': shop_id,
'ssid': ssid,
'password': password,
'bssid': bssid,
}
)
def list_devices(self, shop_id=None, page_index=1, page_size=20):
"""
查询设备
详情请参考
http://mp.weixin.qq.com/wiki/10/6232005bdc497f7cf8e19d4e843c70d2.html
:param shop_id: 可选,门店 ID
:param page_index: 可选分页下标默认从1开始
:param page_size: 可选每页的个数默认20个最大20个
:return: 返回的 JSON 数据包
"""
data = optionaldict(
shop_id=shop_id,
pageindex=page_index,
pagesize=page_size
)
res = self._post(
'device/list',
data=data,
result_processor=lambda x: x['data']
)
return res
def delete_device(self, bssid):
"""
删除设备
详情请参考
http://mp.weixin.qq.com/wiki/10/6232005bdc497f7cf8e19d4e843c70d2.html
:param bssid: 无线网络设备无线mac地址格式冒号分隔字符长度17个并且字母小写
:return: 返回的 JSON 数据包
"""
return self._post('device/delete', data={'bssid': bssid})
def get_qrcode_url(self, shop_id, img_id):
"""
获取物料二维码图片网址
详情请参考
http://mp.weixin.qq.com/wiki/7/fcd0378ef00617fc276be2b3baa80973.html
:param shop_id: 门店 ID
:param img_id: 物料样式编号0-二维码,可用于自由设计宣传材料;
1-桌贴二维码100mm×100mm(宽×高),可直接张贴
:return: 二维码图片网址
"""
res = self._post(
'qrcode/get',
data={
'shop_id': shop_id,
'img_id': img_id,
},
result_processor=lambda x: x['data']['qrcode_url']
)
return res
def set_homepage(self, shop_id, template_id, url=None):
"""
设置商家主页
详情请参考
http://mp.weixin.qq.com/wiki/6/2732f3cf83947e0e4971aa8797ee9d6a.html
:param shop_id: 门店 ID
:param template_id: 模板ID0-默认模板1-自定义url
:param url: 自定义链接当template_id为1时必填
:return: 返回的 JSON 数据包
"""
data = {
'shop_id': shop_id,
'template_id': template_id,
}
if url:
data['struct'] = {'url': url}
return self._post('homepage/set', data=data)
def get_homepage(self, shop_id):
"""
查询商家主页
详情请参考
http://mp.weixin.qq.com/wiki/6/2732f3cf83947e0e4971aa8797ee9d6a.html
:param shop_id: 门店 ID
:return: 返回的 JSON 数据包
"""
res = self._post(
'homepage/get',
data={'shop_id': shop_id},
result_processor=lambda x: x['data']
)
return res
def list_statistics(self, begin_date, end_date, shop_id=-1):
"""
Wi-Fi数据统计
详情请参考
http://mp.weixin.qq.com/wiki/8/dfa2b756b66fca5d9b1211bc18812698.html
:param begin_date: 起始日期时间最长时间跨度为30天
:param end_date: 结束日期时间戳最长时间跨度为30天
:param shop_id: 可选,门店 ID按门店ID搜索-1为总统计
:return: 返回的 JSON 数据包
"""
if isinstance(begin_date, (datetime, date)):
begin_date = begin_date.strftime('%Y-%m-%d')
if isinstance(end_date, (datetime, date)):
end_date = end_date.strftime('%Y-%m-%d')
res = self._post(
'statistics/list',
data={
'begin_date': begin_date,
'end_date': end_date,
'shop_id': shop_id
},
result_processor=lambda x: x['data']
)
return res