diff --git a/cache.js b/cache.js index fbd4fb3..8389650 100644 --- a/cache.js +++ b/cache.js @@ -1,30 +1,27 @@ 'use strict' function cacheContact(modContactList, obj) { - for (var o of modContactList) { - if (o.UserName.startsWith('@@')) { // 群组 + modContactList.forEach(o=>{ + if (o.UserName.startsWith('@@')) { // 群组直接替换了 + // console.log('群缓存更新', o.NickName) obj.groupContact[o.UserName] = { nickName: o.NickName, memberList: o.MemberList, } } else { // 用户 - // 查找与替换 - var length = obj.memberList.length - let find = false; - for (let i = 0; i < length; i++) { - let user = obj.memberList[i]; - if (user['UserName'] == o.UserName) { - obj.memberList[i] = o; - find = true; - break; - } - } - // 如果没有找到 - if (!find) { + // 如果不在缓存中 + var index = obj.memberList.findIndex(user=> user['UserName'] == o.UserName); + if (index < 0) { + // console.log('用户缓存推入', o.NickName) obj.memberList.push(o); + } else { + // console.log('用户缓存替换', o.NickName) + obj.memberList[index] = o; } } - } + }); } + + module.exports.cacheContact = cacheContact; diff --git a/index.js b/index.js index 4dac1ae..f0f20cc 100644 --- a/index.js +++ b/index.js @@ -8,20 +8,27 @@ var parseRedirectUrl = require('./webwx.js').parseRedirectUrl; var login = require('./webwx.js').login; var getbaseRequest = require('./webwx.js').getbaseRequest; var webwxinit = require('./webwx.js').webwxinit; + +var wechatLogger = require('./logger.js').wechatLogger; +var generateReplys = require('./reply.js').generateReplys; + var getContact = require('./webwx.js').getContact; var robot = require('./webwx.js').robot; -getUUID. - then(checkAndParseUUID). - then(showQRImage). - then(checkLogin). - then(parseRedirectUrl). - then(login). - then(getbaseRequest). - then(webwxinit). - then(getContact). - then(robot). - catch((e)=>{ +getUUID + .then(checkAndParseUUID) + .then(showQRImage) + .then(checkLogin) + .then(parseRedirectUrl) + .then(login) + .then(getbaseRequest) + .then(webwxinit) + .then(getContact) + .then(robot( + [(obj)=>o=>true], + [wechatLogger, generateReplys] + )) + .catch((e)=>{ console.error(e); process.exit(1); }); diff --git a/logger.js b/logger.js index b085afe..d2a481d 100644 --- a/logger.js +++ b/logger.js @@ -35,19 +35,26 @@ function logPrivateMsg(o, obj) { function handlePrivate(username, replyContent, obj) { // 如果没找到,请求啊 // 查看Object Array中是否有UserName属性为username的Object - function _has(list, Property, username) { - for (let l of list) { - if (l[Property] == username) { - return true; - } + // FIXME_TEST: find替换 + + var p = new Promise((resolve, reject)=>{ + if (obj.memberList.findIndex(m=>m['UserName']==username) < 0) { + // memberList中不存在 + var contactP = _requestUserInfo(username, obj); + } else { + var contactP = Promise.resolve(obj); } - return false; - } + contactP.then(_logPrivateTextMsg).catch(reject); - var p = new Promise((resolve, reject)=>{ - if (!_has(obj.memberList, 'UserName', username)) { - var contactP = new Promise((resolve, reject)=>{ + function _logPrivateTextMsg(obj) { + // FIXME_TEST: 用find替换 + var m = obj.memberList.find(m=>m.UserName==username); + resolve("[" + m.NickName + "说]" + replyContent); + } + + function _requestUserInfo(username, obj) { + return new Promise((resolve, reject)=>{ var postData = { BaseRequest: obj.BaseRequest, Count: 1, @@ -82,19 +89,6 @@ function handlePrivate(username, replyContent, obj) { resolve(obj); }); }); - } else { - var contactP = Promise.resolve(obj); - } - - contactP.then(_logPrivateTextMsg).catch(reject); - - function _logPrivateTextMsg(obj) { - for (var i = 0; i < obj.memberList.length; i++) { - if (obj.memberList[i]['UserName'] == username) { - console.log('[' + obj.memberList[i]['NickName'] + ' 说]', replyContent); - return; - } - } } }); return p; @@ -116,7 +110,21 @@ function handleGroup(groupUserName, replyContent, obj) { } // 查看是否缓存中有 if (!(groupUserName in obj.groupContact)) { - var contactP = new Promise((resolve, reject)=>{ + var contactP = _requestGroupInfo(groupUserName, obj) + } else { + var contactP = Promise.resolve(obj); + } + + contactP.then(_logGroupTextMsg); + // 记录群消息函数 + function _logGroupTextMsg(obj) { + var groupRealName = obj.groupContact[groupUserName]['nickName']; + var m = obj.groupContact[groupUserName]['memberList'].find(m=>m.UserName==fromUserName) + resolve("[" + groupRealName + "]" + m.NickName + replyContent.replace(fromUserName, '').replace("
", "")); + } + + function _requestGroupInfo(groupUserName, obj) { + return new Promise((resolve, reject)=>{ var postData = { BaseRequest: obj.BaseRequest, Count: 1, @@ -153,23 +161,8 @@ function handleGroup(groupUserName, replyContent, obj) { nickName: groupRealName, }; resolve(obj); - }); - }) - } else { - var contactP = Promise.resolve(obj); - } - - contactP.then(_logGroupTextMsg); - // 记录群消息函数 - function _logGroupTextMsg(obj) { - // 直接更新 - for (let m of obj.groupContact[groupUserName]['memberList']) { - if (fromUserName && (fromUserName == m.UserName)) { - var nickName = m.NickName; - var groupRealName = obj.groupContact[groupUserName]['nickName']; - resolve("[" + groupRealName + "]" + nickName + replyContent.replace(fromUserName, '').replace("
", "")); - } - } + }); // request + }); //promise } }); return p; diff --git a/package.json b/package.json index 2fc1dbd..7d5e4ba 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,8 @@ "dependencies": { "request": "^2.67.0", "segment": "^0.1.3" + }, + "devDependencies": { + "mocha": "^2.3.4" } } diff --git a/webwx.js b/webwx.js index 2e8b83a..9a3edea 100644 --- a/webwx.js +++ b/webwx.js @@ -42,7 +42,7 @@ var getUUID = new Promise((resolve, reject)=>{ function checkAndParseUUID(text) { var result = /window.QRLogin.code = (\d+); window.QRLogin.uuid = "([^"]+)";/.exec(text); //debug("checkAndParseUUID"); - if (result[1] != '200') { + if (!result || result[1] != '200') { return false; } return result[2]; @@ -333,68 +333,77 @@ function synccheck(obj) { return p; } -function webwxsync(obj) { - // https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=xWam498tVKzNaHLt&skey=@crypt_3bb2969_a8ec83465d303fb83bf7ddcf512c081d&lang=en_US&pass_ticket=YIBmwsusvnbs8l7Z4wtRdBXtslA8JjyHxsy0Fsf3PN8NTiP3fzhjB9rOE%252Fzu6Nur - if (!obj.webwxsync) { - return Promise.resolve(obj); - } - var p = new Promise((resolve, reject) => { - //debug('obj in webwxsync:\n' + inspect(obj)); - var BaseRequest = obj.BaseRequest; - var pass_ticket = obj.pass_ticket; - var rr = ~Date.now(); - var postData = { - BaseRequest: obj.BaseRequest, - SyncKey: obj.SyncKey - }; - var options = { - baseUrl: 'https://wx.qq.com', - uri: `/cgi-bin/mmwebwx-bin/webwxsync?sid=${obj.BaseRequest.Sid}&skey=${obj.BaseRequest.Skey}&lang=en_US&pass_ticket=${pass_ticket}&rr=${rr}`, - method: 'POST', - body: postData, - json: true, - jar: true, +function webwxsync(filters, mappers) { + return (obj)=>{ + // https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=xWam498tVKzNaHLt&skey=@crypt_3bb2969_a8ec83465d303fb83bf7ddcf512c081d&lang=en_US&pass_ticket=YIBmwsusvnbs8l7Z4wtRdBXtslA8JjyHxsy0Fsf3PN8NTiP3fzhjB9rOE%252Fzu6Nur + if (!obj.webwxsync) { + return Promise.resolve(obj); } + return new Promise((resolve, reject) => { + //debug('obj in webwxsync:\n' + inspect(obj)); + var BaseRequest = obj.BaseRequest; + var pass_ticket = obj.pass_ticket; + var rr = ~Date.now(); + var postData = { + BaseRequest: obj.BaseRequest, + SyncKey: obj.SyncKey + }; + var options = { + baseUrl: 'https://wx.qq.com', + uri: `/cgi-bin/mmwebwx-bin/webwxsync?sid=${obj.BaseRequest.Sid}&skey=${obj.BaseRequest.Skey}&lang=en_US&pass_ticket=${pass_ticket}&rr=${rr}`, + method: 'POST', + body: postData, + json: true, + jar: true, + } - //debug("options in webwxsync: \n" + inspect(options)); - //debug("postData in webwxsync: \n" + inspect(postData)); + //debug("options in webwxsync: \n" + inspect(options)); + //debug("postData in webwxsync: \n" + inspect(postData)); - // 请在评论教我该怎么在循环中优雅地使用Promise。。。 - request(options, (error, response, body)=>{ - // console.log("websync:" + inspect(obj.SyncKey)); - // fs.writeFile('webwxsync.json', JSON.stringify(body)); - // 更新 synckey - obj.SyncKey = body.SyncKey; - //debug("in websync body: " + inspect(body)) - //FIXME: 队列,非要处理完单次的更新吗? - - var replys = body.AddMsgList. - filter(o=>(o.ToUserName === obj.username)). // 过滤不是给我的信息 - filter(o=>(SPECIAL_USERS.indexOf(o.FromUserName) < 0)). // 不是特殊用户 - filter(o=>true). // 用户定义黑白名单 - - map(wechatLogger(obj)). // 日志 - map(generateReplys(obj)); // 回复 - - // get all replys resolved 所有回复完成 - // FIXME: 不对,如果单个消息回复失败则不该所有该批次更新都失败 - // 也许可以对失败回复回复以特殊值undefined - Promise.all(replys).then(()=>{ - resolve(obj); // 在回调中控制权交给botSpeak + // 请在评论教我该怎么在循环中优雅地使用Promise。。。 + request(options, (error, response, body)=>{ + // console.log("websync:" + inspect(obj.SyncKey)); + // fs.writeFile('webwxsync.json', JSON.stringify(body)); + // 更新 synckey + obj.SyncKey = body.SyncKey; + //debug("in websync body: " + inspect(body)) + //FIXME: 队列,非要处理完单次的更新吗? + //FIXME: 将这些filter和map作为参数以类似eventListener注册的方式传入? + + var replys = body.AddMsgList // 先是默认filter + .filter(o=>(o.ToUserName === obj.username)) // 过滤不是给我的信息 + .filter(o=>(SPECIAL_USERS.indexOf(o.FromUserName) < 0)) // 不是特殊用户 + + filters.forEach(f=> { + replys=replys.filter(f(obj)); + }); + + mappers.forEach(f=> { + replys=replys.map(f(obj)); + }); + + // get all replys resolved 所有回复完成 + // FIXME: 不对,如果单个消息回复失败则不该所有该批次更新都失败 + // 也许可以对失败回复回复以特殊值undefined + Promise.all(replys).then(()=>{ + resolve(obj); // 在回调中控制权交给botSpeak + }); + + // 更新联系人如果有的话 + cacheContact(body.ModContactList, obj); }); - - // 更新联系人如果有的话 - cacheContact(body.ModContactList, obj); }); - }); - return p; + } } -function robot(obj) { - synccheck(obj). - then(webwxsync). - then(botSpeak).then(robot). - catch(console.error); +function robot(filters, mappers) { + return (obj) => { + synccheck(obj) + .then(webwxsync(filters, mappers)) + .then(botSpeak) + .then(robot(filters, mappers)) + .catch(console.error); + } } function processExit(code, signal) {