Skip to content

Commit c2d5b2c

Browse files
committed
update media support
1 parent 461128e commit c2d5b2c

File tree

3 files changed

+73
-52
lines changed

3 files changed

+73
-52
lines changed

README.md

+33-20
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1-
weixinpy
2-
===================
3-
这是一个python版本的腾讯微信公共平台的API的sdk。
1+
#weixinpy
2+
----
3+
这是一个简单的python版本的腾讯微信公共平台的sdk。<br>
44

5-
__version__ = '0.4.9'
6-
1.修复bug.
7-
2.默认采用FileCache方式保存access_token
8-
3.增加对“微信智能接口”、“微信摇一摇周边”、“网页授权”、“数据统计”、“微信小店”的支持。
5+
##更新日志
6+
----
97

10-
----------
8+
###当前版本 version 0.5.0
9+
----
10+
1、HTTP GET方法关键字由'_get'改为'dget'(原来的'_get'仍然可用)。<br>
11+
2、修改完善媒体文件的上传(支持"mpeg4/jpeg/jpg/png/gif/bmp/mp3/wma/wav/amr"), 具体看usage。<br>
1112

12-
Usage
13-
-------------
13+
###version 0.4.9
14+
----
15+
1、修复bug。<br>
16+
2、默认采用FileCache方式保存access_token。<br>
17+
3、增加对“微信智能接口”、“微信摇一摇周边”、“网页授权”、“数据统计”、“微信小店”的支持。<br>
18+
19+
##Usage
20+
----
1421
初始化过程:
1522
```
1623
from weixin import WeixinClient, APIError, AccessTokenError
@@ -22,22 +29,22 @@ wc.request_access_token()
2229
```
2330

2431
请求用户列表:
25-
API格式https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
32+
API URLhttps://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
2633
```
2734
# 我们这里是第一次调用所以next_openid=None
28-
rjson = wc.user.get._get(next_openid=None)
35+
rjson = wc.user.get.dget(next_openid=None)
2936
count = rjson.count
3037
id_list = rjson.data.openid
3138
while count < rjson.total:
32-
rjson = wc.user.get._get(next_openid=rjson.next_openid)
39+
rjson = wc.user.get.dget(next_openid=rjson.next_openid)
3340
count += rjson.count
3441
id_list.extend(rjson.openid)
3542
# 最后看看都有哪些用户
3643
print id_list
3744
```
3845

3946
发送文字信息:
40-
API格式https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
47+
API URLhttps://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN
4148
```
4249
for uid in id_list:
4350
content = '{"touser":"%s", "msgtype":"text", "text":{ "content":"hello!"}}' %uid
@@ -49,22 +56,19 @@ for uid in id_list:
4956
```
5057

5158
上传并发送媒体:
52-
API格式http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
59+
API URLhttp://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
5360
```
54-
rjson = wc.media.upload.file(type='image', pic=open('/home/pi/ocr_pi.png', 'rb'))
61+
rjson = wc.media.upload.file(type='image', png=open('/home/pi/ocr_pi.png', 'rb'))
5562
print rjson
5663
# 把上传的图片发出去
5764
for uid in id_list:
5865
content = '{"touser":"%s", "msgtype":"image", ' \
5966
'"image":{ "media_id":"%s"}}' % (uid, rjson.media_id)
60-
try:
6167
print wc.message.custom.send.post(body=content)
62-
except APIError, e:
63-
print e, uid
6468
```
6569

6670
下载媒体文件,如何判断token失效:
67-
API格式http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
71+
API URLhttp://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
6872
```
6973
# 这里演示了如何捕获token失效的异常,产生这个异常就要更新token了
7074
try:
@@ -88,5 +92,14 @@ q = '{"query":"查一下鹰潭的天气", "city":"鹰潭", "category":"weather",
8892
print wc.semantic.semproxy.search.post(body=q)
8993
```
9094

95+
媒体文件上传
96+
```
97+
print wc.media.upload.file(type='video', mpeg4=open('./a.mp4', 'rb'))
98+
print wc.media.upload.file(type='image', jpeg=open('./a.jpg', 'rb'))
99+
print wc.media.upload.file(type='image', gif=open('./a.gif', 'rb'))
100+
print wc.media.upload.file(type='voice', amr=open('./a.amr', 'rb'))
101+
print wc.media.upload.file(type='voice', wma=open('./a.wma', 'rb'))
102+
```
103+
91104
Have Fun!
92105

