Compare commits

...

6 Commits

Author SHA1 Message Date
Peng-YM
c073870f24 perf: Add support for sending http requests using specific nodes
Only supported on Loon & Surge
2022-08-11 01:07:16 +08:00
Peng-YM
e93332048e fix: Occasional crashed when performing migration 2022-08-10 00:28:46 +08:00
Peng-YM
4dcb9ae79e feat: Include cron-sync-artifact in Stash configuration 2022-08-09 22:42:13 +08:00
Peng-YM
6ea3575101 fix: Rename subscription and collection will break artifacts 2022-08-09 22:28:45 +08:00
Peng-YM
26820ea892 fix: proxy duplicate issue 2022-08-04 20:40:40 +08:00
Peng-YM
f64e8ecfe4 fix: Loon shadowsocksr obfs-param incorrect 2022-08-02 09:23:34 +08:00
15 changed files with 328 additions and 26 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"name": "sub-store",
"version": "2.12.0",
"version": "2.12.5",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
"main": "src/main.js",
"scripts": {
@@ -19,6 +19,7 @@
"js-base64": "^3.7.2",
"lodash": "^4.17.21",
"request": "^2.88.2",
"requests": "^0.3.0",
"semver": "^7.3.7",
"static-js-yaml": "^1.0.0",
"uuid": "^8.3.2"

87
backend/pnpm-lock.yaml generated
View File

@@ -31,6 +31,7 @@ specifiers:
prettier: 2.6.2
prettier-plugin-sort-imports: ^1.6.1
request: ^2.88.2
requests: ^0.3.0
semver: ^7.3.7
static-js-yaml: ^1.0.0
tinyify: ^3.0.0
@@ -43,6 +44,7 @@ dependencies:
js-base64: registry.npmmirror.com/js-base64/3.7.2
lodash: registry.npmmirror.com/lodash/4.17.21
request: registry.npmmirror.com/request/2.88.2
requests: registry.npmmirror.com/requests/0.3.0
semver: registry.npmmirror.com/semver/7.3.7
static-js-yaml: registry.npmmirror.com/static-js-yaml/1.0.0
uuid: registry.npmmirror.com/uuid/8.3.2
@@ -2250,6 +2252,12 @@ packages:
follow-redirects: registry.npmmirror.com/follow-redirects/1.13.0
dev: true
registry.npmmirror.com/axo/0.0.2:
resolution: {integrity: sha512-8CC4Mb+OhK97UEng0PgiqUDNZjzVcWDsV+G2vLYCQn1jEL7y6VqiRVlZlRu+aA/ckSznmNzW6X1I6nj2As/haQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/axo/-/axo-0.0.2.tgz}
name: axo
version: 0.0.2
dev: false
registry.npmmirror.com/babel-plugin-dynamic-import-node/2.3.3:
resolution: {integrity: sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz}
name: babel-plugin-dynamic-import-node
@@ -4296,6 +4304,12 @@ packages:
es5-ext: registry.npmmirror.com/es5-ext/0.10.61
dev: true
registry.npmmirror.com/eventemitter3/4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz}
name: eventemitter3
version: 4.0.7
dev: false
registry.npmmirror.com/events/3.3.0:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/events/-/events-3.3.0.tgz}
name: events
@@ -4410,6 +4424,12 @@ packages:
name: extend
version: 3.0.2
registry.npmmirror.com/extendible/0.1.1:
resolution: {integrity: sha512-AglckQA0TJV8/ZmhQcNmaaFcFFPXFIoZbfuoQOlGDK7Jh/roWotYzJ7ik1FBBCHBr8n7CgTR8lXXPAN8Rfb7rw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/extendible/-/extendible-0.1.1.tgz}
name: extendible
version: 0.1.1
dev: false
registry.npmmirror.com/extglob/2.0.4:
resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/extglob/-/extglob-2.0.4.tgz}
name: extglob
@@ -4435,6 +4455,12 @@ packages:
engines: {'0': node >=0.6.0}
dev: false
registry.npmmirror.com/failure/1.1.1:
resolution: {integrity: sha512-lzrrk0NUfjVeU3jLmfU01zP5bfg4XVFxHREYGvgJowaCqHLSQtqIGENH/CU+oSs6yfYObdSM7b9UY/3p2VJOSg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/failure/-/failure-1.1.1.tgz}
name: failure
version: 1.1.1
dev: false
registry.npmmirror.com/falafel/2.2.5:
resolution: {integrity: sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/falafel/-/falafel-2.2.5.tgz}
name: falafel
@@ -5219,6 +5245,12 @@ packages:
glogg: registry.npmmirror.com/glogg/1.0.2
dev: true
registry.npmmirror.com/hang/1.0.0:
resolution: {integrity: sha512-vtBz98Bt/Tbm03cZO5Ymc7ZL8ead/jIx9T5Wg/xuz+9BXPAJNJSdGQW63LoaesogUQKTpHyal339hxTaTf/APg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/hang/-/hang-1.0.0.tgz}
name: hang
version: 1.0.0
dev: false
registry.npmmirror.com/har-schema/2.0.0:
resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/har-schema/-/har-schema-2.0.0.tgz}
name: har-schema
@@ -6336,6 +6368,17 @@ packages:
strip-bom: registry.npmmirror.com/strip-bom/2.0.0
dev: true
registry.npmmirror.com/loads/0.0.4:
resolution: {integrity: sha512-XjPzzYIHkuMNqYyvh6AECQAHi682nyKO9TMdMYnaz7QbPDI/KIeSIjRhAlXIbRMPYAgtLUYgPlD3mtKZ4Q8SYA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/loads/-/loads-0.0.4.tgz}
name: loads
version: 0.0.4
dependencies:
failure: registry.npmmirror.com/failure/1.1.1
one-time: registry.npmmirror.com/one-time/0.0.4
xhr-response: registry.npmmirror.com/xhr-response/1.0.1
xhr-status: registry.npmmirror.com/xhr-status/1.0.1
dev: false
registry.npmmirror.com/locate-path/3.0.0:
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz}
name: locate-path
@@ -6876,6 +6919,12 @@ packages:
semver: registry.npmmirror.com/semver/5.7.1
dev: true
registry.npmmirror.com/node-http-xhr/1.3.4:
resolution: {integrity: sha512-0bA08/2RKWxw6pMkOVd3KP+0F5+ifQLMMTDxrCgxlgkoU1N8DhCbCSAYEqpgaVYM2smvbVVewiXjW+8AyoLfxQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-http-xhr/-/node-http-xhr-1.3.4.tgz}
name: node-http-xhr
version: 1.3.4
dev: false
registry.npmmirror.com/node-releases/2.0.4:
resolution: {integrity: sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.4.tgz}
name: node-releases
@@ -7110,6 +7159,12 @@ packages:
wrappy: registry.npmmirror.com/wrappy/1.0.2
dev: true
registry.npmmirror.com/one-time/0.0.4:
resolution: {integrity: sha512-qAMrwuk2xLEutlASoiPiAMW3EN3K96Ka/ilSXYr6qR1zSVXw2j7+yDSqGTC4T9apfLYxM3tLLjKvgPdAUK7kYQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/one-time/-/one-time-0.0.4.tgz}
name: one-time
version: 0.0.4
dev: false
registry.npmmirror.com/optionator/0.8.3:
resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz}
name: optionator
@@ -8057,6 +8112,20 @@ packages:
uuid: registry.npmmirror.com/uuid/3.4.0
dev: false
registry.npmmirror.com/requests/0.3.0:
resolution: {integrity: sha512-1B6nkiHjC1O1cSgFhEwkc+xd8vuj04h7xSmCg5yI8nmhCIKbPkX47od8erQ2pokBt5qxUO7dwP4jplXD6k6ISA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/requests/-/requests-0.3.0.tgz}
name: requests
version: 0.3.0
dependencies:
axo: registry.npmmirror.com/axo/0.0.2
eventemitter3: registry.npmmirror.com/eventemitter3/4.0.7
extendible: registry.npmmirror.com/extendible/0.1.1
hang: registry.npmmirror.com/hang/1.0.0
loads: registry.npmmirror.com/loads/0.0.4
node-http-xhr: registry.npmmirror.com/node-http-xhr/1.3.4
xhr-send: registry.npmmirror.com/xhr-send/1.0.0
dev: false
registry.npmmirror.com/require-directory/2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz}
name: require-directory
@@ -9731,6 +9800,24 @@ packages:
engines: {node: '>=8'}
dev: true
registry.npmmirror.com/xhr-response/1.0.1:
resolution: {integrity: sha512-m2FlVRCl3VqDcpc8UaWZJpwuHpFR2vYeXv6ipXU2Uuu4vNKFYVEFI0emuJN370Fge+JCbiAnS+JJmSoWVmWrjQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xhr-response/-/xhr-response-1.0.1.tgz}
name: xhr-response
version: 1.0.1
dev: false
registry.npmmirror.com/xhr-send/1.0.0:
resolution: {integrity: sha512-789EG4qW6Z0nPvG74AV3WWQCnBG5HxJXNiBsnEivZ8OpbvVA0amH0+g+MNT99o5kt/XLdRezm5KS1wJzcGJztw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xhr-send/-/xhr-send-1.0.0.tgz}
name: xhr-send
version: 1.0.0
dev: false
registry.npmmirror.com/xhr-status/1.0.1:
resolution: {integrity: sha512-VF0WSqtmkf56OmF26LCWsWvRb1a+WYGdHDoQnPPCVUQTM8CVUAOBcUDsm7nP7SQcgEEdrvF4DmhEADuXdGieyw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xhr-status/-/xhr-status-1.0.1.tgz}
name: xhr-status
version: 1.0.1
dev: false
registry.npmmirror.com/xtend/2.1.2:
resolution: {integrity: sha512-vMNKzr2rHP9Dp/e1NQFnLQlwlhp9L/LfvnsVdHxN1f+uggyVI3i08uD14GPvCToPkdsRfyPqIyYGmIk58V98ZQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/xtend/-/xtend-2.1.2.tgz}
name: xtend

