supertelegram 0.2.0 → 0.3.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 CHANGED
@@ -27,21 +27,36 @@ that's it! now you're ready to use telegram from the cli.
27
27
 
28
28
  ## usage
29
29
 
30
+ ### messages
31
+
30
32
  ```bash
31
33
  # send a message
32
- telegram send <username> <message>
34
+ telegram send @username "hello there"
33
35
 
34
- # read messages
35
- telegram read <username> [limit]
36
+ # read messages (shows [photo], [video], [file] indicators)
37
+ telegram read @username 10
36
38
 
37
39
  # reply to latest message
38
- telegram reply <username> <message>
40
+ telegram reply "John" "hey back!"
39
41
 
40
- # get unread messages (json output)
41
- telegram unread [limit]
42
+ # get unread messages (json output with media info)
43
+ telegram unread 20
42
44
 
43
45
  # list dialogs
44
- telegram dialogs [limit]
46
+ telegram dialogs 10
47
+ ```
48
+
49
+ ### media (images/videos/files)
50
+
51
+ ```bash
52
+ # send a file with optional caption
53
+ telegram send-file @username photo.jpg
54
+ telegram send-file @username video.mp4 "check this out!"
55
+
56
+ # download media from a message
57
+ # (use 'read' command to get message IDs)
58
+ telegram download @username 12345 ./downloaded.jpg
59
+ telegram download @username 12346 # auto-named file
45
60
  ```
46
61
 
47
62
  ### config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supertelegram",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "telegram cli for humans and bots",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -6,9 +6,12 @@ import {
6
6
  disconnect,
7
7
  getClient,
8
8
  login as telegramLogin,
9
+ sendFile as telegramSendFile,
10
+ downloadMedia as telegramDownloadMedia,
9
11
  } from "../client/telegram";
10
12
  import { askPhoneNumber, askPhoneCode, askPassword, askAppId, askAppHash } from "./prompts";
11
13
  import { getApiCredentials, setConfig } from "../config/manager";
14
+ import { Api } from "telegram";
12
15
 
