diff --git a/jikimo_sale_order_message_notify/__init__.py b/jikimo_sale_order_message_notify/__init__.py
new file mode 100644
index 00000000..9a7e03ed
--- /dev/null
+++ b/jikimo_sale_order_message_notify/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/jikimo_sale_order_message_notify/__manifest__.py b/jikimo_sale_order_message_notify/__manifest__.py
new file mode 100644
index 00000000..45570c08
--- /dev/null
+++ b/jikimo_sale_order_message_notify/__manifest__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+{
+ 'name': '机企猫智能工厂 消息提醒',
+ 'version': '1.0',
+ 'summary': '智能工厂消息提醒模块',
+ 'sequence': 1,
+ 'description': """
+
+ """,
+ 'category': 'sf',
+ 'website': 'https://www.sf.jikimo.com',
+ 'depends': ['jikimo_message_notify'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'data/bussiness_node.xml',
+ # 'views/sf_message_template_view.xml',
+ ],
+ 'test': [
+ ],
+ 'license': 'LGPL-3',
+ 'installable': True,
+ 'auto_install': False,
+ 'application': False,
+}
diff --git a/jikimo_sale_order_message_notify/data/bussiness_node.xml b/jikimo_sale_order_message_notify/data/bussiness_node.xml
new file mode 100644
index 00000000..e6800a48
--- /dev/null
+++ b/jikimo_sale_order_message_notify/data/bussiness_node.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ 订单确认
+ sale.order
+
+
+
\ No newline at end of file
diff --git a/jikimo_sale_order_message_notify/models/__init__.py b/jikimo_sale_order_message_notify/models/__init__.py
new file mode 100644
index 00000000..c0462bbd
--- /dev/null
+++ b/jikimo_sale_order_message_notify/models/__init__.py
@@ -0,0 +1,2 @@
+from . import jikimo_message_template
+from . import sale_order
diff --git a/jikimo_sale_order_message_notify/models/jikimo_message_template.py b/jikimo_sale_order_message_notify/models/jikimo_message_template.py
new file mode 100644
index 00000000..4c2530f5
--- /dev/null
+++ b/jikimo_sale_order_message_notify/models/jikimo_message_template.py
@@ -0,0 +1,10 @@
+from odoo import models, fields, api
+
+class JikimoMessageTemplate(models.Model):
+ _inherit = "jikimo.message.template"
+
+ def _get_message_model(self):
+ res = super(JikimoMessageTemplate, self)._get_message_model()
+ res.append("sale.order")
+ return res
+
diff --git a/jikimo_sale_order_message_notify/models/sale_order.py b/jikimo_sale_order_message_notify/models/sale_order.py
new file mode 100644
index 00000000..ad0c3209
--- /dev/null
+++ b/jikimo_sale_order_message_notify/models/sale_order.py
@@ -0,0 +1,11 @@
+from odoo import models, fields, api
+
+
+class SaleOrder(models.Model):
+ _name = "sale.order"
+ _inherit = ["sale.order", "jikimo.message.dispatch"]
+
+ def create(self, vals_list):
+ res = super(SaleOrder, self).create(vals_list)
+ res.add_queue('订单确认')
+ return res
diff --git a/jikimo_sale_order_message_notify/security/group_security.xml b/jikimo_sale_order_message_notify/security/group_security.xml
new file mode 100644
index 00000000..fdbc3ae5
--- /dev/null
+++ b/jikimo_sale_order_message_notify/security/group_security.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/jikimo_sale_order_message_notify/security/ir.model.access.csv b/jikimo_sale_order_message_notify/security/ir.model.access.csv
new file mode 100644
index 00000000..0b7f9c7b
--- /dev/null
+++ b/jikimo_sale_order_message_notify/security/ir.model.access.csv
@@ -0,0 +1,6 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+
+
+
+
+
diff --git a/jikimo_sale_order_message_notify/views/sf_message_template_view.xml b/jikimo_sale_order_message_notify/views/sf_message_template_view.xml
new file mode 100644
index 00000000..21920b64
--- /dev/null
+++ b/jikimo_sale_order_message_notify/views/sf_message_template_view.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+ sf.message.template.view.form
+ message.template
+
+
+
+
+
+
+ sf.message.template.view.tree
+ message.template
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sf.message.template.search.view
+ message.template
+
+
+
+
+
+
+
+
+
+
+
+ 消息模板
+ message.template
+ tree,form
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sg_wechat_enterprise/.gitignore b/sg_wechat_enterprise/.gitignore
deleted file mode 100644
index a81c8ee1..00000000
--- a/sg_wechat_enterprise/.gitignore
+++ /dev/null
@@ -1,138 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
diff --git a/sg_wechat_enterprise/__init__.py b/sg_wechat_enterprise/__init__.py
deleted file mode 100644
index 4589499c..00000000
--- a/sg_wechat_enterprise/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from . import we_api
-from . import models
-from . import controllers
diff --git a/sg_wechat_enterprise/__manifest__.py b/sg_wechat_enterprise/__manifest__.py
deleted file mode 100644
index 883747fd..00000000
--- a/sg_wechat_enterprise/__manifest__.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-{
- 'name': '企业微信',
- 'version': '0.1',
- 'summary': '企业通讯录\消息处理\企业应用\无缝登录',
- 'sequence': 30,
- "author": 'SmartGo Studio.,',
- 'description': '''用于企业内部员工的管理,
-ER企业微信模块
-=====================================================
-主要针对odoo使用微信进行管理,包括以下功能:
-1) 公众号信息管理(企业号下多applicaiton管理)
-2) 接收消息处理
-3) 发送消息处理
-4) 自定义菜单处理
-
-....
-本安装包使用了WechatEnterpriseSDK/wechat_sdk.py,在此表示感谢。
-源代码可以访问github.地址如下:https://github.com/facert/WechatEnterpriseSDK
-
- ''',
- 'category': '基础信息',
- 'website': 'https://www.smartgo.cn',
- 'depends': ['base', 'mail','hr'],
- 'data': [
- 'security/ir.model.access.csv',
- 'views/we_config_view.xml',
- 'views/we_app_view.xml',
- 'views/we_send_message_view.xml',
- 'views/we_receive_message_view.xml',
- 'views/we_message_process_view.xml',
- 'views/we_templates.xml',
- # "views/mail_view.xml",
- "views/res_users_view.xml",
- 'views/menu_view.xml',
- # 'views/we_menu.xml',
- "data/data.xml"
- ],
- 'demo': [
- 'demo/we_config_demo.xml',
- ],
- 'qweb': [
- # "static/src/xml/base.xml",
- # "static/src/xml/account_payment.xml",
- # "static/src/xml/account_report_backend.xml",
- ],
- 'installable': True,
- 'application': True,
- 'auto_install': False,
- # 'post_init_hook': '_auto_install_l10n',
-}
diff --git a/sg_wechat_enterprise/controllers/WXBizMsgCrypt.py b/sg_wechat_enterprise/controllers/WXBizMsgCrypt.py
deleted file mode 100644
index 820a9c96..00000000
--- a/sg_wechat_enterprise/controllers/WXBizMsgCrypt.py
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/usr/bin/env python
-# -*- encoding:utf-8 -*-
-
-""" 对企业微信发送给企业后台的消息加解密示例代码.
-@copyright: Copyright (c) 1998-2014 Tencent Inc.
-
-"""
-# ------------------------------------------------------------------------
-
-import base64
-import string
-import random
-import hashlib
-import time
-import struct
-from Crypto.Cipher import AES
-import xml.etree.cElementTree as ET
-import socket
-from . import ierror
-
-"""
-关于Crypto.Cipher模块,ImportError: No module named 'Crypto'解决方案
-请到官方网站 https://www.dlitz.net/software/pycrypto/ 下载pycrypto。
-下载后,按照README中的“Installation”小节的提示进行pycrypto安装。
-"""
-
-
-class FormatException(Exception):
- pass
-
-
-def throw_exception(message, exception_class=FormatException):
- """my define raise exception function"""
- raise exception_class(message)
-
-
-class SHA1:
- """计算企业微信的消息签名接口"""
-
- def getSHA1(self, token, timestamp, nonce, encrypt):
- """用SHA1算法生成安全签名
- @param token: 票据
- @param timestamp: 时间戳
- @param encrypt: 密文
- @param nonce: 随机字符串
- @return: 安全签名
- """
- try:
- sortlist = [token, timestamp, nonce, encrypt]
- sortlist.sort()
- sha = hashlib.sha1()
- sort_str = "".join(sortlist)
- sha.update(sort_str.encode('utf-8'))
- return ierror.WXBizMsgCrypt_OK, sha.hexdigest()
- except Exception as e:
- return ierror.WXBizMsgCrypt_ComputeSignature_Error, None
-
-
-class XMLParse:
- """提供提取消息格式中的密文及生成回复消息格式的接口"""
-
- # xml消息模板
- AES_TEXT_RESPONSE_TEMPLATE = """
-
-
-%(timestamp)s
-
-"""
-
- def extract(self, xmltext):
- """提取出xml数据包中的加密消息
- @param xmltext: 待提取的xml字符串
- @return: 提取出的加密消息字符串
- """
- try:
- xml_tree = ET.fromstring(xmltext)
- encrypt = xml_tree.find("Encrypt")
- touser_name = xml_tree.find("ToUserName")
- return ierror.WXBizMsgCrypt_OK, encrypt.text, touser_name.text
- except Exception as e:
- return ierror.WXBizMsgCrypt_ParseXml_Error, None, None
-
- def generate(self, encrypt, signature, timestamp, nonce):
- """生成xml消息
- @param encrypt: 加密后的消息密文
- @param signature: 安全签名
- @param timestamp: 时间戳
- @param nonce: 随机字符串
- @return: 生成的xml字符串
- """
- resp_dict = {
- 'msg_encrypt': encrypt,
- 'msg_signaturet': signature,
- 'timestamp': timestamp,
- 'nonce': nonce,
- }
- resp_xml = self.AES_TEXT_RESPONSE_TEMPLATE % resp_dict
- return resp_xml
-
-
-class PKCS7Encoder():
- """提供基于PKCS7算法的加解密接口"""
-
- block_size = 32
-
- def encode(self, text):
- """ 对需要加密的明文进行填充补位
- @param text: 需要进行填充补位操作的明文
- @return: 补齐明文字符串
- """
- text_length = len(text)
- # 计算需要填充的位数
- amount_to_pad = self.block_size - (text_length % self.block_size)
- if amount_to_pad == 0:
- amount_to_pad = self.block_size
- # 获得补位所用的字符
- pad = chr(amount_to_pad)
- return text + pad * amount_to_pad
-
- def decode(self, decrypted):
- """删除解密后明文的补位字符
- @param decrypted: 解密后的明文
- @return: 删除补位字符后的明文
- """
- pad = ord(decrypted[-1])
- if pad < 1 or pad > 32:
- pad = 0
- return decrypted[:-pad]
-
-
-class Prpcrypt(object):
- """提供接收和推送给企业微信消息的加解密接口"""
-
- def __init__(self, key):
-
- # self.key = base64.b64decode(key+"=")
- self.key = key
- # 设置加解密模式为AES的CBC模式
- self.mode = AES.MODE_CBC
-
- def encrypt(self, text, corpid):
- """对明文进行加密
- @param text: 需要加密的明文
- @return: 加密得到的字符串
- """
- # 16位随机字符串添加到明文开头
- text = self.get_random_str() + str(struct.pack("I", socket.htonl(len(text))), encoding='utf8')\
- + str(text, encoding='utf8') + corpid
- # 使用自定义的填充方式对明文进行补位填充
- pkcs7 = PKCS7Encoder()
- text = pkcs7.encode(text)
- # 加密
- cryptor = AES.new(self.key, self.mode, self.key[:16])
- try:
- ciphertext = cryptor.encrypt(text)
- # 使用BASE64对加密后的字符串进行编码
- return ierror.WXBizMsgCrypt_OK, base64.b64encode(ciphertext)
- except Exception as e:
- return ierror.WXBizMsgCrypt_EncryptAES_Error, None
-
- def decrypt(self, text, corpid):
- """对解密后的明文进行补位删除
- @param text: 密文
- @return: 删除填充补位后的明文
- """
- try:
- cryptor = AES.new(self.key, self.mode, self.key[:16])
- # 使用BASE64对密文进行解码,然后AES-CBC解密
- plain_text = cryptor.decrypt(base64.b64decode(text))
- except Exception as e:
- return ierror.WXBizMsgCrypt_DecryptAES_Error, None
- try:
- pad = plain_text[-1]
- # 去掉补位字符串
- # pkcs7 = PKCS7Encoder()
- # plain_text = pkcs7.encode(plain_text)
- # 去除16位随机字符串
- content = plain_text[16:-pad]
- xml_len = socket.ntohl(struct.unpack("I", content[: 4])[0])
- xml_content = content[4: xml_len + 4]
- from_corpid = content[xml_len + 4:]
- except Exception as e:
- return ierror.WXBizMsgCrypt_IllegalBuffer, None
- if str(from_corpid, encoding="utf8") != corpid and len(ET.fromstring(xml_content).findall("SuiteId")) < 1:
- return ierror.WXBizMsgCrypt_ValidateCorpid_Error, None
- return 0, xml_content
-
- def get_random_str(self):
- """ 随机生成16位字符串
- @return: 16位字符串
- """
- rule = string.ascii_letters + string.digits
- str = random.sample(rule, 16)
- return "".join(str)
-
-
-class WXBizMsgCrypt(object):
- # 构造函数
- # @param sToken: 企业微信后台,开发者设置的Token
- # @param sEncodingAESKey: 企业微信后台,开发者设置的EncodingAESKey
- # @param sCorpId: 企业号的CorpId
- def __init__(self, sToken, sEncodingAESKey, sCorpId):
- try:
- self.key = base64.b64decode(sEncodingAESKey + "=")
- assert len(self.key) == 32
- except:
- throw_exception("[error]: EncodingAESKey unvalid !", FormatException)
- # return ierror.WXBizMsgCrypt_IllegalAesKey,None
- self.m_sToken = sToken
- self.m_sCorpid = sCorpId
-
- # 验证URL
- # @param sMsgSignature: 签名串,对应URL参数的msg_signature
- # @param sTimeStamp: 时间戳,对应URL参数的timestamp
- # @param sNonce: 随机串,对应URL参数的nonce
- # @param sEchoStr: 随机串,对应URL参数的echostr
- # @param sReplyEchoStr: 解密之后的echostr,当return返回0时有效
- # @return:成功0,失败返回对应的错误码
-
- def VerifyURL(self, sMsgSignature, sTimeStamp, sNonce, sEchoStr):
- sha1 = SHA1()
- ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, sEchoStr)
- if ret != 0:
- return ret, None
- if not signature == sMsgSignature:
- return ierror.WXBizMsgCrypt_ValidateSignature_Error, None
- pc = Prpcrypt(self.key)
- ret, sReplyEchoStr = pc.decrypt(sEchoStr, self.m_sCorpid)
- return ret, sReplyEchoStr
-
- def EncryptMsg(self, sReplyMsg, sNonce, timestamp=None):
- # 将企业回复用户的消息加密打包
- # @param sReplyMsg: 企业号待回复用户的消息,xml格式的字符串
- # @param sTimeStamp: 时间戳,可以自己生成,也可以用URL参数的timestamp,如为None则自动用当前时间
- # @param sNonce: 随机串,可以自己生成,也可以用URL参数的nonce
- # sEncryptMsg: 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
- # return:成功0,sEncryptMsg,失败返回对应的错误码None
- pc = Prpcrypt(self.key)
- ret, encrypt = pc.encrypt(sReplyMsg, self.m_sCorpid)
- if ret != 0:
- return ret, None
- if timestamp is None:
- timestamp = str(int(time.time()))
- # 生成安全签名
- sha1 = SHA1()
- ret, signature = sha1.getSHA1(self.m_sToken, timestamp, sNonce, encrypt)
- if ret != 0:
- return ret, None
- xmlParse = XMLParse()
- return ret, xmlParse.generate(encrypt, signature, timestamp, sNonce)
-
- def DecryptMsg(self, sPostData, sMsgSignature, sTimeStamp, sNonce):
- # 检验消息的真实性,并且获取解密后的明文
- # @param sMsgSignature: 签名串,对应URL参数的msg_signature
- # @param sTimeStamp: 时间戳,对应URL参数的timestamp
- # @param sNonce: 随机串,对应URL参数的nonce
- # @param sPostData: 密文,对应POST请求的数据
- # xml_content: 解密后的原文,当return返回0时有效
- # @return: 成功0,失败返回对应的错误码
- # 验证安全签名
- xmlParse = XMLParse()
- ret, encrypt, touser_name = xmlParse.extract(sPostData)
- if ret != 0:
- return ret, None
- sha1 = SHA1()
- ret, signature = sha1.getSHA1(self.m_sToken, sTimeStamp, sNonce, encrypt)
- if ret != 0:
- return ret, None
- if not signature == sMsgSignature:
- return ierror.WXBizMsgCrypt_ValidateSignature_Error, None
- pc = Prpcrypt(self.key)
- ret, xml_content = pc.decrypt(encrypt, self.m_sCorpid)
- return ret, xml_content
diff --git a/sg_wechat_enterprise/controllers/__init__.py b/sg_wechat_enterprise/controllers/__init__.py
deleted file mode 100644
index 0b1b2d96..00000000
--- a/sg_wechat_enterprise/controllers/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-
-from . import wechat_enterprise
-
diff --git a/sg_wechat_enterprise/controllers/ierror.py b/sg_wechat_enterprise/controllers/ierror.py
deleted file mode 100644
index 6678fecf..00000000
--- a/sg_wechat_enterprise/controllers/ierror.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#########################################################################
-# Author: jonyqin
-# Created Time: Thu 11 Sep 2014 01:53:58 PM CST
-# File Name: ierror.py
-# Description:定义错误码含义
-#########################################################################
-WXBizMsgCrypt_OK = 0
-WXBizMsgCrypt_ValidateSignature_Error = -40001
-WXBizMsgCrypt_ParseXml_Error = -40002
-WXBizMsgCrypt_ComputeSignature_Error = -40003
-WXBizMsgCrypt_IllegalAesKey = -40004
-WXBizMsgCrypt_ValidateCorpid_Error = -40005
-WXBizMsgCrypt_EncryptAES_Error = -40006
-WXBizMsgCrypt_DecryptAES_Error = -40007
-WXBizMsgCrypt_IllegalBuffer = -40008
-WXBizMsgCrypt_EncodeBase64_Error = -40009
-WXBizMsgCrypt_DecodeBase64_Error = -40010
-WXBizMsgCrypt_GenReturnXml_Error = -40011
diff --git a/sg_wechat_enterprise/controllers/wechat_enterprise.py b/sg_wechat_enterprise/controllers/wechat_enterprise.py
deleted file mode 100644
index 0c7f8738..00000000
--- a/sg_wechat_enterprise/controllers/wechat_enterprise.py
+++ /dev/null
@@ -1,231 +0,0 @@
-import time
-import functools
-import base64
-from json import *
-
-from odoo import http, fields
-from odoo.http import request
-from . import WXBizMsgCrypt
-from werkzeug.exceptions import abort
-import xml.etree.cElementTree as Et
-import requests as req
-
-import logging
-from lxml import etree
-
-
-_logger = logging.getLogger(__name__)
-
-
-def wechat_login(func):
- """
- 用来根据userid取得合作伙伴的id
- :return:
- """
-
- @functools.wraps(func)
- def wrapper(*args, **kw):
- # now_time = time.time()
- # request.session['session_time'] = time.time()
- # if 'session_time' not in request.session:
- # request.session['session_time'] = 1
- # if now_time > request.session['session_time'] + 350:
- _logger.info(u"没进来之前的kw值为%s" % JSONEncoder().encode(kw))
- # if not request.session['login'] or not request.session['login'] or request.session[
- # 'login'] == "public":
- enterprise, agent_id = request.env['we.config'].sudo().get_odoo_wechat()
- if enterprise and agent_id:
- if 'code' in kw and 'state' in kw: # 检查是否取得了code
- if 'kw' in request.session.keys():
- _logger.info('code:%s' % kw['code'])
- account = enterprise.oauth.get_user_info(code=kw['code'])
- _logger.info('account:%s' % account)
- if account: # 是否取得了微信企业号通讯录的账号
- user_detail = enterprise.user.get_detail(account['user_ticket'])
- _logger.info('user_detail:%s' % user_detail)
- request.env['we.employee'].we_privacy_update(user_detail)
- user = request.env['res.users'].sudo().search(
- [('we_employee_id', '=', account['UserId'])])
- _logger.info('user:%s' % user)
- if user: # 是否取得了用户
- if 'state' in kw:
- state = base64.b64encode(kw['state'].encode('utf-8')).decode()
- kw['state'] = state
- uid = request.session.authenticate(request.session.db, user.login,
- account['UserId'])
- kw['user_id'] = uid
- request.session['session_time'] = time.time()
- request.session['login'] = user.login
-
- _logger.info(u"进来之后的kw值为%s" % kw)
- return func(*args, **kw)
- else:
- _logger.warning(u'用户不存在.')
- return request.render('sg_wechat_enterprise.wechat_warning',
- {'title': u'警告', 'content': u'该员工的未配置登录用户.'})
- else:
- _logger.warning(u'微信企业号验证失败.')
- return request.render('sg_wechat_enterprise.wechat_warning',
- {'title': u'警告', 'content': u'微信企业号验证失败.'})
- else:
- # 返回时候进入的地方
- del kw['code']
- del kw['state']
- request.session['kw'] = base64.b64encode(JSONEncoder().encode(kw).encode('utf-8')).decode()
- if len(kw) == 0:
- base_url = request.httprequest.base_url
- else:
- base_url = request.httprequest.base_url + '?'
- for item, value in kw.items():
- base_url += item + '=' + value + "&"
- base_url = base_url[: -1]
-
- url = enterprise.oauth.authorize_url(base_url,
- state=base64.b64encode(
- JSONEncoder().encode(kw).encode('utf-8')).decode(),
- agent_id=agent_id,
- scope='snsapi_privateinfo')
- _logger.warning(u"这是授权的url:" + url)
- value = {"url": url}
- return request.render("sg_wechat_enterprise.Transfer", value)
- else: # 开始微信企业号登录认证
- request.session['kw'] = base64.b64encode(JSONEncoder().encode(kw).encode('utf-8')).decode()
-
- if len(kw) == 0:
- base_url = request.httprequest.base_url
- else:
- base_url = request.httprequest.base_url + '?'
- for item, value in kw.items():
- base_url += item + '=' + value + "&"
- base_url = base_url[: -1]
- url = enterprise.oauth.authorize_url(base_url,
- state=base64.b64encode(
- JSONEncoder().encode(kw).encode('utf-8')).decode(),
- agent_id=agent_id,
- scope='snsapi_privateinfo'
- )
- _logger.warning(u"这是授权的url:" + url)
- value = {"url": url}
- return request.render("sg_wechat_enterprise.Transfer", value)
- else:
- _logger.warning(u'微信企业号初始化失败.')
- return request.render('sg_wechat_enterprise.wechat_warning',
- {'title': u'警告', 'content': u'微信企业号初始化失败.'})
-
- # return func(*args, **kw)
-
- return wrapper
-
-
-class WechatEnterprise(http.Controller):
- """
- 用于接收微信发过来的任何消息,并转发给相应的业务类进行处理
- """
- __check_str = 'NDOEHNDSY#$_@$JFDK:Q{!'
- BASE_URL = '/we'
-
- @wechat_login
- @http.route(BASE_URL + '/auth', type='http', auth='none')
- def auth(self, *args, **kw):
- """
- 企业微信免登认证
- """
- try:
- # user_id = (request.session['uid'])
- redirect1 = kw['redirect'] if 'redirect' in kw else None
- uid = kw['user_id'] if 'redirect' in kw else None
- _logger.info('user_id %s', uid)
- if uid is not False:
- request.params['login_success'] = True
- if not redirect1:
- redirect1 = '/web'
- redirect1 = redirect1.replace('-', '&').replace('?', '#')
- logging.info('url:%s' % redirect1)
- return request.redirect(redirect1)
- except Exception as ex:
- _logger.error('无有效的登录凭证.')
- _logger.warning('auth exceptions:%s' % ex)
- return request.render('sg_wechat_enterprise.wechat_warning',
- {'title': u'警告', 'content': u'无有效的登录凭证.'})
-
- @http.route('/WechatEnterprise//api', type='http', auth="public", methods=["GET", "POST"], csrf=False)
- def process(self, code, **kwargs):
- """
- 处理从微信服务器发送过来的请求
- :param code: 自定义代码
- :param kwargs: 包含 (msg_signature, timestamp, nonce, echostr) 等参数
- :return:
- """
- _logger.info(u'处理从微信服务器发送过来的请求code: %s, kwargs: %s' % (code, kwargs))
- app_id = request.env['we.app'].sudo().search([('code', '=', code)], limit=1)
- if not app_id:
- _logger.warning(u'Can not find wechat app by code: {code}')
- abort(403)
- corp_id = app_id.enterprise_id
- we_chat_cpt = WXBizMsgCrypt.WXBizMsgCrypt(app_id.Token, app_id.EncodingAESKey, corp_id.corp_id)
- signature, timestamp, nonce = kwargs['msg_signature'], kwargs['timestamp'], kwargs['nonce']
- if kwargs.get('echostr'):
- echo_string = kwargs['echostr']
- sort_list = [app_id.Token, timestamp, nonce, echo_string]
- if request.env['we.tools'].sudo(). \
- check_message_signature(message_list=sort_list, msg_signature=signature):
- ret, signature_echo_string = we_chat_cpt.VerifyURL(signature, timestamp, nonce, echo_string)
- if ret == 0:
- return str(signature_echo_string, encoding="utf8")
- body_text = request.httprequest.data
- ret, signature_message = we_chat_cpt.DecryptMsg(body_text, signature, timestamp, nonce)
- xml_tree = Et.fromstring(signature_message)
- if len(xml_tree.findall("SuiteId")) > 0:
- return "success"
- if len(xml_tree.find("ApprovalInfo")) > 0:
- xmlstr = etree.fromstring(signature_message)
- # data = xml2json_from_elementtree(xmlstr)
- return request.env['we.receive.message'].sudo().sys_approval_change(xml_tree.find("ApprovalInfo"))
- data = {
- 'MsgType': xml_tree.find("MsgType").text,
- # 'AgentID': xml_tree.find("AgentID").text,
- 'ToUserName': xml_tree.find("ToUserName").text,
- 'FromUserName': xml_tree.find("FromUserName").text,
- 'CreateTime': xml_tree.find("CreateTime").text
- }
- if xml_tree.find("AgentID") != None:
- data['AgentID'] = xml_tree.find("AgentID").text
- if data["MsgType"] == "text":
- data["Content"] = xml_tree.find("Content").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "image":
- data["PicUrl"] = xml_tree.find("PicUrl").text
- data["MediaId"] = xml_tree.find("MediaId").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "voice":
- data["MediaId"] = xml_tree.find("MediaId").text
- data["Format"] = xml_tree.find("Format").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "video" or data["MsgType"] == "shortvideo":
- data["MediaId"] = xml_tree.find("MediaId").text
- data["ThumbMediaId"] = xml_tree.find("ThumbMediaId").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "location":
- data["Location_X"] = xml_tree.find("Location_X").text
- data["Location_Y"] = xml_tree.find("Location_Y").text
- data["Scale"] = xml_tree.find("Scale").text
- data["Label"] = xml_tree.find("Label").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "link":
- data["Title"] = xml_tree.find("Title").text
- data["Description"] = xml_tree.find("Description").text
- data["PicUrl"] = xml_tree.find("PicUrl").text
- data["MsgId"] = xml_tree.find("MsgId").text
- if data["MsgType"] == "event":
- if xml_tree.find("Event") == "subscribe" or xml_tree.find("Event") == "unsubscribe":
- data["Event"] = xml_tree.find("Event").text
- else:
- ret, signature_message = we_chat_cpt.EncryptMsg(signature_message, nonce, timestamp)
- return signature_message
- request.env['we.receive.message'].sudo().process_message(data)
- return ''
-
- @http.route('/WechatEnterprise/transfer', type='http', auth="public", methods=["POST", "GET"], csrf=False)
- def transfer(self, url):
- value = {"url": url}
- return request.render('sg_wechat_enterprise.Transfer', value)
diff --git a/sg_wechat_enterprise/data/data.xml b/sg_wechat_enterprise/data/data.xml
deleted file mode 100644
index bebcfa10..00000000
--- a/sg_wechat_enterprise/data/data.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- SNS Message To 企业微信
-
- code
- model.send_we_message()
- 1
- minutes
- -1
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sg_wechat_enterprise/demo/we_config_demo.xml b/sg_wechat_enterprise/demo/we_config_demo.xml
deleted file mode 100644
index 605bdbd9..00000000
--- a/sg_wechat_enterprise/demo/we_config_demo.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
- SmartGo
- wwad4f2c227d490637
- kq_AzJN1FoPdWjyEwAQs_cqzJhALmKhmwYMBQyJzuEs
- 1
-
-
-
-
-
diff --git a/sg_wechat_enterprise/models/__init__.py b/sg_wechat_enterprise/models/__init__.py
deleted file mode 100644
index b8fb8a02..00000000
--- a/sg_wechat_enterprise/models/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from . import we_app
-from . import we_conf
-from . import we_send_message
-from . import we_receive_message
-from . import we_receive_message_process
-from . import res_users
-from . import we_tools
-from . import mail
-from . import client
diff --git a/sg_wechat_enterprise/models/client/__init__.py b/sg_wechat_enterprise/models/client/__init__.py
deleted file mode 100644
index 72bca418..00000000
--- a/sg_wechat_enterprise/models/client/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from wechatpy.enterprise import WeChatClient
-
-from . import api
-
-
-class JkmWeChatClient(WeChatClient):
- def __init__(self, corp_id, secret, session=None):
- super(JkmWeChatClient, self).__init__(corp_id, secret, session=session)
-
- oauth = api.JkmWechatOauth()
- user = api.JkmWechatUser()
diff --git a/sg_wechat_enterprise/models/client/api/__init__.py b/sg_wechat_enterprise/models/client/api/__init__.py
deleted file mode 100644
index f50e1972..00000000
--- a/sg_wechat_enterprise/models/client/api/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from .jkm_user import JkmWechatUser
-from .jkm_oauth import JkmWechatOauth
diff --git a/sg_wechat_enterprise/models/client/api/jkm_oauth.py b/sg_wechat_enterprise/models/client/api/jkm_oauth.py
deleted file mode 100644
index 3d491389..00000000
--- a/sg_wechat_enterprise/models/client/api/jkm_oauth.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from __future__ import absolute_import, unicode_literals
-import six
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class JkmWechatOauth(BaseWeChatAPI):
- OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize'
-
- def authorize_url(self, redirect_uri, state=None, agent_id=None, scope='snsapi_base'):
- """
- 构造网页授权链接
- 详情请参考
- https://work.weixin.qq.com/api/doc#90000/90135/91022
-
- :param redirect_uri: 授权后重定向的回调链接地址
- :param state: 重定向后会带上 state 参数
- :param agent_id: 企业应用的id
- :param scope: 应用授权作用域
- :return: 返回的 JSON 数据包
- """
- redirect_uri = six.moves.urllib.parse.quote(redirect_uri, safe=b'')
- url_list = [
- self.OAUTH_BASE_URL,
- '?appid=',
- self._client.corp_id,
- '&redirect_uri=',
- redirect_uri,
- '&response_type=code&scope=',
- scope,
- ]
- if state:
- url_list.extend(['&state=', state])
- url_list.append('#wechat_redirect')
- if agent_id:
- url_list.extend(['&agentid=', str(agent_id)])
- return ''.join(url_list)
-
- def get_user_info(self, code):
- """
- 获取访问用户身份
- 详情请参考
- https://work.weixin.qq.com/api/doc#90000/90135/91023
-
- :param code: 通过成员授权获取到的code
- :return: 返回的 JSON 数据包
- """
-
- return self._get(
- 'user/getuserinfo',
- params={
- 'code': code,
- }
- )
diff --git a/sg_wechat_enterprise/models/client/api/jkm_user.py b/sg_wechat_enterprise/models/client/api/jkm_user.py
deleted file mode 100644
index 44a8c616..00000000
--- a/sg_wechat_enterprise/models/client/api/jkm_user.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from wechatpy.enterprise.client.api import WeChatUser
-
-
-class JkmWechatUser(WeChatUser):
-
- def get_detail(self, user_ticket):
- """
- 获取访问用户敏感信息
- 详情请参考
- https://developer.work.weixin.qq.com/document/path/95833
-
- :param user_ticket: 成员票据
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'user/getuserdetail',
- data={
- 'user_ticket': user_ticket
- }
- )
-
diff --git a/sg_wechat_enterprise/models/mail.py b/sg_wechat_enterprise/models/mail.py
deleted file mode 100644
index 7a0c91b4..00000000
--- a/sg_wechat_enterprise/models/mail.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# -*- coding: utf-8 -*-
-from odoo import models, fields, api
-import logging
-
-_logger = logging.getLogger(__name__)
-
-# -*- coding: utf-8-*-
-import re
-##过滤HTML中的标签
-#将HTML中标签等信息去掉
-#@param htmlstr HTML字符串.
-def filter_tags(htmlstr):
- #先过滤CDATA
- re_cdata=re.compile('//]*//\]\]>',re.I) #匹配CDATA
- re_script=re.compile('<\s*script[^>]*>[^<]*<\s*/\s*script\s*>',re.I)#Script
- re_style=re.compile('<\s*style[^>]*>[^<]*<\s*/\s*style\s*>',re.I)#style
- re_br=re.compile('
')#处理换行
- re_h=re.compile('?\w+[^>]*>')#HTML标签
- re_comment=re.compile('')#HTML注释
- s=re_cdata.sub('',htmlstr)#去掉CDATA
- s=re_script.sub('',s) #去掉SCRIPT
- s=re_style.sub('',s)#去掉style
- s=re_br.sub('\n',s)#将br转换为换行
- s=re_h.sub('',s) #去掉HTML 标签
- s=re_comment.sub('',s)#去掉HTML注释
- #去掉多余的空行
- blank_line=re.compile('\n+')
- s=blank_line.sub('\n',s)
- s=replaceCharEntity(s)#替换实体
- return s
-
-##替换常用HTML字符实体.
-#使用正常的字符替换HTML中特殊的字符实体.
-#你可以添加新的实体字符到CHAR_ENTITIES中,处理更多HTML字符实体.
-#@param htmlstr HTML字符串.
-def replaceCharEntity(htmlstr):
- CHAR_ENTITIES={'nbsp':' ','160':' ',
- 'lt':'<','60':'<',
- 'gt':'>','62':'>',
- 'amp':'&','38':'&',
- 'quot':'"','34':'"',}
-
- re_charEntity=re.compile(r'?(?P\w+);')
- sz=re_charEntity.search(htmlstr)
- while sz:
- entity=sz.group()#entity全称,如>
- key=sz.group('name')#去除&;后entity,如>为gt
- try:
- htmlstr=re_charEntity.sub(CHAR_ENTITIES[key],htmlstr,1)
- sz=re_charEntity.search(htmlstr)
- except KeyError:
- #以空串代替
- htmlstr=re_charEntity.sub('',htmlstr,1)
- sz=re_charEntity.search(htmlstr)
- return htmlstr
-
-def repalce(s,re_exp,repl_string):
- return re_exp.sub(repl_string,s)
-
-
-class MailMessage(models.Model):
- _inherit = 'mail.message'
-
- we_is_send = fields.Selection([('-1', '不需要发送'), ('0', '需要发送'), ('1', '已发送')], string='订订消息发送状态', default='0',
- index=True)
- we_error_msg = fields.Char('消息不发送原因')
-
- def send_we_message(self):
- """
- 定时批量发送钉钉消息
- :return:
- """
- messages = self.search([('we_is_send', '=', '0')])
- if messages and len(messages) > 0:
- try:
- messages.send_message_to_we()
- except Exception as ex:
- pass
-
- def send_message_to_we(self):
- """
- :param agent_id: 必填,企业应用的id,整型。可在应用的设置页面查看。
- :param user_ids: 成员ID列表(消息接收者,多个接收者用‘|’分隔,最多支持1000个)。
- :param title: 标题,不超过128个字节,超过会自动截断。
- :param description: 必填,描述,不超过512个字节,超过会自动截断
- :param url: 必填,点击后跳转的链接。
- :param btntxt: 按钮文字。 默认为“详情”, 不超过4个文字,超过自动截断。
- :param party_ids: 部门ID列表。
- :param tag_ids: 标签ID列表。
- :return:
- """
- # 获取配置信息
- config = self.env['ir.config_parameter'].sudo()
- # 获取wechat
- wechat,agent_id = self.env['we.config'].sudo().get_odoo_wechat()
- for m in self:
- we_user = m.get_we_user()
- if we_user and len(we_user)>0:
- try:
- _logger.info('wechat:%s' % wechat)
- model = self.env['ir.model'].search([('model', '=', m.model)], limit=1)
- title = "[%s] %s" % (model.name, m.record_name or m.subject or '')
- description = filter_tags(m.body) if len(filter_tags(m.body)) > 0 else '有新的状态和数据变更,请注意跟进.'
- url = '{base_url}/web#id={id}&model={model}&view_type=form'.format(
- base_url=config.get_param('web.base.url'),
- id=m.res_id,
- model=m.model)
- _logger.info('description:%s title:%s' % (description, title))
- ret = wechat.message.send_text_card(agent_id, we_user , title, description, url, btntxt='点此跟进..',
- party_ids='', tag_ids='')
- _logger.info('message_id:%s ret:%s' % (m.message_id, ret))
- m.write({'we_is_send':'1','we_error_msg':''})
- except Exception as ex:
- m.we_error_msg = '向企业微信发送消息失败! 消息编号:%s 原因:%s' % (m.message_id, ex)
- _logger.error('向企业微信发送消息失败! 消息编号:%s 原因:%s' % (m.message_id, ex))
- else:
- m.write({'we_is_send': '-1', 'we_error_msg': '无法发送,没有收件人!'})
- _logger.info('message_id:%s 不需要发送,没有收件人!' % m.message_id)
-
-
- return False
\ No newline at end of file
diff --git a/sg_wechat_enterprise/models/res_users.py b/sg_wechat_enterprise/models/res_users.py
deleted file mode 100644
index 275d3bdb..00000000
--- a/sg_wechat_enterprise/models/res_users.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-import random
-from odoo import models, fields, api
-from odoo.http import request
-from odoo.exceptions import AccessDenied
-
-import logging
-
-_logger = logging.getLogger(__name__)
-
-
-class ResUsers(models.Model):
- _inherit = 'res.users'
-
- we_employee_id = fields.Char(string=u'企业微信账号', default="")
-
- def _check_credentials(self, we_employee_id, env):
- """
- 用户验证
- """
- try:
- return super(ResUsers, self)._check_credentials(we_employee_id, env)
- except AccessDenied:
- user_id = self.env['res.users'].sudo().search([('we_employee_id', '=', we_employee_id)])
- if not (user_id and user_id.id == self.env.uid):
- raise
diff --git a/sg_wechat_enterprise/models/we_app.py b/sg_wechat_enterprise/models/we_app.py
deleted file mode 100644
index c4ea91ef..00000000
--- a/sg_wechat_enterprise/models/we_app.py
+++ /dev/null
@@ -1,213 +0,0 @@
-from odoo import api, models, exceptions, fields
-import logging
-
-from odoo.http import request
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseApp(models.Model):
- _name = 'we.app'
- _description = 'Wechat Enterprise App Manage'
-
- agentid = fields.Integer(string=u'应用ID', required=True)
- code = fields.Char(string=u'自定义代码')
- name = fields.Char(u'企业号应用名称')
- description = fields.Text(u'企业号应用详情')
- redirect_domain = fields.Char(u'企业应用可信域名')
- isreportuser = fields.Selection([('0', u'不接受'), ('1', u'接收')], u'变更通知', required=True, default='0')
- isreportenter = fields.Selection([('0', u'不接受'),
- ('1', u'接收')], u'进入应用事件上报', required=True, default='0')
-
- enterprise_id = fields.Many2one('we.config', string=u'企业微信')
-
- secret = fields.Char(string=u'Secret', size=100)
- home_url = fields.Char(u'主页型应用url')
- square_logo_url = fields.Char(u'方形头像url')
- round_logo_url = fields.Char(u'圆形头像url')
- type = fields.Selection([('1', u'消息型'), ('2', u'主页型')], u'应用类型', required=True, default='1')
- allow_userinfos = fields.Char(u'企业应用可见范围(人员)')
- allow_partys = fields.Char(u'企业应用可见范围(部门)')
- allow_tags = fields.Char(u'企业应用可见范围(标签)')
- report_location_flag = fields.Selection([('0', u'不上报'), ('1', u'进入会话上报'), ('2', u'持续上报')], u'位置上报',
- required=True, default='1')
- logo_mediaid = fields.Char(u'企业应用头像的mediaid')
- close = fields.Selection([('0', u'否'), ('1', u'是')], u'是否禁用', required=True, default='0')
- app_menu_ids = fields.One2many('we.app.menu', 'agentid', string=u'自定义菜单')
- Token = fields.Char(u'Token')
- EncodingAESKey = fields.Char(u'EncodingAESKey')
-
- def pull_app_from_we(self, wechat):
- """
- 从微信获取app列表
- :return:
- """
- try:
- app_lists = wechat.agent.list()
- if app_lists:
- for app_list in app_lists:
- if 'agentid' in app_list:
- app_detail = wechat.agent.get(app_list['agentid'])
- if app_detail:
- data = {
- 'agentid': str(app_detail['agentid'])
- }
- my_app = request.env["we.app"].search(
- [("agentid", "=", str(app_detail['agentid']))])
- if my_app and len(my_app) > 0:
- continue
- data['name'] = app_detail['name']
- data['square_logo_url'] = app_detail['square_logo_url']
- data['description'] = app_detail['description']
- data['close'] = str(app_detail['close'])
- data['redirect_domain'] = app_detail['redirect_domain']
- data['report_location_flag'] = str(app_detail['report_location_flag'])
- data['isreportenter'] = str(app_detail['isreportenter'])
- data['enterprise_id'] = self.id
- request.env["we.app"].create(data)
-
- except Exception as e:
- raise Warning((u'获取应用列表失败,原因:%s', str(e)))
-
- def push_app_to_we(self):
- """
- 同步app到微信
- :return:
- """
- wechat = self.env['we.config'].sudo().get_wechat()
- if wechat:
- try:
- for account in self:
- app_json = {
- 'name': account.name
- }
- if account.description:
- app_json['description'] = account.description
- if account.redirect_domain:
- app_json['redirect_domain'] = account.redirect_domain
- app_json['agentid'] = int(account.agentid)
- app_json['report_location_flag'] = int(account.report_location_flag)
- if account.type == "1": # 消息型应用
- if account.name and account.agentid \
- and account.isreportuser and account.isreportenter and account.report_location_flag:
- app_json['isreportuser'] = int(account.isreportuser)
- app_json['isreportenter'] = int(account.isreportenter)
- wechat.agent.set(agent_id=app_json['agentid'], name=app_json['name'],
- description=app_json['description'],
- redirect_domain=app_json['redirect_domain'],
- is_report_user=app_json['isreportuser'],
- is_report_enter=app_json['isreportenter'],
- report_location_flag=app_json['report_location_flag'])
- elif account.type == "2": # 主页型应用
- if account.name and account.agentid \
- and account.report_location_flag and account.home_url:
- app_json['home_url'] = account.home_url
- wechat.agent.set(agent_id=app_json['agentid'], name=app_json['name'],
- description=app_json['description'],
- redirect_domain=app_json['redirect_domain'],
- is_report_user=app_json['isreportuser'],
- is_report_enter=app_json['isreportenter'],
- report_location_flag=app_json['report_location_flag'])
-
- except Exception as e:
- _logger.warning(u'更新app失败,原因:%s', str(e))
- raise Warning(u'更新app失败,原因:%s', str(e))
- else:
- raise exceptions.Warning(u"初始化企业号失败")
-
- def update_app_menu(self):
- """
- 同步菜单至app
- :return:
- """
- wechat = self.env['we.config'].sudo().get_wechat(agentID=self.agentid)
- menus = self.env['we.app.menu'].sudo().search([("agentid", "=", self.name)])
- wechat.menu.delete(agent_id=self.agentid)
- menu_json = {'button': []}
- button = []
- if wechat and menus:
- for menu in menus:
- menu_data = {
- 'name': menu['name']
- }
- if not menu['partner_menu_id']:
- sub_menus = request.env['we.app.menu'].sudo().search(
- [("agentid", "=", self.name), ("partner_menu_id", "=", menu['name'])])
- if sub_menus and (len(sub_menus) > 0) and (len(sub_menus) < 6):
- sub_menu_list = []
- for sub_menu in sub_menus:
- sub_menu_data = {
- 'name': sub_menu['name']
- }
- if menu['type'] == 'xml' or menu['type'] == 'sub_button':
- sub_menu_data['type'] = sub_menu['type']
- sub_menu_data['url'] = sub_menu['url']
- else:
- sub_menu_data['type'] = sub_menu['type']
- sub_menu_data['key'] = sub_menu['key']
- sub_menu_list.append(sub_menu_data)
- menu_data['sub_button'] = sub_menu_list
- else:
- if menu['type'] == 'xml' or menu['type'] == 'sub_button':
- menu_data['type'] = menu['type']
- menu_data['url'] = menu['url']
- else:
- menu_data['type'] = menu['type']
- menu_data['key'] = menu['key']
- button.append(menu_data)
- menu_json['button'] = button
- wechat.menu.update(agent_id=self.agentid, menu_data=menu_json)
- else:
- raise exceptions.Warning(u"初始化企业号失败或该应用无菜单")
-
-
-class WechatEnterpriseAppMenu(models.Model):
- _name = 'we.app.menu'
- _description = 'Wechat Enterprise App Menu Manage'
-
- agentid = fields.Many2one('we.app', u'企业应用', required=True)
- partner_menu_id = fields.Many2one('we.app.menu', u'上级菜单')
- type = fields.Selection(
- [('sub_button', u'跳转至子菜单'), ('click', u'点击推事件'), ('xml', u'跳转URL'), ('scancode_push', u'扫码推事件'),
- ('scancode_waitmsg', u'扫码推事件且弹出“消息接收中”提示框'),
- ('pic_sysphoto', u'弹出系统拍照发图'), ('pic_photo_or_album', u'弹出拍照或者相册发图'), ('pic_weixin', u'弹出微信相册发图器'),
- ('location_select', u'弹出地理位置选择器')], u'按钮的类型', required=True, default='xml')
- name = fields.Char(u'菜单标题', required=True)
- key = fields.Char(u'菜单KEY值')
- url = fields.Char(u'网页链接')
-
- @api.constrains('partner_menu_id', 'name')
- def _check_menu_name_length(self):
- if self.name and self.partner_menu_id and len(self.name) > 7:
- raise Warning(u'二级菜单显示名称不能超过14个字符或7个汉字.')
- elif self.name and not self.partner_menu_id and len(self.name) > 4:
- raise Warning(u'一级菜单显示名称不能超过8个字符或4个汉字.')
- else:
- return True
-
- @api.constrains('agentid')
- def check_menu_number(self):
- """
- 取得一个app的一级菜单量
- :return:
- """
- menus_ids = self.sudo().search([('agentid', '=', self.agentid['name']), ('partner_menu_id', '=', False)])
-
- if menus_ids and len(menus_ids) > 3:
- raise Warning(u'公众号的一级菜单数据不能超过3个.')
-
- return True
-
- @api.constrains('partner_menu_id')
- def check_submenu_number(self):
- """
- 取得一个一级菜单的子菜单数量
- :return:
- """
- sub_menus_ids = self.sudo().search(
- [('partner_menu_id', '=', self.partner_menu_id['name']), ('partner_menu_id', '!=', False)])
-
- if sub_menus_ids and len(sub_menus_ids) > 5:
- raise Warning(u'一级菜单的二级子菜单数据不能超过5个.')
-
- return True
diff --git a/sg_wechat_enterprise/models/we_conf.py b/sg_wechat_enterprise/models/we_conf.py
deleted file mode 100644
index 403e900c..00000000
--- a/sg_wechat_enterprise/models/we_conf.py
+++ /dev/null
@@ -1,62 +0,0 @@
-import logging
-import requests
-import time
-import base64
-from odoo.http import request
-from odoo import api, models, exceptions, fields, _
-from wechatpy.enterprise import WeChatClient
-from .client import JkmWeChatClient
-from json import *
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseConfigration(models.Model):
- _name = 'we.config'
-
- name = fields.Char(u'名称', required=True, help=u'取个好名字吧!')
- corp_id = fields.Char(u'企业ID', required=True, help=u'企业ID,必填项')
- corp_secret = fields.Char(u'通讯录同步Secret', required=True, help=u'通讯录同步Secret,必填项')
- odoo_app_id = fields.Many2one('we.app', u'Odoo应用', help=u'在企业微信工作台配置的与Odoo进行连接的应用')
- company_id = fields.Many2one('res.company', string=u'所属公司')
-
- _sql_constraints = [
- ('code_complete_name_uniq', 'unique (company_id)', '一个所属公司只能定义一个企业微信!')
- ]
-
- def get_wechat(self, agent_id=None, company_id=1):
- """
- 取得wechat app的实例
- :param agent_id: conf or None (是企业号对象还是应用对象)
- :return:
- """
- enterprise = self.env['we.config'].sudo().search([('company_id', '=', company_id)], limit=1)
- if agent_id:
- enterprise_app = self.env['we.app'].sudo().search([('agentid', '=', agent_id)])
- return WeChatClient(corp_id=enterprise.corp_id, secret=enterprise_app.secret)
- return WeChatClient(corp_id=enterprise.corp_id, secret=enterprise.corp_secret)
-
- def get_odoo_wechat(self, company_id=1):
- """
- 取得Odoo wechat app的实例
- :param agent_id: conf or None (是企业号对象还是应用对象)
- :return:
- """
- enterprise = self.env['we.config'].sudo().search([('company_id', '=', company_id)], limit=1)
- if enterprise.odoo_app_id:
- return (JkmWeChatClient(corp_id=enterprise.corp_id, secret=enterprise.odoo_app_id.secret),
- enterprise.odoo_app_id.agentid)
- else:
- raise exceptions.Warning(u'Odoo应用未配置. ')
-
- def get_wechat_app(self, app_code=None, company_id=1):
- """
- 取得wechat app的实例
- :param app_code: 应用代码
- :return:
- """
- enterprise = self.env['we.config'].sudo().search([('company_id', '=', company_id)], limit=1)
- if app_code:
- enterprise_app = self.env['we.app'].sudo().search([('code', '=', app_code)])
- return WeChatClient(corp_id=enterprise.corp_id, secret=enterprise_app.secret)
- return WeChatClient(corp_id=enterprise.corp_id, secret=enterprise.corp_secret)
\ No newline at end of file
diff --git a/sg_wechat_enterprise/models/we_receive_message.py b/sg_wechat_enterprise/models/we_receive_message.py
deleted file mode 100644
index cbeaedcc..00000000
--- a/sg_wechat_enterprise/models/we_receive_message.py
+++ /dev/null
@@ -1,143 +0,0 @@
-from odoo import api, models, fields
-import logging
-from odoo.http import request
-import datetime
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseReceiveMessage(models.Model):
- _name = 'we.receive.message'
- _description = 'Wechat Enterprise Receive Message Manage'
-
- name = fields.Char(required=True, index=True)
- ToUserName = fields.Char(u'企业号CorpID')
- FromUserName = fields.Char(u'成员UserID')
- CreateTime = fields.Char(u'消息创建时间')
- MsgId = fields.Char(u'消息id')
- AgentID = fields.Many2one('we.app', u'企业应用')
- MsgType = fields.Selection([('text', u'文本'),
- ('voice', u'语音'),
- ('image', u'图片'),
- ('video', u'视频'),
- ('shortvideo', u'短视频'),
- ('location', u'位置'),
- ('link', u'链接'),
- ('subscribe', u'关注'),
- ('unsubscribe', u'取消关注'),
- ('xml', u'自定义菜单链接跳转'),
- ('click', u'自定义菜单点击'),
- ('scan', u'扫描二维码'),
- ('scancode_waitmsg', u'扫描二维码并等待'),
- ('event', u'取消关注')],
- string=u'消息类型', required=True, default='text')
- Content = fields.Text(u'文本消息内容')
- state = fields.Selection([('1', u'未处理'), ('2', u'已处理'), ('3', u'处理失败')], u'状态', default='1')
- PicUrl = fields.Char(u'图片链接')
- MediaId = fields.Char(u'图片媒体文件id')
- Format = fields.Char(u'语音格式')
- ThumbMediaId = fields.Char(u'视频消息缩略图的媒体id')
- Location_X = fields.Char(u'地理位置纬度')
- Location_Y = fields.Char(u'地理位置经度')
- Scale = fields.Char(u'地图缩放大小')
- Label = fields.Char(u'地理位置信息')
- Title = fields.Char(u'标题')
- Description = fields.Char(u'描述')
- Cover_PicUrl = fields.Char(u'封面缩略图的url')
-
- @api.depends('MsgType', 'MsgId')
- def name_get(self):
- result = []
- for receive in self:
- name = receive.MsgType + '_' + receive.MsgId
- result.append((receive.id, name))
- return result
-
- def add_message(self, data):
- """
- 增加一条待处理的上传消息
- :param data:
- :return:
- """
-
- app = request.env['we.app'].sudo().search([("agentid", "=", data["AgentID"])])
- receive_message_data = {
- 'AgentID': app.id,
- 'MsgType': data["MsgType"],
- 'FromUserName': data["FromUserName"],
- 'ToUserName': data["ToUserName"]
- }
- current_time = datetime.datetime.now()
- real_time = current_time + datetime.timedelta(hours=8)
- receive_message_data["CreateTime"] = real_time
- receive_message_data["name"] = data["MsgType"] + data["MsgId"]
-
- if data["MsgType"] == "text":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["Content"] = data["Content"]
- if data["MsgType"] == "image":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["PicUrl"] = data["PicUrl"]
- receive_message_data["MediaId"] = data["MediaId"]
- if data["MsgType"] == "voice":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["MediaId"] = data["MediaId"]
- receive_message_data["Format"] = data["Format"]
- if data["MsgType"] == "video" or data["MsgType"] == "shortvideo":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["MediaId"] = data["MediaId"]
- receive_message_data["ThumbMediaId"] = data["ThumbMediaId"]
- if data["MsgType"] == "location":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["Location_X"] = data["Location_X"]
- receive_message_data["Location_Y"] = data["Location_Y"]
- receive_message_data["Scale"] = data["Scale"]
- receive_message_data["Label"] = data["Label"]
- if data["MsgType"] == "link":
- receive_message_data["MsgId"] = data["MsgId"]
- receive_message_data["Title"] = data["Title"]
- receive_message_data["Description"] = data["Description"]
- receive_message_data["Cover_PicUrl"] = data["PicUrl"]
- if data["MsgType"] == "event":
- if data["Event"] == "subscribe":
- receive_message_data["MsgType"] = "subscribe"
- if data["Event"] == "unsubscribe":
- receive_message_data["MsgType"] = "unsubscribe"
-
- return super(WechatEnterpriseReceiveMessage, self).create(receive_message_data)
-
- def process_message(self, data):
- """
- 处理未处理和失败的消息
- :param data:
- :return:
- """
- messages = self.sudo().add_message(data)
- for message in messages:
- if message:
- if message.state == '2':
- break
- if data["MsgType"] == "text":
- process = self.env['we.receive.message.process'].get_message_process(data["MsgType"],
- data["Content"],
- data["AgentID"])
- else:
- process = self.env['we.receive.message.process'].get_message_process(data["MsgType"],
- " ",
- data["AgentID"])
- try:
- if process:
- if data["MsgType"] == "voice" or data["MsgType"] == "image" or data["MsgType"] == "video" or \
- data["MsgType"] == "shortvideo":
- process.sudo().exec_class_mothed(data["FromUserName"], data["AgentID"], data["MediaId"])
- else:
- process.sudo().exec_class_mothed(data["FromUserName"], data["AgentID"])
- else:
- return self.env['we.send.message'].sudo().send_text_message(data["FromUserName"],
- data["AgentID"],
- content=u'感谢您的关注!')
-
- message.sudo().write({'state': '1'})
- except Exception as e:
- message.sudo().write({'state': u'处理失败'})
- raise Warning(u'处理失败, 原因:%s', str(e))
diff --git a/sg_wechat_enterprise/models/we_receive_message_process.py b/sg_wechat_enterprise/models/we_receive_message_process.py
deleted file mode 100644
index 02acb682..00000000
--- a/sg_wechat_enterprise/models/we_receive_message_process.py
+++ /dev/null
@@ -1,109 +0,0 @@
-from odoo import models, fields
-import logging
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseReceiveMessageProcess(models.Model):
- _name = 'we.receive.message.process'
- _description = 'Wechat Enterprise Process Receive Process Message'
-
- name = fields.Char(u'名称', help=u'取个好名称方便管理,比如优惠信息索取', required=True)
- message_type = fields.Selection([('text', u'文本'),
- ('voice', u'语音'),
- ('image', u'图片'),
- ('video', u'视频'),
- ('shortvideo', u'短视频'),
- ('location', u'位置'),
- ('link', u'链接'),
- ('subscribe', u'关注'),
- ('unsubscribe', u'取消关注'),
- ('xml', u'自定义菜单链接跳转'),
- ('click', u'自定义菜单点击'),
- ('scan', u'扫描二维码'),
- ('scancode_waitmsg', u'扫描二维码并等待'),
- ('unsubscribe', u'取消关注')],
- string=u'消息类型', required=True)
- message_key = fields.Char(u'关键字', required=True)
- class_name = fields.Char(u'负责处理的类', help='此处填写进行处理的类的名称),例如:topro_service_base.test', required=True, default="类的名称")
- method_name = fields.Char(u'负责处理的方法', help='此处填写进入处理的方法名,方法必须包括参数是message和account_id(微信公众号的id),这是一个dict',
- required=True, default="方法名")
- agentID = fields.Many2one('we.app', u'企业应用', required=True)
- note = fields.Text(u'备注')
-
- def get_message_process(self, message_type, key_word=False, agent_id=False):
- """
- 取得消息处理的设置
- :param message_type:
- :param key_word:
- :param agent_id:
- :return:
- """
- process = False
- if message_type:
- process = self.sudo().search(
- [('message_type', '=', message_type), ('message_key', '=', key_word), ('agentID', '=', agent_id)],
- limit=1)
- if not process and message_type and key_word:
- process = self.sudo().search([('message_type', '=', message_type), ('message_key', '=', key_word)], limit=1)
- if not process and message_type:
- process = self.sudo().search([('message_type', '=', message_type)], limit=1)
- return process
-
- def exec_by_message_type(self, message_type, message_key, agent_id):
- """
- 根据消息的类型动态调用类进行执行
- :param message_type:
- :param message_key:
- :param agent_id:
- :return:
- """
-
- # 取得对象,
- if message_type and message_key:
- process = self.get_message_process(message_type, message_key, agent_id)
- process.sudo().exec_class_mothed(message_key, agent_id)
-
- def exec_class_mothed(self, from_user_name, agent_id, media_id=None):
- """
- 执行类的方法
- :param from_user_name:
- :param agent_id:
- :param media_id:
- :return:
- """
-
- _logger.debug('exec_class_mothed')
- object_function = getattr(self.env[self.class_name], self.method_name)
- ret = object_function(from_user_name, agent_id, media_id)
-
- return ret
-
- def hello(self, from_user_name, agent_id):
- """
- demo方法,模拟动态处理客户的需求
- :param from_user_name:
- :param agent_id:
- :return:
- """
- try:
- self.env['we.send.message'].sudo(). \
- send_news_message(from_user_name, agent_id, u'测试图文信息', u'测试图文信息的描述',
- 'http://www.baidu.com',
- 'http://www.kia-hnsyt.com.cn/uploads/allimg/141204/1-1412041624240-L.jpg')
- except Exception as e:
- _logger.warning(u'发送微信文本失败原因:%s', str(e))
- raise Warning(str(e))
-
- def send_img(self, from_user_name, agent_id):
- """
- demo方法,模拟动态处理客户的需求
- :param from_user_name:
- :param agent_id:
- :return:
- """
- try:
- self.env['we.send.message'].sudo().send_text_message(from_user_name, agent_id, u'即将为您发送一条消息')
- except Exception as e:
- _logger.warning(u'发送微信文本失败原因:%s', str(e))
- raise Warning(str(e))
diff --git a/sg_wechat_enterprise/models/we_send_message.py b/sg_wechat_enterprise/models/we_send_message.py
deleted file mode 100644
index 63a06042..00000000
--- a/sg_wechat_enterprise/models/we_send_message.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from odoo import api, models, exceptions, fields
-import logging
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseSendMessage(models.Model):
- _name = 'we.send.message'
- _description = 'Wechat Enterprise Send Message Manage'
-
- touser = fields.Many2many('res.users', 'send_message_to_users_ref',
- 'wechat_contacts_id', 'touser', u'成员列表')
- msgtype = fields.Selection([('text', u'文字消息'), ('image', u'图片消息'), ('voice', u'语音消息'), ('video', u'视频消息'),
- ('file', u'文件消息'), ('news', u'图文消息'), ('mpnews', u'微信后台图文消息')], u'消息类型',
- required=True, default='text')
- agentid = fields.Many2one('we.app', u'发送消息的企业应用', required=True)
- content = fields.Char(u'消息内容')
- media_id = fields.Char(u'媒体文件')
- title = fields.Char(u'标题')
- description = fields.Text(u'描述')
- articles = fields.Char(u'图文消息')
- url = fields.Char(u'点击后跳转的链接')
- picurl = fields.Char(u'图文消息的图片链接')
- thumb_media_id = fields.Char(u'图文消息缩略图')
- author = fields.Char(u'图文消息的作者')
- content_source_url = fields.Char(u'图文消息点击“阅读原文”之后的页面链接')
- news_content = fields.Char(u'图文消息的内容,支持html标签')
- digest = fields.Char(u'图文消息的描述')
- show_cover_pic = fields.Selection([('0', u'否'), ('1', u'是')], u'是否显示封面', default='0')
- safe = fields.Selection([('0', u'否'), ('1', u'是')], u'是否是保密消息', required=True, default='0')
-
- def send_message(self):
- """
- 发送消息给关注企业号的用户
- :return:
- """
- users = ""
- i = 0
- if self.touser and len(self.touser) > 0:
- for data in self.touser:
- i = i + 1
- if i == len(self.touser):
- if data['we_employee_id']:
- users = users + data['we_employee_id']
- else:
- if data['we_employee_id']:
- users = users + data['we_employee_id'] + "|"
- if users == "":
- users = '@all'
- partys = ""
-
- if self.msgtype == "news":
- self.send_news_message(users, self.agentid['agentid'], self.title, self.description, self.url, self.picurl)
- elif self.msgtype == "text":
- self.send_text_message(users, self.agentid['agentid'], self.content, partys)
-
- def send_text_message(self, userid, agentid, content, partyid=None):
- """
- 发送文本消息给关注企业号的用户
- :param userid:
- :param agentid:
- :param content:
- :param partyid:
- :return:
- """
- try:
- wechat = self.env['we.config'].sudo().get_wechat(agent_id=agentid)
- if wechat:
- data = {
- 'safe': "0",
- 'msgtype': 'text',
- 'agentid': agentid,
- 'touser': userid,
- 'toparty': partyid,
- 'content': content
- }
- wechat.message.send_text(agent_id=data['agentid'], user_ids=data['touser'], content=data['content'],
- party_ids=data['toparty'], safe=data['safe'])
- else:
- raise exceptions.Warning(u"初始化企业号失败")
- except Exception as e:
- logging.error('send_text_message:%s' % e)
-
- # 发送图文消息给关注企业号的用户
- def send_news_message(self, userid, agentid, title=None, description=None, url=None, picurl=None):
- """
- 发送图文消息给关注企业号的用户
- :param userid:
- :param agentid:
- :param title:
- :param description:
- :param url:
- :param picurl:
- :return:
- """
- wechat = self.env['we.config'].sudo().get_wechat(agent_id=agentid)
- if wechat:
- articles = [
- {
- 'url': url,
- 'image': picurl,
- 'description': description,
- 'title': title
- }
- ]
- wechat.message.send_articles(agent_id=agentid, user_ids=userid, articles=articles)
- else:
- raise exceptions.Warning(u"初始化企业号失败")
diff --git a/sg_wechat_enterprise/models/we_tools.py b/sg_wechat_enterprise/models/we_tools.py
deleted file mode 100644
index 3dd8fc45..00000000
--- a/sg_wechat_enterprise/models/we_tools.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from odoo import api, models, exceptions
-from odoo.http import request
-import logging
-import hashlib
-import base64
-import time
-import requests
-
-_logger = logging.getLogger(__name__)
-
-
-class WechatEnterpriseTools(models.Model):
- """
- 微信企业号工具类
- """
- _name = 'we.tools'
- _description = '微信企业号工具类'
-
- def get_media(self, media_id):
- """
- 通过media_id 获取媒体文件
- :param media_id: media id
- :return:
- """
- wechat = self.env['we.config'].sudo().get_wechat()
- try:
- media = wechat.media.download(media_id=media_id)
- return {
- 'errcode': 0,
- 'errmsg': 'ok',
- 'media': media.content
- }
- except Exception as ex:
- _logger.info(u'get media fail, message: {str(ex)}.')
- return {
- 'errcode': 30001,
- 'errmsg': str(ex)
- }
-
- def get_jsapi_ticket(self):
- """
- 获取jsapi_ticket
- :return:
- """
- if request.session.get('ticket') and request.session.get('ticket_time') \
- and int(time.time()) - request.session['ticket_time'] <= 7000:
- return {
- 'errcode': 0,
- 'errmsg': 'ok',
- 'ticket': request.session['ticket']
- }
- wechat = self.env['we.config'].sudo().get_wechat()
- get_token = wechat.fetch_access_token()
- if get_token['errcode'] == 0 and get_token['errmsg'] == 'ok':
- access_token = get_token['access_token']
- url = u'https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token={access_token}'
- response = requests.get(url).json()
- if response['errcode'] == 0 and response['errmsg'] == 'ok':
- request.session['ticket'] = response['ticket']
- request.session['ticket_time'] = int(time.time())
- return response
- return {
- "errcode": 10002,
- "errmsg": "get ticket fail."
- }
-
- def check_message_signature(self, message_list, msg_signature):
- """
- 校验消息的正确性
- :param message_list: 消息列表 (list: token, timestamp, nonce, echo_string)
- :param msg_signature: 签名
- :return: true or false
- """
- _logger.info(u'check message signature.')
- message_list.sort()
- message_str = "".join(message_list)
- sha1_message = hashlib.sha1(str(message_str).encode('utf-8')).hexdigest()
- if sha1_message == msg_signature:
- return True
- return False
-
- def decode_echo_str(self, echo_str):
- """
- 解密echo string 得到明文内容
- :param echo_str: 加密字符串
- :return: message
- """
- _logger.info(u'decode echo string.')
- base64_str = base64.b64decode(echo_str)
diff --git a/sg_wechat_enterprise/requirements.txt b/sg_wechat_enterprise/requirements.txt
deleted file mode 100644
index c4f4248f..00000000
--- a/sg_wechat_enterprise/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-wechatpy==1.8.6
diff --git a/sg_wechat_enterprise/security/ir.model.access.csv b/sg_wechat_enterprise/security/ir.model.access.csv
deleted file mode 100644
index 4a21fc53..00000000
--- a/sg_wechat_enterprise/security/ir.model.access.csv
+++ /dev/null
@@ -1,13 +0,0 @@
-id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-
-access_user_we_app,we_app,model_we_app,,1,1,1,1
-
-access_user_we_config,we_config,model_we_config,,1,1,1,1
-
-access_user_we_receive_message,we_receive_message,model_we_receive_message,,1,1,1,1
-
-access_user_we_receive_message_process,we_receive_message_process,model_we_receive_message_process,,1,1,1,1
-
-access_user_we_send_message,we_send_message,model_we_send_message,,1,1,1,1
-
-access_user_we_app_menu,we_app_menu,model_we_app_menu,,1,1,1,1
diff --git a/sg_wechat_enterprise/static/css/loading.css b/sg_wechat_enterprise/static/css/loading.css
deleted file mode 100644
index dd6243a7..00000000
--- a/sg_wechat_enterprise/static/css/loading.css
+++ /dev/null
@@ -1,24 +0,0 @@
-@-webkit-keyframes loadingCircle {
- to {
- transform:rotate(1turn)
- }
-}
-@keyframes loadingCircle {
- to {
- transform:rotate(1turn)
- }
-}
-.load-box {
- position:fixed;
- top:calc(50% - 11px);
- left:calc(50% - 111px);
- display:flex;
- align-items:center;
-}
-.load-icon {
- animation:loadingCircle 1s linear infinite;
-}
-.load-txt {
- font-size:16px;
- margin-left:10px;
-}
\ No newline at end of file
diff --git a/sg_wechat_enterprise/static/description/icon.png b/sg_wechat_enterprise/static/description/icon.png
deleted file mode 100644
index c83caaea..00000000
Binary files a/sg_wechat_enterprise/static/description/icon.png and /dev/null differ
diff --git a/sg_wechat_enterprise/static/description/qyh.png b/sg_wechat_enterprise/static/description/qyh.png
deleted file mode 100644
index 9c4e4f24..00000000
Binary files a/sg_wechat_enterprise/static/description/qyh.png and /dev/null differ
diff --git a/sg_wechat_enterprise/static/js/url_transfers.js b/sg_wechat_enterprise/static/js/url_transfers.js
deleted file mode 100644
index d3b61ee1..00000000
--- a/sg_wechat_enterprise/static/js/url_transfers.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Created by jiangxiang on 2016/3/14.
- */
-
-//��ȡ���Ӵ�������openID����
-function getUrlParam(url, name) {
- var pattern = new RegExp("[?&]" + name + "\=([^&]+)", "g");
- var matcher = pattern.exec(url);
- var items = null;
- if (matcher != null) {
- try {
- items = decodeURIComponent(decodeURIComponent(matcher[1]));
- } catch (e) {
- try {
- items = decodeURIComponent(matcher[1]);
- } catch (e) {
- items = matcher[1];
- }
- }
- }
- items = items.replace(/^\s*/, "");
- return items;
-}
-
-var url = document.getElementById("url").innerText;
-window.location.href = url;
\ No newline at end of file
diff --git a/sg_wechat_enterprise/tests/__init__.py b/sg_wechat_enterprise/tests/__init__.py
deleted file mode 100644
index 40a96afc..00000000
--- a/sg_wechat_enterprise/tests/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# -*- coding: utf-8 -*-
diff --git a/sg_wechat_enterprise/views/app_view.xml b/sg_wechat_enterprise/views/app_view.xml
deleted file mode 100644
index 8243900b..00000000
--- a/sg_wechat_enterprise/views/app_view.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
- we.app.form
- we.app
-
-
-
-
-
-
-
- we.app.tree
- we.app
-
-
-
-
-
-
-
-
-
-
-
-
-
- we.app.search
- we.app
- primary
-
-
-
-
-
-
-
-
-
- 应用配置
- ir.actions.act_window
- we.app
- tree,form
- form
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/mail_view.xml b/sg_wechat_enterprise/views/mail_view.xml
deleted file mode 100644
index 5aee337b..00000000
--- a/sg_wechat_enterprise/views/mail_view.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
- mail.message.form.dingtalk
- mail.message
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sg_wechat_enterprise/views/menu_view.xml b/sg_wechat_enterprise/views/menu_view.xml
deleted file mode 100644
index 781102cc..00000000
--- a/sg_wechat_enterprise/views/menu_view.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/res_users_view.xml b/sg_wechat_enterprise/views/res_users_view.xml
deleted file mode 100644
index c65d8e46..00000000
--- a/sg_wechat_enterprise/views/res_users_view.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
- res.users.account.form
- res.users
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_app_view.xml b/sg_wechat_enterprise/views/we_app_view.xml
deleted file mode 100644
index 1d85dd14..00000000
--- a/sg_wechat_enterprise/views/we_app_view.xml
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
- we.app.form
- we.app
-
-
-
-
-
-
-
- we.app.tree
- we.app
-
-
-
-
-
-
-
-
-
-
-
-
-
- we.app.search
- we.app
- primary
-
-
-
-
-
-
-
-
-
- 应用配置
- ir.actions.act_window
- we.app
- tree,form
- form
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_config_view.xml b/sg_wechat_enterprise/views/we_config_view.xml
deleted file mode 100644
index 85a970a7..00000000
--- a/sg_wechat_enterprise/views/we_config_view.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
- we.config.form
- we.config
-
-
-
-
-
-
-
- we.config.tree
- we.config
-
-
-
-
-
-
-
-
-
-
-
- 企业微信
- ir.actions.act_window
- we.config
- tree,form
- form
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_menu.xml b/sg_wechat_enterprise/views/we_menu.xml
deleted file mode 100644
index d65c669b..00000000
--- a/sg_wechat_enterprise/views/we_menu.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_message_process_view.xml b/sg_wechat_enterprise/views/we_message_process_view.xml
deleted file mode 100644
index 26fef5d0..00000000
--- a/sg_wechat_enterprise/views/we_message_process_view.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
- we.receive.message.process.form
- we.receive.message.process
-
-
-
-
-
-
-
- we.receive.message.process.tree
- we.receive.message.process
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- we.receive.message.process.search
- we.receive.message.process
- primary
-
-
-
-
-
-
-
-
-
- 接收消息处理
- ir.actions.act_window
- we.receive.message.process
- tree,form
- form
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_receive_message_view.xml b/sg_wechat_enterprise/views/we_receive_message_view.xml
deleted file mode 100644
index 26db7b3e..00000000
--- a/sg_wechat_enterprise/views/we_receive_message_view.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
- we.receive.message.form
- we.receive.message
-
-
-
-
-
-
-
- we.receive.message.tree
- we.receive.message
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- we.receive.message.search
- we.receive.message
- primary
-
-
-
-
-
-
-
-
-
- 接收消息
- ir.actions.act_window
- we.receive.message
- tree,form
- form
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_send_message_view.xml b/sg_wechat_enterprise/views/we_send_message_view.xml
deleted file mode 100644
index e18ff9f7..00000000
--- a/sg_wechat_enterprise/views/we_send_message_view.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
- we.send.message.form
- we.send.message
-
-
-
-
-
-
-
- we.send.message.tree
- we.send.message
-
-
-
-
-
-
-
-
-
-
-
-
- we.send.message.search
- we.send.message
- primary
-
-
-
-
-
-
-
-
-
-
-
- 发送消息
- ir.actions.act_window
- we.send.message
- tree,form
- form
-
-
-
-
-
-
diff --git a/sg_wechat_enterprise/views/we_templates.xml b/sg_wechat_enterprise/views/we_templates.xml
deleted file mode 100644
index 6694833a..00000000
--- a/sg_wechat_enterprise/views/we_templates.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
- <!DOCTYPE html>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/sg_wechat_enterprise/we_api/__init__.py b/sg_wechat_enterprise/we_api/__init__.py
deleted file mode 100644
index e4a50359..00000000
--- a/sg_wechat_enterprise/we_api/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from __future__ import absolute_import, unicode_literals
-import logging
-try:
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
-except ImportError:
- from pkg_resources import declare_namespace
- declare_namespace(__name__)
-
-from wechatpy.parser import parse_message # NOQA
-from wechatpy.replies import create_reply # NOQA
-from wechatpy.client import WeChatClient # NOQA
-from wechatpy.exceptions import WeChatException # NOQA
-from wechatpy.exceptions import WeChatClientException # NOQA
-from wechatpy.oauth import WeChatOAuth # NOQA
-from wechatpy.exceptions import WeChatOAuthException # NOQA
-from wechatpy.pay import WeChatPay # NOQA
-from wechatpy.exceptions import WeChatPayException # NOQA
-from wechatpy.component import WeChatComponent # NOQA
-
-
-__version__ = '1.3.1'
-__author__ = 'messense'
-
-# Set default logging handler to avoid "No handler found" warnings.
-try: # Python 2.7+
- from logging import NullHandler
-except ImportError:
- class NullHandler(logging.Handler):
- def emit(self, record):
- pass
-
-logging.getLogger(__name__).addHandler(NullHandler())
diff --git a/sg_wechat_enterprise/we_api/_compat.py b/sg_wechat_enterprise/we_api/_compat.py
deleted file mode 100644
index d2889243..00000000
--- a/sg_wechat_enterprise/we_api/_compat.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy._compat
- ~~~~~~~~~~~~~~~~~
-
- This module makes it easy for wechatpy to run on both Python 2 and 3.
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import sys
-import six
-import warnings
-
-warnings.warn("Module `wechatpy._compat` is deprecated, will be removed in 2.0"
- "use `wechatpy.utils` instead",
- DeprecationWarning, stacklevel=2)
-
-from wechatpy.utils import get_querystring
-from wechatpy.utils import json
diff --git a/sg_wechat_enterprise/we_api/client/__init__.py b/sg_wechat_enterprise/we_api/client/__init__.py
deleted file mode 100644
index 02c6c525..00000000
--- a/sg_wechat_enterprise/we_api/client/__init__.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-try:
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
-except ImportError:
- from pkg_resources import declare_namespace
- declare_namespace(__name__)
-
-import time
-
-from wechatpy.client.base import BaseWeChatClient
-from wechatpy.client import api
-
-
-class WeChatClient(BaseWeChatClient):
-
- """
- 微信 API 操作类
- 通过这个类可以操作微信 API,发送主动消息、群发消息和创建自定义菜单等。
- """
-
- API_BASE_URL = 'https://api.weixin.qq.com/cgi-bin/'
-
- menu = api.WeChatMenu()
- user = api.WeChatUser()
- group = api.WeChatGroup()
- media = api.WeChatMedia()
- card = api.WeChatCard()
- qrcode = api.WeChatQRCode()
- message = api.WeChatMessage()
- misc = api.WeChatMisc()
- merchant = api.WeChatMerchant()
- customservice = api.WeChatCustomService()
- datacube = api.WeChatDataCube()
- jsapi = api.WeChatJSAPI()
- material = api.WeChatMaterial()
- semantic = api.WeChatSemantic()
- shakearound = api.WeChatShakeAround()
- device = api.WeChatDevice()
- template = api.WeChatTemplate()
- poi = api.WeChatPoi()
- wifi = api.WeChatWiFi()
- scan = api.WeChatScan()
-
- def __init__(self, appid, secret, access_token=None,
- session=None, timeout=None, auto_retry=True):
- super(WeChatClient, self).__init__(
- appid, access_token, session, timeout, auto_retry
- )
- self.appid = appid
- self.secret = secret
-
- def fetch_access_token(self):
- """
- 获取 access token
- 详情请参考 http://mp.weixin.qq.com/wiki/index.php?title=通用接口文档
-
- :return: 返回的 JSON 数据包
- """
- return self._fetch_access_token(
- url='https://api.weixin.qq.com/cgi-bin/token',
- params={
- 'grant_type': 'client_credential',
- 'appid': self.appid,
- 'secret': self.secret
- }
- )
-
-
-class WeChatComponentClient(WeChatClient):
-
- """
- 开放平台代公众号调用客户端
- """
-
- def __init__(self, appid, component, access_token=None,
- refresh_token=None, session=None, timeout=None):
- # 未用到secret,所以这里没有
- super(WeChatComponentClient, self).__init__(
- appid, '', access_token, session, timeout
- )
- self.appid = appid
- self.component = component
- # 如果公众号是刚授权,外部还没有缓存access_token和refresh_token
- # 可以传入这两个值,session 会缓存起来。
- # 如果外部已经缓存,这里只需要传入 appid,component和session即可
- if access_token:
- self.session.set(self.access_token_key, access_token, 7200)
- if refresh_token:
- self.session.set(self.refresh_token_key, refresh_token, 7200)
-
- @property
- def access_token_key(self):
- return '{0}_access_token'.format(self.appid)
-
- @property
- def refresh_token_key(self):
- return '{0}_refresh_token'.format(self.appid)
-
- @property
- def access_token(self):
- access_token = self.session.get(self.access_token_key)
- if not access_token:
- self.fetch_access_token()
- access_token = self.session.get(self.access_token_key)
- return access_token
-
- @property
- def refresh_token(self):
- return self.session.get(self.refresh_token_key)
-
- def fetch_access_token(self):
- """
- 获取 access token
- 详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list\
- &t=resource/res_list&verify=1&id=open1419318587&token=&lang=zh_CN
-
- 这是内部刷新机制。请不要完全依赖!
- 因为有可能在缓存期间没有对此公众号的操作,造成refresh_token失效。
-
- :return: 返回的 JSON 数据包
- """
- expires_in = 7200
- result = self.component.refresh_authorizer_token(
- self.appid, self.refresh_token)
- if 'expires_in' in result:
- expires_in = result['expires_in']
- self.session.set(
- self.access_token_key,
- result['authorizer_access_token'],
- expires_in
- )
- self.session.set(
- self.refresh_token_key,
- result['authorizer_refresh_token'],
- expires_in
- )
- self.expires_at = int(time.time()) + expires_in
- return result
diff --git a/sg_wechat_enterprise/we_api/client/api/__init__.py b/sg_wechat_enterprise/we_api/client/api/__init__.py
deleted file mode 100644
index 30291577..00000000
--- a/sg_wechat_enterprise/we_api/client/api/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/base.py b/sg_wechat_enterprise/we_api/client/api/base.py
deleted file mode 100644
index 790d9414..00000000
--- a/sg_wechat_enterprise/we_api/client/api/base.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/card.py b/sg_wechat_enterprise/we_api/client/api/card.py
deleted file mode 100644
index 5f6ebe35..00000000
--- a/sg_wechat_enterprise/we_api/client/api/card.py
+++ /dev/null
@@ -1,431 +0,0 @@
-# -*- 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
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/customservice.py b/sg_wechat_enterprise/we_api/client/api/customservice.py
deleted file mode 100644
index 0a2db30a..00000000
--- a/sg_wechat_enterprise/we_api/client/api/customservice.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/datacube.py b/sg_wechat_enterprise/we_api/client/api/datacube.py
deleted file mode 100644
index f6d679c8..00000000
--- a/sg_wechat_enterprise/we_api/client/api/datacube.py
+++ /dev/null
@@ -1,360 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/device.py b/sg_wechat_enterprise/we_api/client/api/device.py
deleted file mode 100644
index 56b5a133..00000000
--- a/sg_wechat_enterprise/we_api/client/api/device.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# -*- 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
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/group.py b/sg_wechat_enterprise/we_api/client/api/group.py
deleted file mode 100644
index 90600916..00000000
--- a/sg_wechat_enterprise/we_api/client/api/group.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- 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
- }
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/jsapi.py b/sg_wechat_enterprise/we_api/client/api/jsapi.py
deleted file mode 100644
index 9c444176..00000000
--- a/sg_wechat_enterprise/we_api/client/api/jsapi.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/material.py b/sg_wechat_enterprise/we_api/client/api/material.py
deleted file mode 100644
index cf40fde0..00000000
--- a/sg_wechat_enterprise/we_api/client/api/material.py
+++ /dev/null
@@ -1,167 +0,0 @@
-# -*- 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')
diff --git a/sg_wechat_enterprise/we_api/client/api/media.py b/sg_wechat_enterprise/we_api/client/api/media.py
deleted file mode 100644
index e04317b2..00000000
--- a/sg_wechat_enterprise/we_api/client/api/media.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/menu.py b/sg_wechat_enterprise/we_api/client/api/menu.py
deleted file mode 100644
index e355e8ad..00000000
--- a/sg_wechat_enterprise/we_api/client/api/menu.py
+++ /dev/null
@@ -1,235 +0,0 @@
-# -*- 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}
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/__init__.py b/sg_wechat_enterprise/we_api/client/api/merchant/__init__.py
deleted file mode 100644
index 93899db7..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/__init__.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- 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
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/category.py b/sg_wechat_enterprise/we_api/client/api/merchant/category.py
deleted file mode 100644
index b26377ed..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/category.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/common.py b/sg_wechat_enterprise/we_api/client/api/merchant/common.py
deleted file mode 100644
index 9730a311..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/common.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/express.py b/sg_wechat_enterprise/we_api/client/api/merchant/express.py
deleted file mode 100644
index 27fe2b22..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/express.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/group.py b/sg_wechat_enterprise/we_api/client/api/merchant/group.py
deleted file mode 100644
index 593b3751..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/group.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/order.py b/sg_wechat_enterprise/we_api/client/api/merchant/order.py
deleted file mode 100644
index 09d4b503..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/order.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- 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
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/shelf.py b/sg_wechat_enterprise/we_api/client/api/merchant/shelf.py
deleted file mode 100644
index 79354951..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/shelf.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- 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
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/merchant/stock.py b/sg_wechat_enterprise/we_api/client/api/merchant/stock.py
deleted file mode 100644
index bfe09de6..00000000
--- a/sg_wechat_enterprise/we_api/client/api/merchant/stock.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- 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
- }
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/message.py b/sg_wechat_enterprise/we_api/client/api/message.py
deleted file mode 100644
index e1fb3e44..00000000
--- a/sg_wechat_enterprise/we_api/client/api/message.py
+++ /dev/null
@@ -1,572 +0,0 @@
-# -*- 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
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/misc.py b/sg_wechat_enterprise/we_api/client/api/misc.py
deleted file mode 100644
index 75623e10..00000000
--- a/sg_wechat_enterprise/we_api/client/api/misc.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/poi.py b/sg_wechat_enterprise/we_api/client/api/poi.py
deleted file mode 100644
index a25ffc02..00000000
--- a/sg_wechat_enterprise/we_api/client/api/poi.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/qrcode.py b/sg_wechat_enterprise/we_api/client/api/qrcode.py
deleted file mode 100644
index 3edb7894..00000000
--- a/sg_wechat_enterprise/we_api/client/api/qrcode.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- 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)
diff --git a/sg_wechat_enterprise/we_api/client/api/scan.py b/sg_wechat_enterprise/we_api/client/api/scan.py
deleted file mode 100644
index 6bbf80f6..00000000
--- a/sg_wechat_enterprise/we_api/client/api/scan.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# -*- 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})
diff --git a/sg_wechat_enterprise/we_api/client/api/semantic.py b/sg_wechat_enterprise/we_api/client/api/semantic.py
deleted file mode 100644
index 6dc61252..00000000
--- a/sg_wechat_enterprise/we_api/client/api/semantic.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- 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
- )
diff --git a/sg_wechat_enterprise/we_api/client/api/shakearound.py b/sg_wechat_enterprise/we_api/client/api/shakearound.py
deleted file mode 100644
index 6f63dd9a..00000000
--- a/sg_wechat_enterprise/we_api/client/api/shakearound.py
+++ /dev/null
@@ -1,376 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/template.py b/sg_wechat_enterprise/we_api/client/api/template.py
deleted file mode 100644
index 46daa41b..00000000
--- a/sg_wechat_enterprise/we_api/client/api/template.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/user.py b/sg_wechat_enterprise/we_api/client/api/user.py
deleted file mode 100644
index 320c905e..00000000
--- a/sg_wechat_enterprise/we_api/client/api/user.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- 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
diff --git a/sg_wechat_enterprise/we_api/client/api/wifi.py b/sg_wechat_enterprise/we_api/client/api/wifi.py
deleted file mode 100644
index 1e8149e4..00000000
--- a/sg_wechat_enterprise/we_api/client/api/wifi.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# -*- 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: 模板ID,0-默认模板,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
diff --git a/sg_wechat_enterprise/we_api/client/base.py b/sg_wechat_enterprise/we_api/client/base.py
deleted file mode 100644
index 3a8edc49..00000000
--- a/sg_wechat_enterprise/we_api/client/base.py
+++ /dev/null
@@ -1,251 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import sys
-import time
-import inspect
-import logging
-
-import six
-import requests
-from wechatpy.utils import json, get_querystring
-from wechatpy.session.memorystorage import MemoryStorage
-from wechatpy.exceptions import WeChatClientException, APILimitedException
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-logger = logging.getLogger(__name__)
-
-
-def _is_api_endpoint(obj):
- return isinstance(obj, BaseWeChatAPI)
-
-
-class BaseWeChatClient(object):
-
- API_BASE_URL = ''
-
- def __new__(cls, *args, **kwargs):
- self = super(BaseWeChatClient, cls).__new__(cls)
- if sys.version_info[:2] == (2, 6):
- # Python 2.6 inspect.gemembers bug workaround
- # http://bugs.python.org/issue1785
- for _class in cls.__mro__:
- if issubclass(_class, BaseWeChatClient):
- for name, api in _class.__dict__.items():
- if isinstance(api, BaseWeChatAPI):
- api_cls = type(api)
- api = api_cls(self)
- setattr(self, name, api)
- else:
- api_endpoints = inspect.getmembers(self, _is_api_endpoint)
- for name, api in api_endpoints:
- api_cls = type(api)
- api = api_cls(self)
- setattr(self, name, api)
- return self
-
- def __init__(self, appid, access_token=None, session=None, timeout=None, auto_retry=True):
- self.appid = appid
- self.expires_at = None
- self.session = session or MemoryStorage()
- self.timeout = timeout
- self.auto_retry = auto_retry
-
- if isinstance(session, six.string_types):
- from shove import Shove
- from wechatpy.session.shovestorage import ShoveStorage
-
- querystring = get_querystring(session)
- prefix = querystring.get('prefix', ['wechatpy'])[0]
-
- shove = Shove(session)
- storage = ShoveStorage(shove, prefix)
- self.session = storage
-
- if access_token:
- self.session.set(self.access_token_key, access_token)
-
- @property
- def access_token_key(self):
- return '{0}_access_token'.format(self.appid)
-
- def _request(self, method, url_or_endpoint, **kwargs):
- if not url_or_endpoint.startswith(('http://', 'https://')):
- api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
- url = '{base}{endpoint}'.format(
- base=api_base_url,
- endpoint=url_or_endpoint
- )
- else:
- url = url_or_endpoint
-
- # 群发消息上传视频接口地址 HTTPS 证书错误,暂时忽略证书验证
- if url.startswith('https://file.api.weixin.qq.com'):
- kwargs['verify'] = False
-
- if 'params' not in kwargs:
- kwargs['params'] = {}
- if isinstance(kwargs['params'], dict) and \
- 'access_token' not in kwargs['params']:
- kwargs['params']['access_token'] = self.access_token
- if isinstance(kwargs.get('data', ''), dict):
- body = json.dumps(kwargs['data'], ensure_ascii=False)
- body = body.encode('utf-8')
- kwargs['data'] = body
-
- kwargs['timeout'] = kwargs.get('timeout', self.timeout)
- result_processor = kwargs.pop('result_processor', None)
- res = requests.request(
- method=method,
- url=url,
- **kwargs
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatClientException(
- errcode=None,
- errmsg=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
-
- return self._handle_result(
- res, method, url, result_processor, **kwargs
- )
-
- def _decode_result(self, res):
- try:
- result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
- except (TypeError, ValueError):
- # Return origin response object if we can not decode it as JSON
- logger.debug('Can not decode response as JSON', exc_info=True)
- return res
- return result
-
- def _handle_result(self, res, method=None, url=None,
- result_processor=None, **kwargs):
- if not isinstance(res, dict):
- # Dirty hack around asyncio based AsyncWeChatClient
- result = self._decode_result(res)
- else:
- result = res
-
- if not isinstance(result, dict):
- return result
-
- if 'base_resp' in result:
- # Different response in device APIs. Fuck tencent!
- result = result['base_resp']
- if 'errcode' in result:
- result['errcode'] = int(result['errcode'])
-
- if 'errcode' in result and result['errcode'] != 0:
- errcode = result['errcode']
- errmsg = result.get('errmsg', errcode)
- if errcode in (40001, 40014, 42001) and self.auto_retry:
- logger.info('Access token expired, fetch a new one and retry request')
- self.fetch_access_token()
- access_token = self.session.get(self.access_token_key)
- kwargs['params']['access_token'] = access_token
- return self._request(
- method=method,
- url_or_endpoint=url,
- result_processor=result_processor,
- **kwargs
- )
- elif errcode == 45009:
- # api freq out of limit
- raise APILimitedException(
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
- else:
- raise WeChatClientException(
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
-
- return result if not result_processor else result_processor(result)
-
- def get(self, url, **kwargs):
- return self._request(
- method='get',
- url_or_endpoint=url,
- **kwargs
- )
-
- _get = get
-
- def post(self, url, **kwargs):
- return self._request(
- method='post',
- url_or_endpoint=url,
- **kwargs
- )
-
- _post = post
-
- def _fetch_access_token(self, url, params):
- """ The real fetch access token """
- logger.info('Fetching access token')
- res = requests.get(
- url=url,
- params=params
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatClientException(
- errcode=None,
- errmsg=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
- result = res.json()
- if 'errcode' in result and result['errcode'] != 0:
- raise WeChatClientException(
- result['errcode'],
- result['errmsg'],
- client=self,
- request=res.request,
- response=res
- )
-
- expires_in = 7200
- if 'expires_in' in result:
- expires_in = result['expires_in']
- self.session.set(
- self.access_token_key,
- result['access_token'],
- expires_in
- )
- self.expires_at = int(time.time()) + expires_in
- return result
-
- def fetch_access_token(self):
- raise NotImplementedError()
-
- @property
- def access_token(self):
- """ WeChat access token """
- access_token = self.session.get(self.access_token_key)
- if access_token:
- if not self.expires_at:
- # user provided access_token, just return it
- return access_token
-
- timestamp = time.time()
- if self.expires_at - timestamp > 60:
- return access_token
-
- self.fetch_access_token()
- return self.session.get(self.access_token_key)
diff --git a/sg_wechat_enterprise/we_api/component.py b/sg_wechat_enterprise/we_api/component.py
deleted file mode 100644
index 79792961..00000000
--- a/sg_wechat_enterprise/we_api/component.py
+++ /dev/null
@@ -1,431 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.component
- ~~~~~~~~~~~~~~~
-
- This module provides client library for WeChat Open Platform
-
- :copyright: (c) 2015 by hunter007.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import time
-import logging
-
-import six
-import requests
-import xmltodict
-
-from wechatpy.utils import to_text, to_binary, get_querystring, json
-from wechatpy.fields import StringField, DateTimeField
-from wechatpy.messages import MessageMetaClass
-from wechatpy.session.memorystorage import MemoryStorage
-from wechatpy.exceptions import WeChatClientException, APILimitedException
-from wechatpy.crypto import WeChatCrypto
-from wechatpy.client import WeChatComponentClient
-
-
-logger = logging.getLogger(__name__)
-
-
-class BaseComponentMessage(six.with_metaclass(MessageMetaClass)):
- """Base class for all component messages and events"""
- type = 'unknown'
- appid = StringField('AppId')
- create_time = DateTimeField('CreateTime')
-
- def __init__(self, message):
- self._data = message
-
- def __repr__(self):
- _repr = "{klass}({msg})".format(
- klass=self.__class__.__name__,
- msg=repr(self._data)
- )
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
-
-
-class ComponentVerifyTicketMessage(BaseComponentMessage):
- """
- component_verify_ticket协议
- """
- type = 'component_verify_ticket'
- verify_ticket = StringField('ComponentVerifyTicket')
-
-
-class ComponentUnauthorizedMessage(BaseComponentMessage):
- """
- 取消授权通知
- """
- type = 'unauthorized'
- authorizer_appid = StringField('AuthorizerAppid')
-
-
-class BaseWeChatComponent(object):
-
- API_BASE_URL = 'https://api.weixin.qq.com/cgi-bin'
-
- def __init__(self,
- component_appid,
- component_appsecret,
- component_token,
- encoding_aes_key,
- session=None):
- """
- :param component_appid: 第三方平台appid
- :param component_appsecret: 第三方平台appsecret
- :param component_token: 公众号消息校验Token
- :param encoding_aes_key: 公众号消息加解密Key
- """
- self.component_appid = component_appid
- self.component_appsecret = component_appsecret
- self.expires_at = None
- self.crypto = WeChatCrypto(
- component_token, encoding_aes_key, component_appid)
- self.session = session or MemoryStorage()
-
- if isinstance(session, six.string_types):
- from shove import Shove
- from wechatpy.session.shovestorage import ShoveStorage
-
- querystring = get_querystring(session)
- prefix = querystring.get('prefix', ['wechatpy'])[0]
-
- shove = Shove(session)
- storage = ShoveStorage(shove, prefix)
- self.session = storage
-
- @property
- def component_verify_ticket(self):
- return self.session.get('component_verify_ticket')
-
- def _request(self, method, url_or_endpoint, **kwargs):
- if not url_or_endpoint.startswith(('http://', 'https://')):
- api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
- url = '{base}{endpoint}'.format(
- base=api_base_url,
- endpoint=url_or_endpoint
- )
- else:
- url = url_or_endpoint
-
- if 'params' not in kwargs:
- kwargs['params'] = {}
- if isinstance(kwargs['params'], dict) and \
- 'component_access_token' not in kwargs['params']:
- kwargs['params'][
- 'component_access_token'] = self.access_token
- if isinstance(kwargs['data'], dict):
- kwargs['data'] = json.dumps(kwargs['data'])
-
- res = requests.request(
- method=method,
- url=url,
- **kwargs
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatClientException(
- errcode=None,
- errmsg=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
-
- return self._handle_result(res, method, url, **kwargs)
-
- def _handle_result(self, res, method=None, url=None, **kwargs):
- result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
- if 'errcode' in result:
- result['errcode'] = int(result['errcode'])
-
- if 'errcode' in result and result['errcode'] != 0:
- errcode = result['errcode']
- errmsg = result['errmsg']
- if errcode == 42001:
- logger.info('Component access token expired, fetch a new one and retry request')
- self.fetch_component_access_token()
- kwargs['params']['component_access_token'] = self.session.get(
- 'component_access_token'
- )
- return self._request(
- method=method,
- url_or_endpoint=url,
- **kwargs
- )
- elif errcode == 45009:
- # api freq out of limit
- raise APILimitedException(
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
- else:
- raise WeChatClientException(
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
-
- return result
-
- def fetch_access_token(self):
- """
- 获取 component_access_token
- 详情请参考 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list\
- &t=resource/res_list&verify=1&id=open1419318587&token=&lang=zh_CN
-
- :return: 返回的 JSON 数据包
- """
- url = '{0}{1}'.format(
- self.API_BASE_URL,
- '/component/api_component_token'
- )
- return self._fetch_access_token(
- url=url,
- data=json.dumps({
- 'component_appid': self.component_appid,
- 'component_appsecret': self.component_appsecret,
- 'component_verify_ticket': self.component_verify_ticket
- })
- )
-
- def _fetch_access_token(self, url, data):
- """ The real fetch access token """
- logger.info('Fetching component access token')
- res = requests.post(
- url=url,
- data=data
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatClientException(
- errcode=None,
- errmsg=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
- result = res.json()
- if 'errcode' in result and result['errcode'] != 0:
- raise WeChatClientException(
- result['errcode'],
- result['errmsg'],
- client=self,
- request=res.request,
- response=res
- )
-
- expires_in = 7200
- if 'expires_in' in result:
- expires_in = result['expires_in']
- self.session.set(
- 'component_access_token',
- result['component_access_token'],
- expires_in
- )
- self.expires_at = int(time.time()) + expires_in
- return result
-
- @property
- def access_token(self):
- """ WeChat component access token """
- access_token = self.session.get('component_access_token')
- if access_token:
- if not self.expires_at:
- # user provided access_token, just return it
- return access_token
-
- timestamp = time.time()
- if self.expires_at - timestamp > 60:
- return access_token
-
- self.fetch_access_token()
- return self.session.get('component_access_token')
-
- def get(self, url, **kwargs):
- return self._request(
- method='get',
- url_or_endpoint=url,
- **kwargs
- )
-
- def post(self, url, **kwargs):
- return self._request(
- method='post',
- url_or_endpoint=url,
- **kwargs
- )
-
-
-class WeChatComponent(BaseWeChatComponent):
-
- def create_preauthcode(self):
- """
- 获取预授权码
- """
- return self.post(
- '/component/api_create_preauthcode',
- data={
- 'component_appid': self.component_appid
- }
- )
-
- def query_auth(self, authorization_code):
- """
- 使用授权码换取公众号的授权信息
-
- :params authorization_code: 授权code,会在授权成功时返回给第三方平台,详见第三方平台授权流程说明
- """
- return self.post(
- '/component/api_query_auth',
- data={
- 'component_appid': self.component_appid,
- 'authorization_code': authorization_code
- }
- )
-
- def refresh_authorizer_token(
- self, authorizer_appid, authorizer_refresh_token):
- """
- 获取(刷新)授权公众号的令牌
-
- :params authorizer_appid: 授权方appid
- :params authorizer_refresh_token: 授权方的刷新令牌
- """
- return self.post(
- '/component/api_authorizer_token',
- data={
- 'component_appid': self.component_appid,
- 'authorizer_appid': authorizer_appid,
- 'authorizer_refresh_token': authorizer_refresh_token
- }
- )
-
- def get_authorizer_info(self, authorizer_appid):
- """
- 获取授权方的账户信息
-
- :params authorizer_appid: 授权方appid
- """
- return self.post(
- '/component/api_get_authorizer_info',
- data={
- 'component_appid': self.component_appid,
- 'authorizer_appid': authorizer_appid,
- }
- )
-
- def get_authorizer_option(self, authorizer_appid, option_name):
- """
- 获取授权方的选项设置信息
-
- :params authorizer_appid: 授权公众号appid
- :params option_name: 选项名称
- """
- return self.post(
- '/component/api_get_authorizer_option',
- data={
- 'component_appid': self.component_appid,
- 'authorizer_appid': authorizer_appid,
- 'option_name': option_name
- }
- )
-
- def set_authorizer_option(
- self, authorizer_appid, option_name, option_value):
- """
- 设置授权方的选项信息
-
- :params authorizer_appid: 授权公众号appid
- :params option_name: 选项名称
- :params option_value: 设置的选项值
- """
- return self.post(
- '/component/api_set_authorizer_option',
- data={
- 'component_appid': self.component_appid,
- 'authorizer_appid': authorizer_appid,
- 'option_name': option_name,
- 'option_value': option_value
- }
- )
-
- def get_client_by_authorization_code(self, authorization_code):
- """
- 通过授权码直接获取 Client 对象
-
- :params authorization_code: 授权code,会在授权成功时返回给第三方平台,详见第三方平台授权流程说明
- """
- result = self.query_auth(authorization_code)
- access_token = result['authorization_info']['authorizer_access_token']
- refresh_token = result['authorization_info']['authorizer_refresh_token'] # NOQA
- authorizer_appid = result['authorization_info']['authorizer_appid'] # noqa
- return WeChatComponentClient(
- authorizer_appid, self, access_token, refresh_token,
- session=self.session
- )
-
- def get_client_by_appid(self, authorizer_appid):
- """
- 通过 authorizer_appid 获取 Client 对象
-
- :params authorizer_appid: 授权公众号appid
- """
- access_token_key = '{0}_access_token'.format(authorizer_appid)
- refresh_token_key = '{0}_refresh_token'.format(authorizer_appid)
- access_token = self.session.get(access_token_key)
- refresh_token = self.session.get(refresh_token_key)
-
- if not access_token:
- ret = self.refresh_authorizer_token(
- authorizer_appid,
- refresh_token
- )
- access_token = ret['authorizer_access_token']
- refresh_token = ret['authorizer_refresh_token']
-
- return WeChatComponentClient(
- authorizer_appid,
- self,
- access_token,
- refresh_token,
- session=self.session
- )
-
- def cache_component_verify_ticket(self, msg, signature, timestamp, nonce):
- """
- 处理 wechat server 推送的 component_verify_ticket消息
-
- :params msg: 加密内容
- :params signature: 消息签名
- :params timestamp: 时间戳
- :params nonce: 随机数
- """
- content = self.crypto.decrypt_message(msg, signature, timestamp, nonce)
- message = xmltodict.parse(to_text(content))['xml']
- o = ComponentVerifyTicketMessage(message)
- self.session.set(o.type, o.verify_ticket, 600)
-
- def get_unauthorized(self, msg, signature, timestamp, nonce):
- """
- 处理取消授权通知
-
- :params msg: 加密内容
- :params signature: 消息签名
- :params timestamp: 时间戳
- :params nonce: 随机数
- """
- content = self.crypto.decrypt_message(msg, signature, timestamp, nonce)
- message = xmltodict.parse(to_text(content))['xml']
- return ComponentUnauthorizedMessage(message)
diff --git a/sg_wechat_enterprise/we_api/crypto/__init__.py b/sg_wechat_enterprise/we_api/crypto/__init__.py
deleted file mode 100644
index ecdeb77c..00000000
--- a/sg_wechat_enterprise/we_api/crypto/__init__.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.crypto
- ~~~~~~~~~~~~~~~~
-
- This module provides some crypto tools for WeChat and WeChat enterprise
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import time
-import base64
-
-from wechatpy.utils import to_text, to_binary, WeChatSigner
-from wechatpy.exceptions import (
- InvalidAppIdException,
- InvalidSignatureException
-)
-from wechatpy.crypto.base import BasePrpCrypto
-
-
-def _get_signature(token, timestamp, nonce, encrypt):
- signer = WeChatSigner()
- signer.add_data(token, timestamp, nonce, encrypt)
- return signer.signature
-
-
-class PrpCrypto(BasePrpCrypto):
-
- def encrypt(self, text, app_id):
- return self._encrypt(text, app_id)
-
- def decrypt(self, text, app_id):
- return self._decrypt(text, app_id, InvalidAppIdException)
-
-
-class BaseWeChatCrypto(object):
-
- def __init__(self, token, encoding_aes_key, _id):
- encoding_aes_key = to_binary(encoding_aes_key + '=')
- self.key = base64.b64decode(encoding_aes_key)
- assert len(self.key) == 32
- self.token = token
- self._id = _id
-
- def _check_signature(self,
- signature,
- timestamp,
- nonce,
- echo_str,
- crypto_class=None):
- _signature = _get_signature(self.token, timestamp, nonce, echo_str)
- if _signature != signature:
- raise InvalidSignatureException()
- pc = crypto_class(self.key)
- return pc.decrypt(echo_str, self._id)
-
- def _encrypt_message(self,
- msg,
- nonce,
- timestamp=None,
- crypto_class=None):
- from wechatpy.replies import BaseReply
-
- xml = """
-
-
-{timestamp}
-
-"""
- if isinstance(msg, BaseReply):
- msg = msg.render()
- timestamp = timestamp or to_binary(int(time.time()))
- pc = crypto_class(self.key)
- encrypt = to_text(pc.encrypt(msg, self._id))
- signature = _get_signature(self.token, timestamp, nonce, encrypt)
- return to_text(xml.format(
- encrypt=encrypt,
- signature=signature,
- timestamp=timestamp,
- nonce=nonce
- ))
-
- def _decrypt_message(self,
- msg,
- signature,
- timestamp,
- nonce,
- crypto_class=None):
- if not isinstance(msg, dict):
- import xmltodict
-
- msg = xmltodict.parse(to_text(msg))['xml']
-
- encrypt = msg['Encrypt']
- _signature = _get_signature(self.token, timestamp, nonce, encrypt)
- if _signature != signature:
- raise InvalidSignatureException()
- pc = crypto_class(self.key)
- return pc.decrypt(encrypt, self._id)
-
-
-class WeChatCrypto(BaseWeChatCrypto):
-
- def __init__(self, token, encoding_aes_key, app_id):
- super(WeChatCrypto, self).__init__(token, encoding_aes_key, app_id)
- self.app_id = app_id
-
- def encrypt_message(self, msg, nonce, timestamp=None):
- return self._encrypt_message(msg, nonce, timestamp, PrpCrypto)
-
- def decrypt_message(self, msg, signature, timestamp, nonce):
- return self._decrypt_message(
- msg,
- signature,
- timestamp,
- nonce,
- PrpCrypto
- )
diff --git a/sg_wechat_enterprise/we_api/crypto/base.py b/sg_wechat_enterprise/we_api/crypto/base.py
deleted file mode 100644
index 7f27fc06..00000000
--- a/sg_wechat_enterprise/we_api/crypto/base.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import struct
-import socket
-import base64
-
-from wechatpy.utils import to_text, to_binary, random_string, byte2int
-from wechatpy.crypto.pkcs7 import PKCS7Encoder
-try:
- from wechatpy.crypto.cryptography import WeChatCipher
-except ImportError:
- try:
- from wechatpy.crypto.pycrypto import WeChatCipher
- except ImportError:
- raise Exception('You must install either cryptography or PyCrypto!')
-
-
-class BasePrpCrypto(object):
-
- def __init__(self, key):
- self.cipher = WeChatCipher(key)
-
- def get_random_string(self):
- return random_string(16)
-
- def _encrypt(self, text, _id):
- text = to_binary(text)
- tmp_list = []
- tmp_list.append(to_binary(self.get_random_string()))
- length = struct.pack(b'I', socket.htonl(len(text)))
- tmp_list.append(length)
- tmp_list.append(text)
- tmp_list.append(to_binary(_id))
-
- text = b''.join(tmp_list)
- text = PKCS7Encoder.encode(text)
-
- ciphertext = to_binary(self.cipher.encrypt(text))
- return base64.b64encode(ciphertext)
-
- def _decrypt(self, text, _id, exception=None):
- text = to_binary(text)
- plain_text = self.cipher.decrypt(base64.b64decode(text))
- padding = byte2int(plain_text[-1])
- content = plain_text[16:-padding]
- xml_length = socket.ntohl(struct.unpack(b'I', content[:4])[0])
- xml_content = to_text(content[4:xml_length + 4])
- from_id = to_text(content[xml_length + 4:])
- if from_id != _id:
- exception = exception or Exception
- raise exception()
- return xml_content
diff --git a/sg_wechat_enterprise/we_api/crypto/cryptography.py b/sg_wechat_enterprise/we_api/crypto/cryptography.py
deleted file mode 100644
index d9f60a03..00000000
--- a/sg_wechat_enterprise/we_api/crypto/cryptography.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-from cryptography.hazmat.backends import default_backend
-
-
-class WeChatCipher(object):
-
- def __init__(self, key):
- backend = default_backend()
- self.cipher = Cipher(
- algorithms.AES(key),
- modes.CBC(key[:16]),
- backend=backend
- )
-
- def encrypt(self, plaintext):
- encryptor = self.cipher.encryptor()
- return encryptor.update(plaintext) + encryptor.finalize()
-
- def decrypt(self, ciphertext):
- decryptor = self.cipher.decryptor()
- return decryptor.update(ciphertext) + decryptor.finalize()
diff --git a/sg_wechat_enterprise/we_api/crypto/pkcs7.py b/sg_wechat_enterprise/we_api/crypto/pkcs7.py
deleted file mode 100644
index 5278fdda..00000000
--- a/sg_wechat_enterprise/we_api/crypto/pkcs7.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.utils import to_binary, byte2int
-
-
-class PKCS7Encoder(object):
- block_size = 32
-
- @classmethod
- def encode(cls, text):
- length = len(text)
- padding_count = cls.block_size - length % cls.block_size
- if padding_count == 0:
- padding_count = cls.block_size
- padding = to_binary(chr(padding_count))
- return text + padding * padding_count
-
- @classmethod
- def decode(cls, decrypted):
- padding = byte2int(decrypted[-1])
- if padding < 1 or padding > 32:
- padding = 0
- return decrypted[:-padding]
diff --git a/sg_wechat_enterprise/we_api/crypto/pycrypto.py b/sg_wechat_enterprise/we_api/crypto/pycrypto.py
deleted file mode 100644
index 3a1e14a5..00000000
--- a/sg_wechat_enterprise/we_api/crypto/pycrypto.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from Crypto.Cipher import AES
-
-
-class WeChatCipher(object):
-
- def __init__(self, key):
- self.cipher = AES.new(key, AES.MODE_CBC, key[:16])
-
- def encrypt(self, plaintext):
- return self.cipher.encrypt(plaintext)
-
- def decrypt(self, ciphertext):
- return self.cipher.decrypt(ciphertext)
diff --git a/sg_wechat_enterprise/we_api/enterprise/__init__.py b/sg_wechat_enterprise/we_api/enterprise/__init__.py
deleted file mode 100644
index d28f7a91..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.enterprise.parser import parse_message # NOQA
-from wechatpy.enterprise.replies import create_reply # NOQA
-from wechatpy.enterprise.crypto import WeChatCrypto # NOQA
-from wechatpy.enterprise.client import WeChatClient # NOQA
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/__init__.py b/sg_wechat_enterprise/we_api/enterprise/client/__init__.py
deleted file mode 100644
index 5198a9d3..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/__init__.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.client.base import BaseWeChatClient
-from wechatpy.enterprise.client import api
-
-
-class WeChatClient(BaseWeChatClient):
-
- API_BASE_URL = 'https://qyapi.weixin.qq.com/cgi-bin/'
-
- user = api.WeChatUser()
- department = api.WeChatDepartment()
- menu = api.WeChatMenu()
- message = api.WeChatMessage()
- tag = api.WeChatTag()
- media = api.WeChatMedia()
- misc = api.WeChatMisc()
- agent = api.WeChatAgent()
- batch = api.WeChatBatch()
- jsapi = api.WeChatJSAPI()
- material = api.WeChatMaterial()
- oauth = api.WeChatOAuth()
- shakearound = api.WeChatShakeAround()
- service = api.WeChatService()
- chat = api.WeChatChat()
-
- def __init__(self, corp_id, secret, access_token=None,
- session=None, timeout=None, auto_retry=True):
- super(WeChatClient, self).__init__(
- corp_id, access_token, session, timeout, auto_retry
- )
- self.corp_id = corp_id
- self.secret = secret
-
- def fetch_access_token(self):
- """ Fetch access token"""
- return self._fetch_access_token(
- url='https://qyapi.weixin.qq.com/cgi-bin/gettoken',
- params={
- 'corpid': self.corp_id,
- 'corpsecret': self.secret
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/__init__.py b/sg_wechat_enterprise/we_api/enterprise/client/api/__init__.py
deleted file mode 100644
index 4c6232ec..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/__init__.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.enterprise.client.api.department import WeChatDepartment # NOQA
-from wechatpy.enterprise.client.api.media import WeChatMedia # NOQA
-from wechatpy.enterprise.client.api.message import WeChatMessage # NOQA
-from wechatpy.enterprise.client.api.menu import WeChatMenu # NOQA
-from wechatpy.enterprise.client.api.tag import WeChatTag # NOQA
-from wechatpy.enterprise.client.api.user import WeChatUser # NOQA
-from wechatpy.enterprise.client.api.misc import WeChatMisc # NOQA
-from wechatpy.enterprise.client.api.agent import WeChatAgent # NOQA
-from wechatpy.enterprise.client.api.batch import WeChatBatch # NOQA
-from wechatpy.enterprise.client.api.jsapi import WeChatJSAPI # NOQA
-from wechatpy.enterprise.client.api.material import WeChatMaterial # NOQA
-from wechatpy.enterprise.client.api.oauth import WeChatOAuth # NOQA
-from wechatpy.enterprise.client.api.shakearound import WeChatShakeAround # NOQA
-from wechatpy.enterprise.client.api.service import WeChatService # NOQA
-from wechatpy.enterprise.client.api.chat import WeChatChat # NOQA
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/agent.py b/sg_wechat_enterprise/we_api/enterprise/client/api/agent.py
deleted file mode 100644
index e9d3b0bd..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/agent.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatAgent(BaseWeChatAPI):
-
- def get(self, agent_id):
- """
- 获取企业号应用
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=获取企业号应用
-
- :param agent_id: 授权方应用 id
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'agent/get',
- params={
- 'agentid': agent_id
- }
- )
-
- def set(self,
- agent_id,
- name=None,
- description=None,
- redirect_domain=None,
- logo_media_id=None,
- report_location_flag=0,
- is_report_user=True,
- is_report_enter=True):
- """
- 设置企业号应用
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=设置企业号应用
-
- :param agent_id: 企业应用的 id
- :param name: 企业应用名称
- :param description: 企业应用详情
- :param redirect_domain: 企业应用可信域名
- :param logo_media_id: 企业应用头像的mediaid,通过多媒体接口上传图片获得mediaid
- :param report_location_flag: 企业应用是否打开地理位置上报 0:不上报;1:进入会话上报;2:持续上报
- :param is_report_user: 是否接收用户变更通知
- :param is_report_enter: 是否上报用户进入应用事件
- :return: 返回的 JSON 数据包
- """
- agent_data = optionaldict()
- agent_data['agentid'] = agent_id
- agent_data['name'] = name
- agent_data['description'] = description
- agent_data['redirect_domain'] = redirect_domain
- agent_data['logo_mediaid'] = logo_media_id
- agent_data['report_location_flag'] = report_location_flag
- agent_data['isreportuser'] = 1 if is_report_user else 0
- agent_data['isreportenter'] = 1 if is_report_enter else 0
- return self._post(
- 'agent/set',
- data=agent_data
- )
-
- def list(self):
- """
- 获取应用概况列表
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=获取应用概况列表
-
- :return: 应用概况列表
- """
- res = self._get('agent/list')
- return res['agentlist']
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/batch.py b/sg_wechat_enterprise/we_api/enterprise/client/api/batch.py
deleted file mode 100644
index db775f5f..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/batch.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-from wechatpy.utils import to_text
-
-
-class WeChatBatch(BaseWeChatAPI):
-
- def invite_user(self, url, token, encoding_aes_key, user_ids=None,
- party_ids=None, tag_ids=None, invite_tips=None):
- """
- 邀请成员关注
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
-
- :param url: 企业应用接收企业号推送请求的访问协议和地址,支持http或https协议
- :param token: 用于生成签名
- :param encoding_aes_key: 用于消息体的加密,是AES密钥的Base64编码
- :param user_ids: 可选,成员ID列表,多个接收者用‘|’分隔,最多支持1000个。
- :param party_ids: 可选,部门ID列表,多个接收者用‘|’分隔,最多支持100个。
- :param tag_ids: 可选,标签ID列表,多个接收者用‘|’分隔。
- :param invite_tips: 可选,推送到微信上的提示语
- :return: 返回的 JSON 数据包
- """
- data = optionaldict()
- data['callback'] = {
- 'url': url,
- 'token': token,
- 'encodingaeskey': encoding_aes_key
- }
- if isinstance(user_ids, (tuple, list)):
- user_ids = '|'.join(map(to_text, user_ids))
- if isinstance(party_ids, (tuple, list)):
- party_ids = '|'.join(map(to_text, party_ids))
- if isinstance(tag_ids, (tuple, list)):
- tag_ids = '|'.join(map(to_text, tag_ids))
- data['touser'] = user_ids
- data['toparty'] = party_ids
- data['totag'] = tag_ids
- data['invite_tips'] = invite_tips
- return self._post(
- 'batch/inviteuser',
- data=data
- )
-
- def sync_user(self, url, token, encoding_aes_key, media_id):
- """
- 增量更新成员
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
-
- :param url: 企业应用接收企业号推送请求的访问协议和地址,支持http或https协议
- :param token: 用于生成签名
- :param encoding_aes_key: 用于消息体的加密,是AES密钥的Base64编码
- :param media_id: 上传的csv文件的media_id
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'batch/syncuser',
- data={
- 'media_id': media_id,
- 'callback': {
- 'url': url,
- 'token': token,
- 'encodingaeskey': encoding_aes_key
- }
- }
- )
-
- def replace_user(self, url, token, encoding_aes_key, media_id):
- """
- 全量覆盖成员
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
-
- :param url: 企业应用接收企业号推送请求的访问协议和地址,支持http或https协议
- :param token: 用于生成签名
- :param encoding_aes_key: 用于消息体的加密,是AES密钥的Base64编码
- :param media_id: 上传的csv文件的media_id
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'batch/replaceuser',
- data={
- 'media_id': media_id,
- 'callback': {
- 'url': url,
- 'token': token,
- 'encodingaeskey': encoding_aes_key
- }
- }
- )
-
- def replace_party(self, url, token, encoding_aes_key, media_id):
- """
- 全量覆盖部门
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
-
- :param url: 企业应用接收企业号推送请求的访问协议和地址,支持http或https协议
- :param token: 用于生成签名
- :param encoding_aes_key: 用于消息体的加密,是AES密钥的Base64编码
- :param media_id: 上传的csv文件的media_id
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'batch/replaceparty',
- data={
- 'media_id': media_id,
- 'callback': {
- 'url': url,
- 'token': token,
- 'encodingaeskey': encoding_aes_key
- }
- }
- )
-
- def get_result(self, job_id):
- """
- 获取异步任务结果
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=异步任务接口
-
- :param job_id: 异步任务id,最大长度为64字符
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'batch/getresult',
- params={
- 'jobid': job_id
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/chat.py b/sg_wechat_enterprise/we_api/enterprise/client/api/chat.py
deleted file mode 100644
index 86c5eb18..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/chat.py
+++ /dev/null
@@ -1,273 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatChat(BaseWeChatAPI):
-
- def create(self, chat_id, name, owner, user_list):
- """
- 创建会话
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param chat_id: 会话id。字符串类型,最长32个字符。只允许字符0-9及字母a-zA-Z,
- 如果值内容为64bit无符号整型:要求值范围在[1, 2^63)之间,
- [2^63, 2^64)为系统分配会话id区间
- :param name: 会话标题
- :param owner: 管理员userid,必须是该会话userlist的成员之一
- :param user_list: 会话成员列表,成员用userid来标识。会话成员必须在3人或以上,1000人以下
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'chat/create',
- data={
- 'chatid': chat_id,
- 'name': name,
- 'owner': owner,
- 'userlist': user_list,
- }
- )
-
- def get(self, chat_id):
- """
- 获取会话
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param chat_id: 会话 ID
- :return: 会话信息
- """
- res = self._get('chat/get', params={'chatid': chat_id})
- return res['chat_info']
-
- def update(self, chat_id, op_user, name=None, owner=None,
- add_user_list=None, del_user_list=None):
- """
- 修改会话
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param chat_id: 会话 ID
- :param op_user: 操作人 userid
- :param name: 会话标题
- :param owner: 管理员userid,必须是该会话userlist的成员之一
- :param add_user_list: 会话新增成员列表,成员用userid来标识
- :param del_user_list: 会话退出成员列表,成员用userid来标识
- :return: 返回的 JSON 数据包
- """
- data = optionaldict(
- chatid=chat_id,
- op_user=op_user,
- name=name,
- owner=owner,
- add_user_list=add_user_list,
- del_user_list=del_user_list,
- )
- return self._post('chat/update', data=data)
-
- def quit(self, chat_id, op_user):
- """
- 退出会话
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param chat_id: 会话 ID
- :param op_user: 操作人 userid
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'chat/quit',
- data={
- 'chatid': chat_id,
- 'op_user': op_user,
- }
- )
-
- def clear_notify(self, op_user, type, id):
- """
- 清除会话未读状态
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param op_user: 会话所有者的userid
- :param type: 会话类型:single|group,分别表示:单聊|群聊
- :param id: 会话值,为userid|chatid,分别表示:成员id|会话id
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'chat/clearnotify',
- data={
- 'op_user': op_user,
- 'chat': {
- 'type': type,
- 'id': id,
- }
- }
- )
-
- def set_mute(self, user_mute_list):
- """
- 设置成员新消息免打扰
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param user_mute_list: 成员新消息免打扰参数,数组,最大支持10000个成员
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'chat/setmute',
- data={'user_mute_list': user_mute_list}
- )
-
- def send_text(self, sender, receiver_type, receiver_id, content):
- """
- 发送文本消息
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param sender: 发送人
- :param receiver_type: 接收人类型:single|group,分别表示:单聊|群聊
- :param receiver_id: 接收人的值,为userid|chatid,分别表示:成员id|会话id
- :param content: 消息内容
- :return: 返回的 JSON 数据包
- """
- data = {
- 'receiver': {
- 'type': receiver_type,
- 'id': receiver_id,
- },
- 'sender': sender,
- 'msgtype': 'text',
- 'text': {
- 'content': content,
- }
- }
- return self._post('chat/send', data=data)
-
- def send_single_text(self, sender, receiver, content):
- """
- 发送单聊文本消息
-
- :param sender: 发送人
- :param receiver: 接收人成员 ID
- :param content: 消息内容
- :return: 返回的 JSON 数据包
- """
- return self.send_text(sender, 'single', receiver, content)
-
- def send_group_text(self, sender, receiver, content):
- """
- 发送群聊文本消息
-
- :param sender: 发送人
- :param receiver: 会话 ID
- :param content: 消息内容
- :return: 返回的 JSON 数据包
- """
- return self.send_text(sender, 'group', receiver, content)
-
- def send_image(self, sender, receiver_type, receiver_id, media_id):
- """
- 发送图片消息
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param sender: 发送人
- :param receiver_type: 接收人类型:single|group,分别表示:单聊|群聊
- :param receiver_id: 接收人的值,为userid|chatid,分别表示:成员id|会话id
- :param media_id: 图片媒体文件id,可以调用上传素材文件接口获取
- :return: 返回的 JSON 数据包
- """
- data = {
- 'receiver': {
- 'type': receiver_type,
- 'id': receiver_id,
- },
- 'sender': sender,
- 'msgtype': 'image',
- 'image': {
- 'media_id': media_id,
- }
- }
- return self._post('chat/send', data=data)
-
- def send_single_image(self, sender, receiver, media_id):
- """
- 发送单聊图片消息
-
- :param sender: 发送人
- :param receiver: 接收人成员 ID
- :param media_id: 图片媒体文件id,可以调用上传素材文件接口获取
- :return: 返回的 JSON 数据包
- """
- return self.send_image(sender, 'single', receiver, media_id)
-
- def send_group_image(self, sender, receiver, media_id):
- """
- 发送群聊图片消息
-
- :param sender: 发送人
- :param receiver: 会话 ID
- :param media_id: 图片媒体文件id,可以调用上传素材文件接口获取
- :return: 返回的 JSON 数据包
- """
- return self.send_image(sender, 'group', receiver, media_id)
-
- def send_file(self, sender, receiver_type, receiver_id, media_id):
- """
- 发送文件消息
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=企业会话接口说明
-
- :param sender: 发送人
- :param receiver_type: 接收人类型:single|group,分别表示:单聊|群聊
- :param receiver_id: 接收人的值,为userid|chatid,分别表示:成员id|会话id
- :param media_id: 文件id,可以调用上传素材文件接口获取, 文件须大于4字节
- :return: 返回的 JSON 数据包
- """
- data = {
- 'receiver': {
- 'type': receiver_type,
- 'id': receiver_id,
- },
- 'sender': sender,
- 'msgtype': 'file',
- 'file': {
- 'media_id': media_id,
- }
- }
- return self._post('chat/send', data=data)
-
- def send_single_file(self, sender, receiver, media_id):
- """
- 发送单聊文件消息
-
- :param sender: 发送人
- :param receiver: 接收人成员 ID
- :param media_id: 文件id,可以调用上传素材文件接口获取, 文件须大于4字节
- :return: 返回的 JSON 数据包
- """
- return self.send_file(sender, 'single', receiver, media_id)
-
- def send_group_file(self, sender, receiver, media_id):
- """
- 发送群聊文件消息
-
- :param sender: 发送人
- :param receiver: 会话 ID
- :param media_id: 文件id,可以调用上传素材文件接口获取, 文件须大于4字节
- :return: 返回的 JSON 数据包
- """
- return self.send_file(sender, 'group', receiver, media_id)
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/department.py b/sg_wechat_enterprise/we_api/enterprise/client/api/department.py
deleted file mode 100644
index 096d6870..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/department.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatDepartment(BaseWeChatAPI):
-
- def create(self, name, parent_id=1, order=None, id=None):
- """
- 创建部门
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
-
- :param name: 部门名称,长度限制为 1~64 个字符
- :param parent_id: 父亲部门 id ,根部门 id 为 1
- :return: 返回的 JSON 数据包
- """
- department_data = optionaldict()
- department_data['name'] = name
- department_data['parentid'] = parent_id
- department_data['order'] = order
- department_data['id'] = id
- return self._post(
- 'department/create',
- data=dict(department_data)
- )
-
- def update(self, id, name=None, parent_id=None, order=None):
- """
- 更新部门
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
-
- :param id: 部门 id
- :param name: 部门名称
- :param parent_id: 父亲部门 id
- :param order: 在父部门中的次序,从 1 开始,数字越大排序越靠后
- :return: 返回的 JSON 数据包
- """
- department_data = optionaldict()
- department_data['id'] = id
- department_data['name'] = name
- department_data['parentid'] = parent_id
- department_data['order'] = order
- return self._post(
- 'department/update',
- data=dict(department_data)
- )
-
- def delete(self, id):
- """
- 删除部门
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
-
- :param id: 部门 id
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'department/delete',
- params={
- 'id': id
- }
- )
-
- def get(self):
- """
- 获取部门列表
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理部门
-
- :return: 部门列表
- """
- res = self._get('department/list')
- return res['department']
-
- def get_users(self, id, status=0, fetch_child=0):
- """
- 获取部门成员列表
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
-
- :param id: 部门 id
- :param status: 0 获取全部员工,1 获取已关注成员列表,
- 2 获取禁用成员列表,4 获取未关注成员列表。可叠加
- :param fetch_child: 1/0:是否递归获取子部门下面的成员
- :return: 部门成员列表
- """
- fetch_child = 1 if fetch_child else 0
- res = self._get(
- 'user/simplelist',
- params={
- 'department_id': id,
- 'status': status,
- 'fetch_child': fetch_child
- }
- )
- return res['userlist']
-
- def get_users_list(self, id, status=0, fetch_child=0):
- """
- 获取部门成员详情列表
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
-
- :param id: 部门 id
- :param status: 0 获取全部员工,1 获取已关注成员列表,
- 2 获取禁用成员列表,4 获取未关注成员列表。可叠加
- :param fetch_child: 1/0:是否递归获取子部门下面的成员
- :return: 部门成员列表
- """
- fetch_child = 1 if fetch_child else 0
- res = self._get(
- 'user/list',
- params={
- 'department_id': id,
- 'status': status,
- 'fetch_child': fetch_child
- }
- )
- return res['userlist']
\ No newline at end of file
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/jsapi.py b/sg_wechat_enterprise/we_api/enterprise/client/api/jsapi.py
deleted file mode 100644
index b0661ea2..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/jsapi.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import time
-
-from wechatpy.client.api.base import BaseWeChatAPI
-from wechatpy.utils import WeChatSigner
-
-
-class WeChatJSAPI(BaseWeChatAPI):
-
- def get_ticket(self):
- """
- 获取微信 JS-SDK ticket
-
- http://qydev.weixin.qq.com/wiki/index.php?title=%E5%BE%AE%E4%BF%A1JS%E6%8E%A5%E5%8F%A3
-
- :return: 返回的 JSON 数据包
- """
- return self._get('get_jsapi_ticket')
-
- def get_jsapi_ticket(self):
- """
- 获取微信 JS-SDK ticket
-
- 该方法会通过 session 对象自动缓存管理 ticket
-
- :return: ticket
- """
- ticket = self.session.get('jsapi_ticket')
- expires_at = self.session.get('jsapi_ticket_expires_at', 0)
- if not ticket or expires_at < int(time.time()):
- jsapi_ticket = self.get_ticket()
- ticket = jsapi_ticket['ticket']
- expires_at = int(time.time()) + int(jsapi_ticket['expires_in'])
- self.session.set('jsapi_ticket', ticket)
- self.session.set('jsapi_ticket_expires_at', expires_at)
- return ticket
-
- def get_jsapi_signature(self, noncestr, ticket, timestamp, url):
- data = [
- 'noncestr={noncestr}'.format(noncestr=noncestr),
- 'jsapi_ticket={ticket}'.format(ticket=ticket),
- 'timestamp={timestamp}'.format(timestamp=timestamp),
- 'url={url}'.format(url=url),
- ]
- signer = WeChatSigner(delimiter=b'&')
- signer.add_data(*data)
- return signer.signature
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/material.py b/sg_wechat_enterprise/we_api/enterprise/client/api/material.py
deleted file mode 100644
index b5547904..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/material.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# encoding: utf-8
-from __future__ import absolute_import, unicode_literals
-import requests
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatMaterial(BaseWeChatAPI):
-
- def add_articles(self, articles):
- """
- 新增永久图文素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param articles: 图文素材数组
- :return: 返回的 JSON 数据包
- """
- articles_data = []
- for article in articles:
- articles_data.append({
- 'thumb_media_id': article['thumb_media_id'],
- 'title': article['title'],
- 'content': article['content'],
- 'author': article.get('author', ''),
- 'content_source_url': article.get('content_source_url', ''),
- 'digest': article.get('digest', ''),
- 'show_cover_pic': article.get('show_cover_pic', '0')
- })
- return self._post(
- 'material/add_mpnews',
- data={
- "mpnews": {
- "articles": articles_data
- }
- }
- )
-
- def add(self, agent_id, media_type, media_file):
- """
- 新增其它类型永久素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param agent_id: 企业应用的id
- :param media_type: 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)普通文件(file)
- :param media_file: 要上传的文件,一个 File-object
- :return: 返回的 JSON 数据包
- """
- params = {
- 'agentid': agent_id,
- 'type': media_type,
- }
- return self._post(
- url='material/add_material',
- params=params,
- files={
- 'media': media_file
- }
- )
-
- def get_url(self, agent_id, media_id):
- """
- 获取永久素材下载地址
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param agent_id: 企业应用的id
- :param media_id: 媒体文件 ID
- :return: 临时素材下载地址
- """
- parts = (
- 'https://qyapi.weixin.qq.com/cgi-bin/material/get',
- '?access_token=',
- self.access_token,
- '&media_id=',
- media_id,
- '&agentid=',
- agent_id,
- )
- return ''.join(parts)
-
- def get(self, agent_id, media_id):
- """
- 获取永久素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param agent_id: 企业应用的id
- :param media_id: 媒体文件 ID
- :return: requests 的 Response 实例
- """
- res = requests.get(self.get_url(agent_id, media_id))
-
- return res
-
- def get_articles(self, agent_id, media_id):
- """
- 获取永久素材:图文消息素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param agent_id: 企业应用的id
- :param media_id: 媒体文件 ID
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'material/get',
- params={
- 'agentid': agent_id,
- 'media_id': media_id,
- }
- )
-
- def delete(self, agent_id, media_id):
- """
- 删除永久素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E5%88%A0%E9%99%A4%E6%B0%B8%E4%B9%85%E7%B4%A0%E6%9D%90
-
- :param agent_id: 企业应用的id
- :param media_id: 媒体文件 ID
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'material/del',
- params={
- 'agentid': agent_id,
- 'media_id': media_id,
- }
- )
-
- def update_articles(self, agent_id, media_id, articles):
- """
- 修改永久图文素材
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E4%BF%AE%E6%94%B9%E6%B0%B8%E4%B9%85%E5%9B%BE%E6%96%87%E7%B4%A0%E6%9D%90
-
- :param media_id: 要修改的图文消息的 id
- :param index: 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义),第一篇为 0
- :param articles: 图文素材数组
- :return: 返回的 JSON 数据包
- """
- articles_data = []
- for article in articles:
- articles_data.append({
- 'thumb_media_id': article['thumb_media_id'],
- 'title': article['title'],
- 'content': article['content'],
- 'author': article.get('author', ''),
- 'content_source_url': article.get('content_source_url', ''),
- 'digest': article.get('digest', ''),
- 'show_cover_pic': article.get('show_cover_pic', '0')
- })
- return self._post(
- 'material/update_news',
- data={
- 'agentid': agent_id,
- 'media_id': media_id,
- 'articles': articles_data
- }
- )
-
- def get_count(self, agent_id):
- """
- 获取素材总数
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%B4%A0%E6%9D%90%E6%80%BB%E6%95%B0
-
- :param agent_id: 企业应用的id
- :return: 返回的 JSON 数据包
- """
- return self._get(
- 'material/get_count',
- params={
- 'agent_id': agent_id,
- }
- )
-
- def batchget(self, agent_id, media_type, offset=0, count=20):
- """
- 批量获取永久素材列表
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E7%B4%A0%E6%9D%90%E5%88%97%E8%A1%A8
-
- :param agent_id: 企业应用的id
- :param media_type: 媒体文件类型,分别有图文(mpnews)、图片(image)、
- 语音(voice)、视频(video)和文件(file)
- :param offset: 从全部素材的该偏移位置开始返回,0 表示从第一个素材返回
- :param count: 返回素材的数量,取值在1到20之间
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'material/batchget',
- data={
- 'agent_id': agent_id,
- 'type': media_type,
- 'offset': offset,
- 'count': count
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/media.py b/sg_wechat_enterprise/we_api/enterprise/client/api/media.py
deleted file mode 100644
index 0541ee54..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/media.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import requests
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatMedia(BaseWeChatAPI):
-
- def upload(self, media_type, media_file):
- """
- 上传临时素材文件
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%8A%E4%BC%A0%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
-
- :param media_type: 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和普通文件(file)
- :param media_file: 要上传的文件,一个 File-object
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'media/upload',
- params={
- 'type': media_type
- },
- files={
- 'media': media_file
- }
- )
-
- def upload_image(self, media_file):
- """
- 上传卡券logo
- :param media_file: 要上传的文件
- :return:
- """
- return self._post(
- 'media/uploadimg',
- params={
- 'type': 'card_logo'
- },
- files={
- 'media': media_file
- }
- )
-
- def download(self, media_id):
- """
- 获取临时素材文件
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
-
- :param media_id: 媒体文件 ID
- :return: requests 的 Response 实例
- """
- return requests.get(self.get_url(media_id))
-
- def get_url(self, media_id):
- """
- 获取临时素材下载地址
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E4%B8%B4%E6%97%B6%E7%B4%A0%E6%9D%90%E6%96%87%E4%BB%B6
-
- :param media_id: 媒体文件 ID
- :return: 临时素材下载地址
- """
- parts = (
- 'https://qyapi.weixin.qq.com/cgi-bin/media/get',
- '?access_token=',
- self.access_token,
- '&media_id=',
- media_id
- )
- return ''.join(parts)
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/menu.py b/sg_wechat_enterprise/we_api/enterprise/client/api/menu.py
deleted file mode 100644
index 2b41f882..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/menu.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.client.api.base import BaseWeChatAPI
-from wechatpy.exceptions import WeChatClientException
-
-
-class WeChatMenu(BaseWeChatAPI):
-
- def create(self, agent_id, menu_data):
- return self._post(
- 'menu/create',
- params={
- 'agentid': agent_id
- },
- data=menu_data
- )
-
- def get(self, agent_id):
- try:
- return self._get(
- 'menu/get',
- params={
- 'agentid': agent_id
- }
- )
- except WeChatClientException as e:
- if e.errcode == 46003:
- # menu not exist
- return None
- else:
- raise e
-
- def delete(self, agent_id):
- return self._get(
- 'menu/delete',
- params={
- 'agentid': agent_id
- }
- )
-
- def update(self, agent_id, menu_data):
- self.delete(agent_id)
- return self.create(agent_id, menu_data)
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/message.py b/sg_wechat_enterprise/we_api/enterprise/client/api/message.py
deleted file mode 100644
index b6a93e28..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/message.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatMessage(BaseWeChatAPI):
-
- def _send_message(self, agent_id, user_ids, party_ids='',
- tag_ids='', msg=None):
- msg = msg or {}
- if isinstance(user_ids, (tuple, list)):
- user_ids = '|'.join(user_ids)
- if isinstance(party_ids, (tuple, list)):
- party_ids = '|'.join(party_ids)
- if isinstance(tag_ids, (tuple, list)):
- tag_ids = '|'.join(tag_ids)
-
- data = {
- 'touser': user_ids,
- 'toparty': party_ids,
- 'totag': tag_ids,
- 'agentid': agent_id
- }
- data.update(msg)
- return self._post(
- 'message/send',
- data=data
- )
-
- def send_text(self, agent_id, user_ids, content,
- party_ids='', tag_ids='', safe=0):
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'text',
- 'text': {'content': content},
- 'safe': safe
- }
- )
-
- def send_image(self, agent_id, user_ids, media_id,
- party_ids='', tag_ids='', safe=0):
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'image',
- 'image': {
- 'media_id': media_id
- },
- 'safe': safe
- }
- )
-
- def send_voice(self, agent_id, user_ids, media_id,
- party_ids='', tag_ids='', safe=0):
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'voice',
- 'voice': {
- 'media_id': media_id
- },
- 'safe': safe
- }
- )
-
- def send_video(self, agent_id, user_ids, media_id, title=None,
- description=None, party_ids='', tag_ids='', safe=0):
- video_data = optionaldict()
- video_data['media_id'] = media_id
- video_data['title'] = title
- video_data['description'] = description
-
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'video',
- 'video': dict(video_data),
- 'safe': safe
- }
- )
-
- def send_file(self, agent_id, user_ids, media_id,
- party_ids='', tag_ids='', safe=0):
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'file',
- 'file': {
- 'media_id': media_id
- },
- 'safe': safe
- }
- )
-
- def send_articles(self, agent_id, user_ids, articles,
- party_ids='', tag_ids=''):
- articles_data = []
- for article in articles:
- articles_data.append({
- 'title': article['title'],
- 'description': article['description'],
- 'url': article['url'],
- 'picurl': article['image']
- })
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'news',
- 'news': {
- 'articles': articles_data
- }
- }
- )
-
- def send_mp_articles(self, agent_id, user_ids, articles,
- party_ids='', tag_ids='', safe=0):
- articles_data = []
- for article in articles:
- articles_data.append({
- 'thumb_media_id': article['thumb_media_id'],
- 'author': article['author'],
- 'title': article['title'],
- 'content': article['content'],
- 'content_source_url': article['content_source_url'],
- 'digest': article['digest'],
- 'show_cover_pic': article['show_cover_pic']
- })
- return self._send_message(
- agent_id,
- user_ids,
- party_ids,
- tag_ids,
- msg={
- 'msgtype': 'mpnews',
- 'mpnews': {
- 'articles': articles_data
- },
- 'safe': safe
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/misc.py b/sg_wechat_enterprise/we_api/enterprise/client/api/misc.py
deleted file mode 100644
index bec63870..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/misc.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatMisc(BaseWeChatAPI):
-
- def get_wechat_ips(self):
- """
- 获取微信服务器 IP 列表
-
- :return: IP 地址列表
- """
- res = self._get('getcallbackip')
- return res['ip_list']
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/oauth.py b/sg_wechat_enterprise/we_api/enterprise/client/api/oauth.py
deleted file mode 100644
index cf9e40d7..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/oauth.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# encoding: utf-8
-from __future__ import absolute_import, unicode_literals
-import six
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatOAuth(BaseWeChatAPI):
-
- OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize'
-
- def authorize_url(self, redirect_uri, state=None):
- """
- 获取授权地址
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=OAuth%E9%AA%8C%E8%AF%81%E6%8E%A5%E5%8F%A3
-
- :param redirect_url: 授权后重定向的回调链接地址
- :param state: 重定向后会带上 state 参数
- :return: 返回的 JSON 数据包
- """
- redirect_uri = six.moves.urllib.parse.quote(redirect_uri)
- url_list = [
- self.OAUTH_BASE_URL,
- '?appid=',
- self._client.corp_id,
- '&redirect_uri=',
- redirect_uri,
- '&response_type=code&scope=snsapi_base',
- ]
- if state:
- url_list.extend(['&state=', state])
- url_list.append('#wechat_redirect')
- return ''.join(url_list)
-
- def get_user_info(self, code):
- """
- 根据 code 获取用户信息
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=OAuth%E9%AA%8C%E8%AF%81%E6%8E%A5%E5%8F%A3
-
- :param code: 通过成员授权获取到的code
- :return: 返回的 JSON 数据包
- """
-
- return self._get(
- 'user/getuserinfo',
- params={
- 'code': code,
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/service.py b/sg_wechat_enterprise/we_api/enterprise/client/api/service.py
deleted file mode 100644
index 07e2bcd8..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/service.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatService(BaseWeChatAPI):
-
- def get_provider_token(self, provider_secret):
- """
- 获取应用提供商凭证
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=获取应用提供商凭证
-
- :param provider_secret: 提供商的secret,在提供商管理页面可见
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'service/get_provider_token',
- data={
- 'corpid': self._client.corp_id,
- 'provider_secret': provider_secret,
- }
- )
-
- def get_login_info(self, provider_access_token, auth_code):
- """
- 获取企业号管理员登录信息
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=获取企业号管理员登录信息
-
- :param provider_access_token: 服务提供商的 accesstoken
- :param auth_code: OAuth 2.0 授权企业号管理员登录产生的 code
- :return: 返回的 JSON 数据包
- """
- return self._post(
- 'service/get_login_info',
- params={
- 'provider_access_token': provider_access_token,
- },
- data={
- 'auth_code': auth_code,
- }
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/shakearound.py b/sg_wechat_enterprise/we_api/enterprise/client/api/shakearound.py
deleted file mode 100644
index 27716498..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/shakearound.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatShakeAround(BaseWeChatAPI):
-
- def get_shake_info(self, ticket):
- """
- 获取摇周边的设备及用户信息
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=获取设备及用户信息
-
- :param ticket: 摇周边业务的ticket,可在摇到的 URL 中得到,ticket 生效时间为30分钟
- :return: 设备及用户信息
- """
- res = self._post(
- 'shakearound/getshakeinfo',
- data={
- 'ticket': ticket
- }
- )
- return res['data']
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/tag.py b/sg_wechat_enterprise/we_api/enterprise/client/api/tag.py
deleted file mode 100644
index c6be2733..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/tag.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatTag(BaseWeChatAPI):
-
- def create(self, name):
- return self._post(
- 'tag/create',
- data={
- 'tagname': name
- }
- )
-
- def update(self, tag_id, name):
- return self._post(
- 'tag/update',
- data={
- 'tagid': tag_id,
- 'tagname': name
- }
- )
-
- def delete(self, tag_id):
- return self._get(
- 'tag/delete',
- params={
- 'tagid': tag_id
- }
- )
-
- def get_users(self, tag_id):
- return self._get(
- 'tag/get',
- params={
- 'tagid': tag_id
- }
- )
-
- def add_users(self, tag_id, user_ids):
- return self._post(
- 'tag/addtagusers',
- data={
- 'tagid': tag_id,
- 'userlist': user_ids
- }
- )
-
- def delete_users(self, tag_id, user_ids):
- return self._post(
- 'tag/deltagusers',
- data={
- 'tagid': tag_id,
- 'userlist': user_ids
- }
- )
-
- def list(self):
- res = self._get('tag/list')
- return res['taglist']
diff --git a/sg_wechat_enterprise/we_api/enterprise/client/api/user.py b/sg_wechat_enterprise/we_api/enterprise/client/api/user.py
deleted file mode 100644
index 97b53b86..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/client/api/user.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from optionaldict import optionaldict
-
-from wechatpy.client.api.base import BaseWeChatAPI
-
-
-class WeChatUser(BaseWeChatAPI):
-
- def create(self, user_id, name, department=None, position=None,
- mobile=None, gender=0, tel=None, email=None,
- weixin_id=None, extattr=None, avatar_mediaid=None, to_invite=True):
- """
- 创建成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- user_data = optionaldict()
- user_data['userid'] = user_id
- user_data['name'] = name
- user_data['gender'] = gender
- user_data['department'] = department
- user_data['position'] = position
- user_data['mobile'] = mobile
- user_data['tel'] = tel
- user_data['email'] = email
- user_data['weixinid'] = weixin_id
- user_data['extattr'] = extattr
- user_data['avatar_mediaid'] = avatar_mediaid
- user_data['to_invite'] = to_invite
-
- return self._post(
- 'user/create',
- data=user_data
- )
-
- def update(self, user_id, name=None, department=None, position=None,
- mobile=None, gender=None, tel=None, email=None,
- weixin_id=None, enable=None, extattr=None, avatar_mediaid=None):
- """
- 更新成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- user_data = optionaldict()
- user_data['userid'] = user_id
- user_data['name'] = name
- user_data['gender'] = gender
- user_data['department'] = department
- user_data['position'] = position
- user_data['mobile'] = mobile
- user_data['tel'] = tel
- user_data['email'] = email
- user_data['weixinid'] = weixin_id
- user_data['extattr'] = extattr
- user_data['enable'] = enable
- user_data['avatar_mediaid'] = avatar_mediaid
-
- return self._post(
- 'user/update',
- data=user_data
- )
-
- def delete(self, user_id):
- """
- 删除成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- return self._get(
- 'user/delete',
- params={
- 'userid': user_id
- }
- )
-
- def get(self, user_id):
- """
- 获取成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- return self._get(
- 'user/get',
- params={
- 'userid': user_id
- }
- )
-
- def verify(self, user_id):
- return self._get(
- 'user/authsucc',
- params={
- 'userid': user_id
- }
- )
-
- def get_info(self, agent_id, code):
- return self._get(
- 'user/getuserinfo',
- params={
- 'agentid': agent_id,
- 'code': code
- }
- )
-
- def batch_delete(self, user_ids):
- """
- 批量删除成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- return self._post(
- 'user/batchdelete',
- data={
- 'useridlist': user_ids
- }
- )
-
- def list(self, department_id, fetch_child=False, status=0):
- """
- 批量获取部门成员
- 详情请参考 http://qydev.weixin.qq.com/wiki/index.php?title=管理成员
- """
- res = self._get(
- 'user/list',
- params={
- 'department_id': department_id,
- 'fetch_child': 1 if fetch_child else 0,
- 'status': status
- }
- )
- return res['userlist']
-
- def convert_to_openid(self, user_id, agent_id=None):
- """
- user_id 转成 openid
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=Userid%E4%B8%8Eopenid%E4%BA%92%E6%8D%A2%E6%8E%A5%E5%8F%A3
-
- :param user_id: 企业号内的成员 ID
- :param agent_id: 可选,需要发送红包的应用ID,若只是使用微信支付和企业转账,则无需该参数
- :return: 返回的 JSON 数据包
- """
- data = optionaldict()
- data['userid'] = user_id
- data['agentid'] = agent_id
- return self._post('user/convert_to_openid', data=data)
-
- def convert_to_user_id(self, openid):
- """
- openid 转成 user_id
-
- 详情请参考
- http://qydev.weixin.qq.com/wiki/index.php?title=Userid%E4%B8%8Eopenid%E4%BA%92%E6%8D%A2%E6%8E%A5%E5%8F%A3
-
- :param openid: 在使用微信支付、微信红包和企业转账之后,返回结果的openid
- :return: 该 openid 在企业号中对应的成员 user_id
- """
- res = self._post(
- 'user/convert_to_userid',
- data={'openid': openid}
- )
- return res['userid']
diff --git a/sg_wechat_enterprise/we_api/enterprise/crypto.py b/sg_wechat_enterprise/we_api/enterprise/crypto.py
deleted file mode 100644
index 7bd46f9c..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/crypto.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.crypto import BasePrpCrypto, BaseWeChatCrypto
-from wechatpy.enterprise.exceptions import InvalidCorpIdException
-
-
-class PrpCrypto(BasePrpCrypto):
-
- def encrypt(self, text, corp_id):
- return self._encrypt(text, corp_id)
-
- def decrypt(self, text, corp_id):
- return self._decrypt(text, corp_id, InvalidCorpIdException)
-
-
-class WeChatCrypto(BaseWeChatCrypto):
-
- def __init__(self, token, encoding_aes_key, corp_id):
- super(WeChatCrypto, self).__init__(token, encoding_aes_key, corp_id)
- self.corp_id = corp_id
-
- def check_signature(self, signature, timestamp, nonce, echo_str):
- return self._check_signature(
- signature,
- timestamp,
- nonce,
- echo_str,
- PrpCrypto
- )
-
- def encrypt_message(self, msg, nonce, timestamp=None):
- return self._encrypt_message(
- msg,
- nonce,
- timestamp,
- PrpCrypto
- )
-
- def decrypt_message(self, msg, signature, timestamp, nonce):
- return self._decrypt_message(
- msg,
- signature,
- timestamp,
- nonce,
- PrpCrypto
- )
diff --git a/sg_wechat_enterprise/we_api/enterprise/events.py b/sg_wechat_enterprise/we_api/enterprise/events.py
deleted file mode 100644
index 6b3ef752..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/events.py
+++ /dev/null
@@ -1,163 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.fields import IntegerField, BaseField
-from wechatpy import events
-
-
-EVENT_TYPES = {}
-
-
-def register_event(event_type):
- def register(cls):
- EVENT_TYPES[event_type] = cls
- return cls
- return register
-
-
-@register_event('subscribe')
-class SubscribeEvent(events.SubscribeEvent):
- """
- 成员关注事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('unsubscribe')
-class UnsubscribeEvent(events.UnsubscribeEvent):
- """
- 成员取消关注事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('click')
-class ClickEvent(events.ClickEvent):
- """
- 点击菜单拉取消息事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('xml')
-class ViewEvent(events.ViewEvent):
- """
- 点击菜单跳转链接事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('location')
-class LocationEvent(events.LocationEvent):
- """
- 上报地理位置事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('scancode_push')
-class ScanCodePushEvent(events.ScanCodePushEvent):
- """
- 扫码推事件的事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('scancode_waitmsg')
-class ScanCodeWaitMsgEvent(events.ScanCodeWaitMsgEvent):
- """
- 扫码推事件且弹出“消息接收中”提示框的事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('pic_sysphoto')
-class PicSysPhotoEvent(events.PicSysPhotoEvent):
- """
- 弹出系统拍照发图事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('pic_photo_or_album')
-class PicPhotoOrAlbumEvent(events.PicPhotoOrAlbumEvent):
- """
- 弹出拍照或相册发图事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('pic_weixin')
-class PicWeChatEvent(events.PicWeChatEvent):
- """
- 弹出微信相册发图器事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('location_select')
-class LocationSelectEvent(events.LocationSelectEvent):
- """
- 弹出地理位置选择器事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
-
-
-@register_event('enter_agent')
-class EnterAgentEvent(events.BaseEvent):
- """
- 用户进入应用的事件推送
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- agent = IntegerField('AgentID', 0)
- event = 'enter_agent'
-
-
-@register_event('batch_job_result')
-class BatchJobResultEvent(events.BaseEvent):
- """
- 异步任务完成事件
- 详情请参阅
- http://qydev.weixin.qq.com/wiki/index.php?title=接受事件
- """
- event = 'batch_job_result'
- batch_job = BaseField('BatchJob')
-
- @property
- def job_id(self):
- return self.batch_job['JobId']
-
- @property
- def job_type(self):
- return self.batch_job['JobType']
-
- @property
- def err_code(self):
- return self.batch_job['ErrCode']
-
- @property
- def err_msg(self):
- return self.batch_job['ErrMsg']
diff --git a/sg_wechat_enterprise/we_api/enterprise/exceptions.py b/sg_wechat_enterprise/we_api/enterprise/exceptions.py
deleted file mode 100644
index 80c136d8..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/exceptions.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.exceptions import WeChatException
-
-
-class InvalidCorpIdException(WeChatException):
-
- def __init__(self, errcode=-40005, errmsg='Invalid corp_id'):
- super(InvalidCorpIdException, self).__init__(errcode, errmsg)
diff --git a/sg_wechat_enterprise/we_api/enterprise/messages.py b/sg_wechat_enterprise/we_api/enterprise/messages.py
deleted file mode 100644
index 8ff46f89..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/messages.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.fields import IntegerField, StringField
-from wechatpy import messages
-
-
-MESSAGE_TYPES = {}
-
-
-def register_message(msg_type):
- def register(cls):
- MESSAGE_TYPES[msg_type] = cls
- return cls
- return register
-
-
-@register_message('text')
-class TextMessage(messages.TextMessage):
- agent = IntegerField('AgentID', 0)
-
-
-@register_message('image')
-class ImageMessage(messages.ImageMessage):
- agent = IntegerField('AgentID', 0)
-
-
-@register_message('voice')
-class VoiceMessage(messages.VoiceMessage):
- agent = IntegerField('AgentID', 0)
-
-
-@register_message('shortvideo')
-class ShortVideoMessage(messages.ShortVideoMessage):
- agent = IntegerField('AgentID', 0)
-
-
-@register_message('video')
-class VideoMessage(messages.VideoMessage):
- agent = IntegerField('AgentID', 0)
-
-
-@register_message('location')
-class LocationMessage(messages.LocationMessage):
- agent = IntegerField('AgentID', 0)
-
-@register_message('link')
-class LinkMessage(messages.LinkMessage):
- agent = IntegerField('AgentID', 0)
- pic_url = StringField('PicUrl')
diff --git a/sg_wechat_enterprise/we_api/enterprise/parser.py b/sg_wechat_enterprise/we_api/enterprise/parser.py
deleted file mode 100644
index a4b8b4e0..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/parser.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import xmltodict
-
-from wechatpy.messages import UnknownMessage
-from wechatpy.utils import to_text
-from wechatpy.enterprise.messages import MESSAGE_TYPES
-from wechatpy.enterprise.events import EVENT_TYPES
-
-
-def parse_message(xml):
- if not xml:
- return
- message = xmltodict.parse(to_text(xml))['xml']
- message_type = message['MsgType'].lower()
- if message_type == 'event':
- event_type = message['Event'].lower()
- message_class = EVENT_TYPES.get(event_type, UnknownMessage)
- else:
- message_class = MESSAGE_TYPES.get(message_type, UnknownMessage)
- return message_class(message)
diff --git a/sg_wechat_enterprise/we_api/enterprise/replies.py b/sg_wechat_enterprise/we_api/enterprise/replies.py
deleted file mode 100644
index 0597f13a..00000000
--- a/sg_wechat_enterprise/we_api/enterprise/replies.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import six
-
-from wechatpy import replies
-from wechatpy.fields import IntegerField
-
-
-REPLY_TYPES = {}
-
-
-def register_reply(reply_type):
- def register(cls):
- REPLY_TYPES[reply_type] = cls
- return cls
- return register
-
-
-@register_reply('text')
-class TextReply(replies.TextReply):
- agent = IntegerField('AgentID', 0)
-
-
-@register_reply('image')
-class ImageReply(replies.ImageReply):
- agent = IntegerField('AgentID', 0)
-
-
-@register_reply('voice')
-class VoiceReply(replies.VoiceReply):
- agent = IntegerField('AgentID', 0)
-
-
-@register_reply('video')
-class VideoReply(replies.VideoReply):
- agent = IntegerField('AgentID', 0)
-
-
-@register_reply('news')
-class ArticlesReply(replies.ArticlesReply):
- agent = IntegerField('AgentID', 0)
-
-
-def create_reply(reply, message=None, render=False):
- r = None
- if isinstance(reply, replies.BaseReply):
- r = reply
- if message:
- r.source = message.target
- r.target = message.source
- r.agent = message.agent
- elif isinstance(reply, six.string_types):
- r = TextReply(
- message=message,
- content=reply
- )
- elif isinstance(reply, (tuple, list)):
- if len(reply) > 10:
- raise AttributeError("Can't add more than 10 articles"
- " in an ArticlesReply")
- r = ArticlesReply(
- message=message,
- articles=reply
- )
- if r and render:
- return r.render()
- return r
diff --git a/sg_wechat_enterprise/we_api/events.py b/sg_wechat_enterprise/we_api/events.py
deleted file mode 100644
index ac2d217f..00000000
--- a/sg_wechat_enterprise/we_api/events.py
+++ /dev/null
@@ -1,629 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.events
- ~~~~~~~~~~~~~~~~
-
- This module contains all the events WeChat callback uses.
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-
-from wechatpy.fields import (
- StringField,
- FloatField,
- IntegerField,
- BaseField,
- Base64DecodeField,
- DateTimeField
-)
-from wechatpy.messages import BaseMessage
-
-
-EVENT_TYPES = {}
-
-
-def register_event(event_type):
- """
- Register the event class so that they can be accessed from EVENT_TYPES
-
- :param event_type: Event type
- """
- def register(cls):
- EVENT_TYPES[event_type] = cls
- return cls
- return register
-
-
-class BaseEvent(BaseMessage):
- """Base class for all events"""
- type = 'event'
- event = ''
-
-
-@register_event('subscribe')
-class SubscribeEvent(BaseEvent):
- """
- 用户关注事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'subscribe'
-
-
-@register_event('unsubscribe')
-class UnsubscribeEvent(BaseEvent):
- """
- 用户取消关注事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'unsubscribe'
-
-
-@register_event('subscribe_scan')
-class SubscribeScanEvent(BaseEvent):
- """
- 用户扫描二维码关注事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'subscribe_scan'
- scene_id = StringField('EventKey')
- ticket = StringField('Ticket')
-
-
-@register_event('scan')
-class ScanEvent(BaseEvent):
- """
- 用户扫描二维码事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'scan'
- scene_id = StringField('EventKey')
- ticket = StringField('Ticket')
-
-
-@register_event('location')
-class LocationEvent(BaseEvent):
- """
- 上报地理位置事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'location'
- latitude = FloatField('Latitude', 0.0)
- longitude = FloatField('Longitude', 0.0)
- precision = FloatField('Precision', 0.0)
-
-
-@register_event('click')
-class ClickEvent(BaseEvent):
- """
- 点击菜单拉取消息事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'click'
- key = StringField('EventKey')
-
-
-@register_event('xml')
-class ViewEvent(BaseEvent):
- """
- 点击菜单跳转链接事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html
- """
- event = 'xml'
- url = StringField('EventKey')
-
-
-@register_event('masssendjobfinish')
-class MassSendJobFinishEvent(BaseEvent):
- """
- 群发消息任务完成事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
- """
- id = IntegerField('MsgID', 0)
- event = 'masssendjobfinish'
- status = StringField('Status')
- total_count = IntegerField('TotalCount', 0)
- filter_count = IntegerField('FilterCount', 0)
- sent_count = IntegerField('SentCount', 0)
- error_count = IntegerField('ErrorCount', 0)
-
-
-@register_event('templatesendjobfinish')
-class TemplateSendJobFinishEvent(BaseEvent):
- """
- 模板消息任务完成事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/17/304c1885ea66dbedf7dc170d84999a9d.html
- """
- id = IntegerField('MsgID')
- event = 'templatesendjobfinish'
- status = StringField('Status')
-
-
-class BaseScanCodeEvent(BaseEvent):
- key = StringField('EventKey')
- scan_code_info = BaseField('ScanCodeInfo', {})
-
- @property
- def scan_type(self):
- return self.scan_code_info['ScanType']
-
- @property
- def scan_result(self):
- return self.scan_code_info['ScanResult']
-
-
-@register_event('scancode_push')
-class ScanCodePushEvent(BaseScanCodeEvent):
- """
- 扫码推事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'scancode_push'
-
-
-@register_event('scancode_waitmsg')
-class ScanCodeWaitMsgEvent(BaseScanCodeEvent):
- """
- 扫码推事件且弹出“消息接收中”提示框的事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'scancode_waitmsg'
-
-
-class BasePictureEvent(BaseEvent):
- key = StringField('EventKey')
- pictures_info = BaseField('SendPicsInfo', {})
-
- @property
- def count(self):
- return int(self.pictures_info['Count'])
-
- @property
- def pictures(self):
- if self.pictures_info['PicList']:
- items = self.pictures_info['PicList']['item']
- if self.count > 1:
- return items
- return [items]
- return []
-
-
-@register_event('pic_sysphoto')
-class PicSysPhotoEvent(BasePictureEvent):
- """
- 弹出系统拍照发图的事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'pic_sysphoto'
-
-
-@register_event('pic_photo_or_album')
-class PicPhotoOrAlbumEvent(BasePictureEvent):
- """
- 弹出拍照或者相册发图的事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'pic_photo_or_album'
-
-
-@register_event('pic_weixin')
-class PicWeChatEvent(BasePictureEvent):
- """
- 弹出微信相册发图器的事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'pic_weixin'
-
-
-@register_event('location_select')
-class LocationSelectEvent(BaseEvent):
- """
- 弹出地理位置选择器的事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/981d772286d10d153a3dc4286c1ee5b5.html
- """
- event = 'location_select'
- key = StringField('EventKey')
- location_info = BaseField('SendLocationInfo', {})
-
- @property
- def location_x(self):
- return self.location_info['Location_X']
-
- @property
- def location_y(self):
- return self.location_info['Location_Y']
-
- @property
- def location(self):
- return self.location_x, self.location_y
-
- @property
- def scale(self):
- return self.location_info['Scale']
-
- @property
- def label(self):
- return self.location_info['Label']
-
- @property
- def poiname(self):
- return self.location_info['Poiname']
-
-
-@register_event('card_pass_check')
-class CardPassCheckEvent(BaseEvent):
- event = 'card_pass_check'
- card_id = StringField('CardId')
-
-
-@register_event('card_not_pass_check')
-class CardNotPassCheckEvent(BaseEvent):
- event = 'card_not_pass_check'
- card_id = StringField('CardId')
-
-
-@register_event('user_get_card')
-class UserGetCardEvent(BaseEvent):
- """
- 领取事件推送
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/16/28b34ee91675a04cb24853768debded4.html#.E9.A2.86.E5.8F.96.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81
- """
- event = 'user_get_card'
- card_id = StringField('CardId')
- is_given_by_friend = IntegerField('IsGiveByFriend')
- friend = StringField('FriendUserName')
- code = StringField('UserCardCode')
- old_code = StringField('OldUserCardCode')
- outer_id = StringField('OuterId')
-
-
-@register_event('user_del_card')
-class UserDeleteCardEvent(BaseEvent):
- """
- 卡券删除事件推送
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/16/28b34ee91675a04cb24853768debded4.html#.E5.88.A0.E9.99.A4.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81
- """
- event = 'user_del_card'
- card_id = StringField('CardId')
- code = StringField('UserCardCode')
-
-
-@register_event('user_consume_card')
-class UserConsumeCardEvent(BaseEvent):
- """
- 卡券核销事件推送
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/16/28b34ee91675a04cb24853768debded4.html#.E6.A0.B8.E9.94.80.E4.BA.8B.E4.BB.B6.E6.8E.A8.E9.80.81
- """
- event = 'user_consume_card'
- card_id = StringField('CardId')
- code = StringField('UserCardCode')
- consume_source = StringField('ConsumeSource')
- location_id = StringField('LocationId')
- staff = StringField('StaffOpenId')
-
-
-@register_event('merchant_order')
-class MerchantOrderEvent(BaseEvent):
- event = 'merchant_order'
- order_id = StringField('OrderId')
- order_status = IntegerField('OrderStatus')
- product_id = StringField('ProductId')
- sku_info = StringField('SkuInfo')
-
-
-@register_event('kf_create_session')
-class KfCreateSessionEvent(BaseEvent):
- event = 'kf_create_session'
- account = StringField('KfAccount')
-
-
-@register_event('kf_close_session')
-class KfCloseSessionEvent(BaseEvent):
- event = 'kf_close_session'
- account = StringField('KfAccount')
-
-
-@register_event('kf_switch_session')
-class KfSwitchSessionEvent(BaseEvent):
- event = 'kf_switch_session'
- from_account = StringField('FromKfAccount')
- to_account = StringField('ToKfAccount')
-
-
-@register_event('device_text')
-class DeviceTextEvent(BaseEvent):
- event = 'device_text'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- session_id = StringField('SessionID')
- content = Base64DecodeField('Content')
-
-
-@register_event('device_bind')
-class DeviceBindEvent(BaseEvent):
- event = 'bind'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- session_id = StringField('SessionID')
- content = Base64DecodeField('Content')
- open_id = StringField('OpenID')
-
-
-@register_event('device_unbind')
-class DeviceUnbindEvent(BaseEvent):
- event = 'unbind'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- session_id = StringField('SessionID')
- content = Base64DecodeField('Content')
- open_id = StringField('OpenID')
-
-
-@register_event('device_subscribe_status')
-class DeviceSubscribeStatusEvent(BaseEvent):
- event = 'subscribe_status'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- open_id = StringField('OpenID')
- op_type = IntegerField('OpType')
-
-
-@register_event('device_unsubscribe_status')
-class DeviceUnsubscribeStatusEvent(BaseEvent):
- event = 'subscribe_status'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- open_id = StringField('OpenID')
- op_type = IntegerField('OpType')
-
-
-@register_event('shakearoundusershake')
-class ShakearoundUserShakeEvent(BaseEvent):
- event = 'shakearound_user_shake'
- _chosen_beacon = BaseField('ChosenBeacon', {})
- _around_beacons = BaseField('AroundBeacons', {})
-
- @property
- def chosen_beacon(self):
- beacon = self._chosen_beacon
- if not beacon:
- return {}
- return {
- 'uuid': beacon['Uuid'],
- 'major': beacon['Major'],
- 'minor': beacon['Minor'],
- 'distance': float(beacon['Distance']),
- }
-
- @property
- def around_beacons(self):
- beacons = self._around_beacons
- if not beacons:
- return []
-
- ret = []
- for beacon in beacons['AroundBeacon']:
- ret.append({
- 'uuid': beacon['Uuid'],
- 'major': beacon['Major'],
- 'minor': beacon['Minor'],
- 'distance': float(beacon['Distance']),
- })
- return ret
-
-
-@register_event('poi_check_notify')
-class PoiCheckNotifyEvent(BaseEvent):
- event = 'poi_check_notify'
- poi_id = StringField('PoiId')
- uniq_id = StringField('UniqId')
- result = StringField('Result')
- message = StringField('Msg')
-
-
-@register_event('wificonnected')
-class WiFiConnectedEvent(BaseEvent):
- event = 'wificconnected'
- connect_time = IntegerField('ConnectTime')
- expire_time = IntegerField('ExpireTime')
- vendor_id = StringField('VendorId')
- shop_id = StringField('PlaceId')
- bssid = StringField('DeviceNo')
-
-
-# ============================================================================
-# 微信认证事件推送
-# ============================================================================
-@register_event('qualification_verify_success')
-class QualificationVerifySuccessEvent(BaseEvent):
- """
- 资质认证成功事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'qualification_verify_success'
- expired_time = DateTimeField('ExpiredTime')
-
-
-@register_event('qualification_verify_fail')
-class QualificationVerifyFailEvent(BaseEvent):
- """
- 资质认证失败事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'qualification_verify_fail'
- fail_time = DateTimeField('FailTime')
- fail_reason = StringField('FailReason')
-
-
-@register_event('naming_verify_success')
-class NamingVerifySuccessEvent(BaseEvent):
- """
- 名称认证成功事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'naming_verify_success'
- expired_time = DateTimeField('ExpiredTime')
-
-
-@register_event('naming_verify_fail')
-class NamingVerifyFailEvent(BaseEvent):
- """
- 名称认证失败事件
-
- 客户端不打勾,但仍有接口权限。详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'naming_verify_fail'
- fail_time = DateTimeField('FailTime')
- fail_reason = StringField('FailReason')
-
-
-@register_event('annual_renew')
-class AnnualRenewEvent(BaseEvent):
- """
- 年审通知事件
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'annual_renew'
- expired_time = DateTimeField('ExpiredTime')
-
-
-@register_event('verify_expired')
-class VerifyExpiredEvent(BaseEvent):
- """
- 认证过期失效通知
-
- 详情请参阅
- http://mp.weixin.qq.com/wiki/1/7f81dec16b801b34629091094c099439.html
- """
- event = 'verify_expired'
- expired_time = DateTimeField('ExpiredTime')
-
-
-@register_event('user_scan_product')
-class UserScanProductEvent(BaseEvent):
- """
- 打开商品主页事件
-
- 详情请参考
- http://mp.weixin.qq.com/wiki/15/f4109a5e44b4bfbc7eb1337eb739f3e3.html
- """
- event = 'user_scan_product'
- standard = StringField('KeyStandard')
- key = StringField('KeyStr')
- country = StringField('Country')
- province = StringField('Province')
- city = StringField('City')
- sex = IntegerField('Sex')
- scene = IntegerField('Scene')
-
-
-@register_event('user_scan_product_enter_session')
-class UserScanProductEnterSessionEvent(BaseEvent):
- """
- 进入公众号事件
-
- 详情请参考
- http://mp.weixin.qq.com/wiki/15/f4109a5e44b4bfbc7eb1337eb739f3e3.html
- """
- event = 'user_scan_product_enter_session'
- standard = StringField('KeyStandard')
- key = StringField('KeyStr')
-
-
-@register_event('user_scan_product_async')
-class UserScanProductAsyncEvent(BaseEvent):
- """
- 地理位置信息异步推送事件
-
- 详情请参考
- http://mp.weixin.qq.com/wiki/15/f4109a5e44b4bfbc7eb1337eb739f3e3.html
- """
- event = 'user_scan_product_async'
- standard = StringField('KeyStandard')
- key = StringField('KeyStr')
- region_code = StringField('RegionCode')
-
-
-@register_event('user_scan_product_verify_action')
-class UserScanProductVerifyActionEvent(BaseEvent):
- """
- 商品审核结果事件
-
- 详情请参考
- http://mp.weixin.qq.com/wiki/15/f4109a5e44b4bfbc7eb1337eb739f3e3.html
- """
- event = 'user_scan_product_verify_action'
- standard = StringField('KeyStandard')
- key = StringField('KeyStr')
- result = StringField('Result')
- reason = StringField('ReasonMsg')
-
-
-@register_event('subscribe_scan_product')
-class SubscribeScanProductEvent(BaseEvent):
- """
- 用户在商品主页中关注公众号事件
-
- 详情请参考
- http://mp.weixin.qq.com/wiki/15/f4109a5e44b4bfbc7eb1337eb739f3e3.html
- """
- event = 'subscribe_scan_product'
- event_key = StringField('EventKey')
-
- @property
- def scene(self):
- return self.event_key.split('|', 1)[0]
-
- @property
- def standard(self):
- return self.event_key.split('|')[1]
-
- @property
- def key(self):
- return self.event_key.split('|')[2]
diff --git a/sg_wechat_enterprise/we_api/exceptions.py b/sg_wechat_enterprise/we_api/exceptions.py
deleted file mode 100644
index 891079c8..00000000
--- a/sg_wechat_enterprise/we_api/exceptions.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.exceptions
- ~~~~~~~~~~~~~~~~~~~~
-
- Basic exceptions definition.
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import six
-
-from wechatpy.utils import to_binary, to_text
-
-
-class WeChatException(Exception):
- """Base exception for wechatpy"""
-
- def __init__(self, errcode, errmsg):
- """
- :param errcode: Error code
- :param errmsg: Error message
- """
- self.errcode = errcode
- self.errmsg = errmsg
-
- def __str__(self):
- if six.PY2:
- return to_binary('Error code: {code}, message: {msg}'.format(
- code=self.errcode,
- msg=self.errmsg
- ))
- else:
- return to_text('Error code: {code}, message: {msg}'.format(
- code=self.errcode,
- msg=self.errmsg
- ))
-
- def __repr__(self):
- _repr = '{klass}({code}, {msg}'.format(
- klass=self.__class__.__name__,
- code=self.errcode,
- msg=self.errmsg
- )
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
-
-
-class WeChatClientException(WeChatException):
- """WeChat API client exception class"""
- def __init__(self, errcode, errmsg, client=None,
- request=None, response=None):
- super(WeChatClientException, self).__init__(errcode, errmsg)
- self.client = client
- self.request = request
- self.response = response
-
-
-class InvalidSignatureException(WeChatException):
- """Invalid signature exception class"""
-
- def __init__(self, errcode=-40001, errmsg='Invalid signature'):
- super(InvalidSignatureException, self).__init__(errcode, errmsg)
-
-
-class APILimitedException(WeChatClientException):
- """WeChat API call limited exception class"""
- pass
-
-
-class InvalidAppIdException(WeChatException):
- """Invalid app_id exception class"""
-
- def __init__(self, errcode=-40005, errmsg='Invalid AppId'):
- super(InvalidAppIdException, self).__init__(errcode, errmsg)
-
-
-class WeChatOAuthException(WeChatClientException):
- """WeChat OAuth API exception class"""
- pass
-
-
-class WeChatPayException(WeChatClientException):
- """WeChat Pay API exception class"""
- def __init__(self, return_code, result_code=None, return_msg=None,
- errcode=None, errmsg=None, client=None,
- request=None, response=None):
- """
- :param return_code: 返回状态码
- :param result_code: 业务结果
- :param return_msg: 返回信息
- :param errcode: 错误代码
- :param errmsg: 错误代码描述
- """
- super(WeChatPayException, self).__init__(
- errcode,
- errmsg,
- client,
- request,
- response
- )
- self.return_code = return_code
- self.result_code = result_code
- self.return_msg = return_msg
-
- def __str__(self):
- if six.PY2:
- return to_binary('Error code: {code}, message: {msg}'.format(
- code=self.return_code,
- msg=self.return_msg
- ))
- else:
- return to_text('Error code: {code}, message: {msg}'.format(
- code=self.return_code,
- msg=self.return_msg
- ))
-
- def __repr__(self):
- _repr = '{klass}({code}, {msg})'.format(
- klass=self.__class__.__name__,
- code=self.return_code,
- msg=self.return_msg
- )
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
diff --git a/sg_wechat_enterprise/we_api/fields.py b/sg_wechat_enterprise/we_api/fields.py
deleted file mode 100644
index 3797a6a3..00000000
--- a/sg_wechat_enterprise/we_api/fields.py
+++ /dev/null
@@ -1,237 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.fields
- ~~~~~~~~~~~~~~~~
-
- This module defines some useful field types for parse WeChat messages
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import time
-from datetime import datetime
-import base64
-import copy
-
-import six
-
-from wechatpy.utils import to_text, to_binary, ObjectDict, timezone
-
-default_timezone = timezone('Asia/Shanghai')
-
-
-class FieldDescriptor(object):
- def __init__(self, field):
- self.field = field
- self.attr_name = field.name
-
- def __get__(self, instance, instance_type=None):
- if instance is not None:
- value = instance._data.get(self.attr_name)
- if value is None:
- value = copy.deepcopy(self.field.default)
- instance._data[self.attr_name] = value
- if isinstance(value, dict):
- value = ObjectDict(value)
- if value and not isinstance(value, (dict, list, tuple)) and \
- six.callable(self.field.converter):
- value = self.field.converter(value)
- return value
- return self.field
-
- def __set__(self, instance, value):
- instance._data[self.attr_name] = value
-
-
-class BaseField(object):
- converter = None
-
- def __init__(self, name, default=None):
- self.name = name
- self.default = default
-
- def to_xml(self, value):
- raise NotImplementedError()
-
- def __repr__(self):
- _repr = '{klass}({name})'.format(
- klass=self.__class__.__name__,
- name=repr(self.name)
- )
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
-
- def add_to_class(self, klass, name):
- self.klass = klass
- klass._fields[name] = self
- setattr(klass, name, FieldDescriptor(self))
-
-
-class StringField(BaseField):
- def __to_text(self, value):
- return to_text(value)
-
- converter = __to_text
-
- def to_xml(self, value):
- value = self.converter(value)
- tpl = '<{name}>{name}>'
- return tpl.format(name=self.name, value=value)
-
-
-class IntegerField(BaseField):
- converter = int
-
- def to_xml(self, value):
- value = self.converter(value) if value is not None else self.default
- tpl = '<{name}>{value}{name}>'
- return tpl.format(name=self.name, value=value)
-
-
-class DateTimeField(BaseField):
- def __converter(self, value):
- v = int(value)
- return datetime.fromtimestamp(v, tz=default_timezone)
-
- converter = __converter
-
- def to_xml(self, value):
- value = time.mktime(datetime.timetuple(value))
- value = int(value)
- tpl = '<{name}>{value}{name}>'
- return tpl.format(name=self.name, value=value)
-
-
-class FloatField(BaseField):
- converter = float
-
- def to_xml(self, value):
- value = self.converter(value) if value is not None else self.default
- tpl = '<{name}>{value}{name}>'
- return tpl.format(name=self.name, value=value)
-
-
-class ImageField(StringField):
- def to_xml(self, value):
- value = self.converter(value)
- tpl = """
-
- """
- return tpl.format(value=value)
-
-
-class VoiceField(StringField):
- def to_xml(self, value):
- value = self.converter(value)
- tpl = """
-
- """
- return tpl.format(value=value)
-
-
-class VideoField(StringField):
- def to_xml(self, value):
- media_id = self.converter(value['media_id'])
- if 'title' in value:
- title = self.converter(value['title'])
- if 'description' in value:
- description = self.converter(value['description'])
- tpl = """"""
- return tpl.format(
- media_id=media_id,
- title=title,
- description=description
- )
-
-
-class MusicField(StringField):
- def to_xml(self, value):
- thumb_media_id = self.converter(value['thumb_media_id'])
- if 'title' in value:
- title = self.converter(value['title'])
- if 'description' in value:
- description = self.converter(value['description'])
- if 'music_url' in value:
- music_url = self.converter(value['music_url'])
- if 'hq_music_url' in value:
- hq_music_url = self.converter(value['hq_music_url'])
- tpl = """
-
-
-
-
-
- """
- return tpl.format(
- thumb_media_id=thumb_media_id,
- title=title,
- description=description,
- music_url=music_url,
- hq_music_url=hq_music_url
- )
-
-
-class ArticlesField(StringField):
- def to_xml(self, articles):
- article_count = len(articles)
- items = []
- for article in articles:
- title = self.converter(article.get('title', ''))
- description = self.converter(article.get('description', ''))
- image = self.converter(article.get('image', ''))
- url = self.converter(article.get('url', ''))
- item_tpl = """-
-
-
-
-
-
"""
- item = item_tpl.format(
- title=title,
- description=description,
- image=image,
- url=url
- )
- items.append(item)
- items_str = '\n'.join(items)
- tpl = """{article_count}
- {items}"""
- return tpl.format(
- article_count=article_count,
- items=items_str
- )
-
-
-class Base64EncodeField(StringField):
- def __base64_encode(self, text):
- return to_text(base64.b64encode(to_binary(text)))
-
- converter = __base64_encode
-
-
-class Base64DecodeField(StringField):
- def __base64_decode(self, text):
- return to_text(base64.b64decode(to_binary(text)))
-
- converter = __base64_decode
-
-
-class HardwareField(StringField):
- def to_xml(self, value=None):
- value = value or {'xml': 'myrank', 'action': 'ranklist'}
- tpl = """<{name}>
-
-
- {name}>"""
- return tpl.format(
- name=self.name,
- view=value.get('xml'),
- action=value.get('action')
- )
diff --git a/sg_wechat_enterprise/we_api/messages.py b/sg_wechat_enterprise/we_api/messages.py
deleted file mode 100644
index b53c6d1b..00000000
--- a/sg_wechat_enterprise/we_api/messages.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.messages
- ~~~~~~~~~~~~~~~~~~
-
- This module defines all the messages you can get from WeChat server
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import copy
-import six
-
-from wechatpy.fields import (
- BaseField,
- StringField,
- IntegerField,
- DateTimeField,
- FieldDescriptor
-)
-from wechatpy.utils import to_text, to_binary
-
-
-MESSAGE_TYPES = {}
-
-
-def register_message(msg_type):
- def register(cls):
- MESSAGE_TYPES[msg_type] = cls
- return cls
- return register
-
-
-class MessageMetaClass(type):
- """Metaclass for all messages"""
- def __new__(cls, name, bases, attrs):
- for b in bases:
- if not hasattr(b, '_fields'):
- continue
-
- for k, v in b.__dict__.items():
- if k in attrs:
- continue
- if isinstance(v, FieldDescriptor):
- attrs[k] = copy.deepcopy(v.field)
-
- cls = super(MessageMetaClass, cls).__new__(cls, name, bases, attrs)
- cls._fields = {}
-
- for name, field in cls.__dict__.items():
- if isinstance(field, BaseField):
- field.add_to_class(cls, name)
- return cls
-
-
-class BaseMessage(six.with_metaclass(MessageMetaClass)):
- """Base class for all messages and events"""
- type = 'unknown'
- id = IntegerField('MsgId', 0)
- source = StringField('FromUserName')
- target = StringField('ToUserName')
- create_time = DateTimeField('CreateTime')
- time = IntegerField('CreateTime')
-
- def __init__(self, message):
- self._data = message
-
- def __repr__(self):
- _repr = "{klass}({msg})".format(
- klass=self.__class__.__name__,
- msg=repr(self._data)
- )
- if six.PY2:
- return to_binary(_repr)
- else:
- return to_text(_repr)
-
-
-@register_message('text')
-class TextMessage(BaseMessage):
- """
- 文本消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'text'
- content = StringField('Content')
-
-
-@register_message('image')
-class ImageMessage(BaseMessage):
- """
- 图片消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'image'
- media_id = StringField('MediaId')
- image = StringField('PicUrl')
-
-
-@register_message('voice')
-class VoiceMessage(BaseMessage):
- """
- 语音消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'voice'
- media_id = StringField('MediaId')
- format = StringField('Format')
- recognition = StringField('Recognition')
-
-
-@register_message('shortvideo')
-class ShortVideoMessage(BaseMessage):
- """
- 短视频消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'shortvideo'
- media_id = StringField('MediaId')
- thumb_media_id = StringField('ThumbMediaId')
-
-
-@register_message('video')
-class VideoMessage(BaseMessage):
- """
- 视频消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'video'
- media_id = StringField('MediaId')
- thumb_media_id = StringField('ThumbMediaId')
-
-
-@register_message('location')
-class LocationMessage(BaseMessage):
- """
- 地理位置消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'location'
- location_x = StringField('Location_X')
- location_y = StringField('Location_Y')
- scale = StringField('Scale')
- label = StringField('Label')
-
- @property
- def location(self):
- return self.location_x, self.location_y
-
-
-@register_message('link')
-class LinkMessage(BaseMessage):
- """
- 链接消息
- 详情请参阅
- http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html
- """
- type = 'link'
- title = StringField('Title')
- description = StringField('Description')
- url = StringField('Url')
-
-
-class UnknownMessage(BaseMessage):
- """未知消息类型"""
- pass
diff --git a/sg_wechat_enterprise/we_api/oauth/__init__.py b/sg_wechat_enterprise/we_api/oauth/__init__.py
deleted file mode 100644
index e58aef38..00000000
--- a/sg_wechat_enterprise/we_api/oauth/__init__.py
+++ /dev/null
@@ -1,219 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.oauth
- ~~~~~~~~~~~~~~~
-
- This module provides OAuth2 library for WeChat
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-try:
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
-except ImportError:
- from pkg_resources import declare_namespace
- declare_namespace(__name__)
-
-import requests
-from six.moves.urllib.parse import quote
-# from urllib import quote
-from wechatpy.utils import json
-from wechatpy.exceptions import WeChatOAuthException
-
-
-class WeChatOAuth(object):
- """微信公众平台 OAuth 网页授权 """
-
- API_BASE_URL = 'https://api.weixin.qq.com/'
- OAUTH_BASE_URL = 'https://open.weixin.qq.com/connect/'
-
- def __init__(self, app_id, secret, redirect_uri,
- scope='snsapi_base', state=''):
- """
-
- :param app_id: 微信公众号 app_id
- :param secret: 微信公众号 secret
- :param redirect_uri: OAuth2 redirect URI
- :param scope: 可选,微信公众号 OAuth2 scope,默认为 ``snsapi_base``
- :param state: 可选,微信公众号 OAuth2 state
- """
- self.app_id = app_id
- self.secret = secret
- self.redirect_uri = redirect_uri
- self.scope = scope
- self.state = state
-
- def _request(self, method, url_or_endpoint, **kwargs):
- if not url_or_endpoint.startswith(('http://', 'https://')):
- url = '{base}{endpoint}'.format(
- base=self.API_BASE_URL,
- endpoint=url_or_endpoint
- )
- else:
- url = url_or_endpoint
-
- if isinstance(kwargs.get('data', ''), dict):
- body = json.dumps(kwargs['data'], ensure_ascii=False)
- body = body.encode('utf-8')
- kwargs['data'] = body
-
- res = requests.request(
- method=method,
- url=url,
- **kwargs
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatOAuthException(
- errcode=None,
- errmsg=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
- result = json.loads(res.content.decode('utf-8', 'ignore'), strict=False)
-
- if 'errcode' in result and result['errcode'] != 0:
- errcode = result['errcode']
- errmsg = result['errmsg']
- raise WeChatOAuthException(
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
-
- return result
-
- def _get(self, url, **kwargs):
- return self._request(
- method='get',
- url_or_endpoint=url,
- **kwargs
- )
-
- @property
- def authorize_url(self):
- """获取授权跳转地址
-
- :return: URL 地址
- """
- redirect_uri = quote(self.redirect_uri)
- url_list = [
- self.OAUTH_BASE_URL,
- 'oauth2/authorize?appid=',
- self.app_id,
- '&redirect_uri=',
- redirect_uri,
- '&response_type=code&scope=',
- self.scope
- ]
- if self.state:
- url_list.extend(['&state=', self.state])
- url_list.append('#wechat_redirect')
- return ''.join(url_list)
-
- @property
- def qrconnect_url(self):
- """生成扫码登录地址
-
- :return: URL 地址
- """
- redirect_uri = quote(self.redirect_uri)
- url_list = [
- self.OAUTH_BASE_URL,
- 'qrconnect?appid=',
- self.app_id,
- '&redirect_uri=',
- redirect_uri,
- '&response_type=code&scope=',
- 'snsapi_login' # scope
- ]
- if self.state:
- url_list.extend(['&state=', self.state])
- url_list.append('#wechat_redirect')
- return ''.join(url_list)
-
- def fetch_access_token(self, code):
- """获取 access_token
-
- :param code: 授权完成跳转回来后 URL 中的 code 参数
- :return: JSON 数据包 1
- """
- res = self._get(
- 'sns/oauth2/access_token',
- params={
- 'appid': self.app_id,
- 'secret': self.secret,
- 'code': code,
- 'grant_type': 'authorization_code'
- }
- )
- self.access_token = res['access_token']
- self.open_id = res['openid']
- self.refresh_token = res['refresh_token']
- self.expires_in = res['expires_in']
- return res
-
- def refresh_access_token(self, refresh_token):
- """刷新 access token
-
- :param refresh_token: OAuth2 refresh token
- :return: JSON 数据包
- """
- res = self._get(
- 'sns/oauth2/refresh_token',
- params={
- 'appid': self.app_id,
- 'grant_type': 'refresh_token',
- 'refresh_token': refresh_token
- }
- )
- self.access_token = res['access_token']
- self.open_id = res['openid']
- self.refresh_token = res['refresh_token']
- self.expires_in = res['expires_in']
- return res
-
- def get_user_info(self, openid=None, access_token=None, lang='zh_CN'):
- """获取用户信息
-
- :param openid: 可选,微信 openid,默认获取当前授权用户信息
- :param access_token: 可选,access_token,默认使用当前授权用户的 access_token
- :param lang: 可选,语言偏好, 默认为 ``zh_CN``
- :return: JSON 数据包
- """
- openid = openid or self.open_id
- access_token = access_token or self.access_token
- return self._get(
- 'sns/userinfo',
- params={
- 'access_token': access_token,
- 'openid': openid,
- 'lang': lang
- }
- )
-
- def check_access_token(self, openid=None, access_token=None):
- """检查 access_token 有效性
-
- :param openid: 可选,微信 openid,默认获取当前授权用户信息
- :param access_token: 可选,access_token,默认使用当前授权用户的 access_token
- :return: 有效返回 True,否则 False
- """
- openid = openid or self.open_id
- access_token = access_token or self.access_token
- res = self._get(
- 'sns/auth',
- params={
- 'access_token': access_token,
- 'openid': openid
- }
- )
- if res['errcode'] == 0:
- return True
- return False
diff --git a/sg_wechat_enterprise/we_api/parser.py b/sg_wechat_enterprise/we_api/parser.py
deleted file mode 100644
index c9e4ab76..00000000
--- a/sg_wechat_enterprise/we_api/parser.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.parser
- ~~~~~~~~~~~~~~~~
- This module provides functions for parsing WeChat messages
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import xmltodict
-
-from wechatpy.messages import MESSAGE_TYPES, UnknownMessage
-from wechatpy.events import EVENT_TYPES
-from wechatpy.utils import to_text
-
-
-def parse_message(xml):
- """
- 解析微信服务器推送的 XML 消息
-
- :param xml: XML 消息
- :return: 解析成功返回对应的消息或事件,否则返回 ``UnknownMessage``
- """
- if not xml:
- return
- message = xmltodict.parse(to_text(xml))['xml']
- message_type = message['MsgType'].lower()
- if message_type in ('event', 'device_event'):
- event_type = message['Event'].lower()
- # special event type for device_event
- if message_type == 'device_event':
- event_type = 'device_{event}'.format(event=event_type)
- if event_type == 'subscribe' and message.get('EventKey'):
- event_key = message['EventKey']
- if event_key.startswith(('scanbarcode|', 'scanimage|')):
- event_type = 'subscribe_scan_product'
- message['Event'] = event_type
- else:
- # Scan to subscribe with scene id event
- event_type = 'subscribe_scan'
- message['Event'] = event_type
- message['EventKey'] = event_key.replace('qrscene_', '')
- message_class = EVENT_TYPES.get(event_type, UnknownMessage)
- else:
- message_class = MESSAGE_TYPES.get(message_type, UnknownMessage)
- return message_class(message)
diff --git a/sg_wechat_enterprise/we_api/pay/__init__.py b/sg_wechat_enterprise/we_api/pay/__init__.py
deleted file mode 100644
index c88a5fc3..00000000
--- a/sg_wechat_enterprise/we_api/pay/__init__.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import sys
-import inspect
-import logging
-
-import requests
-import xmltodict
-from xml.parsers.expat import ExpatError
-from optionaldict import optionaldict
-
-from wechatpy.utils import random_string
-from wechatpy.exceptions import WeChatPayException, InvalidSignatureException
-from wechatpy.pay.utils import (
- calculate_signature, _check_signature, dict_to_xml
-)
-from wechatpy.pay.base import BaseWeChatPayAPI
-from wechatpy.pay import api
-
-
-logger = logging.getLogger(__name__)
-
-
-def _is_api_endpoint(obj):
- return isinstance(obj, BaseWeChatPayAPI)
-
-
-class WeChatPay(object):
- """
- 微信红包接口
-
- :param appid: 微信公众号 appid
- :param api_key: 商户 key
- :param mch_id: 商户号
- :param sub_mch_id: 可选,子商户号,受理模式下必填
- :param mch_cert: 必填,商户证书路径
- :param mch_key: 必填,商户证书私钥路径
- """
- redpack = api.WeChatRedpack()
- """红包接口"""
- transfer = api.WeChatTransfer()
- """企业付款接口"""
- coupon = api.WeChatCoupon()
- """代金券接口"""
- order = api.WeChatOrder()
- """订单接口"""
- refund = api.WeChatRefund()
- """退款接口"""
- micropay = api.WeChatMicroPay()
- """刷卡支付接口"""
- tools = api.WeChatTools()
- """工具类接口"""
- jsapi = api.WeChatJSAPI()
-
- API_BASE_URL = 'https://api.mch.weixin.qq.com/'
-
- def __new__(cls, *args, **kwargs):
- self = super(WeChatPay, cls).__new__(cls)
- if sys.version_info[:2] == (2, 6):
- import copy
- # Python 2.6 inspect.gemembers bug workaround
- # http://bugs.python.org/issue1785
- for name, _api in self.__class__.__dict__.items():
- if isinstance(_api, BaseWeChatPayAPI):
- _api = copy.deepcopy(_api)
- _api._client = self
- setattr(self, name, _api)
- else:
- api_endpoints = inspect.getmembers(self, _is_api_endpoint)
- for name, _api in api_endpoints:
- api_cls = type(_api)
- _api = api_cls(self)
- setattr(self, name, _api)
- return self
-
- def __init__(self, appid, api_key, mch_id, sub_mch_id=None,
- mch_cert=None, mch_key=None):
- """
- :param appid: 微信公众号 appid
- :param api_key: 商户 key
- :param mch_id: 商户号
- :param sub_mch_id: 可选,子商户号,受理模式下必填
- :param mch_cert: 商户证书路径
- :param mch_key: 商户证书私钥路径
- """
- self.appid = appid
- self.api_key = api_key
- self.mch_id = mch_id
- self.sub_mch_id = sub_mch_id
- self.mch_cert = mch_cert
- self.mch_key = mch_key
-
- def _request(self, method, url_or_endpoint, **kwargs):
- if not url_or_endpoint.startswith(('http://', 'https://')):
- api_base_url = kwargs.pop('api_base_url', self.API_BASE_URL)
- url = '{base}{endpoint}'.format(
- base=api_base_url,
- endpoint=url_or_endpoint
- )
- else:
- url = url_or_endpoint
-
- if isinstance(kwargs.get('data', ''), dict):
- data = optionaldict(kwargs['data'])
- if 'mchid' not in data:
- # Fuck Tencent
- data.setdefault('mch_id', self.mch_id)
- data.setdefault('sub_mch_id', self.sub_mch_id)
- data.setdefault('nonce_str', random_string(32))
- sign = calculate_signature(data, self.api_key)
- body = dict_to_xml(data, sign)
- body = body.encode('utf-8')
- kwargs['data'] = body
-
- # 商户证书
- if self.mch_cert and self.mch_key:
- kwargs['cert'] = (self.mch_cert, self.mch_key)
-
- res = requests.request(
- method=method,
- url=url,
- **kwargs
- )
- try:
- res.raise_for_status()
- except requests.RequestException as reqe:
- raise WeChatPayException(
- return_code=None,
- client=self,
- request=reqe.request,
- response=reqe.response
- )
-
- return self._handle_result(res)
-
- def _handle_result(self, res):
- res.encoding = 'utf-8'
- xml = res.text
- try:
- data = xmltodict.parse(xml)['xml']
- except (xmltodict.ParsingInterrupted, ExpatError):
- # 解析 XML 失败
- logger.debug('WeChat payment result xml parsing error', exc_info=True)
- return xml
-
- return_code = data['return_code']
- return_msg = data.get('return_msg')
- result_code = data.get('result_code')
- errcode = data.get('err_code')
- errmsg = data.get('err_code_des')
- if return_code != 'SUCCESS' or result_code != 'SUCCESS':
- # 返回状态码不为成功
- raise WeChatPayException(
- return_code,
- result_code,
- return_msg,
- errcode,
- errmsg,
- client=self,
- request=res.request,
- response=res
- )
- return data
-
- def get(self, url, **kwargs):
- return self._request(
- method='get',
- url_or_endpoint=url,
- **kwargs
- )
-
- def post(self, url, **kwargs):
- return self._request(
- method='post',
- url_or_endpoint=url,
- **kwargs
- )
-
- def check_signature(self, params):
- return _check_signature(params, self.api_key)
-
- def parse_payment_result(self, xml):
- """解析微信支付结果通知"""
- try:
- data = xmltodict.parse(xml)
- except (xmltodict.ParsingInterrupted, ExpatError):
- raise InvalidSignatureException()
-
- if not data or 'xml' not in data:
- raise InvalidSignatureException()
-
- data = data['xml']
- sign = data.pop('sign', None)
- real_sign = calculate_signature(data, self.api_key)
- if sign != real_sign:
- raise InvalidSignatureException()
-
- data['sign'] = sign
- return data
diff --git a/sg_wechat_enterprise/we_api/pay/api/__init__.py b/sg_wechat_enterprise/we_api/pay/api/__init__.py
deleted file mode 100644
index 75e80fd8..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/__init__.py
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.pay.api.redpack import WeChatRedpack # NOQA
-from wechatpy.pay.api.transfer import WeChatTransfer # NOQA
-from wechatpy.pay.api.coupon import WeChatCoupon # NOQA
-from wechatpy.pay.api.order import WeChatOrder # NOQA
-from wechatpy.pay.api.refund import WeChatRefund # NOQA
-from wechatpy.pay.api.tools import WeChatTools # NOQA
-from wechatpy.pay.api.jsapi import WeChatJSAPI # NOQA
-from wechatpy.pay.api.micropay import WeChatMicroPay # NOQA
diff --git a/sg_wechat_enterprise/we_api/pay/api/coupon.py b/sg_wechat_enterprise/we_api/pay/api/coupon.py
deleted file mode 100644
index 6be5d5d6..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/coupon.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import random
-from datetime import datetime
-
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatCoupon(BaseWeChatPayAPI):
-
- def send(self, user_id, stock_id, op_user_id=None, device_info=None,
- out_trade_no=None):
- """
- 发放代金券
-
- :param user_id: 用户在公众号下的 openid
- :param stock_id: 代金券批次 ID
- :param op_user_id: 可选,操作员账号,默认为商户号
- :param device_info: 可选,微信支付分配的终端设备号
- :param out_trade_no: 可选,商户订单号,需保持唯一性,默认自动生成
- :return: 返回的结果信息
- """
- if not out_trade_no:
- now = datetime.now()
- out_trade_no = '{0}{1}{2}'.format(
- self.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'appid': self.appid,
- 'coupon_stock_id': stock_id,
- 'openid': user_id,
- 'openid_count': 1,
- 'partner_trade_no': out_trade_no,
- 'op_user_id': op_user_id,
- 'device_info': device_info,
- 'version': '1.0',
- 'type': 'XML',
- }
- return self._post('mmpaymkttransfers/send_coupon', data=data)
-
- def query_stock(self, stock_id, op_user_id=None, device_info=None):
- """
- 查询代金券批次
-
- :param stock_id: 代金券批次 ID
- :param op_user_id: 可选,操作员账号,默认为商户号
- :param device_info: 可选,微信支付分配的终端设备号
- :return: 返回的结果信息
- """
- data = {
- 'appid': self.appid,
- 'coupon_stock_id': stock_id,
- 'op_user_id': op_user_id,
- 'device_info': device_info,
- 'version': '1.0',
- 'type': 'XML',
- }
- return self._post('mmpaymkttransfers/query_coupon_stock', data=data)
-
- def query_coupon(self, coupon_id, user_id,
- op_user_id=None, device_info=None):
- """
- 查询代金券信息
-
- :param coupon_id: 代金券 ID
- :param user_id: 用户在公众号下的 openid
- :param op_user_id: 可选,操作员账号,默认为商户号
- :param device_info: 可选,微信支付分配的终端设备号
- :return: 返回的结果信息
- """
- data = {
- 'coupon_id': coupon_id,
- 'openid': user_id,
- 'appid': self.appid,
- 'op_user_id': op_user_id,
- 'device_info': device_info,
- 'version': '1.0',
- 'type': 'XML',
- }
- return self._post('promotion/query_coupon', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/jsapi.py b/sg_wechat_enterprise/we_api/pay/api/jsapi.py
deleted file mode 100644
index 6f3196c4..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/jsapi.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import time
-
-from wechatpy.utils import random_string, to_text
-from wechatpy.pay.base import BaseWeChatPayAPI
-from wechatpy.pay.utils import calculate_signature
-
-
-class WeChatJSAPI(BaseWeChatPayAPI):
-
- def get_jsapi_signature(self, prepay_id, timestamp=None, nonce_str=None):
- """
- 获取 JSAPI 签名
-
- :param prepay_id: 统一下单接口返回的 prepay_id 参数值
- :param timestamp: 可选,时间戳,默认为当前时间戳
- :param nonce_str: 可选,随机字符串,默认自动生成
- :return: 签名
- """
- data = {
- 'appId': self.appid,
- 'timeStamp': timestamp or to_text(int(time.time())),
- 'nonceStr': nonce_str or random_string(32),
- 'signType': 'MD5',
- 'package': 'prepay_id={0}'.format(prepay_id),
- }
- return calculate_signature(data, self._client.api_key)
-
- def get_jsapi_params(self, prepay_id, timestamp=None, nonce_str=None):
- """
- 获取 JSAPI 参数
-
- :param prepay_id: 统一下单接口返回的 prepay_id 参数值
- :param timestamp: 可选,时间戳,默认为当前时间戳
- :param nonce_str: 可选,随机字符串,默认自动生成
- :return: 签名
- """
- data = {
- 'appId': self.appid,
- 'timeStamp': timestamp or to_text(int(time.time())),
- 'nonceStr': nonce_str or random_string(32),
- 'signType': 'MD5',
- 'package': 'prepay_id={0}'.format(prepay_id),
- }
- sign = calculate_signature(data, self._client.api_key)
- data['paySign'] = sign
- return data
diff --git a/sg_wechat_enterprise/we_api/pay/api/micropay.py b/sg_wechat_enterprise/we_api/pay/api/micropay.py
deleted file mode 100644
index 5a77bf0d..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/micropay.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import random
-from datetime import datetime
-
-from wechatpy.pay.utils import get_external_ip
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatMicroPay(BaseWeChatPayAPI):
- def create(self, body, total_fee, auth_code, client_ip=None, out_trade_no=None, detail=None, attach=None,
- fee_type='CNY', goods_tag=None, device_info=None, limit_pay=None):
- """
- 刷卡支付接口
- :param device_info: 可选,终端设备号(商户自定义,如门店编号)
- :param body: 商品描述
- :param detail: 可选,商品详情
- :param attach: 可选,附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
- :param client_ip: 可选,APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
- :param out_trade_no: 可选,商户订单号,默认自动生成
- :param total_fee: 总金额,单位分
- :param fee_type: 可选,符合ISO 4217标准的三位字母代码,默认人民币:CNY
- :param goods_tag: 可选,商品标记,代金券或立减优惠功能的参数
- :param limit_pay: 可选,指定支付方式,no_credit--指定不能使用信用卡支付
- :param auth_code: 授权码,扫码支付授权码,设备读取用户微信中的条码或者二维码信息
- :return: 返回的结果数据
- """
- now = datetime.now()
- if not out_trade_no:
- out_trade_no = '{0}{1}{2}'.format(
- self.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'appid': self.appid,
- 'device_info': device_info,
- 'body': body,
- 'detail': detail,
- 'attach': attach,
- 'out_trade_no': out_trade_no,
- 'total_fee': total_fee,
- 'fee_type': fee_type,
- 'spbill_create_ip': client_ip or get_external_ip(),
- 'goods_tag': goods_tag,
- 'limit_pay': limit_pay,
- 'auth_code': auth_code,
- }
- return self._post('pay/micropay', data=data)
-
- def query(self, transaction_id=None, out_trade_no=None):
- """
- 查询订单
-
- :param transaction_id: 微信的订单号,优先使用
- :param out_trade_no: 商户系统内部的订单号,当没提供transaction_id时需要传这个。
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'transaction_id': transaction_id,
- 'out_trade_no': out_trade_no,
- }
- return self._post('pay/orderquery', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/order.py b/sg_wechat_enterprise/we_api/pay/api/order.py
deleted file mode 100644
index fb233845..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/order.py
+++ /dev/null
@@ -1,138 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import time
-import random
-from datetime import datetime, timedelta
-
-from wechatpy.utils import timezone
-from wechatpy.pay.utils import get_external_ip
-from wechatpy.pay.base import BaseWeChatPayAPI
-from wechatpy.utils import random_string, to_text
-from wechatpy.pay.utils import calculate_signature
-
-
-class WeChatOrder(BaseWeChatPayAPI):
-
- def create(self, trade_type, body, total_fee, notify_url, client_ip=None,
- user_id=None, out_trade_no=None, detail=None, attach=None,
- fee_type='CNY', time_start=None, time_expire=None,
- goods_tag=None, product_id=None, device_info=None, limit_pay=None):
- """
- 统一下单接口
-
- :param trade_type: 交易类型,取值如下:JSAPI,NATIVE,APP,WAP
- :param body: 商品描述
- :param total_fee: 总金额,单位分
- :param notify_url: 接收微信支付异步通知回调地址
- :param client_ip: 可选,APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
- :param user_id: 可选,用户在商户appid下的唯一标识。trade_type=JSAPI,此参数必传
- :param out_trade_no: 可选,商户订单号,默认自动生成
- :param detail: 可选,商品详情
- :param attach: 可选,附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
- :param fee_type: 可选,符合ISO 4217标准的三位字母代码,默认人民币:CNY
- :param time_start: 可选,订单生成时间,默认为当前时间
- :param time_expire: 可选,订单失效时间,默认为订单生成时间后两小时
- :param goods_tag: 可选,商品标记,代金券或立减优惠功能的参数
- :param product_id: 可选,trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义
- :param device_info: 可选,终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
- :param limit_pay: 可选,指定支付方式,no_credit--指定不能使用信用卡支付
- :return: 返回的结果数据
- """
- now = datetime.fromtimestamp(time.time(), tz=timezone('Asia/Shanghai'))
- hours_later = now + timedelta(hours=2)
- if time_start is None:
- time_start = now
- if time_expire is None:
- time_expire = hours_later
- if not out_trade_no:
- out_trade_no = '{0}{1}{2}'.format(
- self.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'appid': self.appid,
- 'device_info': device_info,
- 'body': body,
- 'detail': detail,
- 'attach': attach,
- 'out_trade_no': out_trade_no,
- 'fee_type': fee_type,
- 'total_fee': total_fee,
- 'spbill_create_ip': client_ip or get_external_ip(),
- 'time_start': time_start.strftime('%Y%m%d%H%M%S'),
- 'time_expire': time_expire.strftime('%Y%m%d%H%M%S'),
- 'goods_tag': goods_tag,
- 'notify_url': notify_url,
- 'trade_type': trade_type,
- 'limit_pay': limit_pay,
- 'product_id': product_id,
- 'openid': user_id,
- }
- return self._post('pay/unifiedorder', data=data)
-
- def query(self, transaction_id=None, out_trade_no=None):
- """
- 查询订单
-
- :param transaction_id: 微信的订单号,优先使用
- :param out_trade_no: 商户系统内部的订单号,当没提供transaction_id时需要传这个。
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'transaction_id': transaction_id,
- 'out_trade_no': out_trade_no,
- }
- return self._post('pay/orderquery', data=data)
-
- def close(self, out_trade_no):
- """
- 关闭订单
-
- :param out_trade_no: 商户系统内部的订单号
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'out_trade_no': out_trade_no,
- }
- return self._post('pay/closeorder', data=data)
-
- def get_appapi_params(self, prepay_id, timestamp=None, nonce_str=None):
- """
- 获取 APP 支付参数
-
- :param prepay_id: 统一下单接口返回的 prepay_id 参数值
- :param timestamp: 可选,时间戳,默认为当前时间戳
- :param nonce_str: 可选,随机字符串,默认自动生成
- :return: 签名
- """
- data = {
- 'appid': self.appid,
- 'partnerid': self.mch_id,
- 'prepayid': prepay_id,
- 'package': 'Sign=WXPay',
- 'timestamp': timestamp or to_text(int(time.time())),
- 'noncestr': nonce_str or random_string(32)
- }
- sign = calculate_signature(data, self._client.api_key)
- data['sign'] = sign
- return data
-
- def reverse(self, transaction_id=None, out_trade_no=None):
- """
- 撤销订单
-
- :param transaction_id: 可选,微信的订单号,优先使用
- :param out_trade_no: 可选,商户系统内部的订单号,
- transaction_id、out_trade_no二选一,
- 如果同时存在优先级:transaction_id> out_trade_no
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'transaction_id': transaction_id,
- 'out_trade_no': out_trade_no,
- }
- return self._post('secapi/pay/reverse', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/redpack.py b/sg_wechat_enterprise/we_api/pay/api/redpack.py
deleted file mode 100644
index 6c5a1b25..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/redpack.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import random
-from datetime import datetime
-
-from wechatpy.pay.utils import get_external_ip
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatRedpack(BaseWeChatPayAPI):
-
- def send(self, user_id, total_amount, send_name, act_name,
- wishing, remark, total_num=1, client_ip=None,
- nick_name=None, min_value=None,
- max_value=None, out_trade_no=None, logo_imgurl=None):
- """
- 发送现金红包
-
- :param user_id: 接收红包的用户在公众号下的 openid
- :param total_amount: 红包金额,单位分
- :param send_name: 商户名称
- :param nick_name: 可选,提供方名称,默认和商户名称相同
- :param act_name: 活动名称
- :param wishing: 红包祝福语
- :param remark: 备注
- :param client_ip: 可选,调用接口的机器 IP 地址
- :param total_num: 可选,红包发放总人数,默认为 1
- :param min_value: 可选,最小红包金额,单位分
- :param max_value: 可选,最大红包金额,单位分
- :param out_trade_no: 可选,商户订单号,默认会自动生成
- :param logo_imgurl: 可选,商户 Logo 的 URL
- :return: 返回的结果数据字典
- """
- if not out_trade_no:
- now = datetime.now()
- out_trade_no = '{0}{1}{2}'.format(
- self.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'wxappid': self.appid,
- 're_openid': user_id,
- 'total_amount': total_amount,
- 'nick_name': nick_name or send_name,
- 'send_name': send_name,
- 'act_name': act_name,
- 'wishing': wishing,
- 'remark': remark,
- 'client_ip': client_ip or get_external_ip(),
- 'total_num': total_num,
- 'min_value': min_value or total_amount,
- 'max_value': max_value or total_amount,
- 'mch_billno': out_trade_no,
- 'logo_imgurl': logo_imgurl,
- }
- return self._post('mmpaymkttransfers/sendredpack', data=data)
-
- def send_group(self, user_id, total_amount, send_name, act_name, wishing,
- remark, total_num, client_ip=None, amt_type="ALL_RAND",
- amt_list=None, out_trade_no=None,
- logo_imgurl=None, watermark_imgurl=None,
- banner_imgurl=None):
- """
- 发送裂变红包
-
- :param user_id: 接收红包的用户在公众号下的 openid
- :param total_amount: 红包金额,单位分
- :param send_name: 商户名称
- :param act_name: 活动名称
- :param wishing: 红包祝福语
- :param remark: 备注
- :param total_num: 红包发放总人数
- :param client_ip: 可选,调用接口的机器 IP 地址
- :param amt_type: 可选,红包金额设置方式
- ALL_RAND—全部随机,商户指定总金额和红包发放总人数,由微信支付随机计算出各红包金额
- ALL_SPECIFIED—全部自定义
- SEED_SPECIFIED—种子红包自定义,其他随机
- :param amt_list: 可选,各红包具体金额,自定义金额时必须设置,单位分
- :param out_trade_no: 可选,商户订单号,默认会自动生成
- :param logo_imgurl: 可选,商户 Logo 的 URL
- :param watermark_imgurl: 可选,背景水印图片 URL
- :param banner_imgurl: 红包详情页面的 banner 图片 URL
- :return: 返回的结果数据字典
- """
- if not out_trade_no:
- now = datetime.now()
- out_trade_no = '{0}{1}{2}'.format(
- self._client.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'wxappid': self.appid,
- 're_openid': user_id,
- 'total_amount': total_amount,
- 'send_name': send_name,
- 'act_name': act_name,
- 'wishing': wishing,
- 'remark': remark,
- 'total_num': total_num,
- 'client_ip': client_ip or get_external_ip(),
- 'amt_type': amt_type,
- 'amt_list': amt_list,
- 'mch_billno': out_trade_no,
- 'logo_imgurl': logo_imgurl,
- 'watermark_imgurl': watermark_imgurl,
- 'banner_imgurl': banner_imgurl,
- }
- return self._post('mmpaymkttransfers/sendgroupredpack', data=data)
-
- def query(self, out_trade_no, bill_type='MCHT'):
- """
- 查询红包发放记录
-
- :param out_trade_no: 商户订单号
- :param bill_type: 可选,订单类型,目前固定为 MCHT
- :return: 返回的红包发放记录信息
- """
- data = {
- 'mch_billno': out_trade_no,
- 'bill_type': bill_type,
- 'appid': self.appid,
- }
- return self._post('mmpaymkttransfers/gethbinfo', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/refund.py b/sg_wechat_enterprise/we_api/pay/api/refund.py
deleted file mode 100644
index 4e6bbc00..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/refund.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatRefund(BaseWeChatPayAPI):
-
- def apply(self, total_fee, refund_fee, out_refund_no, transaction_id=None,
- out_trade_no=None, fee_type='CNY', op_user_id=None,
- device_info=None,notify_url=None,refund_account='REFUND_SOURCE_UNSETTLED_FUNDS'):
- """
- 申请退款
-
- :param total_fee: 订单总金额,单位为分
- :param refund_fee: 退款总金额,单位为分
- :param out_refund_no: 商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
- :param transaction_id: 可选,微信订单号
- :param out_trade_no: 可选,商户系统内部的订单号,与 transaction_id 二选一
- :param fee_type: 可选,货币类型,符合ISO 4217标准的三位字母代码,默认人民币:CNY
- :param op_user_id: 可选,操作员帐号, 默认为商户号
- :param refund_account: 可选,退款资金来源,仅针对老资金流商户使用,默认使用未结算资金退款
- :param device_info: 可选,终端设备号
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'device_info': device_info,
- 'transaction_id': transaction_id,
- 'out_trade_no': out_trade_no,
- 'out_refund_no': out_refund_no,
- 'total_fee': total_fee,
- 'refund_fee': refund_fee,
- 'refund_fee_type': fee_type,
- 'op_user_id': op_user_id if op_user_id else self.mch_id,
- "notify_url":notify_url,
- 'refund_account': refund_account,
-
- }
- return self._post('secapi/pay/refund', data=data)
-
- def query(self, refund_id=None, out_refund_no=None, transaction_id=None,
- out_trade_no=None, device_info=None):
- """
- 查询退款
-
- :param refund_id: 微信退款单号
- :param out_refund_no: 商户退款单号
- :param transaction_id: 微信订单号
- :param out_trade_no: 商户系统内部的订单号
- :param device_info: 可选,终端设备号
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'device_info': device_info,
- 'transaction_id': transaction_id,
- 'out_trade_no': out_trade_no,
- 'out_refund_no': out_refund_no,
- 'refund_id': refund_id,
- }
- return self._post('pay/refundquery', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/tools.py b/sg_wechat_enterprise/we_api/pay/api/tools.py
deleted file mode 100644
index ab794081..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/tools.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from datetime import datetime, date
-
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatTools(BaseWeChatPayAPI):
-
- def short_url(self, long_url):
- """
- 长链接转短链接
-
- :param long_url: 长链接
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'long_url': long_url,
- }
- return self._post('tools/shorturl', data=data)
-
- def download_bill(self, bill_date, bill_type='ALL', device_info=None):
- """
- 下载对账单
-
- :param bill_date: 下载对账单的日期
- :param bill_type: 账单类型,ALL,返回当日所有订单信息,默认值
- SUCCESS,返回当日成功支付的订单,
- REFUND,返回当日退款订单,
- REVOKED,已撤销的订单
- :param device_info: 微信支付分配的终端设备号,填写此字段,只下载该设备号的对账单
- :return: 返回的结果数据
- """
- if isinstance(bill_date, (datetime, date)):
- bill_date = bill_date.strftime('%Y%m%d')
-
- data = {
- 'appid': self.appid,
- 'bill_date': bill_date,
- 'bill_type': bill_type,
- 'device_info': device_info,
- }
- return self._post('pay/downloadbill', data=data)
-
- def auto_code_to_openid(self, auth_code):
- """
- 授权码查询 openid 接口
-
- :param auth_code: 扫码支付授权码,设备读取用户微信中的条码或者二维码信息
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'auth_code': auth_code,
- }
- return self._post('tools/authcodetoopenid', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/api/transfer.py b/sg_wechat_enterprise/we_api/pay/api/transfer.py
deleted file mode 100644
index 8e207635..00000000
--- a/sg_wechat_enterprise/we_api/pay/api/transfer.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import random
-from datetime import datetime
-
-from wechatpy.pay.utils import get_external_ip
-from wechatpy.pay.base import BaseWeChatPayAPI
-
-
-class WeChatTransfer(BaseWeChatPayAPI):
-
- def transfer(self, user_id, amount, desc, client_ip=None,
- check_name='OPTION_CHECK', real_name=None,
- out_trade_no=None, device_info=None):
- """
- 企业付款接口
-
- :param user_id: 接受收红包的用户在公众号下的 openid
- :param amount: 付款金额,单位分
- :param desc: 付款说明
- :param client_ip: 可选,调用接口机器的 IP 地址
- :param check_name: 可选,校验用户姓名选项,
- NO_CHECK:不校验真实姓名,
- FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账),
- OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功),
- 默认为 OPTION_CHECK
- :param real_name: 可选,收款用户真实姓名,
- 如果check_name设置为FORCE_CHECK或OPTION_CHECK,则必填用户真实姓名
- :param out_trade_no: 可选,商户订单号,需保持唯一性,默认自动生成
- :param device_info: 可选,微信支付分配的终端设备号
- :return: 返回的结果信息
- """
- if not out_trade_no:
- now = datetime.now()
- out_trade_no = '{0}{1}{2}'.format(
- self.mch_id,
- now.strftime('%Y%m%d%H%M%S'),
- random.randint(1000, 10000)
- )
- data = {
- 'mch_appid': self.appid,
- 'mchid': self.mch_id,
- 'device_info': device_info,
- 'partner_trade_no': out_trade_no,
- 'openid': user_id,
- 'check_name': check_name,
- 're_user_name': real_name,
- 'amount': amount,
- 'desc': desc,
- 'spbill_create_ip': client_ip or get_external_ip(),
- }
- return self._post('mmpaymkttransfers/promotion/transfers', data=data)
-
- def query(self, out_trade_no):
- """
- 企业付款查询接口
-
- :param out_trade_no: 商户调用企业付款API时使用的商户订单号
- :return: 返回的结果数据
- """
- data = {
- 'appid': self.appid,
- 'partner_trade_no': out_trade_no,
- }
- return self._post('mmpaymkttransfers/gettransferinfo', data=data)
diff --git a/sg_wechat_enterprise/we_api/pay/base.py b/sg_wechat_enterprise/we_api/pay/base.py
deleted file mode 100644
index e1d17c28..00000000
--- a/sg_wechat_enterprise/we_api/pay/base.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-
-class BaseWeChatPayAPI(object):
- """ WeChat Pay 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 appid(self):
- return self._client.appid
-
- @property
- def mch_id(self):
- return self._client.mch_id
-
- @property
- def sub_mch_id(self):
- return self._client.sub_mch_id
diff --git a/sg_wechat_enterprise/we_api/pay/utils.py b/sg_wechat_enterprise/we_api/pay/utils.py
deleted file mode 100644
index 18691ca3..00000000
--- a/sg_wechat_enterprise/we_api/pay/utils.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-import copy
-import hashlib
-import socket
-
-import six
-
-from wechatpy.utils import to_binary, to_text
-
-
-def format_url(params, api_key=None):
- data = [to_binary('{0}={1}'.format(k, params[k])) for k in sorted(params) if params[k]]
- if api_key:
- data.append(to_binary('key={0}'.format(api_key)))
- return b"&".join(data)
-
-
-def calculate_signature(params, api_key):
- url = format_url(params, api_key)
- return to_text(hashlib.md5(url).hexdigest().upper())
-
-
-def _check_signature(params, api_key):
- _params = copy.deepcopy(params)
- sign = _params.pop('sign', '')
- return sign == calculate_signature(_params, api_key)
-
-
-def dict_to_xml(d, sign):
- xml = ['\n']
- for k in sorted(d):
- # use sorted to avoid test error on Py3k
- v = d[k]
- if isinstance(v, six.integer_types) or v.isdigit():
- xml.append('<{0}>{1}{0}>\n'.format(to_text(k), to_text(v)))
- else:
- xml.append(
- '<{0}>{0}>\n'.format(to_text(k), to_text(v))
- )
- xml.append('\n'.format(to_text(sign)))
- return ''.join(xml)
-
-
-def get_external_ip():
- sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- try:
- wechat_ip = socket.gethostbyname('api.mch.weixin.qq.com')
- sock.connect((wechat_ip, 80))
- addr, port = sock.getsockname()
- sock.close()
- return addr
- except socket.error:
- return '127.0.0.1'
diff --git a/sg_wechat_enterprise/we_api/replies.py b/sg_wechat_enterprise/we_api/replies.py
deleted file mode 100644
index cdcef25d..00000000
--- a/sg_wechat_enterprise/we_api/replies.py
+++ /dev/null
@@ -1,341 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.replies
- ~~~~~~~~~~~~~~~~~~
- This module defines all kinds of replies you can send to WeChat
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import time
-import six
-
-from wechatpy.fields import(
- StringField,
- IntegerField,
- ImageField,
- VoiceField,
- VideoField,
- MusicField,
- ArticlesField,
- Base64EncodeField,
- HardwareField,
-)
-from wechatpy.messages import BaseMessage, MessageMetaClass
-from wechatpy.utils import to_text, to_binary
-
-
-REPLY_TYPES = {}
-
-
-def register_reply(reply_type):
- def register(cls):
- REPLY_TYPES[reply_type] = cls
- return cls
- return register
-
-
-class BaseReply(six.with_metaclass(MessageMetaClass)):
- """Base class for all replies"""
- source = StringField('FromUserName')
- target = StringField('ToUserName')
- time = IntegerField('CreateTime', time.time())
- type = 'unknown'
-
- def __init__(self, **kwargs):
- self._data = {}
- message = kwargs.pop('message', None)
- if message and isinstance(message, BaseMessage):
- if 'source' not in kwargs:
- kwargs['source'] = message.target
- if 'target' not in kwargs:
- kwargs['target'] = message.source
- if hasattr(message, 'agent') and 'agent' not in kwargs:
- kwargs['agent'] = message.agent
- if 'time' not in kwargs:
- kwargs['time'] = time.time()
- for name, value in kwargs.items():
- field = self._fields.get(name)
- if field:
- self._data[field.name] = value
- else:
- setattr(self, name, value)
-
- def render(self):
- """Render reply from Python object to XML string"""
- tpl = '\n{data}\n'
- nodes = []
- msg_type = ''.format(
- msg_type=self.type
- )
- nodes.append(msg_type)
- for name, field in self._fields.items():
- value = getattr(self, name, field.default)
- node_xml = field.to_xml(value)
- nodes.append(node_xml)
- data = '\n'.join(nodes)
- return tpl.format(data=data)
-
- def __str__(self):
- if six.PY2:
- return to_binary(self.render())
- else:
- return to_text(self.render())
-
-
-@register_reply('empty')
-class EmptyReply(BaseReply):
- """
- 回复空串
-
- 微信服务器不会对此作任何处理,并且不会发起重试
- """
- def __init__(self):
- pass
-
- def render(self):
- return ''
-
-
-@register_reply('text')
-class TextReply(BaseReply):
- """
- 文本回复
- 详情请参阅 http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'text'
- content = StringField('Content')
-
-
-@register_reply('image')
-class ImageReply(BaseReply):
- """
- 图片回复
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'image'
- image = ImageField('Image')
-
- @property
- def media_id(self):
- return self.image
-
- @media_id.setter
- def media_id(self, value):
- self.image = value
-
-
-@register_reply('voice')
-class VoiceReply(BaseReply):
- """
- 语音回复
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'voice'
- voice = VoiceField('Voice')
-
- @property
- def media_id(self):
- return self.voice
-
- @media_id.setter
- def media_id(self, value):
- self.voice = value
-
-
-@register_reply('video')
-class VideoReply(BaseReply):
- """
- 视频回复
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'video'
- video = VideoField('Video', {})
-
- @property
- def media_id(self):
- return self.video.get('media_id')
-
- @media_id.setter
- def media_id(self, value):
- video = self.video
- video['media_id'] = value
- self.video = video
-
- @property
- def title(self):
- return self.video.get('title')
-
- @title.setter
- def title(self, value):
- video = self.video
- video['title'] = value
- self.video = video
-
- @property
- def description(self):
- return self.video.get('description')
-
- @description.setter
- def description(self, value):
- video = self.video
- video['description'] = value
- self.video = video
-
-
-@register_reply('music')
-class MusicReply(BaseReply):
- """
- 音乐回复
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'music'
- music = MusicField('Music', {})
-
- @property
- def thumb_media_id(self):
- return self.music.get('thumb_media_id')
-
- @thumb_media_id.setter
- def thumb_media_id(self, value):
- music = self.music
- music['thumb_media_id'] = value
- self.music = music
-
- @property
- def title(self):
- return self.music.get('title')
-
- @title.setter
- def title(self, value):
- music = self.music
- music['title'] = value
- self.music = music
-
- @property
- def description(self):
- return self.music.get('description')
-
- @description.setter
- def description(self, value):
- music = self.music
- music['description'] = value
- self.music = music
-
- @property
- def music_url(self):
- return self.music.get('music_url')
-
- @music_url.setter
- def music_url(self, value):
- music = self.music
- music['music_url'] = value
- self.music = music
-
- @property
- def hq_music_url(self):
- return self.music.get('hq_music_url')
-
- @hq_music_url.setter
- def hq_music_url(self, value):
- music = self.music
- music['hq_music_url'] = value
- self.music = music
-
-
-@register_reply('news')
-class ArticlesReply(BaseReply):
- """
- 图文回复
- 详情请参阅
- http://mp.weixin.qq.com/wiki/9/2c15b20a16019ae613d413e30cac8ea1.html
- """
- type = 'news'
- articles = ArticlesField('Articles', [])
-
- def add_article(self, article):
- if len(self.articles) == 10:
- raise AttributeError("Can't add more than 10 articles"
- " in an ArticlesReply")
- articles = self.articles
- articles.append(article)
- self.articles = articles
-
-
-@register_reply('transfer_customer_service')
-class TransferCustomerServiceReply(BaseReply):
- """
- 将消息转发到多客服
- 详情请参阅
- http://mp.weixin.qq.com/wiki/5/ae230189c9bd07a6b221f48619aeef35.html
- """
- type = 'transfer_customer_service'
-
-
-@register_reply('device_text')
-class DeviceTextReply(BaseReply):
- type = 'device_text'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- session_id = StringField('SessionID')
- content = Base64EncodeField('Content')
-
-
-@register_reply('device_event')
-class DeviceEventReply(BaseReply):
- type = 'device_event'
- event = StringField('Event')
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- session_id = StringField('SessionID')
- content = Base64EncodeField('Content')
-
-
-@register_reply('device_status')
-class DeviceStatusReply(BaseReply):
- type = 'device_status'
- device_type = StringField('DeviceType')
- device_id = StringField('DeviceID')
- status = StringField('DeviceStatus')
-
-
-@register_reply('hardware')
-class HardwareReply(BaseReply):
- type = 'hardware'
- func_flag = IntegerField('FuncFlag', 0)
- hardware = HardwareField('HardWare')
-
-
-def create_reply(reply, message=None, render=False):
- """
- Create a reply quickly
- """
- r = None
- if not reply:
- r = EmptyReply()
- elif isinstance(reply, BaseReply):
- r = reply
- if message:
- r.source = message.target
- r.target = message.source
- elif isinstance(reply, six.string_types):
- r = TextReply(
- message=message,
- content=reply
- )
- elif isinstance(reply, (tuple, list)):
- if len(reply) > 10:
- raise AttributeError("Can't add more than 10 articles"
- " in an ArticlesReply")
- r = ArticlesReply(
- message=message,
- articles=reply
- )
- if r and render:
- return r.render()
- return r
diff --git a/sg_wechat_enterprise/we_api/session/__init__.py b/sg_wechat_enterprise/we_api/session/__init__.py
deleted file mode 100644
index 197397bc..00000000
--- a/sg_wechat_enterprise/we_api/session/__init__.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-
-
-class SessionStorage(object):
-
- def get(self, key, default=None):
- raise NotImplementedError()
-
- def set(self, key, value, ttl=None):
- raise NotImplementedError()
-
- def delete(self, key):
- raise NotImplementedError()
-
- def __getitem__(self, key):
- self.get(key)
-
- def __setitem__(self, key, value):
- self.set(key, value)
-
- def __delitem__(self, key):
- self.delete(key)
diff --git a/sg_wechat_enterprise/we_api/session/memcachedstorage.py b/sg_wechat_enterprise/we_api/session/memcachedstorage.py
deleted file mode 100644
index dda13bc8..00000000
--- a/sg_wechat_enterprise/we_api/session/memcachedstorage.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.session import SessionStorage
-from wechatpy.utils import to_text
-from wechatpy.utils import json
-
-
-class MemcachedStorage(SessionStorage):
-
- def __init__(self, mc, prefix='wechatpy'):
- for method_name in ('get', 'set', 'delete'):
- assert hasattr(mc, method_name)
- self.mc = mc
- self.prefix = prefix
-
- def key_name(self, key):
- return '{0}:{1}'.format(self.prefix, key)
-
- def get(self, key, default=None):
- key = self.key_name(key)
- value = self.mc.get(key)
- if value is None:
- return default
- return json.loads(to_text(value))
-
- def set(self, key, value, ttl=None):
- if value is None:
- return
- key = self.key_name(key)
- value = json.dumps(value)
- self.mc.set(key, value)
-
- def delete(self, key):
- key = self.key_name(key)
- self.mc.delete(key)
diff --git a/sg_wechat_enterprise/we_api/session/memorystorage.py b/sg_wechat_enterprise/we_api/session/memorystorage.py
deleted file mode 100644
index 7eb2e4e6..00000000
--- a/sg_wechat_enterprise/we_api/session/memorystorage.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.session import SessionStorage
-
-
-class MemoryStorage(SessionStorage):
-
- def __init__(self):
- self._data = {}
-
- def get(self, key, default=None):
- return self._data.get(key, default)
-
- def set(self, key, value, ttl=None):
- if value is None:
- return
- self._data[key] = value
-
- def delete(self, key):
- self._data.pop(key, None)
diff --git a/sg_wechat_enterprise/we_api/session/redisstorage.py b/sg_wechat_enterprise/we_api/session/redisstorage.py
deleted file mode 100644
index 42171e6f..00000000
--- a/sg_wechat_enterprise/we_api/session/redisstorage.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.session import SessionStorage
-from wechatpy.utils import to_text
-from wechatpy.utils import json
-
-
-class RedisStorage(SessionStorage):
-
- def __init__(self, redis, prefix='wechatpy'):
- for method_name in ('get', 'set', 'delete'):
- assert hasattr(redis, method_name)
- self.redis = redis
- self.prefix = prefix
-
- def key_name(self, key):
- return '{0}:{1}'.format(self.prefix, key)
-
- def get(self, key, default=None):
- key = self.key_name(key)
- value = self.redis.get(key)
- if value is None:
- return default
- return json.loads(to_text(value))
-
- def set(self, key, value, ttl=None):
- if value is None:
- return
- key = self.key_name(key)
- value = json.dumps(value)
- self.redis.set(key, value, ex=ttl)
-
- def delete(self, key):
- key = self.key_name(key)
- self.redis.delete(key)
diff --git a/sg_wechat_enterprise/we_api/session/shovestorage.py b/sg_wechat_enterprise/we_api/session/shovestorage.py
deleted file mode 100644
index 7742d71c..00000000
--- a/sg_wechat_enterprise/we_api/session/shovestorage.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import absolute_import, unicode_literals
-from wechatpy.session import SessionStorage
-
-
-class ShoveStorage(SessionStorage):
-
- def __init__(self, shove, prefix='wechatpy'):
- self.shove = shove
- self.prefix = prefix
-
- def key_name(self, key):
- return '{0}:{1}'.format(self.prefix, key)
-
- def get(self, key, default=None):
- key = self.key_name(key)
- try:
- return self.shove[key]
- except KeyError:
- return default
-
- def set(self, key, value, ttl=None):
- if value is None:
- return
-
- key = self.key_name(key)
- self.shove[key] = value
-
- def delete(self, key):
- key = self.key_name(key)
- try:
- del self.shove[key]
- except KeyError:
- pass
diff --git a/sg_wechat_enterprise/we_api/utils.py b/sg_wechat_enterprise/we_api/utils.py
deleted file mode 100644
index 7e54e1a3..00000000
--- a/sg_wechat_enterprise/we_api/utils.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- wechatpy.utils
- ~~~~~~~~~~~~~~~
-
- This module provides some useful utilities.
-
- :copyright: (c) 2014 by messense.
- :license: MIT, see LICENSE for more details.
-"""
-from __future__ import absolute_import, unicode_literals
-import six
-import six.moves.urllib.parse as urlparse
-import sys
-import string
-import random
-import hashlib
-
-try:
- '''Use simplejson if we can, fallback to json otherwise.'''
- import simplejson as json
-except ImportError:
- import json # NOQA
-
-
-class ObjectDict(dict):
- """Makes a dictionary behave like an object, with attribute-style access.
- """
-
- def __getattr__(self, key):
- if key in self:
- return self[key]
- return None
-
- def __setattr__(self, key, value):
- self[key] = value
-
-
-class WeChatSigner(object):
- """WeChat data signer"""
-
- def __init__(self, delimiter=b''):
- self._data = []
- self._delimiter = to_binary(delimiter)
-
- def add_data(self, *args):
- """Add data to signer"""
- for data in args:
- self._data.append(to_binary(data))
-
- @property
- def signature(self):
- """Get data signature"""
- self._data.sort()
- str_to_sign = self._delimiter.join(self._data)
- return hashlib.sha1(str_to_sign).hexdigest()
-
-
-def check_signature(token, signature, timestamp, nonce):
- """Check WeChat callback signature, raises InvalidSignatureException
- if check failed.
-
- :param token: WeChat callback token
- :param signature: WeChat callback signature sent by WeChat server
- :param timestamp: WeChat callback timestamp sent by WeChat server
- :param nonce: WeChat callback nonce sent by WeChat sever
- """
- signer = WeChatSigner()
- signer.add_data(token, timestamp, nonce)
- if signer.signature != signature:
- from wechatpy.exceptions import InvalidSignatureException
-
- raise InvalidSignatureException()
-
-
-def to_text(value, encoding='utf-8'):
- """Convert value to unicode, default encoding is utf-8
-
- :param value: Value to be converted
- :param encoding: Desired encoding
- """
- if not value:
- return ''
- if isinstance(value, six.text_type):
- return value
- if isinstance(value, six.binary_type):
- return value.decode(encoding)
- return six.text_type(value)
-
-
-def to_binary(value, encoding='utf-8'):
- """Convert value to binary string, default encoding is utf-8
-
- :param value: Value to be converted
- :param encoding: Desired encoding
- """
- if not value:
- return b''
- if isinstance(value, six.binary_type):
- return value
- if isinstance(value, six.text_type):
- return value.encode(encoding)
- return six.binary_type(value)
-
-
-def timezone(zone):
- """Try to get timezone using pytz or python-dateutil
-
- :param zone: timezone str
- :return: timezone tzinfo or None
- """
- try:
- import pytz
- return pytz.timezone(zone)
- except ImportError:
- pass
- try:
- from dateutil.tz import gettz
- return gettz(zone)
- except ImportError:
- return None
-
-
-def random_string(length=16):
- rule = string.ascii_letters + string.digits
- rand_list = random.sample(rule, length)
- return ''.join(rand_list)
-
-
-def get_querystring(uri):
- """Get Qeruystring information from uri.
-
- :param uri: uri
- :return: querystring info or {}
- """
- parts = urlparse.urlsplit(uri)
- if sys.version_info[:2] == (2, 6):
- query = parts.path
- if query.startswith('?'):
- query = query[1:]
- else:
- query = parts.query
- return urlparse.parse_qs(query)
-
-
-def byte2int(c):
- if six.PY2:
- return ord(c)
- return c