@overpod/mcp-telegram 1.22.0 → 1.24.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/README.md +43 -0
- package/dist/telegram-client.d.ts +71 -0
- package/dist/telegram-client.js +307 -0
- package/dist/tools/account.d.ts +3 -0
- package/dist/tools/account.js +280 -0
- package/dist/tools/contacts.js +16 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/stickers.d.ts +3 -0
- package/dist/tools/stickers.js +139 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -130,6 +130,31 @@ mcp-telegram # run server
|
|
|
130
130
|
mcp-telegram login # QR login
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
### Pre-built binary (no runtime needed)
|
|
134
|
+
|
|
135
|
+
Download from [Releases](https://github.com/overpod/mcp-telegram/releases) — standalone single-file binaries, zero dependencies:
|
|
136
|
+
|
|
137
|
+
| Platform | Server | Login CLI |
|
|
138
|
+
|----------|--------|-----------|
|
|
139
|
+
| Linux x64 | `mcp-telegram-linux-x64` | `mcp-telegram-login-linux-x64` |
|
|
140
|
+
| Linux ARM64 | `mcp-telegram-linux-arm64` | `mcp-telegram-login-linux-arm64` |
|
|
141
|
+
| macOS x64 | `mcp-telegram-darwin-x64` | `mcp-telegram-login-darwin-x64` |
|
|
142
|
+
| macOS ARM64 | `mcp-telegram-darwin-arm64` | `mcp-telegram-login-darwin-arm64` |
|
|
143
|
+
| Windows x64 | `mcp-telegram-windows-x64.exe` | `mcp-telegram-login-windows-x64.exe` |
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Download (example for Linux x64)
|
|
147
|
+
curl -L -o mcp-telegram https://github.com/overpod/mcp-telegram/releases/latest/download/mcp-telegram-linux-x64
|
|
148
|
+
curl -L -o mcp-telegram-login https://github.com/overpod/mcp-telegram/releases/latest/download/mcp-telegram-login-linux-x64
|
|
149
|
+
chmod +x mcp-telegram mcp-telegram-login
|
|
150
|
+
|
|
151
|
+
# Login
|
|
152
|
+
TELEGRAM_API_ID=YOUR_ID TELEGRAM_API_HASH=YOUR_HASH ./mcp-telegram-login
|
|
153
|
+
|
|
154
|
+
# Run
|
|
155
|
+
./mcp-telegram
|
|
156
|
+
```
|
|
157
|
+
|
|
133
158
|
### From source
|
|
134
159
|
|
|
135
160
|
```bash
|
|
@@ -208,6 +233,24 @@ claude mcp add telegram -s user \
|
|
|
208
233
|
|
|
209
234
|
> **Note**: No terminal required! Login works entirely through Claude Desktop.
|
|
210
235
|
|
|
236
|
+
### Claude Desktop (Binary)
|
|
237
|
+
|
|
238
|
+
Same setup, but using the pre-built binary instead of npx:
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"mcpServers": {
|
|
243
|
+
"telegram": {
|
|
244
|
+
"command": "/path/to/mcp-telegram",
|
|
245
|
+
"env": {
|
|
246
|
+
"TELEGRAM_API_ID": "YOUR_ID",
|
|
247
|
+
"TELEGRAM_API_HASH": "YOUR_HASH"
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
211
254
|
### Claude Desktop (Docker)
|
|
212
255
|
|
|
213
256
|
1. Login via terminal first (see [Docker](#docker) section above).
|
|
@@ -325,4 +325,75 @@ export declare class TelegramService {
|
|
|
325
325
|
title?: string;
|
|
326
326
|
}): Promise<void>;
|
|
327
327
|
removeAdmin(chatId: string, userId: string): Promise<void>;
|
|
328
|
+
unblockUser(userId: string): Promise<void>;
|
|
329
|
+
muteChat(chatId: string, muteUntil: number): Promise<void>;
|
|
330
|
+
exportInviteLink(chatId: string, options?: {
|
|
331
|
+
expireDate?: number;
|
|
332
|
+
usageLimit?: number;
|
|
333
|
+
requestNeeded?: boolean;
|
|
334
|
+
title?: string;
|
|
335
|
+
}): Promise<string>;
|
|
336
|
+
getInviteLinks(chatId: string, limit?: number, adminId?: string): Promise<Array<{
|
|
337
|
+
link: string;
|
|
338
|
+
title?: string;
|
|
339
|
+
expired: boolean;
|
|
340
|
+
revoked: boolean;
|
|
341
|
+
usageCount: number;
|
|
342
|
+
}>>;
|
|
343
|
+
revokeInviteLink(chatId: string, link: string): Promise<void>;
|
|
344
|
+
getChatFolders(): Promise<Array<{
|
|
345
|
+
id: number;
|
|
346
|
+
title: string;
|
|
347
|
+
emoticon?: string;
|
|
348
|
+
pinnedCount: number;
|
|
349
|
+
includeCount: number;
|
|
350
|
+
}>>;
|
|
351
|
+
setAutoDelete(chatId: string, period: number): Promise<void>;
|
|
352
|
+
getActiveSessions(): Promise<Array<{
|
|
353
|
+
hash: string;
|
|
354
|
+
device: string;
|
|
355
|
+
platform: string;
|
|
356
|
+
appName: string;
|
|
357
|
+
appVersion: string;
|
|
358
|
+
ip: string;
|
|
359
|
+
country: string;
|
|
360
|
+
dateActive: string;
|
|
361
|
+
current: boolean;
|
|
362
|
+
}>>;
|
|
363
|
+
terminateSession(hash: string): Promise<void>;
|
|
364
|
+
terminateAllOtherSessions(): Promise<void>;
|
|
365
|
+
private static PRIVACY_KEYS;
|
|
366
|
+
setPrivacy(setting: string, rule: "everyone" | "contacts" | "nobody", allowUsers?: string[], disallowUsers?: string[]): Promise<void>;
|
|
367
|
+
updateProfile(options: {
|
|
368
|
+
firstName?: string;
|
|
369
|
+
lastName?: string;
|
|
370
|
+
bio?: string;
|
|
371
|
+
}): Promise<void>;
|
|
372
|
+
updateUsername(username: string): Promise<void>;
|
|
373
|
+
getStickerSet(shortName: string): Promise<{
|
|
374
|
+
title: string;
|
|
375
|
+
shortName: string;
|
|
376
|
+
count: number;
|
|
377
|
+
stickers: Array<{
|
|
378
|
+
id: string;
|
|
379
|
+
accessHash: string;
|
|
380
|
+
emoji: string;
|
|
381
|
+
}>;
|
|
382
|
+
}>;
|
|
383
|
+
searchStickerSets(query: string): Promise<Array<{
|
|
384
|
+
title: string;
|
|
385
|
+
shortName: string;
|
|
386
|
+
count: number;
|
|
387
|
+
}>>;
|
|
388
|
+
getInstalledStickerSets(): Promise<Array<{
|
|
389
|
+
title: string;
|
|
390
|
+
shortName: string;
|
|
391
|
+
count: number;
|
|
392
|
+
}>>;
|
|
393
|
+
sendSticker(chatId: string, stickerSetShortName: string, stickerIndex: number, replyTo?: number): Promise<Api.Message | Api.UpdateShortSentMessage | undefined>;
|
|
394
|
+
getRecentStickers(): Promise<Array<{
|
|
395
|
+
id: string;
|
|
396
|
+
accessHash: string;
|
|
397
|
+
emoji: string;
|
|
398
|
+
}>>;
|
|
328
399
|
}
|
package/dist/telegram-client.js
CHANGED
|
@@ -1637,4 +1637,311 @@ export class TelegramService {
|
|
|
1637
1637
|
rank: "",
|
|
1638
1638
|
}));
|
|
1639
1639
|
}
|
|
1640
|
+
// ── New tools: feature parity ──────────────────────────────────────
|
|
1641
|
+
async unblockUser(userId) {
|
|
1642
|
+
if (!this.client || !this.connected)
|
|
1643
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1644
|
+
const entity = await this.client.getInputEntity(userId);
|
|
1645
|
+
await this.client.invoke(new Api.contacts.Unblock({ id: entity }));
|
|
1646
|
+
}
|
|
1647
|
+
async muteChat(chatId, muteUntil) {
|
|
1648
|
+
if (!this.client || !this.connected)
|
|
1649
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1650
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1651
|
+
const peer = await this.client.getInputEntity(resolved);
|
|
1652
|
+
await this.client.invoke(new Api.account.UpdateNotifySettings({
|
|
1653
|
+
peer: new Api.InputNotifyPeer({ peer }),
|
|
1654
|
+
settings: new Api.InputPeerNotifySettings({ muteUntil }),
|
|
1655
|
+
}));
|
|
1656
|
+
}
|
|
1657
|
+
async exportInviteLink(chatId, options) {
|
|
1658
|
+
if (!this.client || !this.connected)
|
|
1659
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1660
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1661
|
+
const peer = await this.client.getInputEntity(resolved);
|
|
1662
|
+
const result = await this.client.invoke(new Api.messages.ExportChatInvite({
|
|
1663
|
+
peer,
|
|
1664
|
+
expireDate: options?.expireDate,
|
|
1665
|
+
usageLimit: options?.usageLimit,
|
|
1666
|
+
requestNeeded: options?.requestNeeded,
|
|
1667
|
+
title: options?.title,
|
|
1668
|
+
}));
|
|
1669
|
+
if (result instanceof Api.ChatInviteExported) {
|
|
1670
|
+
return result.link;
|
|
1671
|
+
}
|
|
1672
|
+
throw new Error("Failed to export invite link");
|
|
1673
|
+
}
|
|
1674
|
+
async getInviteLinks(chatId, limit = 20, adminId) {
|
|
1675
|
+
if (!this.client || !this.connected)
|
|
1676
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1677
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1678
|
+
const peer = await this.client.getInputEntity(resolved);
|
|
1679
|
+
const admin = adminId ? await this.client.getInputEntity(await this.resolvePeer(adminId)) : new Api.InputUserSelf();
|
|
1680
|
+
const result = await this.client.invoke(new Api.messages.GetExportedChatInvites({
|
|
1681
|
+
peer,
|
|
1682
|
+
adminId: admin,
|
|
1683
|
+
limit,
|
|
1684
|
+
}));
|
|
1685
|
+
return result.invites
|
|
1686
|
+
.filter((inv) => inv instanceof Api.ChatInviteExported)
|
|
1687
|
+
.map((inv) => {
|
|
1688
|
+
const expiredByDate = inv.expireDate ? inv.expireDate < Math.floor(Date.now() / 1000) : false;
|
|
1689
|
+
const expiredByUsage = inv.usageLimit !== undefined && inv.usage !== undefined ? inv.usage >= inv.usageLimit : false;
|
|
1690
|
+
return {
|
|
1691
|
+
link: inv.link,
|
|
1692
|
+
title: inv.title,
|
|
1693
|
+
expired: expiredByDate || expiredByUsage,
|
|
1694
|
+
revoked: inv.revoked ?? false,
|
|
1695
|
+
usageCount: inv.usage ?? 0,
|
|
1696
|
+
};
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
async revokeInviteLink(chatId, link) {
|
|
1700
|
+
if (!this.client || !this.connected)
|
|
1701
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1702
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1703
|
+
const peer = await this.client.getInputEntity(resolved);
|
|
1704
|
+
await this.client.invoke(new Api.messages.EditExportedChatInvite({
|
|
1705
|
+
peer,
|
|
1706
|
+
link,
|
|
1707
|
+
revoked: true,
|
|
1708
|
+
}));
|
|
1709
|
+
}
|
|
1710
|
+
async getChatFolders() {
|
|
1711
|
+
if (!this.client || !this.connected)
|
|
1712
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1713
|
+
const result = await this.client.invoke(new Api.messages.GetDialogFilters());
|
|
1714
|
+
const filters = "filters" in result ? result.filters : [];
|
|
1715
|
+
return filters
|
|
1716
|
+
.filter((f) => f instanceof Api.DialogFilter)
|
|
1717
|
+
.map((f) => ({
|
|
1718
|
+
id: f.id,
|
|
1719
|
+
title: typeof f.title === "string" ? f.title : f.title.text,
|
|
1720
|
+
emoticon: f.emoticon,
|
|
1721
|
+
pinnedCount: f.pinnedPeers?.length ?? 0,
|
|
1722
|
+
includeCount: f.includePeers?.length ?? 0,
|
|
1723
|
+
}));
|
|
1724
|
+
}
|
|
1725
|
+
async setAutoDelete(chatId, period) {
|
|
1726
|
+
if (!this.client || !this.connected)
|
|
1727
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1728
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1729
|
+
const peer = await this.client.getInputEntity(resolved);
|
|
1730
|
+
await this.client.invoke(new Api.messages.SetHistoryTTL({ peer, period }));
|
|
1731
|
+
}
|
|
1732
|
+
async getActiveSessions() {
|
|
1733
|
+
if (!this.client || !this.connected)
|
|
1734
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1735
|
+
const result = await this.client.invoke(new Api.account.GetAuthorizations());
|
|
1736
|
+
return result.authorizations.map((a) => ({
|
|
1737
|
+
hash: a.hash.toString(),
|
|
1738
|
+
device: a.deviceModel,
|
|
1739
|
+
platform: a.platform,
|
|
1740
|
+
appName: a.appName,
|
|
1741
|
+
appVersion: a.appVersion,
|
|
1742
|
+
ip: a.ip,
|
|
1743
|
+
country: a.country,
|
|
1744
|
+
dateActive: new Date(a.dateActive * 1000).toISOString(),
|
|
1745
|
+
current: a.current ?? false,
|
|
1746
|
+
}));
|
|
1747
|
+
}
|
|
1748
|
+
async terminateSession(hash) {
|
|
1749
|
+
if (!this.client || !this.connected)
|
|
1750
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1751
|
+
await this.client.invoke(new Api.account.ResetAuthorization({ hash: bigInt(hash) }));
|
|
1752
|
+
}
|
|
1753
|
+
async terminateAllOtherSessions() {
|
|
1754
|
+
if (!this.client || !this.connected)
|
|
1755
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1756
|
+
await this.client.invoke(new Api.auth.ResetAuthorizations());
|
|
1757
|
+
}
|
|
1758
|
+
static PRIVACY_KEYS = {
|
|
1759
|
+
phone_number: () => new Api.InputPrivacyKeyPhoneNumber(),
|
|
1760
|
+
last_seen: () => new Api.InputPrivacyKeyStatusTimestamp(),
|
|
1761
|
+
profile_photo: () => new Api.InputPrivacyKeyProfilePhoto(),
|
|
1762
|
+
forwards: () => new Api.InputPrivacyKeyForwards(),
|
|
1763
|
+
calls: () => new Api.InputPrivacyKeyPhoneCall(),
|
|
1764
|
+
groups: () => new Api.InputPrivacyKeyChatInvite(),
|
|
1765
|
+
bio: () => new Api.InputPrivacyKeyAbout(),
|
|
1766
|
+
};
|
|
1767
|
+
async setPrivacy(setting, rule, allowUsers, disallowUsers) {
|
|
1768
|
+
if (!this.client || !this.connected)
|
|
1769
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1770
|
+
const keyFactory = TelegramService.PRIVACY_KEYS[setting];
|
|
1771
|
+
if (!keyFactory)
|
|
1772
|
+
throw new Error(`Unknown privacy setting: ${setting}. Valid: ${Object.keys(TelegramService.PRIVACY_KEYS).join(", ")}`);
|
|
1773
|
+
const rules = [];
|
|
1774
|
+
// Exceptions must come before the general rule so they are not shadowed
|
|
1775
|
+
if (disallowUsers?.length) {
|
|
1776
|
+
const users = [];
|
|
1777
|
+
const invalid = [];
|
|
1778
|
+
for (const u of disallowUsers) {
|
|
1779
|
+
const inputEntity = await this.client.getInputEntity(u);
|
|
1780
|
+
if (inputEntity instanceof Api.InputPeerUser) {
|
|
1781
|
+
users.push(new Api.InputUser({ userId: inputEntity.userId, accessHash: inputEntity.accessHash }));
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
invalid.push(u);
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
if (invalid.length > 0) {
|
|
1788
|
+
throw new Error(`disallowUsers entries are not valid users: ${invalid.join(", ")}`);
|
|
1789
|
+
}
|
|
1790
|
+
if (users.length > 0) {
|
|
1791
|
+
rules.push(new Api.InputPrivacyValueDisallowUsers({ users }));
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
if (allowUsers?.length) {
|
|
1795
|
+
const users = [];
|
|
1796
|
+
const invalid = [];
|
|
1797
|
+
for (const u of allowUsers) {
|
|
1798
|
+
const inputEntity = await this.client.getInputEntity(u);
|
|
1799
|
+
if (inputEntity instanceof Api.InputPeerUser) {
|
|
1800
|
+
users.push(new Api.InputUser({ userId: inputEntity.userId, accessHash: inputEntity.accessHash }));
|
|
1801
|
+
}
|
|
1802
|
+
else {
|
|
1803
|
+
invalid.push(u);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
if (invalid.length > 0) {
|
|
1807
|
+
throw new Error(`allowUsers entries are not valid users: ${invalid.join(", ")}`);
|
|
1808
|
+
}
|
|
1809
|
+
if (users.length > 0) {
|
|
1810
|
+
rules.push(new Api.InputPrivacyValueAllowUsers({ users }));
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (rule === "everyone")
|
|
1814
|
+
rules.push(new Api.InputPrivacyValueAllowAll());
|
|
1815
|
+
else if (rule === "contacts")
|
|
1816
|
+
rules.push(new Api.InputPrivacyValueAllowContacts(), new Api.InputPrivacyValueDisallowAll());
|
|
1817
|
+
else
|
|
1818
|
+
rules.push(new Api.InputPrivacyValueDisallowAll());
|
|
1819
|
+
await this.client.invoke(new Api.account.SetPrivacy({ key: keyFactory(), rules }));
|
|
1820
|
+
}
|
|
1821
|
+
async updateProfile(options) {
|
|
1822
|
+
if (!this.client || !this.connected)
|
|
1823
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1824
|
+
await this.client.invoke(new Api.account.UpdateProfile({
|
|
1825
|
+
firstName: options.firstName,
|
|
1826
|
+
lastName: options.lastName,
|
|
1827
|
+
about: options.bio,
|
|
1828
|
+
}));
|
|
1829
|
+
}
|
|
1830
|
+
async updateUsername(username) {
|
|
1831
|
+
if (!this.client || !this.connected)
|
|
1832
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1833
|
+
await this.client.invoke(new Api.account.UpdateUsername({ username }));
|
|
1834
|
+
}
|
|
1835
|
+
// ─── Stickers ──────────────────────────────────────────────
|
|
1836
|
+
async getStickerSet(shortName) {
|
|
1837
|
+
if (!this.client || !this.connected)
|
|
1838
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1839
|
+
const result = await this.client.invoke(new Api.messages.GetStickerSet({
|
|
1840
|
+
stickerset: new Api.InputStickerSetShortName({ shortName }),
|
|
1841
|
+
hash: 0,
|
|
1842
|
+
}));
|
|
1843
|
+
if (result instanceof Api.messages.StickerSetNotModified) {
|
|
1844
|
+
throw new Error("Sticker set was not modified");
|
|
1845
|
+
}
|
|
1846
|
+
const set = result.set;
|
|
1847
|
+
const packs = result.packs;
|
|
1848
|
+
// Build emoji map: document id -> emoji
|
|
1849
|
+
const emojiMap = new Map();
|
|
1850
|
+
for (const pack of packs) {
|
|
1851
|
+
for (const docId of pack.documents) {
|
|
1852
|
+
emojiMap.set(docId.toString(), pack.emoticon);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
return {
|
|
1856
|
+
title: set.title,
|
|
1857
|
+
shortName: set.shortName,
|
|
1858
|
+
count: set.count,
|
|
1859
|
+
stickers: result.documents.map((doc) => ({
|
|
1860
|
+
id: doc.id.toString(),
|
|
1861
|
+
accessHash: doc.accessHash.toString(),
|
|
1862
|
+
emoji: emojiMap.get(doc.id.toString()) || "",
|
|
1863
|
+
})),
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
async searchStickerSets(query) {
|
|
1867
|
+
if (!this.client || !this.connected)
|
|
1868
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1869
|
+
const result = await this.client.invoke(new Api.messages.SearchStickerSets({
|
|
1870
|
+
q: query,
|
|
1871
|
+
hash: bigInt(0),
|
|
1872
|
+
}));
|
|
1873
|
+
if (result instanceof Api.messages.FoundStickerSetsNotModified) {
|
|
1874
|
+
return [];
|
|
1875
|
+
}
|
|
1876
|
+
return result.sets.map((covered) => {
|
|
1877
|
+
const set = covered.set;
|
|
1878
|
+
return {
|
|
1879
|
+
title: set.title,
|
|
1880
|
+
shortName: set.shortName,
|
|
1881
|
+
count: set.count,
|
|
1882
|
+
};
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
async getInstalledStickerSets() {
|
|
1886
|
+
if (!this.client || !this.connected)
|
|
1887
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1888
|
+
const result = await this.client.invoke(new Api.messages.GetAllStickers({ hash: bigInt(0) }));
|
|
1889
|
+
if (result instanceof Api.messages.AllStickersNotModified) {
|
|
1890
|
+
return [];
|
|
1891
|
+
}
|
|
1892
|
+
return result.sets.map((set) => ({
|
|
1893
|
+
title: set.title,
|
|
1894
|
+
shortName: set.shortName,
|
|
1895
|
+
count: set.count,
|
|
1896
|
+
}));
|
|
1897
|
+
}
|
|
1898
|
+
async sendSticker(chatId, stickerSetShortName, stickerIndex, replyTo) {
|
|
1899
|
+
if (!this.client || !this.connected)
|
|
1900
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1901
|
+
return await this.rateLimiter.execute(async () => {
|
|
1902
|
+
if (!Number.isInteger(stickerIndex)) {
|
|
1903
|
+
throw new Error(`Sticker index must be an integer, got ${stickerIndex}`);
|
|
1904
|
+
}
|
|
1905
|
+
// Fetch raw sticker set to get the actual Api.Document with valid fileReference
|
|
1906
|
+
const rawResult = await this.client?.invoke(new Api.messages.GetStickerSet({
|
|
1907
|
+
stickerset: new Api.InputStickerSetShortName({ shortName: stickerSetShortName }),
|
|
1908
|
+
hash: 0,
|
|
1909
|
+
}));
|
|
1910
|
+
if (!rawResult || rawResult instanceof Api.messages.StickerSetNotModified) {
|
|
1911
|
+
throw new Error("Sticker set not found");
|
|
1912
|
+
}
|
|
1913
|
+
const stickerSet = rawResult;
|
|
1914
|
+
if (stickerIndex < 0 || stickerIndex >= stickerSet.documents.length) {
|
|
1915
|
+
throw new Error(`Sticker index ${stickerIndex} out of range (0-${stickerSet.documents.length - 1})`);
|
|
1916
|
+
}
|
|
1917
|
+
const sticker = stickerSet.documents[stickerIndex];
|
|
1918
|
+
if (!(sticker instanceof Api.Document)) {
|
|
1919
|
+
throw new Error("Selected sticker is not a valid document");
|
|
1920
|
+
}
|
|
1921
|
+
const resolved = await this.resolvePeer(chatId);
|
|
1922
|
+
return await this.client?.sendFile(resolved, {
|
|
1923
|
+
file: sticker,
|
|
1924
|
+
...(replyTo ? { replyTo } : {}),
|
|
1925
|
+
});
|
|
1926
|
+
}, `sendSticker to ${chatId}`);
|
|
1927
|
+
}
|
|
1928
|
+
async getRecentStickers() {
|
|
1929
|
+
if (!this.client || !this.connected)
|
|
1930
|
+
throw new Error(NOT_CONNECTED_ERROR);
|
|
1931
|
+
const result = await this.client.invoke(new Api.messages.GetRecentStickers({ hash: bigInt(0) }));
|
|
1932
|
+
if (result instanceof Api.messages.RecentStickersNotModified) {
|
|
1933
|
+
return [];
|
|
1934
|
+
}
|
|
1935
|
+
const emojiMap = new Map();
|
|
1936
|
+
for (const pack of result.packs) {
|
|
1937
|
+
for (const docId of pack.documents) {
|
|
1938
|
+
emojiMap.set(docId.toString(), pack.emoticon);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return result.stickers.map((doc) => ({
|
|
1942
|
+
id: doc.id.toString(),
|
|
1943
|
+
accessHash: doc.accessHash.toString(),
|
|
1944
|
+
emoji: emojiMap.get(doc.id.toString()) || "",
|
|
1945
|
+
}));
|
|
1946
|
+
}
|
|
1640
1947
|
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DESTRUCTIVE, fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
|
|
3
|
+
const MUTE_FOREVER_UNTIL = 2147483647; // max 32-bit signed int
|
|
4
|
+
export function registerAccountTools(server, telegram) {
|
|
5
|
+
server.registerTool("telegram-mute-chat", {
|
|
6
|
+
description: "Mute or unmute notifications for a Telegram chat. Set muted=true to mute (optionally with duration in seconds), muted=false to unmute",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
9
|
+
muted: z.boolean().describe("true to mute, false to unmute"),
|
|
10
|
+
duration: z
|
|
11
|
+
.number()
|
|
12
|
+
.int()
|
|
13
|
+
.positive()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Mute duration in seconds (only when muted=true, must be > 0). Omit to mute forever"),
|
|
16
|
+
},
|
|
17
|
+
annotations: WRITE,
|
|
18
|
+
}, async ({ chatId, muted, duration }) => {
|
|
19
|
+
const err = await requireConnection(telegram);
|
|
20
|
+
if (err)
|
|
21
|
+
return fail(new Error(err));
|
|
22
|
+
try {
|
|
23
|
+
let muteUntil;
|
|
24
|
+
if (!muted) {
|
|
25
|
+
muteUntil = 0;
|
|
26
|
+
}
|
|
27
|
+
else if (duration !== undefined && duration > 0) {
|
|
28
|
+
const now = Math.floor(Date.now() / 1000);
|
|
29
|
+
muteUntil = Math.min(now + duration, MUTE_FOREVER_UNTIL);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
muteUntil = MUTE_FOREVER_UNTIL;
|
|
33
|
+
}
|
|
34
|
+
await telegram.muteChat(chatId, muteUntil);
|
|
35
|
+
const status = !muted
|
|
36
|
+
? "unmuted"
|
|
37
|
+
: duration !== undefined && duration > 0
|
|
38
|
+
? `muted for ${duration}s`
|
|
39
|
+
: "muted forever";
|
|
40
|
+
return ok(`Chat ${chatId} ${status}`);
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
return fail(e);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
server.registerTool("telegram-get-chat-folders", {
|
|
47
|
+
description: "Get list of your Telegram chat folders (filters) with their names and chat counts",
|
|
48
|
+
inputSchema: {},
|
|
49
|
+
annotations: READ_ONLY,
|
|
50
|
+
}, async () => {
|
|
51
|
+
const err = await requireConnection(telegram);
|
|
52
|
+
if (err)
|
|
53
|
+
return fail(new Error(err));
|
|
54
|
+
try {
|
|
55
|
+
const folders = await telegram.getChatFolders();
|
|
56
|
+
if (folders.length === 0)
|
|
57
|
+
return ok("No chat folders");
|
|
58
|
+
const text = folders
|
|
59
|
+
.map((f) => `[${f.id}] ${f.emoticon ? `${f.emoticon} ` : ""}${f.title} (${f.includeCount} chats, ${f.pinnedCount} pinned)`)
|
|
60
|
+
.join("\n");
|
|
61
|
+
return ok(sanitize(text));
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
return fail(e);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
server.registerTool("telegram-set-auto-delete", {
|
|
68
|
+
description: "Set auto-delete timer for messages in a chat. Common values: 86400 (1 day), 604800 (1 week), 2592000 (1 month). Use 0 to disable",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
71
|
+
period: z
|
|
72
|
+
.number()
|
|
73
|
+
.int()
|
|
74
|
+
.nonnegative()
|
|
75
|
+
.describe("Auto-delete period in seconds. 0 = disable. Common: 86400 (1d), 604800 (1w), 2592000 (1mo)"),
|
|
76
|
+
},
|
|
77
|
+
annotations: WRITE,
|
|
78
|
+
}, async ({ chatId, period }) => {
|
|
79
|
+
const err = await requireConnection(telegram);
|
|
80
|
+
if (err)
|
|
81
|
+
return fail(new Error(err));
|
|
82
|
+
try {
|
|
83
|
+
await telegram.setAutoDelete(chatId, period);
|
|
84
|
+
const status = period === 0 ? "disabled" : `set to ${period}s`;
|
|
85
|
+
return ok(`Auto-delete for ${chatId} ${status}`);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
return fail(e);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
server.registerTool("telegram-get-sessions", {
|
|
92
|
+
description: "Get list of all active Telegram sessions (logged-in devices) with device info, IP, and last active time",
|
|
93
|
+
inputSchema: {},
|
|
94
|
+
annotations: READ_ONLY,
|
|
95
|
+
}, async () => {
|
|
96
|
+
const err = await requireConnection(telegram);
|
|
97
|
+
if (err)
|
|
98
|
+
return fail(new Error(err));
|
|
99
|
+
try {
|
|
100
|
+
const sessions = await telegram.getActiveSessions();
|
|
101
|
+
if (sessions.length === 0)
|
|
102
|
+
return ok("No active sessions");
|
|
103
|
+
const text = sessions
|
|
104
|
+
.map((s) => `${s.current ? "→ " : " "}${s.device} (${s.platform}) — ${s.appName} ${s.appVersion}\n IP: ${s.ip} (${s.country}) | Last active: ${s.dateActive}${s.current ? " [CURRENT]" : ""}\n Hash: ${s.hash}`)
|
|
105
|
+
.join("\n\n");
|
|
106
|
+
return ok(sanitize(text));
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
return fail(e);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
server.registerTool("telegram-terminate-session", {
|
|
113
|
+
description: "Terminate a specific Telegram session by its hash, or explicitly terminate all other sessions by setting terminateAllOther=true",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
sessionId: z
|
|
116
|
+
.string()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe("Session hash to terminate (numeric string from get-sessions). Required when terminateAllOther is not set")
|
|
119
|
+
.refine((v) => v === undefined || /^\d+$/.test(v), { message: "sessionId must be a numeric string" }),
|
|
120
|
+
terminateAllOther: z
|
|
121
|
+
.boolean()
|
|
122
|
+
.optional()
|
|
123
|
+
.describe("Set to true to terminate all other sessions (excludes current). Cannot be combined with sessionId"),
|
|
124
|
+
},
|
|
125
|
+
annotations: DESTRUCTIVE,
|
|
126
|
+
}, async ({ sessionId, terminateAllOther }) => {
|
|
127
|
+
const err = await requireConnection(telegram);
|
|
128
|
+
if (err)
|
|
129
|
+
return fail(new Error(err));
|
|
130
|
+
try {
|
|
131
|
+
if (terminateAllOther) {
|
|
132
|
+
if (sessionId) {
|
|
133
|
+
return fail(new Error("Provide either sessionId or terminateAllOther=true, not both"));
|
|
134
|
+
}
|
|
135
|
+
await telegram.terminateAllOtherSessions();
|
|
136
|
+
return ok("All other sessions terminated");
|
|
137
|
+
}
|
|
138
|
+
if (!sessionId) {
|
|
139
|
+
return fail(new Error("Provide sessionId to terminate a specific session, or set terminateAllOther=true"));
|
|
140
|
+
}
|
|
141
|
+
await telegram.terminateSession(sessionId);
|
|
142
|
+
return ok(`Session ${sessionId} terminated`);
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
return fail(e);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
server.registerTool("telegram-set-privacy", {
|
|
149
|
+
description: "Configure privacy settings for your Telegram account. Controls who can see your phone number, last seen, profile photo, etc.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
setting: z
|
|
152
|
+
.enum(["phone_number", "last_seen", "profile_photo", "forwards", "calls", "groups", "bio"])
|
|
153
|
+
.describe("Privacy setting to change"),
|
|
154
|
+
rule: z.enum(["everyone", "contacts", "nobody"]).describe("Who can see/access this"),
|
|
155
|
+
allowUsers: z.array(z.string()).optional().describe("User IDs/usernames to always allow (exceptions)"),
|
|
156
|
+
disallowUsers: z.array(z.string()).optional().describe("User IDs/usernames to always disallow (exceptions)"),
|
|
157
|
+
},
|
|
158
|
+
annotations: WRITE,
|
|
159
|
+
}, async ({ setting, rule, allowUsers, disallowUsers }) => {
|
|
160
|
+
const err = await requireConnection(telegram);
|
|
161
|
+
if (err)
|
|
162
|
+
return fail(new Error(err));
|
|
163
|
+
try {
|
|
164
|
+
await telegram.setPrivacy(setting, rule, allowUsers, disallowUsers);
|
|
165
|
+
return ok(`Privacy: ${setting} set to "${rule}"`);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
return fail(e);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
server.registerTool("telegram-update-profile", {
|
|
172
|
+
description: "Update your Telegram profile — first name, last name, bio, or username",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
firstName: z.string().optional().describe("New first name"),
|
|
175
|
+
lastName: z.string().optional().describe("New last name"),
|
|
176
|
+
bio: z.string().optional().describe("New bio/about text (max 70 chars, 300 for Premium)"),
|
|
177
|
+
username: z.string().optional().describe("New username (without @)"),
|
|
178
|
+
},
|
|
179
|
+
annotations: WRITE,
|
|
180
|
+
}, async ({ firstName, lastName, bio, username }) => {
|
|
181
|
+
const err = await requireConnection(telegram);
|
|
182
|
+
if (err)
|
|
183
|
+
return fail(new Error(err));
|
|
184
|
+
try {
|
|
185
|
+
const updates = [];
|
|
186
|
+
if (firstName !== undefined || lastName !== undefined || bio !== undefined) {
|
|
187
|
+
await telegram.updateProfile({ firstName, lastName, bio });
|
|
188
|
+
if (firstName !== undefined)
|
|
189
|
+
updates.push(`firstName: ${firstName}`);
|
|
190
|
+
if (lastName !== undefined)
|
|
191
|
+
updates.push(`lastName: ${lastName}`);
|
|
192
|
+
if (bio !== undefined)
|
|
193
|
+
updates.push(`bio: ${bio}`);
|
|
194
|
+
}
|
|
195
|
+
if (username !== undefined) {
|
|
196
|
+
const normalizedUsername = username.replace(/^@/, "");
|
|
197
|
+
await telegram.updateUsername(normalizedUsername);
|
|
198
|
+
updates.push(`username: @${normalizedUsername}`);
|
|
199
|
+
}
|
|
200
|
+
return ok(updates.length ? `Profile updated: ${updates.join(", ")}` : "No changes specified");
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
return fail(e);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
server.registerTool("telegram-create-invite-link", {
|
|
207
|
+
description: "Create a new invite link for a group or channel",
|
|
208
|
+
inputSchema: {
|
|
209
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
210
|
+
expireDate: z.number().optional().describe("Link expiration as Unix timestamp"),
|
|
211
|
+
memberLimit: z.number().optional().describe("Max number of users who can join via this link"),
|
|
212
|
+
requestApproval: z.boolean().optional().describe("Require admin approval to join"),
|
|
213
|
+
title: z.string().optional().describe("Label for the invite link (only visible to admins)"),
|
|
214
|
+
},
|
|
215
|
+
annotations: WRITE,
|
|
216
|
+
}, async ({ chatId, expireDate, memberLimit, requestApproval, title }) => {
|
|
217
|
+
const err = await requireConnection(telegram);
|
|
218
|
+
if (err)
|
|
219
|
+
return fail(new Error(err));
|
|
220
|
+
try {
|
|
221
|
+
const link = await telegram.exportInviteLink(chatId, {
|
|
222
|
+
expireDate,
|
|
223
|
+
usageLimit: memberLimit,
|
|
224
|
+
requestNeeded: requestApproval,
|
|
225
|
+
title,
|
|
226
|
+
});
|
|
227
|
+
return ok(`Invite link created: ${link}`);
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
return fail(e);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
server.registerTool("telegram-get-invite-links", {
|
|
234
|
+
description: "Get list of invite links for a group or channel. By default returns links created by the current account; pass adminId to query another admin's links",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
237
|
+
limit: z.number().default(20).describe("Max links to return"),
|
|
238
|
+
adminId: z
|
|
239
|
+
.string()
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("Admin user ID or username to list links for (default: current account)"),
|
|
242
|
+
},
|
|
243
|
+
annotations: READ_ONLY,
|
|
244
|
+
}, async ({ chatId, limit, adminId }) => {
|
|
245
|
+
const err = await requireConnection(telegram);
|
|
246
|
+
if (err)
|
|
247
|
+
return fail(new Error(err));
|
|
248
|
+
try {
|
|
249
|
+
const links = await telegram.getInviteLinks(chatId, limit, adminId);
|
|
250
|
+
if (links.length === 0)
|
|
251
|
+
return ok("No invite links");
|
|
252
|
+
const text = links
|
|
253
|
+
.map((l) => `${l.link}${l.title ? ` (${l.title})` : ""} — ${l.usageCount} uses${l.expired ? " [EXPIRED]" : ""}${l.revoked ? " [REVOKED]" : ""}`)
|
|
254
|
+
.join("\n");
|
|
255
|
+
return ok(sanitize(text));
|
|
256
|
+
}
|
|
257
|
+
catch (e) {
|
|
258
|
+
return fail(e);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
server.registerTool("telegram-revoke-invite-link", {
|
|
262
|
+
description: "Revoke an invite link for a group or channel",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
265
|
+
link: z.string().describe("The invite link to revoke"),
|
|
266
|
+
},
|
|
267
|
+
annotations: DESTRUCTIVE,
|
|
268
|
+
}, async ({ chatId, link }) => {
|
|
269
|
+
const err = await requireConnection(telegram);
|
|
270
|
+
if (err)
|
|
271
|
+
return fail(new Error(err));
|
|
272
|
+
try {
|
|
273
|
+
await telegram.revokeInviteLink(chatId, link);
|
|
274
|
+
return ok(`Invite link revoked: ${link}`);
|
|
275
|
+
}
|
|
276
|
+
catch (e) {
|
|
277
|
+
return fail(e);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
package/dist/tools/contacts.js
CHANGED
|
@@ -116,6 +116,22 @@ export function registerContactTools(server, telegram) {
|
|
|
116
116
|
return fail(e);
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
|
+
server.registerTool("telegram-unblock-user", {
|
|
120
|
+
description: "Unblock a previously blocked Telegram user",
|
|
121
|
+
inputSchema: { userId: z.string().describe("User ID or username to unblock") },
|
|
122
|
+
annotations: WRITE,
|
|
123
|
+
}, async ({ userId }) => {
|
|
124
|
+
const err = await requireConnection(telegram);
|
|
125
|
+
if (err)
|
|
126
|
+
return fail(new Error(err));
|
|
127
|
+
try {
|
|
128
|
+
await telegram.unblockUser(userId);
|
|
129
|
+
return ok(`User unblocked: ${userId}`);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
return fail(e);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
119
135
|
server.registerTool("telegram-report-spam", {
|
|
120
136
|
description: "Report a chat as spam to Telegram",
|
|
121
137
|
inputSchema: { chatId: z.string().describe("Chat ID or username to report") },
|
package/dist/tools/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerAccountTools } from "./account.js";
|
|
1
2
|
import { registerAuthTools } from "./auth.js";
|
|
2
3
|
import { registerChatTools } from "./chats.js";
|
|
3
4
|
import { registerContactTools } from "./contacts.js";
|
|
@@ -5,6 +6,7 @@ import { registerExtraTools } from "./extras.js";
|
|
|
5
6
|
import { registerMediaTools } from "./media.js";
|
|
6
7
|
import { registerMessageTools } from "./messages.js";
|
|
7
8
|
import { registerReactionTools } from "./reactions.js";
|
|
9
|
+
import { registerStickerTools } from "./stickers.js";
|
|
8
10
|
export function registerTools(server, telegram) {
|
|
9
11
|
registerAuthTools(server, telegram);
|
|
10
12
|
registerMessageTools(server, telegram);
|
|
@@ -13,4 +15,6 @@ export function registerTools(server, telegram) {
|
|
|
13
15
|
registerContactTools(server, telegram);
|
|
14
16
|
registerReactionTools(server, telegram);
|
|
15
17
|
registerExtraTools(server, telegram);
|
|
18
|
+
registerAccountTools(server, telegram);
|
|
19
|
+
registerStickerTools(server, telegram);
|
|
16
20
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { fail, ok, READ_ONLY, requireConnection, sanitize, WRITE } from "./shared.js";
|
|
3
|
+
export function registerStickerTools(server, telegram) {
|
|
4
|
+
server.registerTool("telegram-get-sticker-set", {
|
|
5
|
+
description: "Get all stickers from a sticker set by its short name. Returns each sticker with index and emoji. Use the index with telegram-send-sticker to send a specific sticker",
|
|
6
|
+
inputSchema: {
|
|
7
|
+
shortName: z
|
|
8
|
+
.string()
|
|
9
|
+
.describe("Short name of the sticker set (e.g. 'AnimatedEmojis', 'HotCherry'). Find names via telegram-search-sticker-sets or from t.me/addstickers/<shortName> links"),
|
|
10
|
+
},
|
|
11
|
+
annotations: READ_ONLY,
|
|
12
|
+
}, async ({ shortName }) => {
|
|
13
|
+
const err = await requireConnection(telegram);
|
|
14
|
+
if (err)
|
|
15
|
+
return fail(new Error(err));
|
|
16
|
+
try {
|
|
17
|
+
const set = await telegram.getStickerSet(shortName);
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push(`📦 ${set.title} (${set.shortName})`);
|
|
20
|
+
lines.push(`${set.count} stickers`);
|
|
21
|
+
lines.push("");
|
|
22
|
+
for (let i = 0; i < set.stickers.length; i++) {
|
|
23
|
+
const s = set.stickers[i];
|
|
24
|
+
lines.push(`[${i}] ${s.emoji}`);
|
|
25
|
+
}
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push(`Send a sticker: telegram-send-sticker(chatId, stickerSet="${set.shortName}", index=N)`);
|
|
28
|
+
return ok(sanitize(lines.join("\n")));
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
return fail(e);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
server.registerTool("telegram-search-sticker-sets", {
|
|
35
|
+
description: "Search for sticker sets by name or keyword. Returns matching sticker pack names that can be used with telegram-get-sticker-set",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
query: z.string().describe("Search query (e.g. 'cat', 'love', 'pepe', 'anime')"),
|
|
38
|
+
},
|
|
39
|
+
annotations: READ_ONLY,
|
|
40
|
+
}, async ({ query }) => {
|
|
41
|
+
const err = await requireConnection(telegram);
|
|
42
|
+
if (err)
|
|
43
|
+
return fail(new Error(err));
|
|
44
|
+
try {
|
|
45
|
+
const sets = await telegram.searchStickerSets(query);
|
|
46
|
+
if (sets.length === 0) {
|
|
47
|
+
return ok(`No sticker sets found for "${query}". Try different keywords.`);
|
|
48
|
+
}
|
|
49
|
+
const lines = [];
|
|
50
|
+
lines.push(`Found ${sets.length} sticker set(s) for "${query}":\n`);
|
|
51
|
+
for (const set of sets) {
|
|
52
|
+
const flags = "";
|
|
53
|
+
lines.push(`• ${set.title}${flags} — ${set.count} stickers`);
|
|
54
|
+
lines.push(` Short name: ${set.shortName}`);
|
|
55
|
+
}
|
|
56
|
+
lines.push("");
|
|
57
|
+
lines.push("Use telegram-get-sticker-set(shortName) to see individual stickers.");
|
|
58
|
+
return ok(sanitize(lines.join("\n")));
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
return fail(e);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
server.registerTool("telegram-get-installed-stickers", {
|
|
65
|
+
description: "List all sticker sets installed by the user. Returns pack names and short names for use with other sticker tools",
|
|
66
|
+
inputSchema: {},
|
|
67
|
+
annotations: READ_ONLY,
|
|
68
|
+
}, async () => {
|
|
69
|
+
const err = await requireConnection(telegram);
|
|
70
|
+
if (err)
|
|
71
|
+
return fail(new Error(err));
|
|
72
|
+
try {
|
|
73
|
+
const sets = await telegram.getInstalledStickerSets();
|
|
74
|
+
if (sets.length === 0) {
|
|
75
|
+
return ok("No sticker sets installed.");
|
|
76
|
+
}
|
|
77
|
+
const lines = [];
|
|
78
|
+
lines.push(`${sets.length} installed sticker set(s):\n`);
|
|
79
|
+
for (const set of sets) {
|
|
80
|
+
const flags = "";
|
|
81
|
+
lines.push(`• ${set.title}${flags} — ${set.count} stickers`);
|
|
82
|
+
lines.push(` Short name: ${set.shortName}`);
|
|
83
|
+
}
|
|
84
|
+
return ok(sanitize(lines.join("\n")));
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
return fail(e);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
server.registerTool("telegram-send-sticker", {
|
|
91
|
+
description: "Send a sticker from a sticker set to a chat. First use telegram-get-sticker-set to browse available stickers and find the index",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
chatId: z.string().describe("Chat ID or username"),
|
|
94
|
+
stickerSet: z.string().describe("Short name of the sticker set (e.g. 'HotCherry')"),
|
|
95
|
+
index: z
|
|
96
|
+
.number()
|
|
97
|
+
.int()
|
|
98
|
+
.nonnegative()
|
|
99
|
+
.describe("Index of the sticker in the set (0-based, get from telegram-get-sticker-set)"),
|
|
100
|
+
replyTo: z.number().int().optional().describe("Message ID to reply to"),
|
|
101
|
+
},
|
|
102
|
+
annotations: WRITE,
|
|
103
|
+
}, async ({ chatId, stickerSet, index, replyTo }) => {
|
|
104
|
+
const err = await requireConnection(telegram);
|
|
105
|
+
if (err)
|
|
106
|
+
return fail(new Error(err));
|
|
107
|
+
try {
|
|
108
|
+
await telegram.sendSticker(chatId, stickerSet, index, replyTo);
|
|
109
|
+
return ok(`Sticker sent from "${stickerSet}" [${index}] to ${chatId}`);
|
|
110
|
+
}
|
|
111
|
+
catch (e) {
|
|
112
|
+
return fail(e);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
server.registerTool("telegram-get-recent-stickers", {
|
|
116
|
+
description: "Get recently used stickers. Returns each sticker with its list index and associated emoji",
|
|
117
|
+
inputSchema: {},
|
|
118
|
+
annotations: READ_ONLY,
|
|
119
|
+
}, async () => {
|
|
120
|
+
const err = await requireConnection(telegram);
|
|
121
|
+
if (err)
|
|
122
|
+
return fail(new Error(err));
|
|
123
|
+
try {
|
|
124
|
+
const stickers = await telegram.getRecentStickers();
|
|
125
|
+
if (stickers.length === 0) {
|
|
126
|
+
return ok("No recent stickers.");
|
|
127
|
+
}
|
|
128
|
+
const lines = [];
|
|
129
|
+
lines.push(`${stickers.length} recent sticker(s):\n`);
|
|
130
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
131
|
+
lines.push(`[${i}] ${stickers[i].emoji}`);
|
|
132
|
+
}
|
|
133
|
+
return ok(sanitize(lines.join("\n")));
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
return fail(e);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
package/package.json
CHANGED