13
16
  export async function send(username: string, message: string) {
14
17
  if (!(await isLoggedIn())) {
@@ -21,6 +24,29 @@ export async function send(username: string, message: string) {
21
24
  await disconnect();
22
25
  }
23
26
 
27
+ function getMediaInfo(msg: Api.Message): string {
28
+ if (!msg.media) return "";
29
+
30
+ if (msg.media instanceof Api.MessageMediaPhoto) {
31
+ return " [photo]";
32
+ }
33
+ if (msg.media instanceof Api.MessageMediaDocument) {
34
+ const doc = msg.media.document;
35
+ if (doc instanceof Api.Document) {
36
+ const attrs = doc.attributes;
37
+ const filenameAttr = attrs.find((a) => "fileName" in a && a.fileName);
38
+ const filename = filenameAttr && "fileName" in filenameAttr ? filenameAttr.fileName : undefined;
39
+ const isVideo = attrs.some((a) => a instanceof Api.DocumentAttributeVideo);
40
+ const isAudio = attrs.some((a) => a instanceof Api.DocumentAttributeAudio);
41
+
42
+ if (isVideo) return ` [video${filename ? `: ${filename}` : ""}]`;
43
+ if (isAudio) return ` [audio${filename ? `: ${filename}` : ""}]`;
44
+ return ` [file${filename ? `: ${filename}` : ""}]`;
45
+ }
46
+ }
47
+ return " [media]";
48
+ }
49
+
24
50
  export async function read(username: string, limit = 10) {
25
51
  if (!(await isLoggedIn())) {
26
52
  console.error("not logged in. run: telegram login");
@@ -31,7 +57,8 @@ export async function read(username: string, limit = 10) {
31
57
  for (const msg of messages.reverse()) {
32
58
  const sender = msg.senderId?.toString() ?? "unknown";
33
59
  const date = msg.date ? new Date(msg.date * 1000).toISOString() : "";
34
- console.log(`[${date}] [${sender}]: ${msg.message}`);
60
+ const mediaInfo = getMediaInfo(msg);
61
+ console.log(`[${date}] [${sender}]: ${msg.message}${mediaInfo}`);
35
62
  }
36
63
  await disconnect();
37
64
  }
@@ -88,6 +115,7 @@ export async function unread(limit = 20) {
88
115
  messages: fromOthers.map((m) => ({
89
116
  id: m.id,
90
117
  text: m.message,
118
+ media: m.media ? getMediaInfo(m).trim() : null,
91
119
  date: m.date ? new Date(m.date * 1000).toISOString() : null,
92
120
  })),
93
121
  });
@@ -179,3 +207,41 @@ export async function config(action?: string, key?: string, value?: string) {
179
207
  console.log(" telegram config set appHash <hash>");
180
208
  console.log(" telegram config get appId");
181
209
  }
210
+
211
+ export async function sendFile(username: string, filePath: string, caption?: string) {
212
+ if (!(await isLoggedIn())) {
213
+ console.error("not logged in. run: telegram login");
214
+ process.exit(1);
215
+ }
216
+
217
+ const result = await telegramSendFile(username, filePath, caption);
218
+ console.log(`sent file id: ${result.id}`);
219
+ await disconnect();
220
+ }
221
+
222
+ export async function downloadMedia(username: string, messageId: number, outputPath?: string) {
223
+ if (!(await isLoggedIn())) {
224
+ console.error("not logged in. run: telegram login");
225
+ process.exit(1);
226
+ }
227
+
228
+ const messages = await getMessages(username, 100);
229
+ const msg = messages.find((m) => m.id === messageId);
230
+
231
+ if (!msg) {
232
+ console.error(`message ${messageId} not found`);
233
+ await disconnect();
234
+ process.exit(1);
235
+ }
236
+
237
+ if (!msg.media) {
238
+ console.error(`message ${messageId} has no media`);
239
+ await disconnect();
240
+ process.exit(1);
241
+ }
242
+
243
+ console.log("downloading media...");
244
+ const path = await telegramDownloadMedia(msg, outputPath);
245
+ console.log(`saved to: ${path || outputPath || "unknown"}`);
246
+ await disconnect();
247
+ }
package/src/cli/run.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bun
2
- import { send, read, dialogs, unread, reply, login, config } from "./commands";
2
+ import { send, read, dialogs, unread, reply, login, config, sendFile, downloadMedia } from "./commands";
3
3
  import { setVerbose, setSessionPath } from "../client/telegram";
4
4
 
5
5
  const VERSION = "0.1.0";
@@ -12,13 +12,15 @@ usage:
12
12
  ${NAME} <command> [options]
13
13
 
14
14
  commands:
15
- send <chat> <message> send a message to a chat
16
- read <chat> [limit] read messages from a chat (default: 10)
17
- reply <chat> <message> reply to a chat by name (partial match)
18
- dialogs [limit] list recent dialogs (default: 10)
19
- unread [limit] show unread messages as json (default: 20)
20
- login authenticate with telegram
21
- config set <key> <val> set API credentials (appId, appHash)
15
+ send <chat> <message> send a message to a chat
16
+ send-file <chat> <path> send a file/image/video (optional: caption)
17
+ read <chat> [limit] read messages from a chat (default: 10)
18
+ download <chat> <id> [out] download media from message id
19
+ reply <chat> <message> reply to a chat by name (partial match)
20
+ dialogs [limit] list recent dialogs (default: 10)
21
+ unread [limit] show unread messages as json (default: 20)
22
+ login authenticate with telegram
23
+ config set <key> <val> set API credentials (appId, appHash)
22
24
 
23
25
  options:
24
26
  -v, --verbose show debug logs
@@ -27,7 +29,9 @@ options:
27
29
 
28
30
  examples:
29
31
  ${NAME} send @username "hello there"
32
+ ${NAME} send-file @username photo.jpg "check this out"
30
33
  ${NAME} read @username 5
34
+ ${NAME} download @username 12345 ./photo.jpg
31
35
  ${NAME} reply "John" "hey!"
32
36
  ${NAME} unread
33
37
  ${NAME} dialogs 20
@@ -88,6 +92,22 @@ async function main() {
88
92
  await reply(rest[0], rest.slice(1).join(" "));
89
93
  break;
90
94
 
95
+ case "send-file":
96
+ if (rest.length < 2 || !rest[0] || !rest[1]) {
97
+ console.error("usage: telegram send-file <chat> <path> [caption]");
98
+ process.exit(1);
99
+ }
100
+ await sendFile(rest[0], rest[1], rest.slice(2).join(" ") || undefined);
101
+ break;
102
+
103
+ case "download":
104
+ if (rest.length < 2 || !rest[0] || !rest[1]) {
105
+ console.error("usage: telegram download <chat> <message-id> [output-path]");
106
+ process.exit(1);
107
+ }
108
+ await downloadMedia(rest[0], Number.parseInt(rest[1]), rest[2]);
109
+ break;
110
+
91
111
  case "login":
92
112
  await login();
93
113
  break;
@@ -84,6 +84,34 @@ export async function sendMessage(
84
84
  return c.sendMessage(username, { message });
85
85
  }
86
86
 
87
+ export async function sendFile(
88
+ username: string,
89
+ filePath: string,
90
+ caption?: string
91
+ ): Promise<Api.Message> {
92
+ const c = await getClient();
93
+ return c.sendFile(username, {
94
+ file: filePath,
95
+ caption: caption,
96
+ });
97
+ }
98
+
99
+ export async function downloadMedia(
100
+ message: Api.Message,
101
+ outputPath?: string
102
+ ): Promise<string | undefined> {
103
+ const c = await getClient();
104
+ if (!message.media) {
105
+ return undefined;
106
+ }
107
+
108
+ const buffer = await c.downloadMedia(message, {
109
+ outputFile: outputPath,
110
+ });
111
+
112
+ return buffer as string | undefined;
113
+ }
114
+
87
115
  export async function getMessages(
88
116
  username: string,
89
117
  limit = 10