test/test.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
#"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN"
1616
print wc.message.custom.send.post(body=data)
1717
#"https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN"
18-
print wc.user.info._get(openid='obMnLt43lgfeeC8Ljn4-cLixEW6Q', lang='zh_CN')
18+
print wc.user.info.dget(openid='obMnLt43lgfeeC8Ljn4-cLixEW6Q', lang='zh_CN')
1919
#"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN"
2020
print wc.message.custom.send.post(body=data)
2121
#"https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID"
22-
print wc.user.get._get(next_openid=None)
22+
print wc.user.get.dget(next_openid=None)
2323
#"http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE"
24-
print wc.media.upload.file(type='image', pic = open('./test.jpg', 'rb'))
24+
print wc.media.upload.file(type='image', jpeg = open('./test.jpg', 'rb'))
2525
#"https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN"
2626
print wc.groups.create.post(body='{"group":{"name":"test_group_01"}}')
2727
#"https://api.weixin.qq.com/cgi-bin/groups/update?access_token=ACCESS_TOKEN"
@@ -31,10 +31,10 @@
3131
#"https://api.weixin.qq.com/cgi-bin/groups/getid?access_token=ACCESS_TOKEN"
3232
print wc.groups.getid.post(body='{"openid":"obMnLt9Qx5ZfPdElO3DQblM7ksl0"}')
3333
#"https://api.weixin.qq.com/cgi-bin/groups/get?access_token=ACCESS_TOKEN"
34-
print wc.groups.get._get()
34+
print wc.groups.get.dget()
3535
#"https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN"
3636
print wc.menu.create.post(body=key)
3737
#"https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN"
38-
print wc.menu.get._get()
38+
print wc.menu.get.dget()
3939
#"http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"
4040
print wc.media.get.file(media_id='OaPSe4DP-HF4s_ABWHEVDgMKOPCUoViID8x-yPUvwCfqTEA0whZOza4hGODiHs93')

weixin.py

+35-27
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import urllib
77
import urllib2
88

9-
__version__ = '0.4.9'
9+
__version__ = '0.5.0'
1010
__author__ = 'Liang Cha (ckmx945@gmail.com)'
1111

1212

@@ -25,6 +25,8 @@
2525

2626
_CONTENT_TYPE_MEDIA = ('image/jpeg', 'audio/amr', 'video/mpeg4')
2727

