claude-telegram-bot 0.1.0 → 0.2.1

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.ko.md CHANGED
@@ -1,306 +1,255 @@
1
- # Claude Telegram Bot (범용)
1
+ # Claude Telegram Bot
2
2
 
3
3
  **한국어** · [English](./README.md)
4
4
 
5
- **의존성 0, 단일 파일, 데몬으로 도는 Claude Code Bun도 Python도 있는 세션도 필요 없음.**
6
-
7
- 텔레그램 메시지를 받아 지정한 프로젝트 폴더에서 `claude -p`(헤드리스 모드)를 실행하고
8
- 결과를 다시 텔레그램으로 보내주는 작은 브릿지. `bot.mjs` 파일 하나, Node 18+ 내장 기능만 사용 —
9
- `npm install`할 것도 없고, 감사(audit)할 거라곤 읽기 쉬운 ~400줄이 전부.
5
+ 텔레그램으로 메시지를 보내면, 집이나 서버에 켜둔 Claude Code 작업하고 결과를 다시 텔레그램으로 돌려주는 봇입니다.
10
6
 
11
7
  ```
12
- [] → Telegram → bot.mjs → claude -p (config.projectDir) → 결과 → Telegram
8
+ [] → 텔레그램 → bot.mjs → claude -p (작업 폴더) → 결과 → 텔레그램
13
9
  ```
14
10
 
15
- 검증 완료: `bypassPermissions` 헤드리스 모드 정상 동작 확인. 쉘·git·테스트까지 자동 실행됨.
16
- **백그라운드 데몬**(launchd)으로 돌아서 인터랙티브 세션을 열어둘 필요가 없음.
11
+ ## 만들었나
17
12
 
18
- > ### ⚠️ 도구는 설계상 원격 코드 실행 도구입니다. 실행 전에 [보안](#-보안) 섹션을 읽으세요.
19
- > 텔레그램으로 보낸 메시지는 봇이 도는 머신에서 **명령으로 실행**됩니다.
20
- > `permissionMode: bypassPermissions`면 한 줄짜리 메시지가 네 계정 권한으로 **무엇이든** 실행할 수 있습니다.
13
+ 자리를 비운 사이에도 폰으로 빌드를 돌려보거나 간단한 수정을 맡기고 싶을 때가 있습니다. 그렇다고 외부에서 데스크톱에 원격 접속해서 터미널을 여는 건 번거롭죠.
21
14
 
22
- ## 다른 도구와 비교
15
+ 텔레그램 봇이면 충분합니다. 메시지를 보내면 집(또는 개인 서버)의 `claude`가 헤드리스로 돌아 작업하고, 답을 채팅으로 보내줍니다. 별도 웹 대시보드도, 추가 서버도 없습니다. 파일 하나(`bot.mjs`)와 설정 파일 하나가 전부입니다.
23
16
 
24
- 분야는 이미 붐비고, Anthropic도 공식 솔루션을 내놨다. 솔직한 지도를 그려두니 용도에 맞게 고르면 된다:
17
+ ## 이런 분께
25
18
 
26
- | | | [공식 Claude Code Channels](https://code.claude.com/docs/en/channels) | [claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) |
27
- |---|---|---|---|
28
- | 런타임 | **Node 내장만** | Bun + MCP 플러그인 | Python 3.11+ + 라이브러리 |
29
- | 실행 모델 | 메시지당 헤드리스 `claude -p` | **열려있는** `claude --channels` 세션에 이벤트 push | Claude SDK / CLI |
30
- | 상시 가동 형태 | **백그라운드 데몬** (세션 안 떠도 됨) | 계속 떠 있는 인터랙티브 세션 | 서비스 / 데몬 |
31
- | 한 레포에 권한 차등 멀티 페르소나 | **가능** (개발자=`bypass`, 기획자=`plan`) | 불가 | 불가 |
32
- | 작업별 권한 승인 (인라인 버튼) | 없음 (`permissionMode`로) | **있음** | 일부 |
33
- | 기능 폭 (웹훅·cron·음성·export) | 최소 | 중간 | **큼** |
34
- | 읽고/포크할 코드량 | **~400줄, 단일 파일** | 큼 | 큼 |
19
+ - 외출·이동 중에 폰으로 테스트나 빌드를 돌려보고 싶은
20
+ - 자리를 비운 사이 간단한 수정·커밋을 맡겨두고 싶은 분
21
+ - 맥미니나 홈서버에 띄워두고 어디서든 접속하고 싶은
22
+ - 거창한 셀프호스트 구성 없이 텔레그램만으로 끝내고 싶은
35
23
 
36
- **작업별 승인이 필요하고** 세션을 열어두는괜찮다면 공식 Channels. **기능이 많이 필요하면** →
37
- claude-code-telegram. **의존성 0·감사 가능·데몬**이 필요하거나, 특히 같은 프로젝트에 **권한이 다른
38
- 역할별 페르소나 봇**을 띄우고 싶다면 → 이 봇.
24
+ OpenClaw처럼 UI까지 갖춘 구성을 써봤다면, 이 프로젝트는 그 반대편이라고 보면 됩니다. 대시보드도 데이터베이스도 없고, 이미 설치해 로그인해 둔 `claude` CLI를 그대로 불러 쓰는 전부입니다. 설정과 코드를 30초면 훑어볼 있습니다.
39
25
 
40
- ## 여러 프로젝트 동시 운영
26
+ ## 동작 방식
41
27
 
42
- 코드는 프로젝트 비종속이라, **프로젝트마다 config 파일 하나씩**만 만들면 여러 개를 동시에 돌릴 수 있다.
28
+ - 텔레그램 API를 롱폴링으로 받습니다.
29
+ - 메시지가 오면 작업 폴더(`projectDir`)에서 `claude -p`(헤드리스 모드)를 실행합니다.
30
+ - 세션은 `--resume`으로 이어지므로, 봇을 재시작해도 대화 맥락이 유지됩니다.
31
+ - 의존성이 없습니다. Node 18+ 내장 기능(`fetch`, `child_process`)만 씁니다.
43
32
 
44
- - 실행: `node bot.mjs /절대경로/프로젝트.config.json` (인자 없으면 같은 폴더의 `config.json`)
45
- - 상태(`state.json`)·첨부(`attachments/`)는 **그 config 파일이 있는 폴더**에 저장돼 프로젝트끼리 안 섞임
46
- - **주의**: 텔레그램은 토큰당 폴링 1개만 허용 → 프로젝트마다 **BotFather 토큰을 따로** 만들어야 동시 운영 가능
47
- - 상시 가동은 `com.claudebot.example.plist`를 프로젝트별로 복사해 등록 (아래 launchd 섹션 참고)
33
+ ## 다른 도구와 비교
48
34
 
49
- 예) 프로젝트:
50
- ```
51
- ~/projects/A/claudebot.config.json (토큰 A, projectDir=~/projects/A)
52
- ~/projects/B/claudebot.config.json (토큰 B, projectDir=~/projects/B)
53
- node bot.mjs ~/projects/A/claudebot.config.json # 인스턴스 A
54
- node bot.mjs ~/projects/B/claudebot.config.json # 인스턴스 B
55
- ```
35
+ 분야에는 이미 여러 도구가 있고, Anthropic도 공식 기능을 내놨습니다. 용도에 맞게 고르시면 됩니다.
56
36
 
