Add BMAD, Claude, Cursor, and OpenCode configuration directories along with AGENTS.md documentation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
235 lines
6.0 KiB
JavaScript
235 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Simple Telegram Bot for OpenCode
|
|
* Sends notifications when session becomes idle
|
|
*/
|
|
|
|
import https from 'https'
|
|
import fs from 'fs'
|
|
import path from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
const __dirname = path.dirname(__filename)
|
|
|
|
class SimpleTelegramBot {
|
|
private botToken: string | undefined
|
|
private chatId: string | undefined
|
|
private botUsername: string
|
|
private idleTimeout: number
|
|
private checkInterval: number
|
|
private lastActivity: number
|
|
private idleTimer: NodeJS.Timeout | null
|
|
private checkTimer: NodeJS.Timeout | null
|
|
private isIdle: boolean
|
|
|
|
constructor() {
|
|
this.loadEnvFile();
|
|
this.botToken = process.env.TELEGRAM_BOT_TOKEN;
|
|
this.chatId = process.env.TELEGRAM_CHAT_ID;
|
|
this.botUsername = process.env.TELEGRAM_BOT_USERNAME || '@OpenCode';
|
|
this.idleTimeout = parseInt(process.env.TELEGRAM_IDLE_TIMEOUT || '300000'); // 5 minutes default
|
|
this.checkInterval = parseInt(process.env.TELEGRAM_CHECK_INTERVAL || '30000'); // 30 seconds default
|
|
|
|
this.lastActivity = Date.now();
|
|
this.idleTimer = null;
|
|
this.checkTimer = null;
|
|
this.isIdle = false;
|
|
|
|
this.validateConfig();
|
|
}
|
|
|
|
/**
|
|
* Load environment variables from .env file
|
|
*/
|
|
private loadEnvFile(): void {
|
|
const envPath = path.join(__dirname, '..', '..', '.env');
|
|
if (fs.existsSync(envPath)) {
|
|
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
envContent.split('\n').forEach(line => {
|
|
const trimmed = line.trim();
|
|
if (trimmed && !trimmed.startsWith('#')) {
|
|
const [key, ...valueParts] = trimmed.split('=');
|
|
if (key && valueParts.length > 0) {
|
|
process.env[key] = valueParts.join('=');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate configuration
|
|
*/
|
|
private validateConfig(): boolean {
|
|
if (!this.botToken) {
|
|
console.warn('⚠️ TELEGRAM_BOT_TOKEN not set');
|
|
return false;
|
|
}
|
|
if (!this.chatId) {
|
|
console.warn('⚠️ TELEGRAM_CHAT_ID not set');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Initialize the bot
|
|
*/
|
|
init(): void {
|
|
if (!this.validateConfig()) {
|
|
// Removed: console.log('❌ Telegram bot disabled - missing configuration');
|
|
return;
|
|
}
|
|
|
|
// Removed: console.log('📱 Telegram bot initialized');
|
|
this.sendMessage('🚀 OpenCode session started');
|
|
this.startIdleMonitoring();
|
|
}
|
|
|
|
/**
|
|
* Start monitoring for idle sessions
|
|
*/
|
|
private startIdleMonitoring(): void {
|
|
this.resetActivity();
|
|
|
|
// Check for idle state periodically
|
|
this.checkTimer = setInterval(() => {
|
|
const timeSinceLastActivity = Date.now() - this.lastActivity;
|
|
if (timeSinceLastActivity > this.idleTimeout && !this.isIdle) {
|
|
this.handleIdle();
|
|
}
|
|
}, this.checkInterval);
|
|
}
|
|
|
|
/**
|
|
* Reset activity timer
|
|
*/
|
|
resetActivity(): void {
|
|
this.lastActivity = Date.now();
|
|
|
|
if (this.isIdle) {
|
|
this.isIdle = false;
|
|
this.sendMessage('🟢 Session resumed - User is active again');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle idle state
|
|
*/
|
|
private handleIdle(): void {
|
|
this.isIdle = true;
|
|
const minutes = Math.floor(this.idleTimeout / 60000);
|
|
this.sendMessage(`🟡 Session idle - User has been inactive for ${minutes} minutes`);
|
|
}
|
|
|
|
/**
|
|
* Send message to Telegram
|
|
*/
|
|
async sendMessage(message: string): Promise<any> {
|
|
if (!this.validateConfig()) {
|
|
// Removed: console.log('Cannot send message - missing configuration');
|
|
return;
|
|
}
|
|
|
|
if (!message || message.trim() === '') {
|
|
// Removed: console.log('Cannot send empty message:', JSON.stringify(message));
|
|
return;
|
|
}
|
|
|
|
const data = JSON.stringify({
|
|
chat_id: this.chatId,
|
|
text: message.trim()
|
|
});
|
|
|
|
const dataBuffer = Buffer.from(data, 'utf8');
|
|
|
|
const options = {
|
|
hostname: 'api.telegram.org',
|
|
port: 443,
|
|
path: `/bot${this.botToken}/sendMessage`,
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json; charset=utf-8',
|
|
'Content-Length': dataBuffer.length
|
|
}
|
|
};
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const req = https.request(options, (res) => {
|
|
let responseData = '';
|
|
|
|
res.on('data', (chunk) => {
|
|
responseData += chunk;
|
|
});
|
|
|
|
res.on('end', () => {
|
|
try {
|
|
const response = JSON.parse(responseData);
|
|
if (response.ok) {
|
|
//console.log('📱 Message sent:', message);
|
|
resolve(response);
|
|
} else {
|
|
//console.error('❌ Failed to send message:', response.description);
|
|
reject(new Error(response.description));
|
|
}
|
|
} catch (error) {
|
|
//console.error('❌ Error parsing response:', error);
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
|
|
req.on('error', (error) => {
|
|
//console.error('❌ Error sending message:', error);
|
|
reject(error);
|
|
});
|
|
|
|
req.write(dataBuffer);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Cleanup resources
|
|
*/
|
|
cleanup(sendEndMessage: boolean = true): void {
|
|
if (this.checkTimer) {
|
|
clearInterval(this.checkTimer);
|
|
}
|
|
if (sendEndMessage) {
|
|
this.sendMessage('🏁 OpenCode session ended');
|
|
}
|
|
// Removed: console.log('📱 Telegram bot cleaned up');
|
|
}
|
|
}
|
|
|
|
// Export for use as module
|
|
export { SimpleTelegramBot }
|
|
export default SimpleTelegramBot
|
|
|
|
// Auto-initialize if run directly
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
const bot = new SimpleTelegramBot();
|
|
bot.init();
|
|
|
|
// Handle cleanup on exit
|
|
process.on('SIGINT', () => {
|
|
bot.cleanup();
|
|
setTimeout(() => process.exit(0), 1000);
|
|
});
|
|
|
|
process.on('SIGTERM', () => {
|
|
bot.cleanup();
|
|
setTimeout(() => process.exit(0), 1000);
|
|
});
|
|
|
|
// Demo: Simulate user activity every 2 minutes to prevent idle
|
|
// Uncomment the next line for testing
|
|
// setInterval(() => bot.resetActivity(), 120000);
|
|
|
|
// Removed: console.log('📱 Telegram bot running... Press Ctrl+C to stop');
|
|
|
|
}
|