1
|
|
|
# -*- coding: utf-8 -*- |
2
|
10 |
|
""" |
3
|
|
|
wechatpy.utils |
4
|
|
|
~~~~~~~~~~~~~~~ |
5
|
|
|
|
6
|
|
|
This module provides some useful utilities. |
7
|
|
|
|
8
|
|
|
:copyright: (c) 2014 by messense. |
9
|
|
|
:license: MIT, see LICENSE for more details. |
10
|
|
|
""" |
11
|
10 |
|
from __future__ import absolute_import, unicode_literals |
12
|
10 |
|
import string |
13
|
10 |
|
import random |
14
|
10 |
|
import hashlib |
15
|
|
|
|
16
|
10 |
|
try: |
17
|
|
|
'''Use simplejson if we can, fallback to json otherwise.''' |
18
|
10 |
|
import simplejson as json |
19
|
10 |
|
except ImportError: |
20
|
10 |
|
import json # NOQA |
21
|
|
|
|
22
|
10 |
|
import six |
23
|
10 |
|
import six.moves.urllib.parse as urlparse |
24
|
|
|
|
25
|
|
|
|
26
|
10 |
|
class ObjectDict(dict): |
27
|
|
|
"""Makes a dictionary behave like an object, with attribute-style access. |
28
|
|
|
""" |
29
|
|
|
|
30
|
10 |
|
def __getattr__(self, key): |
31
|
10 |
|
if key in self: |
32
|
10 |
|
return self[key] |
33
|
10 |
|
return None |
34
|
|
|
|
35
|
10 |
|
def __setattr__(self, key, value): |
36
|
10 |
|
self[key] = value |
37
|
|
|
|
38
|
|
|
|
39
|
10 |
|
class WeChatSigner(object): |
40
|
|
|
"""WeChat data signer""" |
41
|
|
|
|
42
|
10 |
|
def __init__(self, delimiter=b''): |
43
|
10 |
|
self._data = [] |
44
|
10 |
|
self._delimiter = to_binary(delimiter) |
45
|
|
|
|
46
|
10 |
|
def add_data(self, *args): |
47
|
|
|
"""Add data to signer""" |
48
|
10 |
|
for data in args: |
49
|
10 |
|
self._data.append(to_binary(data)) |
50
|
|
|
|
51
|
10 |
|
@property |
52
|
|
|
def signature(self): |
53
|
|
|
"""Get data signature""" |
54
|
10 |
|
self._data.sort() |
55
|
10 |
|
str_to_sign = self._delimiter.join(self._data) |
56
|
10 |
|
return hashlib.sha1(str_to_sign).hexdigest() |
57
|
|
|
|
58
|
|
|
|
59
|
10 |
|
def check_signature(token, signature, timestamp, nonce): |
60
|
|
|
"""Check WeChat callback signature, raises InvalidSignatureException |
61
|
|
|
if check failed. |
62
|
|
|
|
63
|
|
|
:param token: WeChat callback token |
64
|
|
|
:param signature: WeChat callback signature sent by WeChat server |
65
|
|
|
:param timestamp: WeChat callback timestamp sent by WeChat server |
66
|
|
|
:param nonce: WeChat callback nonce sent by WeChat sever |
67
|
|
|
""" |
68
|
10 |
|
signer = WeChatSigner() |
69
|
10 |
|
signer.add_data(token, timestamp, nonce) |
70
|
10 |
|
if signer.signature != signature: |
71
|
10 |
|
from wechatpy.exceptions import InvalidSignatureException |
72
|
|
|
|
73
|
10 |
|
raise InvalidSignatureException() |
74
|
|
|
|
75
|
|
|
|
76
|
10 |
|
def to_text(value, encoding='utf-8'): |
77
|
|
|
"""Convert value to unicode, default encoding is utf-8 |
78
|
|
|
|
79
|
|
|
:param value: Value to be converted |
80
|
|
|
:param encoding: Desired encoding |
81
|
|
|
""" |
82
|
10 |
|
if not value: |
83
|
10 |
|
return '' |
84
|
10 |
|
if isinstance(value, six.text_type): |
85
|
10 |
|
return value |
86
|
10 |
|
if isinstance(value, six.binary_type): |
87
|
10 |
|
return value.decode(encoding) |
88
|
10 |
|
return six.text_type(value) |
89
|
|
|
|
90
|
|
|
|
91
|
10 |
|
def to_binary(value, encoding='utf-8'): |
92
|
|
|
"""Convert value to binary string, default encoding is utf-8 |
93
|
|
|
|
94
|
|
|
:param value: Value to be converted |
95
|
|
|
:param encoding: Desired encoding |
96
|
|
|
""" |
97
|
10 |
|
if not value: |
98
|
10 |
|
return b'' |
99
|
10 |
|
if isinstance(value, six.binary_type): |
100
|
10 |
|
return value |
101
|
10 |
|
if isinstance(value, six.text_type): |
102
|
10 |
|
return value.encode(encoding) |
103
|
|
|
return six.binary_type(value) |
104
|
|
|
|
105
|
|
|
|
106
|
10 |
|
def timezone(zone): |
107
|
|
|
"""Try to get timezone using pytz or python-dateutil |
108
|
|
|
|
109
|
|
|
:param zone: timezone str |
110
|
|
|
:return: timezone tzinfo or None |
111
|
|
|
""" |
112
|
10 |
|
try: |
113
|
10 |
|
import pytz |
114
|
|
|
return pytz.timezone(zone) |
115
|
10 |
|
except ImportError: |
116
|
10 |
|
pass |
117
|
10 |
|
try: |
118
|
10 |
|
from dateutil.tz import gettz |
119
|
10 |
|
return gettz(zone) |
120
|
|
|
except ImportError: |
121
|
|
|
return None |
122
|
|
|
|
123
|
|
|
|
124
|
10 |
|
def random_string(length=16): |
125
|
10 |
|
rule = string.ascii_letters + string.digits |
126
|
10 |
|
rand_list = random.sample(rule, length) |
127
|
10 |
|
return ''.join(rand_list) |
128
|
|
|
|
129
|
|
|
|
130
|
10 |
|
def get_querystring(uri): |
131
|
|
|
"""Get Querystring information from uri. |
132
|
|
|
|
133
|
|
|
:param uri: uri |
134
|
|
|
:return: querystring info or {} |
135
|
|
|
""" |
136
|
10 |
|
parts = urlparse.urlsplit(uri) |
137
|
10 |
|
return urlparse.parse_qs(parts.query) |
138
|
|
|
|
139
|
|
|
|
140
|
10 |
|
def byte2int(c): |
141
|
10 |
|
if six.PY2: |
142
|
4 |
|
return ord(c) |
143
|
|
|
return c |
144
|
|
|
|