diff --git a/.env.example b/.env.example index bbda8ec..387cd4b 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,8 @@ TWITTER=ccbikai GITHUB=ccbikai DISCORD=https://DISCORD.com PODCASRT=https://PODCASRT.com +MASTODON=mastodon.social/@Mastodon +BLUESKY=bsky.app FOOTER_INJECT=FOOTER_INJECT HEADER_INJECT=HEADER_INJECT diff --git a/README.md b/README.md index ce14139..6587ec6 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,21 @@ English | [简体中文](./README.zh-cn.md) ### Real users +- [面条实验室](https://memo.miantiao.me/) - [Find Blog👁发现博客](https://broadcastchannel.pages.dev/) - [Memos 广场 🎪](https://now.memobbs.app/) - [APPDO 数字生活指南](https://mini.appdo.xyz/) - [85.60×53.98卡粉订阅/提醒](https://tg.docofcard.com/) +- [新闻在花频道](https://tg.istore.app/) +- [Charles Chin's Whisper](https://memo.eallion.com/) +- [PlayStation 新闻转发](https://playstationnews.pages.dev) +- [Yu's Life](https://daily.pseudoyu.com/) +- [Leslie 和朋友们](https://tg.imlg.co/) +- [OKHK 分享](https://tg.okhk.net/) +- [gledos 的微型博客](https://microblogging.gledos.science) +- [Steve Studio](https://tgc.surgeee.me/) +- [LiFePO4:沙雕吐槽](https://lifepo4.top) +- [Hotspot Hourly](https://hourly.top/) ### Platform @@ -61,6 +72,8 @@ TIMEZONE=America/New_York TELEGRAM=ccbikai TWITTER=ccbikai GITHUB=ccbikai +MASTODON=mastodon.social/@Mastodon +BLUESKY=bsky.app ## The following two social media need to be URLs DISCORD=https://DISCORD.com diff --git a/README.zh-cn.md b/README.zh-cn.md index a67db90..510916e 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -17,10 +17,21 @@ ### 真实用户 +- [面条实验室](https://memo.miantiao.me/) - [Find Blog👁发现博客](https://broadcastchannel.pages.dev/) - [Memos 广场 🎪](https://now.memobbs.app/) - [APPDO 数字生活指南](https://mini.appdo.xyz/) - [85.60×53.98卡粉订阅/提醒](https://tg.docofcard.com/) +- [新闻在花频道](https://tg.istore.app/) +- [Charles Chin's Whisper](https://memo.eallion.com/) +- [PlayStation 新闻转发](https://playstationnews.pages.dev) +- [Yu's Life](https://daily.pseudoyu.com/) +- [Leslie 和朋友们](https://tg.imlg.co/) +- [OKHK 分享](https://tg.okhk.net/) +- [gledos 的微型博客](https://microblogging.gledos.science) +- [Steve Studio](https://tgc.surgeee.me/) +- [LiFePO4:沙雕吐槽](https://lifepo4.top) +- [Hotspot Hourly](https://hourly.top/) ### 平台 diff --git a/package.json b/package.json index 1481943..7dd14a2 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "cheerio": "1.0.0-rc.12", "dayjs": "^1.11.12", "lru-cache": "^11.0.0", - "ofetch": "^1.3.4" + "ofetch": "^1.3.4", + "sanitize-html": "^2.13.0" }, "devDependencies": { "@antfu/eslint-config": "^2.24.1", @@ -47,5 +48,8 @@ }, "lint-staged": { "*": "eslint --fix" - } + }, + "browserslist": [ + "defaults" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e577ddd..fff0b33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: ofetch: specifier: ^1.3.4 version: 1.3.4 + sanitize-html: + specifier: ^2.13.0 + version: 2.13.0 devDependencies: '@antfu/eslint-config': specifier: ^2.24.1 @@ -2261,6 +2264,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -2407,11 +2414,11 @@ packages: engines: {node: '>=0.8.0'} escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + resolution: {integrity: sha1-FLqDpdNz49MR5a/KKc9b+tllvzQ=} engines: {node: '>=10'} escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + resolution: {integrity: sha1-RoMSa1ALYXYvLb66zhgG6L4xscg=} engines: {node: '>=12'} eslint-compat-utils@0.5.1: @@ -3020,6 +3027,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@5.0.0: + resolution: {integrity: sha1-RCf1CrNCnpAl6n1S6QQ6nvQVk0Q=} + engines: {node: '>=0.10.0'} + is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3592,6 +3603,9 @@ packages: parse-latin@7.0.0: resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + parse-srcset@1.0.2: + resolution: {integrity: sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=} + parse5-htmlparser2-tree-adapter@7.0.0: resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==} @@ -4069,6 +4083,9 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=} + sanitize-html@2.13.0: + resolution: {integrity: sha512-Xff91Z+4Mz5QiNSLdLWwjgBDm5b1RU6xBT0+12rapjiaR7SwfRdjw8f+6Rir2MXKLrDicRFHdb51hGOAxmsUIA==} + sass-formatter@0.7.9: resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==} @@ -7019,6 +7036,8 @@ snapshots: deep-is@0.1.4: {} + deepmerge@4.3.1: {} + defu@6.1.4: {} delegates@1.0.0: {} @@ -7978,6 +7997,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@5.0.0: {} + is-stream@3.0.0: {} is-unicode-supported@1.3.0: {} @@ -8740,6 +8761,8 @@ snapshots: unist-util-visit-children: 3.0.0 vfile: 6.0.2 + parse-srcset@1.0.2: {} + parse5-htmlparser2-tree-adapter@7.0.0: dependencies: domhandler: 5.0.3 @@ -9243,6 +9266,15 @@ snapshots: safe-buffer@5.2.1: {} + sanitize-html@2.13.0: + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.4.40 + sass-formatter@0.7.9: dependencies: suf-log: 2.5.3 diff --git a/postcss.config.cjs b/postcss.config.cjs index ef22201..9e6cc3f 100644 --- a/postcss.config.cjs +++ b/postcss.config.cjs @@ -1,6 +1,9 @@ module.exports = { plugins: [ - // require('postcss-nesting'), + require('postcss-nesting')({ + edition: '2021', + noIsPseudoSelector: true, + }), require('autoprefixer'), require('cssnano'), ], diff --git a/src/assets/bluesky.svg b/src/assets/bluesky.svg new file mode 100644 index 0000000..c605d10 --- /dev/null +++ b/src/assets/bluesky.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/assets/global.css b/src/assets/global.css index 1af9f8a..3104f5f 100644 --- a/src/assets/global.css +++ b/src/assets/global.css @@ -1,3 +1,7 @@ +* { + -webkit-tap-highlight-color: transparent; +} + .site-title { view-transition-name: site-title; transition: 0.2s ease; @@ -7,55 +11,6 @@ transition: 0.2s ease; } -[popover] { - display: none; - &:popover-open { - display: block; - } -} - -.image-preview-wrap { - display: block; -} - -.image-preview-button { - -webkit-appearance: none; - outline: none; - border: none; - background: transparent; - padding: 0; - margin-bottom: 16px; -} - -.modal { - position: fixed; - top: 0px; - left: 0px; - z-index: 1000; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.8); - backdrop-filter: blur(20px); -} - -.modal-img { - margin: auto; - max-width: calc(100% - 40px) !important; - max-height: calc(100% - 40px) !important; - border-radius: var(--media-border-radius); - border: 1px solid var(--border-color); - box-shadow: var(--shadows); - cursor: pointer; - object-fit: scale-down; -} - -@media screen and (min-width: 600px) { - .modal-img { - max-width: calc(100% - 80px) !important; - max-height: calc(100% - 80px) !important; - } -} - .search-icon { position: absolute; top: 20px; @@ -63,8 +18,10 @@ width: 24px; height: 24px; padding: 4px; + appearance: none; + outline: none; - &::after { + &:after { content: '🔍'; width: 100%; height: 100%; @@ -81,6 +38,7 @@ .search-icon:checked + .search-form { display: block !important; } + .search-form { display: none; background: rgba(255, 255, 255, 0.75); diff --git a/src/assets/item.css b/src/assets/item.css new file mode 100644 index 0000000..43da170 --- /dev/null +++ b/src/assets/item.css @@ -0,0 +1,213 @@ +.content { + word-break: break-word; + + img { + width: calc(100% - var(--box-margin)); + } + + .tgme_widget_message_link_preview { + margin-top: 16px; + display: none; + + .link_preview_site_name, + .link_preview_title, + .link_preview_description { + display: none; + } + } + + .tgme_widget_message_link_preview:has(.link_preview_site_name) { + display: block; + background: var(--cell-background-color); + border-left: 3px solid var(--highlight-color); + padding: 6px; + padding-left: 10px; + border-radius: var(--box-border-radius); + + .link_preview_title { + display: block; + font-size: 1em; + font-weight: bolder; + line-height: 2; + } + + .link_preview_description { + display: block; + font-size: 0.8em; + line-height: 1.5; + } + } + + .tgme_widget_message_video, + .tgme_widget_message_roundvideo { + aspect-ratio: 1 / 1; + } + + .tgme_widget_message_link_preview:has(.link_preview_image) { + display: flex; + position: relative; + border: none; + padding: 0; + background: transparent; + + .link_preview_image { + aspect-ratio: 1200 / 630; + object-fit: cover; + } + + .link_preview_site_name { + display: block; + position: absolute; + bottom: var(--box-margin); + left: var(--box-margin); + padding-left: 4px; + padding-right: 4px; + background-color: rgba(0, 0, 0, 0.66); + font-size: 14px; + color: #fff; + line-height: 1.5; + border-radius: var(--box-border-radius); + text-overflow: ellipsis; + max-width: calc(100% - 28px); + white-space: nowrap; + overflow: hidden; + } + + .link_preview_title, + .link_preview_description { + display: none; + } + } + + blockquote { + margin: 16px 0; + font-size: 0.8em; + background: var(--cell-background-color); + border-left: 3px solid var(--highlight-color); + padding: 6px; + padding-left: 10px; + border-radius: var(--box-border-radius); + } + + .tgme_widget_message_sticker { + display: block; + } + + &:has(.tgme_widget_message_user_photo) { + display: flex; + + .tgme_widget_message_user_photo { + width: 60px; + height: 60px; + } + } + + .tgme_widget_message_voice { + display: block !important; + } + + .tgme_widget_message_poll_options { + display: block; + + .tgme_widget_message_poll_option_percent { + float: left; + margin-right: 8px; + } + } + + .tgme_widget_message_location_wrap { + display: block; + .tgme_widget_message_location { + padding-top: 50%; + background: no-repeat center; + background-size: cover; + } + } + + .emoji { + font-style: normal; + margin-right: 2px; + } + + .sticker { + box-shadow: none; + border: none; + } + + .spoiler-button { + cursor: pointer; + + input { + display: none; + } + + tg-spoiler { + color: transparent; + margin: auto 2px; + border-radius: var(--box-border-radius); + background: #ccc 60% 60% / 3000px 3000px; + background-image: repeating-conic-gradient( + #999 0 0.0001%, + #0000 0 0.0002% + ); + } + + input:checked + tg-spoiler { + background: unset; + color: unset; + } + } +} + +.tag-box { + flex-wrap: wrap; +} + +[popover] { + display: none; + &:popover-open { + display: block; + } +} + +.image-preview-wrap { + display: block; +} + +.image-preview-button { + appearance: none; + outline: none; + border: none; + background: transparent; + padding: 0; + margin-bottom: 16px; +} + +.modal { + position: fixed; + top: 0px; + left: 0px; + z-index: 1000; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + backdrop-filter: blur(20px); +} + +.modal-img { + margin: auto; + max-width: calc(100% - 40px) !important; + max-height: calc(100% - 40px) !important; + border-radius: var(--media-border-radius); + border: 1px solid var(--border-color); + box-shadow: var(--shadows); + cursor: pointer; + object-fit: scale-down; +} + +@media screen and (min-width: 600px) { + .modal-img { + max-width: calc(100% - 80px) !important; + max-height: calc(100% - 80px) !important; + } +} diff --git a/src/assets/mastodon.svg b/src/assets/mastodon.svg new file mode 100644 index 0000000..730bf5c --- /dev/null +++ b/src/assets/mastodon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/components/header.astro b/src/components/header.astro index a9ac503..b8e3cc7 100644 --- a/src/components/header.astro +++ b/src/components/header.astro @@ -7,6 +7,8 @@ import twitter from '../assets/twitter.svg' import github from '../assets/github.svg' import discord from '../assets/discord.svg' import telegram from '../assets/telegram.svg' +import mastodon from '../assets/mastodon.svg' +import bluesky from '../assets/bluesky.svg' const { SITE_URL } = Astro.locals const { channel } = Astro.props @@ -16,6 +18,8 @@ const TWITTER = getEnv(import.meta.env, Astro, 'TWITTER') const GITHUB = getEnv(import.meta.env, Astro, 'GITHUB') const TELEGRAM = getEnv(import.meta.env, Astro, 'TELEGRAM') const DISCORD = getEnv(import.meta.env, Astro, 'DISCORD') +const MASTODON = getEnv(import.meta.env, Astro, 'MASTODON') +const BLUESKY = getEnv(import.meta.env, Astro, 'BLUESKY') const staticProxy = getEnv(import.meta.env, Astro, 'STATIC_PROXY') ?? '/static/' --- @@ -110,6 +114,36 @@ const staticProxy = getEnv(import.meta.env, Astro, 'STATIC_PROXY') ?? '/static/' ) } + + { + MASTODON && MASTODON.length > 0 && ( + + {`@${MASTODON}`} + + ) + } + + { + BLUESKY && BLUESKY.length > 0 && ( + + {`@${BLUESKY}`} + + ) + } diff --git a/src/components/item.astro b/src/components/item.astro index 5a846ad..bb49ece 100644 --- a/src/components/item.astro +++ b/src/components/item.astro @@ -1,4 +1,5 @@ --- +import '../assets/item.css' import dayjs from '../lib/dayjs' import { getEnv } from '../lib/env' @@ -48,145 +49,3 @@ const timeago = datetime.isBefore(dayjs().subtract(1, 'w')) ) } - - diff --git a/src/layouts/base.astro b/src/layouts/base.astro index a6959b1..a377e81 100644 --- a/src/layouts/base.astro +++ b/src/layouts/base.astro @@ -47,6 +47,7 @@ const FOOTER_INJECT = getEnv(import.meta.env, Astro, 'FOOTER_INJECT') + { $(a)?.attr('title', $(a)?.text()) }) - $(content).find('.emoji')?.attr('style', '') + $(content).find('tg-spoiler')?.each((_index, spoiler) => { + const id = `spoiler-${index}-${_index}` + $(spoiler)?.attr('id', id) + ?.wrap('') + ?.before(``) + }) return content } function getPost($, item, { channel, staticProxy, index = 0 }) { item = item ? $(item).find('.tgme_widget_message') : $('.tgme_widget_message') - const content = modifyHTMLContent($, $(item).find('.tgme_widget_message_text')) + const content = modifyHTMLContent($, $(item).find('.tgme_widget_message_text'), { index }) const title = content?.text()?.match(/[^。\n]*(?=[。\n]|http)/g)?.[0] ?? content?.text() ?? '' const id = $(item).attr('data-post')?.replace(`${channel}/`, '') @@ -105,13 +118,13 @@ function getPost($, item, { channel, staticProxy, index = 0 }) { $.html($(item).find('.tgme_widget_message_reply')?.wrapInner('')?.wrapInner('
')), getImages($, item, { staticProxy, id, index, title }), getVideo($, item, { staticProxy, id, index, title }), + getAudio($, item, { staticProxy, id, index, title }), content?.html(), getImageStickers($, item, { staticProxy, index }), getVideoStickers($, item, { staticProxy, index }), // $(item).find('.tgme_widget_message_sticker_wrap')?.html(), $(item).find('.tgme_widget_message_poll')?.html(), $.html($(item).find('.tgme_widget_message_document_wrap')), - $.html($(item).find('.tgme_widget_message_voice')?.attr('controls', true)), $.html($(item).find('.tgme_widget_message_location_wrap')), getLinkPreview($, item, { staticProxy, index }), ].filter(Boolean).join('').replace(/(url\(["'])((https?:)?\/\/)/g, (match, p1, p2, _p3) => { @@ -133,7 +146,7 @@ export async function getChannelInfo(Astro, { before = '', after = '', q = '', t const cachedResult = cache.get(cacheKey) if (cachedResult) { - console.info('Macth Cache', { before, after, q, type, id }) + console.info('Match Cache', { before, after, q, type, id }) return JSON.parse(JSON.stringify(cachedResult)) } diff --git a/src/pages/rss.xml.js b/src/pages/rss.xml.js index 9adc848..99b3826 100644 --- a/src/pages/rss.xml.js +++ b/src/pages/rss.xml.js @@ -1,5 +1,5 @@ import rss from '@astrojs/rss' - +import sanitizeHtml from 'sanitize-html' import { getChannelInfo } from '../lib/telegram' export const prerender = false @@ -22,7 +22,14 @@ export async function GET(Astro) { title: item.title, description: item.description, pubDate: new Date(item.datetime), - content: item.content, + content: sanitizeHtml(item.content, { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'video', 'audio']), + allowedAttributes: { + video: ['src', 'width', 'height', 'poster'], + audio: ['src', 'controls'], + img: ['src', 'width', 'height', 'loading'], + }, + }), })), }) }