UNPKG

@zigasebenik/zte-sms

Version:

A node library to send sms messages using ZTE MF79U modem.

291 lines (268 loc) 7.78 kB
const merge = require('lodash.merge'); const http = require('http'); const qs = require('qs'); const { hex_md5 } = require('./md5'); const { getCurrentTimeString, encodeMessage, decodeMessage, transTime, promiseRetry, } = require('./utils'); class Modem { constructor({ modemIP = '192.168.0.1', modemPassword = ''} = {}) { this.modemIP = modemIP; this.modemPassword = modemPassword this.getPath = `/goform/goform_get_cmd_process`; this.setPath = `/goform/goform_set_cmd_process`; this.referer = `http://${this.modemIP}/index.html`; this.loginCookieValue = ''; this.modemVersion = ''; } async #request(options, data = '') { const headers = { Referer: this.referer }; data = qs.stringify(data); options.path = this.getPath; if (options.method === 'POST') { options.path = this.setPath; headers['Content-Length'] = data.length; } else if (options.method === 'GET') { options.path += `?${data}`; data = ''; } options = merge(options, { hostname: this.modemIP, insecureHTTPParser: true, headers, }); return new Promise((resolve, reject) => { const request = http.request(options, (response) => { let data = ''; response.on('data', (chunk) => { data += chunk.toString(); }); response.on('end', () => { resolve({ response, data: JSON.parse(data) }); }); response.on('error', (error) => { reject(error); }); }); request.on('error', (error) => { reject(error); }); data && request.write(data); request.end(); }); } async #login() { const data = { isTest: false, goformId: 'LOGIN', password: Buffer.from(this.modemPassword).toString('base64'), }; const options = { method: 'POST' }; const response = await this.#request(options, data); if (response.data.result !== '0') { throw new Error('Login to modem failed.'); } this.loginCookieValue = response.response.headers['set-cookie'][0].split(';')[0]; } async #logout() { const data = { isTest: false, goformId: 'LOGOUT', AD: await this.#getAD(), }; const options = { method: 'POST' }; await this.#request(options, data); this.loginCookieValue = ''; } async #getModemVersion() { if (this.modemVersion) { return this.modemVersion; } const data = { isTest: false, cmd: 'cr_version,wa_inner_version', multi_data: 1, } const options = { method: 'GET' }; const response = await this.#request(options, data); if (response.data.wa_inner_version || response.data.cr_version) { this.modemVersion = `${response.data?.cr_version}${response.data?.wa_inner_version}`; return this.modemVersion; } else { throw new Error('Getting modem version failed.'); } } async #getAD() { const modemVersion = await this.#getModemVersion(); const RD = await this.#getRD(); return hex_md5(`${hex_md5(modemVersion)}${RD}`); } async #getRD() { const data = { isTest: false, cmd: 'RD', } const options = { method: 'GET' }; const response = await this.#request(options, data); if (!response.data.RD) { throw new Error('Getting RD failed.'); } return response.data.RD; } async #getCmdStatusInfo(sms_cmd) { const data = { isTest: false, cmd: 'sms_cmd_status_info', sms_cmd, }; const options = { method: 'GET', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); return response.data; } async #awaitConfirmation(sms_cmd) { await promiseRetry(async () => { const info = await this.#getCmdStatusInfo(sms_cmd); return (!info?.sms_cmd_status_result || info.sms_cmd_status_result === '1') ? Promise.reject() : Promise.resolve(); }); } async getSmsCapacityInfo() { await this.#login(); const data = { isTest: false, cmd: 'sms_capacity_info', }; const options = { method: 'GET', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); await this.#logout(); return response.data; } async getAllSms() { await this.#login(); const data = { isTest: false, cmd: 'sms_data_total', page: 0, data_per_page: 5000, mem_store: 1, tags: 10, order_by: 'order by id desc', }; const options = { method: 'GET', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); await this.#logout(); response.data.messages.forEach((m) => { m.date = transTime(m.date, '3', '24'); m.content = decodeMessage(m.content); }); return response.data.messages; } async deleteSms(ids) { ids = Array.isArray(ids) ? ids : Array(ids); await this.#login(); const data = { isTest: false, goformId: 'DELETE_SMS', msg_id: `${ids.join(';')};`, notCallback: true, AD: await this.#getAD(), }; const options = { method: 'POST', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); await this.#awaitConfirmation(6); await this.#logout(); if (response.data.result !== 'success') { throw new Error('Error deleting SMS.'); } } // sms tags // 1 Unread received message. // 0 Received message. // 2 Message sent. // 3 Message failed to be sent. // 4 Draft. async deleteAllSms(tag = '') { tag = tag.toString(); let messages = await this.getAllSms(); messages = messages.filter((m) => !tag || m.tag === tag); const messageIds = messages.map((m) => m.id); return await this.deleteSms(messageIds); } async setSmsAsRead(ids) { ids = Array.isArray(ids) ? ids : Array(ids); await this.#login(); const data = { isTest: false, goformId: 'SET_MSG_READ', msg_id: `${ids.join(';')};`, tag: '0', AD: await this.#getAD(), }; const options = { method: 'POST', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); if (response.data.result !== 'success') { throw new Error('Error marking SMS as read.'); } await this.#awaitConfirmation(5); await this.#logout(); } async setAllSmsAsRead() { let messages = await this.getAllSms(); messages = messages.filter((m) => m.tag === '1'); const messageIds = messages.map((m) => m.id); return await this.setSmsAsRead(messageIds); } async sendSms(number, message) { await this.#login(); const sms_time = getCurrentTimeString(); const sms_time_compare = transTime(sms_time.replaceAll(';', ','), '3', '24'); const data = { isTest: false, goformId: 'SEND_SMS', notCallback: true, Number: number.toString(), sms_time, MessageBody: encodeMessage(message), ID: -1, encode_type: 'UNICODE', AD: await this.#getAD(), }; const options = { method: 'POST', headers: { Cookie: this.loginCookieValue }, }; const response = await this.#request(options, data); if (response.data.result !== 'success') { throw new Error('Error sending SMS.'); } await this.#awaitConfirmation(4); let messages = await this.getAllSms(); message = messages.filter((m) => m.tag === '2' && m.number === number && m.content === message && sms_time_compare === m.date ); await this.#logout(); return message; } } module.exports = Modem;