Skip to content

Commit 7c5d9b4

Browse files
committed
v11.13.0
1 parent 77e55b5 commit 7c5d9b4

4 files changed

Lines changed: 155 additions & 6 deletions

File tree

package-lock.json

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "@codebam/cf-workers-telegram-bot",
3-
"version": "11.12.0",
3+
"version": "11.13.0",
44
"description": "serverless telegram bot on cf workers",
55
"main": "./dist/main.js",
66
"module": "./dist/main.js",
7-
"types": "./dist/types.d.ts",
7+
"types": "./dist/main.d.ts",
88
"files": [
99
"dist",
1010
"LICENSE",
@@ -53,7 +53,8 @@
5353
"wrangler": "^4.90.1"
5454
},
5555
"dependencies": {
56-
"@eslint/eslintrc": "^3.3.5"
56+
"@eslint/eslintrc": "^3.3.5",
57+
"marked": "^18.0.3"
5758
},
5859
"typedocOptions": {
5960
"entryPoints": [

src/main.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import TelegramCallbackQuery from './types/TelegramCallbackQuery.js';
4141
import TelegramPreCheckoutQuery from './types/TelegramPreCheckoutQuery.js';
4242
import TelegramDocument from './types/TelegramDocument.js';
4343
import TelegramSuccessfulPayment from './types/TelegramSuccessfulPayment.js';
44+
import { markdownToHtml, fetchTool } from './utils.js';
4445

4546
export default TelegramBot;
4647
export {
@@ -86,4 +87,6 @@ export {
8687
TelegramPreCheckoutQuery,
8788
TelegramDocument,
8889
TelegramSuccessfulPayment,
90+
markdownToHtml,
91+
fetchTool,
8992
};

src/utils.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { marked } from 'marked';
2+
3+
export async function markdownToHtml(s: string): Promise<string> {
4+
const renderer = new marked.Renderer();
5+
6+
// Telegram supports: b, strong, i, em, u, ins, s, strike, del, span, tg-spoiler, a, code, pre, blockquote
7+
8+
renderer.heading = ({ tokens, depth }) => {
9+
const text = renderer.parser.parseInline(tokens);
10+
if (depth === 1) return `<b>${text}</b>\n\n`;
11+
if (depth === 2) return `<b>${text}</b>\n\n`;
12+
return `<b>${text}</b>\n\n`;
13+
};
14+
15+
renderer.paragraph = ({ tokens }) => {
16+
const text = renderer.parser.parseInline(tokens);
17+
return `${text}\n\n`;
18+
};
19+
20+
renderer.br = () => '\n';
21+
22+
renderer.list = ({ items, ordered, start }) => {
23+
let result = '';
24+
for (let i = 0; i < items.length; i++) {
25+
const item = items[i];
26+
const prefix = ordered ? `${(start !== '' && start !== undefined) ? Number(start) + i : i + 1}. ` : '• ';
27+
result += `${prefix}${renderer.listitem(item)}\n`;
28+
}
29+
return result;
30+
};
31+
32+
renderer.listitem = (item) => {
33+
return renderer.parser.parseInline(item.tokens);
34+
};
35+
36+
renderer.strong = ({ tokens }) => `<b>${renderer.parser.parseInline(tokens)}</b>`;
37+
renderer.em = ({ tokens }) => `<i>${renderer.parser.parseInline(tokens)}</i>`;
38+
renderer.codespan = ({ text }) => `<code>${text}</code>`;
39+
renderer.code = ({ text, lang }) => {
40+
if (lang) {
41+
return `<pre><code class="language-${lang}">${text}</code></pre>\n`;
42+
}
43+
return `<pre><code>${text}</code></pre>\n`;
44+
};
45+
renderer.del = ({ tokens }) => `<s>${renderer.parser.parseInline(tokens)}</s>`;
46+
47+
renderer.link = ({ href, tokens }) => `<a href="${href}">${renderer.parser.parseInline(tokens)}</a>`;
48+
renderer.image = ({ href, text }) => `<a href="${href}">${text}</a>`;
49+
50+
renderer.blockquote = ({ tokens }) => {
51+
return `<blockquote>${renderer.parser.parse(tokens)}</blockquote>\n`;
52+
};
53+
54+
renderer.hr = () => `────────\n\n`;
55+
56+
// html tag pass-through for supported tags or escaping
57+
renderer.html = ({ text }) => {
58+
const allowedTags = [
59+
'b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike', 'del',
60+
'span', 'tg-spoiler', 'a', 'code', 'pre', 'blockquote'
61+
];
62+
const match = /^<\/?([a-z0-9\-]+)(?:\s+[^>]*)?>/i.exec(text);
63+
if (match) {
64+
const tagName = match[1].toLowerCase();
65+
if (allowedTags.includes(tagName)) {
66+
return text; // Allow through
67+
}
68+
}
69+
// Escape everything else
70+
return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
71+
};
72+
73+
renderer.text = (token) => {
74+
if ('tokens' in token && token.tokens) {
75+
return renderer.parser.parseInline(token.tokens);
76+
}
77+
// Escape standard HTML entities
78+
return token.text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
79+
};
80+
81+
marked.setOptions({
82+
gfm: true,
83+
breaks: true,
84+
});
85+
86+
const parsed = await marked.parse(s, { renderer });
87+
88+
// Trim multiple newlines
89+
return parsed.replace(/\n{3,}/g, '\n\n').trim();
90+
}
91+
92+
export const fetchTool = {
93+
name: 'fetch',
94+
description:
95+
'Make an HTTP request to fetch a website or API, returning the HTML or JSON. You MUST use this tool when the user asks to fetch a URL, visit a website, or make a GET request, instead of writing code.',
96+
parameters: {
97+
type: 'object',
98+
properties: {
99+
url: { type: 'string', description: 'The URL to fetch' },
100+
method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE'], default: 'GET' },
101+
headers: { type: 'object', description: 'HTTP headers to include in the request' },
102+
body: { type: 'string', description: 'The request body' }
103+
},
104+
required: ['url']
105+
},
106+
function: async ({
107+
url,
108+
method,
109+
headers,
110+
body
111+
}: {
112+
url: string;
113+
method?: string;
114+
headers?: Record<string, string>;
115+
body?: string;
116+
}) => {
117+
try {
118+
const res = await fetch(url, {
119+
method: method || 'GET',
120+
headers: {
121+
'User-Agent': 'Mozilla/5.0 (Cloudflare Worker Telegram Bot)',
122+
...headers
123+
},
124+
body: body ? (typeof body === 'string' ? body : JSON.stringify(body)) : undefined
125+
});
126+
const text = await res.text();
127+
return text.slice(0, 10000);
128+
} catch (e) {
129+
return `Error executing fetch: ${String(e)}`;
130+
}
131+
}
132+
};

0 commit comments

Comments
 (0)