话不多说💊🎈
首先新建一个频道,并记下频道ID

新建一个
Cloudflare Worker,复制下面的代码粘贴进去tgtalk.js代码:
addEventListener('fetch', event => { event.respondWith(TelgramChannelStarter(event.request)) }) const ChannelNameList = ['thun888sthinks', 'Thun888sDraftArticles', 'thun888sgrocery']; const deniedRegion = [""]; const baseproxyurl = "https://get-tg-channel-api.hzchu.top/?proxy=" async function TelgramChannelStarter(request) { const url = new URL(request.url); const Region = request.headers.get('cf-ipcountry'); let denied = deniedRegion.includes(Region); const proxyUrl = url.searchParams.get('proxy'); let mid = parseInt(url.searchParams.get('mid')); let cid = parseInt(url.searchParams.get('cid')) || 0; let tagname = url.searchParams.get('tag') || "DefaultTag"; let showtag = url.searchParams.get('showtag') || "true"; let usemd = url.searchParams.get('usemd') || "true"; usemd = usemd === 'true'; showtag = showtag === 'true'; let showview = url.searchParams.get('showview'); showview = showview === 'true'; let usetgclassification = url.searchParams.get('usetgclassification'); usetgclassification = usetgclassification === 'true'; let ChannelName = ChannelNameList[cid] if (!!proxyUrl) { if (!(proxyUrl.match(/\:\/\/(.*?)\.telegram\.org/g) || proxyUrl.match(/\:\/\/(.*)\.cdn\-telegram\.org/g) || proxyUrl.match(/\:\/\/telegram\.org/g))) { return new Response('Proxy URL is not valid'); } return fetch(proxyUrl); } const offset = url.searchParams.get('offset'); let ChannelUrl if (usetgclassification){ ChannelUrl = new URL(`https://t.me/s/${ChannelName}?q=%23${tagname}`); }else{ ChannelUrl = new URL(`https://t.me/s/${ChannelName}`); } if (!!offset) { ChannelUrl.searchParams.set('before', offset); } let getDataFromTelegram = await fetch(ChannelUrl, { "headers": { "x-requested-with": "XMLHttpRequest" }, "method": "POST" }).then(res => res.text()) .then(res => res .replace(/\\n/g, '') .replace(/\\(.)/g, '$1') .replace(/(^\"|\"$)/g, '') ); if (url.searchParams.get('rawHtml') === 'true') { return new Response(getDataFromTelegram, { headers: { "content-type": "text/html;charset=UTF-8", "Access-Control-Allow-Origin": "*" } }); } const nextBeforeMatch = [...getDataFromTelegram.matchAll(/data-before="([0-9]+)"/g)]; const nextBefore = nextBeforeMatch.length > 0 ? Number(nextBeforeMatch[0][1]) : 0; const ChannelMessages = ElementSpliter(getDataFromTelegram, '<div class="tgme_widget_message_wrap'); const ChannelMessageData = {}; ChannelMessages.forEach(ChannelMessage => { const MessageIdMatch = ChannelMessage.match(new RegExp(`data-post\="${ChannelName}\/(\\d+)"`)); if (MessageIdMatch) { const MessageId = MessageIdMatch[1]; // 确保消息ID是唯一的,并且没有重复 if (!ChannelMessageData.hasOwnProperty(MessageId)) { const MessageTextMatch = ChannelMessage.match(/<div class="tgme_widget_message_text js-message_text"(.+?)<\/div>/s); const MessagePhotoMatches = [...ChannelMessage.matchAll(/background-image:url\('(.+?)'\)/g)]; const MessageTimeMatch = ChannelMessage.match(/datetime="(.+?)"/); const MessageViewsMatch = ChannelMessage.match(/<span class="tgme_widget_message_views">\s*(\d+)\s*<\/span>/); let msg_views = MessageViewsMatch ? MessageViewsMatch[1] : "0"; let msg_time = MessageTimeMatch ? Math.round(new Date(MessageTimeMatch[1]).getTime() / 1000) : Math.round(new Date().getTime() / 1000); let msg_text = MessageTextMatch[0].replace(/<div (.*?)>/g, '').replace(/<\/div>/g, ''); msg_text = msg_text.replace(/href=\"\?q=(.+?)\"/g, function (match, p1) { return 'href="https://t.me/s/' + ChannelName + '/' + p1 + '"'; }); // emoji处理 msg_text = msg_text.replace(/<i class="emoji" style="background-image:url\(\'\/\/telegram.org\/img\/emoji\/40\/.+?\)\">/g, '') // msg_text = msg_text.replace(/<i class="emoji".*?url\(\'(.*?)\'\).*?<\/i>/g, function(match, p1) { // // 此处代码结合stellar样式,但不太理想 // return `<span class="tag-plugin emoji"><img src="${baseproxyurl}https:${p1}"></span>`; // }); // 是否显示标签 if (!showtag) { msg_text = msg_text.replace(/<a href="https:\/\/t\.me\/s\/.+?\/%23.+?">#[^<]+<\/a>/g, ''); } // 是否显示浏览量 if (showview) { msg_text = msg_text + '<br>(👀:' + msg_views + ')' } // 是否使用Markdown if (usemd) { // 将<a>标签替换为md格式 msg_text = msg_text.replace(/<a href=\"(.*?)\">(.*?)<\/a>/g, (match, $1, $2) => { if ($2.startsWith('#')) { return `[${$2}](${$1})`; } else { return $2; } }); // 将!替换为! msg_text = msg_text.replace(/!/g, '!'); // 将%替换为% msg_text = msg_text.replace(/%/g, '%'); // 将<br/> 替换为换行 msg_text = msg_text.replace(/<br\/>/g, '\n'); // 将<替换为< msg_text = msg_text.replace(/</g, '<'); // 将>替换为> msg_text = msg_text.replace(/>/g, '>'); // 将&替换为& msg_text = msg_text.replace(/&/g, '&'); // 将" 替换为" msg_text = msg_text.replace(/"/g, '"'); } if (MessageTextMatch && (!denied && MessageTextMatch[0].includes('#' + tagname))) { let resourceList = MessagePhotoMatches.filter(m => !m[1].includes('//telegram.org/img/emoji/')) .map(m => ({ "createdTs": msg_time, "externalLink": baseproxyurl + m[1], "type": "image/jpeg", "size": 0 })); ChannelMessageData[MessageId] = { id: MessageId, name: MessageId, rowStatus: "NORMAL", creatorId: 1, content: msg_text, createdTs: msg_time, updatedTs: msg_time, displayTs: msg_time, visibility: "PUBLIC", pinned: false, creatorName: ChannelName, creatorUsername: ChannelName, resourceList: resourceList, relationList: [], views: msg_views, }; } } } }); if (mid) { return new Response(JSON.stringify([ChannelMessageData[mid]]), { headers: { "content-type": "application/json;charset=UTF-8", "Access-Control-Allow-Origin": "*" } }); } // 如果设置了limit参数,则按照时间倒序排序,并限制输出的消息数量 let sortedMessages = Object.values(ChannelMessageData).sort((a, b) => b.createdTs - a.createdTs); const messagesCountParam = url.searchParams.get('limit'); const messagesCount = messagesCountParam ? parseInt(messagesCountParam, 10) : sortedMessages.length; sortedMessages = sortedMessages.slice(0, messagesCount); return new Response(JSON.stringify(sortedMessages), { headers: { "content-type": "application/json;charset=UTF-8", "Access-Control-Allow-Origin": "*" } }); } const ElementSpliter = (html, StartElement) => { const Elements = [] const ElementSpliterOnce = (html, StartElement) => { let ElementName = [...StartElement.matchAll(/\<(?<ELENAME>[a-zA-Z0-9]+)\s/g)][0].groups.ELENAME let ElementContent = StartElement for (let Start = html.indexOf(StartElement) + StartElement.length; Start < html.length; Start++) { ElementContent += html[Start] if (ElementContent.endsWith(`</${ElementName}>`)) { const PrefixCount = Object.keys(ElementContent.match(new RegExp(`\<${ElementName}`, 'g'))).length const SuffixCount = Object.keys(ElementContent.match(new RegExp(`\<\/${ElementName}(.*?)>`, 'g')) || []).length if ( PrefixCount === SuffixCount && PrefixCount !== 0 ) { return ElementContent } } } } while (1) { if (html.indexOf(StartElement) === -1) break const SplitOnce = ElementSpliterOnce(html, StartElement) Elements.push(SplitOnce) html = html.replace(SplitOnce, '') } return Elements }
并修改顶部配置
const ChannelNameList = ['thun888sthinks']; //频道ID,可添加多个 const deniedRegion = [""]; 屏蔽区域,如CN,US const baseproxyurl="https://get-tg-channel-api.hzchu.top/?proxy=" //将中间域名替换为你为worker绑定的域名
可传入参数
参数 | 参数名 | 默认值 | 描述 |
cid | 频道id | 0 | 指在代码里配置的第n个频道,并不是上文所述频道id |
mid | 信息id | / | 指定后只返回该信息,信息id可在发送后查看 |
tag | 标签名 | DefaultTag | 筛选传入的标签返回 |
limit | 获取条数 | / | / |
offset | 获取信息偏移量 | / | 如果有一条信息 id为80的信息,那么当传入80时将返回这条信息之前的所有信息 |
showtag | 是否展示标签 | true | / |
showview | 是否展示访问量 | false | 启用时会在末尾附上(views:number) |
usemd | 是否使用Markdown | true | 开启后返回未被tg解析过的md语法 |
usetgclassification | 是否使用tg自带的分类 | false | 开启后使用tg自带的分类来进行标签筛选,防止过久导致无法显示。但注意会有缓存 |
更行了cloudflare worker代码通过输入: https://你的域名/?num=3
可以获取最近的三条信息
众所周知,0是第一个自然数,所以cid传入0时表示获取第1个频道tag为空时默认返回#DefaultTag的数据,可在代码中自行更改tag原版是#SFCN,一开始没想明白是什么意思,现在明白了:Suitable For China,可以有别的玩法o( ̄▽ ̄)d
这里有个小功能:如果你输入
https://你的域名/?limit=5
便可以得到最近的五条消息
usemd: 在默认情况下,tg会解析md语法,对于不支持的语法会转义,从而导致前端无法正常解析md语法,在使用该参数后程序会将解析后的文本还原为正常md语法并返回。默认开启
我的修改:
tag筛选模糊改成精确
if (MessageTextMatch && (!denied && MessageTextMatch[0].includes('#' + tagname)))
改成了
if (MessageTextMatch && (!denied && new RegExp(#${tagname}(?![0-9a-zA-Z\\u4e00-\\u9fa5])).test(MessageTextMatch[0])))
我的演示:
可以输入【公告】两个字试试效果
参考:
字节君stellar主题集成Telegram Channel说说
stellar主题集成Telegram Channel说说
前言 众所周知,hexo主题stellar集成的说说功能是利用Github issue API来支持的 何为“说说”,即在某一瞬间,想要记载的内容。因此发说说需要操作方便。 在之前,我使用的是别人提供的微信公众号转Github issue来发说说,但因为某些原因,该公众号关闭了服务 如果有兴趣在微信公众号上发说说的可以参考这篇文章: https://github.com/xaoxuu/hexo-

把Tg Channel接入到Stellar时间线
使用Cloudflare Worker,获取Tg频道内容,并接入到Stellar时间线