57
- ## 여러 페르소나(역할)
37
+ | | 이 봇 | [공식 Claude Code Channels](https://code.claude.com/docs/en/channels) | [claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) |
38
+ |---|---|---|---|
39
+ | 런타임 | Node 내장만 | Bun + MCP 플러그인 | Python 3.11+ |
40
+ | 실행 모델 | 메시지마다 `claude -p` | 떠 있는 세션에 이벤트 push | Claude SDK / CLI |
41
+ | 상시 가동 | 백그라운드 데몬 | 인터랙티브 세션 유지 | 서비스 / 데몬 |
42
+ | 권한 차등 페르소나 | 가능 | 불가 | 불가 |
43
+ | 작업별 권한 승인 버튼 | 없음 | 있음 | 일부 |
44
+ | 기능 범위 | 최소 | 중간 | 많음 |
58
45
 
59
- **같은 프로젝트**를 역할별 봇으로 나눠 띄울 수 있다 (예: **개발자** + **기획자**).
60
- 코드는 하나, **config 파일만 역할별로** 따로 둔다.
46
+ 정리하면 이렇습니다.
61
47
 
62
- - **`persona`**: config에 역할 시스템 프롬프트를 넣으면 봇의 정체성이 된다.
63
- 텔레그램용 간결 지침은 자동으로 함께 주입되므로 `persona`엔 역할만 적으면 됨.
64
- - **`permissionMode`로 권한 차등**: 같은 폴더를 공유하므로 **셸을 쓰는 봇(`bypassPermissions`)은
65
- 하나로 제한**하면 동시 편집 충돌을 피할 수 있다. 읽기·계획만 시키려면 `plan`.
66
- - **세션 분리**: `state` 파일은 config 이름에서 파생된다
67
- (`config.json`→`state.json`, `dev.config.json`→`dev.config.state.json`). 같은 폴더에
68
- config 여러 개를 둬도 봇끼리 맥락이 안 섞임.
69
- - **봇마다 토큰 1개**: 각 봇은 BotFather에서 별도 토큰 발급 (`allowedChatId`는 동일해도 됨).
48
+ - 작업마다 승인 버튼이 필요하고 세션을 계속 띄워둬도 괜찮다면 **공식 Channels**
49
+ - 웹훅, cron, 음성 기능이 많이 필요하다면 **claude-code-telegram**
50
+ - 구성을 최대한 단순하게 가져가고 싶거나, 코드로 여러 페르소나 봇을 굴리고 싶다면 → **이 봇**
70
51
 
71
- 예) 개발자 + 기획자:
72
- ```
73
- dev.config.json (permissionMode: bypassPermissions, persona: "시니어 개발자...")
74
- planner.config.json (permissionMode: plan, persona: "기획자 겸 UX 담당...")
75
- node bot.mjs dev.config.json
76
- node bot.mjs planner.config.json
77
- ```
52
+ ## 요구 사항
78
53
 
79
- | | permissionMode | 역할 |
80
- |---|---|---|
81
- | 개발자 | `bypassPermissions` | 코드 구현·수정·테스트·git |
82
- | 기획자 | `plan` (읽기·계획만) | 기능 제안·스펙·UX/디자인 방향 |
54
+ - Node.js 18 이상 (내장 `fetch` 사용)
55
+ - `claude` CLI 설치 및 로그인 (봇은 이 인증을 그대로 씁니다)
56
+ - 텔레그램 토큰 ([@BotFather](https://t.me/BotFather)에서 발급)
83
57
 
84
- > 상시 가동은 `com.claudebot.example.plist`를 **봇마다** 복사해 `Label`·config 인자·로그
85
- > 경로를 다르게 등록한다 (아래 launchd 섹션 참고).
58
+ 상시 가동 예시는 macOS의 launchd 기준입니다. 리눅스라면 systemd나 pm2로 같은 구성을 만들면 됩니다.
86
59
 
87
- ---
60
+ ## 설치 & 실행
88
61
 
89
- ## 빠른 시작 (내가 일)
62
+ 라이브러리가 아니라 단독으로 도는 CLI입니다. `import`해서 쓰는 게 아니라, 전역으로 설치하거나 `npx`로 실행합니다. 작업 폴더는 설정의 `projectDir`로 정하므로, 봇을 어디에 설치하든 상관없습니다.
90
63
 
91
- **1) 토큰 발급** — 텔레그램에서 `@BotFather` → `/newbot` → 이름/username 지정 → 토큰 복사
64
+ **npx로 바로 실행**
92
65
 
93
- **2) 설정 파일 생성**
94
66
  ```sh
95
- cd /Users/jtchoi/Projects/cube-brain-trainer/tools/claude-telegram-bot
96
- cp config.example.json config.json
97
- # config.json BotFather 토큰만 붙여넣기 (allowedChatId 는 일단 비워둠)
98
- # permissionMode 는 이미 bypassPermissions 로 설정돼 있음
67
+ npx claude-telegram-bot init # 현재 폴더에 config.json 생성
68
+ # config.json 편집 (token, projectDir 등)
69
+ npx claude-telegram-bot # config.json 으로 실행
99
70
  ```
100
71
 
101
- **3) chatId 알아내기 + 실행**
72
+ **전역 설치 (상시 가동에 권장)**
73
+
102
74
  ```sh
103
- node bot.mjs
104
- # → 텔레그램에서 봇에게 아무 메시지나 전송 → 봇이 이 채팅의 chatId 를 답장
105
- # 숫자를 config.json 의 allowedChatId 에 넣고 봇 재시작 (Ctrl+C 후 다시 node bot.mjs)
106
- # 이제 너만 사용 가능
75
+ npm i -g claude-telegram-bot
76
+
77
+ claude-telegram-bot init ~/botconfigs/myproj # 해당 경로에 config.json 생성
78
+ # config.json 편집
79
+ claude-telegram-bot ~/botconfigs/myproj/config.json
107
80
  ```
