├── .gitignore
├── LICENSE
├── README.md
├── app.js
├── config
├── global.js
└── weixin.js
├── demo
├── home.png
└── search.png
├── lib
├── cookiePool.js
├── counter.js
├── factory.js
├── redis.js
├── render.js
├── request.js
├── rss
│ ├── weibo.js
│ └── weixin.js
├── sogouEncrypt.js
└── sogouEncryptSalt.js
├── package.json
├── task
└── cookiePool.js
└── views
├── home.html
└── weixin.html
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 wlwr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rss
2 |
3 |
4 | **搜狗接口变更,目前失效中**
5 |
6 |
7 | ----
8 |
9 | 提供微信公众号RSS订阅接口,基于nodejs koajs开发
10 |
11 | 演示地址:[http://rss.wlwr.net](http://rss.wlwr.net)
12 |
13 | 注意:因演示地址访问量过多,服务器IP被搜狗加入黑名单,故不定期关闭演示地址。建议取代码搭建在自己服务器上。
14 |
15 | ## 更新日志
16 |
17 | - 2015.04.26 搜狗接口变更 (已修复)
18 | 1. 去掉 `phantomjs` 依赖,不再需要定时生成cookie池 (好消息)
19 | 2. 以前搜狗的openid标识失效,改用微信号ID作为标识 (坏消息)
20 |
21 | - 2015.10.20 搜狗调整加密请求方式 (已修复)
22 |
23 | - 2015.08.11 增加微信账号查询功能
24 |
25 | - 2015.06.28 cookie池采集采用 `phantomjs`,及 加密盐值采集
26 |
27 | - 2015.05.22 搜狗微信接口做了加密处理,导致采集失败。(已修复)
28 |
29 | ---
30 |
31 | ## 搭建
32 |
33 | - 安装 `io.js` 或 `Node.js 0.11` 以上版本,才支持 ES6相关语法
34 |
35 | - 安装 `redis-server` 端,默认端口是 `6379`
36 |
37 |
38 | 进入项目根目录, `npm install`,然后 `node --harmony app.js` 即可启动
39 |
40 | ----
41 |
42 | ## 截图:
43 |
44 | 
45 |
46 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Module dependencies.
4 | */
5 |
6 | var logger = require('koa-logger');
7 | var route = require('koa-route');
8 | var parse = require('co-body');
9 | var koa = require('koa');
10 | var render = require('./lib/render');
11 | var factory = require('./lib/factory');
12 | var counter = require('./lib/counter');
13 |
14 |
15 | var app = koa();
16 | // "database"
17 |
18 |
19 | // middleware
20 |
21 | app.use(logger());
22 |
23 | // route middleware
24 |
25 | app.use(route.get('/', home));
26 | app.use(route.get('/weixin/:id', weixin));
27 | app.use(route.get('/search/:keyword', search));
28 |
29 |
30 | // route definitions
31 |
32 | /**
33 | * home
34 | */
35 |
36 | function *home() {
37 | var count = yield counter.get();
38 | this.body = yield render('home', {count : count});
39 | }
40 |
41 |
42 | /**
43 | * weixin-rss builder
44 | */
45 |
46 | function *weixin(id) {
47 | try {
48 | var handler = factory.create('weixin', id);
49 | var data = yield handler.getData();
50 | yield counter.incr();
51 | this.type = 'text/xml; charset=UTF-8';
52 | this.body = yield render('weixin', {rss : data});
53 | } catch (err) {
54 | console.log(err);
55 | this.body = err.message;
56 | }
57 | }
58 |
59 | function *search(keyword) {
60 | try {
61 | var handler = factory.create('weixin');
62 | var html = yield handler.searchNameProxy(keyword);
63 | var result = {error : false, data : html, message : 'success'};
64 | this.body = JSON.stringify(result);
65 | } catch (err) {
66 | console.log(err);
67 | this.body = {error : true, data : null, message : err.message};
68 | }
69 | }
70 |
71 |
72 | // listen
73 | app.listen(3000);
74 | console.log('listening on port 3000');
75 |
--------------------------------------------------------------------------------
/config/global.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 |
4 | proxyEnable: false,
5 |
6 | redis: {
7 | host: '127.0.0.1',
8 | port: '6379',
9 | },
10 |
11 | proxy: 'http://114.36.105.161:8088'
12 |
13 | }
--------------------------------------------------------------------------------
/config/weixin.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |
3 | imageProxy : 'http://read.html5.qq.com/image?src=forum&q=5&r=0&imgflag=7&imageUrl=%s',
4 |
5 | cacheEnable: true,
6 |
7 | api: {
8 | getList: 'http://weixin.sogou.com/gzhjs?cb=sogou.weixin.gzhcb&openid=%s&ext=%s&page=1&t=%d',
9 | getCookie: 'http://weixin.sogou.com/weixin?query=123',
10 | searchName: 'http://weixin.sogou.com/weixin?query=%s'
11 | },
12 |
13 |
14 | listCachePolicy: {
15 | lifeTime: 86400,
16 | cacheId: 'rss:list:%s'
17 | },
18 |
19 | contentCachePolicy: {
20 | lifeTime: 864000,
21 | cacheId: 'rss:content:%s'
22 | },
23 |
24 | searchCachePolicy: {
25 | lifeTime: 864000,
26 | cacheId: 'rss:search:%s'
27 | },
28 |
29 | //options : request || phantom
30 | cookieCollectEngine : 'phantom',
31 |
32 | cookieNum : 10,
33 |
34 | cookieCachePolicy: {
35 | lifeTime: 43200,
36 | cacheId: 'rss:cookies'
37 | }
38 | }
--------------------------------------------------------------------------------
/demo/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlwr/rss/714e8a3521e2bcd806ea43adc76eacb965635712/demo/home.png
--------------------------------------------------------------------------------
/demo/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlwr/rss/714e8a3521e2bcd806ea43adc76eacb965635712/demo/search.png
--------------------------------------------------------------------------------
/lib/cookiePool.js:
--------------------------------------------------------------------------------
1 | var request = require('./request');
2 | var redis = require('./redis');
3 | var util = require('util');
4 | var config = require('../config/weixin');
5 | var phantom = require('phantom');
6 |
7 | function CookiePool() {
8 | this.cacheId = config.cookieCachePolicy.cacheId;
9 | this.lifeTime = config.cookieCachePolicy.lifeTime;
10 | this.num = config.cookieNum;
11 | this.engine = config.cookieCollectEngine;
12 | }
13 |
14 | CookiePool.prototype.collectAll = function*() {
15 | var cookies = [];
16 | for (var i = 0, l = this.num; i < l; i++) {
17 | var cookie = yield this.collect();
18 | if (cookie)
19 | cookies.push(cookie);
20 | };
21 | return cookies;
22 | }
23 |
24 | CookiePool.prototype.save = function*(cookies) {
25 | yield redis.sadd(this.cacheId, cookies);
26 | yield redis.expire(this.cacheId, this.lifeTime);
27 | }
28 |
29 | CookiePool.prototype.collect = function*() {
30 | var actionName = this.engine + 'Collect';
31 | var apiUrl = util.format(config.api.getCookie);
32 | var cookie = yield this[actionName](apiUrl);
33 | console.log(cookie);
34 | return cookie;
35 | }
36 |
37 | CookiePool.prototype.requestCollect = function*(url) {
38 | var result = yield request(url, {
39 | proxyEnable: false
40 | });
41 | if (!result.error && result.statusCode == 200) {
42 | var cookieStr = result.headers['set-cookie'].join(';');
43 | return this.parseCookie(cookieStr);
44 | }
45 | }
46 |
47 | CookiePool.prototype.phantomCollect = function(url) {
48 | var me = this;
49 | return function(callback) {
50 | phantom.create(function (ph) {
51 | ph.createPage(function (page) {
52 | page.set('onResourceRequested', function(requestData, request) {
53 | if ((/http:\/\/.+?\.css/gi).test(requestData['url']) || requestData['Content-Type'] == 'text/css') {
54 | // console.log('The url of the request is matching. Aborting: ' + requestData['url']);
55 | request['abort()'];
56 | }
57 | });
58 | page.open(url, function (status) {
59 | page.evaluate(function () { return document.cookie; }, function (result) {
60 | callback(null, me.parseCookie(result + ';'));
61 | ph.exit();
62 | });
63 | });
64 | });
65 | }, {parameters: {'load-images': 'no'}});
66 | }
67 | }
68 |
69 | CookiePool.prototype.parseCookie = function(cookieStr) {
70 | var SNUID = cookieStr.match(/(SNUID=\S+?);/);
71 | var SUID = cookieStr.match(/(SUID=\S+?);/)[1];
72 | if (SNUID)
73 | SNUID = SNUID[1];
74 | var SUV = 'SUV=' + ((new Date()).getTime())*1000+Math.round(Math.random()*1000);
75 | var cookie = [SNUID, SUID, SUV].join(';');
76 | return cookie;
77 | }
78 |
79 | CookiePool.prototype.getRandom = function*() {
80 | return yield redis.srandmember(this.cacheId);
81 | }
82 |
83 | CookiePool.prototype.destroy = function*() {
84 | return yield redis.del(this.cacheId);
85 | }
86 |
87 | CookiePool.prototype.getAll = function*() {
88 | return yield redis.smembers(this.cacheId);
89 | }
90 |
91 | module.exports = new CookiePool();
92 |
--------------------------------------------------------------------------------
/lib/counter.js:
--------------------------------------------------------------------------------
1 | var redis = require('./redis');
2 |
3 | function Counter() {
4 | this.id = 'rss:counter';
5 | }
6 |
7 | Counter.prototype.incr = function* () {
8 | return yield redis.incr(this.id);
9 | }
10 |
11 | Counter.prototype.get = function* () {
12 | return yield redis.get(this.id);
13 | }
14 |
15 | module.exports = new Counter();
16 |
--------------------------------------------------------------------------------
/lib/factory.js:
--------------------------------------------------------------------------------
1 | function factory() {
2 |
3 | }
4 |
5 | factory.create = function(source, id) {
6 | var Handler = require('./rss/' + source + '.js');
7 | return new Handler(id);
8 | }
9 |
10 | module.exports = factory;
--------------------------------------------------------------------------------
/lib/redis.js:
--------------------------------------------------------------------------------
1 | var co = require('co');
2 | var config = require('../config/global');
3 | var redisClient = require('redis').createClient(config.redis.port, config.redis.host);
4 | redisClient.on("error", function (err) {
5 | console.log("Error " + err);
6 | });
7 | var wrapper = require('co-redis');
8 | var redisCo = wrapper(redisClient);
9 | module.exports = redisCo;
--------------------------------------------------------------------------------
/lib/render.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Module dependencies.
4 | */
5 |
6 | var views = require('co-views');
7 |
8 | // setup views mapping .html
9 | // to the swig template engine
10 |
11 | module.exports = views(__dirname + '/../views', {
12 | map: { html: 'swig' }
13 | });
--------------------------------------------------------------------------------
/lib/request.js:
--------------------------------------------------------------------------------
1 | var config = require('../config/global');
2 | var _request = require('request');
3 |
4 |
5 | function request(uri, options) {
6 | return function(callback) {
7 | if (options && options.proxyEnable) {
8 | _request = _request.defaults({proxy: config.proxy});
9 | }
10 | _request(uri, options, function(error, response, body) {
11 | callback(error, response);
12 | })
13 | }
14 | }
15 |
16 | module.exports = request;
--------------------------------------------------------------------------------
/lib/rss/weibo.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wlwr/rss/714e8a3521e2bcd806ea43adc76eacb965635712/lib/rss/weibo.js
--------------------------------------------------------------------------------
/lib/rss/weixin.js:
--------------------------------------------------------------------------------
1 | var request = require('../request');
2 | var redis = require('../redis');
3 | var xmlParser = require('xml2json');
4 | var util = require('util');
5 | var config = require('../../config/weixin');
6 |
7 | function Weixin(id) {
8 | this.id = id;
9 | }
10 |
11 | Weixin.prototype.getData = function*() {
12 | var rss = {};
13 | var now = new Date();
14 |
15 | //rss-cache
16 | var rssCacheId = util.format(config.listCachePolicy.cacheId, this.id);
17 | var rssCache = yield redis.get(rssCacheId);
18 | if (config.cacheEnable && rssCache) {
19 | return JSON.parse(rssCache);
20 | }
21 |
22 | //query-list-url
23 | var users = yield this.searchNameProxy(this.id);
24 | if (users.length == 0)
25 | throw Error('不存在此微信号');
26 | var rss = users[0];
27 | var apiUrl = rss.url;
28 |
29 | //list-collect
30 | rss.items = yield this.getListData(apiUrl);
31 | for (var i = 0, l = rss.items.length; i < l; i++) {
32 | var item = rss.items[i];
33 | now.setTime(item.lastModified*1000);
34 | item.formatDate = now.toUTCString();
35 | var content = yield this.getContentDataProxy(item);
36 | if (content) {
37 | item.fullContent = content.content;
38 | }
39 | };
40 | if (rss.items.length == 0)
41 | throw Error('无相关文章');
42 |
43 | rss = this.getRssInfo(rss);
44 | yield redis.setex(rssCacheId, config.listCachePolicy.lifeTime, JSON.stringify(rss));
45 | return rss;
46 | };
47 |
48 |
49 | Weixin.prototype.getRssInfo = function(rss) {
50 | var now = new Date();
51 | var recentItem = rss.items[0];
52 | rss.id = this.id;
53 | rss.title = recentItem.sourcename;
54 | rss.lastModified = recentItem.lastModified;
55 | now.setTime(rss.lastModified*1000);
56 | rss.formatDate = now.toUTCString();
57 | rss.author = recentItem.sourcename;
58 | return rss;
59 | };
60 |
61 |
62 | Weixin.prototype.getListData = function*(apiUrl) {
63 | var items = [];
64 | var response =
65 | yield request(apiUrl, {
66 | headers: yield this.getRequestHeaders()
67 | });
68 | if (response.error)
69 | throw new Error(response.error);
70 | if (response.statusCode != 200)
71 | throw new Error('response statusCode: ' + response.statusCode);
72 |
73 | var body = response.body;
74 | var match = body.match(/var\smsgList\s=\s'([\s\S]*?)';/);
75 | if (!match || !match[1]) {
76 | throw new Error('无法获取列表数据');
77 | }
78 | var dataJson = JSON.parse(match[1].html());
79 | if (dataJson.list.length === 0)
80 | throw new Error(this.id + ': 无相关文章');
81 | dataJson.list.forEach(function(item) {
82 | var _item = {
83 | 'docid' : item.comm_msg_info.id,
84 | 'lastModified' : item.comm_msg_info.datetime,
85 | 'title' : item.app_msg_ext_info.title,
86 | 'content' : item.app_msg_ext_info.digest,
87 | 'url' : 'http://mp.weixin.qq.com' + item.app_msg_ext_info.content_url.substr(1).html(),
88 | 'sourcename' : item.app_msg_ext_info.author
89 | };
90 | items.push(_item);
91 | //subList
92 | var subList = item.app_msg_ext_info.multi_app_msg_item_list;
93 | if (subList.length > 0) {
94 | subList.forEach(function(subItem) {
95 | var _item = {
96 | 'docid' : subItem.fileid,
97 | 'lastModified' : item.comm_msg_info.datetime,
98 | 'title' : subItem.title,
99 | 'content' : subItem.digest,
100 | 'url' : 'http://mp.weixin.qq.com' + subItem.content_url.substr(1).html(),
101 | 'sourcename' : subItem.author
102 | };
103 | items.push(_item);
104 | });
105 | }
106 | });
107 | return items;
108 | }
109 |
110 |
111 | Weixin.prototype.getContentDataProxy = function*(item) {
112 | //content-cache
113 | var contentCacheId = util.format(config.contentCachePolicy.cacheId, item.docid);
114 | var content = yield redis.get(contentCacheId);
115 | if (!config.cacheEnable || !content || !content.content) {
116 | //content-collect
117 | content = yield this.getContentData(item);
118 | if (content) {
119 | yield redis.setex(contentCacheId, config.contentCachePolicy.lifeTime, JSON.stringify(content));
120 | }
121 | } else {
122 | content = JSON.parse(content);
123 | }
124 | return content;
125 | };
126 |
127 |
128 | Weixin.prototype.getContentData = function*(item) {
129 | var url = item.url;
130 | var result =
131 | yield request(url, {
132 | headers: yield this.getRequestHeaders()
133 | });
134 | if (!result.error && result.statusCode == 200) {
135 | var match = result.body.match(/id=\"js_content\">([\s\S]*?)<\/div>/);
136 | var cover = this.getCover(result.body);
137 | if (match && match[1]) {
138 | content = match[1].replace(/data-src/g, 'src');
139 | var coverHtml = cover ? '
' : '';
140 | content = coverHtml + content;
141 | return {'content' : content, 'source' : result.request.href};
142 | }
143 | }
144 | }
145 |
146 | Weixin.prototype.getCover = function(html) {
147 | var match = html.match(/var\scover\s=\s\"([\s\S]*?)\";/);
148 | if (match && match[1]) {
149 | return match[1];
150 | }
151 | };
152 |
153 | Weixin.prototype.getRequestHeaders = function*() {
154 | return {
155 | 'Accept': '*/*',
156 | 'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
157 | 'Cache-Control': 'max-age=0',
158 | 'Connection': 'keep-alive',
159 | 'Cookie': 'sd_userid=74761436088560498; sd_cookie_crttime=1436088560498; 3g_guest_id=-9124332030488707072; ts_refer=www.baidu.com/link; ts_uid=1816741185; eas_sid=f1w4I5H7w224X4z3w9J27844g9; _ga=GA1.2.1140860462.1438155424; pgv_pvid=1467256065; o_cookie=527114214; noticeLoginFlag=1; ptui_loginuin=527114214; ptcz=2a4b878f712cb71f3eeb0fda5a3d488fd4688d292820e1485fa04ee62c2c9500; pt2gguin=o0527114214; uin=o0527114214; skey=@k3f0ouWCY; qm_username=527114214; qm_sid=705e43ed71261bdea26e56d389d4d334,cpfgcNUS3Cs4.; ptisp=ctc',
160 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36',
161 | 'Referer' : 'http://weixin.sogou.com/weixin?type=1&query=%E4%B8%80%E5%85%9C%E7%B3%96&ie=utf8&_sug_=y&_sug_type_=&w=01019900&sut=2112&sst0=1468836385188&lkt=1%2C1468836385080%2C1468836385080'
162 | }
163 | }
164 |
165 | Weixin.prototype.searchNameProxy = function*(keyword) {
166 | var searchCacheId = util.format(config.searchCachePolicy.cacheId, keyword);
167 | var result = yield redis.get(searchCacheId);
168 | if (!config.cacheEnable || !result) {
169 | var result = yield this.searchName(keyword);
170 | yield redis.setex(searchCacheId, config.searchCachePolicy.lifeTime, JSON.stringify(result));
171 | } else {
172 | result = JSON.parse(result);
173 | }
174 | return result;
175 | };
176 |
177 | Weixin.prototype.searchName = function*(keyword) {
178 | var apiUrl = util.format(config.api.searchName, encodeURIComponent(keyword));
179 | var response = yield request(apiUrl, {
180 | headers: yield this.getRequestHeaders()
181 | });
182 | if (response.error)
183 | throw new Error(response.error);
184 | if (response.statusCode != 200)
185 | throw new Error('response statusCode: ' + response.statusCode);
186 | var regexp = //g;
187 | var matches = response.body.match(regexp);
188 | if (!matches)
189 | throw new Error('查询不到相关账号');
190 | var result = [];
191 | for (var i = 0, l = matches.length; i < l; i++) {
192 | var item = matches[i];
193 | var match = item.match(/href="([^"]*)"[\s\S]*
(.*)<\/h3>[\s\S]*
微信号:(.*)<\/label><\/span>[\s\S]*功能介绍:<\/span>(.*)<\/span>/);
194 | if (match) {
195 | var info = {
196 | 'url' : match[1].html(),
197 | 'pic' : util.format(config.imageProxy, match[2]),
198 | 'name' : this.stripTags(match[3]),
199 | 'id' : match[4],
200 | 'description' : this.stripTags(match[5])
201 | }
202 | result.push(info);
203 | }
204 | };
205 | return result;
206 | }
207 |
208 | Weixin.prototype.stripTags = function(string) {
209 | return string.replace(/<(.*?)>/g, '');
210 | }
211 |
212 | String.prototype.html = function(encode) {
213 | var replace =["'", "'", """, '"', " ", " ", ">", ">", "<", "<", "&", "&", "¥", "¥"];
214 | if (encode) {
215 | replace.reverse();
216 | }
217 | for (var i=0,str=this;i< replace.length;i+= 2) {
218 | str=str.replace(new RegExp(replace[i],'g'),replace[i+1]);
219 | }
220 | return str;
221 | };
222 |
223 |
224 | module.exports = Weixin;
--------------------------------------------------------------------------------
/lib/sogouEncrypt.js:
--------------------------------------------------------------------------------
1 | var SogouEncrypt;
2 | var CryptoJS = CryptoJS || function(o, e) {
3 | var h = {},
4 | g = h.lib = {},
5 | z = function() {},
6 | y = g.Base = {
7 | extend: function(b) {
8 | z.prototype = this;
9 | var d = new z;
10 | b && d.mixIn(b);
11 | d.hasOwnProperty("init") || (d.init = function() {
12 | d.$super.init.apply(this, arguments)
13 | });
14 | d.init.prototype = d;
15 | d.$super = this;
16 | return d
17 | },
18 | create: function() {
19 | var b = this.extend();
20 | b.init.apply(b, arguments);
21 | return b
22 | },
23 | init: function() {},
24 | mixIn: function(b) {
25 | for (var d in b) {
26 | b.hasOwnProperty(d) && (this[d] = b[d])
27 | }
28 | b.hasOwnProperty("toString") && (this.toString = b.toString)
29 | },
30 | clone: function() {
31 | return this.init.prototype.extend(this)
32 | }
33 | },
34 | a = g.WordArray = y.extend({
35 | init: function(b, d) {
36 | b = this.words = b || [];
37 | this.sigBytes = d != e ? d : 4 * b.length
38 | },
39 | toString: function(b) {
40 | return (b || m).stringify(this)
41 | },
42 | concat: function(b) {
43 | var p = this.words,
44 | n = b.words,
45 | l = this.sigBytes;
46 | b = b.sigBytes;
47 | this.clamp();
48 | if (l % 4) {
49 | for (var d = 0; d < b; d++) {
50 | p[l + d >>> 2] |= (n[d >>> 2] >>> 24 - 8 * (d % 4) & 255) << 24 - 8 * ((l + d) % 4)
51 | }
52 | } else {
53 | if (65535 < n.length) {
54 | for (d = 0; d < b; d += 4) {
55 | p[l + d >>> 2] = n[d >>> 2]
56 | }
57 | } else {
58 | p.push.apply(p, n)
59 | }
60 | }
61 | this.sigBytes += b;
62 | return this
63 | },
64 | clamp: function() {
65 | var b = this.words,
66 | d = this.sigBytes;
67 | b[d >>> 2] &= 4294967295 << 32 - 8 * (d % 4);
68 | b.length = o.ceil(d / 4)
69 | },
70 | clone: function() {
71 | var b = y.clone.call(this);
72 | b.words = this.words.slice(0);
73 | return b
74 | },
75 | random: function(b) {
76 | for (var l = [], d = 0; d < b; d += 4) {
77 | l.push(4294967296 * o.random() | 0)
78 | }
79 | return new a.init(l, b)
80 | }
81 | }),
82 | k = h.enc = {},
83 | m = k.Hex = {
84 | stringify: function(b) {
85 | var p = b.words;
86 | b = b.sigBytes;
87 | for (var n = [], l = 0; l < b; l++) {
88 | var d = p[l >>> 2] >>> 24 - 8 * (l % 4) & 255;
89 | n.push((d >>> 4).toString(16));
90 | n.push((d & 15).toString(16))
91 | }
92 | return n.join("")
93 | },
94 | parse: function(b) {
95 | for (var n = b.length, l = [], d = 0; d < n; d += 2) {
96 | l[d >>> 3] |= parseInt(b.substr(d, 2), 16) << 24 - 4 * (d % 8)
97 | }
98 | return new a.init(l, n / 2)
99 | }
100 | },
101 | i = k.Latin1 = {
102 | stringify: function(b) {
103 | var n = b.words;
104 | b = b.sigBytes;
105 | for (var l = [], d = 0; d < b; d++) {
106 | l.push(String.fromCharCode(n[d >>> 2] >>> 24 - 8 * (d % 4) & 255))
107 | }
108 | return l.join("")
109 | },
110 | parse: function(b) {
111 | for (var n = b.length, l = [], d = 0; d < n; d++) {
112 | l[d >>> 2] |= (b.charCodeAt(d) & 255) << 24 - 8 * (d % 4)
113 | }
114 | return new a.init(l, n)
115 | }
116 | },
117 | j = k.Utf8 = {
118 | stringify: function(b) {
119 | try {
120 | return decodeURIComponent(escape(i.stringify(b)))
121 | } catch (d) {
122 | throw Error("Malformed UTF-8 data")
123 | }
124 | },
125 | parse: function(b) {
126 | return i.parse(unescape(encodeURIComponent(b)))
127 | }
128 | },
129 | c = g.BufferedBlockAlgorithm = y.extend({
130 | reset: function() {
131 | this._data = new a.init;
132 | this._nDataBytes = 0
133 | },
134 | _append: function(b) {
135 | "string" == typeof b && (b = j.parse(b));
136 | this._data.concat(b);
137 | this._nDataBytes += b.sigBytes
138 | },
139 | _process: function(l) {
140 | var t = this._data,
141 | s = t.words,
142 | p = t.sigBytes,
143 | n = this.blockSize,
144 | d = p / (4 * n),
145 | d = l ? o.ceil(d) : o.max((d | 0) - this._minBufferSize, 0);
146 | l = d * n;
147 | p = o.min(4 * l, p);
148 | if (l) {
149 | for (var r = 0; r < l; r += n) {
150 | this._doProcessBlock(s, r)
151 | }
152 | r = s.splice(0, l);
153 | t.sigBytes -= p
154 | }
155 | return new a.init(r, p)
156 | },
157 | clone: function() {
158 | var b = y.clone.call(this);
159 | b._data = this._data.clone();
160 | return b
161 | },
162 | _minBufferSize: 0
163 | });
164 | g.Hasher = c.extend({
165 | cfg: y.extend(),
166 | init: function(b) {
167 | this.cfg = this.cfg.extend(b);
168 | this.reset()
169 | },
170 | reset: function() {
171 | c.reset.call(this);
172 | this._doReset()
173 | },
174 | update: function(b) {
175 | this._append(b);
176 | this._process();
177 | return this
178 | },
179 | finalize: function(b) {
180 | b && this._append(b);
181 | return this._doFinalize()
182 | },
183 | blockSize: 16,
184 | _createHelper: function(b) {
185 | return function(d, l) {
186 | return (new b.init(l)).finalize(d)
187 | }
188 | },
189 | _createHmacHelper: function(b) {
190 | return function(d, l) {
191 | return (new f.HMAC.init(b, l)).finalize(d)
192 | }
193 | }
194 | });
195 | var f = h.algo = {};
196 | return h
197 | }(Math);
198 | (function() {
199 | var a = CryptoJS,
200 | b = a.lib.WordArray;
201 | a.enc.Base64 = {
202 | stringify: function(j) {
203 | var e = j.words,
204 | i = j.sigBytes,
205 | g = this._map;
206 | j.clamp();
207 | j = [];
208 | for (var h = 0; h < i; h += 3) {
209 | for (var c = (e[h >>> 2] >>> 24 - 8 * (h % 4) & 255) << 16 | (e[h + 1 >>> 2] >>> 24 - 8 * ((h + 1) % 4) & 255) << 8 | e[h + 2 >>> 2] >>> 24 - 8 * ((h + 2) % 4) & 255, f = 0; 4 > f && h + 0.75 * f < i; f++) {
210 | j.push(g.charAt(c >>> 6 * (3 - f) & 63))
211 | }
212 | }
213 | if (e = g.charAt(64)) {
214 | for (; j.length % 4;) {
215 | j.push(e)
216 | }
217 | }
218 | return j.join("")
219 | },
220 | parse: function(k) {
221 | var f = k.length,
222 | i = this._map,
223 | h = i.charAt(64);
224 | h && (h = k.indexOf(h), -1 != h && (f = h));
225 | for (var h = [], j = 0, e = 0; e < f; e++) {
226 | if (e % 4) {
227 | var g = i.indexOf(k.charAt(e - 1)) << 2 * (e % 4),
228 | c = i.indexOf(k.charAt(e)) >>> 6 - 2 * (e % 4);
229 | h[j >>> 2] |= (g | c) << 24 - 8 * (j % 4);
230 | j++
231 | }
232 | }
233 | return b.create(h, j)
234 | },
235 | _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
236 | }
237 | })();
238 | (function(k) {
239 | function c(d, s, l, r, q, p, o) {
240 | d = d + (s & l | ~s & r) + q + o;
241 | return (d << p | d >>> 32 - p) + s
242 | }
243 |
244 | function f(d, s, l, r, q, p, o) {
245 | d = d + (s & r | l & ~r) + q + o;
246 | return (d << p | d >>> 32 - p) + s
247 | }
248 |
249 | function e(d, s, l, r, q, p, o) {
250 | d = d + (s ^ l ^ r) + q + o;
251 | return (d << p | d >>> 32 - p) + s
252 | }
253 |
254 | function n(d, s, l, r, q, p, o) {
255 | d = d + (l ^ (s | ~r)) + q + o;
256 | return (d << p | d >>> 32 - p) + s
257 | }
258 | for (var m = CryptoJS, a = m.lib, i = a.WordArray, j = a.Hasher, a = m.algo, g = [], h = 0; 64 > h; h++) {
259 | g[h] = 4294967296 * k.abs(k.sin(h + 1)) | 0
260 | }
261 | a = a.MD5 = j.extend({
262 | _doReset: function() {
263 | this._hash = new i.init([1732584193, 4023233417, 2562383102, 271733878])
264 | },
265 | _doProcessBlock: function(J, L) {
266 | for (var U = 0; 16 > U; U++) {
267 | var T = L + U,
268 | S = J[T];
269 | J[T] = (S << 8 | S >>> 24) & 16711935 | (S << 24 | S >>> 8) & 4278255360
270 | }
271 | var U = this._hash.words,
272 | T = J[L + 0],
273 | S = J[L + 1],
274 | O = J[L + 2],
275 | N = J[L + 3],
276 | b = J[L + 4],
277 | H = J[L + 5],
278 | y = J[L + 6],
279 | l = J[L + 7],
280 | o = J[L + 8],
281 | K = J[L + 9],
282 | I = J[L + 10],
283 | G = J[L + 11],
284 | p = J[L + 12],
285 | F = J[L + 13],
286 | s = J[L + 14],
287 | d = J[L + 15],
288 | R = U[0],
289 | M = U[1],
290 | Q = U[2],
291 | P = U[3],
292 | R = c(R, M, Q, P, T, 7, g[0]),
293 | P = c(P, R, M, Q, S, 12, g[1]),
294 | Q = c(Q, P, R, M, O, 17, g[2]),
295 | M = c(M, Q, P, R, N, 22, g[3]),
296 | R = c(R, M, Q, P, b, 7, g[4]),
297 | P = c(P, R, M, Q, H, 12, g[5]),
298 | Q = c(Q, P, R, M, y, 17, g[6]),
299 | M = c(M, Q, P, R, l, 22, g[7]),
300 | R = c(R, M, Q, P, o, 7, g[8]),
301 | P = c(P, R, M, Q, K, 12, g[9]),
302 | Q = c(Q, P, R, M, I, 17, g[10]),
303 | M = c(M, Q, P, R, G, 22, g[11]),
304 | R = c(R, M, Q, P, p, 7, g[12]),
305 | P = c(P, R, M, Q, F, 12, g[13]),
306 | Q = c(Q, P, R, M, s, 17, g[14]),
307 | M = c(M, Q, P, R, d, 22, g[15]),
308 | R = f(R, M, Q, P, S, 5, g[16]),
309 | P = f(P, R, M, Q, y, 9, g[17]),
310 | Q = f(Q, P, R, M, G, 14, g[18]),
311 | M = f(M, Q, P, R, T, 20, g[19]),
312 | R = f(R, M, Q, P, H, 5, g[20]),
313 | P = f(P, R, M, Q, I, 9, g[21]),
314 | Q = f(Q, P, R, M, d, 14, g[22]),
315 | M = f(M, Q, P, R, b, 20, g[23]),
316 | R = f(R, M, Q, P, K, 5, g[24]),
317 | P = f(P, R, M, Q, s, 9, g[25]),
318 | Q = f(Q, P, R, M, N, 14, g[26]),
319 | M = f(M, Q, P, R, o, 20, g[27]),
320 | R = f(R, M, Q, P, F, 5, g[28]),
321 | P = f(P, R, M, Q, O, 9, g[29]),
322 | Q = f(Q, P, R, M, l, 14, g[30]),
323 | M = f(M, Q, P, R, p, 20, g[31]),
324 | R = e(R, M, Q, P, H, 4, g[32]),
325 | P = e(P, R, M, Q, o, 11, g[33]),
326 | Q = e(Q, P, R, M, G, 16, g[34]),
327 | M = e(M, Q, P, R, s, 23, g[35]),
328 | R = e(R, M, Q, P, S, 4, g[36]),
329 | P = e(P, R, M, Q, b, 11, g[37]),
330 | Q = e(Q, P, R, M, l, 16, g[38]),
331 | M = e(M, Q, P, R, I, 23, g[39]),
332 | R = e(R, M, Q, P, F, 4, g[40]),
333 | P = e(P, R, M, Q, T, 11, g[41]),
334 | Q = e(Q, P, R, M, N, 16, g[42]),
335 | M = e(M, Q, P, R, y, 23, g[43]),
336 | R = e(R, M, Q, P, K, 4, g[44]),
337 | P = e(P, R, M, Q, p, 11, g[45]),
338 | Q = e(Q, P, R, M, d, 16, g[46]),
339 | M = e(M, Q, P, R, O, 23, g[47]),
340 | R = n(R, M, Q, P, T, 6, g[48]),
341 | P = n(P, R, M, Q, l, 10, g[49]),
342 | Q = n(Q, P, R, M, s, 15, g[50]),
343 | M = n(M, Q, P, R, H, 21, g[51]),
344 | R = n(R, M, Q, P, p, 6, g[52]),
345 | P = n(P, R, M, Q, N, 10, g[53]),
346 | Q = n(Q, P, R, M, I, 15, g[54]),
347 | M = n(M, Q, P, R, S, 21, g[55]),
348 | R = n(R, M, Q, P, o, 6, g[56]),
349 | P = n(P, R, M, Q, d, 10, g[57]),
350 | Q = n(Q, P, R, M, y, 15, g[58]),
351 | M = n(M, Q, P, R, F, 21, g[59]),
352 | R = n(R, M, Q, P, b, 6, g[60]),
353 | P = n(P, R, M, Q, G, 10, g[61]),
354 | Q = n(Q, P, R, M, O, 15, g[62]),
355 | M = n(M, Q, P, R, K, 21, g[63]);
356 | U[0] = U[0] + R | 0;
357 | U[1] = U[1] + M | 0;
358 | U[2] = U[2] + Q | 0;
359 | U[3] = U[3] + P | 0
360 | },
361 | _doFinalize: function() {
362 | var d = this._data,
363 | q = d.words,
364 | l = 8 * this._nDataBytes,
365 | p = 8 * d.sigBytes;
366 | q[p >>> 5] |= 128 << 24 - p % 32;
367 | var o = k.floor(l / 4294967296);
368 | q[(p + 64 >>> 9 << 4) + 15] = (o << 8 | o >>> 24) & 16711935 | (o << 24 | o >>> 8) & 4278255360;
369 | q[(p + 64 >>> 9 << 4) + 14] = (l << 8 | l >>> 24) & 16711935 | (l << 24 | l >>> 8) & 4278255360;
370 | d.sigBytes = 4 * (q.length + 1);
371 | this._process();
372 | d = this._hash;
373 | q = d.words;
374 | for (l = 0; 4 > l; l++) {
375 | p = q[l], q[l] = (p << 8 | p >>> 24) & 16711935 | (p << 24 | p >>> 8) & 4278255360
376 | }
377 | return d
378 | },
379 | clone: function() {
380 | var d = j.clone.call(this);
381 | d._hash = this._hash.clone();
382 | return d
383 | }
384 | });
385 | m.MD5 = j._createHelper(a);
386 | m.HmacMD5 = j._createHmacHelper(a)
387 | })(Math);
388 | (function() {
389 | var b = CryptoJS,
390 | e = b.lib,
391 | f = e.Base,
392 | a = e.WordArray,
393 | e = b.algo,
394 | c = e.EvpKDF = f.extend({
395 | cfg: f.extend({
396 | keySize: 4,
397 | hasher: e.MD5,
398 | iterations: 1
399 | }),
400 | init: function(g) {
401 | this.cfg = this.cfg.extend(g)
402 | },
403 | compute: function(k, g) {
404 | for (var i = this.cfg, t = i.hasher.create(), l = a.create(), o = l.words, h = i.keySize, i = i.iterations; o.length < h;) {
405 | j && t.update(j);
406 | var j = t.update(k).finalize(g);
407 | t.reset();
408 | for (var m = 1; m < i; m++) {
409 | j = t.finalize(j), t.reset()
410 | }
411 | l.concat(j)
412 | }
413 | l.sigBytes = 4 * h;
414 | return l
415 | }
416 | });
417 | b.EvpKDF = function(i, g, h) {
418 | return c.create(h).compute(i, g)
419 | }
420 | })();
421 | CryptoJS.lib.Cipher || function(B) {
422 | var g = CryptoJS,
423 | j = g.lib,
424 | i = j.Base,
425 | D = j.WordArray,
426 | C = j.BufferedBlockAlgorithm,
427 | e = g.enc.Base64,
428 | z = g.algo.EvpKDF,
429 | A = j.Cipher = C.extend({
430 | cfg: i.extend(),
431 | createEncryptor: function(c, b) {
432 | return this.create(this._ENC_XFORM_MODE, c, b)
433 | },
434 | createDecryptor: function(c, b) {
435 | return this.create(this._DEC_XFORM_MODE, c, b)
436 | },
437 | init: function(l, d, c) {
438 | this.cfg = this.cfg.extend(c);
439 | this._xformMode = l;
440 | this._key = d;
441 | this.reset()
442 | },
443 | reset: function() {
444 | C.reset.call(this);
445 | this._doReset()
446 | },
447 | process: function(a) {
448 | this._append(a);
449 | return this._process()
450 | },
451 | finalize: function(a) {
452 | a && this._append(a);
453 | return this._doFinalize()
454 | },
455 | keySize: 4,
456 | ivSize: 4,
457 | _ENC_XFORM_MODE: 1,
458 | _DEC_XFORM_MODE: 2,
459 | _createHelper: function(a) {
460 | return {
461 | encrypt: function(c, l, n) {
462 | return ("string" == typeof l ? k : o).encrypt(a, c, l, n)
463 | },
464 | decrypt: function(c, l, n) {
465 | return ("string" == typeof l ? k : o).decrypt(a, c, l, n)
466 | }
467 | }
468 | }
469 | });
470 | j.StreamCipher = A.extend({
471 | _doFinalize: function() {
472 | return this._process(!0)
473 | },
474 | blockSize: 1
475 | });
476 | var m = g.mode = {},
477 | y = function(p, n, l) {
478 | var r = this._iv;
479 | r ? this._iv = B : r = this._prevBlock;
480 | for (var q = 0; q < l; q++) {
481 | p[n + q] ^= r[q]
482 | }
483 | },
484 | f = (j.BlockCipherMode = i.extend({
485 | createEncryptor: function(c, b) {
486 | return this.Encryptor.create(c, b)
487 | },
488 | createDecryptor: function(c, b) {
489 | return this.Decryptor.create(c, b)
490 | },
491 | init: function(c, b) {
492 | this._cipher = c;
493 | this._iv = b
494 | }
495 | })).extend();
496 | f.Encryptor = f.extend({
497 | processBlock: function(n, l) {
498 | var d = this._cipher,
499 | p = d.blockSize;
500 | y.call(this, n, l, p);
501 | d.encryptBlock(n, l);
502 | this._prevBlock = n.slice(l, l + p)
503 | }
504 | });
505 | f.Decryptor = f.extend({
506 | processBlock: function(p, n) {
507 | var l = this._cipher,
508 | r = l.blockSize,
509 | q = p.slice(n, n + r);
510 | l.decryptBlock(p, n);
511 | y.call(this, p, n, r);
512 | this._prevBlock = q
513 | }
514 | });
515 | m = m.CBC = f;
516 | f = (g.pad = {}).Pkcs7 = {
517 | pad: function(r, p) {
518 | for (var u = 4 * p, u = u - r.sigBytes % u, s = u << 24 | u << 16 | u << 8 | u, q = [], t = 0; t < u; t += 4) {
519 | q.push(s)
520 | }
521 | u = D.create(q, u);
522 | r.concat(u)
523 | },
524 | unpad: function(b) {
525 | b.sigBytes -= b.words[b.sigBytes - 1 >>> 2] & 255
526 | }
527 | };
528 | j.BlockCipher = A.extend({
529 | cfg: A.cfg.extend({
530 | mode: m,
531 | padding: f
532 | }),
533 | reset: function() {
534 | A.reset.call(this);
535 | var l = this.cfg,
536 | d = l.iv,
537 | l = l.mode;
538 | if (this._xformMode == this._ENC_XFORM_MODE) {
539 | var n = l.createEncryptor
540 | } else {
541 | n = l.createDecryptor, this._minBufferSize = 1
542 | }
543 | this._mode = n.call(l, this, d && d.words)
544 | },
545 | _doProcessBlock: function(d, c) {
546 | this._mode.processBlock(d, c)
547 | },
548 | _doFinalize: function() {
549 | var d = this.cfg.padding;
550 | if (this._xformMode == this._ENC_XFORM_MODE) {
551 | d.pad(this._data, this.blockSize);
552 | var c = this._process(!0)
553 | } else {
554 | c = this._process(!0), d.unpad(c)
555 | }
556 | return c
557 | },
558 | blockSize: 4
559 | });
560 | var h = j.CipherParams = i.extend({
561 | init: function(b) {
562 | this.mixIn(b)
563 | },
564 | toString: function(b) {
565 | return (b || this.formatter).stringify(this)
566 | }
567 | }),
568 | m = (g.format = {}).OpenSSL = {
569 | stringify: function(d) {
570 | var c = d.ciphertext;
571 | d = d.salt;
572 | return (d ? D.create([1398893684, 1701076831]).concat(d).concat(c) : c).toString(e)
573 | },
574 | parse: function(l) {
575 | l = e.parse(l);
576 | var d = l.words;
577 | if (1398893684 == d[0] && 1701076831 == d[1]) {
578 | var n = D.create(d.slice(2, 4));
579 | d.splice(0, 4);
580 | l.sigBytes -= 16
581 | }
582 | return h.create({
583 | ciphertext: l,
584 | salt: n
585 | })
586 | }
587 | },
588 | o = j.SerializableCipher = i.extend({
589 | cfg: i.extend({
590 | format: m
591 | }),
592 | encrypt: function(q, n, s, r) {
593 | r = this.cfg.extend(r);
594 | var p = q.createEncryptor(s, r);
595 | n = p.finalize(n);
596 | p = p.cfg;
597 | return h.create({
598 | ciphertext: n,
599 | key: s,
600 | iv: p.iv,
601 | algorithm: q,
602 | mode: p.mode,
603 | padding: p.padding,
604 | blockSize: q.blockSize,
605 | formatter: r.format
606 | })
607 | },
608 | decrypt: function(n, l, q, p) {
609 | p = this.cfg.extend(p);
610 | l = this._parse(l, p.format);
611 | return n.createDecryptor(q, p).finalize(l.ciphertext)
612 | },
613 | _parse: function(d, c) {
614 | return "string" == typeof d ? c.parse(d, this) : d
615 | }
616 | }),
617 | g = (g.kdf = {}).OpenSSL = {
618 | execute: function(n, l, q, p) {
619 | p || (p = D.random(8));
620 | n = z.create({
621 | keySize: l + q
622 | }).compute(n, p);
623 | q = D.create(n.words.slice(l), 4 * q);
624 | n.sigBytes = 4 * l;
625 | return h.create({
626 | key: n,
627 | iv: q,
628 | salt: p
629 | })
630 | }
631 | },
632 | k = j.PasswordBasedCipher = o.extend({
633 | cfg: o.cfg.extend({
634 | kdf: g
635 | }),
636 | encrypt: function(a, q, p, n) {
637 | n = this.cfg.extend(n);
638 | p = n.kdf.execute(p, a.keySize, a.ivSize);
639 | n.iv = p.iv;
640 | a = o.encrypt.call(this, a, q, p.key, n);
641 | a.mixIn(p);
642 | return a
643 | },
644 | decrypt: function(a, q, p, n) {
645 | n = this.cfg.extend(n);
646 | q = this._parse(q, n.format);
647 | p = n.kdf.execute(p, a.keySize, a.ivSize, q.salt);
648 | n.iv = p.iv;
649 | return o.decrypt.call(this, a, q, p.key, n)
650 | }
651 | })
652 | }();
653 | (function() {
654 | for (var C = CryptoJS, K = C.lib.BlockCipher, Q = C.algo, M = [], E = [], D = [], I = [], m = [], A = [], S = [], h = [], J = [], L = [], T = [], R = 0; 256 > R; R++) {
655 | T[R] = 128 > R ? R << 1 : R << 1 ^ 283
656 | }
657 | for (var P = 0, O = 0, R = 0; 256 > R; R++) {
658 | var N = O ^ O << 1 ^ O << 2 ^ O << 3 ^ O << 4,
659 | N = N >>> 8 ^ N & 255 ^ 99;
660 | M[P] = N;
661 | E[N] = P;
662 | var f = T[P],
663 | B = T[f],
664 | o = T[B],
665 | g = 257 * T[N] ^ 16843008 * N;
666 | D[P] = g << 24 | g >>> 8;
667 | I[P] = g << 16 | g >>> 16;
668 | m[P] = g << 8 | g >>> 24;
669 | A[P] = g;
670 | g = 16843009 * o ^ 65537 * B ^ 257 * f ^ 16843008 * P;
671 | S[N] = g << 24 | g >>> 8;
672 | h[N] = g << 16 | g >>> 16;
673 | J[N] = g << 8 | g >>> 24;
674 | L[N] = g;
675 | P ? (P = f ^ T[T[T[o ^ f]]], O ^= T[T[O]]) : P = O = 1
676 | }
677 | var i = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54],
678 | Q = Q.AES = K.extend({
679 | _doReset: function() {
680 | for (var b = this._key, r = b.words, q = b.sigBytes / 4, b = 4 * ((this._nRounds = q + 6) + 1), p = this._keySchedule = [], n = 0; n < b; n++) {
681 | if (n < q) {
682 | p[n] = r[n]
683 | } else {
684 | var l = p[n - 1];
685 | n % q ? 6 < q && 4 == n % q && (l = M[l >>> 24] << 24 | M[l >>> 16 & 255] << 16 | M[l >>> 8 & 255] << 8 | M[l & 255]) : (l = l << 8 | l >>> 24, l = M[l >>> 24] << 24 | M[l >>> 16 & 255] << 16 | M[l >>> 8 & 255] << 8 | M[l & 255], l ^= i[n / q | 0] << 24);
686 | p[n] = p[n - q] ^ l
687 | }
688 | }
689 | r = this._invKeySchedule = [];
690 | for (q = 0; q < b; q++) {
691 | n = b - q, l = q % 4 ? p[n] : p[n - 4], r[q] = 4 > q || 4 >= n ? l : S[M[l >>> 24]] ^ h[M[l >>> 16 & 255]] ^ J[M[l >>> 8 & 255]] ^ L[M[l & 255]]
692 | }
693 | },
694 | encryptBlock: function(d, c) {
695 | this._doCryptBlock(d, c, this._keySchedule, D, I, m, A, M)
696 | },
697 | decryptBlock: function(b, j) {
698 | var e = b[j + 1];
699 | b[j + 1] = b[j + 3];
700 | b[j + 3] = e;
701 | this._doCryptBlock(b, j, this._invKeySchedule, S, h, J, L, E);
702 | e = b[j + 1];
703 | b[j + 1] = b[j + 3];
704 | b[j + 3] = e
705 | },
706 | _doCryptBlock: function(ac, ab, aa, Z, Y, U, G, X) {
707 | for (var F = this._nRounds, W = ac[ab] ^ aa[0], V = ac[ab + 1] ^ aa[1], H = ac[ab + 2] ^ aa[2], z = ac[ab + 3] ^ aa[3], y = 4, w = 1; w < F; w++) {
708 | var x = Z[W >>> 24] ^ Y[V >>> 16 & 255] ^ U[H >>> 8 & 255] ^ G[z & 255] ^ aa[y++],
709 | v = Z[V >>> 24] ^ Y[H >>> 16 & 255] ^ U[z >>> 8 & 255] ^ G[W & 255] ^ aa[y++],
710 | u = Z[H >>> 24] ^ Y[z >>> 16 & 255] ^ U[W >>> 8 & 255] ^ G[V & 255] ^ aa[y++],
711 | z = Z[z >>> 24] ^ Y[W >>> 16 & 255] ^ U[V >>> 8 & 255] ^ G[H & 255] ^ aa[y++],
712 | W = x,
713 | V = v,
714 | H = u
715 | }
716 | x = (X[W >>> 24] << 24 | X[V >>> 16 & 255] << 16 | X[H >>> 8 & 255] << 8 | X[z & 255]) ^ aa[y++];
717 | v = (X[V >>> 24] << 24 | X[H >>> 16 & 255] << 16 | X[z >>> 8 & 255] << 8 | X[W & 255]) ^ aa[y++];
718 | u = (X[H >>> 24] << 24 | X[z >>> 16 & 255] << 16 | X[W >>> 8 & 255] << 8 | X[V & 255]) ^ aa[y++];
719 | z = (X[z >>> 24] << 24 | X[W >>> 16 & 255] << 16 | X[V >>> 8 & 255] << 8 | X[H & 255]) ^ aa[y++];
720 | ac[ab] = x;
721 | ac[ab + 1] = v;
722 | ac[ab + 2] = u;
723 | ac[ab + 3] = z
724 | },
725 | keySize: 8
726 | });
727 | C.AES = K._createHelper(Q)
728 | })();
729 | (function() {
730 | function a() {}
731 | var d = "";
732 | var c = "";
733 | a.setKv = function(h, i) {
734 | c = h;
735 | d = i
736 | };
737 |
738 | function g(i) {
739 | if (typeof i == "undefined" || i == null || i == "") {
740 | return "sogou"
741 | }
742 | var h = i.split("-");
743 | if (h.length > 2) {
744 | return f(h[2])
745 | } else {
746 | return f(h[0])
747 | }
748 | }
749 |
750 | function f(h) {
751 | if (typeof h == "undefined" || h == null || h == "") {
752 | return "sogou"
753 | }
754 | if (h.length > 5) {
755 | h = h.substr(h.length - 5, 5)
756 | }
757 | while (h.length < 5) {
758 | h += "s"
759 | }
760 | return h
761 | }
762 |
763 | function e(k, o) {
764 | if (k == "" || o == "" || typeof o == "undefined") {
765 | return ""
766 | }
767 | if (o.length != 5) {
768 | o = "sogou"
769 | }
770 | var n = k.length;
771 | var l = 0;
772 | var h = "";
773 | for (var m = 0; m < n; m++) {
774 | h += k.charAt(m);
775 | if (m == Math.pow(2, l)) {
776 | h += o.charAt(l++)
777 | }
778 | }
779 | return h
780 | }
781 |
782 | function b(j) {
783 | var i = document.cookie;
784 | var m = j + "=";
785 | var l = i.indexOf("; " + m);
786 | if (l == -1) {
787 | l = i.indexOf(m);
788 | if (l != 0) {
789 | return ""
790 | }
791 | } else {
792 | l += 2
793 | }
794 | var h = document.cookie.indexOf(";", l);
795 | if (h == -1) {
796 | h = i.length
797 | }
798 | var k = unescape(i.substring(l + m.length, h));
799 | return k == null ? "" : k
800 | }
801 | a.encryptquery = function(q, o) {
802 | try {
803 | if (typeof q == "undefined" && typeof o == "undefined") {
804 | return ""
805 | }
806 | if (q == null) {
807 | q = ""
808 | }
809 | if (o == null) {
810 | o = ""
811 | }
812 | if (c == null || c == "" || c.length != 11 || d == null || d == "") {
813 | return "openid=" + encodeURIComponent(q) + "&hdq=" + o
814 | }
815 | var i = c;
816 | var n = g(o);
817 | i += n;
818 | if (i == null || i == "" || i.length != 16) {
819 | return "openid=" + encodeURIComponent(q) + "&hdq=" + o
820 | }
821 | i = CryptoJS.enc.Utf8.parse(i);
822 | var h = CryptoJS.enc.Utf8.parse("0000000000000000");
823 | var p = q + "hdq=" + o;
824 | var k = CryptoJS.AES.encrypt(p, i, {
825 | iv: h
826 | });
827 | var s = e(k.toString(), n);
828 | if (s == null || s == "") {
829 | return "openid=" + encodeURIComponent(q) + "&hdq=" + o
830 | }
831 | var j = encodeURIComponent(s.toString());
832 | return "eqs=" + j + "&ekv=" + d
833 | } catch (r) {
834 | return "openid=" + encodeURIComponent(q) + "&hdq=" + o
835 | }
836 | };
837 | SogouEncrypt = a
838 | })();
839 |
840 | module.exports = SogouEncrypt;
--------------------------------------------------------------------------------
/lib/sogouEncryptSalt.js:
--------------------------------------------------------------------------------
1 | var request = require('./request');
2 | var redis = require('./redis');
3 | var util = require('util');
4 | var config = require('../config/weixin');
5 | var phantom = require('phantom');
6 |
7 | function Salt() {
8 | this.apiUrl = config.api.getEncryptSalt;
9 | this.cacheId = config.saltCachePolicy.cacheId;
10 | this.lifeTime = config.saltCachePolicy.lifeTime;
11 | }
12 |
13 | Salt.prototype.get = function*() {
14 | return yield redis.get(this.cacheId);
15 | }
16 |
17 | Salt.prototype.save = function*(salt) {
18 | yield redis.setex(this.cacheId, this.lifeTime, salt);
19 | }
20 |
21 | Salt.prototype.collect = function*(headers) {
22 | var result =
23 | yield request(this.apiUrl, {
24 | headers: headers
25 | });
26 | if (!result.error && result.statusCode == 200) {
27 | console.log(this.apiUrl);
28 | var match = result.body.match(/SogouEncrypt\.setKv\(\"(.*?)\"\)/);
29 | if (match && match[1]) {
30 | sale = match[1].replace('","', ',');
31 | return sale;
32 | }
33 | }
34 | return '';
35 | }
36 |
37 |
38 | Salt.prototype.destroy = function*() {
39 | return yield redis.del(this.cacheId);
40 | }
41 |
42 |
43 |
44 | module.exports = new Salt();
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rss",
3 | "version": "0.1.1",
4 | "description": "rss-builder",
5 | "main": "app.js",
6 | "dependencies": {
7 | "co": "^4.5.2",
8 | "co-body": "^1.1.0",
9 | "co-views": "^0.3.0",
10 | "co-redis": "^1.1.1",
11 | "koa": "^0.18.1",
12 | "koa-logger": "^1.2.2",
13 | "koa-route": "^2.4.1",
14 | "redis": "^0.12.1",
15 | "request": "^2.55.0",
16 | "swig": "^1.4.2",
17 | "xml2json": "^0.6.1"
18 | },
19 | "devDependencies": {},
20 | "scripts": {
21 | "test": "echo \"Error: no test specified\" && exit 1"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "https://github.com/wlwr/rss.git"
26 | },
27 | "keywords": [
28 | "weixin",
29 | "rss"
30 | ],
31 | "author": "wlwr",
32 | "license": "ISC",
33 | "bugs": {
34 | "url": "https://github.com/wlwr/rss/issues"
35 | },
36 | "homepage": "https://github.com/wlwr/rss"
37 | }
38 |
--------------------------------------------------------------------------------
/task/cookiePool.js:
--------------------------------------------------------------------------------
1 | var cookiePool = require('../lib/cookiePool');
2 | var co = require('co');
3 | co(function*() {
4 | var cookies = yield cookiePool.collectAll();
5 | if (cookies.toString().indexOf('SNUID=') != -1) {
6 | yield cookiePool.destroy();
7 | yield cookiePool.save(cookies);
8 | console.log(cookies.join(' '));
9 | console.log('update cookies success');
10 | } else {
11 | console.log('update cookies error');
12 | }
13 | });
--------------------------------------------------------------------------------
/views/home.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RSS生成器 - 提供微信公众号RSS订阅接口
6 |
7 |
19 |
20 |
21 |
22 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
45 |
49 |
50 |
搜索历史记录
51 |
52 |
oIWsFt0GzJ9B8He5MfCo4DpkeYBg
53 |
oIWsFt0GzJ9B8He5MfCo4DpkeYBg
54 |
55 |
56 |
57 |
58 |
59 |
获取微信公众号openid方法:
60 |
74 |
75 |
76 |
77 |
85 |
86 |
87 |
88 |
89 |
147 |
148 |
--------------------------------------------------------------------------------
/views/weixin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{ rss.name }}
5 | {{ rss.url }}
6 | {{ rss.description }}
7 | zh-cn
8 | {{ rss.formatDate }}
9 | {{ rss.formatDate }}
10 | {% for item in rss.items %}
11 | -
12 | {% autoescape false %}
13 |
14 | {% endautoescape %}
15 | {{ item.url }}
16 | {{ rss.author }}
17 | {% autoescape false %}
18 |
19 | {% endautoescape %}
20 |
21 | {{ item.formatDate }}
22 | {{ 'weixin_' + item.docid }}
23 |
24 | {% endfor %}
25 |
26 |
--------------------------------------------------------------------------------