jpush-async
Version:
JPush's officially supported Node.js client library.
479 lines (430 loc) • 16.1 kB
JavaScript
var debug = require('debug')('jpush')
var JError = require('./JPushError')
var Request2 = require('./Request2')
var JModel = require('./PushPayload')
var PUSH_API_URL = 'https://api.jpush.cn/v3/push'
var GROUP_API_URL = 'https://api.jpush.cn/v3/grouppush'
var REPORT_API_URL = 'https://report.jpush.cn/v3'
var SCHEDULE_API_URL = 'https://api.jpush.cn/v3/schedules' // 定时任务
var REPORT_RECEIVED = '/received'
var REPORT_RECEIVED_DETAIL = '/received/detail'
var REPORT_STATUS_MESSAGE = '/status/message'
var REPORT_USER = '/users'
var REPORT_MESSAGE = '/messages'
var REPORT_MESSAGE_DETAIL = '/messages/detail'
var HOST_NAME_SSL = 'https://device.jpush.cn'
var DEVICE_PATH = '/v3/devices'
var TAG_PATH = '/v3/tags'
var ALIAS_PATH = '/v3/aliases'
var VALIDATE = '/validate'
var DEFAULT_MAX_RETRY_TIMES = 3
var READ_TIMEOUT = 30 * 1000
// Pattern
var PUSH_PATTERNS = /^[a-zA-Z0-9]{24}/
var MSG_IDS_PATTERNS = /[^\d,]/
exports.buildClient = function (appKey, masterSecret, retryTimes, isDebug, readTimeOut, proxy, isGroup) {
if (arguments.length === 1 && typeof arguments[0] === 'object') {
var options = arguments[0]
return new JPushClient(options.appKey,
options.masterSecret,
options.retryTimes,
options.isDebug,
options.readTimeOut,
options.proxy,
options.isGroup
)
} else {
return new JPushClient(appKey, masterSecret, retryTimes, isDebug, readTimeOut, proxy, isGroup)
}
}
function JPushClient (appKey, masterSecret, retryTimes, isDebug, readTimeOut = null, proxy = null, isGroup) {
if (!appKey || !masterSecret) {
throw JError.InvalidArgumentError('appKey and masterSecret are both required.')
}
this.isGroup = isGroup;
if (typeof appKey !== 'string' || typeof masterSecret !== 'string' ||
!PUSH_PATTERNS.test(appKey) || !PUSH_PATTERNS.test(masterSecret)) {
throw new JError.InvalidArgumentError('Key and Secret format is incorrect. ' +
'They should be 24 size, and be composed with alphabet and numbers. ' +
'Please confirm that they are coming from JPush Web Portal.')
}
this.appkey = isGroup ? 'group-' + appKey : appKey
this.masterSecret = masterSecret
this.isGroup = isGroup
if (retryTimes) {
if (typeof retryTimes !== 'number') {
throw JError.InvalidArgumentError('Invalid retryTimes.')
}
this.retryTimes = retryTimes
} else {
this.retryTimes = DEFAULT_MAX_RETRY_TIMES
}
if (isDebug != null) {
this.isDebug = isDebug
} else {
this.isDebug = true
}
if (readTimeOut != null) {
this.readTimeOut = readTimeOut
} else {
this.readTimeOut = READ_TIMEOUT
}
if (proxy != null) {
this.proxy = proxy
}
}
/**
* create a push payload
* @returns {exports.PushPayload}
*/
function push () {
return new JModel.PushPayload(this)
}
function sendPush (payload, callback) {
return _request(this, this.isGroup === true ? GROUP_API_URL : PUSH_API_URL, payload, 'POST', callback)
}
function getReportReceiveds(msgIds, callback) {
if (MSG_IDS_PATTERNS.test(msgIds)) {
throw new JError.InvalidArgumentError(
'Invalid msg_ids, msg_ids should be composed with alphabet and comma.')
}
var url = REPORT_API_URL + REPORT_RECEIVED + '?msg_ids=' + msgIds
return _request(this, url, null, 'GET', callback)
}
function getReportReceivedDetail(msgIds, callback) {
if (MSG_IDS_PATTERNS.test(msgIds)) {
throw new JError.InvalidArgumentError(
'Invalid msg_ids, msg_ids should be composed with alphabet and comma.')
}
var url = REPORT_API_URL + REPORT_RECEIVED_DETAIL + '?msg_ids=' + msgIds
return _request(this, url, null, 'GET', callback)
}
function getReportStatusMessage(msgId, registrationIds, date, callback) {
if (msgId == null) {
throw new JError.InvalidArgumentError('msgId is null!');
}
// if (typeof(msgId) != 'number') {
// throw new JError.InvalidArgumentError('msgId is not number type!');
// }
if (registrationIds == null) {
throw new JError.InvalidArgumentError('registrationIds is null!');
}
var json = {
"msg_id": msgId,
"registration_ids": registrationIds
};
if (date) {
json.date = date;
}
var url = REPORT_API_URL + REPORT_STATUS_MESSAGE;
return _request(this, url, JSON.stringify(json), 'POST', callback);
}
function getReportMessages(msgIds, callback) {
if (MSG_IDS_PATTERNS.test(msgIds)) {
throw new JError.InvalidArgumentError(
'Invalid msg_ids, msg_ids should be composed with alphabet and comma.')
}
var url = REPORT_API_URL + REPORT_MESSAGE + '?msg_ids=' + msgIds
return _request(this, url, null, 'GET', callback)
}
function getReportMessagesDetail(msgIds, callback) {
if (MSG_IDS_PATTERNS.test(msgIds)) {
throw new JError.InvalidArgumentError(
'Invalid msg_ids, msg_ids should be composed with alphabet and comma.')
}
var url = REPORT_API_URL + REPORT_MESSAGE_DETAIL + '?msg_ids=' + msgIds
return _request(this, url, null, 'GET', callback)
}
function getReportUsers (timeUnit, start, duration, callback) {
var url = REPORT_API_URL + REPORT_USER + '?time_unit=' + timeUnit + '&start=' + start + '&duration=' + duration
return _request(this, url, null, 'GET', callback)
}
/**
* device api
*
* @param registrationId
*/
function getDeviceTagAlias (registrationId, callback) {
var url = HOST_NAME_SSL + DEVICE_PATH + '/' + registrationId
return _request(this, url, null, 'GET', callback)
}
// 结合短信业务使用,需要先调用该方法将用户的手机号码与设备的 registration id 匹配。
function setMobile (registrationId, mobileNumber, callback) {
var json = {}
json['mobile'] = mobileNumber
var url = HOST_NAME_SSL + DEVICE_PATH + '/' + registrationId
return _request(this, url, JSON.stringify(json), 'POST', callback)
}
function updateDeviceTagAlias (registrationId, alias, clearTag, tagsToAdd, tagsToRemove, callback) {
var url = HOST_NAME_SSL + DEVICE_PATH + '/' + registrationId
if (tagsToAdd instanceof Array && tagsToRemove instanceof Array) {
var json = {}
var tags = {}
if (alias != null) {
json['alias'] = alias
}
if (clearTag) {
json['tags'] = ''
} else {
if (tagsToAdd != null && tagsToAdd.length > 0) {
tags['add'] = tagsToAdd
}
if (tagsToRemove != null && tagsToRemove.length > 0) {
tags['remove'] = tagsToRemove
}
json['tags'] = tags
debug(json)
}
} else {
throw new JError.InvalidArgumentError('tagsToAdd or tagsToRemove type should be array')
}
return _request(this, url, JSON.stringify(json), 'POST', callback)
}
function getTagList (callback) {
var url = HOST_NAME_SSL + TAG_PATH
return _request(this, url, null, 'GET', callback)
}
function isDeviceInTag (theTag, registrationID, callback) {
var url = HOST_NAME_SSL + TAG_PATH + '/' + theTag + '/registration_ids/' + registrationID
return _request(this, url, null, 'GET', callback)
}
function addRemoveDevicesFromTag (theTag, toAddUsers, toRemoveUsers, callback) {
var url = HOST_NAME_SSL + TAG_PATH + '/' + theTag
var registrationIds = {}
if (toAddUsers != null && toAddUsers.length > 0) {
registrationIds['add'] = toAddUsers
}
if (toRemoveUsers != null && toRemoveUsers.length > 0) {
registrationIds['remove'] = toRemoveUsers
}
var json = {}
json['registration_ids'] = registrationIds
debug(json['registration_ids'])
return _request(this, url, JSON.stringify(json), 'POST', callback)
}
function deleteTag (theTag, platform, callback) {
var url = HOST_NAME_SSL + TAG_PATH + '/' + theTag
if (platform != null) {
url += ('/?platform=' + platform)
}
return _request(this, url, null, 'delete', callback)
}
function getAliasDeviceList (alias, platform, callback) {
var url = HOST_NAME_SSL + ALIAS_PATH + '/' + alias
if (platform != null) {
url += '/?platform=' + platform
}
return _request(this, url, null, 'GET', callback)
}
function deleteAlias (alias, platform, callback) {
var url = HOST_NAME_SSL + ALIAS_PATH + '/' + alias
if (platform != null) {
url += '/?platform=' + platform
}
return _request(this, url, null, 'delete', callback)
}
function validate (payload, callback) {
return _request(this, PUSH_API_URL + VALIDATE, payload, 'POST', callback)
}
// 定时任务 start
function setSchedule (payload, callback) {
return _request(this, SCHEDULE_API_URL, payload, 'POST', callback)
}
function updateSchedule (scheduleId, payload, callback) {
var url = SCHEDULE_API_URL + '/' + scheduleId
return _request(this, url, payload, 'PUT', callback)
}
// 获取有效的定时任务列表。
function getScheduleList (page, callback) {
if (typeof page !== 'number') {
throw new JError.InvalidArgumentError('Invalid argument, it can only be set to the Number.')
}
var url = SCHEDULE_API_URL + '?page=' + page
return _request(this, url, null, 'GET', callback)
}
// 获取指定的定时任务。
function getSchedule (scheduleId, callback) {
if (typeof scheduleId !== 'string') {
throw new JError.InvalidArgumentError('Invalid argument, it can only be set to the String.')
}
var url = SCHEDULE_API_URL + '/' + scheduleId
return _request(this, url, null, 'GET', callback)
}
// 删除指定的定时任务。
function delSchedule (scheduleId, callback) {
if (typeof scheduleId !== 'string') {
throw new JError.InvalidArgumentError('Invalid argument, it can only be set to the String.')
}
var url = SCHEDULE_API_URL + '/' + scheduleId
return _request(this, url, null, 'DELETE', callback)
}
// 获取定时任务对应的所有 msg_id
function getScheduleMsgIds (scheduleId, callback) {
if (typeof scheduleId !== 'string') {
throw new JError.InvalidArgumentError('Invalid argument, it can only be set to the String.')
}
var url = SCHEDULE_API_URL + '/' + scheduleId + '/msg_ids'
return _request(this, url, null, 'GET', callback)
}
/**
* 获取推送唯一标识符
* http://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#cid
* @param {*} count 可选参数。数值类型,不传则默认为 1。范围为 [1, 1000]
* @param {*} type 可选参数。CID 类型。取值:push(默认),schedule
* @param {*} callback
*/
function getCid(count, type, callback) {
if (!count) {
count = 1;
}
if (!type) {
type = 'push';
}
var url = PUSH_API_URL + '/cid?count=' + count + '&type=' + type;
return _request(this, url, null, 'GET', callback);
}
/**
* 针对RegID方式批量单推(VIP专属接口)
* http://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#vip
* @param {*} singlePayloads 单推payload数组
* @param {*} callback
*/
function batchPushByRegid(singlePayloads, callback) {
var url = PUSH_API_URL + '/batch/regid/single';
return batchPush.call(this, url, singlePayloads, callback);
}
/**
* 针对Alias方式批量单推(VIP专属接口)s
* http://docs.jiguang.cn/jpush/server/push/rest_api_v3_push/#vip
* @param {*} singlePayloads 单推payload数组
* @param {*} callback
*/
function batchPushByAlias(singlePayloads, callback) {
var url = PUSH_API_URL + '/batch/alias/single';
return batchPush.call(this, url, singlePayloads, callback);
}
function batchPush(url, singlePayloads, callback) {
var client = this;
return getCid.call(client, singlePayloads.length, 'push', function(err, res) {
if (err) {
return callback(err);
}
var body = {"pushlist":{}};
for (var i = 0; i < singlePayloads.length; i++) {
body.pushlist[res.cidlist[i]] = singlePayloads[i];
}
return _request(client, url, JSON.stringify(body), 'POST', callback);
});
}
// 定时任务 end
/**
* 获取用户在线状态(vip专属接口)
* https://docs.jiguang.cn//jpush/server/push/rest_api_v3_device/#vip
* @param {*} regIds 需要在线状态的用户 registration_id
* @param {*} callback
*/
function getDeviceStatus(regIds, callback) {
var json = {
"registration_ids": regIds
};
var url = HOST_NAME_SSL + DEVICE_PATH + '/status/';
return _request(this, url, JSON.stringify(json), 'POST', callback);
}
// Proxy start
// Proxy end
function _request (client, url, body, method, callback, times = 1) {
if (client.isDebug) {
debug('Push URL :' + url)
if (body) {
debug('Body :' + body)
}
// debug("Auth :" + JSON.stringify(auth))
debug('Method :' + method)
debug('Times/MaxTryTimes : ' + times + '/' + client.maxTryTimes)
}
var _callback = function (err, res, body) {
if (err) {
if (err.code === 'ETIMEDOUT' && err.syscall !== 'connect') {
// response timeout
return callback(new JError.APIConnectionError(
'Response timeout. Your request to the server may have already received, please check whether or not to push',
true))
} else if (err.code === 'ENOTFOUND') {
// unknown host
return callback(new JError.APIConnectionError('Known host : ' + url))
}
// other connection error
if (times < client.maxTryTimes) {
return _request(client, url, body, method, callback, times + 1)
} else {
return callback(new JError.APIConnectionError('Connect timeout. Please retry later.'))
}
}
if (res.statusCode === 200) {
if (body.length !== 0) {
if (client.isDebug) {
debug('Success, response : ' + body)
}
try {
callback(null, JSON.parse(body))
} catch (e) {
callback(e)
}
} else {
if (client.isDebug) {
debug('Success, response : ' + body)
}
callback(null, 200)
}
} else {
if (client.isDebug) {
debug('Fail, HttpStatusCode: ' + res.statusCode + ' result: ' + body.toString())
}
callback(new JError.APIRequestError(res.statusCode, body))
}
}
Request2(client, url, { method, body: JSON.parse(body), timeout: client.readTimeOut }, _callback);
}
JPushClient.prototype.sendPush = sendPush
JPushClient.prototype.getReportReceiveds = getReportReceiveds
JPushClient.prototype.getReportReceivedDetail = getReportReceivedDetail
JPushClient.prototype.getReportStatusMessage = getReportStatusMessage
JPushClient.prototype.getReportMessagesDetail = getReportMessagesDetail
JPushClient.prototype.push = push
JPushClient.prototype.setMobile = setMobile
JPushClient.prototype.getDeviceTagAlias = getDeviceTagAlias
JPushClient.prototype.updateDeviceTagAlias = updateDeviceTagAlias
JPushClient.prototype.getTagList = getTagList
JPushClient.prototype.isDeviceInTag = isDeviceInTag
JPushClient.prototype.addRemoveDevicesFromTag = addRemoveDevicesFromTag
JPushClient.prototype.deleteTag = deleteTag
JPushClient.prototype.getAliasDeviceList = getAliasDeviceList
JPushClient.prototype.deleteAlias = deleteAlias
JPushClient.prototype.getDeviceStatus = getDeviceStatus
JPushClient.prototype.validate = validate
JPushClient.prototype.getReportMessages = getReportMessages
JPushClient.prototype.getReportUsers = getReportUsers
JPushClient.prototype.getScheduleList = getScheduleList
JPushClient.prototype.getSchedule = getSchedule
JPushClient.prototype.delSchedule = delSchedule
JPushClient.prototype.setSchedule = setSchedule
JPushClient.prototype.updateSchedule = updateSchedule
JPushClient.prototype.getCid = getCid
JPushClient.prototype.batchPushByRegid = batchPushByRegid
JPushClient.prototype.batchPushByAlias = batchPushByAlias
// exports constants and methods
exports.ALL = JModel.ALL
exports.tag = JModel.tag
exports.tag_and = JModel.tag_and
exports.tag_not = JModel.tag_not
exports.alias = JModel.alias
exports.registration_id = JModel.registration_id
exports.segment = JModel.segment
exports.abtest = JModel.abtest
exports.ios = JModel.ios
exports.android = JModel.android
exports.winphone = JModel.winphone
// error
exports.APIConnectionError = JError.APIConnectionError
exports.APIRequestError = JError.APIRequestError
exports.InvalidArgumentError = JError.InvalidArgumentError