Add Telegram notification integration for geofencing
Features: - Multi-channel notifications (Email + Telegram) - User-configurable notification settings per channel - Telegram bot integration with rich messages, location pins, and inline buttons - QR code generation for easy bot access (@myidbot support) - Admin UI for notification settings management - Test functionality for Telegram connection - Comprehensive documentation Implementation: - lib/telegram-service.ts: Telegram API integration - lib/notification-settings-db.ts: Database layer for user notification preferences - lib/geofence-notifications.ts: Extended for parallel multi-channel delivery - API routes for settings management and testing - Admin UI with QR code display and step-by-step instructions - Database table: UserNotificationSettings Documentation: - docs/telegram.md: Technical implementation guide - docs/telegram-anleitung.md: User guide with @myidbot instructions - docs/telegram-setup.md: Admin setup guide - README.md: Updated NPM scripts section Docker: - Updated Dockerfile to copy public directory - Added TELEGRAM_BOT_TOKEN environment variable - Integrated notification settings initialization in db:init 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
157
lib/notification-settings-db.ts
Normal file
157
lib/notification-settings-db.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import path from 'path';
|
||||
|
||||
const dbPath = path.join(process.cwd(), 'data', 'database.sqlite');
|
||||
|
||||
export interface UserNotificationSettings {
|
||||
user_id: string;
|
||||
email_enabled: boolean;
|
||||
telegram_enabled: boolean;
|
||||
telegram_chat_id: string | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notification settings for a user
|
||||
* Returns default settings if not found
|
||||
*/
|
||||
export function getUserNotificationSettings(
|
||||
userId: string
|
||||
): UserNotificationSettings {
|
||||
const db = new Database(dbPath);
|
||||
try {
|
||||
const stmt = db.prepare(`
|
||||
SELECT
|
||||
user_id,
|
||||
email_enabled,
|
||||
telegram_enabled,
|
||||
telegram_chat_id,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM UserNotificationSettings
|
||||
WHERE user_id = ?
|
||||
`);
|
||||
|
||||
const result = stmt.get(userId) as any;
|
||||
|
||||
// Return defaults if no settings found
|
||||
if (!result) {
|
||||
return {
|
||||
user_id: userId,
|
||||
email_enabled: true,
|
||||
telegram_enabled: false,
|
||||
telegram_chat_id: null,
|
||||
};
|
||||
}
|
||||
|
||||
// Convert SQLite integers to booleans
|
||||
return {
|
||||
user_id: result.user_id,
|
||||
email_enabled: result.email_enabled === 1,
|
||||
telegram_enabled: result.telegram_enabled === 1,
|
||||
telegram_chat_id: result.telegram_chat_id,
|
||||
created_at: result.created_at,
|
||||
updated_at: result.updated_at,
|
||||
};
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update notification settings for a user
|
||||
*/
|
||||
export function updateUserNotificationSettings(
|
||||
userId: string,
|
||||
settings: Partial<UserNotificationSettings>
|
||||
): UserNotificationSettings {
|
||||
const db = new Database(dbPath);
|
||||
try {
|
||||
// Check if settings exist
|
||||
const existing = db
|
||||
.prepare('SELECT user_id FROM UserNotificationSettings WHERE user_id = ?')
|
||||
.get(userId);
|
||||
|
||||
if (existing) {
|
||||
// Update existing
|
||||
const updates: string[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
if (settings.email_enabled !== undefined) {
|
||||
updates.push('email_enabled = ?');
|
||||
values.push(settings.email_enabled ? 1 : 0);
|
||||
}
|
||||
if (settings.telegram_enabled !== undefined) {
|
||||
updates.push('telegram_enabled = ?');
|
||||
values.push(settings.telegram_enabled ? 1 : 0);
|
||||
}
|
||||
if (settings.telegram_chat_id !== undefined) {
|
||||
updates.push('telegram_chat_id = ?');
|
||||
values.push(settings.telegram_chat_id);
|
||||
}
|
||||
|
||||
updates.push('updated_at = datetime(\'now\')');
|
||||
values.push(userId);
|
||||
|
||||
const stmt = db.prepare(`
|
||||
UPDATE UserNotificationSettings
|
||||
SET ${updates.join(', ')}
|
||||
WHERE user_id = ?
|
||||
`);
|
||||
|
||||
stmt.run(...values);
|
||||
} else {
|
||||
// Insert new
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO UserNotificationSettings (
|
||||
user_id,
|
||||
email_enabled,
|
||||
telegram_enabled,
|
||||
telegram_chat_id
|
||||
) VALUES (?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
stmt.run(
|
||||
userId,
|
||||
settings.email_enabled !== undefined ? (settings.email_enabled ? 1 : 0) : 1,
|
||||
settings.telegram_enabled !== undefined ? (settings.telegram_enabled ? 1 : 0) : 0,
|
||||
settings.telegram_chat_id ?? null
|
||||
);
|
||||
}
|
||||
|
||||
return getUserNotificationSettings(userId);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize notification settings table
|
||||
*/
|
||||
export function initNotificationSettingsTable(): void {
|
||||
const db = new Database(dbPath);
|
||||
try {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS UserNotificationSettings (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
email_enabled INTEGER DEFAULT 1,
|
||||
telegram_enabled INTEGER DEFAULT 0,
|
||||
telegram_chat_id TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
|
||||
FOREIGN KEY (user_id) REFERENCES User(id) ON DELETE CASCADE,
|
||||
CHECK (email_enabled IN (0, 1)),
|
||||
CHECK (telegram_enabled IN (0, 1))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_notification_settings_user
|
||||
ON UserNotificationSettings(user_id);
|
||||
`);
|
||||
|
||||
console.log('✓ UserNotificationSettings table initialized');
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user