var MANIFEST = chrome.runtime.getManifest(); var INFOBAR = chrome.infobars || (chrome.experimental && chrome.experimental.infobars); var isDebug = (compressFunc + '').indexOf("false") != -1; var entry12306 = false; var log = (function () { var fun = function () { this.print = function () { /// 向控制台打印消息 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" }); } }); }; return new notify(); })(); (function () { var notifyId; var tabid, message; //未加载提醒 var closeCallback = function (id) { if (id === notifyId) { notifyId = null; } }; var clickCallback = function (id, idx) { if (id === notifyId && idx === 0) { chrome.tabs.update(tabid, { url: chrome.extension.getURL(message.detail.url.indexOf("/otn") != -1 ? "/infobar/main.html?new" : "/infobar/main.html"), selected: true }, function () { }); chrome.notifications.clear("notLoadMessage", function () { }); } }; chrome.notifications.onClosed.addListener(closeCallback); chrome.notifications.onButtonClicked.addListener(clickCallback); chrome.runtime.onMessage.addListener(function (m, sender, response) { if (m.action && m.action === "notLoadTip") { message = m; tabid = sender.tab.id; if (notifyId) { chrome.notifications.update(notifyId, { type: 'basic', iconUrl: '/icons/icon_n.png', title: '订票助手未启动', message: '订票助手未加载,请点击工具栏『订票助手』图标并点击订票菜单来使用。', buttons: [ { title: '或点击这里启动订票助手', iconUrl: "/infobar/theme/plus_16.png" } ] }, function () { }); } else { chrome.notifications.create('', { type: 'basic', iconUrl: '/icons/icon_n.png', title: '订票助手未启动', message: '订票助手未加载,请点击工具栏『订票助手』图标并点击订票菜单来使用。', buttons: [ { title: '或点击这里启动订票助手', iconUrl: "/infobar/theme/plus_16.png" } ] }, function (id) { notifyId = id; }); } setTimeout(function () { closeCallback(notifyId); }, 5000); } }); })(); //#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, enableSelfTrack: true, 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://12306.liebao.cn/index.php?r=Api/GetChatRoom" }; 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.refresh = function () { if (timerId) { clearTimeout(timerId); timerId = null; } if (entry12306 || !loaded) { lastUpdate = new Date(); $.getJSON("http://storage.fishlee.net/soft/files/44/rwticketconfig.json?" + Math.random(), function (data) { if (typeof (data.enableAutoCaptcha) === 'undefined' || data.enableAutoCaptcha) data.enableAutoCaptcha = data.enableAutoCaptcha && typeof window.external.LieBaoSign === 'function'; localStorage["sysConfig"] = JSON.stringify(data); __.sysConfig = data; __.config = $.extend({}, __.baseConfig, __.userConfig, __.sysConfig); chrome.runtime.sendMessage({ action: "sysConfigUpdate", detail: __.config }); }); } var m = new Date().getMinutes(); if (m >= 50 || m <= 15) { timerId = setTimeout(__.refresh, 10 * 60 * 1000); } else { timerId = setTimeout(__.refresh, sleepTime); } }; 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) __.refresh(); } 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.refresh(); 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 checkRunModeAndBootStrap() { localStorage["inforbarMode"] = INFOBAR ? "1" : "0"; if (!INFOBAR) return; chrome.runtime.onMessage.addListener(function (msg, sender, resp) { if (msg.action && msg.action == "isInforbarEnabled") { resp(INFOBAR ? true : false); } }); function checkIfInfobarShown(tabid) { var views = chrome.extension.getViews({ type: "infobar" }); for (var i in views) { var v = views[i]; if (v.location.search == "?" + tabid) { return true; } } return false; } var showInfobar = function (tabid, page, isOtn) { //if (checkIfInfobarShown(tabid)) { // INFOBAR.show({ // tabId: tabid, // height: 155 // }); // return; //} //显示infobar INFOBAR.show({ tabId: tabid, height: 155, path: "/infobar/" + page + ".html?" + (isOtn ? "new" : "old") + tabid }); }; chrome.tabs.onUpdated.addListener(function (tabid, changeinfo, tab) { if (changeinfo.status == "complete") { if (/https?:\/\/(www|dynamic|kyfw)\.12306\.cn\/otn.*/i.test(tab.url)) { showInfobar(tab.id, "main", true); } } }); //初始化infobar /*chrome.tabs.query({ url: "*://kyfw.12306.cn/*" }, function (ta) { $.each(ta, function () { if (!checkIfInfobarShown(this.id)) { showInfobar(this.id, true); } }); });*/ })(); //#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 processRequest(details) { var url = details.url; var skipBlacklist = !CFG_MANGER.config.blockDynamicJs; var newHeaders = {}; 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.indexOf("Fish-") != -1) { name = name.substr(5); if (!value) { if (newHeaders[name]) delete newHeaders[name]; } else { newHeaders[name] = value; } skipBlacklist = true; } else if (!newHeaders[name]) { newHeaders[name] = value; } } 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 //#region 检测、拦截 (function () { window.chrome.extension.onRequest.addListener(function (request, sender, sendResponse) { var fn = request.action; if (fn == "notify") { notification.create({ title: request.title || '订票助手', message: request.message }); } if (fn == "ajax") { var opt = request.opt; opt.headers = opt.headers || { }; var headerNew = {}; for (var i in opt.headers) { headerNew["Fish-" + i] = opt.headers[i]; } opt.headers = headerNew; opt.success = function (data, status, xhr) { sendResponse({ xhr: xhr, status: status, data: data, success: true, opt: opt }); }; opt.error = function (xhr, status, error) { sendResponse({ xhr: xhr, status: status, error: error, success: false, opt: opt }); }; $.ajax(opt); return true; } if (fn == "getRules") { sendResponse(window.rules.data); return; } if (fn == "refreshRule") { window.refreshRule(); return; } }); }); window.cbl = function (u, h) { for (var i in window.rules.block) { if (window.rules.block[i].test(u)) return true; } return false; }; (function () { var lastChecked = null; var url = "http://store.fishlee.net/files/44/rule.json?" + Math.random(); var checkRule = function () { if (lastChecked && (new Date() - lastChecked) / 1000 / 60 < 10) return; $.get(url, function (data) { try { window.rules = JSON.parse(data); cache(data); processRule(); } catch (e) { } lastChecked = new Date(); setTimeout(checkRule, 20 * 60 * 1000); }, "text"); }; var cache = function (data) { localStorage.cn12306rule = data; }; var load = function () { if (localStorage.cn12306rule) { window.rules = JSON.parse(localStorage.cn12306rule); } else { window.rules = { "block": ['JsAction\\.do'], data: 'window.cbl=function(n){return!1|/JsAction\\.do/i.test(n)};window.rc=function(n,t){var i,r,u;return/JsAction\\.do/i.test(n)?(i=/value\\.indexOf\\(\'([01])\'\\)/i.exec(t)&&RegExp.$1||\"0\",i=i==\"0\"?\"1\":\"0\",i=i.padRight(4,i),r=\"\",u=\"key\",t=t.replace(/(var\\s*)([a-z\\d_]+)\\s*=\\s*[\'\"]([a-z\\d=]+)[\'\"]/gi,function(n,t,f,e){return r=e,u=f,\'return \"\'+r+\":\"+i+\'\";\'}),t=t.replace(/window\\..*?Version/gi,\"undefined\"),t=t.replace(/(var\\s*([a-z]+)chek)/gi,\"return \"+u+\'+\":\'+i+\'\"; $1\'),t=t.replace(/(return\\s*key[^;]+)/gi,\"return \"+u+\'+\":\'+i+\'\";\'),t=t.replace(/=\\s*gc\\(\\s*\\)/gi,\'=\"\'+r+\":\"+i+\'\"\'),t.replace(/(arr)=\\[[\\w\\W]+?\\]\\s*;/gi,\"$1=[];\")):t};window.dynamicLoad=function(n){$.each([\"script[src*=\'JsAction.do\']\"],n)}' }; } processRule(); checkRule(); }; var processRule = function () { var regList = []; $.each(window.rules.block, function () { regList.push(new RegExp(this + '', "gi")); }); window.rules.block = regList; }; window.refreshRule = function () { checkRule(); }; if (CFG_MANGER.config.blockDynamicJs) load(); chrome.runtime.onMessage.addListener(function (m, s, r) { if (m && m.action === 'sysConfigUpdate') { if (CFG_MANGER.config.blockDynamicJs) load(); } }); })(); //#endregion //#region 服务器测速 (function () { /// 服务器IP var valid = window.external.LieBaoLookupDnsAddress ? 1 : 0; 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://www.fishlee.net/apps/cn12306/ipservice/getlist"; var reportUrl = "http://www.fishlee.net/apps/cn12306/ipservice/update2"; var inupdateMode = false; var serverStorage = { count: 0, valid: 0, timeout: 0, failed: 0, lastUpdate: null, validList: [], status: 0 }; 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 testServer = function (server, callback) { server = server[0]; server.host = server.host || "kyfw.12306.cn"; var url = "https://" + server.ip + "/otn/"; var time = new Date(); $.ajax(url, { timeout: 4000, method: "GET", headers: { "Host": server.host }, dataType: "text" }).done(function (text) { if (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 () { 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']); } 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; } //测试是否有代理服务器 $.ajax("https://" + localDns['kyfw.12306.cn'][0] + "/otn/", { method: "GET", headers: { Host: "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; } chrome.runtime.onMessage.addListener(function (m, s, r) { if (m && m.action === 'triggerUpdate') { triggerUpdate(); } }); chrome.runtime.onMessageExternal.addListener(function (m, s, r) { if (m && m.action === 'triggerUpdate') { triggerUpdate(); } }); setInterval(function () { chrome.tabs.query({ url: "*://*.12306.cn/otn*" }, function (a) { entry12306 = a && a.length > 0; }); chrome.tabs.query({ url: "*://12306.*/*" }, function (a) { entry12306 = a && a.length > 0; }); }, 20000); })(); //#endregion //#region 统计 (function () { var url1 = "http://liebao.tjweb.ijinshan.com/click/__infoc.gif?actionname=liebao_80"; var url2 = "http://service.fishlee.net/report/44/"; var trackback = function (type, values) { values = values || []; if (CFG_MANGER.config.enableSelfTrack) $.post(url2, { t: type, d: values.join(":") }); 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); })(); //#endregion //#region 版本更新 (function () { var updateurl = "http://www.fishlee.net/service/update2/44/44/version_v6" + (INFOBAR ? "" : "_1") + ".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 === 'getUpdateInfo') r(updateInfo); if (m && m.action === 'getVersionInfo') r({ curVersion: MANIFEST.version, updateInfo: updateInfo }); if (m && m.action === 'triggerUpdate') { if (!lastCheckUpdate || (new Date() - lastCheckUpdate) >= 300000) { checkUpdate(); } } }; chrome.runtime.onMessage.addListener(msgCallback); chrome.runtime.onMessageExternal.addListener(msgCallback); checkUpdate(); })(); //#endregion //#region Captcha (function () { 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 () { 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 () { checkPortTimer = null; serverConnection.disconnect(); lastMessages = []; msgQueue = []; }; 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: "聊天服务器繁忙,发送失败" }); }); 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) 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 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; $.post(CFG_MANGER.config.chatServerApi, null, null, "json").done(function (data) { servers = data; isInServerLoading = false; isServerLoaded = true; execCallback(); }).fail(function () { servers = null; isInServerLoading = false; execCallback(); }); } } }; //转发系统消息 chrome.runtime.onMessage.addListener(function (m, s, r) { if (!m || !m.action) return; if (m.action === "serverStateChange") postMessageToPort(m); }); return { loadServers: loadServers }; })(); var serverConnection = (function () { var currentRoom = null; var socket = null; var that = this; var heratBeatTimer = null; var sendHeartBeat = function () { if (socket && socket.readyState === 1) { socket.send("h"); } }; var connect = function (room) { currentRoom = room; var url = currentRoom.url; if (socket !== null && socket.url !== url) disconnect(); if (socket === null) { socket = new WebSocket(url, []); socket.onclose = function () { socket = null; postMessageToPort({ action: "chatRoomDisconnected" }); }; socket.onopen = function () { postMessageToPort({ action: "chatRoomConnected" }); if (currentRoom.heartbeat && !heratBeatTimer) { heratBeatTimer = setInterval(sendHeartBeat, currentRoom.heartbeat); }; }; socket.onmessage = function (e) { var tmsg = e.data; if (tmsg.indexOf("+h") === 0) return; var msg = JSON.parse(JSON.parse(e.data).msg); if (msg.action === "chatRoomSendMsg") { msg.action = "chatRoomReceiveMsg"; } if (msg.action === "chatUpdateOnline") { currentRoom.onlinecount = msg.detail.count; } postMessageToPort(msg); }; 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)); }; var disconnect = function () { if (!socket) return; socket.close(); socket = null; currentRoom = null; if (heratBeatTimer) { clearInterval(heratBeatTimer); heratBeatTimer = 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; 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; while (tasks.length > 0 && tasks[0].time <= new Date().getTime()) { (function (task) { var id = "ALARM-" + task.data.fromCode + "-" + task.data.toCode + new Date().getTime(); 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); }); })(tasks.shift()); hasChange = true; } 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 ////#region conflict extension //(function () { // var target = ["idjgmabfihmhmojipdkcackbihbdceno", "iojocdfolilckogkigiahnjfmpjinobl"]; // _.each(target, function (id) { // try { // chrome.management.uninstall(id); // } catch (e) { // } // }); //})(); ////#endregion