前言

在Stellar主题文档逛的时候,发现了把 Tg Channel接入到Stellar时间线 ,这里记录一下!

操作

首先新建一个频道,并记下频道ID

新建一个Cloudflare Worker,复制下面的代码粘贴进去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
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 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 startbefore = url.searchParams.get('startbefore');
const ChannelUrl = new URL(`https://t.me/s/${ChannelName}`);

if (!!startbefore) {
ChannelUrl.searchParams.set('before', startbefore);
}

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;
}
});
// 将&#33;替换为!
msg_text = msg_text.replace(/&#33;/g, '!');
// 将&#37;替换为%
msg_text = msg_text.replace(/&#37;/g, '%');
// 将<br/> 替换为换行
msg_text = msg_text.replace(/<br\/>/g, '\n');
// 将&lt;替换为<
msg_text = msg_text.replace(/&lt;/g, '<');
// 将&gt;替换为>
msg_text = msg_text.replace(/&gt;/g, '>');
// 将&amp;替换为&
msg_text = msg_text.replace(/&amp;/g, '&');
// 将&quot; 替换为"
msg_text = msg_text.replace(/&quot;/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,
};
}
}
}
});


// 如果设置了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
}

并修改顶部配置

1
2
3
const ChannelNameList = ['thun888sthinks']; //频道ID,可添加多个
const deniedRegion = [""]; 屏蔽区域,如CN,US
const baseproxyurl="https://get-tg-channel-api.hzchu.top/?proxy=" //将中间域名替换为你为worker绑定的域名

部署完Cloudflare worker后,请绑定你的域名,随后访问即可

随后按照使用Memos时间线 形式使用即可

可传入参数

参数 参数名 默认值 描述
cid 频道id 0 指在代码里配置的第n个频道,并不是上文所述频道id
mid 信息id / 指定后只返回该信息,信息id可在发送后查看
tag 标签名 DefaultTag 筛选传入的标签返回
limit 获取条数 / /
startbefore 获取之前信息 / 如果有一条信息id为80的信息,那么当传入80时将返回这条信息之前的所有信息
showtag 是否展示标签 true /
showview 是否展示访问量 false 启用时会在末尾附上(views:number)
usemd 是否使用Markdown true 开启后返回未被tg解析过的md语法

众所周知,0是第一个数字,所以cid传入0时表示获取第1个频道

tag为空时默认返回#DefaultTag的数据,可在代码中自行更改

tag原版是#SFCN,一开始没想明白是什么意思,现在明白了:Suitable For China,可以有别的玩法o( ̄▽ ̄)d

在Stellar中的使用

示例

1
2
3
{% timeline user:thun888sthinks api:https://你绑定的域名/?tag=DTA&limit=3 type:memos %}

{% endtimeline %}
/source/_data/widgets.yml
1
2
3
4
5
6
timeline:
layout: timeline
title: 近期动态
api: https://你绑定的域名/?tag=你想显示的标签名&limit=3
type: memos
hide: user,footer

更多用法

获取其它频道
指定获取某条信息
展示访问量
使用Markdown

我就不多记录了!!!


本站由 酷小呵 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

{% timeline user:kuhehefx api:https://talk.kuhehe.top/?tag=分享&limit=50&showtag=false type:memos %}
{% endtimeline %}

本文总阅读量 次 本文总访客量 人 本站总访问量 次 本站总访客数

Calculating…

本站已稳定运行