Last active
August 6, 2019 10:08
-
-
Save adi-li/5f3196ecb7705360fb6c1c160e44e65f to your computer and use it in GitHub Desktop.
Unofficial Meteorsis API for node.js (https://www.meteorsis.com)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const rp = require('request-promise-native'); | |
const https = require('https'); | |
const querystring = require('querystring'); | |
const iconv = require('iconv-lite'); | |
const baseUrl = 'https://www.meteorsis.com/misweb/'; | |
const MeteorsisApiErrorCodeMap = { | |
1: '非預期系統錯誤', | |
2: '用戶名稱與密碼不符', | |
3: '戶口己被停用', | |
5: '短訊的排程日期只限於 5 年之內', | |
6: '短訊內容必須存在,同時英文短訊的字數只限於 459;或統一碼短訊的字數 只限於 201', | |
8: '顯示名稱必須存在,並只限於 11 個位的英文及數字組合或 16 個位的數字', | |
9: 'SMSD ID 不存在', | |
16: '餘額不足', | |
17: '電話號碼格式錯誤', | |
18: '中國短訊的內容包含非法詞語', | |
20: '連接逾時', | |
240: '網址格式錯誤:參數數目不符', | |
241: '網址格式錯誤:content', | |
242: '網址格式錯誤:langeng', | |
243: '網址格式錯誤:recipient', | |
244: '網址格式錯誤:dos', | |
245: '網址格式錯誤:senderid', | |
246: '網址格式錯誤:smsdid', | |
247: '網址格式錯誤:flash', | |
248: '網址格式錯誤:dncr', | |
249: '網址格式錯誤:URL 包含危險性的字元', | |
}; | |
const MeteorsisSmsStatusMap = { | |
1: '上傳失敗', | |
2: '上傳成功', | |
3: '排程', | |
4: '處理', | |
19: '重發', | |
20: '過期', | |
21: '無法傳送', | |
22: '確認傳送', | |
}; | |
const MeteorsisSmsErrorCode = { | |
300: '不適用', | |
301: '回條未接收', | |
302: '號碼格式錯誤', | |
303: '號碼不存在、己停用', | |
304: '手機已關、接收不良、飛線', | |
305: '短訊匣已滿', | |
306: '短訊匣已滿', | |
307: '拒收名冊號碼', | |
308: '餘額不足', | |
309: '中國短訊非法詞語', | |
312: '訊息被封鎖,例如客戶欠款、濫發訊息、客戶拒收、程式攔截', | |
315: '本地電訊商系統故障', | |
326: '短訊收費超出上限', | |
}; | |
const chunk = (array, chunkSize) => { | |
const result = []; | |
for (let index = 0; index < array.length; index += chunkSize) { | |
result.push(array.slice(index, index + chunkSize)) | |
} | |
return result; | |
}; | |
class MeteorsisApiError extends Error { | |
static fromCode(code) { | |
return new MeteorsisApiError(`API error: ${code} ${MeteorsisApiErrorCodeMap[code] || 'Unknown Error'}`); | |
} | |
} | |
class MeteorsisApi { | |
constructor(username, password) { | |
this.username = username; | |
this.password = password; | |
this.defaultSendingOptions = { | |
dos: 'now', // schedule time, 'now' / 'yyyy-MM-dd hh:mm' | |
dncr: 0, // 1 for skipping the numbers in Do-not-call Registers | |
senderid: 'RANDOMID', // specify sender id | |
}; | |
this.client = rp.defaults({ | |
// dangerous server, but better than no SSL at all | |
agent: new https.Agent({ | |
host: 'www.meteorsis.com', | |
port: 443, | |
ciphers: 'DES-CBC3-SHA', | |
rejectUnauthorized: false, | |
}), | |
}); | |
} | |
async sendRequest({ action, ...options }) { | |
const resp = await this.client({ | |
uri: `${baseUrl}${action}`, | |
method: 'GET', | |
...options, | |
}); | |
const result = querystring.parse(resp, ';', ':'); | |
if ('ERROR' in result) { | |
throw MeteorsisApiError.fromCode(result.ERROR); | |
} | |
return result; | |
} | |
async sendSms(recipient, content, options = {}) { | |
const recipients = ( | |
recipient instanceof Array | |
? recipient | |
: [recipient] | |
) | |
.map(rpt => rpt.replace(/[\D]/g, '')); | |
// eslint-disable-next-line no-control-regex | |
const langeng = /^[\x00-\x7F]*$/g.test(content) ? 1 : 0; | |
const encodedContent = langeng | |
? content | |
: iconv | |
.encode(content, 'UTF-16BE') | |
.toString('hex') | |
.toUpperCase(); | |
const params = { | |
username: this.username, | |
password: this.password, | |
langeng, | |
...this.defaultSendingOptions, | |
...options, | |
content: encodedContent, | |
}; | |
const result = []; | |
const chunkedRecipients = chunk(recipients, 20); | |
for (let index = 0; index < chunkedRecipients.length; index++) { | |
const chunkedRecipient = chunkedRecipients[index]; | |
// eslint-disable-next-line no-await-in-loop | |
const resp = await this.sendRequest({ | |
action: 'f_sendsms.aspx', | |
qs: { | |
...params, | |
recipient: chunkedRecipient.join(','), | |
}, | |
}); | |
const smsdids = resp.SMSDID.split(','); | |
result.push(...smsdids); | |
} | |
return { SMSDID: result }; | |
} | |
async getSmsStatus(smsdid) { | |
return this.sendRequest({ | |
action: 'f_getsmsdstatus.aspx', | |
qs: { | |
smsdid, | |
username: this.username, | |
password: this.password, | |
}, | |
}); | |
} | |
async getUserCredit() { | |
return this.sendRequest({ | |
action: 'f_getusercredit.aspx', | |
qs: { | |
username: this.username, | |
password: this.password, | |
}, | |
}); | |
} | |
async getServerQueue() { | |
return this.sendRequest({ | |
action: 'f_getserverqueue.aspx', | |
qs: { | |
username: this.username, | |
password: this.password, | |
}, | |
}); | |
} | |
static webhook(req, res) { | |
console.log(req.query); | |
if (req.query.smsdstatus !== '22') { | |
// no send receipt was replied | |
// try resend or do something else | |
console.log( | |
'SMS not sent', | |
MeteorsisSmsStatusMap[req.query.smsdstatus], | |
MeteorsisSmsErrorCode[req.query.smsderrorcode], | |
); | |
} | |
res.send('OK'); | |
} | |
} | |
module.exports = MeteorsisApi; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment