├── logs
└── .gitkeep
├── .gitignore
├── models
├── index.js
├── sequelize.js
└── weibo.js
├── Dockerfile
├── index.js
├── routes
├── config.js
├── all.js
├── get.js
└── negative.js
├── tools
├── logger.js
└── redis.js
├── package.json
└── README.md
/logs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | node_modules
3 | npm-debug.log
4 | Weibo2RSS.log*
5 |
--------------------------------------------------------------------------------
/models/index.js:
--------------------------------------------------------------------------------
1 | var Sequelize = require('sequelize');
2 | var sequelize = require('./sequelize').sequelize();
3 | var Weibo = sequelize.import('./weibo');
4 |
5 | sequelize.sync();
6 |
7 | exports.Weibo = Weibo;
8 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:4.4-onbuild
2 | EXPOSE 1207
3 | RUN npm install -g forever
4 | RUN echo "Asia/Shanghai" > /etc/timezone
5 | RUN dpkg-reconfigure -f noninteractive tzdata
6 | ENTRYPOINT forever --spinSleepTime 1000 --minUptime 1000 index.js
--------------------------------------------------------------------------------
/models/sequelize.js:
--------------------------------------------------------------------------------
1 | var Sequelize = require('sequelize');
2 | var SQL_USER = require('../routes/config').SQL_USER;
3 | var SQL_PASSWORD = require('../routes/config').SQL_PASSWORD;
4 |
5 | exports.sequelize = function () {
6 | return new Sequelize('weibo', SQL_USER, SQL_PASSWORD, {'dialect': 'mysql',host: 'localhost', port:3306});
7 | }
8 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var logger = require('./tools/logger');
3 |
4 | logger.info(`🍻 Weibo2RSS start! Cheers!`);
5 |
6 | var app = express();
7 | app.all('*', require('./routes/all'));
8 | app.get('/rss/:uid', require('./routes/get'));
9 | app.get('/negative/:uid', require('./routes/negative'));
10 | app.listen(1206);
--------------------------------------------------------------------------------
/routes/config.js:
--------------------------------------------------------------------------------
1 | const SQL_USER = 'root';
2 | const SQL_PASSWORD = '';
3 | const EMAIL_USER = 'gx-deng@163.com';
4 | const EMAIL_PASSWORD = '';
5 | const EMAIL_HOST = 'smtp.163.com';
6 |
7 | exports.SQL_USER = SQL_USER;
8 | exports.SQL_PASSWORD = SQL_PASSWORD;
9 | exports.EMAIL_USER = EMAIL_USER;
10 | exports.EMAIL_PASSWORD = EMAIL_PASSWORD;
11 | exports.EMAIL_HOST = EMAIL_HOST;
12 |
--------------------------------------------------------------------------------
/tools/logger.js:
--------------------------------------------------------------------------------
1 | var log4js = require('log4js');
2 | log4js.configure({
3 | appenders: [
4 | {
5 | type: "file",
6 | filename: 'logs/Weibo2RSS.log',
7 | maxLogSize: 20480,
8 | backups: 3,
9 | category: [ 'Weibo2RSS','console' ]
10 | },
11 | {
12 | type: "console"
13 | }
14 | ],
15 | replaceConsole: true
16 | });
17 | var logger = log4js.getLogger('Weibo2RSS');
18 | logger.setLevel('INFO');
19 |
20 | module.exports = logger;
--------------------------------------------------------------------------------
/routes/all.js:
--------------------------------------------------------------------------------
1 | var https = require('https');
2 |
3 | module.exports = function (req, res, next) {
4 | https.get(`https://api.prprpr.me/count/?id=DIYgod-Weibo2RSS&action=add`);
5 | res.header('Access-Control-Allow-Origin', '*');
6 | res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
7 | res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
8 |
9 | if (req.method == 'OPTIONS') {
10 | res.send(200);
11 | }
12 | else {
13 | next();
14 | }
15 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Weibo2RSS",
3 | "version": "0.0.1",
4 | "description": "使用RSS订阅喜欢的微博博主,更新微博邮件提醒,并使用咕咕机打印。",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/airingursb/Weibo2RSS.git"
9 | },
10 | "keywords": [
11 | "Weibo",
12 | "RSS"
13 | ],
14 | "author": "DIYGod",
15 | "license": "SATA",
16 | "devDependencies": {},
17 | "dependencies": {
18 | "cheerio": "^0.22.0",
19 | "emailjs": "^1.0.10",
20 | "express": "^4.14.0",
21 | "log4js": "^0.6.38",
22 | "mysql": "^2.13.0",
23 | "mysql2": "^1.3.6",
24 | "node-fetch": "^1.6.3",
25 | "redis": "^2.6.3",
26 | "sequelize": "^4.3.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/tools/redis.js:
--------------------------------------------------------------------------------
1 | var logger = require('./logger');
2 | var redis = require("redis");
3 | var client;
4 | if (process.env.REDIS_PORT_6379_TCP_ADDR && process.env.REDIS_PORT_6379_TCP_PORT && process.env.REDIS_PASSWORD) {
5 | client = redis.createClient({
6 | host: process.env.REDIS_PORT_6379_TCP_ADDR,
7 | port: process.env.REDIS_PORT_6379_TCP_PORT,
8 | password: process.env.REDIS_PASSWORD
9 | });
10 | }
11 | else {
12 | client = redis.createClient();
13 | }
14 |
15 |
16 | client.on("error", function (err) {
17 | logger.error('Redis Error ' + err);
18 | });
19 |
20 | module.exports = {
21 | set: function (key, value) {
22 | client.set(key, value, redis.print);
23 | client.expire(key, 300);
24 | logger.info('Set redis: ' + key);
25 | },
26 | client: client
27 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Weibo2RSS
2 |
3 | > 使用RSS订阅喜欢的微博博主,更新微博邮件提醒,并使用咕咕机打印。
4 |
5 | ## Install
6 |
7 | 硬件环境:RaspberryPi B+,MEMOBIRD 2
8 | 软件环境:Nodejs,MySQL
9 |
10 | 请在 `routes/config.js` 下填写您的数据库配置与邮箱配置。
11 |
12 | ## 介绍
13 |
14 | Demo:https://api.prprpr.me/weibo/rss/3306934123
15 |
16 | RSS 格式输出一个微博博主最新的 15 条微博,可以使用 RSS 阅读器来获取及时推送,配合 [IFTTT](https://ifttt.com/) 还可以实现更多好玩的功能。
17 |
18 | Demo:https://api.prprpr.me/weibo/negative/3306934123
19 |
20 | 使用 [Text2Emotion](https://github.com/DIYgod/Text2Emotion) 实现,仅输出消极情绪的微博,用来监控博主的消极情绪。
21 |
22 | ## 使用
23 |
24 | 使用 RSS 阅读器订阅:https://api.prprpr.me/weibo/rss/{微博博主的uid}
25 |
26 | 获取uid:进入博主的微博主页,控制台执行
27 | ```js
28 | /uid=(\d+)/. exec(document.querySelector('.opt_box .btn_bed').getAttribute('action-data'))[1]
29 | ```
30 |
31 | ## 搭建
32 |
33 | 需要环境:Node.js
34 |
35 | 推荐使用 Docker
36 |
37 | ## LICENSE
38 |
39 | MIT © [DIYgod](http://github.com/DIYgod)
40 |
41 | MIT © [Airing](http://github.com/airingursb)
--------------------------------------------------------------------------------
/models/weibo.js:
--------------------------------------------------------------------------------
1 | module.exports = function (sequelize, DataTypes) {
2 | return sequelize.define(
3 | 'weibo',
4 | {
5 | 'uid': {
6 | 'type': DataTypes.INTEGER,
7 | 'allowNull': false
8 | },
9 | 'home': {
10 | 'type': DataTypes.STRING(125),
11 | 'allowNull': false
12 | },
13 | 'name': {
14 | 'type': DataTypes.STRING(125),
15 | 'allowNull': true
16 | },
17 | 'emotion': {
18 | 'type': DataTypes.DOUBLE,
19 | 'allowNull': true
20 | },
21 | 'title': {
22 | 'type': DataTypes.TEXT,
23 | 'allowNull': true
24 | },
25 | 'description': {
26 | 'type': DataTypes.TEXT,
27 | 'allowNull': true
28 | },
29 | 'pubDate': {
30 | 'type': DataTypes.STRING(125),
31 | 'allowNull': true
32 | },
33 | 'link': {
34 | 'type': DataTypes.STRING(125),
35 | 'allowNull': true
36 | }
37 | }
38 | );
39 | }
--------------------------------------------------------------------------------
/routes/get.js:
--------------------------------------------------------------------------------
1 | var fetch = require('node-fetch');
2 | var cheerio = require('cheerio');
3 | var url = require('url');
4 | var logger = require('../tools/logger');
5 | // var redis = require('../tools/redis');
6 |
7 | function getTime (html) {
8 | var math;
9 | var date = new Date();
10 | if (math = /(\d+)分钟前/.exec(html)) {
11 | date.setMinutes(date.getMinutes() - math[1]);
12 | return date.toUTCString();
13 | }
14 | else if (math = /今天 (\d+):(\d+)/.exec(html)) {
15 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), math[1], math[2]);
16 | return date.toUTCString();
17 | }
18 | else if (math = /(\d+)月(\d+)日 (\d+):(\d+)/.exec(html)) {
19 | date = new Date(date.getFullYear(), math[1] - 1, parseInt(math[2]), math[3], math[4]);
20 | return date.toUTCString();
21 | }
22 | return html;
23 | }
24 |
25 | module.exports = function (req, res) {
26 | res.header('Content-Type', 'application/xml; charset=utf-8');
27 |
28 | var ip = req.headers['x-forwarded-for'] ||
29 | req.connection.remoteAddress ||
30 | req.socket.remoteAddress ||
31 | req.connection.socket.remoteAddress;
32 |
33 | var query = url.parse(req.url,true).query;
34 | var debug = query.debug;
35 |
36 | var uid = req.params.uid;
37 |
38 | logger.info(`Weibo2RSS uid ${uid} form origin, IP: ${ip}`);
39 |
40 | fetch(`http://service.weibo.com/widget/widget_blog.php?uid=${uid}`).then(
41 | response => response.text()
42 | ).then((data) => {
43 | var $ = cheerio.load(data, {
44 | decodeEntities: false
45 | });
46 | var wbs = [];
47 | var items = $('.wgtCell');
48 | var wb, item, titleEle;
49 | items.map(function (index, ele) {
50 | wb = {};
51 | item = $(this);
52 | titleEle = item.find('.wgtCell_txt');
53 | wb.title = titleEle.text().replace(/^\s+|\s+$/g, '');
54 | if (wb.title.length > 24) {
55 | wb.title = wb.title.slice(0, 24) + '...';
56 | }
57 | wb.description = titleEle.html().replace(/^\s+|\s+$/g, '').replace(/thumbnail/, 'large');
58 | wb.pubDate = getTime(item.find('.link_d').html());
59 | wb.link = item.find('.wgtCell_tm a').attr('href');
60 | wbs.push(wb);
61 | });
62 | var name = $('.userNm').text();
63 |
64 | var rss =
65 | `
66 |
67 |
68 | ${name}的微博
69 | http://weibo.com/${uid}/
70 | ${name}的微博RSS,使用 Weibo2RSS(https://github.com/DIYgod/Weibo2RSS) 构建
71 | zh-cn
72 | ${new Date().toUTCString()}
73 | 300`
74 | for (var i = 0; i < wbs.length; i++) {
75 | rss +=`
76 | -
77 |
78 |
79 | ${wbs[i].pubDate}
80 | ${wbs[i].link}
81 | ${wbs[i].link}
82 |
`
83 | }
84 | rss += `
85 |
86 | `
87 | res.send(rss);
88 | }
89 | ).catch(
90 | e => logger.error("Weibo2RSS Error: getting widget", e)
91 | );
92 | };
--------------------------------------------------------------------------------
/routes/negative.js:
--------------------------------------------------------------------------------
1 | var fetch = require('node-fetch');
2 | var cheerio = require('cheerio');
3 | var url = require('url');
4 | var logger = require('../tools/logger');
5 | var WeiboModel = require('../models/index').Weibo;
6 | var email = require('emailjs/email');
7 | var EMAIL_USER = require('./config').EMAIL_USER;
8 | var EMAIL_PASSWORD = require('./config').EMAIL_PASSWORD;
9 | var EMAIL_HOST = require('./config').EMAIL_HOST;
10 |
11 | var server = email.server.connect({
12 | user: EMAIL_USER,
13 | password: EMAIL_PASSWORD,
14 | host: EMAIL_HOST,
15 | ssl: true
16 | });
17 |
18 | function getTime (html) {
19 | var math;
20 | var date = new Date();
21 | if (math = /(\d+)分钟前/.exec(html)) {
22 | date.setMinutes(date.getMinutes() - math[1]);
23 | return date.toUTCString();
24 | }
25 | else if (math = /今天 (\d+):(\d+)/.exec(html)) {
26 | date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), math[1], math[2]);
27 | return date.toUTCString();
28 | }
29 | else if (math = /(\d+)月(\d+)日 (\d+):(\d+)/.exec(html)) {
30 | date = new Date(date.getFullYear(), math[1] - 1, parseInt(math[2]), math[3], math[4]);
31 | return date.toUTCString();
32 | }
33 | return html;
34 | }
35 |
36 | module.exports = function (req, res) {
37 | res.header('Content-Type', 'application/xml; charset=utf-8');
38 |
39 | var ip = req.headers['x-forwarded-for'] ||
40 | req.connection.remoteAddress ||
41 | req.socket.remoteAddress ||
42 | req.connection.socket.remoteAddress;
43 |
44 | var query = url.parse(req.url,true).query;
45 | var debug = query.debug;
46 |
47 | var uid = req.params.uid;
48 |
49 | logger.info(`Weibo2RSS negative uid ${uid} form origin, IP: ${ip}`);
50 |
51 | fetch(`http://service.weibo.com/widget/widget_blog.php?uid=${uid}`).then(
52 | response => response.text()
53 | ).then((data) => {
54 | var $ = cheerio.load(data, {
55 | decodeEntities: false
56 | });
57 | var wbs = [];
58 | var items = $('.wgtCell');
59 | var name = $('.userNm').text();
60 | var wb, item, titleEle;
61 | var emotionCound = 0;
62 |
63 | items.map(function (index, ele) {
64 | wb = {};
65 | item = $(this);
66 | titleEle = item.find('.wgtCell_txt');
67 | wb.title = titleEle.text().replace(/^\s+|\s+$/g, '');
68 | if (wb.title.length > 24) {
69 | wb.title = wb.title.slice(0, 24) + '...';
70 | }
71 | wb.description = titleEle.html().replace(/^\s+|\s+$/g, '').replace(/thumbnail/, 'large');
72 | wb.pubDate = getTime(item.find('.link_d').html());
73 | wb.link = item.find('.wgtCell_tm a').attr('href');
74 | wbs.push(wb);
75 | });
76 | for (let i = 0; i < wbs.length; i++) {
77 | fetch(`https://api.prprpr.me/emotion/wenzhi?password=DIYgod&text=${encodeURIComponent(wbs[i].description)}`).then(
78 | response => response.json()
79 | ).then((data) => {
80 | emotionCound++;
81 | wbs[i].emotion = data.positive - data.negative;
82 |
83 | if (emotionCound === wbs.length) {
84 | wbs.forEach(function(wb) {
85 | WeiboModel.find({
86 | where: {
87 | link: wb.link
88 | }
89 | }).then(function(result) {
90 | console.log(result)
91 | if (result == null) {
92 | wb.uid = uid;
93 | wb.home = 'http://weibo.com/' + uid;
94 | wb.name = name;
95 | WeiboModel.create(wb).then(function() {
96 | var subject = wb.name + '发布了新微博,情绪值为:' + (wb.emotion * 100).toFixed(4) + '%,快去看看吧!'
97 | server.send({
98 | text: wb.description,
99 | from: "gx-deng ",
100 | to: "airing <361411192@qq.com>",
101 | subject: subject
102 | }, function(err, message) { console.log(err || message); });
103 | });
104 | }
105 | });
106 | });
107 | var rss =
108 | `
109 |
110 |
111 | ${name}的情绪微博
112 | http://weibo.com/${uid}/
113 | ${name}的消极情绪微博RSS,使用 Weibo2RSS(https://github.com/DIYgod/Weibo2RSS) 构建
114 | zh-cn
115 | ${new Date().toUTCString()}
116 | 300`
117 | for (let j = 0; j < wbs.length; j++) {
118 | if (wbs[j].emotion) {
119 | rss +=`
120 | -
121 |
122 |
123 | ${wbs[j].pubDate}
124 | ${wbs[j].link}
125 | ${wbs[j].link}
126 |
`
127 |
128 | }
129 | }
130 | rss += `
131 |
132 | `
133 | res.send(rss);
134 | }
135 | }).catch(
136 | e => logger.error("Weibo2RSS negative Error: getting emotion", e)
137 | );
138 | }
139 | }
140 | ).catch(
141 | e => logger.error("Weibo2RSS negative Error: getting widget", e)
142 | );
143 | };
--------------------------------------------------------------------------------