utils.py 4.2 KB

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