safari fix
This commit is contained in:
@@ -7,6 +7,10 @@
|
|||||||
<meta name="theme-color" content="#007AFF">
|
<meta name="theme-color" content="#007AFF">
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||||
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||||
|
<meta http-equiv="Pragma" content="no-cache">
|
||||||
|
<meta http-equiv="Expires" content="0">
|
||||||
|
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="style.css">
|
||||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||||
</head>
|
</head>
|
||||||
@@ -50,6 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="dist/script.js"></script>
|
<script src="dist/script.js?v=2"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -59,9 +59,7 @@ class QuixoticApp {
|
|||||||
this.tg.MainButton.hide();
|
this.tg.MainButton.hide();
|
||||||
|
|
||||||
// Debug Telegram user info
|
// Debug Telegram user info
|
||||||
console.log('🔧 Telegram WebApp initialized');
|
console.log('🔧 WebApp ready, user:', this.tg.initDataUnsafe?.user?.id);
|
||||||
console.log('👤 User ID:', this.tg.initDataUnsafe?.user?.id);
|
|
||||||
console.log('📋 Full initData:', this.tg.initDataUnsafe);
|
|
||||||
} else {
|
} else {
|
||||||
console.log('❌ Telegram WebApp not available');
|
console.log('❌ Telegram WebApp not available');
|
||||||
}
|
}
|
||||||
@@ -202,6 +200,26 @@ class QuixoticApp {
|
|||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
|
// Add touch event listeners to prevent sticky states
|
||||||
|
this.results.querySelectorAll('.tg-list-item').forEach(item => {
|
||||||
|
const element = item as HTMLElement;
|
||||||
|
|
||||||
|
// Reset visual state on touch end
|
||||||
|
element.addEventListener('touchend', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
element.blur();
|
||||||
|
element.style.background = '';
|
||||||
|
element.style.transform = '';
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also handle mouse leave for desktop
|
||||||
|
element.addEventListener('mouseleave', () => {
|
||||||
|
element.style.background = '';
|
||||||
|
element.style.transform = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.results.classList.remove('tg-hidden');
|
this.results.classList.remove('tg-hidden');
|
||||||
this.results.classList.add('tg-list--visible');
|
this.results.classList.add('tg-list--visible');
|
||||||
}
|
}
|
||||||
@@ -217,17 +235,21 @@ class QuixoticApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async convertVideo(videoId: string, title: string, url: string): Promise<void> {
|
public async convertVideo(videoId: string, title: string, url: string): Promise<void> {
|
||||||
console.log('🎵 CONVERT VIDEO CALLED:', { videoId, title, url });
|
console.log('🎵 Converting:', title);
|
||||||
console.log('🔧 Telegram WebApp available:', !!this.tg);
|
|
||||||
|
|
||||||
// Find the clicked element by looking for the one that contains this videoId
|
// Find the clicked element by looking for the one that contains this videoId
|
||||||
const videoElement = document.querySelector(`[onclick*="${videoId}"]`) as HTMLElement;
|
const videoElement = document.querySelector(`[onclick*="${videoId}"]`) as HTMLElement;
|
||||||
if (videoElement) {
|
if (videoElement) {
|
||||||
|
// Force blur to reset any active/focus states
|
||||||
|
videoElement.blur();
|
||||||
|
|
||||||
|
// Remove any stuck hover/active classes on touch devices
|
||||||
|
videoElement.classList.remove('tg-list-item--active');
|
||||||
|
|
||||||
videoElement.classList.add('tg-list-item--converting');
|
videoElement.classList.add('tg-list-item--converting');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Sending convert request...');
|
|
||||||
const response = await fetch('/api/convert', {
|
const response = await fetch('/api/convert', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -241,18 +263,13 @@ class QuixoticApp {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Response status:', response.status);
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Conversion failed with status: ${response.status}`);
|
throw new Error(`Conversion failed with status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: ConvertResponse = await response.json();
|
const data: ConvertResponse = await response.json();
|
||||||
console.log('Response data:', data);
|
|
||||||
|
|
||||||
if (data.audioUrl) {
|
if (data.audioUrl) {
|
||||||
// MP3 conversion successful!
|
|
||||||
console.log('🎉 MP3 conversion successful:', data.audioUrl);
|
|
||||||
console.log('🔧 About to send to Telegram, tg available:', !!this.tg);
|
|
||||||
|
|
||||||
if (this.tg) {
|
if (this.tg) {
|
||||||
// Try WebApp method first (might not work)
|
// Try WebApp method first (might not work)
|
||||||
@@ -261,52 +278,31 @@ class QuixoticApp {
|
|||||||
audioUrl: data.audioUrl,
|
audioUrl: data.audioUrl,
|
||||||
title: title
|
title: title
|
||||||
};
|
};
|
||||||
console.log('📤 Attempting WebApp sendData first:', payload);
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.tg.sendData(JSON.stringify(payload));
|
|
||||||
console.log('✅ WebApp sendData called');
|
|
||||||
this.showMessage('✓ MP3 готов! Отправляем в чат...', 'success');
|
|
||||||
} catch (sendError) {
|
|
||||||
console.error('❌ WebApp sendData failed:', sendError);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always use direct API as primary method now
|
|
||||||
const userId = this.tg?.initDataUnsafe?.user?.id;
|
const userId = this.tg?.initDataUnsafe?.user?.id;
|
||||||
console.log('👤 Current user ID for sending:', userId);
|
|
||||||
|
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
console.error('❌ No user ID available from Telegram WebApp');
|
|
||||||
this.showMessage('❌ Ошибка: не удается определить пользователя', 'error');
|
this.showMessage('❌ Ошибка: не удается определить пользователя', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send via direct API
|
||||||
try {
|
try {
|
||||||
console.log('🔄 Sending via direct API...');
|
|
||||||
const requestData = {
|
|
||||||
userId: userId,
|
|
||||||
audioUrl: data.audioUrl,
|
|
||||||
title: title
|
|
||||||
};
|
|
||||||
console.log('📦 Request data:', requestData);
|
|
||||||
|
|
||||||
const directResponse = await fetch('/api/telegram-send', {
|
const directResponse = await fetch('/api/telegram-send', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(requestData)
|
body: JSON.stringify({
|
||||||
|
userId: userId,
|
||||||
|
audioUrl: data.audioUrl,
|
||||||
|
title: title
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
if (directResponse.ok) {
|
if (directResponse.ok) {
|
||||||
const result = await directResponse.json();
|
|
||||||
console.log('✅ Direct API success:', result);
|
|
||||||
this.showMessage('✅ MP3 отправлен в чат!', 'success');
|
this.showMessage('✅ MP3 отправлен в чат!', 'success');
|
||||||
} else {
|
} else {
|
||||||
const error = await directResponse.json();
|
|
||||||
console.error('❌ Direct API failed:', error);
|
|
||||||
this.showMessage('❌ Ошибка отправки в Telegram', 'error');
|
this.showMessage('❌ Ошибка отправки в Telegram', 'error');
|
||||||
}
|
}
|
||||||
} catch (directError) {
|
} catch (directError) {
|
||||||
console.error('❌ Direct API request failed:', directError);
|
|
||||||
this.showMessage('❌ Ошибка соединения с ботом', 'error');
|
this.showMessage('❌ Ошибка соединения с ботом', 'error');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -254,17 +254,30 @@ body {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tg-list-item:hover {
|
/* Hover effects for desktop */
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
.tg-list-item:hover {
|
||||||
background: var(--tg-color-secondary-bg);
|
background: var(--tg-color-secondary-bg);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Touch feedback for mobile - brief highlight only */
|
||||||
.tg-list-item:active {
|
.tg-list-item:active {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
background: var(--tg-color-secondary-bg);
|
background: var(--tg-color-secondary-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prevent sticky hover states on touch devices */
|
||||||
|
@media (hover: none) {
|
||||||
|
.tg-list-item:hover {
|
||||||
|
background: var(--tg-color-section-bg);
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.tg-list-item__content {
|
.tg-list-item__content {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
183
src/bot.ts
183
src/bot.ts
@@ -64,19 +64,11 @@ export class QuixoticBot {
|
|||||||
private setupHandlers(): void {
|
private setupHandlers(): void {
|
||||||
console.log('🔧 Setting up bot handlers...');
|
console.log('🔧 Setting up bot handlers...');
|
||||||
|
|
||||||
// Log all incoming messages for debugging
|
// Handle messages
|
||||||
this.bot.on('message', (msg: any) => {
|
this.bot.on('message', (msg: any) => {
|
||||||
console.log('📨 Received message:', {
|
|
||||||
type: msg.web_app ? 'web_app_data' : (msg.text ? 'text' : 'other'),
|
|
||||||
from: msg.from?.id,
|
|
||||||
chat: msg.chat?.id,
|
|
||||||
hasWebAppData: !!msg.web_app?.data,
|
|
||||||
webAppDataLength: msg.web_app?.data?.length || 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle web app data in regular message event
|
// Handle web app data in regular message event
|
||||||
if (msg.web_app?.data) {
|
if (msg.web_app?.data) {
|
||||||
console.log('🔍 Web app data found in message:', msg.web_app.data);
|
|
||||||
this.handleWebAppData(msg);
|
this.handleWebAppData(msg);
|
||||||
return; // Important: don't process as regular message
|
return; // Important: don't process as regular message
|
||||||
}
|
}
|
||||||
@@ -177,31 +169,21 @@ export class QuixoticBot {
|
|||||||
|
|
||||||
// Handle web app data - primary event handler
|
// Handle web app data - primary event handler
|
||||||
this.bot.on('web_app_data', async (msg: Message) => {
|
this.bot.on('web_app_data', async (msg: Message) => {
|
||||||
console.log('🔍 Web app data received via web_app_data event:', msg.web_app?.data);
|
|
||||||
this.handleWebAppData(msg);
|
this.handleWebAppData(msg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Additional handler for callback queries (sometimes WebApp data comes here)
|
// Handle callback queries
|
||||||
this.bot.on('callback_query', async (query: any) => {
|
this.bot.on('callback_query', async (query: any) => {
|
||||||
console.log('📞 Callback query received:', {
|
|
||||||
id: query.id,
|
|
||||||
data: query.data,
|
|
||||||
from: query.from?.id
|
|
||||||
});
|
|
||||||
|
|
||||||
if (query.data) {
|
if (query.data) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(query.data);
|
const data = JSON.parse(query.data);
|
||||||
if (data.action === 'send_audio') {
|
if (data.action === 'send_audio') {
|
||||||
console.log('🎵 Audio action from callback query');
|
|
||||||
await this.sendAudioFileInternal(query.message.chat.id, data.audioUrl, data.title);
|
await this.sendAudioFileInternal(query.message.chat.id, data.audioUrl, data.title);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
console.log('Callback query data is not JSON, ignoring');
|
// Not JSON, ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Answer callback query to remove loading state
|
|
||||||
await this.bot.answerCallbackQuery(query.id);
|
await this.bot.answerCallbackQuery(query.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -259,51 +241,6 @@ export class QuixoticBot {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add universal event logger for debugging
|
|
||||||
this.bot.on('edited_message', (msg: any) => {
|
|
||||||
console.log('✏️ Edited message received, checking for web app data:', !!msg.web_app?.data);
|
|
||||||
if (msg.web_app?.data) {
|
|
||||||
this.handleWebAppData(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.bot.on('channel_post', (msg: any) => {
|
|
||||||
console.log('📢 Channel post received, checking for web app data:', !!msg.web_app?.data);
|
|
||||||
if (msg.web_app?.data) {
|
|
||||||
this.handleWebAppData(msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Log all update types
|
|
||||||
this.bot.on('update', (update: any) => {
|
|
||||||
const updateTypes = Object.keys(update).filter(key => key !== 'update_id');
|
|
||||||
console.log('🔄 Update received:', {
|
|
||||||
types: updateTypes,
|
|
||||||
update_id: update.update_id
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check for web_app_data in any part of the update
|
|
||||||
const checkForWebAppData = (obj: any, path = ''): any => {
|
|
||||||
if (!obj || typeof obj !== 'object') return null;
|
|
||||||
|
|
||||||
if (obj.web_app?.data) {
|
|
||||||
console.log(`🎯 Found web_app_data at ${path}:`, obj.web_app.data);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(obj)) {
|
|
||||||
const result = checkForWebAppData(value, `${path}.${key}`);
|
|
||||||
if (result) return result;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const msgWithData = checkForWebAppData(update);
|
|
||||||
if (msgWithData && msgWithData.chat?.id) {
|
|
||||||
console.log('🚀 Processing web app data found in update');
|
|
||||||
this.handleWebAppData(msgWithData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('✅ Bot handlers setup complete');
|
console.log('✅ Bot handlers setup complete');
|
||||||
}
|
}
|
||||||
@@ -319,144 +256,64 @@ export class QuixoticBot {
|
|||||||
|
|
||||||
private async sendAudioFileInternal(chatId: number, audioUrl: string, title: string): Promise<void> {
|
private async sendAudioFileInternal(chatId: number, audioUrl: string, title: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log(`🎵 Starting sendAudioFile to chat ${chatId}`);
|
console.log(`📤 Sending: ${title} to chat ${chatId}`);
|
||||||
console.log(`🔗 Audio URL: ${audioUrl}`);
|
|
||||||
console.log(`📝 Title: ${title}`);
|
|
||||||
|
|
||||||
// Send initial status message
|
// Try sending as audio
|
||||||
const statusMsg = await this.bot.sendMessage(chatId, '⏳ Подготавливаю MP3 файл...');
|
|
||||||
|
|
||||||
// Validate audio URL
|
|
||||||
if (!audioUrl.startsWith('http')) {
|
|
||||||
throw new Error(`Invalid audio URL: ${audioUrl}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try sending as audio with proper error handling
|
|
||||||
try {
|
try {
|
||||||
console.log('🚀 Attempting sendAudio...');
|
await this.bot.sendAudio(chatId, audioUrl, {
|
||||||
const audioResult = await this.bot.sendAudio(chatId, audioUrl, {
|
|
||||||
title: title,
|
title: title,
|
||||||
performer: 'SoundCloud',
|
performer: 'SoundCloud',
|
||||||
caption: `🎵 ${title}\n\n🤖 Загружено через Quixotic`,
|
caption: `🎵 ${title}`,
|
||||||
parse_mode: undefined,
|
parse_mode: undefined
|
||||||
protect_content: false
|
|
||||||
});
|
});
|
||||||
|
console.log(`✅ Audio sent: ${title}`);
|
||||||
console.log(`✅ Audio sent successfully! Message ID: ${audioResult.message_id}`);
|
|
||||||
|
|
||||||
// Delete status message after success
|
|
||||||
try {
|
|
||||||
await this.bot.deleteMessage(chatId, statusMsg.message_id);
|
|
||||||
} catch {
|
|
||||||
console.log('Could not delete status message (not critical)');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (audioError: any) {
|
} catch (audioError: any) {
|
||||||
console.error('❌ SendAudio failed:', audioError.message);
|
// Fallback: try as document
|
||||||
console.error('Audio error details:', audioError);
|
|
||||||
|
|
||||||
// Update status message
|
|
||||||
await this.bot.editMessageText('📄 Отправляю как документ...', {
|
|
||||||
chat_id: chatId,
|
|
||||||
message_id: statusMsg.message_id
|
|
||||||
});
|
|
||||||
|
|
||||||
// Fallback: try sending as document
|
|
||||||
try {
|
try {
|
||||||
console.log('🚀 Attempting sendDocument fallback...');
|
await this.bot.sendDocument(chatId, audioUrl, {
|
||||||
const docResult = await this.bot.sendDocument(chatId, audioUrl, {
|
caption: `🎵 ${title}`,
|
||||||
caption: `🎵 ${title}\n\n🤖 Загружено через Quixotic`,
|
|
||||||
parse_mode: undefined
|
parse_mode: undefined
|
||||||
});
|
});
|
||||||
|
console.log(`✅ Document sent: ${title}`);
|
||||||
console.log(`✅ Document sent successfully! Message ID: ${docResult.message_id}`);
|
|
||||||
|
|
||||||
// Delete status message after success
|
|
||||||
try {
|
|
||||||
await this.bot.deleteMessage(chatId, statusMsg.message_id);
|
|
||||||
} catch {
|
|
||||||
console.log('Could not delete status message (not critical)');
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} catch (documentError: any) {
|
} catch (documentError: any) {
|
||||||
console.error('❌ SendDocument also failed:', documentError.message);
|
|
||||||
console.error('Document error details:', documentError);
|
|
||||||
throw documentError;
|
throw documentError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('💥 Complete send audio failure:', error.message);
|
console.error('❌ Send failed:', error.message);
|
||||||
console.error('Full error object:', error);
|
|
||||||
|
|
||||||
// Send fallback message with direct link
|
// Send fallback with link
|
||||||
try {
|
try {
|
||||||
await this.bot.sendMessage(chatId,
|
await this.bot.sendMessage(chatId,
|
||||||
'❌ Не удалось отправить файл автоматически.\n\n' +
|
`❌ Не удалось отправить файл.\n🎵 ${title}\n🔗 ${audioUrl}`
|
||||||
`🎵 *${title}*\n\n` +
|
|
||||||
`📥 Скачайте напрямую: [Ссылка на MP3](${audioUrl})\n\n` +
|
|
||||||
`_Ошибка: ${error.message}_`,
|
|
||||||
{
|
|
||||||
parse_mode: 'Markdown',
|
|
||||||
disable_web_page_preview: false
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} catch (msgError: any) {
|
} catch {
|
||||||
console.error('💥 Failed to send error message:', msgError.message);
|
// Silent fail
|
||||||
// Last resort - try without markdown
|
|
||||||
try {
|
|
||||||
await this.bot.sendMessage(chatId,
|
|
||||||
`❌ Ошибка отправки файла.\n🎵 ${title}\n🔗 ${audioUrl}`
|
|
||||||
);
|
|
||||||
} catch (lastError) {
|
|
||||||
console.error('💥 All fallback methods failed:', lastError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleWebAppData(msg: Message): Promise<void> {
|
private async handleWebAppData(msg: Message): Promise<void> {
|
||||||
const chatId = msg.chat.id;
|
const chatId = msg.chat.id;
|
||||||
const userId = msg.from?.id;
|
|
||||||
|
|
||||||
console.log('🔧 HandleWebAppData called with:', {
|
|
||||||
chatId,
|
|
||||||
userId,
|
|
||||||
hasWebAppData: !!msg.web_app?.data,
|
|
||||||
dataLength: msg.web_app?.data?.length || 0
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!msg.web_app?.data) {
|
if (!msg.web_app?.data) {
|
||||||
console.log('❌ No web app data found in message');
|
|
||||||
await this.bot.sendMessage(chatId, '❌ Данные не получены. Попробуйте еще раз.');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('📝 Raw web app data:', msg.web_app.data);
|
|
||||||
const data: WebAppData = JSON.parse(msg.web_app.data);
|
const data: WebAppData = JSON.parse(msg.web_app.data);
|
||||||
console.log('✅ Parsed data successfully:', data);
|
|
||||||
|
|
||||||
if (data.action === 'send_audio') {
|
if (data.action === 'send_audio') {
|
||||||
console.log(`🎵 Processing audio request for user ${userId}, chat ${chatId}: ${data.title}`);
|
console.log(`🎵 WebApp request: ${data.title}`);
|
||||||
console.log(`🔗 Audio URL: ${data.audioUrl}`);
|
|
||||||
|
|
||||||
// Send immediate confirmation
|
|
||||||
await this.bot.sendMessage(chatId, '⏳ Получил запрос, отправляю аудио...');
|
|
||||||
|
|
||||||
await this.sendAudioFileInternal(chatId, data.audioUrl, data.title);
|
await this.sendAudioFileInternal(chatId, data.audioUrl, data.title);
|
||||||
} else {
|
|
||||||
console.log('⚠️ Unknown action:', data.action);
|
|
||||||
await this.bot.sendMessage(chatId, `❌ Неизвестное действие: ${data.action}`);
|
|
||||||
}
|
}
|
||||||
} catch (parseError: any) {
|
} catch (parseError: any) {
|
||||||
console.error('❌ Web app data parse error:', parseError.message);
|
console.error('❌ WebApp data parse error:', parseError.message);
|
||||||
console.error('Raw data that failed to parse:', msg.web_app?.data);
|
|
||||||
await this.bot.sendMessage(chatId, `❌ Ошибка обработки данных: ${parseError.message}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,17 @@ const soundcloud = new SoundCloudService();
|
|||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Cache-busting middleware for iOS Safari
|
||||||
|
app.use('/dist/*.js', (req: Request, res: Response, next) => {
|
||||||
|
res.set({
|
||||||
|
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
|
||||||
|
'Pragma': 'no-cache',
|
||||||
|
'Expires': '0'
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
app.use(express.static('public'));
|
app.use(express.static('public'));
|
||||||
|
|
||||||
// Ensure downloads directory exists
|
// Ensure downloads directory exists
|
||||||
@@ -29,7 +40,21 @@ if (!fs.existsSync(downloadsDir)) {
|
|||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
app.get('/', (req: Request, res: Response) => {
|
app.get('/', (req: Request, res: Response) => {
|
||||||
res.sendFile(path.join(__dirname, '../public/index.html'));
|
// Read and modify index.html to add timestamp for iOS cache busting
|
||||||
|
const indexPath = path.join(__dirname, '../public/index.html');
|
||||||
|
let html = fs.readFileSync(indexPath, 'utf8');
|
||||||
|
|
||||||
|
// Add timestamp to script URL for cache busting
|
||||||
|
const timestamp = Date.now();
|
||||||
|
html = html.replace('dist/script.js?v=2', `dist/script.js?v=${timestamp}`);
|
||||||
|
|
||||||
|
res.set({
|
||||||
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
||||||
|
'Pragma': 'no-cache',
|
||||||
|
'Expires': '0'
|
||||||
|
});
|
||||||
|
|
||||||
|
res.send(html);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Search videos
|
// Search videos
|
||||||
@@ -189,35 +214,32 @@ app.post('/api/convert', async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Direct Telegram API for sending audio when WebApp fails
|
// Direct Telegram API for sending audio
|
||||||
app.post('/api/telegram-send', async (req: Request, res: Response) => {
|
app.post('/api/telegram-send', async (req: Request, res: Response) => {
|
||||||
|
console.log('🚀 Telegram send request received');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { userId, audioUrl, title }: { userId?: string; audioUrl?: string; title?: string } = req.body;
|
const { userId, audioUrl, title }: { userId?: string; audioUrl?: string; title?: string } = req.body;
|
||||||
console.log('🔄 Direct Telegram send request:', { userId, audioUrl, title });
|
console.log(`📤 Sending to user ${userId}: ${title}`);
|
||||||
|
|
||||||
if (!userId || !audioUrl || !title) {
|
if (!userId || !audioUrl || !title) {
|
||||||
return res.status(400).json({ error: 'Missing required fields' });
|
return res.status(400).json({ error: 'Missing required fields' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get bot instance
|
|
||||||
const botInstance = (global as any).quixoticBot;
|
const botInstance = (global as any).quixoticBot;
|
||||||
if (!botInstance) {
|
if (!botInstance) {
|
||||||
console.log('❌ No bot instance available');
|
console.log('❌ Bot not available');
|
||||||
return res.status(500).json({ error: 'Bot not available' });
|
return res.status(500).json({ error: 'Bot not available' });
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('🤖 Using bot instance for direct send');
|
|
||||||
|
|
||||||
// Use userId as chatId for private chats (this is how Telegram works)
|
|
||||||
const chatId = parseInt(userId);
|
const chatId = parseInt(userId);
|
||||||
console.log(`📤 Sending audio to chat ${chatId}`);
|
|
||||||
|
|
||||||
await botInstance.sendAudioFile(chatId, audioUrl, title);
|
await botInstance.sendAudioFile(chatId, audioUrl, title);
|
||||||
console.log('✅ Audio sent successfully via direct API');
|
console.log('✅ Audio sent successfully');
|
||||||
|
|
||||||
res.json({ success: true, message: 'Audio sent successfully' });
|
res.json({ success: true, message: 'Audio sent successfully' });
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('❌ Direct send error:', error.message);
|
console.error('❌ Send failed:', error.message);
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user