108
81
 
109
- **4) 사용** 텔레그램으로 메시지 전송:
110
- - `cross 솔버 테스트 돌리고 통과하면 커밋 후 push 해줘`
111
- - `solve-2nd-floor-edges.ts 에 엣지 케이스 추가해줘`
82
+ > **설정 파일은 git에 올리지 마세요.** config 파일에는 봇 토큰이 들어 있습니다. git 레포 안에 둔다면 `config.json`, `state*.json`, `attachments/`를 그 프로젝트의 `.gitignore`에 추가하세요. 이 레포는 해당 패턴을 이미 무시하므로 `claudebot.config.json` 같은 이름도 안전하지만, 다른 프로젝트는 직접 지정해야 합니다.
112
83
 
113
- **5) 항상 켜두기** (선택)
114
- ```sh
115
- cp com.cube.claudebot.plist ~/Library/LaunchAgents/
116
- launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
117
- tail -f bot.log
118
- ```
84
+ ## 설정
85
+
86
+ `config.json`의 키는 다음과 같습니다.
119
87
 
120
- > ⚠️ 봇은 현재 작업 브랜치(`develop`) 기준으로 동작함. push 시점 등 브랜치 관리는 메시지로 명확히 지시할 것.
88
+ | | 설명 |
89
+ |---|---|
90
+ | `token` | BotFather에서 받은 봇 토큰 |
91
+ | `allowedChatId` | 처음엔 비워두세요. 봇이 chatId를 알려줍니다 (아래 첫 실행 참고) |
92
+ | `projectDir` | Claude가 작업할 폴더의 절대경로 |
93
+ | `claudeBin` | `which claude` 결과 (절대경로 권장) |
94
+ | `permissionMode` | `plan`(읽기·계획만) / `acceptEdits`(편집 자동 승인) / `bypassPermissions`(쉘 포함 전부 자동) |
95
+ | `model` | 비우면 기본 모델. `opus`, `sonnet` 등 |
96
+ | `lang` | (선택) UI 언어. 비우면 사용자별 자동 판별(기본 영어, 텔레그램이 한국어면 한국어). `"en"`/`"ko"`로 고정 가능 |
97
+ | `name` | (선택) `/help`에 표시되는 봇 이름. 여러 봇 구분용 |
98
+ | `persona` | (선택) 역할 시스템 프롬프트. 페르소나 봇 정의용 |
99
+ | `appendSystemPrompt` | (선택) 기본 "간결하게 답하기" 지침을 직접 덮어쓸 때 |
100
+ | `env` | (선택) `claude` 프로세스에 넘길 환경 변수 |
101
+ | `schedule` | (선택) 정해진 시각에 프롬프트를 실행하는 cron 작업 — [예약 작업](#예약-작업-cron) 참고 |
121
102
 
122
- 자세한 설명은 아래 섹션 참고.
103
+ `state.json`과 첨부 파일(`attachments/`)은 config 파일과 같은 폴더에 저장됩니다. 그래서 config만 따로 두면 프로젝트끼리 섞이지 않습니다.
123
104
 
124
- ---
105
+ ## 첫 실행
125
106
 
126
- ## 1. 만들기 (BotFather)
107
+ 1. **봇 토큰 발급** — 텔레그램에서 [@BotFather](https://t.me/BotFather)에게 `/newbot`을 보내고, 이름과 username(`_bot`으로 끝나야 함)을 정하면 토큰을 줍니다. `config.json`의 `token`에 넣고 `allowedChatId`는 비워둡니다.
108
+ 2. **chatId 확인 후 잠그기** — 봇을 실행하고 텔레그램에서 아무 메시지나 보내면, 봇이 이 채팅의 `chatId`를 답장합니다. 그 숫자를 `allowedChatId`에 넣고 재시작하면 나만 쓸 수 있습니다. ([보안](#보안) 참고 — 이게 유일한 인증 수단입니다.)
109
+ 3. **사용** — 그냥 메시지를 보냅니다.
110
+ - `테스트 돌려보고 통과하면 커밋하고 push 해줘`
111
+ - `api.ts 에 에러 핸들링 추가해줘`
127
112
 
128
- 1. 텔레그램에서 **@BotFather** 검색 대화 시작
129
- 2. `/newbot` 입력 → 봇 이름과 username 지정 (username은 `_bot`으로 끝나야 함)
130
- 3. 받은 **토큰**을 복사 (예: `123456789:AAxxxxxxxx`)
113
+ 명령어: `/new`(맥락 초기화) · `/cron`(예약 작업 보기·추가·삭제) · `/restart`(문법 검사 후 재시작) · `/id`(채팅 ID 확인) · `/help`(도움말)
131
114
 
132
- ## 2. 설정
115
+ > **`/restart`** 는 먼저 `bot.mjs` 에 `node --check` 를 돌려 **문법 오류가 있으면 재시작을 취소**합니다(잘못된 수정이 봇을 크래시 루프에 빠뜨리는 것 방지). 통과하면 프로세스를 종료하고, 다시 띄우는 건 프로세스 관리자에게 맡깁니다. [launchd 설정](#상시-실행-launchd)(`KeepAlive`)이면 바로 동작하고, 관리자 없이 `node bot.mjs` 로만 돌리면 그냥 멈춥니다. 재시작 후 대화 세션은 `state.json` 의 ID로 이어집니다.
133
116
 
134
- ```sh
135
- cd tools/claude-telegram-bot
136
- cp config.example.json config.json
137
- ```
117
+ ## 사용 메모
138
118
 
139
- `config.json` 편집:
119
+ - **세션 유지** — 대화는 `--resume`으로 자동으로 이어집니다. 마지막 세션 ID가 `state.json`에 저장되므로 봇을 재시작해도 맥락이 남습니다. 새로 시작하려면 `/new`.
120
+ - **간결한 답변** — 텔레그램에 맞게 짧게 답하도록 시스템 프롬프트가 기본으로 붙습니다. 바꾸려면 `appendSystemPrompt`에 직접 넣으세요 (빈 문자열이면 끔).
121
+ - **언어** — 봇 자체 문구(`/help`, 명령 메뉴, 상태 메시지)는 **기본 영어**, 텔레그램이 한국어인 사용자에겐 한국어로 나옵니다. `lang`(`"en"`/`"ko"`)으로 고정할 수 있습니다. Claude의 실제 답변은 **사용자가 쓴 언어**를 따라갑니다. `/` 명령 메뉴는 `setMyCommands`로 언어별 등록됩니다.
122
+ - **서식 변환** — 답변의 마크다운(굵게·코드·표 등)을 텔레그램 HTML로 바꿔 보냅니다. 변환이 깨지는 경우엔 평문으로 다시 보냅니다.
123
+ - **첨부 파일** — 사진·문서·음성·영상을 보내면 `attachments/`에 내려받고, 그 경로를 Claude에게 전달합니다(캡션도 함께). 이미지는 Read로 열어볼 수 있습니다.
140
124
 
141
- | | 설명 |
142
- |---|---|
143
- | `token` | BotFather에서 받은 토큰 |
144
- | `allowedChatId` | **비워두고 시작** → 봇이 알려줌 (아래 3단계) |
145
- | `projectDir` | 작업 폴더 (기본값 그대로 두면 됨) |
146
- | `claudeBin` | `which claude` 결과 (절대경로 권장) |
147
- | `permissionMode` | `plan`(읽기·계획만) / `acceptEdits`(파일편집 자동승인) / `bypassPermissions`(전부 자동, 쉘 포함) |
148
- | `model` | 비워두면 기본 모델. `opus` / `sonnet` 등 |
149
- | `name` | (선택) `/help`에 표시되는 봇 이름 — 멀티 봇 구분용 |
150
- | `persona` | (선택) 역할 시스템 프롬프트 — 페르소나(개발자/기획자 등) 정의. 자세히는 아래 페르소나 섹션 |
125
+ ## 예약 작업 (cron)
151
126
 
152
- ## 3. chatId 알아내기
127
+ config에 `schedule` 배열을 두면 정해진 시각에 프롬프트를 자동 실행합니다 — 아침 브리핑, 주기적 점검, 리마인더 등. 각 항목은 프롬프트를 실행하고 결과를 `allowedChatId`로 보냅니다.
153
128
 
154
- ```sh
155
- node bot.mjs
129
+ ```json
130
+ "schedule": [
131
+ { "cron": "0 9 * * 1-5", "label": "아침 브리핑", "prompt": "오늘 처리할 이슈/할 일을 요약해줘" },
132
+ { "cron": "*/30 * * * *", "prompt": "CI 상태 확인해서 빨간 게 있을 때만 알려줘" }
133
+ ]
156
134
  ```
157
135
 
158
- 실행 텔레그램에서 봇에게 아무 메시지나 보내면, 봇이 **이 채팅의 chatId**를 답장해줌.
159
- 숫자를 `config.json`의 `allowedChatId`에 넣고 봇을 재시작. 이제 너만 있음.
160
-
161
- ## 4. 사용
136
+ - **`cron`** 표준 5필드 `분 요일` (예: `0 9 * * 1-5` = 평일 09:00). `*`, 목록(`1,3,5`), 범위(`1-5`), 스텝(`*/15`)을 지원합니다. 요일 `0`과 `7`은 둘 다 일요일. 시각은 **호스트의 로컬 시간대** 기준입니다. 외부 의존성 없이 파서가 `bot.mjs` 안에 들어 있습니다.
137
+ - **`prompt`**(필수) Claude에게 보낼 메시지. **`label`**(선택) 답장 푸터와 `/cron` 목록에 표시되는 짧은 이름.
138
+ - **새 세션** — 예약 작업은 **독립된 세션**으로 돌아가서 내 대화 맥락을 오염시키지 않습니다(`state.json`은 내 것 그대로). 단일 작업 락을 공유하므로, 발사 시점에 다른 작업이 진행 중이면 그 회차는 **건너뜁니다**(로그 남김).
162
139
 
163
- 봇에게 그냥 메시지를 보내면 됨:
140
+ **채팅에서 자연어로 추가하기**
164
141
 
165
- - `cross 솔버 테스트 돌려보고 결과 알려줘`
166
- - `solve-2nd-floor-edges.ts 엣지 케이스 추가해줘`
142
+ ```
143
+ /cron add 매일 아침 9시에 열린 이슈 요약해줘
144
+ ```
167
145
 
168
- 명령어:
169
- - `/new` — 대화 맥락 초기화 (새 세션)
170
- - `/id` — 채팅 ID 확인
171
- - `/help` — 도움말
146
+ 봇이 이 문장을 Claude에게 보내 cron 표현식으로 바꾸고, **해석한 내용을 되돌려 보여줍니다**(잘못 읽었으면 바로 확인 가능). 그리고 `state.json`에 저장하므로 **재시작이 필요 없습니다**. 동적 작업에는 번호가 붙고, 다음으로 관리합니다.
172
147
 
173
- 세션은 자동으로 이어짐 (`--resume`). `state.json`에 마지막 세션 ID가 저장돼서
174
- 봇을 재시작해도 맥락이 유지됨. 맥락을 끊고 싶으면 `/new`.
148
+ - `/cron` 전체 목록 (config 작업은 `[config]`, 동적 작업은 `#번호`로 표시)
149
+ - `/cron add <자연어 요청>` 예: `/cron add 30분마다 CI 빨간 거 있으면 알려줘`
150
+ - `/cron rm <번호>` — 동적 작업 삭제 (config 작업은 파일에서 수정)
175
151
 
176
- ### 응답 형식 / 첨부
152
+ config에 적은 작업은 바꾸려면 재시작이 필요하고, 채팅으로 추가한 작업만 즉시 반영됩니다.
177
153
 
178
- - **간결 모드**: 텔레그램용으로 짧게 답하도록 `--append-system-prompt`가 기본 적용됨.
179
- 지침을 바꾸려면 `config.json`의 `appendSystemPrompt`에 문자열을 넣으면 됨(빈 문자열이면 비활성).
180
- - **서식**: 응답의 마크다운(굵게/코드/제목/표)을 텔레그램 HTML로 변환해 전송함.
181
- 변환이 실패하는 예외 케이스는 자동으로 평문으로 재전송됨.
182
- - **파일 첨부**: 사진/문서/음성/영상을 보내면 `attachments/`에 내려받아 그 경로를
183
- Claude에게 전달함(캡션은 메시지로 같이 전달). 이미지도 Read로 열어볼 수 있음.
154
+ ## 여러 프로젝트 / 페르소나
184
155
 
185
- ---
156
+ 코드는 프로젝트에 종속되지 않습니다. config 파일만 하나씩 더 만들면 여러 봇을 동시에 굴릴 수 있습니다.
186
157
 
187
- ## 5. 실행 방법
158
+ ```sh
159
+ claude-telegram-bot ~/projects/A/claudebot.config.json # 프로젝트 A
160
+ claude-telegram-bot ~/projects/B/claudebot.config.json # 프로젝트 B
161
+ ```
188
162
 
189
- | 방법 | 터미널 닫으면 | 재부팅 | 크래시 | 용도 |
190
- |---|---|---|---|---|
191
- | `node bot.mjs` | 종료됨 | ✗ | ✗ | 테스트·chatId 확인 |
192
- | `nohup node bot.mjs > bot.log 2>&1 &` | 유지 | ✗ | ✗ | 임시 백그라운드 |
193
- | **launchd (LaunchAgent)** | 유지 | ✅ 자동 시작 | ✅ 자동 재시작 | **상시 가동 (권장)** |
163
+ - 텔레그램은 토큰 하나당 폴링 하나만 허용합니다. 그래서 봇마다 BotFather 토큰을 따로 발급해야 합니다.
164
+ - `state`와 `attachments`는 config 옆에 저장되므로 봇끼리 섞이지 않습니다.
194
165
 
195
- > `node bot.mjs &` 백그라운드로 돌긴 하지만 터미널을 닫으면 SIGHUP으로 같이 죽음.
196
- > 터미널을 닫아도 유지하려면 최소 `nohup`, 재부팅·크래시까지 견디려면 launchd.
166
+ **같은 프로젝트**를 역할별 봇으로 나눌 수도 있습니다. 예를 들어 개발자 봇과 기획자 봇으로요. 코드는 그대로 두고 config만 역할별로 둡니다.
197
167
 
198
- ---
168
+ | 봇 | permissionMode | 역할 |
169
+ |---|---|---|
170
+ | 개발자 | `bypassPermissions` | 구현·수정·테스트·git |
171
+ | 기획자 | `plan` | 기능 제안·스펙·UX 방향 |
199
172
 
200
- ## 6. 항상 켜두기 (launchd 설정)
173
+ - `persona`에 역할 프롬프트를 넣으면 그 봇의 정체성이 됩니다. (텔레그램용 간결 지침은 자동으로 같이 붙습니다.)
174
+ - 같은 폴더를 공유한다면 쉘을 쓰는 봇(`bypassPermissions`)은 하나로 제한하는 편이 안전합니다. 동시 편집 충돌을 피할 수 있습니다.
175
+ - `state` 파일 이름은 config 이름에서 만들어집니다 (`dev.config.json` → `dev.config.state.json`). 같은 폴더에 config가 여러 개여도 맥락이 안 섞입니다.
201
176
 
202
- 재부팅·크래시에도 자동으로 살아나는 상시 가동 방식. 로그인 세션에서 도는
203
- **LaunchAgent**라서 claude의 키체인/OAuth 인증을 그대로 사용함.
177
+ ## 상시 실행 (launchd)
204
178
 
205
- ### 6-1. plist 확인 (경로/노드 버전 점검)
179
+ 맥을 재부팅하거나 봇이 죽어도 자동으로 다시 뜨게 하려면 launchd를 씁니다. 로그인 세션에서 도는 LaunchAgent라서 `claude`의 키체인/OAuth 인증을 그대로 사용합니다.
206
180
 
207
- `com.cube.claudebot.plist`는 아래 경로들을 가정함. 다르면 먼저 수정:
181
+ 저장소의 `com.claudebot.example.plist`를 복사해 쓰면 됩니다. 먼저 경로부터 확인하세요.
208
182
 
209
183
  ```sh
210
- which node # ProgramArguments 첫 줄의 node 경로와 일치하는지
211
- which claude # PATH 에 디렉토리가 포함됐는지 (EnvironmentVariables)
184
+ which node # ProgramArguments 첫 줄의 node 경로와 같은지
185
+ which claude # 이 경로가 PATH(EnvironmentVariables)에 포함됐는지
212
186
  ```
213
187
 
214
- plist에서 확인할 항목:
215
- - `ProgramArguments` 1번째 — node 절대경로
216
- - `ProgramArguments` 2번째 — `bot.mjs` 절대경로
217
- - `WorkingDirectory` — 프로젝트 폴더
218
- - `EnvironmentVariables > PATH` — nvm node/claude 경로 포함
188
+ plist에서 맞춰야 항목:
189
+
190
+ - `ProgramArguments` — node 절대경로, `bot.mjs` 절대경로, config 절대경로
191
+ - `WorkingDirectory` — 작업 폴더
192
+ - `EnvironmentVariables > PATH` — node·claude 경로 포함
219
193
  - `StandardOutPath` / `StandardErrorPath` — 로그 파일 경로
194
+ - `Label` — 봇마다 고유하게 (예: `com.claudebot.myproj`)
220
195
 
221
- ### 6-2. 등록 & 시작
196
+ 등록과 관리:
222
197
 
223
198
  ```sh
224
- cd /Users/jtchoi/Projects/cube-brain-trainer/tools/claude-telegram-bot
225
- cp com.cube.claudebot.plist ~/Library/LaunchAgents/
226
- launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
227
- ```
228
-
229
- > 최신 macOS 권장 방식은 `bootstrap`/`bootout`. 구버전(`load`/`unload`)도 동작하지만
230
- > deprecated 경고가 뜰 수 있음. `bootstrap`이 안 되면 `launchctl load ~/Library/LaunchAgents/com.cube.claudebot.plist`로 대체.
199
+ cp com.claudebot.example.plist ~/Library/LaunchAgents/
200
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
231
201
 
232
- ### 6-3. 상태 확인 & 관리
233
-
234
- ```sh
235
- launchctl list | grep claudebot # 등록·동작 확인 (PID가 보이면 실행 중)
236
- tail -f bot.log # 실행 로그
237
- tail -f bot.error.log # 에러 로그
202
+ launchctl list | grep claudebot # 상태 확인 (PID가 보이면 실행 중)
203
+ tail -f bot.log # 로그
238
204
 
239
205
  # 중지
240
- launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
206
+ launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
241
207
 
242
- # 코드 수정 후 재시작 (bootout → bootstrap)
243
- launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
244
- launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
208
+ # 코드 수정 후 재시작
209
+ launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
210
+ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
245
211
  ```
246
212
 
247
- ### 6-4. 자주 겪는 문제
248
-
249
- - **`launchctl list`에 PID 없이 에러 코드만 보임** → `bot.error.log` 확인. 보통 node/claude
250
- 경로 문제(`command not found`)거나 `config.json` 누락.
251
- - **봇이 응답 없음** → claude 인증 만료일 수 있음. 터미널에서 `node bot.mjs` 직접 실행해
252
- `claude` 로그인 상태부터 확인.
253
- - **맥이 잠자기 모드면 폴링도 멈춤** → 시스템 설정 > 배터리/전원에서 절전 해제 권장.
254
- - **"폴링 오류" 반복 (ETIMEDOUT)** → 일부 네트워크에서 IPv6 경로가 막혀 Node의 fetch가
255
- api.telegram.org(IPv6 보유)에서 타임아웃나는 문제. `bot.mjs`가 IPv4 우선
256
- (`dns.setDefaultResultOrder('ipv4first')` + 자동선택 끄기)으로 이미 회피하도록 돼 있음.
257
- 그래도 안 되면 `curl https://api.telegram.org` 로 네트워크/방화벽부터 확인.
213
+ > 최신 macOS는 `bootstrap`/`bootout`을 권장합니다. 구버전 `load`/`unload`도 동작하지만 deprecated 경고가 뜰 수 있습니다.
258
214
 
259
- ---
215
+ ## 자주 겪는 문제
260
216
 
261
- ## ⚠️ 보안
217
+ - **`launchctl list`에 PID 없이 에러 코드만 보임** — `bot.error.log`를 확인하세요. 보통 node/claude 경로 문제이거나 config 누락입니다.
218
+ - **봇이 응답하지 않음** — `claude` 인증이 만료됐을 수 있습니다. 터미널에서 `node bot.mjs`를 직접 실행해 로그인 상태부터 확인하세요.
219
+ - **맥이 잠자기에 들어가면 폴링도 멈춤** — 시스템 설정 > 배터리/전원에서 절전을 풀어두세요.
220
+ - **폴링 오류 반복 (ETIMEDOUT)** — 일부 네트워크는 IPv6 경로가 막혀 있어 `fetch`가 타임아웃 납니다. `bot.mjs`는 IPv4를 우선하도록 이미 처리해 뒀습니다. 그래도 안 되면 `curl https://api.telegram.org`로 네트워크부터 확인하세요.
262
221
 
263
- **이 도구는 채팅 앱 안에 들어있는 SSH 키라고 생각하라.** 명령을 실행하는 게 목적이고,
264
- 그 힘이 곧 위험이다. 노출 전에 반드시 읽을 것.
222
+ ## 보안
265
223
 
266
- ### 위협 모델 누가 머신에서 명령을 실행할 있나
224
+ 봇은 **채팅으로 받은 메시지를 머신에서 명령으로 실행합니다.** 편한 만큼 위험하니 아래는 꼭 지키세요.
267
225
 
268
- 1. **허가된 채팅.** `allowedChatId`로 허용한 텔레그램 계정에 접근 가능한 사람은 명령을 실행할 수 있다.
269
- 폰 잠금과 텔레그램 계정 2단계 인증(2FA)을 켜둘 것.
270
- 2. **봇 토큰을 가진 사람.** 토큰은 봇의 비밀번호다. 토큰만 있으면 들어오는 메시지를 읽고 봇을 사칭할 수
271
- 있다. `allowedChatId` 화이트리스트가 명령 *실행*은 여전히 막아주지만(텔레그램이 부여하는 `chatId`는
272
- 위조 불가), **토큰 유출은 사고로 취급**하라. `@BotFather` → `/revoke`로 폐기하고 새로 발급할 것.
273
- 3. **프롬프트 인젝션.** 웹페이지·파일·이슈 내용을 봇에 넘기며 처리시키면, 그 안에 숨은 악성 지시가
274
- Claude를 조종할 수 있다. 신뢰할 수 없는 콘텐츠를 `bypassPermissions` 봇에 그대로 흘려넣지 말 것.
226
+ **누가 명령을 실행할 수 있나**
275
227
 
276
- ### 타협 불가 원칙
228
+ - **허가된 채팅** — `allowedChatId`로 허용한 텔레그램 계정에 접근할 수 있는 사람. 폰 잠금과 텔레그램 2FA를 켜두세요.
229
+ - **봇 토큰을 가진 사람** — 토큰은 봇의 비밀번호입니다. 토큰만으로 메시지를 읽고 봇을 사칭할 수 있습니다. `allowedChatId`가 명령 실행은 막아주지만(텔레그램이 주는 chatId는 위조 불가), 토큰이 새면 사고로 보고 `@BotFather`의 `/revoke`로 폐기하세요.
230
+ - **프롬프트 인젝션** — 외부 웹페이지나 파일을 봇에 넘겨 처리시키면, 그 안에 숨은 지시가 Claude를 조종할 수 있습니다. 신뢰할 수 없는 내용을 `bypassPermissions` 봇에 그대로 넣지 마세요.
277
231
 
278
- - **`allowedChatId`는 반드시 설정.** 설정 전에는 봇이 아무 것도 실행하지 않고 채팅 ID만 알려준다.
279
- 설정 후에는 그 채팅만 명령 가능 — 이게 유일한 인증 계층이므로 반드시 채워야 한다.
280
- - **토큰을 자격증명처럼 보호.** `config.json`·`state.json`은 `.gitignore`에 있어 커밋되지 않는다 —
281
- 그대로 둘 것. 토큰을 이슈·로그·스크린샷에 절대 붙여넣지 말 것. 시작 로그는 토큰을 가린다
282
- (`token: <redacted>`) — 되살리지 말 것.
283
- - **샌드박스는 없다.** 봇은 `claude`를 *네* 계정 권한으로 실행한다. 네 파일시스템, SSH/git 자격증명,
284
- Claude OAuth/키체인 세션에 그대로 접근한다. 네가 할 수 있는 건 봇도 할 수 있다.
232
+ **꼭 지킬 것**
285
233
 
286
- ### 감당 가능한 최소 권한을 선택하라
234
+ - `allowedChatId`를 반드시 설정하세요. 설정 전에는 봇이 아무것도 실행하지 않고 chatId만 알려줍니다. 이게 유일한 인증 수단입니다.
235
+ - 토큰을 자격증명처럼 다루세요. 이슈·로그·스크린샷에 붙여넣지 마세요. 시작 로그는 토큰을 `<redacted>`로 가립니다.
236
+ - 샌드박스는 없습니다. 봇은 `claude`를 내 계정 권한으로 실행합니다. 내 파일, SSH/git 자격증명, Claude 인증 세션에 그대로 접근합니다.
287
237
 
288
- `permissionMode`가 핵심 안전 다이얼이다:
238
+ **권한 모드 선택**
289
239
 
290
240
  | 모드 | 허용 범위 | 권장 상황 |
291
241
  |---|---|---|
292
- | `plan` | 읽기·계획만, 편집 없음 | Q&A, 코드 리뷰, "기획자" 페르소나 |
293
- | `acceptEdits` | 파일 편집 자동 승인, 그 외(등)는 제한 | **권장 기본값** — 유용하면서 범위 제한 |
294
- | `bypassPermissions` | **임의 포함** 전부 자동 실행 | 채팅 한 = 임의 코드 실행을 감수할 때 |
242
+ | `plan` | 읽기·계획만 | Q&A, 코드 리뷰, 기획자 페르소나 |
243
+ | `acceptEdits` | 파일 편집 자동,등은 제한 | 기본값으로 무난 |
244
+ | `bypassPermissions` | 쉘 포함 전부 자동 | 채팅 한 줄이 임의 코드 실행임을 감수할 때 |
295
245
 
296
- 실전 강화 팁:
246
+ - 자율 쉘·git이 꼭 필요한 게 아니면 `acceptEdits`를 쓰세요.
247
+ - `projectDir`는 홈 디렉터리가 아니라 특정 프로젝트를 가리키게 해 피해 범위를 줄이세요.
248
+ - 페르소나 봇을 여럿 둘 땐 하나만 `bypassPermissions`로 두고 나머지는 `plan`으로.
249
+ - 상시 가동한다면 전용 계정이나 VM도 고려해 보세요.
297
250
 
298
- - 자율 쉘/git이 필요한 아니면 `bypassPermissions`보다 `acceptEdits`를 쓸 것.
299
- - `projectDir`는 홈 디렉터리가 아니라 **특정 프로젝트**를 가리키게 해 피해 범위를 줄일 것.
300
- - 멀티 페르소나에서는 **하나의** 봇만 `bypassPermissions`로 두고 나머지는 `plan`으로.
301
- - 상시 가동할 거면 전용 사용자 계정이나 VM에서 돌리는 것도 고려.
251
+ 보안 이슈는 공개로 올리기보다 GitHub 이슈(민감한 내용은 메인테이너에게 비공개)로 알려주세요.
302
252
 
303
- ### 취약점 신고
253
+ ## 라이선스
304
254
 
305
- 보안 이슈를 발견하면 익스플로잇 세부를 공개로 올리기보다 GitHub 이슈(민감한 건 메인테이너에게 비공개
306
- 연락)로 알려주세요.
255
+ MIT © Jongtaek Choi
package/README.md CHANGED
@@ -103,43 +103,69 @@ sensitive reports) rather than posting exploit details publicly.
103
103
 
104
104
  ---
105
105
 
106
- ## Quick start
106
+ ## Install & run
107
107
 
108
- **1) Create a bot token** In Telegram, open `@BotFather` → `/newbot` pick a name and a
109
- `username` ending in `_bot` copy the token (looks like `123456789:AA...`).
108
+ This is a standalone CLI/daemon, **not a library** you don't `import` it. Install it globally (or
109
+ run via `npx`), point a config file at any project, and run it. `projectDir` in the config decides
110
+ which folder Claude works in, independent of where the bot is installed.
111
+
112
+ Prerequisites: **Node 18+** and the **`claude` CLI installed and authenticated** on the host.
110
113
 
111
- **2) Create a config file**
114
+ **Option A npx (no install)**
112
115
 
113
116
  ```sh
114
- # from the repo folder
115
- cp config.example.json config.json
116
- # paste your BotFather token into config.json (leave allowedChatId empty for now)
117
+ npx claude-telegram-bot init # writes ./config.json
118
+ # edit config.json (token, projectDir, …)
119
+ npx claude-telegram-bot # runs ./config.json
117
120
  ```
118
121
 
119
- Or scaffold one anywhere with the CLI:
122
+ **Option B global install (recommended for an always-on daemon)**
120
123
 
121
124
  ```sh
122
- node bot.mjs init ~/my-project # writes ~/my-project/config.json
125
+ npm i -g claude-telegram-bot
126
+
127
+ claude-telegram-bot init ~/botconfigs/myproj # writes ~/botconfigs/myproj/config.json
128
+ # edit that config.json (token, projectDir, …)
129
+ claude-telegram-bot ~/botconfigs/myproj/config.json
123
130
  ```
124
131
 
125
- **3) Find your chatId and lock the bot to it**
132
+ Run several projects/personas by making one config file each and passing its path
133
+ `state.json` and `attachments/` live next to that config, so they don't mix.
126
134
 
