claude-telegram-bot 0.1.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.ko.md +306 -0
- package/README.md +317 -0
- package/bot.mjs +0 -0
- package/com.claudebot.example.plist +42 -0
- package/config.example.json +12 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jongtaek Choi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Claude Telegram Bot (범용)
|
|
2
|
+
|
|
3
|
+
**한국어** · [English](./README.md)
|
|
4
|
+
|
|
5
|
+
**의존성 0, 단일 파일, 데몬으로 도는 Claude Code 봇 — Bun도 Python도 떠 있는 세션도 필요 없음.**
|
|
6
|
+
|
|
7
|
+
텔레그램 메시지를 받아 지정한 프로젝트 폴더에서 `claude -p`(헤드리스 모드)를 실행하고
|
|
8
|
+
결과를 다시 텔레그램으로 보내주는 작은 브릿지. `bot.mjs` 파일 하나, Node 18+ 내장 기능만 사용 —
|
|
9
|
+
`npm install`할 것도 없고, 감사(audit)할 거라곤 읽기 쉬운 ~400줄이 전부.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
[너] → Telegram → bot.mjs → claude -p (config.projectDir) → 결과 → Telegram
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
검증 완료: `bypassPermissions` 헤드리스 모드 정상 동작 확인. 쉘·git·테스트까지 자동 실행됨.
|
|
16
|
+
**백그라운드 데몬**(launchd)으로 돌아서 인터랙티브 세션을 열어둘 필요가 없음.
|
|
17
|
+
|
|
18
|
+
> ### ⚠️ 이 도구는 설계상 원격 코드 실행 도구입니다. 실행 전에 [보안](#-보안) 섹션을 꼭 읽으세요.
|
|
19
|
+
> 텔레그램으로 보낸 메시지는 봇이 도는 머신에서 **명령으로 실행**됩니다.
|
|
20
|
+
> `permissionMode: bypassPermissions`면 한 줄짜리 메시지가 네 계정 권한으로 **무엇이든** 실행할 수 있습니다.
|
|
21
|
+
|
|
22
|
+
## 다른 도구와 비교
|
|
23
|
+
|
|
24
|
+
이 분야는 이미 붐비고, Anthropic도 공식 솔루션을 내놨다. 솔직한 지도를 그려두니 용도에 맞게 고르면 된다:
|
|
25
|
+
|
|
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줄, 단일 파일** | 큼 | 큼 |
|
|
35
|
+
|
|
36
|
+
**작업별 승인이 필요하고** 세션을 열어두는 게 괜찮다면 → 공식 Channels. **기능이 많이 필요하면** →
|
|
37
|
+
claude-code-telegram. **의존성 0·감사 가능·데몬**이 필요하거나, 특히 같은 프로젝트에 **권한이 다른
|
|
38
|
+
역할별 페르소나 봇**을 띄우고 싶다면 → 이 봇.
|
|
39
|
+
|
|
40
|
+
## 여러 프로젝트 동시 운영
|
|
41
|
+
|
|
42
|
+
이 코드는 프로젝트 비종속이라, **프로젝트마다 config 파일 하나씩**만 만들면 여러 개를 동시에 돌릴 수 있다.
|
|
43
|
+
|
|
44
|
+
- 실행: `node bot.mjs /절대경로/프로젝트.config.json` (인자 없으면 같은 폴더의 `config.json`)
|
|
45
|
+
- 상태(`state.json`)·첨부(`attachments/`)는 **그 config 파일이 있는 폴더**에 저장돼 프로젝트끼리 안 섞임
|
|
46
|
+
- **주의**: 텔레그램은 토큰당 폴링 1개만 허용 → 프로젝트마다 **BotFather 토큰을 따로** 만들어야 동시 운영 가능
|
|
47
|
+
- 상시 가동은 `com.claudebot.example.plist`를 프로젝트별로 복사해 등록 (아래 launchd 섹션 참고)
|
|
48
|
+
|
|
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
|
+
```
|
|
56
|
+
|
|
57
|
+
## 여러 페르소나(역할) 봇
|
|
58
|
+
|
|
59
|
+
**같은 프로젝트**를 역할별 봇으로 나눠 띄울 수 있다 (예: **개발자** + **기획자**).
|
|
60
|
+
코드는 하나, **config 파일만 역할별로** 따로 둔다.
|
|
61
|
+
|
|
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`는 동일해도 됨).
|
|
70
|
+
|
|
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
|
+
```
|
|
78
|
+
|
|
79
|
+
| 봇 | permissionMode | 역할 |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| 개발자 | `bypassPermissions` | 코드 구현·수정·테스트·git |
|
|
82
|
+
| 기획자 | `plan` (읽기·계획만) | 기능 제안·스펙·UX/디자인 방향 |
|
|
83
|
+
|
|
84
|
+
> 상시 가동은 `com.claudebot.example.plist`를 **봇마다** 복사해 `Label`·config 인자·로그
|
|
85
|
+
> 경로를 다르게 등록한다 (아래 launchd 섹션 참고).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## ⚡ 빠른 시작 (내가 할 일)
|
|
90
|
+
|
|
91
|
+
**1) 봇 토큰 발급** — 텔레그램에서 `@BotFather` → `/newbot` → 이름/username 지정 → 토큰 복사
|
|
92
|
+
|
|
93
|
+
**2) 설정 파일 생성**
|
|
94
|
+
```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 로 설정돼 있음
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**3) chatId 알아내기 + 실행**
|
|
102
|
+
```sh
|
|
103
|
+
node bot.mjs
|
|
104
|
+
# → 텔레그램에서 봇에게 아무 메시지나 전송 → 봇이 이 채팅의 chatId 를 답장
|
|
105
|
+
# → 그 숫자를 config.json 의 allowedChatId 에 넣고 봇 재시작 (Ctrl+C 후 다시 node bot.mjs)
|
|
106
|
+
# 이제 너만 사용 가능
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**4) 사용** — 텔레그램으로 메시지 전송:
|
|
110
|
+
- `cross 솔버 테스트 돌리고 통과하면 커밋 후 push 해줘`
|
|
111
|
+
- `solve-2nd-floor-edges.ts 에 엣지 케이스 추가해줘`
|
|
112
|
+
|
|
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
|
+
```
|
|
119
|
+
|
|
120
|
+
> ⚠️ 봇은 현재 작업 브랜치(`develop`) 기준으로 동작함. push 시점 등 브랜치 관리는 메시지로 명확히 지시할 것.
|
|
121
|
+
|
|
122
|
+
자세한 설명은 아래 섹션 참고.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 1. 봇 만들기 (BotFather)
|
|
127
|
+
|
|
128
|
+
1. 텔레그램에서 **@BotFather** 검색 → 대화 시작
|
|
129
|
+
2. `/newbot` 입력 → 봇 이름과 username 지정 (username은 `_bot`으로 끝나야 함)
|
|
130
|
+
3. 받은 **토큰**을 복사 (예: `123456789:AAxxxxxxxx`)
|
|
131
|
+
|
|
132
|
+
## 2. 설정
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
cd tools/claude-telegram-bot
|
|
136
|
+
cp config.example.json config.json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`config.json` 편집:
|
|
140
|
+
|
|
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` | (선택) 역할 시스템 프롬프트 — 페르소나(개발자/기획자 등) 정의. 자세히는 아래 페르소나 섹션 |
|
|
151
|
+
|
|
152
|
+
## 3. 내 chatId 알아내기
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
node bot.mjs
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
실행 후 텔레그램에서 봇에게 아무 메시지나 보내면, 봇이 **이 채팅의 chatId**를 답장해줌.
|
|
159
|
+
그 숫자를 `config.json`의 `allowedChatId`에 넣고 봇을 재시작. → 이제 너만 쓸 수 있음.
|
|
160
|
+
|
|
161
|
+
## 4. 사용
|
|
162
|
+
|
|
163
|
+
봇에게 그냥 메시지를 보내면 됨:
|
|
164
|
+
|
|
165
|
+
- `cross 솔버 테스트 돌려보고 결과 알려줘`
|
|
166
|
+
- `solve-2nd-floor-edges.ts 에 엣지 케이스 추가해줘`
|
|
167
|
+
|
|
168
|
+
명령어:
|
|
169
|
+
- `/new` — 대화 맥락 초기화 (새 세션)
|
|
170
|
+
- `/id` — 채팅 ID 확인
|
|
171
|
+
- `/help` — 도움말
|
|
172
|
+
|
|
173
|
+
세션은 자동으로 이어짐 (`--resume`). `state.json`에 마지막 세션 ID가 저장돼서
|
|
174
|
+
봇을 재시작해도 맥락이 유지됨. 맥락을 끊고 싶으면 `/new`.
|
|
175
|
+
|
|
176
|
+
### 응답 형식 / 첨부
|
|
177
|
+
|
|
178
|
+
- **간결 모드**: 텔레그램용으로 짧게 답하도록 `--append-system-prompt`가 기본 적용됨.
|
|
179
|
+
지침을 바꾸려면 `config.json`의 `appendSystemPrompt`에 문자열을 넣으면 됨(빈 문자열이면 비활성).
|
|
180
|
+
- **서식**: 응답의 마크다운(굵게/코드/제목/표)을 텔레그램 HTML로 변환해 전송함.
|
|
181
|
+
변환이 실패하는 예외 케이스는 자동으로 평문으로 재전송됨.
|
|
182
|
+
- **파일 첨부**: 사진/문서/음성/영상을 보내면 `attachments/`에 내려받아 그 경로를
|
|
183
|
+
Claude에게 전달함(캡션은 메시지로 같이 전달). 이미지도 Read로 열어볼 수 있음.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 5. 실행 방법
|
|
188
|
+
|
|
189
|
+
| 방법 | 터미널 닫으면 | 재부팅 후 | 크래시 시 | 용도 |
|
|
190
|
+
|---|---|---|---|---|
|
|
191
|
+
| `node bot.mjs` | 종료됨 | ✗ | ✗ | 테스트·chatId 확인 |
|
|
192
|
+
| `nohup node bot.mjs > bot.log 2>&1 &` | 유지 | ✗ | ✗ | 임시 백그라운드 |
|
|
193
|
+
| **launchd (LaunchAgent)** | 유지 | ✅ 자동 시작 | ✅ 자동 재시작 | **상시 가동 (권장)** |
|
|
194
|
+
|
|
195
|
+
> `node bot.mjs &` 도 백그라운드로 돌긴 하지만 터미널을 닫으면 SIGHUP으로 같이 죽음.
|
|
196
|
+
> 터미널을 닫아도 유지하려면 최소 `nohup`, 재부팅·크래시까지 견디려면 launchd.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 6. 항상 켜두기 (launchd 설정)
|
|
201
|
+
|
|
202
|
+
맥 재부팅·크래시에도 자동으로 살아나는 상시 가동 방식. 로그인 세션에서 도는
|
|
203
|
+
**LaunchAgent**라서 claude의 키체인/OAuth 인증을 그대로 사용함.
|
|
204
|
+
|
|
205
|
+
### 6-1. plist 확인 (경로/노드 버전 점검)
|
|
206
|
+
|
|
207
|
+
`com.cube.claudebot.plist`는 아래 경로들을 가정함. 다르면 먼저 수정:
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
which node # ProgramArguments 첫 줄의 node 경로와 일치하는지
|
|
211
|
+
which claude # PATH 에 이 디렉토리가 포함됐는지 (EnvironmentVariables)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
plist에서 확인할 항목:
|
|
215
|
+
- `ProgramArguments` 1번째 — node 절대경로
|
|
216
|
+
- `ProgramArguments` 2번째 — `bot.mjs` 절대경로
|
|
217
|
+
- `WorkingDirectory` — 프로젝트 폴더
|
|
218
|
+
- `EnvironmentVariables > PATH` — nvm node/claude 경로 포함
|
|
219
|
+
- `StandardOutPath` / `StandardErrorPath` — 로그 파일 경로
|
|
220
|
+
|
|
221
|
+
### 6-2. 등록 & 시작
|
|
222
|
+
|
|
223
|
+
```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`로 대체.
|
|
231
|
+
|
|
232
|
+
### 6-3. 상태 확인 & 관리
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
launchctl list | grep claudebot # 등록·동작 확인 (PID가 보이면 실행 중)
|
|
236
|
+
tail -f bot.log # 실행 로그
|
|
237
|
+
tail -f bot.error.log # 에러 로그
|
|
238
|
+
|
|
239
|
+
# 중지
|
|
240
|
+
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.cube.claudebot.plist
|
|
241
|
+
|
|
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
|
|
245
|
+
```
|
|
246
|
+
|
|
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` 로 네트워크/방화벽부터 확인.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## ⚠️ 보안
|
|
262
|
+
|
|
263
|
+
**이 도구는 채팅 앱 안에 들어있는 SSH 키라고 생각하라.** 명령을 실행하는 게 목적이고,
|
|
264
|
+
그 힘이 곧 위험이다. 노출 전에 반드시 읽을 것.
|
|
265
|
+
|
|
266
|
+
### 위협 모델 — 누가 네 머신에서 명령을 실행할 수 있나
|
|
267
|
+
|
|
268
|
+
1. **허가된 채팅.** `allowedChatId`로 허용한 텔레그램 계정에 접근 가능한 사람은 명령을 실행할 수 있다.
|
|
269
|
+
폰 잠금과 텔레그램 계정 2단계 인증(2FA)을 켜둘 것.
|
|
270
|
+
2. **봇 토큰을 가진 사람.** 토큰은 봇의 비밀번호다. 토큰만 있으면 들어오는 메시지를 읽고 봇을 사칭할 수
|
|
271
|
+
있다. `allowedChatId` 화이트리스트가 명령 *실행*은 여전히 막아주지만(텔레그램이 부여하는 `chatId`는
|
|
272
|
+
위조 불가), **토큰 유출은 사고로 취급**하라. `@BotFather` → `/revoke`로 폐기하고 새로 발급할 것.
|
|
273
|
+
3. **프롬프트 인젝션.** 웹페이지·파일·이슈 내용을 봇에 넘기며 처리시키면, 그 안에 숨은 악성 지시가
|
|
274
|
+
Claude를 조종할 수 있다. 신뢰할 수 없는 콘텐츠를 `bypassPermissions` 봇에 그대로 흘려넣지 말 것.
|
|
275
|
+
|
|
276
|
+
### 타협 불가 원칙
|
|
277
|
+
|
|
278
|
+
- **`allowedChatId`는 반드시 설정.** 설정 전에는 봇이 아무 것도 실행하지 않고 채팅 ID만 알려준다.
|
|
279
|
+
설정 후에는 그 채팅만 명령 가능 — 이게 유일한 인증 계층이므로 반드시 채워야 한다.
|
|
280
|
+
- **토큰을 자격증명처럼 보호.** `config.json`·`state.json`은 `.gitignore`에 있어 커밋되지 않는다 —
|
|
281
|
+
그대로 둘 것. 토큰을 이슈·로그·스크린샷에 절대 붙여넣지 말 것. 시작 로그는 토큰을 가린다
|
|
282
|
+
(`token: <redacted>`) — 되살리지 말 것.
|
|
283
|
+
- **샌드박스는 없다.** 봇은 `claude`를 *네* 계정 권한으로 실행한다. 네 파일시스템, SSH/git 자격증명,
|
|
284
|
+
Claude OAuth/키체인 세션에 그대로 접근한다. 네가 할 수 있는 건 봇도 할 수 있다.
|
|
285
|
+
|
|
286
|
+
### 감당 가능한 최소 권한을 선택하라
|
|
287
|
+
|
|
288
|
+
`permissionMode`가 핵심 안전 다이얼이다:
|
|
289
|
+
|
|
290
|
+
| 모드 | 허용 범위 | 권장 상황 |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `plan` | 읽기·계획만, 편집 없음 | Q&A, 코드 리뷰, "기획자" 페르소나 |
|
|
293
|
+
| `acceptEdits` | 파일 편집 자동 승인, 그 외(쉘 등)는 제한 | **권장 기본값** — 유용하면서 범위 제한 |
|
|
294
|
+
| `bypassPermissions` | **임의 쉘 포함** 전부 자동 실행 | 채팅 한 줄 = 임의 코드 실행을 감수할 때 |
|
|
295
|
+
|
|
296
|
+
실전 강화 팁:
|
|
297
|
+
|
|
298
|
+
- 자율 쉘/git이 꼭 필요한 게 아니면 `bypassPermissions`보다 `acceptEdits`를 쓸 것.
|
|
299
|
+
- `projectDir`는 홈 디렉터리가 아니라 **특정 프로젝트**를 가리키게 해 피해 범위를 줄일 것.
|
|
300
|
+
- 멀티 페르소나에서는 **하나의** 봇만 `bypassPermissions`로 두고 나머지는 `plan`으로.
|
|
301
|
+
- 상시 가동할 거면 전용 사용자 계정이나 VM에서 돌리는 것도 고려.
|
|
302
|
+
|
|
303
|
+
### 취약점 신고
|
|
304
|
+
|
|
305
|
+
보안 이슈를 발견하면 익스플로잇 세부를 공개로 올리기보다 GitHub 이슈(민감한 건 메인테이너에게 비공개
|
|
306
|
+
연락)로 알려주세요.
|
package/README.md
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
# Claude Telegram Bot
|
|
2
|
+
|
|
3
|
+
[한국어](./README.ko.md) · **English**
|
|
4
|
+
|
|
5
|
+
**A zero-dependency, single-file, daemonized Claude Code bot — no Bun, no Python, no open session.**
|
|
6
|
+
|
|
7
|
+
A tiny bridge that takes your Telegram messages, runs `claude -p` (Claude Code headless mode)
|
|
8
|
+
in a project folder, and sends the result back to the chat. One `.mjs` file on Node 18+ built-ins —
|
|
9
|
+
nothing to `npm install`, nothing to audit but ~400 readable lines.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
[you] → Telegram → bot.mjs → claude -p (config.projectDir) → result → Telegram
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Drive Claude Code from your phone: run tests, edit files, commit, push — all from a chat.
|
|
16
|
+
It runs as a **background daemon** (launchd), so there's no interactive session to keep open.
|
|
17
|
+
|
|
18
|
+
> ### ⚠️ This is a remote code-execution tool by design. Read the [Security](#security) section before running it.
|
|
19
|
+
> A message you send from Telegram is executed as a command on the machine running the bot.
|
|
20
|
+
> With `permissionMode: bypassPermissions`, a one-line message can run **anything** as your user.
|
|
21
|
+
|
|
22
|
+
**Highlights**
|
|
23
|
+
|
|
24
|
+
- **Zero dependencies** — just Node 18+. No npm install, no supply chain.
|
|
25
|
+
- **Multi-project** — one codebase drives many projects via per-project config files.
|
|
26
|
+
- **Multi-persona** — split the *same* project into role-based bots (e.g. Developer + Planner)
|
|
27
|
+
with per-bot system prompts and **differentiated permission levels**.
|
|
28
|
+
- **Session continuity** — conversations resume across restarts (`--resume`); `/new` to reset.
|
|
29
|
+
- **Attachments** — send photos/docs/voice/video; they're saved locally and handed to Claude.
|
|
30
|
+
- **Always-on** — ships with a launchd template for macOS (auto-start, auto-restart).
|
|
31
|
+
|
|
32
|
+
## How it compares
|
|
33
|
+
|
|
34
|
+
This space is crowded, and Anthropic now ships an official solution. Here's an honest map so you
|
|
35
|
+
can pick the right tool:
|
|
36
|
+
|
|
37
|
+
| | This bot | [Official Claude Code Channels](https://code.claude.com/docs/en/channels) | [claude-code-telegram](https://github.com/RichardAtCT/claude-code-telegram) |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| Runtime | **Node built-ins only** | Bun + MCP plugin | Python 3.11+ + libs |
|
|
40
|
+
| Execution model | headless `claude -p` per message | events pushed into an **open** `claude --channels` session | Claude SDK / CLI |
|
|
41
|
+
| Stays running as | **background daemon** (no session open) | a live interactive session you keep running | service / daemon |
|
|
42
|
+
| Multi-persona, permission-scoped bots on one repo | **yes** (dev=`bypass`, planner=`plan`) | no | no |
|
|
43
|
+
| Per-action permission approval (inline buttons) | no (set `permissionMode`) | **yes** | partial |
|
|
44
|
+
| Feature breadth (webhooks, cron, voice, export) | minimal | medium | **large** |
|
|
45
|
+
| Lines of code to read/fork | **~400, one file** | larger | large |
|
|
46
|
+
|
|
47
|
+
**Use the official Channels** if you want per-action approvals and don't mind keeping a session
|
|
48
|
+
open. **Use claude-code-telegram** if you want maximum features. **Use this** if you want a
|
|
49
|
+
minimal, auditable, zero-dependency daemon — and especially if you want **role-split persona bots**
|
|
50
|
+
with different permission levels on the same project.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Security
|
|
55
|
+
|
|
56
|
+
**Treat this tool like an SSH key into your machine that lives in a chat app.** It is designed
|
|
57
|
+
to execute commands; that power is the point, and also the risk. Read this before exposing it.
|
|
58
|
+
|
|
59
|
+
### Threat model — who can run commands on your machine
|
|
60
|
+
|
|
61
|
+
1. **The allowed chat.** Anyone with access to the Telegram account whose `chatId` you allow can
|
|
62
|
+
run commands. Lock your phone and Telegram account (2FA).
|
|
63
|
+
2. **Whoever holds the bot token.** The token is the bot's password. With it, an attacker can read
|
|
64
|
+
incoming messages and impersonate the bot. The `allowedChatId` whitelist still blocks command
|
|
65
|
+
*execution* (Telegram-supplied `chatId`s can't be forged), but **treat a leaked token as an
|
|
66
|
+
incident**: revoke it via `@BotFather` → `/revoke` and issue a new one.
|
|
67
|
+
3. **Prompt-injected content.** If you forward a webpage, file, or repo issue and ask the bot to act
|
|
68
|
+
on it, malicious instructions inside that content can steer Claude. Don't pipe untrusted content
|
|
69
|
+
into a `bypassPermissions` bot.
|
|
70
|
+
|
|
71
|
+
### Non-negotiables
|
|
72
|
+
|
|
73
|
+
- **Always set `allowedChatId`.** Until you do, the bot refuses to run anything and just replies
|
|
74
|
+
with the chat's ID. Once set, only that chat can issue commands — this is your only auth layer,
|
|
75
|
+
so it must be set.
|
|
76
|
+
- **Guard the token like a credential.** `config.json` and `state.json` are in `.gitignore` so you
|
|
77
|
+
don't commit it — keep it that way. Never paste the token into issues, logs, or screenshots. The
|
|
78
|
+
startup log redacts it (`token: <redacted>`); don't add it back.
|
|
79
|
+
- **There is no sandbox.** The bot runs `claude` as *your* user, with your filesystem, your SSH/git
|
|
80
|
+
credentials, and your Claude OAuth/keychain session. It can do anything you can.
|
|
81
|
+
|
|
82
|
+
### Choose the least permission you can live with
|
|
83
|
+
|
|
84
|
+
`permissionMode` is the main safety dial:
|
|
85
|
+
|
|
86
|
+
| Mode | What it allows | Use when |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `plan` | Read & plan only, no edits | Q&A, code review, a "planner" persona |
|
|
89
|
+
| `acceptEdits` | Auto-approve file edits; other actions (shell, etc.) still gated | **Recommended default** — useful but bounded |
|
|
90
|
+
| `bypassPermissions` | Everything auto-runs, **including arbitrary shell** | You accept that one chat message = arbitrary code execution |
|
|
91
|
+
|
|
92
|
+
Practical hardening:
|
|
93
|
+
|
|
94
|
+
- Prefer `acceptEdits` over `bypassPermissions` unless you specifically need autonomous shell/git.
|
|
95
|
+
- Point `projectDir` at a **specific project**, not your home directory — limit the blast radius.
|
|
96
|
+
- For multi-persona setups, give only **one** bot `bypassPermissions`; keep the rest on `plan`.
|
|
97
|
+
- Consider running on a dedicated user account or VM if you'll leave it always-on.
|
|
98
|
+
|
|
99
|
+
### Reporting a vulnerability
|
|
100
|
+
|
|
101
|
+
Found a security issue? Please open a GitHub issue (or contact the maintainer privately for
|
|
102
|
+
sensitive reports) rather than posting exploit details publicly.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Quick start
|
|
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...`).
|
|
110
|
+
|
|
111
|
+
**2) Create a config file**
|
|
112
|
+
|
|
113
|
+
```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
|
+
```
|
|
118
|
+
|
|
119
|
+
Or scaffold one anywhere with the CLI:
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
node bot.mjs init ~/my-project # writes ~/my-project/config.json
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**3) Find your chatId and lock the bot to it**
|
|
126
|
+
|
|
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
|
+
```
|
|
134
|
+
|
|
135
|
+
**4) Use it** — just send messages:
|
|
136
|
+
|
|
137
|
+
- `run the solver tests and commit + push if they pass`
|
|
138
|
+
- `add an edge case to solve-2nd-floor-edges.ts`
|
|
139
|
+
|
|
140
|
+
Commands: `/new` (reset context / new session) · `/id` (show chat ID) · `/help`.
|
|
141
|
+
|
|
142
|
+
**5) Keep it always on (optional)** — see [Always-on with launchd](#always-on-with-launchd-macos).
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Configuration
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
cp config.example.json config.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Edit `config.json`:
|
|
153
|
+
|
|
154
|
+
| Key | Description |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `token` | Bot token from BotFather |
|
|
157
|
+
| `allowedChatId` | **Leave empty at first** → the bot tells you (step 3). Required before it runs anything. |
|
|
158
|
+
| `projectDir` | Absolute path to the working folder Claude runs in |
|
|
159
|
+
| `claudeBin` | Output of `which claude` (absolute path recommended) |
|
|
160
|
+
| `permissionMode` | `plan` / `acceptEdits` / `bypassPermissions` — see [Security](#security) |
|
|
161
|
+
| `model` | Empty = default. Or `opus` / `sonnet`, etc. |
|
|
162
|
+
| `name` | (optional) Bot name shown in `/help` — handy for telling multiple bots apart |
|
|
163
|
+
| `persona` | (optional) Role system prompt — defines a persona (developer/planner/…). See below |
|
|
164
|
+
| `appendSystemPrompt` | (optional) Override the default "be concise for Telegram" instruction |
|
|
165
|
+
| `env` | (optional) Extra environment variables passed to the `claude` process |
|
|
166
|
+
|
|
167
|
+
State (`state.json`) and downloaded `attachments/` are written **next to the config file**, so
|
|
168
|
+
projects stay isolated.
|
|
169
|
+
|
|
170
|
+
### Usage details
|
|
171
|
+
|
|
172
|
+
- **Concise mode**: a `--append-system-prompt` is applied by default so replies stay short for
|
|
173
|
+
Telegram. Override it via `appendSystemPrompt` (empty string disables it).
|
|
174
|
+
- **Formatting**: the reply's Markdown (bold/code/headings/tables) is converted to Telegram-safe
|
|
175
|
+
HTML. If conversion ever produces invalid HTML, the message is automatically resent as plain text.
|
|
176
|
+
- **Attachments**: send a photo/document/voice/video and it's downloaded into `attachments/`; the
|
|
177
|
+
absolute path is handed to Claude (caption included as the message). Images can be opened with Read.
|
|
178
|
+
- **Sessions**: conversations resume automatically (`--resume`); the last session id is saved in
|
|
179
|
+
`state.json`, so context survives restarts. Use `/new` to start fresh.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Running multiple projects
|
|
184
|
+
|
|
185
|
+
The code is project-agnostic: make **one config file per project** and run several at once.
|
|
186
|
+
|
|
187
|
+
- Run: `node bot.mjs /absolute/path/to/project.config.json` (no arg → `./config.json`)
|
|
188
|
+
- `state.json` and `attachments/` live in the **config file's folder**, so projects don't mix.
|
|
189
|
+
- **Note**: Telegram allows only one poller per token → each project needs its **own BotFather
|
|
190
|
+
token**.
|
|
191
|
+
- For always-on, copy the launchd template per project (see below).
|
|
192
|
+
|
|
193
|
+
Example — two projects:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
~/projects/A/claudebot.config.json (token A, projectDir=~/projects/A)
|
|
197
|
+
~/projects/B/claudebot.config.json (token B, projectDir=~/projects/B)
|
|
198
|
+
node bot.mjs ~/projects/A/claudebot.config.json # instance A
|
|
199
|
+
node bot.mjs ~/projects/B/claudebot.config.json # instance B
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Multiple personas (roles)
|
|
203
|
+
|
|
204
|
+
You can split the **same project** into role-based bots (e.g. **Developer** + **Planner**).
|
|
205
|
+
One codebase, **a separate config file per role**.
|
|
206
|
+
|
|
207
|
+
- **`persona`**: a role system prompt in the config becomes that bot's identity. The concise-Telegram
|
|
208
|
+
instruction is injected automatically, so `persona` only needs the role itself.
|
|
209
|
+
- **Differentiated permissions via `permissionMode`**: since the bots share a folder, keep the
|
|
210
|
+
shell-using bot (`bypassPermissions`) to **just one** to avoid concurrent-edit conflicts. For
|
|
211
|
+
read/plan-only, use `plan`.
|
|
212
|
+
- **Session isolation**: the `state` filename is derived from the config name
|
|
213
|
+
(`config.json` → `state.json`, `dev.config.json` → `dev.config.state.json`), so multiple configs
|
|
214
|
+
in one folder don't share context.
|
|
215
|
+
- **One token per bot**: each bot needs its own BotFather token (`allowedChatId` can be the same).
|
|
216
|
+
|
|
217
|
+
Example — Developer + Planner:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
dev.config.json (permissionMode: bypassPermissions, persona: "Senior developer...")
|
|
221
|
+
planner.config.json (permissionMode: plan, persona: "Product/UX planner...")
|
|
222
|
+
node bot.mjs dev.config.json
|
|
223
|
+
node bot.mjs planner.config.json
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
| Bot | permissionMode | Role |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
| Developer | `bypassPermissions` | Implement, edit, test, git |
|
|
229
|
+
| Planner | `plan` (read/plan only) | Feature proposals, specs, UX direction |
|
|
230
|
+
|
|
231
|
+
> For always-on, copy `com.claudebot.example.plist` **per bot** and register each with a distinct
|
|
232
|
+
> `Label`, config argument, and log paths (see below).
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## How to run
|
|
237
|
+
|
|
238
|
+
| Method | When terminal closes | After reboot | On crash | Use for |
|
|
239
|
+
|---|---|---|---|---|
|
|
240
|
+
| `node bot.mjs` | stops | ✗ | ✗ | testing, finding chatId |
|
|
241
|
+
| `nohup node bot.mjs > bot.log 2>&1 &` | survives | ✗ | ✗ | quick background run |
|
|
242
|
+
| **launchd (LaunchAgent)** | survives | ✅ auto-start | ✅ auto-restart | **always-on (recommended)** |
|
|
243
|
+
|
|
244
|
+
> `node bot.mjs &` also backgrounds it, but closing the terminal kills it (SIGHUP). Use `nohup` to
|
|
245
|
+
> survive that, and launchd to survive reboots/crashes.
|
|
246
|
+
|
|
247
|
+
## Always-on with launchd (macOS)
|
|
248
|
+
|
|
249
|
+
Keeps the bot alive across reboots and crashes. It runs as a **LaunchAgent** in your login session,
|
|
250
|
+
so it reuses Claude's keychain/OAuth auth.
|
|
251
|
+
|
|
252
|
+
### 1. Check the plist (paths / node version)
|
|
253
|
+
|
|
254
|
+
`com.claudebot.example.plist` assumes certain paths — fix them first if yours differ:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
which node # must match the node path in ProgramArguments
|
|
258
|
+
which claude # its directory must be on PATH (EnvironmentVariables)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Items to verify in the plist:
|
|
262
|
+
|
|
263
|
+
- `ProgramArguments` [0] — absolute path to `node`
|
|
264
|
+
- `ProgramArguments` [1] — absolute path to `bot.mjs`
|
|
265
|
+
- `WorkingDirectory` — the project folder
|
|
266
|
+
- `EnvironmentVariables > PATH` — includes your node/claude directories
|
|
267
|
+
- `StandardOutPath` / `StandardErrorPath` — log file paths
|
|
268
|
+
|
|
269
|
+
### 2. Register & start
|
|
270
|
+
|
|
271
|
+
```sh
|
|
272
|
+
cp com.claudebot.example.plist ~/Library/LaunchAgents/
|
|
273
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
> Modern macOS prefers `bootstrap`/`bootout`. The old `load`/`unload` still works but may print a
|
|
277
|
+
> deprecation warning. If `bootstrap` fails, fall back to
|
|
278
|
+
> `launchctl load ~/Library/LaunchAgents/com.claudebot.example.plist`.
|
|
279
|
+
|
|
280
|
+
### 3. Manage
|
|
281
|
+
|
|
282
|
+
```sh
|
|
283
|
+
launchctl list | grep claudebot # registered/running? (a PID means it's up)
|
|
284
|
+
tail -f bot.log # run log
|
|
285
|
+
tail -f bot.error.log # error log
|
|
286
|
+
|
|
287
|
+
# stop
|
|
288
|
+
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
|
|
289
|
+
|
|
290
|
+
# restart after editing code (bootout → bootstrap)
|
|
291
|
+
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
|
|
292
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.claudebot.example.plist
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Troubleshooting
|
|
296
|
+
|
|
297
|
+
- **`launchctl list` shows an error code with no PID** → check `bot.error.log`. Usually a node/claude
|
|
298
|
+
path issue (`command not found`) or a missing `config.json`.
|
|
299
|
+
- **Bot doesn't respond** → Claude auth may have expired. Run `node bot.mjs` directly and confirm
|
|
300
|
+
`claude` is logged in.
|
|
301
|
+
- **Mac is asleep → polling stops** → disable sleep in System Settings > Battery/Power.
|
|
302
|
+
- **Repeated "polling error" (ETIMEDOUT)** → some networks block IPv6, so Node's fetch times out
|
|
303
|
+
against api.telegram.org (which has an IPv6 address). `bot.mjs` already works around this by
|
|
304
|
+
preferring IPv4 (`dns.setDefaultResultOrder('ipv4first')` + disabling auto-select). If it still
|
|
305
|
+
fails, check the network/firewall with `curl https://api.telegram.org`.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Requirements
|
|
310
|
+
|
|
311
|
+
- Node.js 18+ (for built-in `fetch`)
|
|
312
|
+
- The `claude` CLI installed and authenticated on the host
|
|
313
|
+
- A Telegram bot token from `@BotFather`
|
|
314
|
+
|
|
315
|
+
## License
|
|
316
|
+
|
|
317
|
+
MIT © Jongtaek Choi
|
package/bot.mjs
ADDED
|
Binary file
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<!--
|
|
4
|
+
프로젝트마다 이 템플릿을 복사해서 쓰세요. 바꿀 곳:
|
|
5
|
+
- Label: com.claudebot.<프로젝트명> (인스턴스마다 고유)
|
|
6
|
+
- 3번째 인자: 해당 프로젝트의 config.json 절대경로
|
|
7
|
+
- 로그 경로: 인스턴스마다 다른 파일
|
|
8
|
+
설치: launchctl bootstrap gui/$(id -u) <이_plist_경로>
|
|
9
|
+
제거: launchctl bootout gui/$(id -u)/<Label>
|
|
10
|
+
-->
|
|
11
|
+
<plist version="1.0">
|
|
12
|
+
<dict>
|
|
13
|
+
<key>Label</key>
|
|
14
|
+
<string>com.claudebot.PROJECT</string>
|
|
15
|
+
|
|
16
|
+
<key>ProgramArguments</key>
|
|
17
|
+
<array>
|
|
18
|
+
<string>/Users/jtchoi/.nvm/versions/node/v23.1.0/bin/node</string>
|
|
19
|
+
<string>/Users/jtchoi/Projects/claude-telegram-bot/bot.mjs</string>
|
|
20
|
+
<string>/ABSOLUTE/PATH/TO/PROJECT/claudebot.config.json</string>
|
|
21
|
+
</array>
|
|
22
|
+
|
|
23
|
+
<!-- nvm node/claude 경로를 PATH 에 포함 -->
|
|
24
|
+
<key>EnvironmentVariables</key>
|
|
25
|
+
<dict>
|
|
26
|
+
<key>PATH</key>
|
|
27
|
+
<string>/Users/jtchoi/.nvm/versions/node/v23.1.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
28
|
+
</dict>
|
|
29
|
+
|
|
30
|
+
<key>KeepAlive</key>
|
|
31
|
+
<true/>
|
|
32
|
+
<key>RunAtLoad</key>
|
|
33
|
+
<true/>
|
|
34
|
+
<key>ThrottleInterval</key>
|
|
35
|
+
<integer>10</integer>
|
|
36
|
+
|
|
37
|
+
<key>StandardOutPath</key>
|
|
38
|
+
<string>/ABSOLUTE/PATH/TO/PROJECT/claudebot.log</string>
|
|
39
|
+
<key>StandardErrorPath</key>
|
|
40
|
+
<string>/ABSOLUTE/PATH/TO/PROJECT/claudebot.error.log</string>
|
|
41
|
+
</dict>
|
|
42
|
+
</plist>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "봇 이름 (도움말/구분용, 선택)",
|
|
3
|
+
"token": "BOTFATHER_에서_받은_봇_토큰",
|
|
4
|
+
"allowedChatId": "",
|
|
5
|
+
"projectDir": "/ABSOLUTE/PATH/TO/PROJECT",
|
|
6
|
+
"claudeBin": "/Users/jtchoi/.nvm/versions/node/v23.1.0/bin/claude",
|
|
7
|
+
"permissionMode": "bypassPermissions",
|
|
8
|
+
"model": "",
|
|
9
|
+
"persona": "이 봇의 역할/말투를 정의하는 시스템 프롬프트 (선택). 멀티 봇(개발자/기획자 등)에서 페르소나 구분에 사용.",
|
|
10
|
+
"appendSystemPrompt": "",
|
|
11
|
+
"env": {}
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-telegram-bot",
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claude-telegram-bot": "bot.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bot.mjs",
|
|
11
|
+
"config.example.json",
|
|
12
|
+
"com.claudebot.example.plist"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"start": "node bot.mjs"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"anthropic",
|
|
24
|
+
"telegram",
|
|
25
|
+
"bot",
|
|
26
|
+
"cli",
|
|
27
|
+
"headless"
|
|
28
|
+
],
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/JongtaekChoi/claude-telegram-bot.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/JongtaekChoi/claude-telegram-bot/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/JongtaekChoi/claude-telegram-bot#readme",
|
|
37
|
+
"author": "Jongtaek Choi",
|
|
38
|
+
"license": "MIT"
|
|
39
|
+
}
|