Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions tests/testapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
import unittest

from yunpian_python_sdk.model.constant import (
YP_FLOW_HOST, YP_SIGN_HOST, YP_SMS_HOST, YP_TPL_HOST, YP_USER_HOST, YP_VOICE_HOST)
YP_FLOW_HOST, YP_SIGN_HOST, YP_SMS_HOST, YP_TPL_HOST, YP_USER_HOST, YP_VOICE_HOST, YP_VSMS_HOST)
from yunpian_python_sdk.ypclient import YunpianClient


class TestYunpianApi(unittest.TestCase):

APIKEY = '******'
APIKEY = 'xxxxxxx'

CONF = {YP_FLOW_HOST:'https://test-api.yunpian.com', YP_SIGN_HOST:'https://test-api.yunpian.com',
YP_SMS_HOST:'https://test-api.yunpian.com', YP_TPL_HOST:'https://test-api.yunpian.com',
YP_USER_HOST:'https://test-api.yunpian.com', YP_VOICE_HOST:'https://test-api.yunpian.com'}
CONF = {YP_FLOW_HOST:'https://sms.yunpian.com', YP_SIGN_HOST:'https://sms.yunpian.com',
YP_SMS_HOST:'https://sms.yunpian.com', YP_TPL_HOST:'https://sms.yunpian.com',
YP_USER_HOST:'https://sms.yunpian.com', YP_VOICE_HOST:'https://sms.yunpian.com',
YP_VSMS_HOST: 'https://vsms.yunpian.com'}

def setUp(self):
self._clnt = YunpianClient(TestYunpianApi.APIKEY, TestYunpianApi.CONF)
Expand Down
Binary file added tests/testapi/pkg.zip
Binary file not shown.
84 changes: 84 additions & 0 deletions tests/testapi/test_vsms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import unittest, json, base64

from yunpian_python_sdk.model.constant import (MOBILE, TPL_ID)

from . import TestYunpianApi


class TestSmsApi(TestYunpianApi):
'''Test SmsApi'''
def test_tpl_batch_send(self):
clnt = self._clnt
param = {MOBILE: 'xxx', TPL_ID: 'xxx'}
r = clnt.vsms().tpl_batch_send(param)
self.show(r)

def test_add_tpl(self):
clnt = self._clnt
l = {
"vlVersion": "0.0.1",
"subject": "title",
"frames": [
{
"index": 1,
"playTimes": 1,
"attachments": [
{
"index": 1,
"fileName": "1.jpg"
},
{
"index": 2,
"fileName": "content.txt"
},
]
}
]
}
param = {
'sign': '【超级短信测试】',
'layout': json.dumps(l),
'material': open('/your_path/yunpian-python-sdk/tests/testapi/pkg.zip', 'rb').read()
}
r = clnt.vsms().add_tpl(param)
self.show(r)

def test_get_tpl(self):
clnt = self._clnt
param = {'tpl_id': '1234'}
r = clnt.vsms().get_tpl(param)
self.show(r)

def test_delete_tpl(self):
clnt = self._clnt
param = {'tpl_id': '1234'}
r = clnt.vsms().delete_tpl(param)
self.show(r)

def test_add_sign(self):
clnt = self._clnt
certificate_file_contents = base64.b64encode(open('xxx.jpg', 'rb').read()).decode()
license_file_contents = base64.b64encode(open('xxx.jpg', 'rb').read()).decode()
param = {'sign': 'test_sign', 'certificate_file_suffix': 'jpg',
'certificate_file_contents': certificate_file_contents,
'license_file_contents': license_file_contents,
'license_file_suffix': 'jpg'}
r = clnt.vsms().add_sign(param)
self.show(r)

def test_search_sign(self):
clnt = self._clnt
param = {'sign': 'aaa'}
r = clnt.vsms().search_sign(param)
self.show(r)

def test_delete_sign(self):
clnt = self._clnt
param = {'sign': 'aaa'}
r = clnt.vsms().delete_sign(param)
self.show(r)


if __name__ == "__main__":
# import sys;sys.argv = ['', 'Test.testName']
unittest.main()
2 changes: 1 addition & 1 deletion yunpian_python_sdk/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
'''
Yunpian APIs
'''
__all__ = ["flow", "sign", "sms", "tpl", "user", "voice"]
__all__ = ["flow", "sign", "sms", "tpl", "user", "voice", "vsms"]
119 changes: 119 additions & 0 deletions yunpian_python_sdk/api/vsms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'''
Created on 2020-05-27

@author: fixJ
'''

