@gonzih/cc-tg 0.2.2 → 0.2.3
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/dist/bot.d.ts +1 -0
- package/dist/bot.js +39 -0
- package/dist/claude.d.ts +5 -0
- package/dist/claude.js +25 -0
- package/package.json +1 -1
package/dist/bot.d.ts
CHANGED
package/dist/bot.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import TelegramBot from "node-telegram-bot-api";
|
|
6
6
|
import { existsSync } from "fs";
|
|
7
7
|
import { resolve, basename } from "path";
|
|
8
|
+
import https from "https";
|
|
9
|
+
import http from "http";
|
|
8
10
|
import { ClaudeProcess, extractText } from "./claude.js";
|
|
9
11
|
import { transcribeVoice, isVoiceAvailable } from "./voice.js";
|
|
10
12
|
import { CronManager } from "./cron.js";
|
|
@@ -53,6 +55,11 @@ export class CcTgBot {
|
|
|
53
55
|
await this.handleVoice(chatId, msg);
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
58
|
+
// Photo — send as base64 image content block to Claude
|
|
59
|
+
if (msg.photo?.length) {
|
|
60
|
+
await this.handlePhoto(chatId, msg);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
56
63
|
const text = msg.text?.trim();
|
|
57
64
|
if (!text)
|
|
58
65
|
return;
|
|
@@ -120,6 +127,26 @@ export class CcTgBot {
|
|
|
120
127
|
await this.bot.sendMessage(chatId, `Voice transcription failed: ${err.message}`);
|
|
121
128
|
}
|
|
122
129
|
}
|
|
130
|
+
async handlePhoto(chatId, msg) {
|
|
131
|
+
// Pick highest resolution photo
|
|
132
|
+
const photos = msg.photo;
|
|
133
|
+
const best = photos[photos.length - 1];
|
|
134
|
+
const caption = msg.caption?.trim();
|
|
135
|
+
console.log(`[photo:${chatId}] received image file_id=${best.file_id}`);
|
|
136
|
+
this.bot.sendChatAction(chatId, "typing").catch(() => { });
|
|
137
|
+
try {
|
|
138
|
+
const fileLink = await this.bot.getFileLink(best.file_id);
|
|
139
|
+
const imageData = await fetchAsBase64(fileLink);
|
|
140
|
+
// Telegram photos are always JPEG
|
|
141
|
+
const session = this.getOrCreateSession(chatId);
|
|
142
|
+
session.claude.sendImage(imageData, "image/jpeg", caption);
|
|
143
|
+
this.startTyping(chatId, session);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
console.error(`[photo:${chatId}] error:`, err.message);
|
|
147
|
+
await this.bot.sendMessage(chatId, `Failed to process image: ${err.message}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
123
150
|
getOrCreateSession(chatId) {
|
|
124
151
|
const existing = this.sessions.get(chatId);
|
|
125
152
|
if (existing && !existing.claude.exited)
|
|
@@ -356,6 +383,18 @@ export class CcTgBot {
|
|
|
356
383
|
}
|
|
357
384
|
}
|
|
358
385
|
}
|
|
386
|
+
/** Download a URL and return its contents as a base64 string */
|
|
387
|
+
function fetchAsBase64(url) {
|
|
388
|
+
return new Promise((resolve, reject) => {
|
|
389
|
+
const client = url.startsWith("https") ? https : http;
|
|
390
|
+
client.get(url, (res) => {
|
|
391
|
+
const chunks = [];
|
|
392
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
393
|
+
res.on("end", () => resolve(Buffer.concat(chunks).toString("base64")));
|
|
394
|
+
res.on("error", reject);
|
|
395
|
+
}).on("error", reject);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
359
398
|
function splitMessage(text, maxLen = 4096) {
|
|
360
399
|
if (text.length <= maxLen)
|
|
361
400
|
return [text];
|
package/dist/claude.d.ts
CHANGED
|
@@ -30,6 +30,11 @@ export declare class ClaudeProcess extends EventEmitter {
|
|
|
30
30
|
private _exited;
|
|
31
31
|
constructor(opts?: ClaudeOptions);
|
|
32
32
|
sendPrompt(text: string): void;
|
|
33
|
+
/**
|
|
34
|
+
* Send an image (with optional text caption) to Claude via stream-json content blocks.
|
|
35
|
+
* mediaType: image/jpeg | image/png | image/gif | image/webp
|
|
36
|
+
*/
|
|
37
|
+
sendImage(base64Data: string, mediaType: string, caption?: string): void;
|
|
33
38
|
kill(): void;
|
|
34
39
|
get exited(): boolean;
|
|
35
40
|
private drainBuffer;
|
package/dist/claude.js
CHANGED
|
@@ -69,6 +69,31 @@ export class ClaudeProcess extends EventEmitter {
|
|
|
69
69
|
});
|
|
70
70
|
this.proc.stdin.write(payload + "\n");
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Send an image (with optional text caption) to Claude via stream-json content blocks.
|
|
74
|
+
* mediaType: image/jpeg | image/png | image/gif | image/webp
|
|
75
|
+
*/
|
|
76
|
+
sendImage(base64Data, mediaType, caption) {
|
|
77
|
+
if (this._exited)
|
|
78
|
+
throw new Error("Claude process has exited");
|
|
79
|
+
const content = [];
|
|
80
|
+
if (caption) {
|
|
81
|
+
content.push({ type: "text", text: caption });
|
|
82
|
+
}
|
|
83
|
+
content.push({
|
|
84
|
+
type: "image",
|
|
85
|
+
source: {
|
|
86
|
+
type: "base64",
|
|
87
|
+
media_type: mediaType,
|
|
88
|
+
data: base64Data,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const payload = JSON.stringify({
|
|
92
|
+
type: "user",
|
|
93
|
+
message: { role: "user", content },
|
|
94
|
+
});
|
|
95
|
+
this.proc.stdin.write(payload + "\n");
|
|
96
|
+
}
|
|
72
97
|
kill() {
|
|
73
98
|
this.proc.kill();
|
|
74
99
|
}
|