gemini-bridge 0.0.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/.env.example +27 -0
- package/EXAMPLES.md +286 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +98 -0
- package/README.md +331 -0
- package/agent-bridge.config.example.json +10 -0
- package/bin/cli.js +237 -0
- package/ecosystem.config.json +22 -0
- package/index.js +553 -0
- package/messaging/telegramClient.js +56 -0
- package/package.json +32 -0
package/.env.example
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Telegram Token (required)
|
|
2
|
+
# Get this from @BotFather on Telegram
|
|
3
|
+
TELEGRAM_TOKEN=your_telegram_bot_token_here
|
|
4
|
+
|
|
5
|
+
# Typing indicator refresh interval in milliseconds (optional, default: 4000)
|
|
6
|
+
TYPING_INTERVAL_MS=4000
|
|
7
|
+
|
|
8
|
+
# Gemini command and mode options
|
|
9
|
+
GEMINI_COMMAND=gemini
|
|
10
|
+
# GEMINI_APPROVAL_MODE=default
|
|
11
|
+
# GEMINI_MODEL=
|
|
12
|
+
|
|
13
|
+
# How ACP permission options are auto-selected when requested by the agent.
|
|
14
|
+
# Supported: allow_once, reject_once, cancelled
|
|
15
|
+
ACP_PERMISSION_STRATEGY=allow_once
|
|
16
|
+
|
|
17
|
+
# Gemini overall timeout in milliseconds (optional, default: 120000)
|
|
18
|
+
GEMINI_TIMEOUT_MS=120000
|
|
19
|
+
|
|
20
|
+
# Gemini no-output timeout in milliseconds (optional, default: 60000)
|
|
21
|
+
# Set to 0 to disable idle-timeout behavior.
|
|
22
|
+
GEMINI_NO_OUTPUT_TIMEOUT_MS=60000
|
|
23
|
+
|
|
24
|
+
# Maximum response length in characters (optional, default: 4000)
|
|
25
|
+
# Prevents memory issues with very long responses
|
|
26
|
+
# Telegram has a 4096 character limit per message anyway
|
|
27
|
+
MAX_RESPONSE_LENGTH=4000
|
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Examples and Extensions
|
|
2
|
+
|
|
3
|
+
This file contains examples of how to extend and customize the Telegram-Gemini bridge.
|
|
4
|
+
|
|
5
|
+
## Example 1: Adding Command Handlers
|
|
6
|
+
|
|
7
|
+
You can add special commands that trigger different behaviors:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// Add this before the general text handler in index.js
|
|
11
|
+
|
|
12
|
+
// Handle /start command
|
|
13
|
+
bot.start((ctx) => {
|
|
14
|
+
ctx.reply(
|
|
15
|
+
'👋 Welcome to Gemini CLI Bridge!\n\n' +
|
|
16
|
+
'Send me any message and I\'ll forward it to your local Gemini agent.\n\n' +
|
|
17
|
+
'Features:\n' +
|
|
18
|
+
'• Real-time streaming responses\n' +
|
|
19
|
+
'• Access to local tools via MCP\n' +
|
|
20
|
+
'• Persistent conversation context\n\n' +
|
|
21
|
+
'Just start chatting!'
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Handle /help command
|
|
26
|
+
bot.help((ctx) => {
|
|
27
|
+
ctx.reply(
|
|
28
|
+
'💡 How to use:\n\n' +
|
|
29
|
+
'1. Send any message or question\n' +
|
|
30
|
+
'2. Wait for Gemini to process it\n' +
|
|
31
|
+
'3. Watch the response stream in real-time\n\n' +
|
|
32
|
+
'Your Gemini CLI session is persistent, so context is maintained across messages!'
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Example 2: Adding Typing Indicator
|
|
38
|
+
|
|
39
|
+
Show that the bot is "typing" while processing:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
bot.on('text', async (ctx) => {
|
|
43
|
+
let fullResponse = "";
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
// Show typing indicator
|
|
47
|
+
await ctx.sendChatAction('typing');
|
|
48
|
+
|
|
49
|
+
const info = await ctx.reply("🤔 Thinking...");
|
|
50
|
+
|
|
51
|
+
// Continue typing periodically
|
|
52
|
+
const typingInterval = setInterval(() => {
|
|
53
|
+
ctx.sendChatAction('typing').catch(() => {});
|
|
54
|
+
}, 5000);
|
|
55
|
+
|
|
56
|
+
const { textStream } = await streamText({
|
|
57
|
+
model: geminiAgent,
|
|
58
|
+
prompt: ctx.message.text,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// ... rest of the streaming logic
|
|
62
|
+
|
|
63
|
+
// Stop typing indicator
|
|
64
|
+
clearInterval(typingInterval);
|
|
65
|
+
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// ... error handling
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Example 3: Adding Response Time Tracking
|
|
73
|
+
|
|
74
|
+
Track how long responses take:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
bot.on('text', async (ctx) => {
|
|
78
|
+
let fullResponse = "";
|
|
79
|
+
const startTime = Date.now();
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const info = await ctx.reply("🤔 Thinking...");
|
|
83
|
+
|
|
84
|
+
const { textStream } = await streamText({
|
|
85
|
+
model: geminiAgent,
|
|
86
|
+
prompt: ctx.message.text,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
for await (const delta of textStream) {
|
|
90
|
+
fullResponse += delta;
|
|
91
|
+
// ... update logic
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const responseTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
95
|
+
|
|
96
|
+
// Add response time to final message
|
|
97
|
+
await ctx.telegram.editMessageText(
|
|
98
|
+
ctx.chat.id,
|
|
99
|
+
info.message_id,
|
|
100
|
+
null,
|
|
101
|
+
`${fullResponse}\n\n⏱️ _Responded in ${responseTime}s_`,
|
|
102
|
+
{ parse_mode: 'Markdown' }
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// ... error handling
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Example 4: User Whitelisting
|
|
112
|
+
|
|
113
|
+
Restrict bot access to specific users:
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// Add at the top after imports
|
|
117
|
+
const ALLOWED_USERS = process.env.ALLOWED_USER_IDS?.split(',').map(id => parseInt(id)) || [];
|
|
118
|
+
|
|
119
|
+
// Add middleware before handlers
|
|
120
|
+
bot.use(async (ctx, next) => {
|
|
121
|
+
const userId = ctx.from?.id;
|
|
122
|
+
|
|
123
|
+
if (ALLOWED_USERS.length > 0 && !ALLOWED_USERS.includes(userId)) {
|
|
124
|
+
await ctx.reply('⛔ Sorry, you are not authorized to use this bot.');
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return next();
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then add to `.env`:
|
|
133
|
+
```env
|
|
134
|
+
ALLOWED_USER_IDS=123456789,987654321
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Example 5: Conversation Context Reset
|
|
138
|
+
|
|
139
|
+
Add a command to reset the Gemini CLI session:
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
bot.command('reset', async (ctx) => {
|
|
143
|
+
try {
|
|
144
|
+
// You would need to implement session management
|
|
145
|
+
// This is a simplified example
|
|
146
|
+
await ctx.reply('🔄 Context reset! Starting fresh conversation.');
|
|
147
|
+
} catch (error) {
|
|
148
|
+
await ctx.reply('❌ Failed to reset context.');
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Example 6: Adding Message Queue
|
|
154
|
+
|
|
155
|
+
Handle multiple concurrent messages gracefully:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
import PQueue from 'p-queue';
|
|
159
|
+
|
|
160
|
+
const queue = new PQueue({ concurrency: 1 });
|
|
161
|
+
|
|
162
|
+
bot.on('text', async (ctx) => {
|
|
163
|
+
// Add to queue
|
|
164
|
+
queue.add(async () => {
|
|
165
|
+
// ... your message handling logic
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (queue.size > 0) {
|
|
169
|
+
await ctx.reply(`⏳ ${queue.size} message(s) in queue...`);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Example 7: Custom Gemini CLI Arguments
|
|
175
|
+
|
|
176
|
+
Pass custom arguments to Gemini CLI:
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
const geminiAgent = new ACP({
|
|
180
|
+
command: 'gemini',
|
|
181
|
+
args: [
|
|
182
|
+
'--protocol', 'acp',
|
|
183
|
+
'--model', 'gemini-2.0-flash-exp', // Specify model
|
|
184
|
+
'--max-tokens', '2048', // Set token limit
|
|
185
|
+
'--temperature', '0.7' // Control creativity
|
|
186
|
+
]
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Example 8: Error Recovery with Retry
|
|
191
|
+
|
|
192
|
+
Add automatic retry on failures:
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
async function streamWithRetry(prompt, maxRetries = 3) {
|
|
196
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
197
|
+
try {
|
|
198
|
+
return await streamText({
|
|
199
|
+
model: geminiAgent,
|
|
200
|
+
prompt: prompt,
|
|
201
|
+
});
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (attempt === maxRetries) throw error;
|
|
204
|
+
console.log(`Attempt ${attempt} failed, retrying...`);
|
|
205
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
bot.on('text', async (ctx) => {
|
|
211
|
+
// ...
|
|
212
|
+
const { textStream } = await streamWithRetry(ctx.message.text);
|
|
213
|
+
// ...
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Example 9: Logging User Interactions
|
|
218
|
+
|
|
219
|
+
Track usage patterns:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
import fs from 'fs/promises';
|
|
223
|
+
|
|
224
|
+
bot.on('text', async (ctx) => {
|
|
225
|
+
const logEntry = {
|
|
226
|
+
timestamp: new Date().toISOString(),
|
|
227
|
+
userId: ctx.from.id,
|
|
228
|
+
username: ctx.from.username,
|
|
229
|
+
message: ctx.message.text,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
await fs.appendFile(
|
|
233
|
+
'interaction_log.jsonl',
|
|
234
|
+
JSON.stringify(logEntry) + '\n'
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// ... rest of handler
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Example 10: Rich Formatting
|
|
242
|
+
|
|
243
|
+
Use Telegram's formatting options:
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
// In your message updates
|
|
247
|
+
await ctx.telegram.editMessageText(
|
|
248
|
+
ctx.chat.id,
|
|
249
|
+
info.message_id,
|
|
250
|
+
null,
|
|
251
|
+
formatResponse(fullResponse),
|
|
252
|
+
{ parse_mode: 'Markdown' }
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
function formatResponse(text) {
|
|
256
|
+
// Convert **bold** to Telegram bold
|
|
257
|
+
text = text.replace(/\*\*(.*?)\*\*/g, '*$1*');
|
|
258
|
+
|
|
259
|
+
// Convert `code` to Telegram code
|
|
260
|
+
text = text.replace(/`([^`]+)`/g, '`$1`');
|
|
261
|
+
|
|
262
|
+
return text;
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Tips for Extensions
|
|
267
|
+
|
|
268
|
+
1. **Keep it modular**: Create separate files for complex features
|
|
269
|
+
2. **Handle errors**: Always wrap async operations in try-catch
|
|
270
|
+
3. **Test locally**: Use `npm run dev` for quick iteration
|
|
271
|
+
4. **Monitor performance**: Log timing and memory usage
|
|
272
|
+
5. **Document changes**: Update README when adding features
|
|
273
|
+
|
|
274
|
+
## Contributing
|
|
275
|
+
|
|
276
|
+
Have a cool extension? Share it by:
|
|
277
|
+
1. Adding it to this file
|
|
278
|
+
2. Creating an example in `/examples` directory
|
|
279
|
+
3. Opening a pull request
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
For more ideas, check out:
|
|
284
|
+
- [Telegraf documentation](https://telegraf.js.org/)
|
|
285
|
+
- [Vercel AI SDK docs](https://sdk.vercel.ai/)
|
|
286
|
+
- [Gemini CLI documentation](https://github.com/google/generative-ai-docs)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hainan Zhao
|
|
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/QUICKSTART.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Quick Start Guide
|
|
2
|
+
|
|
3
|
+
Get your Telegram-Gemini bridge running in 5 minutes!
|
|
4
|
+
|
|
5
|
+
## Prerequisites Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] Node.js 18+ installed (`node --version`)
|
|
8
|
+
- [ ] Gemini CLI installed (`gemini --version`)
|
|
9
|
+
- [ ] Telegram bot token from [@BotFather](https://t.me/BotFather)
|
|
10
|
+
|
|
11
|
+
## Setup Steps
|
|
12
|
+
|
|
13
|
+
### 1. Install Dependencies
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Configure Environment
|
|
19
|
+
```bash
|
|
20
|
+
cp .env.example .env
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Edit `.env` and add your bot token:
|
|
24
|
+
```env
|
|
25
|
+
TELEGRAM_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 3. Test Gemini CLI
|
|
29
|
+
Verify Gemini CLI supports ACP:
|
|
30
|
+
```bash
|
|
31
|
+
gemini --protocol acp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 4. Run the Bridge
|
|
35
|
+
|
|
36
|
+
**Quick test:**
|
|
37
|
+
```bash
|
|
38
|
+
npm start
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Development (auto-restart):**
|
|
42
|
+
```bash
|
|
43
|
+
npm run dev
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Production (with PM2):**
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g pm2
|
|
49
|
+
pm2 start ecosystem.config.json
|
|
50
|
+
pm2 logs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Verify It's Working
|
|
54
|
+
|
|
55
|
+
1. Open Telegram
|
|
56
|
+
2. Find your bot (search for the username you gave it)
|
|
57
|
+
3. Send a message: "Hello!"
|
|
58
|
+
4. You should see "🤔 Thinking..." followed by Gemini's response
|
|
59
|
+
|
|
60
|
+
## Common Issues
|
|
61
|
+
|
|
62
|
+
### "TELEGRAM_TOKEN is required"
|
|
63
|
+
- Check your `.env` file exists
|
|
64
|
+
- Verify the token is on the line `TELEGRAM_TOKEN=...`
|
|
65
|
+
- No quotes needed around the token
|
|
66
|
+
|
|
67
|
+
### "command not found: gemini"
|
|
68
|
+
- Install Gemini CLI first
|
|
69
|
+
- Verify with: `which gemini`
|
|
70
|
+
|
|
71
|
+
### Bot doesn't respond
|
|
72
|
+
- Check logs: `pm2 logs` (if using PM2)
|
|
73
|
+
- Or check console output
|
|
74
|
+
- Verify your bot token is correct
|
|
75
|
+
|
|
76
|
+
### Rate limit errors (429)
|
|
77
|
+
- Increase `UPDATE_INTERVAL_MS` in `.env` to 2000 or higher
|
|
78
|
+
- Restart the bot
|
|
79
|
+
|
|
80
|
+
## Next Steps
|
|
81
|
+
|
|
82
|
+
- Read the full [README.md](README.md) for detailed configuration
|
|
83
|
+
- Configure MCP servers for tool use
|
|
84
|
+
- Set up auto-start with PM2
|
|
85
|
+
|
|
86
|
+
## Getting Help
|
|
87
|
+
|
|
88
|
+
- Check [README.md](README.md) troubleshooting section
|
|
89
|
+
- Review Gemini CLI documentation
|
|
90
|
+
- Open an issue on GitHub
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
**Pro tip:** Keep the bridge running with PM2 and set it to auto-start on boot:
|
|
95
|
+
```bash
|
|
96
|
+
pm2 startup
|
|
97
|
+
pm2 save
|
|
98
|
+
```
|