28+
_MEIDA_TYPE = ('mpeg4', 'jpeg', 'jpg', 'gif', 'png', 'bmp', 'mp3', 'wav', 'wma', 'amr')
29+
2830
_CONTENT_TYPE_JSON= (
2931
'application/json; encoding=utf-8',
3032
'application/json',
@@ -49,6 +51,7 @@ class APIError(StandardError):
4951
'''
5052
raise APIError if reciving json message indicating failure.
5153
'''
54+
5255
def __init__(self, error_code, error_msg):
5356
self.error_code = error_code
5457
self.error_msg = error_msg
@@ -62,6 +65,7 @@ class AccessTokenError(APIError):
6265
'''
6366
raise AccessTokenError if reciving json message indicating failure.
6467
'''
68+
6569
def __init__(self, error_code, error_msg):
6670
APIError.__init__(self, error_code, error_msg)
6771

@@ -73,6 +77,7 @@ class JsonDict(dict):
7377
'''
7478
general json object that allows attributes to bound to and also behaves like a dict
7579
'''
80+
7681
def __getattr__(self, attr):
7782
try:
7883
return self[attr]
@@ -84,7 +89,7 @@ def __setattr__(self, attr, value):
8489

8590

8691
def _parse_json(s):
87-
' parse str into JsonDict '
92+
''' parse str into JsonDict '''
8893

8994
def _obj_hook(pairs):
9095
o = JsonDict()
@@ -108,13 +113,13 @@ def _encode_params(**kw):
108113
if k == 'body':
109114
body = v
110115
continue
111-
if k in ['pic']:
116+
if k in _MEIDA_TYPE:
112117
continue
113118
if isinstance(v, basestring):
114119
qv = v.encode('utf-8') if isinstance(v, unicode) else v
115120
args.append('%s=%s' %(k, urllib.quote(qv)))
116121
else:
117-
if v == None:
122+
if v is None:
118123
args.append('%s=' %(k))
119124
else:
120125
qv = str(v)
@@ -126,25 +131,23 @@ def _encode_multipart(**kw):
126131
'''
127132
build a multipart/form-data body with randomly generated boundary
128133
'''
129-
boundary = '----------%s' % hex(int(time.time()) * 1000)
130134
data = []
131-
for k, v in kw.iteritems():
132-
if hasattr(v, 'read'):
133-
data.append('--%s' % boundary)
134-
filename = getattr(v, 'name', '')
135-
if filename == None or len(filename) == 0:
136-
filename = '/tmp/test.jpg'
137-
content = v.read()
138-
data.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (k, filename))
139-
data.append('Content-Length: %d' % len(content))
140-
#data.append('Content-Type: application/octet-stream')
141-
data.append('Content-Type: image/jpeg')
142-
data.append('Content-Transfer-Encoding: binary\r\n')
143-
data.append(content)
144-
if hasattr(v, 'close'):
145-
v.close()
146-
break
135+
boundary = '----------%s' % hex(int(time.time()) * 1000)
136+
media_key = [key for key in _MEIDA_TYPE if key in kw.keys()]
137+
media_key = media_key[0] if media_key else 'null'
138+
fd = kw.get(media_key)
139+
media_type = kw.get('type') if kw.has_key('type') else 'null'
140+
content = fd.read() if hasattr(fd, 'read') else 'null'
141+
filename = getattr(fd, 'name', None)
142+
if filename is None: filename = '/tmp/fake.%s' %(media_key)
143+
data.append('--%s' % boundary)
144+
data.append('Content-Disposition: form-data; name="%s"; filename="%s"' %(media_key, filename))
145+
data.append('Content-Length: %d' % len(content))
146+
data.append('Content-Type: %s/%s' %(media_type, media_key))
147+
data.append('Content-Transfer-Encoding: binary\r\n')
148+
data.append(content)
147149
data.append('--%s--\r\n' % boundary)
150+
if hasattr(fd, 'close'): fd.close()
148151
return '\r\n'.join(data), boundary
149152

150153

@@ -196,9 +199,9 @@ def _http_call(the_url, method, token, **kw):
196199
'''
197200
send an http request and return a json object if no error occurred.
198201
'''
202+
body = None
199203
params = None
200204
boundary = None
201-
body = None
202205
(params, body) = _encode_params(**kw)
203206
if method == _HTTP_FILE:
204207
the_url = the_url.replace('https://api.', 'http://file.api.')
@@ -210,8 +213,7 @@ def _http_call(the_url, method, token, **kw):
210213
http_url = '%s&%s' %(the_url, params) if (method == _HTTP_GET or method == _HTTP_FILE) else the_url
211214
http_body = str(body) if (method == _HTTP_POST) else body
212215
req = urllib2.Request(http_url, data = http_body)
213-
if boundary != None:
214-
req.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boundary)
216+
if boundary: req.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boundary)
215217
try:
216218
resp = urllib2.urlopen(req, timeout = 8)
217219
except urllib2.HTTPError, e:
@@ -319,9 +321,15 @@ def refurbish_access_token(self):
319321
self.del_access_token()
320322
self.request_access_token()
321323

322-
def set_access_token(self, token, expires):
324+
def set_access_token(self, token, expires_in, persistence=False):
323325
self.access_token = token
324-
self.expires = expires
326+
self.expires = expires_in + int(time.time())
327+
if persistence:
328+
token_key = 'access_token_%s' %(self.app_id)
329+
expires_key = 'expires_%s' %(self.app_id)
330+
self.mc.set(token_key, token, time = expires_in)
331+
self.mc.set(expires_key, self.expires, time = expires_in)
332+
if self.fc: self.mc.save()
325333

326334
def is_expires(self):
327335
return not self.access_token or int(time.time()) >= (self.expires - 10)
@@ -360,7 +368,7 @@ def __init__(self, client, name):
360368
self._name = name
361369

362370
def __getattr__(self, attr):
363-
if attr == '_get':
371+
if attr in ('dget', '_get'):
364372
return _Executable(self._client, _HTTP_GET, self._name)
365373
if attr == 'post':
366374
return _Executable(self._client, _HTTP_POST, self._name)

0 commit comments

Comments
 (0)