127
- ```sh
128
- node bot.mjs
129
- # send the bot any message in Telegram
130
- # it replies with this chat's chatId
131
- # put that number into config.json `allowedChatId`, then restart the bot (Ctrl+C, run again)
132
- # now only you can use it
133
- ```
135
+ > **Keep your config out of git.** The config file holds your bot token. If you drop one inside a git
136
+ > repo, add it (plus `state*.json` and `attachments/`) to *that* project's `.gitignore`. This repo
137
+ > already ignores `config.json`, `config.*.json`, `*.config.json`, `state*.json`, and `attachments/`,
138
+ > so any name like `claudebot.config.json` is covered here — but your own project won't ignore them
139
+ > until you say so.
134
140
 
135
- **4) Use it** — just send messages:
141
+ ### First-run steps
142
+
143
+ **1) Create a bot token** — In Telegram, open `@BotFather` → `/newbot` → pick a name and a
144
+ `username` ending in `_bot` → copy the token (looks like `123456789:AA...`). Put it in `config.json`,
145
+ leave `allowedChatId` empty for now.
146
+
147
+ **2) Find your chatId and lock the bot to it** — Start the bot (`claude-telegram-bot …`), send it any
148
+ message in Telegram; it replies with this chat's `chatId`. Put that number into `config.json`
149
+ `allowedChatId` and restart. Now only you can use it. (See [Security](#security) — this is your only
150
+ auth layer.)
151
+
152
+ **3) Use it** — just send messages:
136
153
 