from ..model.constant import (APIKEY, MOBILE, RESULT, VERSION_V2, TPL_ID, YP_VSMS_HOST, SIGN, LAYOUT, MATERIAL,
CERTIFICATE_FILE_SUFFIX, CERTIFICATE_FILE_CONTENTS, LICENSE_FILE_SUFFIX, LICENSE_FILE_CONTENTS)
from .ypapi import YunpianApi, CommonResultHandler


class VsmsApi(YunpianApi):

def _init(self, clnt):
super(VsmsApi, self)._init(clnt)
self.host(clnt.conf(YP_VSMS_HOST, 'https://sms.yunpian.com'))

def tpl_batch_send(self, param, must=[APIKEY, MOBILE, TPL_ID]):
"""
批量发送超级短信
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
mobile 必传: 手机号,多个号码用英文逗号,分隔,限制 1000 个
tpl_id 必传: 超级短信模版id
uid 非必传: 用户自定义的 uid,系统会在状态报告中返回此字段
tpl_param_json 非必传: 短信模板变量对应的实际值,JSON格式。 如果JSON中需要带换行符,请参照标准的JSON协议处理;且模板变量值的个数必须与手机号码个数相同、内容一一对应,表示向指定手机号码中发对应变量内容的短信,且短信模板中的变量参数替换为对应的值。
callback_url 非必传: 短信发送后将向这个地址推送(运营商返回的)发送报告。 如推送地址固定,可以在“超级短信设置-数据推送设置”中添加。 如后台已设置地址,且请求内也包含此参数,将以请求内地址为准。
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('tpl_batch_send.json').post(param, h, r)

def add_tpl(self, param, must=[APIKEY, SIGN, LAYOUT, MATERIAL]):
"""
添加超级短信模板
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
sign 必传: 超级短信签名
layout 必传: 内容布局的 json 描述文件
material 必传: 素材的字节数组,即素材内容压成zip包后转成字节数组。若需在文本中添加变量,请用英文双#号表示,变量名格式为10字以内的英文、数字和短线,如#name_1#
注意: 所有素材文件直接放在zip包根目录下,
zip包内没有文件夹,且所有资源小于1.8M。
callback_url 非必传: 模板审核结果更新后将向这个地址推送。 如推送地址固定,可以在“超级短信设置-数据推送设置”中添加。 如后台已设置地址,且添加模板请求内也包含此参数,将以请求内地址为准。
"""
r = self.verify_param(param, must)
file = {
MATERIAL: (MATERIAL, param.pop(MATERIAL), 'application/octet-stream')
}
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('add_tpl.json').post_multipart(param, file, h, r)

def get_tpl(self, param, must=[APIKEY, TPL_ID]):
"""
查询超级短信模板
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
tpl_id 必传: 超级短信模版 id
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('get_tpl.json').post(param, h, r)

def delete_tpl(self, param, must=[APIKEY, TPL_ID]):
"""
删除视频短信模板
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
tpl_id 必传: 超级短信模版 id
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('delete_tpl.json').post(param, h, r)

def add_sign(self, param, must=[APIKEY, SIGN, CERTIFICATE_FILE_SUFFIX,
CERTIFICATE_FILE_CONTENTS, LICENSE_FILE_SUFFIX, LICENSE_FILE_CONTENTS]):
"""
添加视频短信签名
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
sign 必传: 签名内容,2-12字,可包含中文、英文、数字和常用符号,不能包含【】
certificate_file_suffix 必传: 签名授权书文件格式,当前支持jpg、png或jpeg格式的图片。
certificate_file_contents 必传: 签名授权书文件经base64编码后的字符串。图片不超过5 MB。
license_file_suffix 必传: 营业执照文件格式,当前支持jpg、png或jpeg格式的图片
license_file_contents 必传: 营业执照文文件经base64编码后的字符串。图片不超过5 MB。
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('add_sign.json').post(param, h, r)

