@classytic/social 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/LICENSE +21 -0
- package/README.md +368 -0
- package/dist/base-Bw7e52V8.mjs +246 -0
- package/dist/base-Bw7e52V8.mjs.map +1 -0
- package/dist/base-DBtKFiSX.d.mts +226 -0
- package/dist/base-DBtKFiSX.d.mts.map +1 -0
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client/index.d.mts +44 -0
- package/dist/client/index.d.mts.map +1 -0
- package/dist/client/index.mjs +154 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/common/index.d.mts +3 -0
- package/dist/common/index.mjs +7 -0
- package/dist/contracts-Cdwa4zlg.d.mts +121 -0
- package/dist/contracts-Cdwa4zlg.d.mts.map +1 -0
- package/dist/contracts-lCa069IK.mjs +221 -0
- package/dist/contracts-lCa069IK.mjs.map +1 -0
- package/dist/env-Bl0cwwjC.mjs +955 -0
- package/dist/env-Bl0cwwjC.mjs.map +1 -0
- package/dist/env-DxOZHf0p.d.mts +394 -0
- package/dist/env-DxOZHf0p.d.mts.map +1 -0
- package/dist/errors-Cm6LeKf7.mjs +32 -0
- package/dist/errors-Cm6LeKf7.mjs.map +1 -0
- package/dist/facebook-l_4CghaA.mjs +95 -0
- package/dist/facebook-l_4CghaA.mjs.map +1 -0
- package/dist/http-DpcLSR1M.mjs +197 -0
- package/dist/http-DpcLSR1M.mjs.map +1 -0
- package/dist/index.d.mts +42 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +71 -0
- package/dist/index.mjs.map +1 -0
- package/dist/instagram-BGaeUFU2.mjs +90 -0
- package/dist/instagram-BGaeUFU2.mjs.map +1 -0
- package/dist/linkedin-70whtVKa.mjs +101 -0
- package/dist/linkedin-70whtVKa.mjs.map +1 -0
- package/dist/meta-D3vcJU1c.mjs +126 -0
- package/dist/meta-D3vcJU1c.mjs.map +1 -0
- package/dist/pkce-jq5II68b.mjs +72 -0
- package/dist/pkce-jq5II68b.mjs.map +1 -0
- package/dist/polling-DZ1apXtA.mjs +25 -0
- package/dist/polling-DZ1apXtA.mjs.map +1 -0
- package/dist/providers/facebook.d.mts +135 -0
- package/dist/providers/facebook.d.mts.map +1 -0
- package/dist/providers/facebook.mjs +450 -0
- package/dist/providers/facebook.mjs.map +1 -0
- package/dist/providers/instagram.d.mts +122 -0
- package/dist/providers/instagram.d.mts.map +1 -0
- package/dist/providers/instagram.mjs +496 -0
- package/dist/providers/instagram.mjs.map +1 -0
- package/dist/providers/linkedin.d.mts +145 -0
- package/dist/providers/linkedin.d.mts.map +1 -0
- package/dist/providers/linkedin.mjs +574 -0
- package/dist/providers/linkedin.mjs.map +1 -0
- package/dist/providers/reddit.d.mts +102 -0
- package/dist/providers/reddit.d.mts.map +1 -0
- package/dist/providers/reddit.mjs +657 -0
- package/dist/providers/reddit.mjs.map +1 -0
- package/dist/providers/telegram.d.mts +139 -0
- package/dist/providers/telegram.d.mts.map +1 -0
- package/dist/providers/telegram.mjs +517 -0
- package/dist/providers/telegram.mjs.map +1 -0
- package/dist/providers/tiktok.d.mts +116 -0
- package/dist/providers/tiktok.d.mts.map +1 -0
- package/dist/providers/tiktok.mjs +676 -0
- package/dist/providers/tiktok.mjs.map +1 -0
- package/dist/providers/twitter.d.mts +150 -0
- package/dist/providers/twitter.d.mts.map +1 -0
- package/dist/providers/twitter.mjs +628 -0
- package/dist/providers/twitter.mjs.map +1 -0
- package/dist/providers/whatsapp.d.mts +79 -0
- package/dist/providers/whatsapp.d.mts.map +1 -0
- package/dist/providers/whatsapp.mjs +376 -0
- package/dist/providers/whatsapp.mjs.map +1 -0
- package/dist/providers/youtube.d.mts +153 -0
- package/dist/providers/youtube.d.mts.map +1 -0
- package/dist/providers/youtube.mjs +902 -0
- package/dist/providers/youtube.mjs.map +1 -0
- package/dist/reddit-B10kS4Se.mjs +126 -0
- package/dist/reddit-B10kS4Se.mjs.map +1 -0
- package/dist/schemas/index.d.mts +819 -0
- package/dist/schemas/index.d.mts.map +1 -0
- package/dist/schemas/index.mjs +31 -0
- package/dist/schemas/index.mjs.map +1 -0
- package/dist/security-BXhfebWm.d.mts +338 -0
- package/dist/security-BXhfebWm.d.mts.map +1 -0
- package/dist/shared-Fvc6xQku.mjs +100 -0
- package/dist/shared-Fvc6xQku.mjs.map +1 -0
- package/dist/telegram-FaUHpZgB.mjs +107 -0
- package/dist/telegram-FaUHpZgB.mjs.map +1 -0
- package/dist/tiktok-B_bMk4G-.mjs +94 -0
- package/dist/tiktok-B_bMk4G-.mjs.map +1 -0
- package/dist/twitter-BC22zfuc.mjs +98 -0
- package/dist/twitter-BC22zfuc.mjs.map +1 -0
- package/dist/types-BFE4psYI.d.mts +102 -0
- package/dist/types-BFE4psYI.d.mts.map +1 -0
- package/dist/types-Bv27tcT0.d.mts +230 -0
- package/dist/types-Bv27tcT0.d.mts.map +1 -0
- package/dist/types-BwkKyqpi.d.mts +253 -0
- package/dist/types-BwkKyqpi.d.mts.map +1 -0
- package/dist/types-CJrHMDV9.mjs +27 -0
- package/dist/types-CJrHMDV9.mjs.map +1 -0
- package/dist/types-ClbVc2rc.d.mts +117 -0
- package/dist/types-ClbVc2rc.d.mts.map +1 -0
- package/dist/types-D91N16Ym.d.mts +242 -0
- package/dist/types-D91N16Ym.d.mts.map +1 -0
- package/dist/types-DfLp_ibQ.d.mts +178 -0
- package/dist/types-DfLp_ibQ.d.mts.map +1 -0
- package/dist/types-DfjDgEoJ.d.mts +88 -0
- package/dist/types-DfjDgEoJ.d.mts.map +1 -0
- package/dist/types-Dp5Z9VBr.mjs +23 -0
- package/dist/types-Dp5Z9VBr.mjs.map +1 -0
- package/dist/types-hriBJTsU.d.mts +129 -0
- package/dist/types-hriBJTsU.d.mts.map +1 -0
- package/dist/types-rn6UuLL8.d.mts +184 -0
- package/dist/types-rn6UuLL8.d.mts.map +1 -0
- package/dist/whatsapp-CFp7ryR4.mjs +101 -0
- package/dist/whatsapp-CFp7ryR4.mjs.map +1 -0
- package/dist/youtube-Bs0fdY7H.mjs +98 -0
- package/dist/youtube-Bs0fdY7H.mjs.map +1 -0
- package/package.json +148 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
import { t as PlatformProvider } from "../base-Bw7e52V8.mjs";
|
|
2
|
+
import { t as SocialError } from "../errors-Cm6LeKf7.mjs";
|
|
3
|
+
import { t as httpRequest } from "../http-DpcLSR1M.mjs";
|
|
4
|
+
import { t as TelegramCredentialsSchema } from "../telegram-FaUHpZgB.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/providers/telegram/index.ts
|
|
7
|
+
/**
|
|
8
|
+
* Telegram Platform Provider
|
|
9
|
+
* ==========================
|
|
10
|
+
* Telegram Bot API integration for channels, groups, and direct messages.
|
|
11
|
+
*
|
|
12
|
+
* Uses token-based auth (Bot Token from @BotFather) — NOT OAuth.
|
|
13
|
+
* The bot must be added as admin to the target channel/group.
|
|
14
|
+
*
|
|
15
|
+
* Key endpoints (Telegram Bot API):
|
|
16
|
+
* - GET /getMe — verify bot identity
|
|
17
|
+
* - POST /sendMessage — send text message
|
|
18
|
+
* - POST /sendPhoto — send photo with caption
|
|
19
|
+
* - POST /sendVideo — send video with caption
|
|
20
|
+
* - POST /sendDocument — send file attachment
|
|
21
|
+
* - POST /sendMediaGroup — send album (2-10 photos/videos)
|
|
22
|
+
* - POST /getChat — get chat/channel info
|
|
23
|
+
* - POST /getChatMemberCount — get subscriber count
|
|
24
|
+
* - POST /pinChatMessage — pin a message
|
|
25
|
+
* - POST /deleteMessage — delete a message
|
|
26
|
+
*
|
|
27
|
+
* @see https://core.telegram.org/bots/api
|
|
28
|
+
*/
|
|
29
|
+
const BOT_API_BASE = "https://api.telegram.org/bot";
|
|
30
|
+
/**
|
|
31
|
+
* Human-friendly explanations for common Telegram Bot API error descriptions.
|
|
32
|
+
* @see https://core.telegram.org/api/errors
|
|
33
|
+
*/
|
|
34
|
+
const ERROR_HINTS = {
|
|
35
|
+
"Unauthorized": "Bot token is invalid or has been revoked. Generate a new token from @BotFather.",
|
|
36
|
+
"chat not found": "The chat/channel ID is wrong or the bot has not been added to it. Add the bot as admin first.",
|
|
37
|
+
"bot is not a member": "The bot is not a member of this chat/channel. Add it as an admin with \"Post Messages\" permission.",
|
|
38
|
+
"not enough rights": "The bot lacks admin permissions. Promote it to admin with the required permissions (Post Messages, Edit Messages, etc.).",
|
|
39
|
+
"CHAT_WRITE_FORBIDDEN": "The bot cannot write to this chat. Check that posting is allowed and the bot has \"Post Messages\" permission.",
|
|
40
|
+
"message to delete not found": "The message has already been deleted or the ID is incorrect.",
|
|
41
|
+
"message is not modified": "The new message content is identical to the current one.",
|
|
42
|
+
"too many requests": "Rate limit hit. Telegram allows ~30 messages/second to different chats, 1 msg/second to same chat. Wait and retry.",
|
|
43
|
+
"Bad Request: wrong file_id": "The file_id is invalid or expired. Re-upload the file.",
|
|
44
|
+
"group chat was upgraded to a supergroup": "The group was converted to a supergroup. Use the new chat ID (starts with -100).",
|
|
45
|
+
"FLOOD_WAIT": "Rate limit — wait the indicated number of seconds before retrying."
|
|
46
|
+
};
|
|
47
|
+
var TelegramProvider = class extends PlatformProvider {
|
|
48
|
+
constructor(config = {}) {
|
|
49
|
+
super(config);
|
|
50
|
+
this.name = "telegram";
|
|
51
|
+
this.displayName = "Telegram";
|
|
52
|
+
this.authType = "token";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Make a request to the Telegram Bot API
|
|
56
|
+
* @private
|
|
57
|
+
* @param method - Bot API method name (e.g. 'sendMessage')
|
|
58
|
+
* @param botToken - Bot token from @BotFather
|
|
59
|
+
* @param body - Request body (JSON or FormData for file uploads)
|
|
60
|
+
* @returns API response result
|
|
61
|
+
*/
|
|
62
|
+
async _api(method, botToken, body = null) {
|
|
63
|
+
const url = `${BOT_API_BASE}${botToken}/${method}`;
|
|
64
|
+
const isForm = body instanceof FormData;
|
|
65
|
+
const data = (await httpRequest("telegram", {
|
|
66
|
+
method: body ? "POST" : "GET",
|
|
67
|
+
url,
|
|
68
|
+
json: !isForm && body ? body : void 0,
|
|
69
|
+
form: isForm ? body : void 0,
|
|
70
|
+
timeout: 6e4,
|
|
71
|
+
retry: { attempts: 2 },
|
|
72
|
+
parseError: (raw, status) => {
|
|
73
|
+
if (raw && typeof raw === "object") {
|
|
74
|
+
const r = raw;
|
|
75
|
+
const description = r.description || `Telegram API error (${status})`;
|
|
76
|
+
const params = r.parameters;
|
|
77
|
+
const hint = Object.entries(ERROR_HINTS).find(([key]) => description.toLowerCase().includes(key.toLowerCase()));
|
|
78
|
+
return {
|
|
79
|
+
message: description,
|
|
80
|
+
errorCode: r.error_code,
|
|
81
|
+
hint: hint ? hint[1] : null,
|
|
82
|
+
retryAfter: params?.retry_after ?? null
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
})).data;
|
|
88
|
+
if (!data.ok) {
|
|
89
|
+
const description = data.description || `Telegram API error (${data.error_code})`;
|
|
90
|
+
const hint = Object.entries(ERROR_HINTS).find(([key]) => description.toLowerCase().includes(key.toLowerCase()));
|
|
91
|
+
throw new SocialError("telegram", description, {
|
|
92
|
+
statusCode: data.error_code >= 400 && data.error_code < 500 ? data.error_code : 502,
|
|
93
|
+
errorCode: data.error_code,
|
|
94
|
+
hint: hint ? hint[1] : null,
|
|
95
|
+
retryAfter: data.parameters?.retry_after || null
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return data.result;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get bot identity and verify token validity
|
|
102
|
+
*/
|
|
103
|
+
async getAccountInfo(accessToken) {
|
|
104
|
+
const bot = await this._api("getMe", accessToken);
|
|
105
|
+
return {
|
|
106
|
+
id: String(bot.id),
|
|
107
|
+
name: bot.first_name + (bot.last_name ? ` ${bot.last_name}` : ""),
|
|
108
|
+
username: bot.username,
|
|
109
|
+
profileImage: null
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Test credential validity
|
|
114
|
+
*/
|
|
115
|
+
async testCredential(credentialData) {
|
|
116
|
+
try {
|
|
117
|
+
const { botToken, chatId } = credentialData;
|
|
118
|
+
if (!botToken) return {
|
|
119
|
+
status: "Error",
|
|
120
|
+
message: "Bot Token is required. Get one from @BotFather on Telegram."
|
|
121
|
+
};
|
|
122
|
+
const botInfo = await this.getAccountInfo(botToken);
|
|
123
|
+
const result = {
|
|
124
|
+
status: "OK",
|
|
125
|
+
message: `Connected as @${botInfo.username}`,
|
|
126
|
+
data: {
|
|
127
|
+
channelId: botInfo.id,
|
|
128
|
+
channelTitle: botInfo.name,
|
|
129
|
+
username: botInfo.username
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
if (chatId) try {
|
|
133
|
+
const chat = await this.getChat(botToken, chatId);
|
|
134
|
+
result.data.chatTitle = chat.title || chat.username || chatId;
|
|
135
|
+
result.data.chatType = chat.type;
|
|
136
|
+
result.message += ` — channel "${chat.title || chatId}" accessible`;
|
|
137
|
+
} catch (chatError) {
|
|
138
|
+
result.status = "Warning";
|
|
139
|
+
const hint = chatError.hint || chatError.message || "unknown error";
|
|
140
|
+
result.message += ` — cannot access chat "${chatId}": ${hint}`;
|
|
141
|
+
const discovered = await this.discoverChats(botToken);
|
|
142
|
+
if (discovered.length > 0) result.data.availableChats = discovered;
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
status: "Error",
|
|
148
|
+
message: error.hint || error.message || "Failed to validate Telegram credential"
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get chat/channel information
|
|
154
|
+
* @param botToken
|
|
155
|
+
* @param chatId - Channel username (@channel) or numeric ID
|
|
156
|
+
*/
|
|
157
|
+
async getChat(botToken, chatId) {
|
|
158
|
+
return this._api("getChat", botToken, { chat_id: chatId });
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get subscriber/member count for a chat
|
|
162
|
+
* @param botToken
|
|
163
|
+
* @param chatId
|
|
164
|
+
* @returns Member count
|
|
165
|
+
*/
|
|
166
|
+
async getChatMemberCount(botToken, chatId) {
|
|
167
|
+
return this._api("getChatMemberCount", botToken, { chat_id: chatId });
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Discover chats the bot has interacted with recently.
|
|
171
|
+
* Uses getUpdates (only works when no webhook is active, falls back gracefully).
|
|
172
|
+
*/
|
|
173
|
+
async discoverChats(botToken) {
|
|
174
|
+
try {
|
|
175
|
+
if ((await this._api("getWebhookInfo", botToken))?.url) return [];
|
|
176
|
+
const updates = await this._api("getUpdates", botToken, { limit: 50 });
|
|
177
|
+
const chats = /* @__PURE__ */ new Map();
|
|
178
|
+
for (const update of updates || []) {
|
|
179
|
+
const sources = [
|
|
180
|
+
update.message?.chat,
|
|
181
|
+
update.channel_post?.chat,
|
|
182
|
+
update.my_chat_member?.chat,
|
|
183
|
+
update.edited_message?.chat
|
|
184
|
+
];
|
|
185
|
+
for (const chat of sources) if (chat?.id) chats.set(chat.id, {
|
|
186
|
+
id: chat.id,
|
|
187
|
+
title: chat.title || chat.username || chat.first_name || String(chat.id),
|
|
188
|
+
type: chat.type
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return Array.from(chats.values());
|
|
192
|
+
} catch {
|
|
193
|
+
return [];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Send a text message
|
|
198
|
+
* @param botToken
|
|
199
|
+
* @param chatId
|
|
200
|
+
* @param text - Message text (1-4096 characters)
|
|
201
|
+
* @param options
|
|
202
|
+
*/
|
|
203
|
+
async sendMessage(botToken, chatId, text, options = {}) {
|
|
204
|
+
return this._api("sendMessage", botToken, {
|
|
205
|
+
chat_id: chatId,
|
|
206
|
+
message_thread_id: options.messageThreadId,
|
|
207
|
+
text,
|
|
208
|
+
parse_mode: options.parseMode,
|
|
209
|
+
disable_notification: options.disableNotification,
|
|
210
|
+
reply_parameters: options.replyToMessageId !== void 0 ? { message_id: options.replyToMessageId } : void 0,
|
|
211
|
+
link_preview_options: options.disableWebPagePreview ? { is_disabled: true } : void 0
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Send a photo with optional caption
|
|
216
|
+
* @param botToken
|
|
217
|
+
* @param chatId
|
|
218
|
+
* @param photo - Photo URL or file_id
|
|
219
|
+
* @param options
|
|
220
|
+
*/
|
|
221
|
+
async sendPhoto(botToken, chatId, photo, options = {}) {
|
|
222
|
+
return this._api("sendPhoto", botToken, {
|
|
223
|
+
chat_id: chatId,
|
|
224
|
+
photo,
|
|
225
|
+
caption: options.caption,
|
|
226
|
+
parse_mode: options.parseMode,
|
|
227
|
+
disable_notification: options.disableNotification
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Send a video with optional caption
|
|
232
|
+
* @param botToken
|
|
233
|
+
* @param chatId
|
|
234
|
+
* @param video - Video URL or file_id
|
|
235
|
+
* @param options
|
|
236
|
+
*/
|
|
237
|
+
async sendVideo(botToken, chatId, video, options = {}) {
|
|
238
|
+
return this._api("sendVideo", botToken, {
|
|
239
|
+
chat_id: chatId,
|
|
240
|
+
video,
|
|
241
|
+
caption: options.caption,
|
|
242
|
+
parse_mode: options.parseMode,
|
|
243
|
+
duration: options.duration,
|
|
244
|
+
width: options.width,
|
|
245
|
+
height: options.height,
|
|
246
|
+
supports_streaming: options.supportsStreaming,
|
|
247
|
+
disable_notification: options.disableNotification
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Send a document/file
|
|
252
|
+
* @param botToken
|
|
253
|
+
* @param chatId
|
|
254
|
+
* @param document - Document URL or file_id
|
|
255
|
+
* @param options
|
|
256
|
+
*/
|
|
257
|
+
async sendDocument(botToken, chatId, document, options = {}) {
|
|
258
|
+
return this._api("sendDocument", botToken, {
|
|
259
|
+
chat_id: chatId,
|
|
260
|
+
document,
|
|
261
|
+
caption: options.caption,
|
|
262
|
+
parse_mode: options.parseMode,
|
|
263
|
+
disable_notification: options.disableNotification
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Send a media group (album) of 2-10 photos/videos
|
|
268
|
+
* @param botToken
|
|
269
|
+
* @param chatId
|
|
270
|
+
* @param media
|
|
271
|
+
* @param options
|
|
272
|
+
*/
|
|
273
|
+
async sendMediaGroup(botToken, chatId, media, options = {}) {
|
|
274
|
+
if (!media || media.length < 2 || media.length > 10) throw new SocialError("telegram", "Media group requires 2-10 items");
|
|
275
|
+
return this._api("sendMediaGroup", botToken, {
|
|
276
|
+
chat_id: chatId,
|
|
277
|
+
media,
|
|
278
|
+
disable_notification: options.disableNotification
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Pin a message in a chat
|
|
283
|
+
* @param botToken
|
|
284
|
+
* @param chatId
|
|
285
|
+
* @param messageId
|
|
286
|
+
* @param options
|
|
287
|
+
*/
|
|
288
|
+
async pinMessage(botToken, chatId, messageId, options = {}) {
|
|
289
|
+
return this._api("pinChatMessage", botToken, {
|
|
290
|
+
chat_id: chatId,
|
|
291
|
+
message_id: messageId,
|
|
292
|
+
disable_notification: options.disableNotification
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Delete a message
|
|
297
|
+
* @param botToken
|
|
298
|
+
* @param chatId
|
|
299
|
+
* @param messageId
|
|
300
|
+
*/
|
|
301
|
+
async deleteMessage(botToken, chatId, messageId) {
|
|
302
|
+
return this._api("deleteMessage", botToken, {
|
|
303
|
+
chat_id: chatId,
|
|
304
|
+
message_id: messageId
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Edit a text message
|
|
309
|
+
* @param botToken
|
|
310
|
+
* @param chatId
|
|
311
|
+
* @param messageId
|
|
312
|
+
* @param text - New message text
|
|
313
|
+
* @param options
|
|
314
|
+
*/
|
|
315
|
+
async editMessage(botToken, chatId, messageId, text, options = {}) {
|
|
316
|
+
return this._api("editMessageText", botToken, {
|
|
317
|
+
chat_id: chatId,
|
|
318
|
+
message_id: messageId,
|
|
319
|
+
text,
|
|
320
|
+
parse_mode: options.parseMode,
|
|
321
|
+
link_preview_options: options.disableWebPagePreview ? { is_disabled: true } : void 0
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
async getChatAdministrators(botToken, chatId) {
|
|
325
|
+
return this._api("getChatAdministrators", botToken, { chat_id: chatId });
|
|
326
|
+
}
|
|
327
|
+
async getChatMember(botToken, chatId, userId) {
|
|
328
|
+
return this._api("getChatMember", botToken, {
|
|
329
|
+
chat_id: chatId,
|
|
330
|
+
user_id: userId
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
async leaveChat(botToken, chatId) {
|
|
334
|
+
return this._api("leaveChat", botToken, { chat_id: chatId });
|
|
335
|
+
}
|
|
336
|
+
async setChatTitle(botToken, chatId, title) {
|
|
337
|
+
return this._api("setChatTitle", botToken, {
|
|
338
|
+
chat_id: chatId,
|
|
339
|
+
title
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
async setChatDescription(botToken, chatId, description) {
|
|
343
|
+
return this._api("setChatDescription", botToken, {
|
|
344
|
+
chat_id: chatId,
|
|
345
|
+
description
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
async sendLocation(botToken, chatId, latitude, longitude, options = {}) {
|
|
349
|
+
return this._api("sendLocation", botToken, {
|
|
350
|
+
chat_id: chatId,
|
|
351
|
+
latitude,
|
|
352
|
+
longitude,
|
|
353
|
+
horizontal_accuracy: options.horizontalAccuracy,
|
|
354
|
+
live_period: options.livePeriod,
|
|
355
|
+
heading: options.heading,
|
|
356
|
+
proximity_alert_radius: options.proximityAlertRadius,
|
|
357
|
+
disable_notification: options.disableNotification,
|
|
358
|
+
protect_content: options.protectContent,
|
|
359
|
+
reply_parameters: options.replyToMessageId !== void 0 ? { message_id: options.replyToMessageId } : void 0
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async sendAudio(botToken, chatId, audio, options = {}) {
|
|
363
|
+
return this._api("sendAudio", botToken, {
|
|
364
|
+
chat_id: chatId,
|
|
365
|
+
audio,
|
|
366
|
+
caption: options.caption,
|
|
367
|
+
parse_mode: options.parseMode,
|
|
368
|
+
duration: options.duration,
|
|
369
|
+
performer: options.performer,
|
|
370
|
+
title: options.title,
|
|
371
|
+
thumbnail: options.thumbnail,
|
|
372
|
+
disable_notification: options.disableNotification,
|
|
373
|
+
protect_content: options.protectContent
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
async sendSticker(botToken, chatId, sticker, options = {}) {
|
|
377
|
+
return this._api("sendSticker", botToken, {
|
|
378
|
+
chat_id: chatId,
|
|
379
|
+
sticker,
|
|
380
|
+
emoji: options.emoji,
|
|
381
|
+
disable_notification: options.disableNotification,
|
|
382
|
+
protect_content: options.protectContent,
|
|
383
|
+
reply_parameters: options.replyToMessageId !== void 0 ? { message_id: options.replyToMessageId } : void 0
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
async sendChatAction(botToken, chatId, action) {
|
|
387
|
+
return this._api("sendChatAction", botToken, {
|
|
388
|
+
chat_id: chatId,
|
|
389
|
+
action
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
async forwardMessage(botToken, chatId, fromChatId, messageId, options = {}) {
|
|
393
|
+
return this._api("forwardMessage", botToken, {
|
|
394
|
+
chat_id: chatId,
|
|
395
|
+
from_chat_id: fromChatId,
|
|
396
|
+
message_id: messageId,
|
|
397
|
+
disable_notification: options.disableNotification,
|
|
398
|
+
protect_content: options.protectContent
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
async copyMessage(botToken, chatId, fromChatId, messageId, options = {}) {
|
|
402
|
+
return this._api("copyMessage", botToken, {
|
|
403
|
+
chat_id: chatId,
|
|
404
|
+
from_chat_id: fromChatId,
|
|
405
|
+
message_id: messageId,
|
|
406
|
+
caption: options.caption,
|
|
407
|
+
parse_mode: options.parseMode,
|
|
408
|
+
disable_notification: options.disableNotification,
|
|
409
|
+
protect_content: options.protectContent
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
async answerCallbackQuery(botToken, callbackQueryId, options = {}) {
|
|
413
|
+
return this._api("answerCallbackQuery", botToken, {
|
|
414
|
+
callback_query_id: callbackQueryId,
|
|
415
|
+
text: options.text,
|
|
416
|
+
show_alert: options.showAlert,
|
|
417
|
+
url: options.url,
|
|
418
|
+
cache_time: options.cacheTime
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Upload a video to a Telegram channel/chat
|
|
423
|
+
* Telegram supports video by URL (up to 50MB for URL, 2GB for binary upload via multipart).
|
|
424
|
+
*
|
|
425
|
+
* @param params - Upload parameters
|
|
426
|
+
*/
|
|
427
|
+
async uploadVideo(params) {
|
|
428
|
+
const { videoUrl, title = "", description = "", tokens, onProgress } = params;
|
|
429
|
+
const { botToken, chatId } = tokens;
|
|
430
|
+
if (!chatId) throw new SocialError("telegram", "Chat ID is required to post. Set it in credential config.");
|
|
431
|
+
if (!videoUrl) throw new SocialError("telegram", "Video URL is required. Telegram accepts public URLs up to 50MB.");
|
|
432
|
+
const caption = [title, description].filter(Boolean).join("\n\n").substring(0, 1024);
|
|
433
|
+
if (onProgress) onProgress(10);
|
|
434
|
+
const message = await this.sendVideo(botToken, chatId, videoUrl, {
|
|
435
|
+
caption,
|
|
436
|
+
parseMode: "HTML",
|
|
437
|
+
supportsStreaming: true
|
|
438
|
+
});
|
|
439
|
+
if (onProgress) onProgress(100);
|
|
440
|
+
return {
|
|
441
|
+
platformVideoId: String(message.message_id),
|
|
442
|
+
platformUrl: chatId.toString().startsWith("@") ? `https://t.me/${chatId.replace("@", "")}/${message.message_id}` : null,
|
|
443
|
+
status: "published",
|
|
444
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
445
|
+
metadata: {
|
|
446
|
+
chatId,
|
|
447
|
+
messageId: message.message_id,
|
|
448
|
+
caption
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
getCredentialZodSchema() {
|
|
453
|
+
return TelegramCredentialsSchema;
|
|
454
|
+
}
|
|
455
|
+
getCredentialSchema() {
|
|
456
|
+
return [{
|
|
457
|
+
name: "botToken",
|
|
458
|
+
displayName: "Bot Token",
|
|
459
|
+
type: "password",
|
|
460
|
+
required: true,
|
|
461
|
+
description: "Bot token from @BotFather (format: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)"
|
|
462
|
+
}, {
|
|
463
|
+
name: "chatId",
|
|
464
|
+
displayName: "Default Chat/Channel ID",
|
|
465
|
+
type: "text",
|
|
466
|
+
required: false,
|
|
467
|
+
description: "Channel username (@channel) or numeric ID (-100xxx). The bot must be admin of this channel."
|
|
468
|
+
}];
|
|
469
|
+
}
|
|
470
|
+
getMetadata() {
|
|
471
|
+
return {
|
|
472
|
+
name: this.name,
|
|
473
|
+
displayName: this.displayName,
|
|
474
|
+
authType: this.authType,
|
|
475
|
+
icon: "telegram",
|
|
476
|
+
brandColor: "#26A5E4",
|
|
477
|
+
description: "Post to Telegram channels, groups, and chats via Bot API",
|
|
478
|
+
scopes: [],
|
|
479
|
+
scopeDescriptions: {},
|
|
480
|
+
supportsScheduling: false,
|
|
481
|
+
supportsEnvironment: false,
|
|
482
|
+
setupGuide: [
|
|
483
|
+
{
|
|
484
|
+
step: 1,
|
|
485
|
+
title: "Open @BotFather",
|
|
486
|
+
description: "On Telegram, search for @BotFather and start a chat"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
step: 2,
|
|
490
|
+
title: "Create a Bot",
|
|
491
|
+
description: "Send /newbot, choose a display name and username (must end in \"bot\")"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
step: 3,
|
|
495
|
+
title: "Copy Bot Token",
|
|
496
|
+
description: "BotFather will give you a token like 123456:ABC-DEF... — paste it above"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
step: 4,
|
|
500
|
+
title: "Add Bot to Channel",
|
|
501
|
+
description: "Open your channel/group → Settings → Admins → Add the bot as admin with \"Post Messages\" permission"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
step: 5,
|
|
505
|
+
title: "Get Channel ID",
|
|
506
|
+
description: "For public channels: use @channelname. For private: forward a channel message to @userinfobot to get the numeric ID (starts with -100)"
|
|
507
|
+
}
|
|
508
|
+
],
|
|
509
|
+
redirectUriPattern: null,
|
|
510
|
+
credentialSchema: this.getCredentialSchema()
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
export { TelegramProvider };
|
|
517
|
+
//# sourceMappingURL=telegram.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.mjs","names":[],"sources":["../../src/providers/telegram/index.ts"],"sourcesContent":["/**\n * Telegram Platform Provider\n * ==========================\n * Telegram Bot API integration for channels, groups, and direct messages.\n *\n * Uses token-based auth (Bot Token from @BotFather) — NOT OAuth.\n * The bot must be added as admin to the target channel/group.\n *\n * Key endpoints (Telegram Bot API):\n * - GET /getMe — verify bot identity\n * - POST /sendMessage — send text message\n * - POST /sendPhoto — send photo with caption\n * - POST /sendVideo — send video with caption\n * - POST /sendDocument — send file attachment\n * - POST /sendMediaGroup — send album (2-10 photos/videos)\n * - POST /getChat — get chat/channel info\n * - POST /getChatMemberCount — get subscriber count\n * - POST /pinChatMessage — pin a message\n * - POST /deleteMessage — delete a message\n *\n * @see https://core.telegram.org/bots/api\n */\n\nimport { PlatformProvider } from '../../base.js';\nimport type { CredentialField, ProviderMetadata, ProviderConfig } from '../../base.js';\nimport { SocialError } from '../../errors.js';\nimport { TelegramCredentialsSchema } from '../../schemas/telegram.js';\nimport { httpRequest } from '../../common/http.js';\nimport type { z } from 'zod';\nimport type {\n TelegramApiResponse,\n TelegramBotInfo,\n TelegramChat,\n TelegramMessage,\n TelegramCredentialData,\n SendMessageOptions,\n SendPhotoOptions,\n SendVideoOptions,\n SendDocumentOptions,\n MediaGroupItem,\n SendMediaGroupOptions,\n PinMessageOptions,\n EditMessageOptions,\n TelegramUploadVideoParams,\n TelegramUploadVideoResult,\n TelegramTestResult,\n DiscoveredChat,\n TelegramUser,\n ChatAdministrator,\n ChatMember,\n SendLocationOptions,\n SendAudioOptions,\n SendStickerOptions,\n ChatAction,\n ForwardMessageOptions,\n CopyMessageOptions,\n AnswerCallbackQueryOptions,\n} from './types.js';\n\nexport type {\n TelegramApiResponse,\n TelegramBotInfo,\n TelegramChat,\n TelegramMessage,\n TelegramCredentialData,\n TelegramParseMode,\n SendMessageOptions,\n SendPhotoOptions,\n SendVideoOptions,\n SendDocumentOptions,\n MediaGroupItem,\n SendMediaGroupOptions,\n PinMessageOptions,\n EditMessageOptions,\n TelegramUploadVideoParams,\n TelegramUploadVideoResult,\n TelegramTestResult,\n DiscoveredChat,\n TelegramUser,\n ChatAdministrator,\n ChatMember,\n SendLocationOptions,\n SendAudioOptions,\n SendStickerOptions,\n ChatAction,\n ForwardMessageOptions,\n CopyMessageOptions,\n AnswerCallbackQueryOptions,\n} from './types.js';\n\nconst BOT_API_BASE = 'https://api.telegram.org/bot';\n\n/**\n * Human-friendly explanations for common Telegram Bot API error descriptions.\n * @see https://core.telegram.org/api/errors\n */\nconst ERROR_HINTS: Record<string, string> = {\n 'Unauthorized': 'Bot token is invalid or has been revoked. Generate a new token from @BotFather.',\n 'chat not found': 'The chat/channel ID is wrong or the bot has not been added to it. Add the bot as admin first.',\n 'bot is not a member': 'The bot is not a member of this chat/channel. Add it as an admin with \"Post Messages\" permission.',\n 'not enough rights': 'The bot lacks admin permissions. Promote it to admin with the required permissions (Post Messages, Edit Messages, etc.).',\n 'CHAT_WRITE_FORBIDDEN': 'The bot cannot write to this chat. Check that posting is allowed and the bot has \"Post Messages\" permission.',\n 'message to delete not found': 'The message has already been deleted or the ID is incorrect.',\n 'message is not modified': 'The new message content is identical to the current one.',\n 'too many requests': 'Rate limit hit. Telegram allows ~30 messages/second to different chats, 1 msg/second to same chat. Wait and retry.',\n 'Bad Request: wrong file_id': 'The file_id is invalid or expired. Re-upload the file.',\n 'group chat was upgraded to a supergroup': 'The group was converted to a supergroup. Use the new chat ID (starts with -100).',\n 'FLOOD_WAIT': 'Rate limit — wait the indicated number of seconds before retrying.',\n};\n\nexport class TelegramProvider extends PlatformProvider {\n constructor(config: ProviderConfig = {}) {\n super(config);\n this.name = 'telegram';\n this.displayName = 'Telegram';\n this.authType = 'token'; // No OAuth — user pastes Bot Token\n }\n\n // ─── API Helper ──────────────────────────────────────────────────────\n\n /**\n * Make a request to the Telegram Bot API\n * @private\n * @param method - Bot API method name (e.g. 'sendMessage')\n * @param botToken - Bot token from @BotFather\n * @param body - Request body (JSON or FormData for file uploads)\n * @returns API response result\n */\n async _api(method: string, botToken: string, body: Record<string, any> | FormData | null = null): Promise<any> {\n const url = `${BOT_API_BASE}${botToken}/${method}`;\n\n const isForm = body instanceof FormData;\n const result = await httpRequest<TelegramApiResponse>('telegram', {\n method: body ? 'POST' : 'GET',\n url,\n json: !isForm && body ? body : undefined,\n form: isForm ? body : undefined,\n timeout: 60_000,\n retry: { attempts: 2 },\n // Telegram returns 200 OK for failures with `ok: false` — the parser\n // below handles non-OK HTTP statuses; the post-processing below handles\n // application-level errors.\n parseError: (raw, status) => {\n if (raw && typeof raw === 'object') {\n const r = raw as Record<string, unknown>;\n const description = (r.description as string) || `Telegram API error (${status})`;\n const params = r.parameters as { retry_after?: number } | undefined;\n const hint = Object.entries(ERROR_HINTS).find(([key]) =>\n description.toLowerCase().includes(key.toLowerCase()),\n );\n return {\n message: description,\n errorCode: r.error_code as number | undefined,\n hint: hint ? hint[1] : null,\n retryAfter: params?.retry_after ?? null,\n };\n }\n return null;\n },\n });\n\n const data = result.data;\n if (!data.ok) {\n const description = data.description || `Telegram API error (${data.error_code})`;\n const hint = Object.entries(ERROR_HINTS).find(([key]) =>\n description.toLowerCase().includes(key.toLowerCase()),\n );\n throw new SocialError('telegram', description, {\n statusCode: data.error_code! >= 400 && data.error_code! < 500 ? data.error_code : 502,\n errorCode: data.error_code,\n hint: hint ? hint[1] : null,\n retryAfter: data.parameters?.retry_after || null,\n });\n }\n\n return data.result;\n }\n\n // ─── Account Info ────────────────────────────────────────────────────\n\n /**\n * Get bot identity and verify token validity\n */\n async getAccountInfo(accessToken: string): Promise<{ id: string; name: string; username: string; profileImage: null }> {\n const bot: TelegramBotInfo = await this._api('getMe', accessToken);\n return {\n id: String(bot.id),\n name: bot.first_name + (bot.last_name ? ` ${bot.last_name}` : ''),\n username: bot.username,\n profileImage: null, // Bot API doesn't return profile photo in getMe\n };\n }\n\n /**\n * Test credential validity\n */\n async testCredential(credentialData: TelegramCredentialData): Promise<TelegramTestResult> {\n try {\n const { botToken, chatId } = credentialData;\n\n if (!botToken) {\n return {\n status: 'Error',\n message: 'Bot Token is required. Get one from @BotFather on Telegram.',\n };\n }\n\n // Verify bot token\n const botInfo = await this.getAccountInfo(botToken);\n const result: TelegramTestResult = {\n status: 'OK',\n message: `Connected as @${botInfo.username}`,\n data: {\n channelId: botInfo.id,\n channelTitle: botInfo.name,\n username: botInfo.username,\n },\n };\n\n // Optionally verify chat access\n if (chatId) {\n try {\n const chat: TelegramChat = await this.getChat(botToken, chatId);\n result.data!.chatTitle = chat.title || chat.username || chatId;\n result.data!.chatType = chat.type;\n result.message += ` — channel \"${chat.title || chatId}\" accessible`;\n } catch (chatError: any) {\n result.status = 'Warning';\n const hint = chatError.hint || chatError.message || 'unknown error';\n result.message += ` — cannot access chat \"${chatId}\": ${hint}`;\n\n // Discover available chats from recent bot activity\n const discovered = await this.discoverChats(botToken);\n if (discovered.length > 0) {\n result.data!.availableChats = discovered;\n }\n }\n }\n\n return result;\n } catch (error: any) {\n return {\n status: 'Error',\n message: error.hint || error.message || 'Failed to validate Telegram credential',\n };\n }\n }\n\n // ─── Chat Operations ─────────────────────────────────────────────────\n\n /**\n * Get chat/channel information\n * @param botToken\n * @param chatId - Channel username (@channel) or numeric ID\n */\n async getChat(botToken: string, chatId: string | number): Promise<TelegramChat> {\n return this._api('getChat', botToken, { chat_id: chatId });\n }\n\n /**\n * Get subscriber/member count for a chat\n * @param botToken\n * @param chatId\n * @returns Member count\n */\n async getChatMemberCount(botToken: string, chatId: string | number): Promise<number> {\n return this._api('getChatMemberCount', botToken, { chat_id: chatId });\n }\n\n /**\n * Discover chats the bot has interacted with recently.\n * Uses getUpdates (only works when no webhook is active, falls back gracefully).\n */\n async discoverChats(botToken: string): Promise<DiscoveredChat[]> {\n try {\n // Check if a webhook is active — if so, getUpdates won't work\n const webhookInfo = await this._api('getWebhookInfo', botToken);\n if (webhookInfo?.url) return []; // webhook active, can't use getUpdates\n\n const updates = await this._api('getUpdates', botToken, { limit: 50 });\n const chats = new Map<number, { id: number; title: string; type: string }>();\n\n for (const update of updates || []) {\n const sources = [\n update.message?.chat,\n update.channel_post?.chat,\n update.my_chat_member?.chat,\n update.edited_message?.chat,\n ];\n for (const chat of sources) {\n if (chat?.id) {\n chats.set(chat.id, {\n id: chat.id,\n title: chat.title || chat.username || chat.first_name || String(chat.id),\n type: chat.type,\n });\n }\n }\n }\n\n return Array.from(chats.values());\n } catch {\n return [];\n }\n }\n\n // ─── Messaging ────────────────────────────────────────────────────────\n\n /**\n * Send a text message\n * @param botToken\n * @param chatId\n * @param text - Message text (1-4096 characters)\n * @param options\n */\n async sendMessage(botToken: string, chatId: string | number, text: string, options: SendMessageOptions = {}): Promise<TelegramMessage> {\n return this._api('sendMessage', botToken, {\n chat_id: chatId,\n message_thread_id: options.messageThreadId,\n text,\n parse_mode: options.parseMode,\n disable_notification: options.disableNotification,\n reply_parameters: options.replyToMessageId !== undefined ? { message_id: options.replyToMessageId } : undefined,\n link_preview_options: options.disableWebPagePreview ? { is_disabled: true } : undefined,\n });\n }\n\n /**\n * Send a photo with optional caption\n * @param botToken\n * @param chatId\n * @param photo - Photo URL or file_id\n * @param options\n */\n async sendPhoto(botToken: string, chatId: string | number, photo: string, options: SendPhotoOptions = {}): Promise<TelegramMessage> {\n return this._api('sendPhoto', botToken, {\n chat_id: chatId,\n photo,\n caption: options.caption,\n parse_mode: options.parseMode,\n disable_notification: options.disableNotification,\n });\n }\n\n /**\n * Send a video with optional caption\n * @param botToken\n * @param chatId\n * @param video - Video URL or file_id\n * @param options\n */\n async sendVideo(botToken: string, chatId: string | number, video: string, options: SendVideoOptions = {}): Promise<TelegramMessage> {\n return this._api('sendVideo', botToken, {\n chat_id: chatId,\n video,\n caption: options.caption,\n parse_mode: options.parseMode,\n duration: options.duration,\n width: options.width,\n height: options.height,\n supports_streaming: options.supportsStreaming,\n disable_notification: options.disableNotification,\n });\n }\n\n /**\n * Send a document/file\n * @param botToken\n * @param chatId\n * @param document - Document URL or file_id\n * @param options\n */\n async sendDocument(botToken: string, chatId: string | number, document: string, options: SendDocumentOptions = {}): Promise<TelegramMessage> {\n return this._api('sendDocument', botToken, {\n chat_id: chatId,\n document,\n caption: options.caption,\n parse_mode: options.parseMode,\n disable_notification: options.disableNotification,\n });\n }\n\n /**\n * Send a media group (album) of 2-10 photos/videos\n * @param botToken\n * @param chatId\n * @param media\n * @param options\n */\n async sendMediaGroup(botToken: string, chatId: string | number, media: MediaGroupItem[], options: SendMediaGroupOptions = {}): Promise<TelegramMessage[]> {\n if (!media || media.length < 2 || media.length > 10) {\n throw new SocialError('telegram', 'Media group requires 2-10 items');\n }\n\n return this._api('sendMediaGroup', botToken, {\n chat_id: chatId,\n media,\n disable_notification: options.disableNotification,\n });\n }\n\n // ─── Message Management ───────────────────────────────────────────────\n\n /**\n * Pin a message in a chat\n * @param botToken\n * @param chatId\n * @param messageId\n * @param options\n */\n async pinMessage(botToken: string, chatId: string | number, messageId: number, options: PinMessageOptions = {}): Promise<true> {\n return this._api('pinChatMessage', botToken, {\n chat_id: chatId,\n message_id: messageId,\n disable_notification: options.disableNotification,\n });\n }\n\n /**\n * Delete a message\n * @param botToken\n * @param chatId\n * @param messageId\n */\n async deleteMessage(botToken: string, chatId: string | number, messageId: number): Promise<true> {\n return this._api('deleteMessage', botToken, {\n chat_id: chatId,\n message_id: messageId,\n });\n }\n\n /**\n * Edit a text message\n * @param botToken\n * @param chatId\n * @param messageId\n * @param text - New message text\n * @param options\n */\n async editMessage(botToken: string, chatId: string | number, messageId: number, text: string, options: EditMessageOptions = {}): Promise<TelegramMessage> {\n return this._api('editMessageText', botToken, {\n chat_id: chatId,\n message_id: messageId,\n text,\n parse_mode: options.parseMode,\n link_preview_options: options.disableWebPagePreview ? { is_disabled: true } : undefined,\n });\n }\n\n // ─── Chat Management ────────────────────────────────────────────────\n\n async getChatAdministrators(botToken: string, chatId: string | number): Promise<ChatAdministrator[]> {\n return this._api('getChatAdministrators', botToken, { chat_id: chatId });\n }\n\n async getChatMember(botToken: string, chatId: string | number, userId: number): Promise<ChatMember> {\n return this._api('getChatMember', botToken, { chat_id: chatId, user_id: userId });\n }\n\n async leaveChat(botToken: string, chatId: string | number): Promise<true> {\n return this._api('leaveChat', botToken, { chat_id: chatId });\n }\n\n async setChatTitle(botToken: string, chatId: string | number, title: string): Promise<true> {\n return this._api('setChatTitle', botToken, { chat_id: chatId, title });\n }\n\n async setChatDescription(botToken: string, chatId: string | number, description: string): Promise<true> {\n return this._api('setChatDescription', botToken, { chat_id: chatId, description });\n }\n\n // ─── Additional Message Types ──────────────────────────────────────\n\n async sendLocation(botToken: string, chatId: string | number, latitude: number, longitude: number, options: SendLocationOptions = {}): Promise<TelegramMessage> {\n return this._api('sendLocation', botToken, {\n chat_id: chatId,\n latitude,\n longitude,\n horizontal_accuracy: options.horizontalAccuracy,\n live_period: options.livePeriod,\n heading: options.heading,\n proximity_alert_radius: options.proximityAlertRadius,\n disable_notification: options.disableNotification,\n protect_content: options.protectContent,\n reply_parameters: options.replyToMessageId !== undefined ? { message_id: options.replyToMessageId } : undefined,\n });\n }\n\n async sendAudio(botToken: string, chatId: string | number, audio: string, options: SendAudioOptions = {}): Promise<TelegramMessage> {\n return this._api('sendAudio', botToken, {\n chat_id: chatId,\n audio,\n caption: options.caption,\n parse_mode: options.parseMode,\n duration: options.duration,\n performer: options.performer,\n title: options.title,\n thumbnail: options.thumbnail,\n disable_notification: options.disableNotification,\n protect_content: options.protectContent,\n });\n }\n\n async sendSticker(botToken: string, chatId: string | number, sticker: string, options: SendStickerOptions = {}): Promise<TelegramMessage> {\n return this._api('sendSticker', botToken, {\n chat_id: chatId,\n sticker,\n emoji: options.emoji,\n disable_notification: options.disableNotification,\n protect_content: options.protectContent,\n reply_parameters: options.replyToMessageId !== undefined ? { message_id: options.replyToMessageId } : undefined,\n });\n }\n\n async sendChatAction(botToken: string, chatId: string | number, action: ChatAction): Promise<true> {\n return this._api('sendChatAction', botToken, { chat_id: chatId, action });\n }\n\n // ─── Message Forwarding & Copying ──────────────────────────────────\n\n async forwardMessage(botToken: string, chatId: string | number, fromChatId: string | number, messageId: number, options: ForwardMessageOptions = {}): Promise<TelegramMessage> {\n return this._api('forwardMessage', botToken, {\n chat_id: chatId,\n from_chat_id: fromChatId,\n message_id: messageId,\n disable_notification: options.disableNotification,\n protect_content: options.protectContent,\n });\n }\n\n async copyMessage(botToken: string, chatId: string | number, fromChatId: string | number, messageId: number, options: CopyMessageOptions = {}): Promise<{ message_id: number }> {\n return this._api('copyMessage', botToken, {\n chat_id: chatId,\n from_chat_id: fromChatId,\n message_id: messageId,\n caption: options.caption,\n parse_mode: options.parseMode,\n disable_notification: options.disableNotification,\n protect_content: options.protectContent,\n });\n }\n\n // ─── Callback Queries ──────────────────────────────────────────────\n\n async answerCallbackQuery(botToken: string, callbackQueryId: string, options: AnswerCallbackQueryOptions = {}): Promise<true> {\n return this._api('answerCallbackQuery', botToken, {\n callback_query_id: callbackQueryId,\n text: options.text,\n show_alert: options.showAlert,\n url: options.url,\n cache_time: options.cacheTime,\n });\n }\n\n // ─── Upload (Video to Channel) ────────────────────────────────────────\n\n /**\n * Upload a video to a Telegram channel/chat\n * Telegram supports video by URL (up to 50MB for URL, 2GB for binary upload via multipart).\n *\n * @param params - Upload parameters\n */\n async uploadVideo(params: TelegramUploadVideoParams): Promise<TelegramUploadVideoResult> {\n const {\n videoUrl,\n title = '',\n description = '',\n tokens,\n onProgress,\n } = params;\n\n const { botToken, chatId } = tokens;\n\n if (!chatId) {\n throw new SocialError('telegram', 'Chat ID is required to post. Set it in credential config.');\n }\n\n if (!videoUrl) {\n throw new SocialError('telegram', 'Video URL is required. Telegram accepts public URLs up to 50MB.');\n }\n\n const caption = [title, description].filter(Boolean).join('\\n\\n').substring(0, 1024);\n\n if (onProgress) onProgress(10);\n\n const message: TelegramMessage = await this.sendVideo(botToken, chatId, videoUrl, {\n caption,\n parseMode: 'HTML',\n supportsStreaming: true,\n });\n\n if (onProgress) onProgress(100);\n\n return {\n platformVideoId: String(message.message_id),\n platformUrl: chatId.toString().startsWith('@')\n ? `https://t.me/${chatId.replace('@', '')}/${message.message_id}`\n : null,\n status: 'published',\n uploadedAt: new Date(),\n metadata: {\n chatId,\n messageId: message.message_id,\n caption,\n },\n };\n }\n\n // ─── Schema & Metadata ────────────────────────────────────────────────\n\n getCredentialZodSchema(): z.ZodType {\n return TelegramCredentialsSchema;\n }\n\n getCredentialSchema(): CredentialField[] {\n return [\n {\n name: 'botToken',\n displayName: 'Bot Token',\n type: 'password',\n required: true,\n description: 'Bot token from @BotFather (format: 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11)',\n },\n {\n name: 'chatId',\n displayName: 'Default Chat/Channel ID',\n type: 'text',\n required: false,\n description: 'Channel username (@channel) or numeric ID (-100xxx). The bot must be admin of this channel.',\n },\n ];\n }\n\n getMetadata(): ProviderMetadata {\n return {\n name: this.name,\n displayName: this.displayName,\n authType: this.authType,\n icon: 'telegram',\n brandColor: '#26A5E4',\n description: 'Post to Telegram channels, groups, and chats via Bot API',\n scopes: [],\n scopeDescriptions: {},\n supportsScheduling: false,\n supportsEnvironment: false,\n setupGuide: [\n { step: 1, title: 'Open @BotFather', description: 'On Telegram, search for @BotFather and start a chat' },\n { step: 2, title: 'Create a Bot', description: 'Send /newbot, choose a display name and username (must end in \"bot\")' },\n { step: 3, title: 'Copy Bot Token', description: 'BotFather will give you a token like 123456:ABC-DEF... — paste it above' },\n { step: 4, title: 'Add Bot to Channel', description: 'Open your channel/group → Settings → Admins → Add the bot as admin with \"Post Messages\" permission' },\n { step: 5, title: 'Get Channel ID', description: 'For public channels: use @channelname. For private: forward a channel message to @userinfobot to get the numeric ID (starts with -100)' },\n ],\n redirectUriPattern: null, // No OAuth redirect needed\n credentialSchema: this.getCredentialSchema(),\n };\n }\n}\n"],"mappings":"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"}
|