From 2ef844425ba5f9e011c46a990ee1d47b50d4053c Mon Sep 17 00:00:00 2001 From: Rongrong <15956627+Rongronggg9@users.noreply.github.com> Date: Wed, 2 Mar 2022 19:25:03 +0800 Subject: [PATCH] feat(route)(telegram/channel): major rewrite (#9213) feed title changed to English route path changed exclude 'no posts found' messages (always) custom route params: exclude forwarded/reply/service messages (selectable) exclude unsupported messages (selectable, default) remove link preview (selectable) remove metadata (via bot / forwarded from / reply to) (selectable) add forwarded message author (selectable, default) add inline buttons (selectable) remove media tags in title (selectable) non-emoji media tags (selectable) search query (selectable, retain backward compatibility) message types newly supported / enhanced: reply (previous not in media tags, lacks of via bot) forwarded (previously not in media tags, lacks of via bot) service (previous not in media tags, img not extracted) via bot (previous unrecognized, treated as normal messages) video (previous use another emoji as media tag) GIF (previous unrecognized, treated as video) poll (previous only poll title) voice (previous use another emoji as media tag, no title/content) music (previous unrecognized, treated as document) document (previous no content, broken if multiple exist) location (previous only media tag) contact (previous totally unsupported) sticker (previous only media tag) animated sticker (previous totally unsupported) unsupported (previous totally unsupported) misc: fix emoji in posts: weird format (Italic and Bold) fix title: lacks of spaces fix title: use pubDate even if media tags exist fix broken documents message if multiple documents exist fix mistaken exception description fix media sequence: video always at last, #4777 fix: autofilled title (pubDate) not in UTC use cache minor refactor Signed-off-by: Rongrong <15956627+Rongronggg9@users.noreply.github.com> --- docs/en/social-media.md | 28 +- docs/social-media.md | 28 +- lib/v2/telegram/channel.js | 572 ++++++++++++++++++++++------ lib/v2/telegram/maintainer.js | 2 +- lib/v2/telegram/router.js | 2 +- lib/v2/telegram/templates/video.art | 4 +- 6 files changed, 512 insertions(+), 124 deletions(-) diff --git a/docs/en/social-media.md b/docs/en/social-media.md index 57244341ca..cfdd423ca5 100644 --- a/docs/en/social-media.md +++ b/docs/en/social-media.md @@ -241,10 +241,36 @@ Only for self-hosted ### Channel - + + +| Key | Description | Accepts | Defaults to | +|-----------------------|-----------------------------------------------------------------------|------------------------------------------------------|-------------------| +| showLinkPreview | Show the link preview from Telegram | 0/1/true/false | true | +| showViaBot | For messages sent via bot, show the bot | 0/1/true/false | true | +| showReplyTo | For reply messages, show the target of the reply | 0/1/true/false | true | +| showFwdFrom | For forwarded messages, show the forwarding source | 0/1/true/false | true | +| showFwdFromAuthor | For forwarded messages, show the author of the forwarding source | 0/1/true/false | true | +| showInlineButtons | Show inline buttons | 0/1/true/false | false | +| showMediaTagInTitle | Show media tags in the title | 0/1/true/false | true | +| showMediaTagAsEmoji | Show media tags as emoji | 0/1/true/false | true | +| includeFwd | Include forwarded messages | 0/1/true/false | true | +| includeReply | Include reply messages | 0/1/true/false | true | +| includeServiceMsg | Include service messages (e.g. message pinned, channel photo updated) | 0/1/true/false | true | +| includeUnsupportedMsg | Include messages unsupported by t.me | 0/1/true/false | false | +| searchQuery | search query | keywords; replace `#` by `%23` for hashtag searching | (search disabled) | + +Specify different option values than default values can meet different needs, URL + +``` +https://rsshub.app/telegram/channel/NewlearnerChannel/showLinkPreview=0&showViaBot=0&showReplyTo=0&showFwdFrom=0&showFwdFromAuthor=0&showInlineButtons=0&showMediaTagInTitle=1&showMediaTagAsEmoji=1&includeFwd=0&includeReply=1&includeServiceMsg=0&includeUnsupportedMsg=0 +``` + +generates an RSS without any link previews and annoying metadata, with emoji media tags in the title, without forwarded messages (but with reply messages), and without messages you don't care about (service messages and unsupported messages), for people who prefer pure subscriptions. ::: tip +For backward compatibility reasons, invalid `routeParams` will be treated as `searchQuery` . + Due to Telegram restrictions, some channels involving pornography, copyright, and politics cannot be subscribed. You can confirm by visiting `https://t.me/s/:username`. ::: diff --git a/docs/social-media.md b/docs/social-media.md index 6de81fcf12..393caa661f 100644 --- a/docs/social-media.md +++ b/docs/social-media.md @@ -602,10 +602,36 @@ Tiny Tiny RSS 会给所有 iframe 元素添加 `sandbox="allow-scripts"` 属性 ### 频道 - + + +| 键 | 含义 | 接受的值 | 默认值 | +|-----------------------|-------------------------|-----------------------------------|--------| +| showLinkPreview | 是否显示 Telegram 的链接预览 | 0/1/true/false | true | +| showViaBot | 对于经 bot 发出的消息,是否显示该 bot | 0/1/true/false | true | +| showReplyTo | 对于回复消息,是否显示回复的目标 | 0/1/true/false | true | +| showFwdFrom | 对于转发消息,是否显示消息的转发来源 | 0/1/true/false | true | +| showFwdFromAuthor | 对于转发消息,是否显示消息的转发来源的原始作者 | 0/1/true/false | true | +| showInlineButtons | 是否显示消息的按钮 | 0/1/true/false | false | +| showMediaTagInTitle | 是否在标题中显示媒体标签 | 0/1/true/false | true | +| showMediaTagAsEmoji | 将媒体标签显示为 emoji | 0/1/true/false | true | +| includeFwd | 包含转发消息 | 0/1/true/false | true | +| includeReply | 包含回复消息 | 0/1/true/false | true | +| includeServiceMsg | 包含服务消息 (如: 置顶了消息,更换了头像) | 0/1/true/false | true | +| includeUnsupportedMsg | 包含 t.me 不支持的消息 | 0/1/true/false | false | +| searchQuery | 搜索关键词 | 关键词; 如需搜索 hashtag 请用 `%23` 替代 `#` | (禁用搜索) | + +指定更多与默认值不同的参数选项可以满足不同的需求,如 + +``` +https://rsshub.app/telegram/channel/NewlearnerChannel/showLinkPreview=0&showViaBot=0&showReplyTo=0&showFwdFrom=0&showFwdFromAuthor=0&showInlineButtons=0&showMediaTagInTitle=1&showMediaTagAsEmoji=1&includeFwd=0&includeReply=1&includeServiceMsg=0&includeUnsupportedMsg=0 +``` + +会生成一个没有任何链接预览和烦人的元数据,在标题中显示 emoji 媒体标签,不含转发消息(但含有回复消息),也不含你不关心的消息(服务消息和不支持的消息)的 RSS,适合喜欢纯净订阅的人。 ::: tip 提示 +为向后兼容,不合法的 `routeParams` 会被视作 `searchQuery` 。 + 由于 Telegram 限制,部分涉及色情、版权、政治的频道无法订阅,可通过访问 确认。 ::: diff --git a/lib/v2/telegram/channel.js b/lib/v2/telegram/channel.js index 8280411834..4cb42eefaf 100644 --- a/lib/v2/telegram/channel.js +++ b/lib/v2/telegram/channel.js @@ -1,23 +1,104 @@ +const config = require('@/config').value; const got = require('@/utils/got'); const cheerio = require('cheerio'); const { parseDate } = require('@/utils/parse-date'); const { art } = require('@/utils/render'); const path = require('path'); +const querystring = require('querystring'); +const { fallback, queryToBoolean } = require('@/utils/readable-social'); + +/* message types */ +const REPLY = 'REPLY'; +const FORWARDED = 'FORWARDED'; +const SERVICE = 'SERVICE'; +const VIA_BOT = 'VIA_BOT'; +const VIDEO = 'VIDEO'; +const GIF = 'GIF'; +const PHOTO = 'PHOTO'; +const POLL = 'POLL'; +const VOICE = 'VOICE'; +const MUSIC = 'MUSIC'; +const DOCUMENT = 'DOCUMENT'; +const LOCATION = 'LOCATION'; +const CONTACT = 'CONTACT'; +const STICKER = 'STICKER'; +const ANIMATED_STICKER = 'ANIMATED_STICKER'; +// const VIDEO_STICKER = 'VIDEO_STICKER'; // not supported yet by t.me +const UNSUPPORTED = 'UNSUPPORTED'; + +/* message media tag dict */ +const mediaTagDict = { + REPLY: ['[Reply]', '↩️'], + FORWARDED: ['[Forwarded]', '🔁'], + SERVICE: ['[Service]', '🔧'], + VIA_BOT: ['[Via bot]', '🤖'], + VIDEO: ['[Video]', '🎬'], + GIF: ['[GIF]', '[GIF]'], + PHOTO: ['[Photo]', '🖼'], + POLL: ['[Poll]', '📊'], + VOICE: ['[Voice]', '🎙'], + MUSIC: ['[Music]', '🎵'], + DOCUMENT: ['[Document]', '📄'], + LOCATION: ['[Location]', '📍'], + CONTACT: ['[Contact]', '📱'], + STICKER: ['[Sticker]', '[Sticker]'], + ANIMATED_STICKER: ['[Animated Sticker]', '[Animated Sticker]'], + // VIDEO_STICKER: ['[Video Sticker]', '[Video Sticker]'], // not supported yet by t.me + UNSUPPORTED: ['[Unsupported]', '🚫'], +}; module.exports = async (ctx) => { const username = ctx.params.username; - const searchQuery = ctx.params.searchQuery; - const resourceUrl = searchQuery ? `https://t.me/s/${username}?q=${encodeURIComponent(searchQuery)}` : `https://t.me/s/${username}`; - - const { data } = await got.get(resourceUrl); - const $ = cheerio.load(data); - const list = $('.tgme_widget_message_wrap'); - - if (list.length === 0 && $('.tgme_channel_history').length === 0) { - throw `Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: https://t.me/s/${username}`; + let routeParams = ctx.params.routeParams; + let showLinkPreview = true; + let showViaBot = true; + let showReplyTo = true; + let showFwdFrom = true; + let showFwdFromAuthor = true; + let showInlineButtons = false; + let showMediaTagInTitle = true; + let showMediaTagAsEmoji = true; + let includeFwd = true; + let includeReply = true; + let includeServiceMsg = true; + let includeUnsupportedMsg = false; + let searchQuery = routeParams; // for backward compatibility + if (routeParams && routeParams.search(/(^|&)(show(LinkPreview|ViaBot|ReplyTo|FwdFrom(Author)?|InlineButtons|MediaTag(InTitle|AsEmoji))|include(Fwd|Reply|(Service|Unsupported)Msg)|searchQuery)=/) !== -1) { + routeParams = querystring.parse(ctx.params.routeParams); + showLinkPreview = !!fallback(undefined, queryToBoolean(routeParams.showLinkPreview), showLinkPreview); + showViaBot = !!fallback(undefined, queryToBoolean(routeParams.showReplyTo), showViaBot); + showReplyTo = !!fallback(undefined, queryToBoolean(routeParams.showViaBot), showReplyTo); + showFwdFrom = !!fallback(undefined, queryToBoolean(routeParams.showFwdFrom), showFwdFrom); + showFwdFromAuthor = !!fallback(undefined, queryToBoolean(routeParams.showFwdFromAuthor), showFwdFromAuthor); + showInlineButtons = !!fallback(undefined, queryToBoolean(routeParams.showInlineButtons), showInlineButtons); + showMediaTagInTitle = !!fallback(undefined, queryToBoolean(routeParams.showMediaTagInTitle), showMediaTagInTitle); + showMediaTagAsEmoji = !!fallback(undefined, queryToBoolean(routeParams.showMediaTagAsEmoji), showMediaTagAsEmoji); + includeFwd = !!fallback(undefined, queryToBoolean(routeParams.includeFwd), includeFwd); + includeReply = !!fallback(undefined, queryToBoolean(routeParams.includeReply), includeReply); + includeServiceMsg = !!fallback(undefined, queryToBoolean(routeParams.includeServiceMsg), includeServiceMsg); + includeUnsupportedMsg = !!fallback(undefined, queryToBoolean(routeParams.includeUnsupportedMsg), includeUnsupportedMsg); + searchQuery = fallback(undefined, routeParams.searchQuery, null); } - const feedTitle = searchQuery ? `「${searchQuery}」- ` + $('.tgme_channel_info_header_title').text() + ' - Telegram 频道' : $('.tgme_channel_info_header_title').text() + ' - Telegram 频道'; + const resourceUrl = searchQuery ? `https://t.me/s/${username}?q=${encodeURIComponent(searchQuery)}` : `https://t.me/s/${username}`; + + let data = await ctx.cache.get(resourceUrl, false); + if (!data) { + const response = await got.get(resourceUrl); + data = response.data; + ctx.cache.set(resourceUrl, data, config.cache.routeExpire); + } + const $ = cheerio.load(data); + const list = includeServiceMsg + ? $('.tgme_widget_message_wrap:not(.tgme_widget_message_wrap:has(.tme_no_messages_found))') // exclude 'no posts found' messages + : $('.tgme_widget_message_wrap:not(.tgme_widget_message_wrap:has(.service_message,.tme_no_messages_found))'); // also exclude service messages + + if (list.length === 0 && $('.tgme_channel_history').length === 0) { + throw `Unable to fetch message feed from this channel. Please check this URL to see if you can view the message preview: ${resourceUrl}`; + } + + const channelName = $('.tgme_channel_info_header_title').text(); + const feedTitle = (searchQuery ? `"${searchQuery}" - ` : '') + channelName + ' - Telegram Channel'; ctx.state.data = { title: feedTitle, @@ -25,7 +106,7 @@ module.exports = async (ctx) => { link: resourceUrl, allowEmpty: true, - itunes_author: $('.tgme_channel_info_header_title').text(), + itunes_author: channelName, image: $('.tgme_page_photo_image > img').attr('src'), item: @@ -34,69 +115,128 @@ module.exports = async (ctx) => { .map((index, item) => { item = $(item); - /* media tag */ + /* message types */ + const msgTypes = []; + if (item.find('.service_message').length) { + // service message can have an image (avatar changed) + msgTypes.push(SERVICE); + } + // if (item.find('.tgme_widget_message_video').length) { // fail if video too big + if (item.find('.tgme_widget_message_video_player').length) { + // video and gif cannot be mixed, it's safe to do that + msgTypes.push(item.find('.message_video_play').length ? VIDEO : GIF); + } + if (item.find('.tgme_widget_message_photo,.tgme_widget_message_service_photo').length) { + // video and photo can be mixed + msgTypes.push(PHOTO); + } + // all other types below cannot be mixed + if (item.find('.tgme_widget_message_poll').length) { + msgTypes.push(POLL); + } + if (item.find('.tgme_widget_message_voice').length) { + msgTypes.push(VOICE); + } + if (item.find('.tgme_widget_message_document').length) { + // music and document cannot be mixed, it's safe to do that + msgTypes.push(item.find('.audio').length ? MUSIC : DOCUMENT); + } + if (item.find('.tgme_widget_message_location').length) { + msgTypes.push(LOCATION); + } + if (item.find('.tgme_widget_message_contact').length) { + msgTypes.push(CONTACT); + } + if (item.find('.tgme_widget_message_sticker').length) { + msgTypes.push(STICKER); + } + if (item.find('.tgme_widget_message_tgsticker').length) { + msgTypes.push(ANIMATED_STICKER); + } + if (msgTypes.length === 0 && item.find('.message_media_not_supported').length) { + msgTypes.unshift(UNSUPPORTED); + if (!includeUnsupportedMsg) { + return null; // drop unsupported message + } + } + // all other types above cannot be mixed + if (item.find('.tgme_widget_message_author .tgme_widget_message_via_bot,.tgme_widget_message_forwarded_from .tgme_widget_message_via_bot').length) { + // can be mixed with other types, excluding service messages + msgTypes.unshift(VIA_BOT); + } + if (item.find('.tgme_widget_message_forwarded_from').length) { + // can be mixed with other types, excluding service messages and reply messages + msgTypes.unshift(FORWARDED); + if (!includeFwd) { + return null; // drop forwarded message + } + } + if (item.find('.tgme_widget_message_reply').length) { + // can be mixed with other types, excluding service messages and forwarded messages + msgTypes.unshift(REPLY); + if (!includeReply) { + return null; // drop reply message + } + } - const mediaTag = () => { - let mediaTag = ''; - if (item.find('.tgme_widget_message_photo').length > 0) { - mediaTag += '🖼'; - } - if (item.find('.tgme_widget_message_video').length > 0) { - mediaTag += '📹'; - } - if (item.find('.tgme_widget_message_poll').length > 0) { - mediaTag += '📊'; - } - if (item.find('.tgme_widget_message_voice').length > 0) { - mediaTag += '🎤'; - } - if (item.find('.tgme_widget_message_document').length > 0) { - mediaTag += '📎'; - } - if (item.find('.tgme_widget_message_location').length > 0) { - mediaTag += '📍'; - } - if (item.find('.tgme_widget_message_sticker').length > 0) { - mediaTag += '[Sticker]'; - } - return mediaTag; - }; + /* media tag */ + let mediaTag = ''; + if (showMediaTagInTitle) { + msgTypes.forEach((type) => { + if (type !== REPLY || type !== FORWARDED || type !== VIA_BOT || (type === REPLY && showReplyTo) || (type === FORWARDED && showFwdFrom) || (type === VIA_BOT && showViaBot)) { + mediaTag += showMediaTagAsEmoji ? mediaTagDict[type][1] : mediaTagDict[type][0]; + } + }); + } + + /* fix emoji */ + item.find('.emoji').each((_, emoji) => { + emoji = $(emoji); + emoji.replaceWith(`${emoji.text()}`); + }); /* "Forwarded From" tag */ const fwdFrom = () => { - const fwdFromNameObject = item.find('.tgme_widget_message_forwarded_from_name'); - if (fwdFromNameObject.length) { - if (fwdFromNameObject.attr('href') !== undefined) { - return `

Forwarded From - ${fwdFromNameObject.text()}

`; - } else { - return `

Forwarded From ${fwdFromNameObject.text()}

`; + let fwdFrom = ''; + const fwdFromNameObj = item.find('.tgme_widget_message_forwarded_from_name'); + if (fwdFromNameObj.length) { + const userLink = fwdFromNameObj.attr('href'); + const userHtml = userLink ? `${fwdFromNameObj.text()}` : fwdFromNameObj.text(); + fwdFrom += `

Forwarded From ${userHtml}`; + const fwdFromAuthorObj = item.find('.tgme_widget_message_forwarded_from_author'); + if (fwdFromAuthorObj.length && showFwdFromAuthor) { + fwdFrom += ` (${fwdFromAuthorObj.text()})`; } - } else { - return ''; + fwdFrom += '

'; } + return fwdFrom; }; /* reply */ const replyContent = () => { - if (item.find('.tgme_widget_message_reply').length !== 0) { - const replyObject = item.find('.tgme_widget_message_reply'); - - const replyAuthor = replyObject.find('.tgme_widget_message_author_name').length ? replyObject.find('.tgme_widget_message_author_name').text() : ''; - const replyLink = replyObject.attr('href').length ? replyObject.attr('href') : ''; - const replyMetatext = replyObject.find('.tgme_widget_message_metatext').length ? `

${replyObject.find('.tgme_widget_message_metatext').html()}

` : ''; - const replyText = replyObject.find('.tgme_widget_message_text').length ? `

${replyObject.find('.tgme_widget_message_text').html()}

` : ''; + const replyObj = item.find('.tgme_widget_message_reply'); + if (replyObj.length !== 0) { + const replyAuthorObj = replyObj.find('.tgme_widget_message_author_name'); + const replyAuthor = replyAuthorObj.length ? replyAuthorObj.text() : ''; + const viaBotObj = replyObj.find('.tgme_widget_message_via_bot'); + const viaBotText = viaBotObj.length ? ` via ${viaBotObj.text()}` : ''; + const replyLinkHref = replyObj.attr('href'); + const replyLink = replyLinkHref.length ? replyLinkHref : ''; + const replyMetaTextObj = replyObj.find('.tgme_widget_message_metatext'); + const replyMetaText = replyMetaTextObj.length ? `

${replyMetaTextObj.html()}

` : ''; + const replyTextObj = replyObj.find('.tgme_widget_message_text'); + const replyText = replyTextObj.length ? `

${replyTextObj.html()}

` : ''; if (replyLink !== '') { return `
-

${replyAuthor}:

- ${replyMetatext} +

${replyAuthor}${viaBotText}:

+ ${replyMetaText} ${replyText}
`; } else { return `
-

${replyAuthor}:

- ${replyMetatext} +

${replyAuthor}${viaBotText}:

+ ${replyMetaText} ${replyText}
`; } @@ -105,54 +245,118 @@ module.exports = async (ctx) => { } }; - /* images */ - - const generateImg = (selector) => { - let tag_images = ''; - if (item.find(selector).length) { - item.find(selector).each(function () { - tag_images += ``; - }); - return tag_images; + /* via bot */ + const viaBot = () => { + const viaBotObj = item.find('.tgme_widget_message_author .tgme_widget_message_via_bot,.tgme_widget_message_forwarded_from .tgme_widget_message_via_bot'); + if (viaBotObj.length) { + const userLink = viaBotObj.attr('href'); + const userHtml = userLink ? `${viaBotObj.text()}` : viaBotObj.text(); + return `

via ${userHtml}

`; } else { return ''; } }; - const messageImgs = generateImg('.tgme_widget_message_photo_wrap'); - /* videos */ - const messageVideos = () => { - let messageVideos = ''; - if (item.find('.tgme_widget_message_video_player').length) { - item.find('.tgme_widget_message_video_player').each(function () { - const source = $(this).find('.tgme_widget_message_video').attr('src'); - if (source) { - const poster = $(this) - .find('.tgme_widget_message_video_thumb') - .css('background-image') - .match(/url\('(.*)'\)/)[1]; - messageVideos += art(path.join(__dirname, 'templates/video.art'), { - source, - poster, - }); - } - }); + /* images and videos */ + const generateMedia = (selector) => { + const nodes = item.find(selector); + if (!nodes.length) { + return ''; + } + let tag_media = ''; + const pictureNodes = nodes.find('picture'); + const imgNodes = nodes.find('img'); + nodes.each((_, node) => { + const $node = $(node); + if (node.attribs && node.attribs.class && node.attribs.class.search(/(^|\s)tgme_widget_message_video_player(\s|$)/) !== -1) { + // video + const videoLink = $node.find('.tgme_widget_message_video').attr('src'); + const thumbBackground = $node.find('.tgme_widget_message_video_thumb').css('background-image'); + const thumbBackgroundUrl = thumbBackground && thumbBackground.match(/url\('(.*)'\)/); + const thumbBackgroundUrlSrc = thumbBackgroundUrl && thumbBackgroundUrl[1]; + tag_media += art(path.join(__dirname, 'templates/video.art'), { + source: videoLink, + poster: thumbBackgroundUrlSrc, + }); + } else if ($node.attr('data-webp')) { + // sticker + tag_media += ``; + } else if (node.name === 'picture') { + // animated sticker + tag_media += ''; + $node.find('source,img').each((_, source) => { + tag_media += $(source).toString(); + }); + tag_media += ''; + } else if (node.name === 'img') { + // unknown + tag_media += $node.toString(); + } else if (pictureNodes.length) { + // unknown + pictureNodes.each((_, picture) => { + tag_media += ''; + $(picture) + .find('source,img') + .each((_, source) => { + tag_media += $(source).toString(); + }); + tag_media += ''; + }); + return tag_media; + } else if (imgNodes.length) { + // service message + imgNodes.each((_, img) => { + tag_media += $(img).toString(); + }); + return tag_media; + } else { + // image message, location + const background = $node.css('background-image'); + const backgroundUrl = background && background.match(/url\('(.*)'\)/); + const backgroundUrlSrc = backgroundUrl && backgroundUrl[1]; + tag_media += backgroundUrlSrc ? `` : ''; + } + }); + return tag_media; + }; + // ordinary message photos, service message photos, stickers, animated stickers, video + const messageMedia = generateMedia('.tgme_widget_message_photo_wrap,.tgme_widget_message_service_photo,.tgme_widget_message_sticker,.tgme_widget_message_tgsticker,.tgme_widget_message_video_player'); + + /* location */ + const location = () => { + const locationObj = item.find('.tgme_widget_message_location_wrap'); + if (locationObj.length) { + const locationLink = locationObj.attr('href'); + const mapBackground = locationObj.find('.tgme_widget_message_location').css('background-image'); + const mapBackgroundUrl = mapBackground && mapBackground.match(/url\('(.*)'\)/); + const mapBackgroundUrlSrc = mapBackgroundUrl && mapBackgroundUrl[1]; + const mapImgHtml = mapBackgroundUrlSrc ? `` : showMediaTagAsEmoji ? mediaTagDict[LOCATION][1] : mediaTagDict[LOCATION][0]; + return locationLink ? `${mapImgHtml}` : mapImgHtml; + } else { + return ''; } - return messageVideos; }; - /* audio */ - const messageAudioUrl = item.find('audio.tgme_widget_message_voice').length ? item.find('audio.tgme_widget_message_voice').attr('src') : ''; - const messageAudioText = item.find('audio.tgme_widget_message_voice').length ? `` : ''; - const messageAudioDuration = () => { - if (item.find('.tgme_widget_message_voice_duration').length) { - const durationInMmss = item.find('.tgme_widget_message_voice_duration').text(); + /* voice */ + const voiceObj = item.find('audio.tgme_widget_message_voice'); + const durationObj = item.find('.tgme_widget_message_voice_duration'); + const durationInMmss = durationObj.text(); + const voiceUrl = voiceObj.length ? voiceObj.attr('src') : ''; + let voiceTitle = ''; + let voiceHtml = ''; + if (voiceUrl) { + if (showMediaTagInTitle) { + voiceTitle = durationInMmss ? `(${durationInMmss})` : ''; + } + voiceHtml += '

'; + voiceHtml += showMediaTagAsEmoji ? mediaTagDict[VOICE][1] : mediaTagDict[VOICE][0]; + voiceHtml += durationInMmss ? ` (${durationInMmss})` : ''; + voiceHtml += '

'; + voiceHtml += ``; + } + const voiceDuration = () => { + if (durationObj.length) { const p = durationInMmss.split(':'); - let second = 0, minute = 1; while (p.length > 0) { @@ -165,18 +369,38 @@ module.exports = async (ctx) => { } }; + /* unsupported */ + let unsupportedHtml = ''; + let unsupportedTitle = ''; + const unsupportedNodes = item.find('.message_media_not_supported'); + if (msgTypes.indexOf(UNSUPPORTED) !== -1 && unsupportedNodes.length) { + unsupportedHtml += '
'; + unsupportedNodes.find('.message_media_not_supported_label').each(function () { + const $this = $(this); + unsupportedTitle += $this.text(); + unsupportedHtml += `

${$this.text()}

`; + }); + unsupportedNodes.find('.message_media_view_in_telegram').each(function () { + const $this = $(this); + unsupportedHtml += $this.attr('href') ? `

${$this.text()}

` : `

${$this.text()}

`; + }); + unsupportedHtml += '
'; + } + /* link preview */ const linkPreview = () => { - const linkPreviewSite = item.find('.link_preview_site_name').length ? `${item.find('.link_preview_site_name').text()}
` : ''; + const linkPreviewSiteObj = item.find('.link_preview_site_name'); + const linkPreviewSite = linkPreviewSiteObj.length ? `${linkPreviewSiteObj.text()}
` : ''; + const linkPreviewTitleObj = item.find('.link_preview_title'); let linkPreviewTitle; - if (item.find('.link_preview_title').length) { - linkPreviewTitle = ` - ${item.find('.link_preview_title').text()}
`; + if (linkPreviewTitleObj.length) { + linkPreviewTitle = `${linkPreviewTitleObj.text()}
`; } else { linkPreviewTitle = ''; } - const linkPreviewDescription = item.find('.link_preview_description').length ? `

${item.find('.link_preview_description').html()}

` : ''; - const linkPreviewImage = generateImg('.link_preview_image') + generateImg('.link_preview_right_image'); + const linkPreviewDescriptionObj = item.find('.link_preview_description'); + const linkPreviewDescription = linkPreviewDescriptionObj.length ? `

${linkPreviewDescriptionObj.html()}

` : ''; + const linkPreviewImage = generateMedia('.link_preview_image') + generateMedia('.link_preview_right_image'); if (linkPreviewSite.length > 0 || linkPreviewTitle.length > 0 || linkPreviewDescription.length > 0 || linkPreviewImage.length > 0) { return `
${linkPreviewSite}${linkPreviewTitle}${linkPreviewDescription}${linkPreviewImage}
`; @@ -186,46 +410,156 @@ module.exports = async (ctx) => { }; /* poll */ - const pollQuestion = item.find('.tgme_widget_message_poll_question').length ? item.find('.tgme_widget_message_poll_question').text() : ''; + const pollQuestionObj = item.find('.tgme_widget_message_poll_question'); + const pollQuestion = pollQuestionObj.length ? pollQuestionObj.text() : ''; + const poll = () => { + let pollHtml = ''; + const pollTypeObj = item.find('.tgme_widget_message_poll_type'); + const pollType = pollTypeObj.length ? pollTypeObj.text() : ''; + const pollOptions = item.find('.tgme_widget_message_poll_option'); + if (pollQuestion && pollType.length > 0 && pollOptions.length > 0) { + pollHtml += `

${pollQuestion}

`; + pollHtml += `

${pollType}

`; + pollOptions.each((_, option) => { + const $option = $(option); + const percentObj = $option.find('.tgme_widget_message_poll_option_percent'); + const percent = percentObj.length ? percentObj.text() : ''; + const textObj = $option.find('.tgme_widget_message_poll_option_text'); + const text = textObj.length ? textObj.text() : ''; + if (percent && text) { + pollHtml += `

${percent} - ${text}

`; + } + }); + } + return pollHtml ? `
${pollHtml}
` : ''; + }; - /* attachment */ - const attachmentTitle = item.find('.tgme_widget_message_document_title').length ? item.find('.tgme_widget_message_document_title').text() + ' / ' + item.find('.tgme_widget_message_document_extra').text() : ''; + /* attachment (document or music) */ + const documentWrapObj = item.find('.tgme_widget_message_document_wrap'); + let attachmentTitle = ''; + let attachmentHtml = ''; + if (documentWrapObj.length) { + documentWrapObj.each((_, wrap) => { + // a message may have multiple attachments + const $wrap = $(wrap); + const documentTitleObj = $wrap.find('.tgme_widget_message_document_title'); + const documentExtraObj = $wrap.find('.tgme_widget_message_document_extra'); + const documentTitle = documentTitleObj.length ? documentTitleObj.text() : ''; + const documentExtra = documentExtraObj.length ? documentExtraObj.text() : ''; + const _attachmentTitle = `${documentTitle}${documentTitle && documentExtra ? ' - ' : ''}${documentExtra}`; + const _attachmentHtml = (documentTitle ? `

${documentTitle}

` : '') + (documentExtra ? `

${documentExtra}

` : ''); + attachmentTitle += attachmentTitle && _attachmentTitle ? ' | ' : ''; + attachmentTitle += _attachmentTitle; + attachmentHtml += _attachmentHtml ? `
${_attachmentHtml}
` : ''; + const wrapNext = $wrap.next('.tgme_widget_message_text'); + if (wrapNext.length) { + const captionHtml = wrapNext.html(); + if (captionHtml.length) { + attachmentHtml += `

${captionHtml}

`; + } + // remove them, avoid being duplicated + wrapNext.each((_, caption) => { + $(caption).remove(); + }); + } + }); + } + + /* contact */ + const contactNameObj = item.find('.tgme_widget_message_contact_name'); + const contactName = contactNameObj.length ? contactNameObj.text() : ''; + const contactNameHtml = contactName ? `${contactName}` : ''; + const contactPhoneObj = item.find('.tgme_widget_message_contact_phone'); + const contactPhone = contactPhoneObj.length ? contactPhoneObj.text() : ''; + const contactPhoneHtml = contactPhone ? `${contactPhone}` : ''; + const contactTitle = contactName + (contactName && contactPhone ? ': ' : '') + contactPhone; + const contactHtml = contactNameHtml || contactPhoneHtml ? `

${contactNameHtml}${contactName && contactPhone ? ': ' : ''}${contactPhoneHtml}

` : ''; + + /* inline buttons */ + let inlineButtons = ''; + const inlineButtonNodes = item.find('.tgme_widget_message_inline_button_text'); + if (showInlineButtons && inlineButtonNodes.length) { + inlineButtons += ''; + inlineButtonNodes.each((_, button) => { + const $button = $(button); + const buttonText = $button.text(); + inlineButtons += ``; + }); + inlineButtons += '
${buttonText}
'; + } /* pubDate */ const pubDate = parseDate(item.find('.tgme_widget_message_date time').attr('datetime')); /* message text & title */ - const messageTextObject = item.find('.tgme_widget_message_bubble > .tgme_widget_message_text'); - let messageText = '', + const messageTextObj = item.find('.tgme_widget_message_bubble > .tgme_widget_message_text'); + let messageHtml = '', messageTitle = ''; - if (messageTextObject.length > 0) { - messageText = `

${messageTextObject.html()}

`; + if (messageTextObj.length > 0) { + messageHtml = `

${messageTextObj.html()}

`; } - if (pollQuestion !== '') { + let titleCompleteFlag = false; + if (pollQuestion) { messageTitle = pollQuestion; - } else if (attachmentTitle !== '') { + titleCompleteFlag = true; + } else if (attachmentTitle) { messageTitle = attachmentTitle; - } else if (messageTextObject.length > 0) { - messageTitle = messageTextObject.text(); - } else { - messageTitle = pubDate; + titleCompleteFlag = true; + } else if (contactTitle) { + messageTitle = contactTitle; + titleCompleteFlag = true; + } else if (voiceTitle) { + messageTitle = voiceTitle; + } else if (unsupportedTitle) { + messageTitle = unsupportedTitle; + titleCompleteFlag = true; } + if (messageTextObj.length > 0 && !titleCompleteFlag) { + const _messageTextObj = $(messageTextObj.toString()); + _messageTextObj.find('br').replaceWith('\n'); + const trimmedTitleText = _messageTextObj.text().replace(/\n/g, ' ').trim(); + messageTitle += (messageTitle && trimmedTitleText ? ': ' : '') + trimmedTitleText; + } + + if (messageTitle === '') { + messageTitle = mediaTag || pubDate.toUTCString(); + } else { + messageTitle = `${mediaTag}${mediaTag ? ' ' : ''}${messageTitle}`; + } + + let description = ''; + if (showFwdFrom) { + description += fwdFrom(); + } + if (showReplyTo) { + description += replyContent(); + } + if (showViaBot) { + description += viaBot(); + } + description += location() + poll() + contactHtml + voiceHtml + attachmentHtml + messageHtml + unsupportedHtml; + if (showLinkPreview) { + description += linkPreview(); + } + description += inlineButtons + messageMedia; + return { - title: mediaTag() + messageTitle, - description: fwdFrom() + replyContent() + messageAudioText + messageText + linkPreview() + messageImgs + messageVideos(), + title: messageTitle, + description, pubDate, link: item.find('.tgme_widget_message_date').attr('href'), author: item.find('.tgme_widget_message_from_author').text(), - enclosure_url: messageAudioUrl, - enclosure_length: messageAudioDuration(), + enclosure_url: voiceUrl, + enclosure_length: voiceDuration(), enclosure_type: 'audio/ogg', }; }) .get() + .filter((item) => item) .reverse(), }; }; diff --git a/lib/v2/telegram/maintainer.js b/lib/v2/telegram/maintainer.js index 58f8e1ad92..bf1397495a 100644 --- a/lib/v2/telegram/maintainer.js +++ b/lib/v2/telegram/maintainer.js @@ -1,5 +1,5 @@ module.exports = { - '/telegram/channel/:username/:searchQuery?': ['DIYgod'], + '/telegram/channel/:username/:routeParams?': ['DIYgod', 'Rongronggg9'], '/telegram/stickerpack/:name': ['DIYgod'], '/telegram/blog': ['fengkx'], }; diff --git a/lib/v2/telegram/router.js b/lib/v2/telegram/router.js index 9c3616e092..23adb0eef6 100644 --- a/lib/v2/telegram/router.js +++ b/lib/v2/telegram/router.js @@ -1,5 +1,5 @@ module.exports = function (router) { - router.get('/channel/:username/:searchQuery?', require('./channel')); + router.get('/channel/:username/:routeParams?', require('./channel')); router.get('/stickerpack/:name', require('./stickerpack')); router.get('/blog', require('./blog')); }; diff --git a/lib/v2/telegram/templates/video.art b/lib/v2/telegram/templates/video.art index 3efe177986..044cb944a6 100644 --- a/lib/v2/telegram/templates/video.art +++ b/lib/v2/telegram/templates/video.art @@ -1,5 +1,7 @@ {{ if source }} +{{ else if poster }} +
Video is too big
{{ else }} -Media is too big +
Video is too big
{{ /if }}