@ebowwa/glm-daemon-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/CLAUDE.md +106 -0
- package/README.md +58 -0
- package/TICKET.md +55 -0
- package/deploy.sh +26 -0
- package/package.json +49 -0
- package/run.sh +3 -0
- package/src/index.ts +128 -0
- package/tsconfig.json +18 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
|
|
2
|
+
Default to using Bun instead of Node.js.
|
|
3
|
+
|
|
4
|
+
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
5
|
+
- Use `bun test` instead of `jest` or `vitest`
|
|
6
|
+
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
7
|
+
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
8
|
+
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
9
|
+
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
10
|
+
- Bun automatically loads .env, so don't use dotenv.
|
|
11
|
+
|
|
12
|
+
## APIs
|
|
13
|
+
|
|
14
|
+
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
15
|
+
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
16
|
+
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
17
|
+
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
18
|
+
- `WebSocket` is built-in. Don't use `ws`.
|
|
19
|
+
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
20
|
+
- Bun.$`ls` instead of execa.
|
|
21
|
+
|
|
22
|
+
## Testing
|
|
23
|
+
|
|
24
|
+
Use `bun test` to run tests.
|
|
25
|
+
|
|
26
|
+
```ts#index.test.ts
|
|
27
|
+
import { test, expect } from "bun:test";
|
|
28
|
+
|
|
29
|
+
test("hello world", () => {
|
|
30
|
+
expect(1).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Frontend
|
|
35
|
+
|
|
36
|
+
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
37
|
+
|
|
38
|
+
Server:
|
|
39
|
+
|
|
40
|
+
```ts#index.ts
|
|
41
|
+
import index from "./index.html"
|
|
42
|
+
|
|
43
|
+
Bun.serve({
|
|
44
|
+
routes: {
|
|
45
|
+
"/": index,
|
|
46
|
+
"/api/users/:id": {
|
|
47
|
+
GET: (req) => {
|
|
48
|
+
return new Response(JSON.stringify({ id: req.params.id }));
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
// optional websocket support
|
|
53
|
+
websocket: {
|
|
54
|
+
open: (ws) => {
|
|
55
|
+
ws.send("Hello, world!");
|
|
56
|
+
},
|
|
57
|
+
message: (ws, message) => {
|
|
58
|
+
ws.send(message);
|
|
59
|
+
},
|
|
60
|
+
close: (ws) => {
|
|
61
|
+
// handle close
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
development: {
|
|
65
|
+
hmr: true,
|
|
66
|
+
console: true,
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
72
|
+
|
|
73
|
+
```html#index.html
|
|
74
|
+
<html>
|
|
75
|
+
<body>
|
|
76
|
+
<h1>Hello, world!</h1>
|
|
77
|
+
<script type="module" src="./frontend.tsx"></script>
|
|
78
|
+
</body>
|
|
79
|
+
</html>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
With the following `frontend.tsx`:
|
|
83
|
+
|
|
84
|
+
```tsx#frontend.tsx
|
|
85
|
+
import React from "react";
|
|
86
|
+
import { createRoot } from "react-dom/client";
|
|
87
|
+
|
|
88
|
+
// import .css files directly and it works
|
|
89
|
+
import './index.css';
|
|
90
|
+
|
|
91
|
+
const root = createRoot(document.body);
|
|
92
|
+
|
|
93
|
+
export default function Frontend() {
|
|
94
|
+
return <h1>Hello, world!</h1>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
root.render(<Frontend />);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Then, run index.ts
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
bun --hot ./index.ts
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# GLM Daemon Telegram Bot
|
|
2
|
+
|
|
3
|
+
Telegram adapter for GLM Daemon with ButlerAgent personality.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
1. **Install dependencies:**
|
|
8
|
+
```bash
|
|
9
|
+
bun install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
2. **Configure Doppler secrets:**
|
|
13
|
+
```
|
|
14
|
+
TELEGRAM_BOT_TOKEN=<your-bot-token>
|
|
15
|
+
TELEGRAM_TEST_CHAT_ID=<your-chat-id> # Optional, for test message
|
|
16
|
+
GLM_API_KEY=<your-api-key>
|
|
17
|
+
DOPPLER_TOKEN=<your-doppler-token>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
3. **Build:**
|
|
21
|
+
```bash
|
|
22
|
+
bun run build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
4. **Run:**
|
|
26
|
+
```bash
|
|
27
|
+
bun run start
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or with Doppler:
|
|
31
|
+
```bash
|
|
32
|
+
doppler run -- bun run start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Getting Telegram Credentials
|
|
36
|
+
|
|
37
|
+
1. **Create a bot:**
|
|
38
|
+
- Message [@BotFather](https://t.me/botfather) on Telegram
|
|
39
|
+
- Send `/newbot`
|
|
40
|
+
- Follow the prompts
|
|
41
|
+
- Copy the bot token (format: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`)
|
|
42
|
+
|
|
43
|
+
2. **Get your Chat ID:**
|
|
44
|
+
- Message [@userinfobot](https://t.me/userinfobot) on Telegram
|
|
45
|
+
- It will reply with your chat ID
|
|
46
|
+
- Add this to Doppler as `TELEGRAM_TEST_CHAT_ID`
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- ā
Natural conversation with ButlerAgent personality
|
|
51
|
+
- ā
Context-aware responses (remembers username, name)
|
|
52
|
+
- ā
Long message chunking (4096 char limit)
|
|
53
|
+
- ā
Error handling with graceful fallbacks
|
|
54
|
+
- ā
Test message on startup
|
|
55
|
+
|
|
56
|
+
## Deployment
|
|
57
|
+
|
|
58
|
+
See `TICKET.md` for deployment details.
|
package/TICKET.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Deployment Ticket: GLM Daemon Telegram Bot
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
Deploy the GLM Daemon Telegram bot to Hetzner VPS
|
|
5
|
+
|
|
6
|
+
## VPS Details
|
|
7
|
+
- **IP**: 46.225.83.174
|
|
8
|
+
- **Name**: workflow-daemon
|
|
9
|
+
- **Specs**: cax11 (2 vCPU, 4GB RAM)
|
|
10
|
+
- **Location**: nbg1
|
|
11
|
+
- **Status**: Running
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
1. Install dependencies (bun install)
|
|
15
|
+
2. Build TypeScript (bun run build)
|
|
16
|
+
3. Configure Doppler secrets (TELEGRAM_BOT_TOKEN, GLM_API_KEY)
|
|
17
|
+
4. Start the bot (bun run start)
|
|
18
|
+
5. Set up systemd service for persistent operation
|
|
19
|
+
|
|
20
|
+
## Known Issues
|
|
21
|
+
- **Terminal MCP has recursive loop** - Cannot use mcp__terminal__exec_ssh
|
|
22
|
+
- Use direct SSH or alternative deployment method
|
|
23
|
+
- Package structure is ready at `/packages/src/glm-daemon-telegram/`
|
|
24
|
+
|
|
25
|
+
## Files Created
|
|
26
|
+
- `package.json` - Dependencies (node-telegram-bot-api, doppler)
|
|
27
|
+
- `src/index.ts` - Telegram bot with ButlerAgent
|
|
28
|
+
- `deploy.sh` - Deployment script
|
|
29
|
+
- `run.sh` - Quick run script
|
|
30
|
+
- `tsconfig.json` - TypeScript config
|
|
31
|
+
- `README.md` - Setup documentation
|
|
32
|
+
|
|
33
|
+
## Doppler Secrets Required
|
|
34
|
+
- `TELEGRAM_BOT_TOKEN` - Telegram bot token from @BotFather
|
|
35
|
+
- `TELEGRAM_TEST_CHAT_ID` - Your Telegram chat ID (optional)
|
|
36
|
+
- `GLM_API_KEY` - GLM 4.7 API key (or ANTHROPIC_API_KEY)
|
|
37
|
+
- `DOPPLER_TOKEN` - Doppler access token
|
|
38
|
+
|
|
39
|
+
## Success Criteria
|
|
40
|
+
- Bot connects to Telegram
|
|
41
|
+
- Sends test message on startup
|
|
42
|
+
- Responds to messages with Butler personality
|
|
43
|
+
- Persistent operation via systemd
|
|
44
|
+
|
|
45
|
+
## First Steps
|
|
46
|
+
1. Build locally: `bun run build`
|
|
47
|
+
2. Test locally: `doppler run -- bun run start`
|
|
48
|
+
3. Should see test message in Telegram
|
|
49
|
+
4. Then deploy to VPS
|
|
50
|
+
|
|
51
|
+
## Created
|
|
52
|
+
2026-02-06 - Initial package structure
|
|
53
|
+
|
|
54
|
+
## Priority
|
|
55
|
+
HIGH - Ready for deployment once Terminal MCP is fixed
|
package/deploy.sh
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
echo "š Deploying GLM Daemon Telegram Bot to Hetzner..."
|
|
5
|
+
|
|
6
|
+
# Load secrets from Doppler
|
|
7
|
+
echo "š¦ Loading secrets from Doppler..."
|
|
8
|
+
eval "$(doppler run --print-envs --token=$DOPPLER_TOKEN | grep -E '^(TELEGRAM_BOT_TOKEN|TELEGRAM_TEST_CHAT_ID|GLM_API_KEY|DOPPLER_TOKEN)=')"
|
|
9
|
+
|
|
10
|
+
# Check required secrets
|
|
11
|
+
if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then
|
|
12
|
+
echo "ā TELEGRAM_BOT_TOKEN not set"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Install dependencies
|
|
17
|
+
echo "š„ Installing dependencies..."
|
|
18
|
+
bun install
|
|
19
|
+
|
|
20
|
+
# Build TypeScript
|
|
21
|
+
echo "šØ Building..."
|
|
22
|
+
bun run build
|
|
23
|
+
|
|
24
|
+
# Start bot
|
|
25
|
+
echo "š¤ Starting Telegram bot..."
|
|
26
|
+
bun run start
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ebowwa/glm-daemon-telegram",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Telegram adapter for GLM Daemon with ButlerAgent personality",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"start": "bun run dist/index.js",
|
|
17
|
+
"dev": "bun --watch src/index.ts"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"telegram",
|
|
21
|
+
"bot",
|
|
22
|
+
"glm",
|
|
23
|
+
"daemon",
|
|
24
|
+
"ai",
|
|
25
|
+
"chatbot",
|
|
26
|
+
"automation",
|
|
27
|
+
"butler"
|
|
28
|
+
],
|
|
29
|
+
"author": "Ebowwa Labs <labs@ebowwa.com>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"homepage": "https://github.com/ebowwa/codespaces/tree/main/packages/src/glm-daemon-telegram#readme",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/ebowwa/codespaces.git",
|
|
35
|
+
"directory": "packages/src/glm-daemon-telegram"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/ebowwa/codespaces/issues"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"node-telegram-bot-api": "^0.66.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/bun": "latest",
|
|
45
|
+
"@types/node": "^22.10.5",
|
|
46
|
+
"@types/node-telegram-bot-api": "^0.64.13",
|
|
47
|
+
"typescript": "^5.9.3"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/run.sh
ADDED
package/src/index.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon Telegram Bot
|
|
3
|
+
*
|
|
4
|
+
* Simple Telegram adapter - sends test message and echoes messages
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import TelegramBot from 'node-telegram-bot-api';
|
|
8
|
+
|
|
9
|
+
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
10
|
+
const TELEGRAM_TEST_CHAT_ID = process.env.TELEGRAM_TEST_CHAT_ID || '';
|
|
11
|
+
|
|
12
|
+
class TelegramGLMBot {
|
|
13
|
+
private bot: TelegramBot;
|
|
14
|
+
|
|
15
|
+
constructor(token: string) {
|
|
16
|
+
this.bot = new TelegramBot(token, { polling: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Start the bot and register handlers
|
|
21
|
+
*/
|
|
22
|
+
async start(): Promise<void> {
|
|
23
|
+
console.log('š¤ GLM Daemon Telegram Bot starting...');
|
|
24
|
+
|
|
25
|
+
// Handle /start command
|
|
26
|
+
this.bot.onText(/\/start/, async (msg: TelegramBot.Message) => {
|
|
27
|
+
const chatId = msg.chat.id;
|
|
28
|
+
await this.bot.sendMessage(
|
|
29
|
+
chatId,
|
|
30
|
+
'š Hello! I\'m the GLM Daemon Telegram Bot.\n\n' +
|
|
31
|
+
'I\'m currently in simple echo mode.\n' +
|
|
32
|
+
'Just send me a message and I\'ll echo it back!\n\n' +
|
|
33
|
+
'Full Butler AI personality coming soon!'
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Handle all text messages
|
|
38
|
+
this.bot.on('message', async (msg: TelegramBot.Message) => {
|
|
39
|
+
// Ignore non-text messages
|
|
40
|
+
if (!msg.text) return;
|
|
41
|
+
|
|
42
|
+
// Ignore commands
|
|
43
|
+
if (msg.text.startsWith('/')) return;
|
|
44
|
+
|
|
45
|
+
const chatId = msg.chat.id;
|
|
46
|
+
const userName = msg.from?.first_name || msg.from?.username || 'User';
|
|
47
|
+
|
|
48
|
+
console.log(`[Telegram] ${userName}: ${msg.text}`);
|
|
49
|
+
|
|
50
|
+
// Echo back with a friendly response
|
|
51
|
+
const response = `š¢ You said: "${msg.text}"\n\nā
Bot is working! Full AI responses coming soon.`;
|
|
52
|
+
|
|
53
|
+
await this.bot.sendMessage(chatId, response);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Handle polling errors
|
|
57
|
+
this.bot.on('polling_error', (error: Error) => {
|
|
58
|
+
console.error('[Telegram] Polling error:', error);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log('ā
Telegram bot is running!');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Send a test message to verify bot is working
|
|
66
|
+
*/
|
|
67
|
+
async sendTestMessage(chatId: number): Promise<void> {
|
|
68
|
+
await this.bot.sendMessage(
|
|
69
|
+
chatId,
|
|
70
|
+
'ā
GLM Daemon Telegram Bot is NOW ONLINE!\n\n' +
|
|
71
|
+
'š The bot is working correctly!\n\n' +
|
|
72
|
+
'Commands:\n' +
|
|
73
|
+
'/start - Show welcome message\n' +
|
|
74
|
+
'Any message - Echo test\n\n' +
|
|
75
|
+
'Full AI personality integration coming soon!'
|
|
76
|
+
);
|
|
77
|
+
console.log(`ā
Test message sent to chat ${chatId}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Stop the bot
|
|
82
|
+
*/
|
|
83
|
+
async stop(): Promise<void> {
|
|
84
|
+
console.log('š Stopping Telegram bot...');
|
|
85
|
+
this.bot.stopPolling();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Main entry point
|
|
91
|
+
*/
|
|
92
|
+
async function main() {
|
|
93
|
+
if (!TELEGRAM_BOT_TOKEN) {
|
|
94
|
+
console.error('ā TELEGRAM_BOT_TOKEN not set');
|
|
95
|
+
console.error('Get your token from @BotFather on Telegram');
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(`š± Bot token loaded: ${TELEGRAM_BOT_TOKEN.substring(0, 10)}...`);
|
|
100
|
+
|
|
101
|
+
// Create and start bot
|
|
102
|
+
const bot = new TelegramGLMBot(TELEGRAM_BOT_TOKEN);
|
|
103
|
+
await bot.start();
|
|
104
|
+
|
|
105
|
+
// Send test message if chat ID is provided
|
|
106
|
+
if (TELEGRAM_TEST_CHAT_ID) {
|
|
107
|
+
await bot.sendTestMessage(Number(TELEGRAM_TEST_CHAT_ID));
|
|
108
|
+
} else {
|
|
109
|
+
console.log('');
|
|
110
|
+
console.log('ā ļø No TELEGRAM_TEST_CHAT_ID set.');
|
|
111
|
+
console.log(' Send /start to your bot to test it!');
|
|
112
|
+
console.log(' Or get your chat ID from @userinfobot');
|
|
113
|
+
console.log('');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle graceful shutdown
|
|
117
|
+
process.on('SIGINT', async () => {
|
|
118
|
+
await bot.stop();
|
|
119
|
+
process.exit(0);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
process.on('SIGTERM', async () => {
|
|
123
|
+
await bot.stop();
|
|
124
|
+
process.exit(0);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main().catch(console.error);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"strict": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|