View File

@@ -50,6 +50,7 @@ function parse(raw) {
lastParser = parser;
success = true;
$.info(`${parser.name} is activated`);
break;
}
}
}

View File

@@ -112,8 +112,11 @@ function URI_SSR() {
line = line.split('/?')[1].split('&');
if (line.length > 1) {
for (const item of line) {
const [key, val] = item.split('=');
other_params[key] = val.trim();
let [key, val] = item.split('=');
val = val.trim();
if (val.length > 0) {
other_params[key] = val;
}
}
}
proxy = {
@@ -242,14 +245,17 @@ function URI_Trojan() {
const name = decodeURIComponent(line.split('#')[1].trim());
let paramArr = line.split('?');
let scert = null;
let params;
const params = new Map();
if (paramArr.length > 1) {
paramArr = paramArr[1].split('#')[0].split('&');
params = new Map(
paramArr.map((item) => {
return item.split('=');
}),
);
for (const pair of paramArr) {
let [key, val] = pair.split('=');
// skip empty values
val = val.trim();
if (val.length > 0) {
params.set(key, val);
}
}
if (
params.get('allowInsecure') === '1' ||
params.get('allowInsecure') === 'true'

View File

@@ -5,6 +5,7 @@ import { getFlag } from '@/utils/geo';
import lodash from 'lodash';
import $ from '@/core/app';
import { hex_md5 } from '@/vendor/md5';
import {ProxyUtils} from '@/core/proxy-utils';
/**
The rule "(name CONTAINS "🇨🇳") AND (port IN [80, 443])" can be expressed as follows:
@@ -631,6 +632,7 @@ function createDynamicFunction(name, script, $arguments) {
'$persistentStore',
'$httpClient',
'$notification',
'ProxyUtils',
`${script}\n return ${name}`,
)(
$arguments,
@@ -642,13 +644,15 @@ function createDynamicFunction(name, script, $arguments) {
$httpClient,
// eslint-disable-next-line no-undef
$notification,
ProxyUtils
);
} else {
return new Function(
'$arguments',
'$substore',
'lodash',
'ProxyUtils',
`${script}\n return ${name}`,
)($arguments, $, lodash);
)($arguments, $, lodash, ProxyUtils);
}
}

View File

@@ -72,7 +72,7 @@ function shadowsocksr(proxy) {
// obfs
result.appendIfPresent(`,obfs=${proxy.obfs}`, 'obfs');
result.appendIfPresent(`,obfs-host=${proxy['obfs-param']}`, 'obfs-param');
result.appendIfPresent(`,obfs-param=${proxy['obfs-param']}`, 'obfs-param');
// tfo
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');

View File

@@ -1,5 +1,5 @@
import { deleteByName, findByName, updateByName } from '@/utils/database';
import { COLLECTIONS_KEY } from '@/constants';
import { COLLECTIONS_KEY, ARTIFACTS_KEY } from '@/constants';
import { failed, success } from '@/restful/response';
import $ from '@/core/app';
import { RequestInvalidError, ResourceNotFoundError } from '@/restful/errors';
@@ -67,6 +67,21 @@ function updateCollection(req, res) {
...collection,
};
$.info(`正在更新组合订阅:${name}...`);
if (name !== newCol.name) {
// update all artifacts referring this collection
const allArtifacts = $.read(ARTIFACTS_KEY) || [];
for (const artifact of allArtifacts) {
if (
artifact.type === 'collection' &&
artifact.source === oldCol.name
) {
artifact.source = newCol.name;
}
}
$.write(allArtifacts, ARTIFACTS_KEY);
}
updateByName(allCols, name, newCol);
$.write(allCols, COLLECTIONS_KEY);
success(res, newCol);

View File

@@ -5,7 +5,7 @@ import {
RequestInvalidError,
} from './errors';
import { deleteByName, findByName, updateByName } from '@/utils/database';
import { SUBS_KEY, COLLECTIONS_KEY } from '@/constants';
import { SUBS_KEY, COLLECTIONS_KEY, ARTIFACTS_KEY } from '@/constants';
import { getFlowHeaders } from '@/utils/flow';
import { success, failed } from './response';
import $ from '@/core/app';
@@ -137,14 +137,28 @@ function updateSubscription(req, res) {
$.info(`正在更新订阅: ${name}`);
// allow users to update the subscription name
if (name !== sub.name) {
// we need to find out all collections refer to this name
const allCols = $.read(COLLECTIONS_KEY);
// update all collections refer to this name
const allCols = $.read(COLLECTIONS_KEY) || [];
for (const collection of allCols) {
const idx = collection.subscriptions.indexOf(name);
if (idx !== -1) {
collection.subscriptions[idx] = sub.name;
}
}
// update all artifacts referring this subscription
const allArtifacts = $.read(ARTIFACTS_KEY) || [];
for (const artifact of allArtifacts) {
if (
artifact.type === 'subscription' &&
artifact.source == name
) {
artifact.source = sub.name;
}
}
$.write(allCols, COLLECTIONS_KEY);
$.write(allArtifacts, ARTIFACTS_KEY);
}
updateByName(allSubs, name, newSub);
$.write(allSubs, SUBS_KEY);

View File

@@ -81,7 +81,8 @@ function doMigrationV2() {
useless: 'DEFAULT',
},
};
processes.forEach((p) => {
for (const p of processes) {
if (!p.type) continue;
if (p.type === 'Useless Filter') {
quickSettingOperator.args.useless = 'ENABLED';
} else if (p.type === 'Set Property Operator') {
@@ -120,7 +121,7 @@ function doMigrationV2() {
} else {
newProcesses.push(p);
}
});
}
newProcesses.unshift(quickSettingOperator);
item.process = newProcesses;
}

View File

@@ -253,6 +253,17 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
events.onRequest(method, options);
if (options.node) {
// Surge & Loon allow connecting to a server using a specified proxy node
if (isSurge) {
const build = $environment['surge-build'];
if (build && parseInt(build) >= 2407) {
options['policy-descriptor'] = options.node;
delete options.node;
}
}
}
let worker;
if (isQX) {
worker = $task.fetch({

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,18 @@ http:
type: request
require-body: true
timeout: 120
cron:
script:
- name: cron-sync-artifacts
cron: "0 0 * * *"
timeout: 120
script-providers:
sub-store:
url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store.min.js
interval: 86400
cron-sync-artifacts:
url: https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js
interval: 86400

151
scripts/ip-flag.js Normal file
View File

@@ -0,0 +1,151 @@
const RESOURCE_CACHE_KEY = '#sub-store-cached-resource';
const CACHE_EXPIRATION_TIME_MS = 10 * 60 * 1000;
const $ = $substore;
class ResourceCache {
constructor(expires) {
this.expires = expires;
if (!$.read(RESOURCE_CACHE_KEY)) {
$.write('{}', RESOURCE_CACHE_KEY);
}
this.resourceCache = JSON.parse($.read(RESOURCE_CACHE_KEY));
this._cleanup();
}
_cleanup() {
// clear obsolete cached resource
let clear = false;
Object.entries(this.resourceCache).forEach((entry) => {
const [id, updated] = entry;
if (new Date().getTime() - updated > this.expires) {
$.delete(`#${id}`);
delete this.resourceCache[id];
clear = true;
}
});
if (clear) this._persist();
}
revokeAll() {
Object.keys(this.resourceCache).forEach((id) => {
$.delete(`#${id}`);
});
this.resourceCache = {};
this._persist();
}
_persist() {
$.write(JSON.stringify(this.resourceCache), RESOURCE_CACHE_KEY);
}
get(id) {
const updated = this.resourceCache[id];
if (updated && new Date().getTime() - updated <= this.expires) {
return $.read(`#${id}`);
}
return null;
}
set(id, value) {
this.resourceCache[id] = new Date().getTime();
this._persist();
$.write(value, `#${id}`);
}
}
const resourceCache = new ResourceCache(CACHE_EXPIRATION_TIME_MS);
async function operator(proxies) {
const { isLoon, isSurge } = $substore.env;
let support = true;
if (isLoon) {
support = true;
} else if (isSurge) {
const build = $environment['surge-build'];
if (build && parseInt(build) >= 2407) {
support = true;
}
}
if (support) {
await Promise.all(proxies.map(async proxy => {
try {
// remove the original flag
let proxyName = removeFlag(proxy.name);
// query ip-api
const countryCode = await queryIpApi(proxy);
proxyName = getFlagEmoji(countryCode) + ' ' + proxyName;
proxy.name = proxyName;
} catch (err) {
// TODO:
}
}));
} else {
$.error(`IP Flag only supports Loon and Surge!`);
}
return proxies;
}
const tasks = new Map();
async function queryIpApi(proxy) {
const id = getId(proxy);
if (tasks.has(id)) {
return tasks.get(id);
}
const ua = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:78.0) Gecko/20100101 Firefox/78.0";
const headers = {
"User-Agent": ua
};
const { isLoon } = $substore.env;
const target = isLoon ? "Loon" : "Surge";
const result = new Promise((resolve, reject) => {
const cached = resourceCache.get(id);
if (cached) {
resolve(cached);
}
const url = `http://ip-api.com/json`;
const node = ProxyUtils.produce([proxy], target);
$.http.get({
url,
headers,
node
}).then(resp => {
const body = resp.body;
const data = JSON.parse(body);
if (data.status === "success") {
resourceCache.set(id, data.countryCode);
resolve(data.countryCode);
} else {
reject(new Error(data.message));
}
}).catch(err => {
console.log(err);
reject(err);
});
});
tasks.set(id, result);
return result;
}
function getId(proxy) {
return MD5(`IP-FLAG-${proxy.server}-${proxy.port}`);
}
function getFlagEmoji(countryCode) {
const codePoints = countryCode
.toUpperCase()
.split('')
.map(char => 127397 + char.charCodeAt());
return String.fromCodePoint(...codePoints);
}
function removeFlag(str) {
return str
.replace(/[\uD83C][\uDDE6-\uDDFF][\uD83C][\uDDE6-\uDDFF]/g, '')
.trim();
}
var MD5 = function (d) { var r = M(V(Y(X(d), 8 * d.length))); return r.toLowerCase() }; function M(d) { for (var _, m = "0123456789ABCDEF", f = "", r = 0; r < d.length; r++)_ = d.charCodeAt(r), f += m.charAt(_ >>> 4 & 15) + m.charAt(15 & _); return f } function X(d) { for (var _ = Array(d.length >> 2), m = 0; m < _.length; m++)_[m] = 0; for (m = 0; m < 8 * d.length; m += 8)_[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32; return _ } function V(d) { for (var _ = "", m = 0; m < 32 * d.length; m += 8)_ += String.fromCharCode(d[m >> 5] >>> m % 32 & 255); return _ } function Y(d, _) { d[_ >> 5] |= 128 << _ % 32, d[14 + (_ + 64 >>> 9 << 4)] = _; for (var m = 1732584193, f = -271733879, r = -1732584194, i = 271733878, n = 0; n < d.length; n += 16) { var h = m, t = f, g = r, e = i; f = md5_ii(f = md5_ii(f = md5_ii(f = md5_ii(f = md5_hh(f = md5_hh(f = md5_hh(f = md5_hh(f = md5_gg(f = md5_gg(f = md5_gg(f = md5_gg(f = md5_ff(f = md5_ff(f = md5_ff(f = md5_ff(f, r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = md5_ff(r, i = md5_ff(i, m = md5_ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = md5_gg(r, i = md5_gg(i, m = md5_gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = md5_hh(r, i = md5_hh(i, m = md5_hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = md5_ii(r, i = md5_ii(i, m = md5_ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551), m = safe_add(m, h), f = safe_add(f, t), r = safe_add(r, g), i = safe_add(i, e) } return Array(m, f, r, i) } function md5_cmn(d, _, m, f, r, i) { return safe_add(bit_rol(safe_add(safe_add(_, d), safe_add(f, i)), r), m) } function md5_ff(d, _, m, f, r, i, n) { return md5_cmn(_ & m | ~_ & f, d, _, r, i, n) } function md5_gg(d, _, m, f, r, i, n) { return md5_cmn(_ & f | m & ~f, d, _, r, i, n) } function md5_hh(d, _, m, f, r, i, n) { return md5_cmn(_ ^ m ^ f, d, _, r, i, n) } function md5_ii(d, _, m, f, r, i, n) { return md5_cmn(m ^ (_ | ~f), d, _, r, i, n) } function safe_add(d, _) { var m = (65535 & d) + (65535 & _); return (d >> 16) + (_ >> 16) + (m >> 16) << 16 | 65535 & m } function bit_rol(d, _) { return d << _ | d >>> 32 - _ }