@overpod/mcp-telegram 1.32.0 → 1.34.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 +29 -0
- package/dist/telegram-client.d.ts +120 -0
- package/dist/telegram-client.js +285 -0
- package/dist/tools/folders.d.ts +3 -0
- package/dist/tools/folders.js +239 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/stars.js +178 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.34.0] — 2026-04-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
**Star Gifts (7 new tools, opt-in `MCP_TELEGRAM_ENABLE_STARS=1`):**
|
|
13
|
+
- **telegram-get-available-star-gifts** — List all available Star Gift catalog items: gift ID, cost in Stars, conversion value, limited-edition availability, and upgrade cost
|
|
14
|
+
- **telegram-get-saved-star-gifts** — List Star Gifts received by a user/chat. Supports pagination (`offset`/`nextOffset`), filter flags (`excludeUnsaved`, `excludeSaved`, `excludeUnlimited`, `excludeLimited`, `excludeUnique`), and `sortByValue`. Returns kind (regular/unique), from-peer, date, and upgrade eligibility
|
|
15
|
+
- **telegram-save-star-gift** — Show or hide a received gift on your profile. For personal gifts pass `msgId`; for chat/channel gifts pass `chatId` + `savedId`. Set `unsave=true` to hide, omit to show
|
|
16
|
+
- **telegram-convert-star-gift** — Convert a received gift into Stars (non-reversible, removes gift from profile). Same addressing as save: `msgId` for personal, `chatId`+`savedId` for chat gifts
|
|
17
|
+
- **telegram-get-stars-topup-options** — List available Stars top-up tiers with star count, currency, price amount (smallest currency units), and extended/standard tier flag
|
|
18
|
+
- **telegram-get-stars-subscriptions** — List active Stars subscriptions for a peer (`me` for self). Returns subscription ID, peer, until date, billing period (seconds), price in Stars, canceled status, and title. Pagination via `offset`
|
|
19
|
+
- **telegram-change-stars-subscription** — Cancel or restore a Stars subscription by ID. `canceled=true` to cancel before next renewal, `canceled=false` to restore
|
|
20
|
+
|
|
21
|
+
## [1.33.0] — 2026-04-24
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
**Folder management (6 new tools):**
|
|
26
|
+
- **telegram-create-folder** — Create a new chat folder. Accepts `title` (max 12 chars), optional `emoticon`, type flags (`contacts`, `nonContacts`, `groups`, `broadcasts`, `bots`), filter flags (`excludeMuted`, `excludeRead`, `excludeArchived`), and peer lists (`includePeers`, `excludePeers`, `pinnedPeers` max 5). Auto-assigns the next available folder ID ≥ 2. Returns the new ID
|
|
27
|
+
- **telegram-edit-folder** — Edit an existing folder by `id`. Only pass fields to change — omitted fields preserve current values (fetches current state first via `messages.GetDialogFilters`)
|
|
28
|
+
- **telegram-delete-folder** — Delete a chat folder by ID. Chats remain in All Chats. System folders (0 = All Chats, 1 = Archive) cannot be deleted. Uses `messages.UpdateDialogFilter` without a filter argument
|
|
29
|
+
- **telegram-reorder-folders** — Set the display order of folders by passing an ordered array of folder IDs. Uses `messages.UpdateDialogFiltersOrder`
|
|
30
|
+
- **telegram-get-suggested-folders** — Fetch Telegram's server-side folder suggestions based on your chat list (e.g. "Unread", "Work", "Personal"). Skips `DialogFilterDefault` entries that lack a title
|
|
31
|
+
- **telegram-toggle-folder-tags** — Enable or disable folder tag labels on messages in chat lists. Requires Telegram Premium. Uses `messages.ToggleDialogFilterTags`
|
|
32
|
+
|
|
33
|
+
**Global privacy (2 new tools):**
|
|
34
|
+
- **telegram-get-global-privacy-settings** — Read all five global privacy flags: `archiveAndMuteNewNoncontactPeers`, `keepArchivedUnmuted`, `keepArchivedFolders`, `hideReadMarks`, `newNoncontactPeersRequirePremium`. Returns JSON
|
|
35
|
+
- **telegram-set-global-privacy-settings** — Update any subset of global privacy flags. Fetches current settings first and merges only the fields you pass. `hideReadMarks` and `newNoncontactPeersRequirePremium` require Telegram Premium
|
|
36
|
+
|
|
8
37
|
## [1.32.0] — 2026-04-24
|
|
9
38
|
|
|
10
39
|
### Added
|
|
@@ -646,6 +646,57 @@ export declare class TelegramService {
|
|
|
646
646
|
includeCount: number;
|
|
647
647
|
}>>;
|
|
648
648
|
setAutoDelete(chatId: string, period: number): Promise<void>;
|
|
649
|
+
createFolder(opts: {
|
|
650
|
+
title: string;
|
|
651
|
+
emoticon?: string;
|
|
652
|
+
contacts?: boolean;
|
|
653
|
+
nonContacts?: boolean;
|
|
654
|
+
groups?: boolean;
|
|
655
|
+
broadcasts?: boolean;
|
|
656
|
+
bots?: boolean;
|
|
657
|
+
excludeMuted?: boolean;
|
|
658
|
+
excludeRead?: boolean;
|
|
659
|
+
excludeArchived?: boolean;
|
|
660
|
+
includePeers?: string[];
|
|
661
|
+
excludePeers?: string[];
|
|
662
|
+
pinnedPeers?: string[];
|
|
663
|
+
}): Promise<number>;
|
|
664
|
+
editFolder(id: number, opts: {
|
|
665
|
+
title?: string;
|
|
666
|
+
emoticon?: string;
|
|
667
|
+
contacts?: boolean;
|
|
668
|
+
nonContacts?: boolean;
|
|
669
|
+
groups?: boolean;
|
|
670
|
+
broadcasts?: boolean;
|
|
671
|
+
bots?: boolean;
|
|
672
|
+
excludeMuted?: boolean;
|
|
673
|
+
excludeRead?: boolean;
|
|
674
|
+
excludeArchived?: boolean;
|
|
675
|
+
includePeers?: string[];
|
|
676
|
+
excludePeers?: string[];
|
|
677
|
+
pinnedPeers?: string[];
|
|
678
|
+
}): Promise<void>;
|
|
679
|
+
deleteFolder(id: number): Promise<void>;
|
|
680
|
+
reorderFolders(ids: number[]): Promise<void>;
|
|
681
|
+
getSuggestedFolders(): Promise<Array<{
|
|
682
|
+
title: string;
|
|
683
|
+
emoticon?: string;
|
|
684
|
+
}>>;
|
|
685
|
+
toggleDialogFilterTags(enabled: boolean): Promise<void>;
|
|
686
|
+
getGlobalPrivacySettings(): Promise<{
|
|
687
|
+
archiveAndMuteNewNoncontactPeers: boolean;
|
|
688
|
+
keepArchivedUnmuted: boolean;
|
|
689
|
+
keepArchivedFolders: boolean;
|
|
690
|
+
hideReadMarks: boolean;
|
|
691
|
+
newNoncontactPeersRequirePremium: boolean;
|
|
692
|
+
}>;
|
|
693
|
+
setGlobalPrivacySettings(opts: {
|
|
694
|
+
archiveAndMuteNewNoncontactPeers?: boolean;
|
|
695
|
+
keepArchivedUnmuted?: boolean;
|
|
696
|
+
keepArchivedFolders?: boolean;
|
|
697
|
+
hideReadMarks?: boolean;
|
|
698
|
+
newNoncontactPeersRequirePremium?: boolean;
|
|
699
|
+
}): Promise<void>;
|
|
649
700
|
getActiveSessions(): Promise<Array<{
|
|
650
701
|
hash: string;
|
|
651
702
|
device: string;
|
|
@@ -850,6 +901,75 @@ export declare class TelegramService {
|
|
|
850
901
|
offset?: string;
|
|
851
902
|
limit?: number;
|
|
852
903
|
}): Promise<StarsStatusSummary>;
|
|
904
|
+
getAvailableStarGifts(): Promise<Array<{
|
|
905
|
+
id: string;
|
|
906
|
+
stars: string;
|
|
907
|
+
convertStars: string;
|
|
908
|
+
limited: boolean;
|
|
909
|
+
availabilityRemains?: number;
|
|
910
|
+
availabilityTotal?: number;
|
|
911
|
+
upgradeStars?: string;
|
|
912
|
+
}>>;
|
|
913
|
+
getSavedStarGifts(chatId: string, opts?: {
|
|
914
|
+
limit?: number;
|
|
915
|
+
offset?: string;
|
|
916
|
+
excludeUnsaved?: boolean;
|
|
917
|
+
excludeSaved?: boolean;
|
|
918
|
+
excludeUnlimited?: boolean;
|
|
919
|
+
excludeLimited?: boolean;
|
|
920
|
+
excludeUnique?: boolean;
|
|
921
|
+
sortByValue?: boolean;
|
|
922
|
+
}): Promise<{
|
|
923
|
+
count: number;
|
|
924
|
+
nextOffset?: string;
|
|
925
|
+
gifts: Array<{
|
|
926
|
+
giftId: string;
|
|
927
|
+
giftKind: "regular" | "unique";
|
|
928
|
+
giftTitle?: string;
|
|
929
|
+
stars?: string;
|
|
930
|
+
convertStars?: string;
|
|
931
|
+
msgId?: number;
|
|
932
|
+
savedId?: string;
|
|
933
|
+
fromPeerId?: string;
|
|
934
|
+
date: number;
|
|
935
|
+
unsaved: boolean;
|
|
936
|
+
canUpgrade: boolean;
|
|
937
|
+
upgradeStars?: string;
|
|
938
|
+
}>;
|
|
939
|
+
}>;
|
|
940
|
+
saveStarGift(opts: {
|
|
941
|
+
msgId?: number;
|
|
942
|
+
chatId?: string;
|
|
943
|
+
savedId?: string;
|
|
944
|
+
unsave?: boolean;
|
|
945
|
+
}): Promise<void>;
|
|
946
|
+
convertStarGift(opts: {
|
|
947
|
+
msgId?: number;
|
|
948
|
+
chatId?: string;
|
|
949
|
+
savedId?: string;
|
|
950
|
+
}): Promise<void>;
|
|
951
|
+
getStarsTopupOptions(): Promise<Array<{
|
|
952
|
+
stars: string;
|
|
953
|
+
currency: string;
|
|
954
|
+
amount: string;
|
|
955
|
+
extended: boolean;
|
|
956
|
+
}>>;
|
|
957
|
+
getStarsSubscriptions(chatId: string, opts?: {
|
|
958
|
+
offset?: string;
|
|
959
|
+
missingBalance?: boolean;
|
|
960
|
+
}): Promise<{
|
|
961
|
+
subscriptions: Array<{
|
|
962
|
+
id: string;
|
|
963
|
+
peerId: string;
|
|
964
|
+
untilDate: number;
|
|
965
|
+
periodSeconds: number;
|
|
966
|
+
priceStars: string;
|
|
967
|
+
canceled: boolean;
|
|
968
|
+
title?: string;
|
|
969
|
+
}>;
|
|
970
|
+
nextOffset?: string;
|
|
971
|
+
}>;
|
|
972
|
+
changeStarsSubscription(chatId: string, subscriptionId: string, canceled: boolean): Promise<void>;
|
|
853
973
|
getQuickReplies(hash?: string): Promise<QuickRepliesSummary>;
|
|
854
974
|
getQuickReplyMessages(shortcutId: number, options?: {
|
|
855
975
|
ids?: number[];
|
package/dist/telegram-client.js
CHANGED
|
@@ -3089,6 +3089,145 @@ export class TelegramService {
|
|
|
3089
3089
|
const peer = await this.client.getInputEntity(resolved);
|
|
3090
3090
|
await this.client.invoke(new Api.messages.SetHistoryTTL({ peer, period }));
|
|
3091
3091
|
}
|
|
3092
|
+
// ─── Folder management (v1.33.0) ───────────────────────────────────────────
|
|
3093
|
+
async createFolder(opts) {
|
|
3094
|
+
if (!this.client || !this.connected)
|
|
3095
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3096
|
+
const client = this.client;
|
|
3097
|
+
const filtersResult = await client.invoke(new Api.messages.GetDialogFilters());
|
|
3098
|
+
const existing = "filters" in filtersResult ? filtersResult.filters : [];
|
|
3099
|
+
const usedIds = existing
|
|
3100
|
+
.filter((f) => "id" in f)
|
|
3101
|
+
.map((f) => f.id);
|
|
3102
|
+
let newId = 2;
|
|
3103
|
+
while (usedIds.includes(newId))
|
|
3104
|
+
newId++;
|
|
3105
|
+
const toInput = async (ids) => {
|
|
3106
|
+
const peers = [];
|
|
3107
|
+
for (const id of ids) {
|
|
3108
|
+
const resolved = await this.resolvePeer(id);
|
|
3109
|
+
peers.push(await client.getInputEntity(resolved));
|
|
3110
|
+
}
|
|
3111
|
+
return peers;
|
|
3112
|
+
};
|
|
3113
|
+
const includePeers = await toInput(opts.includePeers ?? []);
|
|
3114
|
+
const excludePeers = await toInput(opts.excludePeers ?? []);
|
|
3115
|
+
const pinnedPeers = await toInput(opts.pinnedPeers ?? []);
|
|
3116
|
+
await client.invoke(new Api.messages.UpdateDialogFilter({
|
|
3117
|
+
id: newId,
|
|
3118
|
+
filter: new Api.DialogFilter({
|
|
3119
|
+
id: newId,
|
|
3120
|
+
title: new Api.TextWithEntities({ text: opts.title, entities: [] }),
|
|
3121
|
+
emoticon: opts.emoticon,
|
|
3122
|
+
contacts: opts.contacts,
|
|
3123
|
+
nonContacts: opts.nonContacts,
|
|
3124
|
+
groups: opts.groups,
|
|
3125
|
+
broadcasts: opts.broadcasts,
|
|
3126
|
+
bots: opts.bots,
|
|
3127
|
+
excludeMuted: opts.excludeMuted,
|
|
3128
|
+
excludeRead: opts.excludeRead,
|
|
3129
|
+
excludeArchived: opts.excludeArchived,
|
|
3130
|
+
pinnedPeers,
|
|
3131
|
+
includePeers,
|
|
3132
|
+
excludePeers,
|
|
3133
|
+
}),
|
|
3134
|
+
}));
|
|
3135
|
+
return newId;
|
|
3136
|
+
}
|
|
3137
|
+
async editFolder(id, opts) {
|
|
3138
|
+
if (!this.client || !this.connected)
|
|
3139
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3140
|
+
const client = this.client;
|
|
3141
|
+
const filtersResult = await client.invoke(new Api.messages.GetDialogFilters());
|
|
3142
|
+
const existing = "filters" in filtersResult ? filtersResult.filters : [];
|
|
3143
|
+
const current = existing.find((f) => f instanceof Api.DialogFilter && f.id === id);
|
|
3144
|
+
if (!current)
|
|
3145
|
+
throw new Error(`Folder with id=${id} not found`);
|
|
3146
|
+
const toInput = async (ids) => {
|
|
3147
|
+
const peers = [];
|
|
3148
|
+
for (const id of ids) {
|
|
3149
|
+
const resolved = await this.resolvePeer(id);
|
|
3150
|
+
peers.push(await client.getInputEntity(resolved));
|
|
3151
|
+
}
|
|
3152
|
+
return peers;
|
|
3153
|
+
};
|
|
3154
|
+
const includePeers = opts.includePeers !== undefined ? await toInput(opts.includePeers) : current.includePeers;
|
|
3155
|
+
const excludePeers = opts.excludePeers !== undefined ? await toInput(opts.excludePeers) : current.excludePeers;
|
|
3156
|
+
const pinnedPeers = opts.pinnedPeers !== undefined ? await toInput(opts.pinnedPeers) : current.pinnedPeers;
|
|
3157
|
+
const titleText = opts.title !== undefined ? opts.title : typeof current.title === "string" ? current.title : current.title.text;
|
|
3158
|
+
await client.invoke(new Api.messages.UpdateDialogFilter({
|
|
3159
|
+
id,
|
|
3160
|
+
filter: new Api.DialogFilter({
|
|
3161
|
+
id,
|
|
3162
|
+
title: new Api.TextWithEntities({ text: titleText, entities: [] }),
|
|
3163
|
+
emoticon: opts.emoticon !== undefined ? opts.emoticon : current.emoticon,
|
|
3164
|
+
contacts: opts.contacts !== undefined ? opts.contacts : current.contacts,
|
|
3165
|
+
nonContacts: opts.nonContacts !== undefined ? opts.nonContacts : current.nonContacts,
|
|
3166
|
+
groups: opts.groups !== undefined ? opts.groups : current.groups,
|
|
3167
|
+
broadcasts: opts.broadcasts !== undefined ? opts.broadcasts : current.broadcasts,
|
|
3168
|
+
bots: opts.bots !== undefined ? opts.bots : current.bots,
|
|
3169
|
+
excludeMuted: opts.excludeMuted !== undefined ? opts.excludeMuted : current.excludeMuted,
|
|
3170
|
+
excludeRead: opts.excludeRead !== undefined ? opts.excludeRead : current.excludeRead,
|
|
3171
|
+
excludeArchived: opts.excludeArchived !== undefined ? opts.excludeArchived : current.excludeArchived,
|
|
3172
|
+
pinnedPeers,
|
|
3173
|
+
includePeers,
|
|
3174
|
+
excludePeers,
|
|
3175
|
+
}),
|
|
3176
|
+
}));
|
|
3177
|
+
}
|
|
3178
|
+
async deleteFolder(id) {
|
|
3179
|
+
if (!this.client || !this.connected)
|
|
3180
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3181
|
+
await this.client.invoke(new Api.messages.UpdateDialogFilter({ id }));
|
|
3182
|
+
}
|
|
3183
|
+
async reorderFolders(ids) {
|
|
3184
|
+
if (!this.client || !this.connected)
|
|
3185
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3186
|
+
await this.client.invoke(new Api.messages.UpdateDialogFiltersOrder({ order: ids }));
|
|
3187
|
+
}
|
|
3188
|
+
async getSuggestedFolders() {
|
|
3189
|
+
if (!this.client || !this.connected)
|
|
3190
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3191
|
+
const result = await this.client.invoke(new Api.messages.GetSuggestedDialogFilters());
|
|
3192
|
+
return result
|
|
3193
|
+
.filter((s) => s.filter instanceof Api.DialogFilter || s.filter instanceof Api.DialogFilterChatlist)
|
|
3194
|
+
.map((s) => ({
|
|
3195
|
+
title: typeof s.filter.title === "string" ? s.filter.title : s.filter.title.text,
|
|
3196
|
+
emoticon: s.filter.emoticon,
|
|
3197
|
+
}));
|
|
3198
|
+
}
|
|
3199
|
+
async toggleDialogFilterTags(enabled) {
|
|
3200
|
+
if (!this.client || !this.connected)
|
|
3201
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3202
|
+
await this.client.invoke(new Api.messages.ToggleDialogFilterTags({ enabled }));
|
|
3203
|
+
}
|
|
3204
|
+
// ─── Global privacy (v1.33.0) ──────────────────────────────────────────────
|
|
3205
|
+
async getGlobalPrivacySettings() {
|
|
3206
|
+
if (!this.client || !this.connected)
|
|
3207
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3208
|
+
const s = await this.client.invoke(new Api.account.GetGlobalPrivacySettings());
|
|
3209
|
+
return {
|
|
3210
|
+
archiveAndMuteNewNoncontactPeers: s.archiveAndMuteNewNoncontactPeers ?? false,
|
|
3211
|
+
keepArchivedUnmuted: s.keepArchivedUnmuted ?? false,
|
|
3212
|
+
keepArchivedFolders: s.keepArchivedFolders ?? false,
|
|
3213
|
+
hideReadMarks: s.hideReadMarks ?? false,
|
|
3214
|
+
newNoncontactPeersRequirePremium: s.newNoncontactPeersRequirePremium ?? false,
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
async setGlobalPrivacySettings(opts) {
|
|
3218
|
+
if (!this.client || !this.connected)
|
|
3219
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
3220
|
+
const current = await this.client.invoke(new Api.account.GetGlobalPrivacySettings());
|
|
3221
|
+
await this.client.invoke(new Api.account.SetGlobalPrivacySettings({
|
|
3222
|
+
settings: new Api.GlobalPrivacySettings({
|
|
3223
|
+
archiveAndMuteNewNoncontactPeers: opts.archiveAndMuteNewNoncontactPeers ?? current.archiveAndMuteNewNoncontactPeers,
|
|
3224
|
+
keepArchivedUnmuted: opts.keepArchivedUnmuted ?? current.keepArchivedUnmuted,
|
|
3225
|
+
keepArchivedFolders: opts.keepArchivedFolders ?? current.keepArchivedFolders,
|
|
3226
|
+
hideReadMarks: opts.hideReadMarks ?? current.hideReadMarks,
|
|
3227
|
+
newNoncontactPeersRequirePremium: opts.newNoncontactPeersRequirePremium ?? current.newNoncontactPeersRequirePremium,
|
|
3228
|
+
}),
|
|
3229
|
+
}));
|
|
3230
|
+
}
|
|
3092
3231
|
// ─── Account & privacy ─────────────────────────────────────────────────────
|
|
3093
3232
|
async getActiveSessions() {
|
|
3094
3233
|
if (!this.client || !this.connected)
|
|
@@ -4057,6 +4196,152 @@ export class TelegramService {
|
|
|
4057
4196
|
return summarizeStarsStatus(response);
|
|
4058
4197
|
}, `getStarsTransactions ${chatId}`);
|
|
4059
4198
|
}
|
|
4199
|
+
// ─── Star Gifts (v1.34.0) ──────────────────────────────────────────────────
|
|
4200
|
+
async getAvailableStarGifts() {
|
|
4201
|
+
if (!this.client || !this.connected)
|
|
4202
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4203
|
+
const result = await this.client.invoke(new Api.payments.GetStarGifts({ hash: 0 }));
|
|
4204
|
+
if (result.className === "payments.StarGiftsNotModified")
|
|
4205
|
+
return [];
|
|
4206
|
+
const gifts = result.className === "payments.StarGifts" ? result.gifts : [];
|
|
4207
|
+
return gifts
|
|
4208
|
+
.filter((g) => g instanceof Api.StarGift)
|
|
4209
|
+
.map((g) => ({
|
|
4210
|
+
id: g.id.toString(),
|
|
4211
|
+
stars: g.stars.toString(),
|
|
4212
|
+
convertStars: g.convertStars.toString(),
|
|
4213
|
+
limited: g.limited ?? false,
|
|
4214
|
+
availabilityRemains: g.availabilityRemains,
|
|
4215
|
+
availabilityTotal: g.availabilityTotal,
|
|
4216
|
+
upgradeStars: g.upgradeStars?.toString(),
|
|
4217
|
+
}));
|
|
4218
|
+
}
|
|
4219
|
+
async getSavedStarGifts(chatId, opts = {}) {
|
|
4220
|
+
if (!this.client || !this.connected)
|
|
4221
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4222
|
+
const client = this.client;
|
|
4223
|
+
const peer = await this.resolvePeer(chatId);
|
|
4224
|
+
const resolved = await client.getInputEntity(peer);
|
|
4225
|
+
const result = await client.invoke(new Api.payments.GetSavedStarGifts({
|
|
4226
|
+
peer: resolved,
|
|
4227
|
+
offset: opts.offset ?? "",
|
|
4228
|
+
limit: opts.limit ?? 20,
|
|
4229
|
+
excludeUnsaved: opts.excludeUnsaved,
|
|
4230
|
+
excludeSaved: opts.excludeSaved,
|
|
4231
|
+
excludeUnlimited: opts.excludeUnlimited,
|
|
4232
|
+
excludeLimited: opts.excludeLimited,
|
|
4233
|
+
excludeUnique: opts.excludeUnique,
|
|
4234
|
+
sortByValue: opts.sortByValue,
|
|
4235
|
+
}));
|
|
4236
|
+
const r = result;
|
|
4237
|
+
return {
|
|
4238
|
+
count: r.count,
|
|
4239
|
+
nextOffset: r.nextOffset,
|
|
4240
|
+
gifts: r.gifts.map((sg) => {
|
|
4241
|
+
const gift = sg.gift;
|
|
4242
|
+
if (gift instanceof Api.StarGift) {
|
|
4243
|
+
return {
|
|
4244
|
+
giftId: gift.id.toString(),
|
|
4245
|
+
giftKind: "regular",
|
|
4246
|
+
stars: gift.stars.toString(),
|
|
4247
|
+
convertStars: gift.convertStars.toString(),
|
|
4248
|
+
msgId: sg.msgId ?? undefined,
|
|
4249
|
+
savedId: sg.savedId?.toString(),
|
|
4250
|
+
fromPeerId: sg.fromId ? summarizePeer(sg.fromId).id : undefined,
|
|
4251
|
+
date: sg.date,
|
|
4252
|
+
unsaved: sg.unsaved ?? false,
|
|
4253
|
+
canUpgrade: sg.canUpgrade ?? false,
|
|
4254
|
+
upgradeStars: sg.upgradeStars?.toString(),
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4257
|
+
const u = gift;
|
|
4258
|
+
return {
|
|
4259
|
+
giftId: u.id.toString(),
|
|
4260
|
+
giftKind: "unique",
|
|
4261
|
+
giftTitle: u.title,
|
|
4262
|
+
msgId: sg.msgId ?? undefined,
|
|
4263
|
+
savedId: sg.savedId?.toString(),
|
|
4264
|
+
fromPeerId: sg.fromId ? summarizePeer(sg.fromId).id : undefined,
|
|
4265
|
+
date: sg.date,
|
|
4266
|
+
unsaved: sg.unsaved ?? false,
|
|
4267
|
+
canUpgrade: false,
|
|
4268
|
+
};
|
|
4269
|
+
}),
|
|
4270
|
+
};
|
|
4271
|
+
}
|
|
4272
|
+
async saveStarGift(opts) {
|
|
4273
|
+
if (!this.client || !this.connected)
|
|
4274
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4275
|
+
const client = this.client;
|
|
4276
|
+
let stargift;
|
|
4277
|
+
if (opts.msgId !== undefined) {
|
|
4278
|
+
stargift = new Api.InputSavedStarGiftUser({ msgId: opts.msgId });
|
|
4279
|
+
}
|
|
4280
|
+
else if (opts.chatId && opts.savedId) {
|
|
4281
|
+
const peer = await this.resolvePeer(opts.chatId);
|
|
4282
|
+
const inputPeer = await client.getInputEntity(peer);
|
|
4283
|
+
stargift = new Api.InputSavedStarGiftChat({ peer: inputPeer, savedId: bigInt(opts.savedId) });
|
|
4284
|
+
}
|
|
4285
|
+
else {
|
|
4286
|
+
throw new Error("Provide msgId (user gift) or both chatId and savedId (chat gift)");
|
|
4287
|
+
}
|
|
4288
|
+
await client.invoke(new Api.payments.SaveStarGift({ stargift, unsave: opts.unsave }));
|
|
4289
|
+
}
|
|
4290
|
+
async convertStarGift(opts) {
|
|
4291
|
+
if (!this.client || !this.connected)
|
|
4292
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4293
|
+
const client = this.client;
|
|
4294
|
+
let stargift;
|
|
4295
|
+
if (opts.msgId !== undefined) {
|
|
4296
|
+
stargift = new Api.InputSavedStarGiftUser({ msgId: opts.msgId });
|
|
4297
|
+
}
|
|
4298
|
+
else if (opts.chatId && opts.savedId) {
|
|
4299
|
+
const peer = await this.resolvePeer(opts.chatId);
|
|
4300
|
+
const inputPeer = await client.getInputEntity(peer);
|
|
4301
|
+
stargift = new Api.InputSavedStarGiftChat({ peer: inputPeer, savedId: bigInt(opts.savedId) });
|
|
4302
|
+
}
|
|
4303
|
+
else {
|
|
4304
|
+
throw new Error("Provide msgId (user gift) or both chatId and savedId (chat gift)");
|
|
4305
|
+
}
|
|
4306
|
+
await client.invoke(new Api.payments.ConvertStarGift({ stargift }));
|
|
4307
|
+
}
|
|
4308
|
+
async getStarsTopupOptions() {
|
|
4309
|
+
if (!this.client || !this.connected)
|
|
4310
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4311
|
+
const result = await this.client.invoke(new Api.payments.GetStarsTopupOptions());
|
|
4312
|
+
return result.map((o) => ({
|
|
4313
|
+
stars: o.stars.toString(),
|
|
4314
|
+
currency: o.currency,
|
|
4315
|
+
amount: o.amount.toString(),
|
|
4316
|
+
extended: o.extended ?? false,
|
|
4317
|
+
}));
|
|
4318
|
+
}
|
|
4319
|
+
async getStarsSubscriptions(chatId, opts = {}) {
|
|
4320
|
+
if (!this.client || !this.connected)
|
|
4321
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4322
|
+
const peer = await this.resolvePeer(chatId);
|
|
4323
|
+
const result = await this.client.invoke(new Api.payments.GetStarsSubscriptions({ peer, offset: opts.offset ?? "", missingBalance: opts.missingBalance }));
|
|
4324
|
+
const r = result;
|
|
4325
|
+
const subs = r.subscriptions ?? [];
|
|
4326
|
+
return {
|
|
4327
|
+
subscriptions: subs.map((s) => ({
|
|
4328
|
+
id: s.id,
|
|
4329
|
+
peerId: summarizePeer(s.peer).id,
|
|
4330
|
+
untilDate: s.untilDate,
|
|
4331
|
+
periodSeconds: s.pricing.period,
|
|
4332
|
+
priceStars: s.pricing.amount.toString(),
|
|
4333
|
+
canceled: s.canceled ?? false,
|
|
4334
|
+
title: s.title,
|
|
4335
|
+
})),
|
|
4336
|
+
nextOffset: r.subscriptionsNextOffset,
|
|
4337
|
+
};
|
|
4338
|
+
}
|
|
4339
|
+
async changeStarsSubscription(chatId, subscriptionId, canceled) {
|
|
4340
|
+
if (!this.client || !this.connected)
|
|
4341
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
4342
|
+
const peer = await this.resolvePeer(chatId);
|
|
4343
|
+
await this.client.invoke(new Api.payments.ChangeStarsSubscription({ peer, subscriptionId, canceled }));
|
|
4344
|
+
}
|
|
4060
4345
|
// ─── Quick replies ─────────────────────────────────────────────────────────
|
|
4061
4346
|
async getQuickReplies(hash) {
|
|
4062
4347
|
if (!this.client || !this.connected)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
|
|
3
|
+
export function registerFolderTools(server, telegram) {
|
|
4
|
+
server.registerTool("telegram-create-folder", {
|
|
5
|
+
description: "Create a new Telegram chat folder (filter). Returns the new folder ID. Pass type flags to auto-include entire categories, or list specific chats in includePeers. Emoticon must be a single emoji character.",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
title: z.string().min(1).max(12).describe("Folder name (max 12 chars)"),
|
|
8
|
+
emoticon: z.string().max(2).optional().describe("Single emoji icon for the folder"),
|
|
9
|
+
contacts: z.boolean().optional().describe("Include all contacts"),
|
|
10
|
+
nonContacts: z.boolean().optional().describe("Include all non-contacts"),
|
|
11
|
+
groups: z.boolean().optional().describe("Include all groups"),
|
|
12
|
+
broadcasts: z.boolean().optional().describe("Include all channels"),
|
|
13
|
+
bots: z.boolean().optional().describe("Include all bots"),
|
|
14
|
+
excludeMuted: z.boolean().optional().describe("Exclude muted chats"),
|
|
15
|
+
excludeRead: z.boolean().optional().describe("Exclude read chats"),
|
|
16
|
+
excludeArchived: z.boolean().optional().describe("Exclude archived chats"),
|
|
17
|
+
includePeers: z
|
|
18
|
+
.array(z.string())
|
|
19
|
+
.max(100)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Chat IDs/usernames to explicitly include (max 100)"),
|
|
22
|
+
excludePeers: z
|
|
23
|
+
.array(z.string())
|
|
24
|
+
.max(100)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Chat IDs/usernames to explicitly exclude (max 100)"),
|
|
27
|
+
pinnedPeers: z.array(z.string()).max(5).optional().describe("Chats to pin at top of this folder (max 5)"),
|
|
28
|
+
},
|
|
29
|
+
annotations: WRITE,
|
|
30
|
+
}, async ({ title, emoticon, contacts, nonContacts, groups, broadcasts, bots, excludeMuted, excludeRead, excludeArchived, includePeers, excludePeers, pinnedPeers, }) => {
|
|
31
|
+
const err = await requireConnection(telegram);
|
|
32
|
+
if (err)
|
|
33
|
+
return fail(new Error(err));
|
|
34
|
+
try {
|
|
35
|
+
const id = await telegram.createFolder({
|
|
36
|
+
title,
|
|
37
|
+
emoticon,
|
|
38
|
+
contacts,
|
|
39
|
+
nonContacts,
|
|
40
|
+
groups,
|
|
41
|
+
broadcasts,
|
|
42
|
+
bots,
|
|
43
|
+
excludeMuted,
|
|
44
|
+
excludeRead,
|
|
45
|
+
excludeArchived,
|
|
46
|
+
includePeers,
|
|
47
|
+
excludePeers,
|
|
48
|
+
pinnedPeers,
|
|
49
|
+
});
|
|
50
|
+
return ok(`Folder created: "${title}" [id=${id}]`);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return fail(e);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
server.registerTool("telegram-edit-folder", {
|
|
57
|
+
description: "Edit an existing Telegram chat folder by its ID (from telegram-get-chat-folders). Only pass fields you want to change — omitted fields keep their current values.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
id: z.number().int().min(2).describe("Folder ID (≥ 2; 0 = All Chats, 1 = Archive are system folders)"),
|
|
60
|
+
title: z.string().min(1).max(12).optional().describe("New folder name (max 12 chars)"),
|
|
61
|
+
emoticon: z.string().max(2).optional().describe("New emoji icon"),
|
|
62
|
+
contacts: z.boolean().optional(),
|
|
63
|
+
nonContacts: z.boolean().optional(),
|
|
64
|
+
groups: z.boolean().optional(),
|
|
65
|
+
broadcasts: z.boolean().optional(),
|
|
66
|
+
bots: z.boolean().optional(),
|
|
67
|
+
excludeMuted: z.boolean().optional(),
|
|
68
|
+
excludeRead: z.boolean().optional(),
|
|
69
|
+
excludeArchived: z.boolean().optional(),
|
|
70
|
+
includePeers: z.array(z.string()).max(100).optional().describe("Replace includePeers list entirely"),
|
|
71
|
+
excludePeers: z.array(z.string()).max(100).optional().describe("Replace excludePeers list entirely"),
|
|
72
|
+
pinnedPeers: z.array(z.string()).max(5).optional().describe("Replace pinnedPeers list entirely"),
|
|
73
|
+
},
|
|
74
|
+
annotations: WRITE,
|
|
75
|
+
}, async ({ id, title, emoticon, contacts, nonContacts, groups, broadcasts, bots, excludeMuted, excludeRead, excludeArchived, includePeers, excludePeers, pinnedPeers, }) => {
|
|
76
|
+
const err = await requireConnection(telegram);
|
|
77
|
+
if (err)
|
|
78
|
+
return fail(new Error(err));
|
|
79
|
+
try {
|
|
80
|
+
await telegram.editFolder(id, {
|
|
81
|
+
title,
|
|
82
|
+
emoticon,
|
|
83
|
+
contacts,
|
|
84
|
+
nonContacts,
|
|
85
|
+
groups,
|
|
86
|
+
broadcasts,
|
|
87
|
+
bots,
|
|
88
|
+
excludeMuted,
|
|
89
|
+
excludeRead,
|
|
90
|
+
excludeArchived,
|
|
91
|
+
includePeers,
|
|
92
|
+
excludePeers,
|
|
93
|
+
pinnedPeers,
|
|
94
|
+
});
|
|
95
|
+
return ok(`Folder ${id} updated`);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
return fail(e);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
server.registerTool("telegram-delete-folder", {
|
|
102
|
+
description: "Delete a Telegram chat folder by its ID. Chats inside the folder are not deleted — they remain in All Chats. System folders (0 = All Chats, 1 = Archive) cannot be deleted.",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
id: z.number().int().min(2).describe("Folder ID to delete (≥ 2)"),
|
|
105
|
+
},
|
|
106
|
+
annotations: WRITE,
|
|
107
|
+
}, async ({ id }) => {
|
|
108
|
+
const err = await requireConnection(telegram);
|
|
109
|
+
if (err)
|
|
110
|
+
return fail(new Error(err));
|
|
111
|
+
try {
|
|
112
|
+
await telegram.deleteFolder(id);
|
|
113
|
+
return ok(`Folder ${id} deleted`);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
return fail(e);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
server.registerTool("telegram-reorder-folders", {
|
|
120
|
+
description: "Reorder Telegram chat folders by specifying a new order of folder IDs. All existing custom folder IDs must be included.",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
order: z
|
|
123
|
+
.array(z.number().int().min(2))
|
|
124
|
+
.min(1)
|
|
125
|
+
.describe("Ordered list of folder IDs (≥ 2). Obtain IDs from telegram-get-chat-folders"),
|
|
126
|
+
},
|
|
127
|
+
annotations: WRITE,
|
|
128
|
+
}, async ({ order }) => {
|
|
129
|
+
const err = await requireConnection(telegram);
|
|
130
|
+
if (err)
|
|
131
|
+
return fail(new Error(err));
|
|
132
|
+
try {
|
|
133
|
+
await telegram.reorderFolders(order);
|
|
134
|
+
return ok(`Folders reordered: [${order.join(", ")}]`);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
return fail(e);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
server.registerTool("telegram-get-suggested-folders", {
|
|
141
|
+
description: "Get Telegram's suggested chat folders based on your chat list (e.g. 'Unread', 'Personal', 'Work'). Returns folder templates you can create with telegram-create-folder.",
|
|
142
|
+
inputSchema: {},
|
|
143
|
+
annotations: READ_ONLY,
|
|
144
|
+
}, async () => {
|
|
145
|
+
const err = await requireConnection(telegram);
|
|
146
|
+
if (err)
|
|
147
|
+
return fail(new Error(err));
|
|
148
|
+
try {
|
|
149
|
+
const suggestions = await telegram.getSuggestedFolders();
|
|
150
|
+
if (!suggestions.length)
|
|
151
|
+
return ok("No folder suggestions available");
|
|
152
|
+
return ok(suggestions.map((s) => `${s.emoticon ? `${s.emoticon} ` : ""}${s.title}`).join("\n"));
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
return fail(e);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
server.registerTool("telegram-toggle-folder-tags", {
|
|
159
|
+
description: "Enable or disable folder tags (colored labels that appear on messages in chat lists when the message belongs to a tagged folder). Requires Telegram Premium.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
enabled: z.boolean().describe("true to enable folder tags, false to disable"),
|
|
162
|
+
},
|
|
163
|
+
annotations: WRITE,
|
|
164
|
+
}, async ({ enabled }) => {
|
|
165
|
+
const err = await requireConnection(telegram);
|
|
166
|
+
if (err)
|
|
167
|
+
return fail(new Error(err));
|
|
168
|
+
try {
|
|
169
|
+
await telegram.toggleDialogFilterTags(enabled);
|
|
170
|
+
return ok(`Folder tags ${enabled ? "enabled" : "disabled"}`);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
return fail(e);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
server.registerTool("telegram-get-global-privacy-settings", {
|
|
177
|
+
description: "Get your account-level global privacy settings: whether new non-contacts are auto-archived/muted, whether archived chats are kept unmuted, whether read receipts are hidden, and whether non-contacts must have Premium to message you.",
|
|
178
|
+
inputSchema: {},
|
|
179
|
+
annotations: READ_ONLY,
|
|
180
|
+
}, async () => {
|
|
181
|
+
const err = await requireConnection(telegram);
|
|
182
|
+
if (err)
|
|
183
|
+
return fail(new Error(err));
|
|
184
|
+
try {
|
|
185
|
+
const s = await telegram.getGlobalPrivacySettings();
|
|
186
|
+
return ok(JSON.stringify(s, null, 2));
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
return fail(e);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
server.registerTool("telegram-set-global-privacy-settings", {
|
|
193
|
+
description: "Update account-level global privacy settings. Only pass the fields you want to change — omitted fields keep their current values. hideReadMarks and newNoncontactPeersRequirePremium require Telegram Premium.",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
archiveAndMuteNewNoncontactPeers: z
|
|
196
|
+
.boolean()
|
|
197
|
+
.optional()
|
|
198
|
+
.describe("Auto-archive and mute messages from unknown users"),
|
|
199
|
+
keepArchivedUnmuted: z.boolean().optional().describe("Keep archived chats unmuted when archiving"),
|
|
200
|
+
keepArchivedFolders: z.boolean().optional().describe("Keep archived chats in their folders"),
|
|
201
|
+
hideReadMarks: z
|
|
202
|
+
.boolean()
|
|
203
|
+
.optional()
|
|
204
|
+
.describe("Hide read receipts — others cannot see when you read their messages (Premium)"),
|
|
205
|
+
newNoncontactPeersRequirePremium: z
|
|
206
|
+
.boolean()
|
|
207
|
+
.optional()
|
|
208
|
+
.describe("Only allow users with Telegram Premium to message you if they are not in your contacts (Premium)"),
|
|
209
|
+
},
|
|
210
|
+
annotations: WRITE,
|
|
211
|
+
}, async ({ archiveAndMuteNewNoncontactPeers, keepArchivedUnmuted, keepArchivedFolders, hideReadMarks, newNoncontactPeersRequirePremium, }) => {
|
|
212
|
+
const err = await requireConnection(telegram);
|
|
213
|
+
if (err)
|
|
214
|
+
return fail(new Error(err));
|
|
215
|
+
try {
|
|
216
|
+
await telegram.setGlobalPrivacySettings({
|
|
217
|
+
archiveAndMuteNewNoncontactPeers,
|
|
218
|
+
keepArchivedUnmuted,
|
|
219
|
+
keepArchivedFolders,
|
|
220
|
+
hideReadMarks,
|
|
221
|
+
newNoncontactPeersRequirePremium,
|
|
222
|
+
});
|
|
223
|
+
const changed = Object.entries({
|
|
224
|
+
archiveAndMuteNewNoncontactPeers,
|
|
225
|
+
keepArchivedUnmuted,
|
|
226
|
+
keepArchivedFolders,
|
|
227
|
+
hideReadMarks,
|
|
228
|
+
newNoncontactPeersRequirePremium,
|
|
229
|
+
})
|
|
230
|
+
.filter(([, v]) => v !== undefined)
|
|
231
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
232
|
+
.join(", ");
|
|
233
|
+
return ok(`Global privacy updated: ${changed || "no fields changed"}`);
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
return fail(e);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { registerChatTools } from "./chats.js";
|
|
|
6
6
|
import { registerContactTools } from "./contacts.js";
|
|
7
7
|
import { registerExtraTools } from "./extras.js";
|
|
8
8
|
import { registerFactCheckTools } from "./fact-check.js";
|
|
9
|
+
import { registerFolderTools } from "./folders.js";
|
|
9
10
|
import { registerGroupCallTools } from "./group-calls.js";
|
|
10
11
|
import { registerMediaTools } from "./media.js";
|
|
11
12
|
import { registerMessageTools } from "./messages.js";
|
|
@@ -29,6 +30,7 @@ export function registerTools(server, telegram) {
|
|
|
29
30
|
registerExtraTools(server, telegram);
|
|
30
31
|
registerAccountTools(server, telegram);
|
|
31
32
|
registerBusinessTools(server, telegram);
|
|
33
|
+
registerFolderTools(server, telegram);
|
|
32
34
|
registerStickerTools(server, telegram);
|
|
33
35
|
registerStoryTools(server, telegram);
|
|
34
36
|
registerBoostTools(server, telegram);
|
package/dist/tools/stars.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { fail, ok, READ_ONLY, requireConnection } from "./shared.js";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, WRITE } from "./shared.js";
|
|
3
3
|
export function isStarsEnabled() {
|
|
4
4
|
return process.env.MCP_TELEGRAM_ENABLE_STARS === "1";
|
|
5
5
|
}
|
|
@@ -68,4 +68,181 @@ export function registerStarsTools(server, telegram) {
|
|
|
68
68
|
return fail(e);
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
|
+
server.registerTool("telegram-get-available-star-gifts", {
|
|
72
|
+
description: "List all available Telegram Star Gifts that can be sent to other users. Returns gift ID, cost in Stars, conversion value, availability (for limited gifts), and upgrade cost. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
73
|
+
inputSchema: {},
|
|
74
|
+
annotations: READ_ONLY,
|
|
75
|
+
}, async () => {
|
|
76
|
+
const err = await requireConnection(telegram);
|
|
77
|
+
if (err)
|
|
78
|
+
return fail(new Error(err));
|
|
79
|
+
try {
|
|
80
|
+
const gifts = await telegram.getAvailableStarGifts();
|
|
81
|
+
return ok(JSON.stringify(gifts));
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
return fail(e);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
server.registerTool("telegram-get-saved-star-gifts", {
|
|
88
|
+
description: "List Star Gifts received by a user or chat. Pass 'me' for your own gifts. Supports pagination via offset/nextOffset and filtering flags. Returns gift ID, kind (regular/unique), stars cost, from-peer, date, and upgrade eligibility. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
89
|
+
inputSchema: {
|
|
90
|
+
chatId: z.string().describe("User or chat to fetch received gifts for — 'me' for self"),
|
|
91
|
+
limit: z.number().int().min(1).max(100).default(20).describe("Max gifts per page"),
|
|
92
|
+
offset: z.string().optional().describe("Pagination cursor from a prior response's nextOffset"),
|
|
93
|
+
excludeUnsaved: z.boolean().optional().describe("Skip gifts the recipient chose to hide"),
|
|
94
|
+
excludeSaved: z.boolean().optional().describe("Skip gifts the recipient chose to show"),
|
|
95
|
+
excludeUnlimited: z.boolean().optional().describe("Skip unlimited-edition gifts"),
|
|
96
|
+
excludeLimited: z.boolean().optional().describe("Skip limited-edition gifts"),
|
|
97
|
+
excludeUnique: z.boolean().optional().describe("Skip unique gifts"),
|
|
98
|
+
sortByValue: z.boolean().optional().describe("Sort by Star value descending instead of date"),
|
|
99
|
+
},
|
|
100
|
+
annotations: READ_ONLY,
|
|
101
|
+
}, async ({ chatId, limit, offset, excludeUnsaved, excludeSaved, excludeUnlimited, excludeLimited, excludeUnique, sortByValue, }) => {
|
|
102
|
+
const err = await requireConnection(telegram);
|
|
103
|
+
if (err)
|
|
104
|
+
return fail(new Error(err));
|
|
105
|
+
try {
|
|
106
|
+
const result = await telegram.getSavedStarGifts(chatId, {
|
|
107
|
+
limit,
|
|
108
|
+
offset,
|
|
109
|
+
excludeUnsaved,
|
|
110
|
+
excludeSaved,
|
|
111
|
+
excludeUnlimited,
|
|
112
|
+
excludeLimited,
|
|
113
|
+
excludeUnique,
|
|
114
|
+
sortByValue,
|
|
115
|
+
});
|
|
116
|
+
return ok(JSON.stringify(result));
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
return fail(e);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
server.registerTool("telegram-save-star-gift", {
|
|
123
|
+
description: "Show or hide a received Star Gift on your profile. Pass msgId for a gift received as a personal message (DM), or chatId + savedId for a gift in a chat/channel. Set unsave=true to hide the gift (remove from profile display). Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1.",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
msgId: z
|
|
126
|
+
.number()
|
|
127
|
+
.int()
|
|
128
|
+
.positive()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe("Message ID of the gift (from your DMs) — use this for personal gifts"),
|
|
131
|
+
chatId: z
|
|
132
|
+
.string()
|
|
133
|
+
.optional()
|
|
134
|
+
.describe("Chat/channel ID where the gift was received (required with savedId for chat gifts)"),
|
|
135
|
+
savedId: z
|
|
136
|
+
.string()
|
|
137
|
+
.regex(/^\d+$/, "must be a numeric saved gift ID")
|
|
138
|
+
.optional()
|
|
139
|
+
.describe("Saved gift ID (from get-saved-star-gifts) — required with chatId for chat gifts"),
|
|
140
|
+
unsave: z.boolean().optional().describe("true = hide the gift from profile; false/omit = show it"),
|
|
141
|
+
},
|
|
142
|
+
annotations: WRITE,
|
|
143
|
+
}, async ({ msgId, chatId, savedId, unsave }) => {
|
|
144
|
+
const err = await requireConnection(telegram);
|
|
145
|
+
if (err)
|
|
146
|
+
return fail(new Error(err));
|
|
147
|
+
if (msgId === undefined && !(chatId && savedId)) {
|
|
148
|
+
return fail(new Error("Provide msgId, or both chatId and savedId"));
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
await telegram.saveStarGift({ msgId, chatId, savedId, unsave });
|
|
152
|
+
return ok(unsave ? "Gift hidden from profile" : "Gift shown on profile");
|
|
153
|
+
}
|
|
154
|
+
catch (e) {
|
|
155
|
+
return fail(e);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
server.registerTool("telegram-convert-star-gift", {
|
|
159
|
+
description: "Convert a received Star Gift into Stars (non-reversible). The gift is removed from your profile and its conversion value is added to your Stars balance. Pass msgId for personal gifts or chatId + savedId for chat gifts. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
msgId: z
|
|
162
|
+
.number()
|
|
163
|
+
.int()
|
|
164
|
+
.positive()
|
|
165
|
+
.optional()
|
|
166
|
+
.describe("Message ID of the gift (from your DMs) — for personal gifts"),
|
|
167
|
+
chatId: z.string().optional().describe("Chat/channel ID (for chat gifts, required with savedId)"),
|
|
168
|
+
savedId: z
|
|
169
|
+
.string()
|
|
170
|
+
.regex(/^\d+$/, "must be a numeric saved gift ID")
|
|
171
|
+
.optional()
|
|
172
|
+
.describe("Saved gift ID — for chat gifts, required with chatId"),
|
|
173
|
+
},
|
|
174
|
+
annotations: WRITE,
|
|
175
|
+
}, async ({ msgId, chatId, savedId }) => {
|
|
176
|
+
const err = await requireConnection(telegram);
|
|
177
|
+
if (err)
|
|
178
|
+
return fail(new Error(err));
|
|
179
|
+
if (msgId === undefined && !(chatId && savedId)) {
|
|
180
|
+
return fail(new Error("Provide msgId, or both chatId and savedId"));
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await telegram.convertStarGift({ msgId, chatId, savedId });
|
|
184
|
+
return ok("Gift converted to Stars");
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
return fail(e);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
server.registerTool("telegram-get-stars-topup-options", {
|
|
191
|
+
description: "List available Telegram Stars top-up tiers (from payments.GetStarsTopupOptions). Returns each option with star count, currency, price amount (in smallest currency units), and whether it is an extended/premium tier. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
192
|
+
inputSchema: {},
|
|
193
|
+
annotations: READ_ONLY,
|
|
194
|
+
}, async () => {
|
|
195
|
+
const err = await requireConnection(telegram);
|
|
196
|
+
if (err)
|
|
197
|
+
return fail(new Error(err));
|
|
198
|
+
try {
|
|
199
|
+
const options = await telegram.getStarsTopupOptions();
|
|
200
|
+
return ok(JSON.stringify(options));
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
return fail(e);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
server.registerTool("telegram-get-stars-subscriptions", {
|
|
207
|
+
description: "List active Telegram Stars subscriptions for a peer (payments.GetStarsSubscriptions). Pass 'me' for your own account or a bot/channel you own. Returns subscription ID, peer, renewal date, period, price in Stars, and canceled status. Paginate with offset. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1. Read-only.",
|
|
208
|
+
inputSchema: {
|
|
209
|
+
chatId: z.string().describe("Peer to query — 'me' for your own subscriptions, or a bot/channel you own"),
|
|
210
|
+
offset: z.string().optional().describe("Pagination cursor from a prior nextOffset"),
|
|
211
|
+
missingBalance: z.boolean().optional().describe("If true, return only subscriptions with missing balance"),
|
|
212
|
+
},
|
|
213
|
+
annotations: READ_ONLY,
|
|
214
|
+
}, async ({ chatId, offset, missingBalance }) => {
|
|
215
|
+
const err = await requireConnection(telegram);
|
|
216
|
+
if (err)
|
|
217
|
+
return fail(new Error(err));
|
|
218
|
+
try {
|
|
219
|
+
const result = await telegram.getStarsSubscriptions(chatId, { offset, missingBalance });
|
|
220
|
+
return ok(JSON.stringify(result));
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
return fail(e);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
server.registerTool("telegram-change-stars-subscription", {
|
|
227
|
+
description: "Cancel or restore a Telegram Stars subscription (payments.ChangeStarsSubscription). Pass canceled=true to cancel an active subscription before its next renewal, or false to restore a previously canceled one. Opt-in: register only when MCP_TELEGRAM_ENABLE_STARS=1.",
|
|
228
|
+
inputSchema: {
|
|
229
|
+
chatId: z
|
|
230
|
+
.string()
|
|
231
|
+
.describe("The peer the subscription belongs to — 'me' for your own subscriptions or a bot/channel you own"),
|
|
232
|
+
subscriptionId: z.string().min(1).describe("Subscription ID (from telegram-get-stars-subscriptions)"),
|
|
233
|
+
canceled: z.boolean().describe("true = cancel the subscription; false = restore a canceled subscription"),
|
|
234
|
+
},
|
|
235
|
+
annotations: WRITE,
|
|
236
|
+
}, async ({ chatId, subscriptionId, canceled }) => {
|
|
237
|
+
const err = await requireConnection(telegram);
|
|
238
|
+
if (err)
|
|
239
|
+
return fail(new Error(err));
|
|
240
|
+
try {
|
|
241
|
+
await telegram.changeStarsSubscription(chatId, subscriptionId, canceled);
|
|
242
|
+
return ok(canceled ? `Subscription ${subscriptionId} canceled` : `Subscription ${subscriptionId} restored`);
|
|
243
|
+
}
|
|
244
|
+
catch (e) {
|
|
245
|
+
return fail(e);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
71
248
|
}
|
package/package.json
CHANGED