Light12306/RwTicketAssistantV3/app/background.js

1415 lines
38 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var MANIFEST = chrome.runtime.getManifest();
//var INFOBAR = chrome.infobars || (chrome.experimental && chrome.experimental.infobars);
var isDebug = (compressFunc + '').indexOf("false") !== -1;
var bv = window.external.LiebaoGetVersion && window.external.LiebaoGetVersion() || /Chrome\/([\d\.]+)/i.exec(navigator.userAgent)[1];
var entry12306 = false;
var log = (function () {
var fun = function () {
this.print = function () {
/// <summary>向控制台打印消息</summary>
if (!isDebug) return;
console.log.apply(console, $.makeArray(arguments));
};
return this;
};
return new fun();
})();
function compressFunc() {
return false;
}
var notification = (function () {
var notify = function () {
var __ = this;
var notifyMap = {};
this.mapParam = function (opts) {
return {
type: opts.type || "basic",
title: opts.title || "提示",
message: opts.message || "",
iconUrl: opts.iconUrl || "/icons/icon_n.png",
//contextMessage: opts.contextMessage || "",
priority: opts.priority || 0,
eventTime: opts.eventTime || null,
buttons: _.map(opts.buttons, function (e) {
return { title: e.title, iconUrl: e.iconUrl || "/icons/icon_16.png" };
}) || null,
imageUrl: opts.imageUrl || null,
items: _.map(opts.items, function (e) {
return { title: e.title, message: e.message };
}) || null//,
//progress: opts.progress || null,
//isClickable: opts.isClickable || true
};
};
__.create = function (opts) {
var opt = __.mapParam(opts);
var id = opts.id || "";
chrome.notifications.create(id, opt, function (_id) {
notifyMap[_id] = opts;
opts.id = _id;
});
opts.update = function () {
__.update(opts);
};
opts.clear = function () {
__.clear(opts.id);
};
};
__.update = function (opts) {
var opt = __.mapParam(opts);
chrome.notifications.update(opts.id, opt, function () {
opts.onUpdate && opts.onUpdate.apply(opts, $.makeArray(arguments));
});
};
__.clear = function (id) {
chrome.notifications.clear(id, function () {
opts.onClear && opts.onClear.apply(opts, $.makeArray(arguments));
});
};
__.getAll = function (callback) {
chrome.notifications.getAll(callback);
};
__.getPermissionLevel = function (callback) {
chrome.notifications.getPermissionLevel(callback);
};
chrome.notifications.onClicked.addListener(function (id) {
var opts = notifyMap[id];
if (opts === undefined || opts === null) return;
if ($.isFunction(opts.onClick)) {
opts.onClick.apply(this);
}
});
chrome.notifications.onClosed.addListener(function (id, byUser) {
var opts = notifyMap[id];
if (opts === undefined || opts === null) return;
if ($.isFunction(opts.onClose)) {
opts.onClose.apply(this, [byUser]);
}
});
chrome.notifications.onButtonClicked.addListener(function (id, index) {
var opts = notifyMap[id];
if (opts === undefined || opts === null) return;
var button = opts.buttons && opts.buttons[index];
if (!button || !$.isFunction(button.onClick)) return;
button.onClick.apply(opts);
});
chrome.runtime.onMessage.addListener(function (m, sender) {
if (m.action === "showNotification")
__.create(
{ message: m.detail.message, title: m.detail.title, iconUrl: m.detail.iconUrl }
);
else if (m.action === "notify") __.create(m.detail);
else if (m.action === "attention" && sender.tab) {
chrome.tabs.update((m.detail && m.detail.tabid) || sender.tab.id, { active: true, highlighted: true });
chrome.windows.update((m.detail && m.detail.windowid), { drawAttention: true, focused: true, state: "maximized" });
}
});
chrome.runtime.onMessage.addListener(function (m, sender) {
if (!m || !m.action)
return;
if (m.action === "attention" && sender.tab) {
chrome.tabs.update((m.detail && m.detail.tabid) || sender.tab.id, { active: true, highlighted: true });
chrome.windows.update((m.detail && m.detail.windowid), { drawAttention: true, focused: true, state: "maximized" });
}
});
};
return new notify();
})();
//#region 系统配置处理
var CFG_MANGER = (function () {
function manager() {
var __ = this;
var loaded = false;
var lastUpdate = null;
var sleepTime = 1000 * 1200;
var timerId = null;
this.baseConfig = {
appendCacheTagHeader: true,
showRealSeatCount: true,
autoSubmitAfterVerifyCode: true,
autoLoginAfterVerifyCode: true,
rememberLoginUser: true,
rememberLoginPwd: true,
enableSoundPrompt: true,
enablePopupPrompt: true,
musicUrl: "http://static.liebao.cn/resources/audio/music2.ogg",
submitOrderDelay: 5,
refreshDelay: 5,
autoWaitToSell: true,
keepOnline: true,
blockDynamicJs: false,
enableRealTimeTicketQuery: true,
showTicketPrice: true,
otnAutoConfirmOrderDelay: 0,
dynamicAutoConfirmOrderDelay: 1000,
dynamicAutoSubmitOrderDelay: 5000,
otnAutoSubmitOrderDelay: 0,
enableServerAutoChange: true,
enableAutoCaptcha: true && typeof window.external.LieBaoSign === 'function',
autoCaptchaFailedLimit: 5,
captchaServerUrl: "http://api.12306.liebao.cn/code.php",
appendPriceUnit: true,
blockVcVerify: false,
blockQueueCount: false,
chatServerApi: "http://srv.12306.liebao.cn/room/list"
};
//if (isDebug)
// this.baseConfig.chatServerApi = "http://chatdev.fishlee.net/room/list";
this.sysConfig = {};
this.userConfig = { enableAutoCaptcha: false };
if (localStorage["sysConfig"])
this.sysConfig = $.extend(this.sysConfig, JSON.parse(localStorage["sysConfig"]));
if (localStorage["userConfig"])
this.userConfig = JSON.parse(localStorage["userConfig"]);
this.refresh = function () {
__.config = $.extend({}, __.baseConfig, __.userConfig, __.sysConfig);
};
this.refresh();
this.isEnabled = function (tag) {
};
var msgHandler = function (m, s, r) {
if (m && m.action === 'enter12306') {
sleepTime = 20 * 60 * 1000;
//if (lastUpdate === null || (new Date() - lastUpdate) >= 20 * 60 * 1000)
// __.refreshConfig();
}
if (m && m.action === 'setUserConfig') {
__.userConfig = $.extend({}, __.userConfig, m.detail);
__.config = $.extend({}, __.baseConfig, __.userConfig, __.sysConfig);
localStorage["userConfig"] = JSON.stringify(__.userConfig);
chrome.runtime.sendMessage({ action: "sysConfigUpdate", detail: __.config });
}
};
chrome.runtime.onMessage.addListener(msgHandler);
chrome.runtime.onMessageExternal.addListener(msgHandler);
//this.refreshConfig();
loaded = true;
return this;
};
var mm = new manager();
var mmCallback = function (m, s, r) {
if (m.action === "getSysConfig") {
r({ action: "responseSysConfig", detail: mm.config });
}
if (m.action === "getBaseSysConfig") {
r({ action: "responseBaseSysConfig", detail: $.extend(mm.baseConfig, mm.sysConfig) });
}
};
chrome.runtime.onMessage.addListener(mmCallback);
chrome.runtime.onMessageExternal.addListener(mmCallback);
return mm;
})();
//#endregion
//#region 检测更新和更新通知
//TODO 检测更新
(function showNotificationIfUpdated() {
if (window.localStorage["cv"] != MANIFEST.version) {
window.localStorage["cv"] = MANIFEST.version;
notification.create({
title: "更新成功",
message: "您的订票助手已成功更新至 " + MANIFEST.version + " :-)"
});
}
})();
//#endregion
//#region 对请求的处理
(function requestManager() {
var filter = {
urls: ["*://*.12306.cn/*"],
types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]
};
var extraInfo = ["blocking", "requestHeaders"];
var autoEnableTrainClassRandom = function (url) {
var qtc = /trainClass=([^&]+)&/g.exec(url)[1];
var tc = qtc.replace(/%23/g, "#").split('#');
if (tc[0] == 'QB') return null;
//QB#D#Z#T#K#QT#
var flags = [0, 0, 0, 0, 0];
for (var t in tc) {
var code = tc[t];
if (code == 'D') flags[0] = 1;
else if (code == 'Z') flags[1] = 1;
else if (code == 'T') flags[2] = 1;
else if (code == 'K') flags[3] = 1;
else if (code == 'QT') flags[4] = 1;
}
//随机化各个请求
var result = [];
var order = ["D", "Z", "T", "K", "QT"];
for (var i = 0; i < flags.length; i++) {
if (!flags[i])
flags[i] = Math.random() > 0.5 ? 1 : 0;
if (flags[i])
result.push(order[i]);
}
//合并请求
var ntc = result.join("#") + "#";
return url.replace(qtc, escape(ntc));
};
function parseCookies(v) {
var arr = {};
v.split('; ').forEach(function (x) { var idx = x.indexOf("="); arr[x.substr(0, idx)] = x.substr(idx + 1); });
return arr;
};
function processRequest(details) {
var url = details.url;
var skipBlacklist = !CFG_MANGER.config.blockDynamicJs;
var newHeaders = {};
var newCookies = null;
var oldCookiesValue = null;
for (var i = 0; i < details.requestHeaders.length; ++i) {
var h = details.requestHeaders[i];
var name = h.name;
var value = h.value;
if (name === 'User-Agent') {
newHeaders[name] = "Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; Trident/5.0;)";
} else if (h.name == "TRefer") {
newHeaders["Referer"] = value;
} else if (h.name == "Referer") {
if (!newHeaders["Referer"] && value.indexOf("12306.cn") != -1)
newHeaders["Referer"] = value;
} else if (name === "Cookie") {
oldCookiesValue = value;
newHeaders["Cookie"] = value;
} else if (name.indexOf("Fish-") !== -1) {
name = name.substr(5);
if (name === "Cookie") {
newCookies = parseCookies(value);
} else {
if (!value) {
if (newHeaders[name])
delete newHeaders[name];
} else {
newHeaders[name] = value;
}
}
skipBlacklist = true;
} else if (!newHeaders[name]) {
newHeaders[name] = value;
}
}
if (newCookies) {
var oc = parseCookies(oldCookiesValue);
newCookies = _.extend({}, oc, newCookies);
newHeaders["Cookie"] = _.filter(_.map(newCookies, function (v, k) {
if (!v)
return "";
return k + "=" + encodeURIComponent((v));
}), function (v) { return v || false; }).join("; ");
if (!newHeaders["Cookie"]) {
delete newHeaders["Cookie"];
}
}
var headerCollection = [];
for (var i in newHeaders) {
headerCollection.push({ name: i, value: newHeaders[i] });
}
if (!skipBlacklist && $.isFunction(window.cbl)) {
//检测是否在黑名单中
if (window.cbl.call(details, url, newHeaders)) {
return { cancel: true };
}
}
if (CFG_MANGER.config.appendCacheTagHeader && url.indexOf("querySingleAction.do?method=queryLeftTicket") != -1) {
headerCollection.push({ name: "If-None-Match", value: Math.random() + "" });
headerCollection.push({ name: "Pragma", value: "No-cache" });
// return { requestHeaders: headerCollection, redirectUrl: autoEnableTrainClassRandom(details.url) };
}
return { requestHeaders: headerCollection };
}
chrome.webRequest.onBeforeSendHeaders.addListener(processRequest, filter, extraInfo);
chrome.webRequest.onBeforeRequest.addListener(function (data) {
//if (data.url.indexOf("querySingleAction.do?method=queryLeftTicket") != -1) {
// return { redirectUrl: autoEnableTrainClassRandom(data.url) };
//}
//if (data.url.indexOf("indexAction.do") != -1 || /Action\.do\?method=[a-z\d]{1,3}$/i.test(data.url)) {
// return { cancel: true };
//}
return null;
}, filter, ["blocking"]);
})();
//#endregion
//#endregion
//#region 服务器测速
(function directAccessModule() {
/// <summary>服务器IP</summary>
//TODO: 由于目前猎豹后端并没有现有的云服务平台,并不支持.NET服务所以暂时下架。
//重新上架需要部署服务器端。详情联系木鱼,给不给回复看心情。
var valid = 0; //window.external.LieBaoLookupDnsAddress ? 1 : 0;
var enableDirectAccess = typeof (chrome.tabs.httpRequest) === "function";
chrome.runtime.onMessage.addListener(function (m, s, r) {
if (m && m.action === "servervalid")
r({ valid: valid });
});
chrome.runtime.onMessageExternal.addListener(function (m, s, r) {
if (m && m.action === "servervalid")
r({ valid: valid });
});
if (!valid)
return;
var serviceUrl = "http://srv.12306.liebao.cn/web/cn12306/ipservice/getlist";
var reportUrl = "http://srv.12306.liebao.cn/web/cn12306/ipservice/update2";
var inupdateMode = false;
var serverStorage = {
count: 0,
valid: 0,
timeout: 0,
failed: 0,
lastUpdate: null,
validList: [],
status: 0,
enableDirectAccess: enableDirectAccess
};
var serverlist = [],
testedServer = [],
currentServer = {
"kyfw.12306.cn": { ip: null, speed: null }
//, "dynamic.12306.cn": { ip: null, speed: null }
//, "www.12306.cn": { ip: null, speed: null }
};
var servers = [
//{ host: "dynamic.12306.cn", url: "http://dynamic.12306.cn/otn/", count: 0 },
//{ host: "www.12306.cn", url: "http://www.12306.cn/otn/", count: 0 },
{ host: "kyfw.12306.cn", url: "https://kyfw.12306.cn/otn/", count: 0 }
],
serverIndex = 0;
var localDns = {}, initialChange = false;
//消息
(function () {
var messageCallback = function (message, sender, response) {
if (message && message.action) {
if (message.action === "getServerStatus") {
response(serverStorage);
} else if (message.action === "getServerList") {
response(serverStorage.status == 2 ? _.flatten([testedServer, serverlist]) : []);
} else if (message.action === "getCurrentServer") {
response(currentServer);
}
}
};
chrome.runtime.onMessage.addListener(messageCallback);
chrome.runtime.onMessageExternal.addListener(messageCallback);
})();
var randomTake = function (list, count) {
if (!list || list.length <= count) return list;
var result = [];
var randomIndex;
while (result.length < count) {
randomIndex = Math.floor(Math.random() * list.length);
result.push(list.splice(randomIndex, 1));
}
return result;
};
var loadFromServer = function () {
serverStorage.status = 1;
chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
chrome.runtime.sendMessage({ action: "track", detail: { type: 90 } });
$.getJSON(serviceUrl).done(function (data) {
//取100个
data = _.groupBy(data, function (e) { return e.host; });
for (var host in data) {
data[host] = randomTake(data[host], 100);
}
data = _.flatten(data);
serverlist = data;
serverStorage.count = data.length;
serverStorage.valid = serverStorage.timeout = serverStorage.failed = 0;
//增加本地解析的地址
if (localDns) {
_.each(localDns, function (ips, host) {
_.each(ips, function (ip) {
serverlist.push({ host: host, ip: ip });
});
});
}
runServerTest();
}).fail(function () {
setTimeout(loadFromServer, 1000 * 60 * 10);
});
};
var runServerTest = function () {
testedServer = [];
serverStorage.status = 2;
chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
serverTestEntry();
};
var serverTestEntry = function () {
if (serverlist.length) {
testServer(serverlist.splice(0, 1), serverTestEntry);
} else {
filterServer();
}
};
var ajaxWrapper = function (url, host) {
var def = new $.Deferred();
chrome.tabs.httpRequest(url, "User-Agent: MSIE 9.0\r\nHost: " + host, 3000, function (result) {
var resp = null;
try {
resp = JSON.parse(result);
} catch (e) {
}
if (!resp) {
def.reject(0, 'unknown error.');
return;
}
var a = resp[0];
var b = resp[1];
var c = resp[2];
if (!a) {
if (b === 10001) {
//timeout
def.reject(0, 'timeout');
} else {
def.reject(b, c);
}
} else {
def.resolve(c, b);
}
});
return def.promise();
};
var testServer = function (server, callback) {
server = server[0];
server.host = server.host || "kyfw.12306.cn";
var url = "https://" + server.ip + "/otn/";
var time = new Date();
ajaxWrapper(url, server.host).done(function (text) {
if (!text || text.indexOf("客运服务") !== -1)
server.speed = new Date() - time;
else {
server.speed = -2;
}
}).fail(function () {
server.speed = -1;
}).always(function () {
server.status = server.speed == -1 || server.speed == -2 ? -1 : server.speed > 1000 ? -2 : 1;
if (inupdateMode) {
if (server.status === -1) serverStorage.failed++;
else if (server.status === -2) serverStorage.timeout++;
else if (server.status === 0) serverStorage.timeout++;
else {
serverStorage.valid++;
serverStorage.validList.push(server);
}
}
testedServer.push(server);
setTimeout(function () {
chrome.runtime.sendMessage({ action: "serverTestResult", detail: server });
callback();
}, 100);
});
};
var filterServer = function () {
serverStorage.status = 3;
chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
//分组
serverStorage.validList = _.groupBy(serverStorage.validList, function (e) {
return e.host;
});
//排序
_.each(serverStorage.validList, function (e, i, l) {
e.sort(function (x, y) {
if (x.speed === y.speed) return 0;
if (x.speed === -1 && y.speed !== -1) return 1;
if (x.speed !== -1 && y.speed === -1) return -1;
if (x.speed === 0 && y.speed !== 0) return 1;
if (x.speed !== 0 && y.speed === 0) return -1;
return x.speed - y.speed;
});
l[i] = e.splice(0, 20);
});
//取前20个
serverStorage.status = 0;
serverStorage.lastUpdate = new Date();
chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
if (inupdateMode) {
//更新模式, 提交速度报告
var data = [];
_.each(testedServer, function (e) {
var x = _.pick(e, 'ip', 'averageSpeed', 'host');
x.averageSpeed = e.speed;
data.push(x);
});
//上报到服务器
var updata = _.groupBy(data, function (e) {
return e.host;
});
var rptData = {};
for (var host in updata) {
rptData[host] = JSON.stringify(updata[host]);
}
chrome.runtime.sendMessage({ action: "track", detail: { type: 91 } });
$.post(reportUrl, rptData);
localStorage["serverStorage"] = JSON.stringify(serverStorage);
} else {
//缓存测速模式
if (_.flatten(serverStorage.validList).length < 5) {
//服务器过少,强行更新一次
inupdateMode = true;
serverStorage.validList = [];
loadFromServer();
return;
} else {
localStorage["serverStorage"] = JSON.stringify(serverStorage);
}
}
//开始实时跟踪当前的状态
testServerLoop();
};
var applyServerLoop = function () {
_.each(currentServer, function (e) {
if (e.ip)
window.external.LieBaoSetHostAddress(e.host, e.ip, 1);
});
};
setInterval(applyServerLoop, 20000);
var applyServer = function (host, ip) {
chrome.runtime.sendMessage({ action: "track", detail: { type: 92, values: [ip] } });
if (ip) {
window.external.LieBaoSetHostAddress(host, ip, 1);
} else {
window.external.LieBaoSetHostAddress(host, localDns[host][0], 0);
}
//if (initialChange)
// chrome.runtime.sendMessage({ action: "showNotification", detail: { title: "订票助手提示", message: "服务器访问不流畅,已经为您切换服务器。", iconUrl: "/icons/icon_128.png" } });
};
var testLoopSleepTime = 20000;
var testServerLoop = function () {
//切换IP会导致掉线所以暂时屏蔽
return;
//if (entry12306) {
// var testCallback = function (time) {
// var server = servers[serverIndex];
// var host = server.host;
// if (CFG_MANGER.config.enableServerAutoChange) {
// if (time > 3000 || (time > 500 && ++currentServer[host].count >= 5)) {
// currentServer[host].count = 0;
// if (!currentServer[host].ip) {
// //速度过慢,请求切换,策略随机选
// var targetList = serverStorage.validList[host];
// var target = targetList[Math.floor(Math.random() * targetList.length)];
// if (target && target.ip && (targetList[0].speed < time || time <= 0)) {
// currentServer[host].rate = Math.max(Math.floor((target.speed - time) * 1000 / time), 10) / 10;
// currentServer[host].ip = target.ip;
// applyServer(host, target.ip);
// //立刻重新测速
// setTimeout(testServerLoop, 1000);
// return;
// }
// } else {
// //实在找不到或已经设置过了,那就恢复默认吧。
// currentServer[host].ip = null;
// applyServer(host, null);
// setTimeout(testServerLoop, 1000);
// return;
// }
// } else {
// currentServer[host].speed = time;
// currentServer[host].count = 0;
// }
// }
// chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
// serverIndex++;
// if (serverIndex >= servers.length) {
// serverIndex = 0;
// if (!initialChange && entry12306) {
// //初始化的时候自动选择一次
// var selected = [];
// _.each(currentServer, function (e, h) {
// var list = serverStorage.validList[h];
// if (!list || !list.length) return;
// if (e.speed > list[0].speed) {
// var rate = Math.max(Math.floor((e.speed - list[0].speed) * 1000 / e.speed), 10) / 10;
// e.ip = list[0].ip;
// e.speed = list[0].speed;
// e.rate = rate;
// applyServer(h, e.ip);
// selected.push(e.ip + "(加速 " + rate + "%)");
// }
// });
// if (selected.length) {
// chrome.runtime.sendMessage({ action: "showNotification", detail: { title: "订票助手已优化服务器", message: "订票助手已为您选择最快的服务器:" + selected.join("; ") + "。", iconUrl: "/icons/icon_128.png" } });
// }
// chrome.runtime.sendMessage({ action: "serverStateChange", detail: { state: serverStorage.status } });
// initialChange = true;
// }
// setTimeout(testServerLoop, testLoopSleepTime);
// testLoopSleepTime += 5000;
// } else {
// testServerLoop();
// }
// };
// testUrlSpeed(servers[serverIndex].url, testCallback);
//} else {
// setTimeout(testServerLoop, 5000);
//}
};
var testUrlSpeed = function (url, callback) {
var n = new Date();
$.ajax(url, { dataType: "text", method: "GET", timeout: 3000 }).done(function () {
callback(new Date() - n);
}).fail(function () {
callback(999999);
});
};
var checkAndStart = function () {
if (localStorage['serverStorage']) {
serverStorage = JSON.parse(localStorage['serverStorage']);
serverStorage.enableDirectAccess = enableDirectAccess;
}
if (!serverStorage.lastUpdate || (new Date().getTime() / 3600000) - new Date(serverStorage.lastUpdate).getTime() / 3600000 >= 12 || serverStorage.validList.length < 5) {
inupdateMode = true;
serverStorage.validList = [];
loadFromServer();
} else {
serverlist = serverStorage.validList;
testServerLoop();
}
};
//尝试解析地址
var boot = function () {
var failedCount = 0;
_.each(servers, function (e) {
localDns[e.host] = _.filter(window.external.LieBaoLookupDnsAddress(e.host, 'false').split(';'), function (e) { return e || false; });
if (!localDns[e.host] || !localDns[e.host].length)
failedCount++;
});
//如果存在不能解析的情况,则失败
if (failedCount) {
valid = -1;
return;
}
//测试是否有代理服务器
ajaxWrapper("https://" + localDns['kyfw.12306.cn'][0] + "/otn/", "kyfw.12306.cn").done(function () {
checkAndStart();
}).fail(function () {
valid = -1;
});
};
//策略如果前一天有用过12306的话那就执行
var booted = false;
var triggerUpdate = function () {
localStorage["lastUsed"] = Math.floor(new Date().getTime() / 86400000);
if (!booted) {
booted = true;
boot();
}
entry12306 = true;
}
var parseJson = function (text) {
try {
return JSON.parse(text);
} catch (e) {
if (isDebug)
console.log("WARNING: unable to parse json response. content -> " + text);
return null;
}
};
chrome.runtime.onMessage.addListener(function (m, s, r) {
if (!m || !m.action)
return;
if (m.action === 'triggerUpdate') {
triggerUpdate();
} else if (m.action === "directAccess") {
var data = m.data;
var host = /^https?:\/\/([^\/]+)\//.exec(data.url) && RegExp.$1;;
var hostip = null;
var hostindex = 0;
if (enableDirectAccess && host && serverStorage.validList && serverStorage.validList[host] && serverStorage.validList[host].length) {
var hosts = serverStorage.validList[host];
hostindex = Math.floor(Math.random() * hosts.length);
hostip = hosts[hostindex];
data.url = data.url.replace("//" + host, "//" + hostip.ip);
}
if (hostip) {
//build header
var headers = [];
if (data.headers) {
for (var i in data.headers) {
var key = i;
if (key.indexOf("Fish-") === 0)
key = key.substr(5);
headers.push(key + ": " + data.headers[i]);
}
}
headers.push("Host: " + host);
//using direct access
if (data.data) {
data.url += "?" + $.param(data.data);
}
if (isDebug)
console.log("direct access to url " + data.url);
chrome.tabs.httpRequest(
data.url,
headers.join("\r\n"),
20000,
function (response) {
if (isDebug)
console.log("direct access response received. data: ", response);
(function (success, status, text, rHeader, base64) {
if (!success || status !== 200) {
//failed.
r([false, status, status, text, rHeader || "", null]);
//block error ip
hostip.failedCount = (hostip.failedCount || 0) + 1;
console.log("bad response for host [" + hostip.ip + "], increase failed counter. current at " + hostip.failedCount);
if (hostip.failedCount > 3) {
//block error ip
serverStorage.validList[host].splice(hostindex, 1);
console.log("bad response for host [" + hostip.ip + "] failed too many times, removed from validlist.");
localStorage["serverStorage"] = JSON.stringify(serverStorage);
}
} else {
r([true, status, status, text, rHeader || "", parseJson(text)]);
}
}).apply(this, parseJson(response));
}
);
} else {
//doing a regular access
$.ajax(data).done(function (result, status, xhr) {
r([true, xhr.status, xhr.statusText, xhr.responseText, xhr.getAllResponseHeaders(), result]);
}).fail(function (xhr) {
r([false, xhr.status, xhr.statusText, xhr.responseText, "", null]);
});
}
return true;
}
});
chrome.runtime.onMessageExternal.addListener(function (m, s, r) {
if (m && m.action === 'triggerUpdate') {
triggerUpdate();
}
});
setInterval(function () {
entry12306 = false;
chrome.tabs.query({ url: "*://*.12306.cn/*" }, function (a) {
entry12306 |= a && a.length > 0;
});
chrome.tabs.query({ url: "*://12306.liebao.cn/*" }, function (a) {
entry12306 |= a && a.length > 0;
});
}, 20000);
})();
//#endregion
//#region 统计
(function statModule() {
var url1 = "http://liebao.tjweb.ijinshan.com/click/__infoc.gif?actionname=liebao_80";
var trackback = function (type, values) {
values = values || [];
var data = { type: type };
for (var index in values) {
if (values[index]) {
data["value" + (parseInt(index) + 1)] = values[index] + '';
}
}
$.get(url1, data);
};
chrome.runtime.onMessage.addListener(function (m, s, r) {
if (!m || !m.action || m.action !== 'track')
return;
trackback(m.detail.type, m.detail.values);
});
chrome.runtime.onMessageExternal.addListener(function (m, s, r) {
if (!m || !m.action || m.action !== 'track')
return;
trackback(m.detail.type, m.detail.values);
});
trackback(99, [MANIFEST.version, bv]);
})();
//#endregion
//#region 版本更新
(function versionUpdateModule() {
//var cv = parseInt(/Chrome\/(\d+)/i.exec(navigator.userAgent)[1]);
//var updateurl = "http://www.fishlee.net/service/update2/44/" + (cv < 34 ? "44" : "60") + "/version_v6.json?" + Math.random();
var updateurl = "http://srv.12306.liebao.cn/data/version_v6.json?" + Math.random();
var lastCheckUpdate = null;
var hasUpdate = false, lastVersion = null, lastMessage = null, updateInfo = null;
var compareVersion = function (v1, v2) {
var vv1 = v1.split('.');
var vv2 = v2.split('.');
var length = Math.min(vv1.length, vv2.length);
for (var i = 0; i < length; i++) {
var s1 = parseInt(vv1[i]);
var s2 = parseInt(vv2[i]);
if (s1 < s2) return -1;
if (s1 > s2) return 1;
}
return vv1.length > vv2.length ? 1 : vv1.length < vv2.length ? -1 : 0;
};
var checkUpdate = function () {
$.getJSON(updateurl, function (data) {
updateInfo = data;
lastVersion = data.version;
lastMessage = data.notify;
updateInfo.hasUpdate = hasUpdate = compareVersion(MANIFEST.version, data.version) < 0;
chrome.runtime.sendMessage({ action: "updateInfoRefreshed", detail: updateInfo });
});
lastCheckUpdate = new Date();
};
var msgCallback = function (m, s, r) {
if (!m || !m.action)
return;
if (m.action === 'getUpdateInfo')
r(updateInfo);
else if (m.action === 'getVersionInfo')
r({ curVersion: MANIFEST.version, updateInfo: updateInfo });
else if (m.action === 'triggerUpdate') {
if (!lastCheckUpdate || (new Date() - lastCheckUpdate) >= 1200000) {
checkUpdate();
}
} else if (m.action === "getBv") {
r({ bv: bv });
}
};
chrome.runtime.onMessage.addListener(msgCallback);
chrome.runtime.onMessageExternal.addListener(msgCallback);
//TODO 移除内建的检查更新机制,因为这个不是很必须
//checkUpdate();
})();
//#endregion
//#region Captcha
(function webCaptchaModule() {
var msgCallback = function (m, s, r) {
if (m && m.action === 'captcha' && CFG_MANGER.config.captchaServerUrl) {
var code;
$.ajax(CFG_MANGER.config.captchaServerUrl, {
method: "POST",
data: { pic: m.detail.base64, sign: window.external.LieBaoSign(m.detail.base64) },
timeout: 3000
}).done(function (data) {
code = data.success ? data.code.replace(/\s/g, "") : "";
}).fail(function () {
code = "";
}).always(function () {
r({ code: code });
});
return true;
}
};
chrome.runtime.onMessage.addListener(msgCallback);
chrome.runtime.onMessageExternal.addListener(msgCallback);
})();
//#endregion
//#region 配置同步和网页上的提示接口
(function webStorageInterface() {
var msgCallback = function (m, s, r) {
if (m.action === "getStorage")
r({ action: "sendStorage", detail: localStorage });
else if (m.action === "setStorage" && m.detail) {
_.each(m.detail, function (v, k) {
if (v)
localStorage.setItem(k, v);
else localStorage.removeItem(k);
});
r({ action: "sendStorage", detail: m.detail });
} else if (m.action === "notify") {
var notify = new Notification(m.title || "订票助手", { body: m.content || null, icon: "/icons/icon_n.png" });
setTimeout(function () {
notify.close();
}, 5000);
}
};
//chrome.runtime.onMessage.addListener(msgCallback);
chrome.runtime.onMessageExternal.addListener(msgCallback);
})();
//#endregion
//#region 聊天系统
(function chatModule() {
var ports = [];
var msgQueue = [];
var lastMessages = [];
var checkPortTimer = null;
var clearPortTimer = function () {
serverConnection.disconnect();
lastMessages = [];
msgQueue = [];
checkPortTimer = null;
};
var portOnMessage = function (msg, port) {
if (!msg || !msg.action)
return;
switch (msg.action) {
case "getChatServerStatus":
serverMgr.loadServers(function (servers) {
port.postMessage({ action: "responseServer", detail: servers });
});
break;
case "getCurrentRoomInfo":
port.postMessage({ action: "responseCurrentRoomInfo", detail: serverConnection.currentRoom });
break;
case "enterChatRoom":
serverConnection.connect(msg.detail);
break;
case "chatRoomSendMsg":
//if (!msg.detail.roomid)
// msg.detail.roomid = serverConnection.currentRoom.id;
//$.post("http://12306.liebao.cn/index.php?r=Api/SentMessage", {
// data: JSON.stringify(msg)
//}).done(function (result) {
// if (result.resCode === 0) {
// } else {
// port.postMessage({ action: "sendMessageFailed", detail: result.message });
// }
//}).fail(function () {
// port.postMessage({ action: "sendMessageFailed", detail: "聊天服务器繁忙,发送失败" });
//});
serverConnection.sendMsg(msg.detail);
break;
case "disconnectChatRoom":
lastMessages = [];
msgQueue = [];
serverConnection.disconnect();
break;
default:
}
};
var portOnDisconnect = function (port) {
var idx = _.indexOf(ports, port);
if (idx > -1) {
ports.splice(idx, 1);
}
if (!checkPortTimer && ports.length === 0)
checkPortTimer = setTimeout(clearPortTimer, 30 * 60 * 1000);
};
var postMessageToPort = function (msg) {
if (msg && msg.action === "chatRoomReceiveMsg") {
if (!ports.length) {
msgQueue.push(msg);
return;
}
lastMessages.push(msg);
lastMessages.length > 20 && lastMessages.splice(0, 1);
}
ports.forEach(function (p) {
p.postMessage(msg);
});
if (msg && msg.action === "chatRoomConnected") {
if (lastMessages.length) {
lastMessages.forEach(function (m) {
ports.forEach(function (p) {
p.postMessage(m);
});
});
}
if (msgQueue.length) {
msgQueue.forEach(function (m) {
ports.forEach(function (p) {
p.postMessage(m);
});
});
msgQueue = [];
}
}
};
chrome.runtime.onConnectExternal.addListener(function (port) {
ports.push(port);
port.onMessage.addListener(portOnMessage);
port.onDisconnect.addListener(portOnDisconnect);
if (checkPortTimer) {
clearTimeout(checkPortTimer);
checkPortTimer = null;
}
});
var serverMgr = (function chatServerManager() {
var isServerLoaded = null;
var isInServerLoading = false;
var servers = null;
var callbackQueue = [];
var lastLoad = null;
var setRoomCount = function (id, count) {
if (!servers)
return;
var room = _.findWhere(servers, { id: id });
if (room) room.onlinecount = count;
};
var execCallback = function () {
var callback;
while (callbackQueue.length) {
(callback = callbackQueue.pop()) && callback(servers);
};
};
var loadServers = function (callback) {
if (!callback)
return;
if (isServerLoaded && lastLoad && (new Date() - lastLoad) < 1000 * 60 * 10)
callback(servers);
else {
callbackQueue.push(callback);
if (!isInServerLoading) {
isInServerLoading = true;
$.get(CFG_MANGER.config.chatServerApi, null, null, "json").done(function (data) {
servers = data;
isInServerLoading = false;
isServerLoaded = true;
execCallback();
}).fail(function () {
servers = null;
isInServerLoading = false;
execCallback();
}).always(function () {
lastLoad = new Date();
});
}
}
};
//转发系统消息
chrome.runtime.onMessage.addListener(function (m, s, r) {
if (!m || !m.action)
return;
if (m.action === "serverStateChange" || m.action === "sysConfigUpdate")
postMessageToPort(m);
});
return {
loadServers: loadServers,
setRoomCount: setRoomCount
};
})();
var serverConnection = (function () {
var currentRoom = null;
var socket = null;
var that = this;
var roomId;
var connect = function (room) {
currentRoom = room;
var url = currentRoom.url;
//如果已变更房间,则先断开
if (socket !== null && room.id !== roomId)
disconnect();
roomId = room.id;
if (socket === null) {
socket = new WebSocket(url, []);
socket.binaryType = "arraybuffer";
socket.onclose = function () {
socket = null;
postMessageToPort({ action: "chatRoomDisconnected" });
};
socket.onopen = function () {
postMessageToPort({ action: "chatRoomConnected" });
};
socket.onmessage = function (e) {
var buffer = new Uint8Array(e.data);
var data = [];
for (var i = 0; i < buffer.length; i++) {
data.push(buffer[i]);
}
postMessageToPort({ action: "chatRoomReceiveMsg", detail: { buffer: data } });
};
socket.onerror = function () {
try {
socket.close();
} catch (e) {
}
};
postMessageToPort({ action: "chatRoomConnecting" });
} else {
postMessageToPort({ action: "chatRoomConnected" });
}
};
var sendMsg = function (msg) {
if (socket !== null) {
//socket.send(JSON.stringify(msg));
socket.send(new Uint8Array(msg).buffer);
}
};
var disconnect = function () {
if (socket) {
socket.close();
socket = null;
}
currentRoom = null;
roomId = null;
};
var ret = {
connect: connect,
disconnect: disconnect,
sendMsg: sendMsg
};
Object.defineProperty(ret, "currentRoom", {
get: function () {
return currentRoom;
}
});
return ret;
})();
})();
//#endregion
//#region 定时提醒
(function TimerAlerm() {
var tasks = JSON.parse(localStorage["alarm"] || "[]");
var buttonImgUrl = "/infobar/theme/plus_16.png";
var iconLeft = "/icons/icon_n.png";
var checkTimer = null;
var alarmData = {};
var saveAlarm = function () {
localStorage["alarm"] = JSON.stringify(tasks);
};
var buttonClickCallback = function (btnId, index) {
if (!alarmData[btnId])
return;
var task = alarmData[btnId];
var data = task.data;
delete alarmData[btnId];
chrome.tabs.create({
active: true,
url: "http://12306.liebao.cn/#ALARM-" + encodeURIComponent(JSON.stringify(data))
});
};
chrome.notifications.onButtonClicked.addListener(buttonClickCallback);
var checkForAlarm = function () {
checkTimer = null;
var hasChange = false;
var alarms = {};
while (tasks.length > 0 && tasks[0].time <= new Date().getTime()) {
var task = tasks.shift();
alarms[task.group] = task;
hasChange = true;
}
if (hasChange) {
_.each(alarms, function (task) {
var id = "ALARM-" + task.data.fromCode + "-" + task.data.toCode + new Date().getTime();
chrome.runtime.sendMessage({ action: "track", detail: { type: 132, values: [task.type || 0, task.point || 0] } });
chrome.notifications.create(id, {
type: "basic",
iconUrl: iconLeft,
title: task.group,
message: task.text,
buttons: [
{ title: "立刻打开订票页面", iconUrl: buttonImgUrl }
]
}, function (_id) {
id = _id;
alarmData[id] = task;
setTimeout(function () {
delete alarmData[_id];
chrome.notifications.clear(_id, function () { });
}, 10000);
});
});
}
if (tasks.length)
checkTimer = setTimeout(checkForAlarm, 30000);
if (hasChange)
saveAlarm();
};
chrome.runtime.onMessageExternal.addListener(function (m, s, r) {
if (!m || !m.action || m.action !== 'setAlarmTask')
return;
var newtasks = m.detail;
_.each(newtasks, function (t) {
tasks.push(t);
});
tasks.sort(function (x, y) {
return x.time - y.time;
});
saveAlarm();
if (!checkTimer)
checkForAlarm();
});
checkForAlarm();
})();
//#endregion