Skip to content

Commit 5a2e252

Browse files
Shun Zicode-factor
andauthored
feat: more readable 微博个人时间线, Twitter 用户时间线, 豆瓣用户广播 (#5886)
Co-authored-by: codefactor-io <[email protected]>
1 parent be27e9f commit 5a2e252

File tree

5 files changed

+195
-75
lines changed

5 files changed

+195
-75
lines changed

lib/routes/douban/people/status.js

Lines changed: 118 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,125 @@
11
const got = require('@/utils/got');
22

3-
function getContentByActivity(status) {
3+
function getContentByActivity(item, isRenderingRepost = false) {
4+
const { status, comments } = item;
45
let description = '';
56
let title = '';
6-
switch (status.activity) {
7-
case '说':
8-
title = `${status.author.name} ${status.activity}:${status.text}`;
9-
description = status.text;
10-
status.images.forEach((image) => {
11-
description += `<p><img src="${image.large.url}"/></p>`;
12-
});
13-
break;
14-
case '转发':
15-
description = `${status.text}<br>`;
16-
if (status.reshared_status.deleted) {
17-
title = `${status.author.name} ${status.activity} 广播:原动态已被发布者删除`;
18-
description += `原动态已被发布者删除`;
19-
} else {
20-
description += getContentByActivity(status.reshared_status).title;
21-
title = `${status.author.name} ${status.activity} ${status.reshared_status.author.name} 的广播:${status.reshared_status.text}`;
22-
status.reshared_status.images.forEach((image) => {
23-
description += `<p><img src="${image.large.url}"/></p>`;
24-
});
25-
}
26-
27-
break;
28-
default:
29-
description = `${status.text ? `${status.text}<br><br>` : ''}`;
30-
title = `${status.author.name}${status.activity}: ${description}`;
31-
if (status.card) {
32-
let image;
33-
if (status.card.image && (status.card.image.large || status.card.image.normal)) {
34-
image = (status.card.image.large || status.card.image.normal).url;
35-
}
36-
if (status.card.rating) {
37-
description += `豆瓣评分:${status.card.rating}<br>${status.card.subtitle}${image ? `<br><img src="${image}">` : ''}<br><a href="${status.card.url}">《${status.card.title}》</a>`;
38-
title = `${status.author.name}${status.activity}:《${status.card.title}》`;
39-
} else {
40-
description += `${status.card.subtitle}${image ? `<br><img src="${image}">` : ''}<br>${status.card.url ? `<a href="${status.card.url}">${status.card.title}</a>` : ''}`;
41-
title = `${status.author.name} ${status.activity}:「${status.card.title}${status.text}`;
42-
}
43-
}
44-
if (status.video_card) {
45-
const videoCover = status.video_card.video_info && status.video_card.video_info.cover_url;
46-
const videoSrc = status.video_card.video_info && status.video_card.video_info.video_url;
47-
description += `${videoSrc ? `<br><video src="${videoSrc}" ${videoCover ? `poster="${videoCover}"` : ''}></video>` : ''}<br><br>${
48-
status.video_card.title ? `<a href="${status.video_card.url}">${status.video_card.title}</a>` : ''
49-
}`;
50-
title = `${status.author.name}${status.activity}:《${status.video_card.title}》`;
51-
}
52-
break;
7+
8+
let usernameAndAvatar = `<a href="${status.author.url}">`;
9+
if (!isRenderingRepost) {
10+
usernameAndAvatar += `<img align="left" width="48" src="${status.author.avatar}" hspace="8" vspace="8" />`;
11+
}
12+
usernameAndAvatar += `<strong>${status.author.name}</strong></a>&nbsp;&nbsp;`;
13+
14+
title += `${status.author.name} ${status.activity}: `;
15+
description += usernameAndAvatar + `${status.activity}`;
16+
17+
description += `<br><small>${status.create_time}</small><br>`;
18+
19+
let text = status.text;
20+
let lastIndex = 0;
21+
const replacedTextSegements = [];
22+
for (const entity of status.entities) {
23+
replacedTextSegements.push(text.slice(lastIndex, entity.start));
24+
replacedTextSegements.push(`<a href="${entity.uri.replace('douban://douban.com', 'https://www.douban.com/doubanapp/dispatch?uri=')}" target="_blank" rel="noopener noreferrer">${entity.title}</a>`);
25+
lastIndex = entity.end;
26+
}
27+
replacedTextSegements.push(text.slice(lastIndex));
28+
text = replacedTextSegements.join('');
29+
30+
// text // images // video_info // parent status
31+
32+
description += text;
33+
34+
if (status.card) {
35+
if (status.card.rating) {
36+
title += `《${status.card.title}》`;
37+
} else {
38+
title += `「${status.card.title}」`;
39+
}
40+
}
41+
42+
title += status.text.replace('\n', '');
43+
44+
if (status.images.length) {
45+
description += `<br clear="both" /><div style="clear: both"></div>`;
46+
status.images.forEach((image) => {
47+
description += '<a href="' + image.large.url + '"><img vspace="8" hspace="4" src="' + image.large.url + '"></a>';
48+
});
49+
}
50+
51+
if (status.video_info) {
52+
description += `<br clear="both" /><div style="clear: both"></div>`;
53+
const videoCover = status.video_info.cover_url;
54+
const videoSrc = status.video_info.video_url;
55+
description += `${videoSrc ? `<video src="${videoSrc}" ${videoCover ? `poster="${videoCover}"` : ''}></video>` : ''}`;
56+
}
57+
58+
if (status.parent_status) {
59+
description += ' 🔁 ';
60+
title += ' 🔁 ';
61+
62+
let usernameAndAvatar = `<a href="${status.parent_status.author.url}">`;
63+
usernameAndAvatar += `<strong>${status.parent_status.author.name}</strong></a>:&nbsp;&nbsp;`;
64+
description += usernameAndAvatar + status.parent_status.text;
65+
title += usernameAndAvatar + status.parent_status.text;
66+
}
67+
68+
// card
69+
if (status.card) {
70+
let image;
71+
if (status.card.image && (status.card.image.large || status.card.image.normal)) {
72+
image = status.card.image.large || status.card.image.normal;
73+
}
74+
75+
description += `<br clear="both" /><div style="clear: both"></div><blockquote>`;
76+
if (image) {
77+
description += `<img height="${image.height}" src="${image.url}" vspace="8" hspace="4" align="left" />`;
78+
}
79+
description += `<a href="${status.card.url}" target="_blank" rel="noopener noreferrer"><strong>${status.card.title}</strong><br><small>${status.card.subtitle}</small>`;
80+
if (status.card.rating) {
81+
description += `<br><small>评分:${status.card.rating}</small>`;
82+
}
83+
description += `</a><br clear="both" /><div style="clear: both"></div></blockquote>`;
84+
}
85+
86+
// video_card
87+
if (status.video_card) {
88+
description += `<br clear="both" /><div style="clear: both"></div><blockquote>`;
89+
const videoCover = status.video_card.video_info && status.video_card.video_info.cover_url;
90+
const videoSrc = status.video_card.video_info && status.video_card.video_info.video_url;
91+
description += `${videoSrc ? `<video src="${videoSrc}" ${videoCover ? `poster="${videoCover}"` : ''}></video>` : ''}<br>${status.video_card.title ? `<a href="${status.video_card.url}">${status.video_card.title}</a>` : ''}`;
92+
description += `</blockquote>`;
5393
}
94+
95+
// reshared_status
96+
if (status.reshared_status) {
97+
description += `<br clear="both" /><div style="clear: both"></div><blockquote>`;
98+
99+
if (status.reshared_status.deleted) {
100+
description += `原动态已被发布者删除`;
101+
title += ` | 原动态已被发布者删除`;
102+
} else {
103+
description += getContentByActivity({ status: status.reshared_status, comments: [] }, true).description;
104+
title += ' | ' + status.reshared_status.text;
105+
}
106+
107+
const reshared_url = status.reshared_status.uri.replace('douban://douban.com', 'https://www.douban.com/doubanapp/dispatch?uri=');
108+
109+
description += `<br><small>原动态:<a href="${reshared_url}" target="_blank" rel="noopener noreferrer">${reshared_url}</a></small><br clear="both" /><div style="clear: both"></div></blockquote>`;
110+
}
111+
112+
// comments
113+
if (!isRenderingRepost) {
114+
if (comments.length > 0) {
115+
description += '<hr>';
116+
}
117+
for (const comment of comments) {
118+
description += `<br>${comment.text} - <a href="${comment.author.url}" target="_blank" rel="noopener noreferrer">${comment.author.name}</a>`;
119+
}
120+
}
121+
122+
description = description.trim().replace(/\n/g, '<br>');
54123
return { title, description };
55124
}
56125

@@ -78,7 +147,7 @@ module.exports = async (ctx) => {
78147
items
79148
.filter((item) => !item.deleted)
80149
.map((item) => {
81-
const r = getContentByActivity(item.status);
150+
const r = getContentByActivity(item);
82151
return {
83152
title: r.title,
84153
link: item.status.sharing_url,

lib/routes/twitter/user.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ module.exports = async (ctx) => {
3737
link: `https://twitter.com/${id}/`,
3838
image: profileImageUrl,
3939
description: userInfo.description,
40-
item: utils.ProcessFeed({
41-
data,
42-
}),
40+
item: utils.ProcessFeed(
41+
{
42+
data,
43+
},
44+
true
45+
),
4346
};
4447
};

lib/routes/twitter/utils.js

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ const URL = require('url');
22
const config = require('@/config').value;
33
const Twit = require('twit');
44

5-
const ProcessFeed = ({ data = [] }, showAuthor = false) => {
5+
const ProcessFeed = ({ data = [] }, showAuthor = false, showAuthorInTitle = false) => {
66
const getQueryParams = (url) => URL.parse(url, true).query;
77
const getOrigionImg = (url) => {
88
// https://greasyfork.org/zh-CN/scripts/2312-resize-image-on-open-image-in-new-tab/code#n150
@@ -28,7 +28,12 @@ const ProcessFeed = ({ data = [] }, showAuthor = false) => {
2828
};
2929

3030
const replaceBreak = (text) => text.replace(/<br><br>|<br>/g, ' ');
31-
const formatText = (text) => text.replace(/https:\/\/t\.co(.*)/g, '').replace(/\n/g, '<br>');
31+
const formatText = (text) =>
32+
text
33+
.replace(/https:\/\/t\.co(.*)/g, '')
34+
.trim()
35+
.replace(/\n/g, '<br>');
36+
const formatTextToPlain = (text) => text.replace(/https:\/\/t\.co(.*)/g, '').trim();
3237
const formatVideo = (media) => {
3338
let content = '';
3439
const video = media.video_info.variants.reduce((video, item) => {
@@ -40,7 +45,7 @@ const ProcessFeed = ({ data = [] }, showAuthor = false) => {
4045

4146
if (video.url) {
4247
const gifAutoPlayAttr = media.type === 'animated_gif' ? `autoplay loop muted webkit-playsinline playsinline` : '';
43-
content = `<br><video src="${video.url}" ${gifAutoPlayAttr} controls="controls" poster="${getOrigionImg(media.media_url_https)}" style="width: 100%"></video>`;
48+
content = `<video src="${video.url}" ${gifAutoPlayAttr} controls="controls" poster="${getOrigionImg(media.media_url_https)}" style="width: 100%"></video>`;
4449
}
4550

4651
return content;
@@ -52,6 +57,7 @@ const ProcessFeed = ({ data = [] }, showAuthor = false) => {
5257
item.extended_entities.media.forEach((item) => {
5358
// https://developer.twitter.com/en/docs/tweets/data-dictionary/overview/extended-entities-object
5459
let content = '';
60+
let originImg;
5561
switch (item.type) {
5662
case 'animated_gif':
5763
case 'video':
@@ -60,39 +66,51 @@ const ProcessFeed = ({ data = [] }, showAuthor = false) => {
6066

6167
case 'photo':
6268
default:
63-
content = `<br><img src="${getOrigionImg(item.media_url_https)}">`;
69+
originImg = getOrigionImg(item.media_url_https);
70+
content = `<a href="${originImg}"><img hspace="4" vspace="8" src="${originImg}"></a>`;
6471
break;
6572
}
6673

6774
img += content;
6875
});
6976

77+
if (img) {
78+
img = `<br clear="both" /><div style="clear: both"></div>` + img;
79+
}
7080
return img;
7181
};
7282
const formatUrl = (item) => {
7383
let url = '';
7484
item.entities.urls.forEach((u) => {
75-
url += `<a href="${u.expanded_url}" target="_blank" rel="noopener noreferrer">${u.expanded_url}</a>`;
85+
url += `<br><a href="${u.expanded_url}" target="_blank" rel="noopener noreferrer">${u.expanded_url}</a>`;
7686
});
7787

7888
return url;
7989
};
8090

8191
return data.map((item) => {
92+
const originalItem = item;
8293
item = item.retweeted_status || item;
8394
item.full_text = formatText(item.full_text);
8495
const img = formatMedia(item);
8596
let url = '';
8697
let quote = '';
98+
let quoteOnTitle = '';
8799

88100
if (item.is_quote_status) {
89101
const quoteData = item.quoted_status;
90102

91103
if (quoteData) {
92104
const author = quoteData.user;
93-
quote += `<br><br>${author.name}: ${formatText(quoteData.full_text)}<br>`;
105+
quote += `<br clear="both" /><div style="clear: both"></div><blockquote><a href="https://twitter.com/${author.screen_name}"><img align="left" src="${author.profile_image_url_https}" hspace="8" vspace="8"><strong>${author.name}</strong></a>:&nbsp;&nbsp;`;
106+
quote += `${formatText(quoteData.full_text)}`;
94107
quote += formatMedia(quoteData);
95108
quote += formatUrl(quoteData);
109+
quoteOnTitle += `| ${author.name}: ${formatTextToPlain(quoteData.full_text)}`;
110+
111+
quote += `<br><small>Source: <a href="https://twitter.com/${author.screen_name}/status/${quoteData.id_str}" target="_blank" rel="noopener noreferrer">https://twitter.com/${author.screen_name}/status/${quoteData.id_str}</a></small>`;
112+
quote += '<br><small>' + new Date(quoteData.created_at).toLocaleString();
113+
quote += `</small><br clear="both" /><div style="clear: both"></div></blockquote>`;
96114
} else {
97115
url = formatUrl(item);
98116
}
@@ -101,11 +119,13 @@ const ProcessFeed = ({ data = [] }, showAuthor = false) => {
101119
}
102120

103121
return {
104-
title: `${showAuthor ? item.user.name + ': ' : ''}${item.in_reply_to_screen_name ? 'Re ' : ''}${replaceBreak(item.full_text)}`,
105-
author: item.user.name,
106-
description: `${showAuthor ? `<img width="15px" src="${item.user.profile_image_url_https}"><strong>${item.user.name}:</strong><br><br>` : ''}${item.in_reply_to_screen_name ? 'Re ' : ''}${
107-
item.full_text
108-
}${url}${img}${quote}`,
122+
title:
123+
originalItem.user.name +
124+
`: ${originalItem === item ? '' : '🔁 '}${showAuthorInTitle || originalItem !== item ? item.user.name + ': ' : ''}${item.in_reply_to_screen_name ? '↩️ ' : ''}${replaceBreak(item.full_text)}${quoteOnTitle}`,
125+
author: originalItem.user.name,
126+
description: `${
127+
showAuthor ? `<a href="https://twitter.com/${item.user.screen_name}"><img align="left" src="${item.user.profile_image_url_https}" hspace="8" vspace="8"><strong>${item.user.name}</strong></a>:&nbsp;&nbsp;` : ''
128+
}${item.in_reply_to_screen_name ? '🔁 ' : ''}${item.full_text}${url}${img}${quote}<hr><small>${new Date(item.created_at).toLocaleString()}</small>`,
109129
pubDate: new Date(item.created_at).toUTCString(),
110130
link: `https://twitter.com/${item.user.screen_name}/status/${item.id_str}`,
111131
};

lib/routes/weibo/user.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ module.exports = async (ctx) => {
4949
const isDataOK = data !== undefined && data.text;
5050
if (isDataOK) {
5151
item.mblog.text = data.text;
52+
item.mblog.created_at = data.created_at;
53+
if (item.mblog.retweeted_status && data.retweeted_status) {
54+
item.mblog.retweeted_status.created_at = data.retweeted_status.created_at;
55+
}
5256
}
5357
// 转发的长微博处理
5458
const retweet = item.mblog.retweeted_status;
@@ -60,8 +64,10 @@ module.exports = async (ctx) => {
6064
}
6165

6266
const link = `https://weibo.com/${uid}/${item.mblog.bid}`;
63-
let description = weiboUtils.format(item.mblog);
64-
const title = description.replace(/<img[\s\S]*?>/g, '[图片]').replace(/<.*?>/g, '');
67+
const formatWithRetweet = weiboUtils.formatWithRetweet(item.mblog);
68+
let description = formatWithRetweet.description;
69+
const retweetedOrOriginal = formatWithRetweet.retweetedOrOriginal;
70+
const title = item.mblog.user.screen_name + ': ' + retweetedOrOriginal.replace(/<img[\s\S]*?>/g, '[图片]').replace(/<.*?>/g, '');
6571
const pubDate = isDataOK ? new Date(data.created_at).toUTCString() : date(item.mblog.created_at, 8);
6672

6773
// 视频的处理
@@ -74,6 +80,7 @@ module.exports = async (ctx) => {
7480
description: description,
7581
link: link,
7682
pubDate: pubDate,
83+
author: item.mblog.user.screen_name,
7784
};
7885
return Promise.resolve(it);
7986
})

0 commit comments

Comments
 (0)