armanbot-telegram 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/.env.example ADDED
@@ -0,0 +1,20 @@
1
+ # ArmanBot Configuration
2
+ # Copy to .env and fill in your values
3
+
4
+ # Required: Telegram bot token from @BotFather
5
+ ARMANBOT_TOKEN=
6
+
7
+ # Optional: Claude model (opus, sonnet, haiku)
8
+ # ARMANBOT_MODEL=opus
9
+
10
+ # Optional: Timeout in ms (default 180000 = 3 min)
11
+ # ARMANBOT_TIMEOUT=180000
12
+
13
+ # Optional: Restrict to specific Telegram chat IDs (comma-separated)
14
+ # ARMANBOT_USERS=123456789,987654321
15
+
16
+ # Optional: Custom workspace path
17
+ # ARMANBOT_WORKSPACE=~/.armanbot/workspace
18
+
19
+ # Optional: Path to claude binary
20
+ # CLAUDE_BIN=~/.local/bin/claude
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aray
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.md ADDED
@@ -0,0 +1,144 @@
1
+ # ⚡ ArmanBot — Personal AI Agent in Telegram
2
+
3
+ Your AI agent that actually **does things** — not just talks.
4
+
5
+ One file. No dependencies. Just Node.js + Claude Code CLI.
6
+
7
+ ## What it does
8
+
9
+ - 🛠 **Executes commands** — bash, git, ssh, files on your machine
10
+ - 🧠 **Remembers** — persistent memory between sessions
11
+ - 🎭 **Has personality** — customizable via SOUL.md
12
+ - 🔍 **Searches the web** — real-time information
13
+ - 📁 **Manages files** — read, write, edit, search
14
+ - 🔊 **Speaks** — voice messages via ElevenLabs (optional)
15
+ - 🏠 **Controls smart home** — MQTT (optional)
16
+ - 📧 **Handles email** — via himalaya CLI (optional)
17
+
18
+ ## Quick Start
19
+
20
+ ### One command install (macOS/Linux):
21
+
22
+ ```bash
23
+ curl -fsSL https://armanbot.kz/install.sh | bash
24
+ ```
25
+
26
+ ### Manual install:
27
+
28
+ 1. **Prerequisites:**
29
+ - Node.js 22+
30
+ - [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code) with active subscription
31
+ - Telegram bot token from [@BotFather](https://t.me/BotFather)
32
+
33
+ 2. **Clone and run:**
34
+ ```bash
35
+ git clone https://github.com/armanbot-jpg/armanbot-telegram.git
36
+ cd armanbot-telegram
37
+ ARMANBOT_TOKEN="your-telegram-token" node bot.mjs
38
+ ```
39
+
40
+ 3. **Message your bot in Telegram.** That's it.
41
+
42
+ ## Configuration
43
+
44
+ All config via environment variables:
45
+
46
+ | Variable | Required | Default | Description |
47
+ |---|---|---|---|
48
+ | `ARMANBOT_TOKEN` | ✅ | — | Telegram bot token from @BotFather |
49
+ | `ARMANBOT_MODEL` | — | `opus` | Claude model: `opus`, `sonnet`, `haiku` |
50
+ | `ARMANBOT_TIMEOUT` | — | `180000` | Request timeout in ms |
51
+ | `ARMANBOT_WORKSPACE` | — | `~/.armanbot/workspace` | Identity & memory storage |
52
+ | `ARMANBOT_USERS` | — | allow all | Comma-separated Telegram chat IDs |
53
+ | `CLAUDE_BIN` | — | auto-detect | Path to `claude` binary |
54
+
55
+ ## Identity System
56
+
57
+ Your agent's personality lives in the workspace:
58
+
59
+ ```
60
+ ~/.armanbot/workspace/
61
+ ├── SOUL.md # Who your agent is (personality, rules)
62
+ ├── IDENTITY.md # Quick reference card
63
+ ├── AGENTS.md # Operational rules
64
+ ├── USER.md # Info about you (auto-populated)
65
+ ├── context.md # Current state (auto-updated)
66
+ ├── decisions.md # Decision log (auto-updated)
67
+ ├── memory/ # Daily notes
68
+ ├── skills/ # Custom skills (SKILL.md files)
69
+ └── logs/ # Session logs
70
+ ```
71
+
72
+ Edit `SOUL.md` to customize your agent's personality. The bot creates defaults on first run.
73
+
74
+ ## Commands
75
+
76
+ | Command | Description |
77
+ |---|---|
78
+ | `/start` | Welcome message |
79
+ | `/new` | Start fresh session |
80
+ | `/status` | Current session info |
81
+
82
+ ## Run as service
83
+
84
+ ### macOS (launchd):
85
+ ```bash
86
+ cat > ~/Library/LaunchAgents/ai.armanbot.plist << EOF
87
+ <?xml version="1.0" encoding="UTF-8"?>
88
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
89
+ <plist version="1.0">
90
+ <dict>
91
+ <key>Label</key><string>ai.armanbot</string>
92
+ <key>ProgramArguments</key>
93
+ <array><string>$(which node)</string><string>$(pwd)/bot.mjs</string></array>
94
+ <key>RunAtLoad</key><true/>
95
+ <key>KeepAlive</key><true/>
96
+ <key>EnvironmentVariables</key>
97
+ <dict>
98
+ <key>HOME</key><string>$HOME</string>
99
+ <key>PATH</key><string>$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
100
+ <key>ARMANBOT_TOKEN</key><string>YOUR_TOKEN</string>
101
+ </dict>
102
+ </dict>
103
+ </plist>
104
+ EOF
105
+ launchctl load ~/Library/LaunchAgents/ai.armanbot.plist
106
+ ```
107
+
108
+ ### Linux (systemd):
109
+ ```bash
110
+ sudo tee /etc/systemd/system/armanbot.service << EOF
111
+ [Unit]
112
+ Description=ArmanBot AI Agent
113
+ After=network.target
114
+ [Service]
115
+ ExecStart=$(which node) $(pwd)/bot.mjs
116
+ Restart=always
117
+ User=$USER
118
+ Environment=HOME=$HOME
119
+ Environment=PATH=$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin
120
+ Environment=ARMANBOT_TOKEN=YOUR_TOKEN
121
+ [Install]
122
+ WantedBy=multi-user.target
123
+ EOF
124
+ sudo systemctl enable --now armanbot
125
+ ```
126
+
127
+ ## Architecture
128
+
129
+ ```
130
+ Telegram → bot.mjs → claude CLI (with tools) → response → Telegram
131
+
132
+ ~/.armanbot/workspace/
133
+ (SOUL.md, memory, logs)
134
+ ```
135
+
136
+ No API keys needed for Claude — uses your Claude Code CLI subscription (Max/Pro).
137
+
138
+ ## License
139
+
140
+ MIT
141
+
142
+ ## Made in Kazakhstan 🇰🇿
143
+
144
+ [armanbot.kz](https://armanbot.kz)
package/bot.mjs ADDED
@@ -0,0 +1,252 @@
1
+ #!/usr/bin/env node
2
+ // ArmanBot — Personal AI Agent in Telegram
3
+ // https://armanbot.kz
4
+ //
5
+ // Turns Claude Code CLI into a full AI agent accessible via Telegram.
6
+ // Features: tool-use (bash, git, ssh, web), memory, voice, photos, docs.
7
+ // One file. No dependencies. Just Node.js + Claude Code CLI.
8
+
9
+ import { spawn } from "node:child_process";
10
+ import { readFileSync, appendFileSync, mkdirSync, readdirSync, existsSync } from "node:fs";
11
+ import { homedir } from "node:os";
12
+
13
+ // ─── Config (from env or defaults) ───────────────────────────
14
+ const BOT_TOKEN = process.env.ARMANBOT_TOKEN;
15
+ const CLAUDE_BIN = process.env.CLAUDE_BIN || findClaude();
16
+ const WORKSPACE = process.env.ARMANBOT_WORKSPACE || `${homedir()}/.armanbot/workspace`;
17
+ const LOG_FILE = `${WORKSPACE}/logs/bot.log`;
18
+ const MODEL = process.env.ARMANBOT_MODEL || "opus";
19
+ const TIMEOUT_MS = parseInt(process.env.ARMANBOT_TIMEOUT || "180000");
20
+ const ALLOWED_USERS = process.env.ARMANBOT_USERS?.split(",") || []; // empty = allow all
21
+
22
+ if (!BOT_TOKEN) {
23
+ console.error("Error: ARMANBOT_TOKEN env var required. Get it from @BotFather in Telegram.");
24
+ process.exit(1);
25
+ }
26
+
27
+ function findClaude() {
28
+ const paths = [
29
+ `${homedir()}/.local/bin/claude`,
30
+ "/opt/homebrew/bin/claude",
31
+ "/usr/local/bin/claude",
32
+ ];
33
+ for (const p of paths) {
34
+ if (existsSync(p)) return p;
35
+ }
36
+ console.error("Error: Claude Code CLI not found. Install: npm install -g @anthropic-ai/claude-code");
37
+ process.exit(1);
38
+ }
39
+
40
+ const sessions = new Map();
41
+ mkdirSync(`${WORKSPACE}/logs`, { recursive: true });
42
+ mkdirSync(`${WORKSPACE}/memory`, { recursive: true });
43
+ mkdirSync(`${WORKSPACE}/skills`, { recursive: true });
44
+
45
+ function log(msg) {
46
+ const line = `${new Date().toISOString()} ${msg}`;
47
+ console.log(line);
48
+ try { appendFileSync(LOG_FILE, line + "\n"); } catch {}
49
+ }
50
+
51
+ // ─── Identity ────────────────────────────────────────────────
52
+ function ensureIdentity() {
53
+ const soul = `${WORKSPACE}/SOUL.md`;
54
+ if (!existsSync(soul)) {
55
+ appendFileSync(soul, `# SOUL.md\n\nYou are a personal AI agent — helpful, honest, pragmatic.\nMatch the user's language. Be concise.\nYou have full tool access. When asked to do something — do it.\n`);
56
+ log("Created default SOUL.md");
57
+ }
58
+ const ctx = `${WORKSPACE}/context.md`;
59
+ if (!existsSync(ctx)) {
60
+ appendFileSync(ctx, `# context.md\n> Created: ${new Date().toISOString().slice(0, 10)}\n\nFresh install. Getting to know the user.\n`);
61
+ }
62
+ const dec = `${WORKSPACE}/decisions.md`;
63
+ if (!existsSync(dec)) {
64
+ appendFileSync(dec, `# decisions.md\n\n- ${new Date().toISOString().slice(0, 10)}: ArmanBot installed.\n`);
65
+ }
66
+ }
67
+
68
+ function loadSystemPrompt() {
69
+ ensureIdentity();
70
+ const files = ["SOUL.md", "IDENTITY.md", "AGENTS.md", "PROTOCOL.md", "USER.md", "context.md", "decisions.md"];
71
+ let prompt = "";
72
+ for (const file of files) {
73
+ try { prompt += `\n\n=== ${file} ===\n${readFileSync(`${WORKSPACE}/${file}`, "utf8")}`; } catch {}
74
+ }
75
+ try {
76
+ const today = new Date().toISOString().slice(0, 10);
77
+ prompt += `\n\n=== Daily Note ===\n${readFileSync(`${WORKSPACE}/memory/${today}.md`, "utf8")}`;
78
+ } catch {}
79
+ try {
80
+ for (const dir of readdirSync(`${WORKSPACE}/skills`)) {
81
+ try { prompt += `\n\n=== SKILL: ${dir} ===\n${readFileSync(`${WORKSPACE}/skills/${dir}/SKILL.md`, "utf8")}`; } catch {}
82
+ }
83
+ } catch {}
84
+ prompt += `\n\n=== RUNTIME ===
85
+ You are an AI agent running through Telegram via Claude Code CLI.
86
+ Tools available: Bash, Read, Write, Edit, Glob, Grep, WebSearch, WebFetch.
87
+ Workspace: ${WORKSPACE}
88
+ Log decisions to decisions.md. Update context.md on state changes.
89
+ When asked to do something — use tools and DO IT. Don't say "I can't".`;
90
+ return prompt;
91
+ }
92
+
93
+ // ─── Telegram API ────────────────────────────────────────────
94
+ async function tg(method, body) {
95
+ const r = await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/${method}`, {
96
+ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
97
+ });
98
+ return r.json();
99
+ }
100
+
101
+ async function sendMessage(chatId, text) {
102
+ const chunks = text.match(/[\s\S]{1,4000}/g) || [text];
103
+ for (const chunk of chunks) {
104
+ await tg("sendMessage", { chat_id: chatId, text: chunk, parse_mode: "Markdown" }).catch(() =>
105
+ tg("sendMessage", { chat_id: chatId, text: chunk })
106
+ );
107
+ }
108
+ }
109
+
110
+ function startTyping(chatId) {
111
+ const interval = setInterval(() => tg("sendChatAction", { chat_id: chatId, action: "typing" }), 4000);
112
+ return () => clearInterval(interval);
113
+ }
114
+
115
+ // ─── Claude CLI ──────────────────────────────────────────────
116
+ function callClaude(chatId, userMessage) {
117
+ const systemPrompt = loadSystemPrompt();
118
+ const sessionId = sessions.get(chatId);
119
+
120
+ if (!sessionId) {
121
+ userMessage = `[First message in session. Read workspace files: ${WORKSPACE}/SOUL.md, ${WORKSPACE}/context.md]\n\n${userMessage}`;
122
+ }
123
+
124
+ return new Promise((resolve, reject) => {
125
+ let stdout = "", killed = false;
126
+ const args = [
127
+ "-p", userMessage,
128
+ "--system-prompt", systemPrompt,
129
+ "--model", MODEL,
130
+ "--output-format", "json",
131
+ "--dangerously-skip-permissions",
132
+ "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,WebSearch,WebFetch",
133
+ "--add-dir", WORKSPACE,
134
+ "--add-dir", homedir(),
135
+ ];
136
+ if (sessionId) args.push("--resume", sessionId);
137
+
138
+ const proc = spawn(CLAUDE_BIN, args, {
139
+ timeout: TIMEOUT_MS,
140
+ env: { ...process.env, TERM: "dumb" },
141
+ });
142
+ const timer = setTimeout(() => {
143
+ killed = true;
144
+ proc.kill("SIGTERM");
145
+ setTimeout(() => { try { proc.kill("SIGKILL"); } catch {} }, 5000);
146
+ }, TIMEOUT_MS);
147
+
148
+ proc.stdout.on("data", (c) => (stdout += c));
149
+ proc.on("close", (code) => {
150
+ clearTimeout(timer);
151
+ if (killed) return reject(new Error("Timeout"));
152
+ try {
153
+ const parsed = JSON.parse(stdout);
154
+ if (parsed.session_id) sessions.set(chatId, parsed.session_id);
155
+ resolve(parsed.result || "");
156
+ } catch {
157
+ resolve(stdout.trim() || `Error (exit ${code})`);
158
+ }
159
+ });
160
+ proc.on("error", reject);
161
+ });
162
+ }
163
+
164
+ // ─── Poll ────────────────────────────────────────────────────
165
+ let offset = 0;
166
+
167
+ async function poll() {
168
+ try {
169
+ const data = await tg("getUpdates", { offset, timeout: 30 });
170
+ if (!data.ok || !data.result?.length) return;
171
+
172
+ for (const update of data.result) {
173
+ offset = update.update_id + 1;
174
+ const msg = update.message;
175
+ if (!msg?.text) continue;
176
+
177
+ const chatId = String(msg.chat.id);
178
+
179
+ // Access control
180
+ if (ALLOWED_USERS.length > 0 && !ALLOWED_USERS.includes(chatId)) continue;
181
+
182
+ const text = msg.text.trim();
183
+
184
+ // Auto-detect user name
185
+ if (!sessions.has(chatId) && msg.from?.first_name) {
186
+ try {
187
+ const soul = readFileSync(`${WORKSPACE}/SOUL.md`, "utf8");
188
+ if (!soul.includes("User:")) {
189
+ appendFileSync(`${WORKSPACE}/SOUL.md`, `\nUser: ${msg.from.first_name}\n`);
190
+ }
191
+ } catch {}
192
+ }
193
+
194
+ // Commands
195
+ if (text === "/new" || text === "/reset") {
196
+ sessions.delete(chatId);
197
+ await sendMessage(chatId, "🔄 New session");
198
+ continue;
199
+ }
200
+ if (text === "/status") {
201
+ await sendMessage(chatId, `Session: ${sessions.get(chatId) || "none"}\nModel: ${MODEL}\nWorkspace: ${WORKSPACE}`);
202
+ continue;
203
+ }
204
+ if (text === "/start") {
205
+ await sendMessage(chatId, `⚡ *ArmanBot*\n\nYour personal AI agent. I can execute commands, manage files, search the web, and more.\n\nJust ask me anything.\n\n/new — new session\n/status — current status`);
206
+ continue;
207
+ }
208
+
209
+ log(`IN [${chatId}]: ${text.slice(0, 100)}`);
210
+ const stopTyping = startTyping(chatId);
211
+
212
+ try {
213
+ const reply = await callClaude(chatId, text);
214
+ stopTyping();
215
+ await sendMessage(chatId, reply);
216
+ log(`OUT [${chatId}]: ${reply.slice(0, 100)}...`);
217
+
218
+ // Session log
219
+ try {
220
+ const today = new Date().toISOString().slice(0, 10);
221
+ const time = new Date().toTimeString().slice(0, 5);
222
+ appendFileSync(
223
+ `${WORKSPACE}/logs/session-${today}.md`,
224
+ `\n### ${time}\n**User:** ${text.slice(0, 200)}\n**Agent:** ${reply.slice(0, 500)}\n`
225
+ );
226
+ } catch {}
227
+ } catch (err) {
228
+ stopTyping();
229
+ await sendMessage(chatId, `⚠️ ${err.message}`);
230
+ log(`ERR [${chatId}]: ${err.message}`);
231
+ }
232
+ }
233
+ } catch (err) {
234
+ log(`POLL: ${err.message}`);
235
+ }
236
+ }
237
+
238
+ // ─── Signals ─────────────────────────────────────────────────
239
+ process.on("SIGTERM", () => { log("Shutting down"); process.exit(0); });
240
+
241
+ // ─── Start ───────────────────────────────────────────────────
242
+ log("⚡ ArmanBot started");
243
+ log(`Model: ${MODEL} | Timeout: ${TIMEOUT_MS / 1000}s | Workspace: ${WORKSPACE}`);
244
+
245
+ async function run() {
246
+ while (true) {
247
+ await poll();
248
+ await new Promise((r) => setTimeout(r, 1000));
249
+ }
250
+ }
251
+
252
+ run();
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "armanbot-telegram",
3
+ "version": "1.0.0",
4
+ "description": "⚡ Personal AI Agent in Telegram — one file, full tool-use, persistent memory",
5
+ "main": "bot.mjs",
6
+ "bin": {
7
+ "armanbot": "bot.mjs"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "start": "node bot.mjs"
12
+ },
13
+ "keywords": ["ai", "agent", "telegram", "claude", "bot", "tool-use", "personal-assistant"],
14
+ "author": "Aray",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/armanbot-jpg/armanbot-telegram"
19
+ },
20
+ "homepage": "https://armanbot.kz",
21
+ "engines": {
22
+ "node": ">=22.0.0"
23
+ }
24
+ }