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 +20 -0
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/bot.mjs +252 -0
- package/package.json +24 -0
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
|
+
}
|