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:
223
src/bot.js
Normal file
223
src/bot.js
Normal file
@@ -0,0 +1,223 @@
|
||||
const TelegramBot = require('node-telegram-bot-api');
|
||||
const Database = require('./database');
|
||||
|
||||
class QuixoticBot {
|
||||
constructor(token, webAppUrl) {
|
||||
this.bot = new TelegramBot(token, { polling: true });
|
||||
this.webAppUrl = webAppUrl;
|
||||
this.db = new Database();
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log('> Telegram bot initialized');
|
||||
this.setupCommands();
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
setupCommands() {
|
||||
// Set bot commands
|
||||
this.bot.setMyCommands([
|
||||
{ command: 'start', description: '0?CAB8BL ?@8;>65=85' },
|
||||
{ command: 'help', description: '><>IL' },
|
||||
{ command: 'history', description: 'AB>@8O ?>8A:0' }
|
||||
]);
|
||||
}
|
||||
|
||||
setupHandlers() {
|
||||
// Start command
|
||||
this.bot.onText(/\/start/, async (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const user = msg.from;
|
||||
|
||||
try {
|
||||
// Add user to database
|
||||
await this.db.addUser(user);
|
||||
|
||||
const keyboard = {
|
||||
inline_keyboard: [[
|
||||
{
|
||||
text: '<µ B:@KBL Quixotic',
|
||||
web_app: { url: this.webAppUrl }
|
||||
}
|
||||
]]
|
||||
};
|
||||
|
||||
await this.bot.sendMessage(chatId,
|
||||
'<µ >1@> ?>60;>20BL 2 Quixotic!\n\n' +
|
||||
'0948 ;N1CN ?5A=N =0 YouTube 8 ?>;CG8 MP3 D09; ?@O<> 2 G0B.\n\n' +
|
||||
'06<8 :=>?:C =865, GB>1K =0G0BL ?>8A::',
|
||||
{ reply_markup: keyboard }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Start command error:', error);
|
||||
await this.bot.sendMessage(chatId, '@>87>H;0 >H81:0. >?@>1C9B5 ?>765.');
|
||||
}
|
||||
});
|
||||
|
||||
// Help command
|
||||
this.bot.onText(/\/help/, async (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
|
||||
const helpText = `<µ *Quixotic - YouTube to MP3*
|
||||
|
||||
*0: ?>;L7>20BLAO:*
|
||||
1ã 06<8 :=>?:C "B:@KBL Quixotic"
|
||||
2ã 2548 =0720=85 ?5A=8 2 ?>8A:>2CN AB@>:C
|
||||
3ã K15@8 =C6=K9 B@5: 87 A?8A:0
|
||||
4ã >;CG8 MP3 D09; 2 G0B!
|
||||
|
||||
*><0=4K:*
|
||||
/start - 0?CAB8BL ?@8;>65=85
|
||||
/help - -B0 A?@02:0
|
||||
/history - AB>@8O ?>8A:0
|
||||
|
||||
*>7<>6=>AB8:*
|
||||
" >8A: ?> YouTube
|
||||
" KA>:>5 :0G5AB2> MP3 (128kbps)
|
||||
" KAB@0O :>=25@B0F8O
|
||||
" AB>@8O ?>8A:0`;
|
||||
|
||||
await this.bot.sendMessage(chatId, helpText, { parse_mode: 'Markdown' });
|
||||
});
|
||||
|
||||
// History command
|
||||
this.bot.onText(/\/history/, async (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const userId = msg.from.id;
|
||||
|
||||
try {
|
||||
const user = await this.db.getUserByTelegramId(userId);
|
||||
if (!user) {
|
||||
await this.bot.sendMessage(chatId, 'AB>@8O ?>8A:0 ?CAB0.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get recent search history
|
||||
const history = await this.db.db.all(
|
||||
`SELECT query, created_at FROM search_history
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10`,
|
||||
[user.id]
|
||||
);
|
||||
|
||||
if (history.length === 0) {
|
||||
await this.bot.sendMessage(chatId, 'AB>@8O ?>8A:0 ?CAB0.');
|
||||
return;
|
||||
}
|
||||
|
||||
let historyText = '=È *>A;54=85 ?>8A:>2K5 70?@>AK:*\n\n';
|
||||
history.forEach((item, index) => {
|
||||
const date = new Date(item.created_at).toLocaleDateString('ru-RU');
|
||||
historyText += `${index + 1}. ${item.query} _(${date})_\n`;
|
||||
});
|
||||
|
||||
await this.bot.sendMessage(chatId, historyText, { parse_mode: 'Markdown' });
|
||||
} catch (error) {
|
||||
console.error('History command error:', error);
|
||||
await this.bot.sendMessage(chatId, 'H81:0 ?>;CG5=8O 8AB>@88.');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle web app data
|
||||
this.bot.on('web_app_data', async (msg) => {
|
||||
const chatId = msg.chat.id;
|
||||
const data = JSON.parse(msg.web_app.data);
|
||||
|
||||
try {
|
||||
if (data.action === 'send_audio') {
|
||||
await this.sendAudioFile(chatId, data.audioUrl, data.title);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Web app data error:', error);
|
||||
await this.bot.sendMessage(chatId, 'H81:0 >1@01>B:8 40==KE.');
|
||||
}
|
||||
});
|
||||
|
||||
// Handle inline queries for search
|
||||
this.bot.on('inline_query', async (query) => {
|
||||
const queryId = query.id;
|
||||
const searchQuery = query.query;
|
||||
|
||||
if (!searchQuery || searchQuery.length < 3) {
|
||||
await this.bot.answerInlineQuery(queryId, []);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const YouTubeService = require('./youtube');
|
||||
const youtube = new YouTubeService();
|
||||
const videos = await youtube.searchVideos(searchQuery, 5);
|
||||
|
||||
const results = videos.map((video, index) => ({
|
||||
type: 'article',
|
||||
id: `${index}`,
|
||||
title: video.title,
|
||||
description: `${video.channel} " ${this.formatDuration(video.duration)}`,
|
||||
thumb_url: video.thumbnail,
|
||||
input_message_content: {
|
||||
message_text: `<µ ${video.title}\n=ú ${video.url}`
|
||||
}
|
||||
}));
|
||||
|
||||
await this.bot.answerInlineQuery(queryId, results, {
|
||||
cache_time: 300,
|
||||
is_personal: true
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Inline query error:', error);
|
||||
await this.bot.answerInlineQuery(queryId, []);
|
||||
}
|
||||
});
|
||||
|
||||
// Error handler
|
||||
this.bot.on('error', (error) => {
|
||||
console.error('Telegram bot error:', error);
|
||||
});
|
||||
|
||||
console.log(' Bot handlers setup complete');
|
||||
}
|
||||
|
||||
async sendAudioFile(chatId, audioUrl, title) {
|
||||
try {
|
||||
await this.bot.sendMessage(chatId, 'ó >43>B02;820N MP3 D09;...');
|
||||
|
||||
// Send audio file
|
||||
await this.bot.sendAudio(chatId, audioUrl, {
|
||||
title: title,
|
||||
performer: 'Quixotic',
|
||||
caption: `<µ ${title}`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Send audio error:', error);
|
||||
await this.bot.sendMessage(chatId,
|
||||
'L 5 C40;>AL >B?@028BL 0C48>D09;. >?@>1C9B5 5I5 @07.\n\n' +
|
||||
`@O<0O AAK;:0: ${audioUrl}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
formatDuration(seconds) {
|
||||
if (!seconds) return '';
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize bot if this file is run directly
|
||||
if (require.main === module) {
|
||||
const token = process.env.TELEGRAM_BOT_TOKEN;
|
||||
const webAppUrl = process.env.WEB_APP_URL || 'https://your-domain.com';
|
||||
|
||||
if (!token) {
|
||||
console.error('L TELEGRAM_BOT_TOKEN environment variable is required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
new QuixoticBot(token, webAppUrl);
|
||||
}
|
||||
|
||||
module.exports = QuixoticBot;
|
||||
Reference in New Issue
Block a user