137
154
  - `run the solver tests and commit + push if they pass`
138
155
  - `add an edge case to solve-2nd-floor-edges.ts`
139
156
 
140
- Commands: `/new` (reset context / new session) · `/id` (show chat ID) · `/help`.
157
+ Commands: `/new` (reset context / new session) · `/cron` (list / add / remove scheduled tasks) · `/restart` (syntax-check & restart the bot) · `/id` (show chat ID) · `/help`.
158
+
159
+ > **`/restart`** runs `node --check` on `bot.mjs` first and **aborts the restart if it has a syntax
160
+ > error** (so a bad edit can't crash-loop the bot), then exits — relying on a process supervisor
161
+ > to relaunch it. Works out of the box with the [launchd setup](#always-on-with-launchd-macos)
162
+ > (`KeepAlive`); under a bare `node bot.mjs` with no supervisor it just stops. Your session resumes
163
+ > after the restart (the id lives in `state.json`).
164
+
165
+ **4) Keep it always on (optional)** — see [Always-on with launchd](#always-on-with-launchd-macos).
141
166
 
142
- **5) Keep it always on (optional)** see [Always-on with launchd](#always-on-with-launchd-macos).
167
+ > **From source** (for hacking on the bot): clone the repo, `cp config.example.json config.json`,
168
+ > then `node bot.mjs [config.json]`. Same behavior as the CLI.
143
169
 
144
170
  ---
145
171
 
@@ -159,10 +185,12 @@ Edit `config.json`:
159
185
  | `claudeBin` | Output of `which claude` (absolute path recommended) |
160
186
  | `permissionMode` | `plan` / `acceptEdits` / `bypassPermissions` — see [Security](#security) |
161
187
  | `model` | Empty = default. Or `opus` / `sonnet`, etc. |
188
+ | `lang` | (optional) UI language. Empty = auto-detect per user (English default, Korean for Korean Telegram clients). Force with `"en"` / `"ko"`. |
162
189
  | `name` | (optional) Bot name shown in `/help` — handy for telling multiple bots apart |
163
190
  | `persona` | (optional) Role system prompt — defines a persona (developer/planner/…). See below |
164
191
  | `appendSystemPrompt` | (optional) Override the default "be concise for Telegram" instruction |
165
192
  | `env` | (optional) Extra environment variables passed to the `claude` process |
193
+ | `schedule` | (optional) Cron jobs that run a prompt on a timer — see [Scheduled tasks](#scheduled-tasks-cron) |
166
194
 
167
195
  State (`state.json`) and downloaded `attachments/` are written **next to the config file**, so
168
196
  projects stay isolated.
@@ -171,6 +199,10 @@ projects stay isolated.
171
199
 
172
200
  - **Concise mode**: a `--append-system-prompt` is applied by default so replies stay short for
173
201
  Telegram. Override it via `appendSystemPrompt` (empty string disables it).
202
+ - **Language**: the bot's own messages (`/help`, command menu, status text) are English by default
203
+ and switch to Korean for users whose Telegram client is Korean. Force one language with `lang`
204
+ (`"en"`/`"ko"`). Claude's actual replies follow the language you write in, regardless. The `/`
205
+ command menu is registered per-language via `setMyCommands`.
174
206
  - **Formatting**: the reply's Markdown (bold/code/headings/tables) is converted to Telegram-safe
175
207
  HTML. If conversion ever produces invalid HTML, the message is automatically resent as plain text.
176
208
  - **Attachments**: send a photo/document/voice/video and it's downloaded into `attachments/`; the
@@ -178,6 +210,44 @@ projects stay isolated.
178
210
  - **Sessions**: conversations resume automatically (`--resume`); the last session id is saved in
179
211
  `state.json`, so context survives restarts. Use `/new` to start fresh.
180
212
 
213
+ ### Scheduled tasks (cron)
214
+
215
+ Add a `schedule` array to the config to run prompts on a timer — daily briefings, periodic
216
+ checks, reminders. Each entry runs the prompt and sends the result to `allowedChatId`.
217
+
218
+ ```json
219
+ "schedule": [
220
+ { "cron": "0 9 * * 1-5", "label": "Morning brief", "prompt": "Summarize today's open issues and TODOs" },
221
+ { "cron": "*/30 * * * *", "prompt": "Check CI status; only reply if something is red" }
222
+ ]
223
+ ```
224
+
225
+ - **`cron`** — standard 5-field expression `minute hour day-of-month month day-of-week`
226
+ (e.g. `0 9 * * 1-5` = 09:00 on weekdays). Supports `*`, lists (`1,3,5`), ranges (`1-5`),
227
+ and steps (`*/15`). Day-of-week `0` and `7` both mean Sunday. Times use the **host's local
228
+ timezone**. No external dependency — the parser lives in `bot.mjs`.
229
+ - **`prompt`** (required) — the message sent to Claude. **`label`** (optional) — a short name
230
+ shown in the reply footer and in `/cron`.
231
+ - **Fresh session**: scheduled jobs run in their **own session** so they never pollute your
232
+ interactive conversation context (`state.json` stays yours). They share the single-task lock,
233
+ so a job is **skipped** (logged) if a task is already running when it fires.
234
+
235
+ **Add jobs from the chat — in plain language:**
236
+
237
+ ```
238
+ /cron add summarize open issues every weekday at 9am
239
+ ```
240
+
241
+ The bot asks Claude to turn that into a cron expression, **echoes back what it understood**
242
+ (so you can catch a misread), and saves it to `state.json` — **no restart needed**. Dynamic
243
+ jobs get an id; manage them with:
244
+
245
+ - `/cron` — list everything (config jobs are tagged `[config]`; dynamic ones show `#id`)
246
+ - `/cron add <plain-language request>` — e.g. `/cron add every 30 min, ping me if CI is red`
247
+ - `/cron rm <id>` — remove a dynamic job (config jobs are edited in the file)
248
+
249
+ Config-defined jobs still require a restart to change; only chat-added jobs are live.
250
+
181
251
  ---
182
252
 
183
253
  ## Running multiple projects
package/bot.mjs CHANGED
Binary file
@@ -1,12 +1,16 @@
1
1
  {
2
- "name": " 이름 (도움말/구분용, 선택)",
3
- "token": "BOTFATHER_에서_받은_봇_토큰",
2
+ "name": "Bot name (shown in /help, for telling bots apart; optional)",
3
+ "token": "BOT_TOKEN_FROM_BOTFATHER",
4
4
  "allowedChatId": "",
5
5
  "projectDir": "/ABSOLUTE/PATH/TO/PROJECT",
6
- "claudeBin": "/Users/jtchoi/.nvm/versions/node/v23.1.0/bin/claude",
6
+ "claudeBin": "/ABSOLUTE/PATH/TO/claude",
7
7
  "permissionMode": "bypassPermissions",
8
8
  "model": "",
9
- "persona": "이 봇의 역할/말투를 정의하는 시스템 프롬프트 (선택). 멀티 봇(개발자/기획자 등)에서 페르소나 구분에 사용.",
9
+ "lang": "",
10
+ "persona": "System prompt defining this bot's role/voice (optional). Used to differentiate persona bots (developer/planner/...).",
10
11
  "appendSystemPrompt": "",
11
- "env": {}
12
+ "env": {},
13
+ "schedule": [
14
+ { "cron": "0 9 * * 1-5", "label": "Morning brief", "prompt": "Summarize today's open issues and TODOs (optional; standard 5-field cron: min hour day month weekday)" }
15
+ ]
12
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-telegram-bot",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Drive Claude Code from Telegram — messages run headless `claude -p` in a project dir and replies come back to the chat. Zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {