ccbot 1.0.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/LICENSE +21 -0
- package/README.en.md +158 -0
- package/README.md +158 -0
- package/dist/commands/help.d.ts +1 -0
- package/dist/commands/help.js +17 -0
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +103 -0
- package/dist/commands/uninstall.d.ts +1 -0
- package/dist/commands/uninstall.js +41 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +85 -0
- package/dist/config-manager.d.ts +15 -0
- package/dist/config-manager.js +71 -0
- package/dist/hook/hook-handler.d.ts +9 -0
- package/dist/hook/hook-handler.js +126 -0
- package/dist/hook/hook-installer.d.ts +11 -0
- package/dist/hook/hook-installer.js +97 -0
- package/dist/hook/hook-server.d.ts +12 -0
- package/dist/hook/hook-server.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +59 -0
- package/dist/monitor/transcript-parser.d.ts +6 -0
- package/dist/monitor/transcript-parser.js +56 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +130 -0
- package/dist/telegram/bot.d.ts +15 -0
- package/dist/telegram/bot.js +79 -0
- package/dist/telegram/message-formatter.d.ts +13 -0
- package/dist/telegram/message-formatter.js +53 -0
- package/dist/telegram/message-sender.d.ts +2 -0
- package/dist/telegram/message-sender.js +44 -0
- package/dist/utils/error-utils.d.ts +1 -0
- package/dist/utils/error-utils.js +3 -0
- package/dist/utils/install-detection.d.ts +4 -0
- package/dist/utils/install-detection.js +40 -0
- package/package.json +67 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
export function formatNotification(data) {
|
|
3
|
+
const parts = [];
|
|
4
|
+
parts.push("🤖 *Claude Code Response*");
|
|
5
|
+
let projectLine = `📂 \`${escapeMarkdownV2(data.projectName)}\``;
|
|
6
|
+
if (data.durationMs > 0) {
|
|
7
|
+
projectLine += ` \\| ⏱ ${escapeMarkdownV2(formatDuration(data.durationMs))}`;
|
|
8
|
+
}
|
|
9
|
+
parts.push(projectLine);
|
|
10
|
+
parts.push("");
|
|
11
|
+
if (data.responseSummary) {
|
|
12
|
+
let summary = data.responseSummary;
|
|
13
|
+
if (summary.length > 2000) {
|
|
14
|
+
summary = summary.slice(0, 2000) + "...";
|
|
15
|
+
}
|
|
16
|
+
parts.push(escapeMarkdownV2(summary));
|
|
17
|
+
}
|
|
18
|
+
if (data.gitChanges.length > 0) {
|
|
19
|
+
parts.push("");
|
|
20
|
+
parts.push("📂 *Changes:*");
|
|
21
|
+
for (const change of data.gitChanges) {
|
|
22
|
+
const emoji = gitChangeEmoji(change.status);
|
|
23
|
+
parts.push(`${emoji} \`${escapeMarkdownV2(change.file)}\``);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return parts.join("\n");
|
|
27
|
+
}
|
|
28
|
+
export function escapeMarkdownV2(text) {
|
|
29
|
+
return text.replace(/[_*[\]()~`>#+\-=|{}.!]/g, "\\$&");
|
|
30
|
+
}
|
|
31
|
+
function formatDuration(ms) {
|
|
32
|
+
const totalSeconds = Math.floor(ms / 1000);
|
|
33
|
+
if (totalSeconds < 60)
|
|
34
|
+
return `${totalSeconds}s`;
|
|
35
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
36
|
+
const seconds = totalSeconds % 60;
|
|
37
|
+
return `${minutes}m${seconds}s`;
|
|
38
|
+
}
|
|
39
|
+
function gitChangeEmoji(status) {
|
|
40
|
+
switch (status) {
|
|
41
|
+
case "added":
|
|
42
|
+
return "➕";
|
|
43
|
+
case "deleted":
|
|
44
|
+
return "❌";
|
|
45
|
+
case "renamed":
|
|
46
|
+
return "📝";
|
|
47
|
+
default:
|
|
48
|
+
return "✏️";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function extractProjectName(cwd) {
|
|
52
|
+
return basename(cwd);
|
|
53
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const TELEGRAM_MAX_MESSAGE_LENGTH = 4096;
|
|
2
|
+
const PAGINATION_FOOTER_RESERVE = 30;
|
|
3
|
+
export async function sendMessage(bot, chatId, text) {
|
|
4
|
+
const pages = splitMessage(text, TELEGRAM_MAX_MESSAGE_LENGTH - PAGINATION_FOOTER_RESERVE);
|
|
5
|
+
for (let i = 0; i < pages.length; i++) {
|
|
6
|
+
let content = pages[i];
|
|
7
|
+
if (pages.length > 1) {
|
|
8
|
+
content = `${content}\n\n_\\[${i + 1}/${pages.length}\\]_`;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
await bot.sendMessage(chatId, content, { parse_mode: "MarkdownV2" });
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
await bot.sendMessage(chatId, pages[i]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function splitMessage(text, maxLen) {
|
|
19
|
+
if (text.length <= maxLen)
|
|
20
|
+
return [text];
|
|
21
|
+
const pages = [];
|
|
22
|
+
let remaining = text;
|
|
23
|
+
while (remaining.length > 0) {
|
|
24
|
+
if (remaining.length <= maxLen) {
|
|
25
|
+
pages.push(remaining);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
const splitAt = findSplitPoint(remaining, maxLen);
|
|
29
|
+
pages.push(remaining.slice(0, splitAt));
|
|
30
|
+
remaining = remaining.slice(splitAt);
|
|
31
|
+
}
|
|
32
|
+
return pages;
|
|
33
|
+
}
|
|
34
|
+
function findSplitPoint(text, maxLen) {
|
|
35
|
+
for (let i = maxLen; i > maxLen - 200 && i > 0; i--) {
|
|
36
|
+
if (text[i] === "\n" && text[i - 1] !== "\\")
|
|
37
|
+
return i + 1;
|
|
38
|
+
}
|
|
39
|
+
for (let i = maxLen; i > maxLen - 200 && i > 0; i--) {
|
|
40
|
+
if (text[i] === " " && text[i - 1] !== "\\")
|
|
41
|
+
return i + 1;
|
|
42
|
+
}
|
|
43
|
+
return maxLen;
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatError(err: unknown): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
export function detectInstallMethod() {
|
|
4
|
+
const execPath = process.argv[1] ?? "";
|
|
5
|
+
if (execPath.includes("npx") || execPath.includes(".npm/_npx")) {
|
|
6
|
+
return "npx";
|
|
7
|
+
}
|
|
8
|
+
const scriptDir = dirname(execPath);
|
|
9
|
+
if (isGitRepo(scriptDir)) {
|
|
10
|
+
return "git-clone";
|
|
11
|
+
}
|
|
12
|
+
return "global";
|
|
13
|
+
}
|
|
14
|
+
export function detectCliPrefix() {
|
|
15
|
+
const method = detectInstallMethod();
|
|
16
|
+
switch (method) {
|
|
17
|
+
case "npx":
|
|
18
|
+
return "npx ccbot";
|
|
19
|
+
case "git-clone":
|
|
20
|
+
return "node dist/index.js";
|
|
21
|
+
default:
|
|
22
|
+
return "ccbot";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function getGitRepoRoot(dir) {
|
|
26
|
+
let current = dir;
|
|
27
|
+
for (let i = 0; i < 5; i++) {
|
|
28
|
+
if (existsSync(join(current, ".git"))) {
|
|
29
|
+
return current;
|
|
30
|
+
}
|
|
31
|
+
const parent = dirname(current);
|
|
32
|
+
if (parent === current)
|
|
33
|
+
break;
|
|
34
|
+
current = parent;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function isGitRepo(dir) {
|
|
39
|
+
return getGitRepoRoot(dir) !== null;
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ccbot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code Telegram Notification Bot — get notified when Claude Code completes a response",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Khong Anh Dung (KAD)",
|
|
9
|
+
"email": "kaida.palooza@gmail.com"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/palooza-kaida/ccbot.git"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/palooza-kaida/ccbot#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/palooza-kaida/ccbot/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"claude",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"telegram",
|
|
23
|
+
"notification",
|
|
24
|
+
"bot",
|
|
25
|
+
"cli",
|
|
26
|
+
"developer-tools",
|
|
27
|
+
"typescript",
|
|
28
|
+
"automation"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"LICENSE",
|
|
36
|
+
"README.md",
|
|
37
|
+
"README.en.md"
|
|
38
|
+
],
|
|
39
|
+
"main": "dist/index.js",
|
|
40
|
+
"types": "dist/index.d.ts",
|
|
41
|
+
"bin": {
|
|
42
|
+
"ccbot": "dist/index.js"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc",
|
|
46
|
+
"prepublishOnly": "npm run build",
|
|
47
|
+
"start": "node dist/index.js",
|
|
48
|
+
"dev": "tsx src/index.ts",
|
|
49
|
+
"setup": "tsx src/index.ts setup",
|
|
50
|
+
"uninstall": "tsx src/index.ts uninstall",
|
|
51
|
+
"help": "tsx src/index.ts help",
|
|
52
|
+
"deploy:web": "gh-pages -d public"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@clack/prompts": "^1.0.1",
|
|
56
|
+
"express": "^5.0.1",
|
|
57
|
+
"node-telegram-bot-api": "^0.66.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/express": "^5.0.0",
|
|
61
|
+
"@types/node": "^22.13.4",
|
|
62
|
+
"@types/node-telegram-bot-api": "^0.64.8",
|
|
63
|
+
"gh-pages": "^6.3.0",
|
|
64
|
+
"tsx": "^4.19.3",
|
|
65
|
+
"typescript": "^5.7.3"
|
|
66
|
+
}
|
|
67
|
+
}
|