def search_sign(self, param, must=[APIKEY]):
"""
查询视频短信签名
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
sign 非必传: 超级短信签名
page_num 非必传: 页码,1 开始,不传或者格式错误则默认为1
page_size 非必传: 返回条数,必须大于 0,不传或者格式错误则默认为20
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('search_sign.json').post(param, h, r)

def delete_sign(self, param, must=[APIKEY, SIGN]):
"""
删除视频短信签名
apikey 必传: 用户唯一标识,在"账号设置"-"子帐号管理"中查看
sign 必传: 签名内容。说明:不带【】
"""
r = self.verify_param(param, must)
if not r.is_succ():
return r
h = CommonResultHandler(lambda rsp: {VERSION_V2: rsp.get(RESULT)}[self.version()])
return self.path('delete_sign.json').post(param, h, r)
16 changes: 15 additions & 1 deletion yunpian_python_sdk/api/ypapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ def name(self, name=None):
def uri(self):
return '{}/{}/{}/{}'.format(self.host(), self.version(), self.name(), self.path())


def post(self, param, h, r):
'''
Args:
Expand All @@ -161,6 +160,21 @@ def post(self, param, h, r):
except ValueError as err:
return h.catch_exception(err, r)

def post_multipart(self, param, file, h, r):
"""
Args:
param: request parameters
file: the part of file
h: ResultHandler
r: YunpianApiResult
"""
try:
rsp = self.client().post_multipart(self.uri(), param, file)
# print(rsp)
return self.result(rsp, h, r)
except ValueError as err:
return h.catch_exception(err, r)

def result(self, rsp, h, r):
code = self.code(rsp, self.version())
return h.succ(code, rsp, r) if code == Code.SUCC else h.fail(code, rsp, r)
Expand Down
8 changes: 8 additions & 0 deletions yunpian_python_sdk/model/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
YP_VOICE_HOST = 'yp.voice.host'
YP_FLOW_HOST = 'yp.flow.host'
YP_CALL_HOST = 'yp.call.host'
YP_VSMS_HOST = 'yp.vsms.host'

# ************************** api ************************************
VERSION_V1 = 'v1'
Expand Down Expand Up @@ -133,3 +134,10 @@
SEPERATOR_COMMA = ','

RECORD_ID = 'record_id'

LAYOUT = 'layout'
MATERIAL = 'material'
CERTIFICATE_FILE_SUFFIX = 'certificate_file_suffix'
CERTIFICATE_FILE_CONTENTS = 'certificate_file_contents'
LICENSE_FILE_SUFFIX = 'license_file_suffix'
LICENSE_FILE_CONTENTS = 'license_file_contents'
23 changes: 21 additions & 2 deletions yunpian_python_sdk/ypclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import requests

from .api import flow, sign, sms, tpl, user, voice
from .api import flow, sign, sms, tpl, user, voice, vsms
from .model.constant import (CHARSET_UTF8, YP_APIKEY, HTTP_CONN_TIMEOUT, HTTP_SO_TIMEOUT)


Expand All @@ -35,7 +35,8 @@ class _YunpianConf(object):
'yp.sms.host':'https://sms.yunpian.com',
'yp.voice.host':'https://voice.yunpian.com',
'yp.flow.host':'https://flow.yunpian.com',
'yp.call.host':'https://call.yunpian.com'
'yp.call.host':'https://call.yunpian.com',
'yp.vsms.host': 'https://vsms.yunpian.com'
}

def __init__(self):
Expand Down Expand Up @@ -98,6 +99,8 @@ def api(self, name):
api = user.UserApi()
elif voice.__name__ == name:
api = voice.VoiceApi()
elif vsms.__name__ == name:
api = vsms.VsmsApi()

assert api, "not found api-" + name

Expand Down Expand Up @@ -172,6 +175,14 @@ def voice(self):
'''
return self._api.api(voice.__name__)

def vsms(self):
"""
vsms api
Returns:
api.vsms.VsmsApi
"""
return self._api.api(vsms.__name__)

def conf(self, key=None, defval=None):
'''return YunpianConf if key=None, else return value in YunpianConf'''
if key is None:
Expand All @@ -192,6 +203,14 @@ def post(self, url, data, charset=CHARSET_UTF8, headers={}):
timeout=(int(self.conf(HTTP_CONN_TIMEOUT, '10')), int(self.conf(HTTP_SO_TIMEOUT, '30'))))
return json.loads(rsp.text)

def post_multipart(self, url, data, file, headers={}):
'''response json text'''
if 'Api-Lang' not in headers:
headers['Api-Lang'] = 'python'
rsp = requests.post(url, data=data, files=file, headers=headers,
timeout=(int(self.conf(HTTP_CONN_TIMEOUT, '10')), int(self.conf(HTTP_SO_TIMEOUT, '30'))))
return json.loads(rsp.text)

def urlEncodeAndJoin(self, seq, sepr=','):
'''sepr.join(urlencode(seq))
Args:
Expand Down