Initial commit: Complete Quixotic Telegram MiniApp implementation

- Set up Express.js server with YouTube search and MP3 conversion API
- Created Telegram Web App frontend with responsive design
- Implemented SQLite database for user management and history
- Added Telegram Bot integration with commands and Web App support
- Configured FFmpeg-based audio conversion pipeline
- Added comprehensive documentation and deployment guides

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andrey Kondratev
2025-08-25 10:37:07 +05:00
commit 2bfb456cf3
9 changed files with 968 additions and 0 deletions

104
src/database.js Normal file
View File

@@ -0,0 +1,104 @@
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
class Database {
constructor() {
this.dbPath = path.join(__dirname, '../database/quixotic.db');
this.db = new sqlite3.Database(this.dbPath);
this.init();
}
init() {
this.db.serialize(() => {
// Users table
this.db.run(`CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
telegram_id INTEGER UNIQUE NOT NULL,
username TEXT,
first_name TEXT,
last_name TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`);
// Search history table
this.db.run(`CREATE TABLE IF NOT EXISTS search_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
query TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
// Downloaded files table
this.db.run(`CREATE TABLE IF NOT EXISTS downloads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
youtube_id TEXT NOT NULL,
title TEXT NOT NULL,
file_path TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users (id)
)`);
});
}
addUser(telegramUser) {
return new Promise((resolve, reject) => {
const { id, username, first_name, last_name } = telegramUser;
this.db.run(
`INSERT OR REPLACE INTO users (telegram_id, username, first_name, last_name)
VALUES (?, ?, ?, ?)`,
[id, username, first_name, last_name],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
}
addSearchHistory(userId, query) {
return new Promise((resolve, reject) => {
this.db.run(
`INSERT INTO search_history (user_id, query) VALUES (?, ?)`,
[userId, query],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
}
addDownload(userId, youtubeId, title, filePath) {
return new Promise((resolve, reject) => {
this.db.run(
`INSERT INTO downloads (user_id, youtube_id, title, file_path) VALUES (?, ?, ?, ?)`,
[userId, youtubeId, title, filePath],
function(err) {
if (err) reject(err);
else resolve(this.lastID);
}
);
});
}
getUserByTelegramId(telegramId) {
return new Promise((resolve, reject) => {
this.db.get(
`SELECT * FROM users WHERE telegram_id = ?`,
[telegramId],
(err, row) => {
if (err) reject(err);
else resolve(row);
}
);
});
}
close() {
this.db.close();
}
}
module.exports = Database;