Riceneeder 58ce6cb67b wewechat++ init
仓库提交至星火社区作品集

Signed-off-by: Riceneeder <86492950+Riceneeder@users.noreply.github.com>
2022-09-01 20:38:13 +08:00

348 lines
12 KiB
JavaScript

/* eslint-disable no-eval */
import axios from 'axios';
import { observable, action } from 'mobx';
import helper from 'utils/helper';
import storage from 'utils/storage';
import { normalize } from 'utils/emoji';
import chat from './chat';
import contacts from './contacts';
const CancelToken = axios.CancelToken;
const headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
'client-version': '2.0.0',
extspam: 'Go8FCIkFEokFCggwMDAwMDAwMRAGGvAESySibk50w5Wb3uTl2c2h64jVVrV7gNs06GFlWplHQbY/5FfiO++1yH4ykCyNPWKXmco+wfQzK5R98D3so7rJ5LmGFvBLjGceleySrc3SOf2Pc1gVehzJgODeS0lDL3/I/0S2SSE98YgKleq6Uqx6ndTy9yaL9qFxJL7eiA/R3SEfTaW1SBoSITIu+EEkXff+Pv8NHOk7N57rcGk1w0ZzRrQDkXTOXFN2iHYIzAAZPIOY45Lsh+A4slpgnDiaOvRtlQYCt97nmPLuTipOJ8Qc5pM7ZsOsAPPrCQL7nK0I7aPrFDF0q4ziUUKettzW8MrAaiVfmbD1/VkmLNVqqZVvBCtRblXb5FHmtS8FxnqCzYP4WFvz3T0TcrOqwLX1M/DQvcHaGGw0B0y4bZMs7lVScGBFxMj3vbFi2SRKbKhaitxHfYHAOAa0X7/MSS0RNAjdwoyGHeOepXOKY+h3iHeqCvgOH6LOifdHf/1aaZNwSkGotYnYScW8Yx63LnSwba7+hESrtPa/huRmB9KWvMCKbDThL/nne14hnL277EDCSocPu3rOSYjuB9gKSOdVmWsj9Dxb/iZIe+S6AiG29Esm+/eUacSba0k8wn5HhHg9d4tIcixrxveflc8vi2/wNQGVFNsGO6tB5WF0xf/plngOvQ1/ivGV/C1Qpdhzznh0ExAVJ6dwzNg7qIEBaw+BzTJTUuRcPk92Sn6QDn2Pu3mpONaEumacjW4w6ipPnPw+g2TfywJjeEcpSZaP4Q3YV5HG8D6UjWA4GSkBKculWpdCMadx0usMomsSS/74QgpYqcPkmamB4nVv1JxczYITIqItIKjD35IGKAUwAA==',
referer: 'https://wx.qq.com/?&lang=zh_CN&target=t',
};
class Session {
@observable loading = true;
@observable auth;
@observable code;
@observable avatar;
@observable user;
syncKey;
genSyncKey(list) {
return (self.syncKey = list.map(e => `${e.Key}_${e.Val}`).join('|'));
}
@action async getCode() {
var response = await axios.get('https://login.wx.qq.com/jslogin', {
params: {
appid: 'wx782c26e4c19acffb',
fun: 'new',
redirect_uri: 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop',
lang: 'zh_CN'
},
headers: headers,
});
// var response = await axios.get('https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=en_US&_=' + +new Date());
var code = response.data.match(/[A-Za-z_\-\d]{10}==/)[0];
self.code = code;
self.check();
return code;
}
@action async check() {
// Already logined
if (self.auth) return;
var response = await axios.get('https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login', {
params: {
loginicon: true,
uuid: self.code,
tip: 1,
r: ~new Date(),
_: +new Date(),
},
headers: headers
});
eval(response.data);
switch (window.code) {
case 200:
let authAddress = window.redirect_uri;
// Set your weChat network route, otherwise you will got a code '1102'
axios.defaults.baseURL = authAddress.match(/^https:\/\/(.*?)\//)[0];
delete window.redirect_uri;
delete window.code;
delete window.userAvatar;
// Login success, create session
let response = await axios.get(authAddress, {
params: {
fun: 'new',
version: 'v2',
},
headers: headers
});
let auth = {};
try {
auth = {
baseURL: axios.defaults.baseURL,
skey: response.data.match(/<skey>(.*?)<\/skey>/)[1],
passTicket: response.data.match(/<pass_ticket>(.*?)<\/pass_ticket>/)[1],
wxsid: response.data.match(/<wxsid>(.*?)<\/wxsid>/)[1],
wxuin: response.data.match(/<wxuin>(.*?)<\/wxuin>/)[1],
};
} catch (ex) {
window.alert('Your login may be compromised. For account security, you cannot log in to Web WeChat. You can try mobile WeChat or Windows WeChat.');
window.location.reload();
}
self.auth = auth;
await storage.set('auth', auth);
await self.initUser();
self.keepalive().catch(ex => self.logout());
break;
case 201:
// Confirm to login
self.avatar = window.userAvatar;
self.check();
break;
case 400:
// QR Code has expired
window.location.reload();
return;
default:
// Continue call server and waite
self.check();
}
}
@action async initUser() {
var response = await axios.post(`/cgi-bin/mmwebwx-bin/webwxinit?r=${-new Date()}&pass_ticket=${self.auth.passTicket}`, {
BaseRequest: {
Sid: self.auth.wxsid,
Uin: self.auth.wxuin,
Skey: self.auth.skey,
}
});
await axios.post(`/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=en_US&pass_ticket=${self.auth.passTicket}`, {
BaseRequest: {
Sid: self.auth.wxsid,
Uin: self.auth.wxuin,
Skey: self.auth.skey,
},
ClientMsgId: +new Date(),
Code: 3,
FromUserName: response.data.User.UserName,
ToUserName: response.data.User.UserName,
});
self.user = response.data;
self.user.ContactList.map(e => {
e.HeadImgUrl = `${axios.defaults.baseURL}${e.HeadImgUrl.substr(1)}`;
});
await contacts.getContats();
await chat.loadChats(self.user.ChatSet);
return self.user;
}
async getNewMessage() {
var auth = self.auth;
var response = await axios.post(`/cgi-bin/mmwebwx-bin/webwxsync?sid=${auth.wxsid}&skey=${auth.skey}&lang=en_US&pass_ticket=${auth.passTicket}`, {
BaseRequest: {
Sid: auth.wxsid,
Uin: auth.wxuin,
Skey: auth.skey,
},
SyncKey: self.user.SyncKey,
rr: ~new Date(),
});
var mods = [];
// Refresh the sync keys
self.user.SyncKey = response.data.SyncCheckKey;
self.genSyncKey(response.data.SyncCheckKey.List);
// Get the new friend, or chat room has change
response.data.ModContactList.map(e => {
var hasUser = contacts.memberList.find(user => user.UserName === e.UserName);
if (hasUser) {
// Just update the user
contacts.updateUser(e);
} else {
// If user not exists put it in batch list
mods.push(e.UserName);
}
});
// Delete user
response.data.DelContactList.map((e) => {
contacts.deleteUser(e.UserName);
chat.removeChat(e);
});
if (mods.length) {
await contacts.batch(mods, true);
}
response.data.AddMsgList.map(e => {
var from = e.FromUserName;
var to = e.ToUserName;
var fromYourPhone = from === self.user.User.UserName && from !== to;
// When message has been readed on your phone, will receive this message
if (e.MsgType === 51) {
return chat.markedRead(fromYourPhone ? from : to);
}
e.Content = normalize(e.Content);
// Sync message from your phone
if (fromYourPhone) {
// Message is sync from your phone
chat.addMessage(e, true);
return;
}
if (from.startsWith('@')) {
chat.addMessage(e);
}
});
return response.data;
}
// A callback for cancel the sync request
cancelCheck = window.Function;
checkTimeout(weakup) {
// Kill the zombie request or duplicate request
self.cancelCheck();
clearTimeout(self.checkTimeout.timer);
if (helper.isSuspend() || weakup) {
return;
}
self.checkTimeout.timer = setTimeout(() => {
self.cancelCheck();
}, 30 * 1000);
}
async keepalive() {
var auth = self.auth;
var response = await axios.post(`/cgi-bin/mmwebwx-bin/webwxsync?sid=${auth.wxsid}&skey=${auth.skey}&lang=en_US&pass_ticket=${auth.passTicket}`, {
BaseRequest: {
Sid: auth.wxsid,
Uin: auth.wxuin,
Skey: auth.skey,
},
SyncKey: self.user.SyncKey,
rr: ~new Date(),
});
var host = axios.defaults.baseURL.replace('//', '//webpush.');
var loop = async() => {
// Start detect timeout
self.checkTimeout();
var response = await axios.get(`${host}cgi-bin/mmwebwx-bin/synccheck`, {
cancelToken: new CancelToken(exe => {
// An executor function receives a cancel function as a parameter
this.cancelCheck = exe;
}),
params: {
r: +new Date(),
sid: auth.wxsid,
uin: auth.wxuin,
skey: auth.skey,
synckey: self.syncKey,
}
}).catch(ex => {
if (axios.isCancel(ex)) {
loop();
} else {
self.logout();
}
});
if (!response) {
// Request has been canceled
return;
}
eval(response.data);
if (+window.synccheck.retcode === 0) {
// 2, Has new message
// 6, New friend
// 4, Conversation refresh ?
// 7, Exit or enter
let selector = +window.synccheck.selector;
if (selector !== 0) {
await self.getNewMessage();
}
// Do next sync keep your wechat alive
return loop();
} else {
self.logout();
}
};
// Load the rencets chats
response.data.AddMsgList.map(
async e => {
await chat.loadChats(e.StatusNotifyUserName);
}
);
self.loading = false;
self.genSyncKey(response.data.SyncCheckKey.List);
return loop();
}
@action async hasLogin() {
var auth = await storage.get('auth');
axios.defaults.baseURL = auth.baseURL;
self.auth = auth && Object.keys(auth).length ? auth : void 0;
if (self.auth) {
await self.initUser().catch(ex => self.logout());
self.keepalive().catch(ex => self.logout());
}
return auth;
}
@action async logout() {
var auth = self.auth;
try {
await axios.post(`/cgi-bin/mmwebwx-bin/webwxlogout?skey=${auth.skey}&redirect=0&type=1`, {
sid: auth.sid,
uin: auth.uid,
});
} finally {
self.exit();
}
}
async exit() {
await storage.remove('auth');
window.location.reload();
}
}
const self = new Session();
export default self;