### Prepare Project for Railway Deployment Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_SETUP.md This bash script demonstrates the commands to prepare a local project for deployment on Railway by staging changes, committing them, and pushing to the main branch of a GitHub repository. ```bash git add . git commit -m "Railway deployment ready" git push origin main ``` -------------------------------- ### Destroy Fly.io Application Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_SETUP.md This bash command is used to remove or destroy an existing application deployed on the Fly.io platform. It is typically used when migrating to a new deployment platform like Railway. ```bash flyctl apps destroy telegram-analytics ``` -------------------------------- ### Railway CLI Installation and Login Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This section provides commands to install the Railway CLI globally using npm and then log in to your Railway account. This is a prerequisite for interacting with Railway services from the command line. ```bash # Установка Railway CLI (если не установлен) npm install -g @railway/cli # Вход в аккаунт railway login ``` -------------------------------- ### Configure Environment Variables for Railway and Supabase Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_SETUP.md This snippet shows how to set up essential environment variables within the Railway Dashboard for integrating with Gemini AI and Telegram, as well as connecting to a Supabase instance. These variables are crucial for the application's functionality and data persistence. ```shell GEMINI_API_KEY=AIzaSyAZjlVV9jMJimDNqHqpyb0DQfSojIP_tw4 TELEGRAM_API_ID=your_api_id TELEGRAM_API_HASH=your_api_hash SUPABASE_URL=your_supabase_url SUPABASE_KEY=your_supabase_key ``` -------------------------------- ### Canvas Animation Setup and Shape Rendering (JavaScript) Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/analytics_interface_neon.html Initializes the canvas, sets up drawing context, and defines functions for creating, drawing, and animating various shapes (circles, triangles, squares, hexagons). It handles responsive resizing, shape generation with random properties, and animation loop updates. Dependencies include the HTML canvas element and its 2D rendering context. ```javascript let dpr = Math.max(1, Math.min(1.5, window.devicePixelRatio || 1)); function resize() { canvas.width = Math.floor(window.innerWidth * dpr); canvas.height = Math.floor(window.innerHeight * dpr); } resize(); window.addEventListener('resize', resize); const shapes = []; const baseNum = 36; const isMobile = Math.min(window.innerWidth, window.innerHeight) < 720; const NUM = isMobile ? Math.floor(baseNum * 0.55) : baseNum; const COLORS = ['#60a5fa', '#8b5cf6', '#f472b6', '#22d3ee']; function rand(min, max) { return Math.random() * (max - min) + min; } function newShape() { const type = ['circle', 'triangle', 'square', 'hex'][Math.floor(Math.random() * 4)]; return { type, x: rand(0, canvas.width), y: rand(0, canvas.height), r: rand(5 * dpr, (isMobile ? 14 : 18) * dpr), vx: rand(-0.18, 0.18) * dpr, vy: rand(-0.12, 0.12) * dpr, rot: rand(0, Math.PI * 2), vr: rand(-0.01, 0.01), alpha: rand(0.3, 0.85), life: rand(6, 14), color: COLORS[Math.floor(Math.random() * COLORS.length)] }; } for (let i = 0; i < NUM; i++) shapes.push(newShape()); function drawShape(s) { ctx.save(); ctx.globalAlpha = s.alpha * 0.9; ctx.translate(s.x, s.y); ctx.rotate(s.rot); ctx.shadowBlur = (isMobile ? 18 : 24) * dpr; ctx.shadowColor = s.color; ctx.strokeStyle = s.color; ctx.lineWidth = 2 * dpr; switch (s.type) { case 'circle': ctx.beginPath(); ctx.arc(0, 0, s.r, 0, Math.PI * 2); ctx.stroke(); break; case 'triangle': ctx.beginPath(); const t = s.r; ctx.moveTo(0, -t); ctx.lineTo(t * 0.866, t * 0.5); ctx.lineTo(-t * 0.866, t * 0.5); ctx.closePath(); ctx.stroke(); break; case 'square': ctx.strokeRect(-s.r, -s.r, s.r * 2, s.r * 2); break; case 'hex': ctx.beginPath(); for (let i = 0; i < 6; i++) { const a = (Math.PI / 3) * i; const px = Math.cos(a) * s.r; const py = Math.sin(a) * s.r; i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py); } ctx.closePath(); ctx.stroke(); break; } ctx.restore(); } let last = 0; function tick(ts) { const dt = Math.min(0.033, (ts - last) / 1000 || 0.016); last = ts; ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < shapes.length; i++) { const s = shapes[i]; s.x += s.vx * canvas.width * 0.05 * dt; s.y += s.vy * canvas.height * 0.05 * dt; s.rot += s.vr; s.life -= dt * (isMobile ? 0.18 : 0.25); if (s.x < -50 || s.x > canvas.width + 50 || s.y < -50 || s.y > canvas.height + 50 || s.life < 0) { shapes[i] = newShape(); continue; } drawShape(s); } requestAnimationFrame(tick); } requestAnimationFrame(tick); ``` -------------------------------- ### Executing the Migration Script Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This command runs the 'migrate_to_railway_full.sh' script, which is intended to automate the process of migrating the project to Railway. It's presented as a quick start option after preparing the necessary files. ```bash ./migrate_to_railway_full.sh ``` -------------------------------- ### Start Animated Neon Geometric Background (JavaScript) Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index — копия.html Initializes an animated geometric background on an HTML canvas element with ID 'neon-bg'. It handles canvas resizing, shape generation (circles, triangles, squares, hexagons), and animation using requestAnimationFrame. The background adapts to screen size and device pixel ratio. Dependencies include a canvas element and CSS for shadows and stroke colors. ```javascript function startNeonBackground() { const canvas = document.getElementById('neon-bg'); const ctx = canvas.getContext('2d'); let dpr = Math.max(1, Math.min(1.5, window.devicePixelRatio || 1)); function resize() { canvas.width = Math.floor(window.innerWidth * dpr); canvas.height = Math.floor(window.innerHeight * dpr); } resize(); window.addEventListener('resize', resize); const shapes = []; const baseNum = 36; const isMobile = Math.min(window.innerWidth, window.innerHeight) < 720; const NUM = isMobile ? Math.floor(baseNum * 0.55) : baseNum; const COLORS = ['#60a5fa', '#8b5cf6', '#f472b6', '#22d3ee']; function rand(min, max) { return Math.random() * (max - min) + min; } function newShape() { const type = ['circle', 'triangle', 'square', 'hex'][Math.floor(Math.random() * 4)]; return { type, x: rand(0, canvas.width), y: rand(0, canvas.height), r: rand(5 * dpr, (isMobile ? 14 : 18) * dpr), vx: rand(-0.18, 0.18) * dpr, vy: rand(-0.12, 0.12) * dpr, rot: rand(0, Math.PI * 2), vr: rand(-0.01, 0.01), alpha: rand(0.3, 0.85), life: rand(6, 14), color: COLORS[Math.floor(Math.random() * COLORS.length)] }; } for (let i = 0; i < NUM; i++) shapes.push(newShape()); function drawShape(s) { ctx.save(); ctx.globalAlpha = s.alpha * 0.9; ctx.translate(s.x, s.y); ctx.rotate(s.rot); ctx.shadowBlur = (isMobile ? 18 : 24) * dpr; ctx.shadowColor = s.color; ctx.strokeStyle = s.color; ctx.lineWidth = 2 * dpr; switch (s.type) { case 'circle': ctx.beginPath(); ctx.arc(0, 0, s.r, 0, Math.PI * 2); ctx.stroke(); break; case 'triangle': ctx.beginPath(); const t = s.r; ctx.moveTo(0, -t); ctx.lineTo(t * 0.866, t * 0.5); ctx.lineTo(-t * 0.866, t * 0.5); ctx.closePath(); ctx.stroke(); break; case 'square': ctx.strokeRect(-s.r, -s.r, s.r * 2, s.r * 2); break; case 'hex': ctx.beginPath(); for (let i = 0; i < 6; i++) { const a = (Math.PI / 3) * i; const px = Math.cos(a) * s.r; const py = Math.sin(a) * s.r; i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py); } ctx.closePath(); ctx.stroke(); break; } ctx.restore(); } let last = 0; function tick(ts) { const dt = Math.min(0.033, (ts - last) / 1000 || 0.016); last = ts; ctx.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < shapes.length; i++) { const s = shapes[i]; s.x += s.vx * canvas.width * 0.05 * dt; s.y += s.vy * canvas.height * 0.05 * dt; s.rot += s.vr; s.life -= dt * (isMobile ? 0.18 : 0.25); if (s.x < -50 || s.x > canvas.width + 50 || s.y < -50 || s.y > canvas.height + 50 || s.life < 0) { shapes[i] = newShape(); continue; } drawShape(s); } requestAnimationFrame(tick); } requestAnimationFrame(tick); } ``` -------------------------------- ### Deploying Application to Railway Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This snippet illustrates two methods for deploying your application to Railway: 'railway up' for automatic deployment and a recommendation to use GitHub integration for automatic deployments upon every push. This streamlines the CI/CD process. ```bash # Автоматический деплой railway up # Или через GitHub (рекомендуется) # 1. Подключите репозиторий в Railway Dashboard # 2. Railway автоматически задеплоит при каждом push ``` -------------------------------- ### Initialize Excel Preview UI and Event Listeners Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/tests/analytics_interface_neon.html Sets up the Excel preview modal, populates sheet and column selection dropdowns, and attaches event listeners to buttons and selection changes. It initializes the workbook context and renders the initial sheet preview. ```javascript function __showExcelPreview(backdrop, XLSX, __excelPreviewCtx) { const wb = __excelPreviewCtx.workbook; const sheetSelect = backdrop.querySelector('#excel-sheet-select'); sheetSelect.innerHTML = ''; wb.SheetNames.forEach((name, idx) => { const opt = document.createElement('option'); opt.value = name; opt.textContent = name; if (idx === 0) opt.selected = true; sheetSelect.appendChild(opt); }); __excelPreviewCtx.sheetName = wb.SheetNames[0]; const colsSelect = backdrop.querySelector('#excel-col-select'); colsSelect.innerHTML = ''; const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; for (let i = 0; i < A.length; i++) { const opt = document.createElement('option'); opt.value = A[i]; opt.textContent = A[i]; if (i === 0) opt.selected = true; colsSelect.appendChild(opt); } backdrop.querySelector('#excel-file-meta').textContent = `${__excelPreviewCtx.file?.name || ''}`; sheetSelect.onchange = () => { __excelPreviewCtx.sheetName = sheetSelect.value; __renderSheetPreview(); }; colsSelect.onchange = () => {}; backdrop.querySelector('#btn-detect-links').onclick = __detectLinksFromSelection; b backdrop.querySelector('#btn-select-all').onclick = () => { document.querySelectorAll('#excel-links-list input[type=checkbox]').forEach(ch => ch.checked = true); __syncSelectedLinks(); }; b backdrop.querySelector('#btn-unselect-all').onclick = () => { document.querySelectorAll('#excel-links-list input[type=checkbox]').forEach(ch => ch.checked = false); __syncSelectedLinks(); }; b backdrop.querySelector('#btn-cancel').onclick = () => { backdrop.classList.remove('show'); }; b backdrop.querySelector('#btn-apply').onclick = __applySelectedLinks; __renderSheetPreview(); bbackdrop.classList.add('show'); } ``` -------------------------------- ### API Health Check and Monitoring (Python) Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt This Python snippet demonstrates how to perform health checks and monitor the status of the API. It uses the 'requests' library to send GET requests to the API's root and '/api' endpoints. The output includes status, version, message, and available endpoints, along with a check for Fly.io health checks. ```python import requests # Check API status response = requests.get('http://localhost:8000/api') api_info = response.json() print(f"Status: {api_info['status']}") print(f"Version: {api_info['version']}") print(f"Message: {api_info['message']}") print("\nAvailable endpoints:") for endpoint in api_info['endpoints']: print(f" - {endpoint}") # For Fly.io health checks health_response = requests.get('http://localhost:8000/') if health_response.status_code == 200: print("Service is healthy") # Expected response time: # - /api endpoint: <50ms # - / endpoint: <100ms (serves HTML) # - Health check frequency: Every 30s on Fly.io ``` -------------------------------- ### Check API Processing Status with JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index.html This asynchronous function checks the processing status of the API to determine if it's busy with active tasks. It makes a GET request to the '/processing-status' endpoint and updates a status element in the DOM with the result. It provides feedback on whether the API is busy or free, including the count of active jobs if available. ```javascript async function checkProcessingStatus() { const statusEl = document.getElementById('disk-status'); statusEl.innerHTML = '
Проверяем занятость API...
'; try { const response = await fetch(`${API_BASE}/processing-status`, { method: 'GET', headers: { 'X-Session-Id': CLIENT_SID } }); const data = await response.json(); if (data.status === 'success') { const msg = data.busy ? `Занят — активных задач: ${data.active_jobs}` : 'Свободен — можно запускать анализ'; statusEl.innerHTML = `
🧠 ${msg}
`; } else { statusEl.innerHTML = `
❌ ${data.message || 'Не удалось получить статус занятости'}
`; } } catch (error) { statusEl.innerHTML = `
❌ Ошибка: ${error.message}
`; } } ``` -------------------------------- ### Railway Project Initialization Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This command initializes a new project within the Railway platform. It's typically run after logging in and prepares the environment for deploying your application. ```bash railway init ``` -------------------------------- ### Railway Support Commands Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This section lists essential commands for troubleshooting and managing your Railway deployment. 'railway logs' provides real-time logs, 'railway variables' displays environment variables, and 'railway status' shows the current deployment status. ```bash # Проверьте логи: railway logs # Проверьте переменные: railway variables # Проверьте статус: railway status ``` -------------------------------- ### Project Preparation and Git Push Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This snippet demonstrates how to stage all project files for commit, create a commit message indicating the migration to Railway, and push the changes to the main branch. It's a standard Git workflow for saving project state before a major change. ```bash git add . git commit -m "Railway migration with full ruBert" git push origin main ``` -------------------------------- ### Setting Environment Variables in Railway Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md These commands show how to set essential environment variables for your Railway project, including API keys for Gemini AI and Telegram, as well as Supabase credentials. These variables are crucial for your application's functionality and database connections. ```bash # Основные API ключи railway variables set GEMINI_API_KEY=AIzaSyAZjlVV9jMJimDNqHqpyb0DQfSojIP_tw4 railway variables set TELEGRAM_API_ID=your_api_id railway variables set TELEGRAM_API_HASH=your_api_hash # Supabase (создайте проект на supabase.com) railway variables set SUPABASE_URL=your_supabase_url railway variables set SUPABASE_KEY=your_supabase_key ``` -------------------------------- ### Initialize DOM Elements and Event Listeners with JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index.html This script runs after the DOM is fully loaded. It initializes date input fields to the current date and a week ago, sets up an event listener for a channel analysis form submission, and configures a slider for AI threshold values. It also logs the visibility status of tab content elements. ```javascript document.addEventListener('DOMContentLoaded', function() { console.log('DOM Content Loaded - initializing...'); const today = new Date(); const lastWeek = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); const form = document.getElementById('channels-form'); if (form) { form.addEventListener('submit', function(e) { e.preventDefault(); analyzeChannels(e); }); } const channelsDateTo = document.getElementById('channels-date-to'); const channelsDateFrom = document.getElementById('channels-date-from'); if (channelsDateTo) { channelsDateTo.value = today.toISOString().split('T')[0]; console.log('Set channels-date-to:', channelsDateTo.value); } else { console.error('channels-date-to element not found!'); } if (channelsDateFrom) { channelsDateFrom.value = lastWeek.toISOString().split('T')[0]; console.log('Set channels-date-from:', channelsDateFrom.value); } else { console.error('channels-date-from element not found!'); } const mechanicalDateTo = document.getElementById('mechanical-date-to'); const mechanicalDateFrom = document.getElementById('mechanical-date-from'); const aiDateTo = document.getElementById('ai-date-to'); const aiDateFrom = document.getElementById('ai-date-from'); if (mechanicalDateTo) mechanicalDateTo.value = today.toISOString().split('T')[0]; if (mechanicalDateFrom) mechanicalDateFrom.value = lastWeek.toISOString().split('T')[0]; if (aiDateTo) aiDateTo.value = today.toISOString().split('T')[0]; if (aiDateFrom) aiDateFrom.value = lastWeek.toISOString().split('T')[0]; console.log('All date fields initialized'); const tabContents = document.querySelectorAll('.tab-content'); console.log('Found tab contents:', tabContents.length); tabContents.forEach((tab, index) => { console.log(`Tab ${index}:`, tab.id, 'visible:', tab.style.display !== 'none'); }); const firstTab = document.getElementById('channels'); if (firstTab) { firstTab.classList.add('active'); console.log('Activated channels tab'); } const thresholdSlider = document.getElementById('ai-threshold'); const thresholdValue = document.getElementById('threshold-value'); if (thresholdSlider && thresholdValue) { thresholdSlider.addEventListener('input', function() { thresholdValue.textContent = this.value; }); } const observer = new IntersectionObserver((entries)=>{ entries.forEach(e=>{ if(e.isInterse }); ``` -------------------------------- ### Interact with AI Chat API (Python) Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt This snippet demonstrates how to interact with the AI chat API using Python's requests library. It shows how to send chat messages, generate tasks, and discuss posts. The primary action is 'chat', with options for 'generate_task' and 'discuss_posts'. Dependencies include the 'requests' library. ```python import requests chat_history = [ {"role": "user", "content": "I want to find posts about AI in healthcare", "timestamp": "2024-01-15T10:00:00Z"} ] response = requests.post('http://localhost:8000/api/chat', json={ "messages": chat_history, "action": "chat" # Options: "chat", "generate_task", "discuss_posts" }) if response.json()["success"]: assistant_message = response.json()["message"] print(f"AI: {assistant_message}") # Add to history and continue conversation chat_history.append({ "role": "assistant", "content": assistant_message, "timestamp": "2024-01-15T10:00:05Z" }) # Generate search task from conversation task_response = requests.post('http://localhost:8000/api/chat', json={ "messages": chat_history, "action": "generate_task" }) if task_response.json()["success"]: search_task = task_response.json()["data"]["task"] print(f"\nGenerated search task:") print(f" Keywords: {search_task['keywords']}") print(f" Themes: {search_task['themes']}") print(f" Date range: {search_task['date_from']} - {search_task['date_to']}") # Discuss search results post_results = [...] # Posts from search discuss_response = requests.post('http://localhost:8000/api/chat', json={ "messages": chat_history, "action": "discuss_posts", "posts": post_results }) if discuss_response.json()["success"]: analysis = discuss_response.json()["message"] print(f"\nAI Analysis of posts:\n{analysis}") # Model fallback order: # 1. gemini-2.5-flash (fastest) # 2. gemini-2.5-pro # 3. gemini-1.5-pro # 4. gemini-1.5-flash (fallback) ``` -------------------------------- ### Interact with Gemini AI Chat Interface using Python Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt Provides an interface for interacting with a Gemini AI chat. This allows users to generate search tasks and discuss post results through a conversational AI. The Python code snippet demonstrates making requests to a local server endpoint for chat interactions. ```python import requests # Example of interacting with the Gemini AI chat interface # This would typically involve sending user messages and receiving AI responses. # For instance: # response = requests.post('http://localhost:8000/gemini-chat', json={'message': 'What are the latest AI trends?'}) # print(response.json()['reply']) ``` -------------------------------- ### Initiate AI Search in JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index — копия.html This JavaScript function, `searchPostsAI`, handles the AI-powered search for Telegram posts. It collects parameters like channels, keywords, custom prompts, and date ranges from the DOM. After validation, it constructs a request body and sends it to the '/search-posts' endpoint, managing the UI's loading state and error display. ```javascript async function searchPostsAI() { console.log('🧠 Запускаем ИИ-поиск...'); // Собираем данные формы const channelsList = document.getElementById('search-channels-list').value.trim(); const keywordsText = document.getElementById('ai-keywords').value.trim(); const hashtagsText = document.getElementById('ai-hashtags').value.trim(); const dateFrom = document.getElementById('ai-date-from').value; const dateTo = document.getElementById('ai-date-to').value; const customPrompt = document.getElementById('ai-custom-prompt').value.trim(); const aiThreshold = parseFloat(document.getElementById('ai-threshold').value); const limit = parseInt(document.getElementById('ai-limit').value) || 400; console.log('📝 Данные ИИ-поиска:', { channelsList, keywordsText, hashtagsText, dateFrom, dateTo, customPrompt, aiThreshold, limit }); // Валидация if (!channelsList || !customPrompt || !dateFrom || !dateTo) { showError('search-results', 'Для ИИ-поиска необходимо заполнить канал, промпт и даты'); return; } // Парсим данные const channels = channelsList.split('\n').map(s => s.trim()).filter(s => s); const keywords = keywordsText ? keywordsText.split(/[,\n]/).map(k => k.trim()).filter(k => k) : []; const hashtags = hashtagsText ? hashtagsText.split(/[,\s]/).map(h => h.trim()).filter(h => h) : []; const searchData = { channels: channels, keywords: keywords, hashtags: hashtags, date_from: dateFrom, date_to: dateTo, mode: 'ai', limit_per_channel: limit, themes: [], // Не используем предустановленные тематики ai_threshold: aiThreshold, prompt_type: 'custom', custom_prompt: customPrompt }; console.log('🚀 Отправляем ИИ-запрос:', searchData); showLoading('search-loading'); showMonitor('ai'); document.getElementById('search-results').innerHTML = ''; try { const response = await fetch('/search-posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(searchData) }); console.log('📡 ИИ ответ:', response.status, response.statusText); const data = await response.json(); hideLoading('search-loading'); if (data.error) { showError('search-results', data.error); finishMonitor('ai', false, data.error); return; } displaySearchResults(data); document.getElementById('export-search-btn').disabled = false; finishMonitor('ai', true, `Найдено ${data.results.length} постов`); // Сохраняем данные для экспорта window.__lastSearchKeywords = keywords; window.__lastSearchHashtags = hashtags; window.__lastSearchChannels = channelsList; } catch (error) { hideLoading('search-loading'); showError('search-results', `Ошибка ИИ-поиска: ${error.message}`); finishMonitor('ai', false, error.message); } } ``` -------------------------------- ### Download Excel File with JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index.html This function generates a downloadable Excel file from base64 encoded data. It dynamically creates an anchor element, sets its href and download attributes, simulates a click to trigger the download, and then cleans up the element. Error handling is included for download failures. ```javascript function downloadExcel(data) { if (!data || !data.data || !data.filename) { showError('search-results', 'Сервер вернул некорректные данные'); return; } try { const link = document.createElement('a'); link.href = `data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,${data.data}`; link.download = data.filename; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); showSuccess('search-results', `Excel файл "${data.filename}" успешно скачан! Найдено постов: ${data.posts_count || 0}`); } catch (downloadError) { showError('search-results', `Ошибка скачивания файла: ${downloadError.message}`); } } ``` -------------------------------- ### Create Particle Effects for a Container (JavaScript) Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index — копия.html Generates and appends a specified number of animated particle divs to a container element. It sets random positions, animation delays, and durations for each particle. Dependencies include a 'container' element in the DOM and CSS for '.particles' and '.particle' classes. ```javascript function createParticles() { if (document.querySelector('.particles')) return; const particlesContainer = document.createElement('div'); particlesContainer.className = 'particles'; container.appendChild(particlesContainer); for (let i = 0; i < 8; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.left = Math.random() * 100 + '%'; particle.style.top = Math.random() * 100 + '%'; particle.style.animationDelay = Math.random() * 6 + 's'; particle.style.animationDuration = (4 + Math.random() * 4) + 's'; particlesContainer.appendChild(particle); } } createParticles(); ``` -------------------------------- ### Making Migration Script Executable Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/RAILWAY_FULL_MIGRATION.md This command changes the permissions of the 'migrate_to_railway_full.sh' script, making it executable. This is necessary before you can run the script to initiate the migration process. ```bash chmod +x migrate_to_railway_full.sh ``` -------------------------------- ### Render Excel Sheet Preview - JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/tests/analytics_interface_neon.html Generates an HTML table to display a preview of the selected Excel sheet. It limits the number of rows and columns displayed for performance. The preview includes cell values, truncated to 180 characters. ```javascript function __renderSheetPreview() { const wb = __excelPreviewCtx.workbook; const sheetName = __excelPreviewCtx.sheetName; const ws = wb.Sheets[sheetName]; if (!ws) return; const range = XLSX.utils.decode_range(ws['!ref'] || 'A1:C20'); const maxRows = Math.min(8, range.e.r + 1); // Ограничиваем до 8 строк для предпросмотра const maxCols = Math.min(8, range.e.c + 1); let html = ''; for (let c = 0; c < maxCols; c++) { html += ``; } html += ''; for (let r = 0; r < maxRows; r++) { html += ``; for (let c = 0; c < maxCols; c++) { const cell = ws[XLSX.utils.encode_cell({ r, c })]; let v = cell?.w || cell?.v || ''; html += ``; } html += ''; } html += '
#${XLSX.utils.encode_col(c)}
${r + 1}${(v + '').toString().substring(0, 180)}
'; document.getElementById('excel-preview-table').innerHTML = html; document.getElementById('excel-links-list').innerHTML = ''; document.getElementById('excel-links-count').textContent = 'Найдено: 0'; } ``` -------------------------------- ### Stream Real-Time Search Logs via SSE using Python Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt Connects to a Server-Sent Events (SSE) stream to receive real-time search progress and logs. It decodes UTF-8 messages, parses JSON log entries, and prints them with their type and timestamp. The stream supports various log types including info, success, warning, error, progress, post analysis, and keyword generation, with specific handling for post analysis and keyword events. ```python import requests import json # Connect to SSE stream url = 'http://localhost:8000/search-logs-stream' response = requests.get(url, stream=True, headers={ 'Accept': 'text/event-stream', 'Cache-Control': 'no-cache' }) print("Connected to log stream...") for line in response.iter_lines(): if line: line = line.decode('utf-8') if line.startswith('data: '): data = line[6:] # Remove 'data: ' prefix log_entry = json.loads(data) if log_entry.get('type') == 'heartbeat': continue # Skip heartbeat messages # Log types: # - info: General information # - success: Successful operations # - warning: Non-critical issues # - error: Critical errors # - progress: Progress updates # - post_analysis: Individual post analysis # - keywords_generated: AI-generated keywords log_type = log_entry.get('level', 'info') message = log_entry.get('message', '') timestamp = log_entry.get('timestamp', '') print(f"[{timestamp}] [{log_type.upper()}] {message}") # Additional data for specific log types if log_type == 'post_analysis': print(f" Post ID: {log_entry.get('post_id')}") print(f" Similarity: {log_entry.get('similarity')}") elif log_type == 'keywords_generated': print(f" Keywords: {log_entry.get('keywords')}") # Stream features: # - Heartbeat every 30 seconds to keep connection alive # - Automatic reconnection on disconnect # - Last 10 logs sent immediately on connect # - CORS enabled for browser clients ``` -------------------------------- ### JavaScript AI Search Functionality and WebSocket Integration Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index.html This JavaScript code handles the initiation of an AI-powered search for Telegram posts. It prepares search data, including keywords, hashtags, and date ranges, and attempts to send the query via WebSocket if available. It includes a fallback mechanism using the fetch API for POST requests to '/search-posts'. Error handling and loading indicators are also managed. ```javascript const keywords = keywordsText ? keywordsText.split(/[,\n]/).map(k => k.trim()).filter(k => k) : []; const hashtags = hashtagsText ? hashtagsText.split(/[,\s]/).map(h => h.trim()).filter(h => h) : []; const searchData = { channels: channels, keywords: keywords, hashtags: hashtags, date_from: dateFrom, date_to: dateTo, mode: 'ai', limit_per_channel: limit, themes: [], // Не используем предустановленные тематики ai_threshold: aiThreshold, prompt_type: 'custom', custom_prompt: customPrompt }; console.log('🚀 Отправляем ИИ-запрос:', searchData); showLoading('search-loading'); showMonitor('ai'); document.getElementById('search-results').innerHTML = ''; try { // Используем WebSocket для ИИ-поиска if (ws && ws.readyState === WebSocket.OPEN) { console.log('📡 Отправляем ИИ-запрос через WebSocket...'); ws.send(JSON.stringify({ type: 'ai_search', query: customPrompt, channels: channelsList, keywords: keywordsText, hashtags: hashtagsText, date_from: dateFrom, date_to: dateTo, ai_threshold: aiThreshold, limit: limit })); } else { // Fallback на fetch если WebSocket недоступен const response = await fetch('/search-posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(searchData) }); console.log('📡 ИИ ответ:', response.status, response.statusText); const data = await response.json(); hideLoading('search-loading'); if (data.error) { showError('search-results', data.error); finishMonitor('ai', false, data.error); return; } // Проверяем что data.results существует if (!data.results) { showError('search-results', 'Сервер вернул некорректные данные: отсутствует results'); finishMonitor('ai', false, 'Некорректные данные от сервера'); return; } displaySearchResults(data); document.getElementById('export-search-btn').disabled = false; finishMonitor('ai', true, `Найдено ${data.results.length} постов`); // Сохраняем данные для экспорта window.__lastSearchKeywords = keywords; window.__lastSearchHashtags = hashtags; window.__lastSearchChannels = channelsList; } } catch (error) { hideLoading('search-loading'); showError('search-results', `Ошибка ИИ-поиска: ${error.message}`); finishMonitor('ai', false, error.message); } ``` -------------------------------- ### Initiate Mechanical Search in JavaScript Source: https://github.com/zikzak134/telegram-ai-chat/blob/main/backend/static/index — копия.html This JavaScript function, `searchPostsMechanical`, initiates a mechanical search for Telegram posts. It gathers search criteria such as keywords, hashtags, and channels from the DOM, then sends a POST request to the '/search-posts' endpoint. The function handles loading states, displays results, and manages potential errors. ```javascript async function searchPostsMechanical() { console.log('⚙️ Запускаем механический поиск...'); const channelsList = document.getElementById('search-channels-list').value.trim(); const keywordsText = document.getElementById('keywords').value.trim(); const hashtagsText = document.getElementById('hashtags').value.trim(); const dateFrom = document.getElementById('date-from').value; const dateTo = document.getElementById('date-to').value; const limit = parseInt(document.getElementById('limit').value) || 400; console.log('📝 Данные механического поиска:', { channelsList, keywordsText, hashtagsText, dateFrom, dateTo, limit }); if (!channelsList || !dateFrom || !dateTo) { showError('search-results', 'Для механического поиска необходимо заполнить канал и даты'); return; } const channels = channelsList.split('\n').map(s => s.trim()).filter(s => s); const keywords = keywordsText ? keywordsText.split(',').map(k => k.trim()).filter(k => k) : []; const hashtags = hashtagsText ? hashtagsText.split(',').map(h => h.trim()).filter(h => h) : []; const searchData = { channels: channels, keywords: keywords, hashtags: hashtags, date_from: dateFrom, date_to: dateTo, mode: 'mechanical', limit_per_channel: limit }; console.log('🚀 Отправляем механический запрос:', searchData); showLoading('search-loading'); showMonitor('mechanical'); document.getElementById('search-results').innerHTML = ''; try { const response = await fetch('/search-posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(searchData) }); console.log('📡 Механический ответ:', response.status, response.statusText); const data = await response.json(); hideLoading('search-loading'); if (data.error) { showError('search-results', data.error); finishMonitor('mechanical', false, data.error); return; } displaySearchResults(data); document.getElementById('export-search-btn').disabled = false; finishMonitor('mechanical', true, `Найдено ${data.results.length} постов`); // Сохраняем данные для экспорта window.__lastSearchKeywords = keywords; window.__lastSearchHashtags = hashtags; window.__lastSearchChannels = channelsList; } catch (error) { hideLoading('search-loading'); showError('search-results', `Ошибка механического поиска: ${error.message}`); finishMonitor('mechanical', false, error.message); } } ``` -------------------------------- ### Telegram Authentication - Phone/SMS Login Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt Initiates a two-factor authentication flow by sending a verification code to the user's phone number and then signing in with the received code and optional 2FA password. ```APIDOC ## Telegram Authentication - Phone/SMS Login ### Description Initiates two-factor authentication flow by sending verification code to user's phone number and signing in with the code. ### Method POST ### Endpoint /auth/send-code, /auth/sign-in ### Parameters #### Request Body for /auth/send-code - **phone** (string) - Required - The phone number of the user. - **force_sms** (boolean) - Optional - True to force SMS instead of Telegram app notification. #### Request Body for /auth/sign-in - **code** (string) - Required - The verification code received via SMS or Telegram. - **password** (string) - Optional - The 2FA password if enabled for the account. #### Headers for /auth/sign-in - **X-Session-Id** (string) - Required - A unique session identifier. ### Request Example ```json # Send verification code { "phone": "+1234567890", "force_sms": false } # Sign in with code { "code": "12345", "password": "" } # Retry with 2FA password { "code": "12345", "password": "my2fapassword" } ``` ### Response #### Success Response (200) - **status** (string) - Indicates the status of the operation (e.g., "code_sent", "authorized"). - **phone_code_hash** (string) - Returned after sending the code, used for subsequent sign-in. #### Error Response (e.g., 400) - **error** (string) - Description of the error (e.g., "PASSWORD_REQUIRED"). #### Response Example ```json { "status": "code_sent", "phone_code_hash": "abc123xyz789" } ``` ```json { "status": "authorized" } ``` ```json { "error": "PASSWORD_REQUIRED" } ``` ``` -------------------------------- ### Semantic Search with ruBERT Source: https://context7.com/zikzak134/telegram-ai-chat/llms.txt Performs semantic search on Telegram posts using the ruBERT model. It collects posts, generates embeddings, calculates cosine similarity, and classifies matches based on defined thresholds. Requires posts to be under 512 tokens and processes approximately 0.5s per post on CPU. ```python import requests response = requests.post('http://localhost:8000/search-posts', json={ "channels": ["https://t.me/techcrunch"], "keywords": [], # Not used in ruBERT mode "hashtags": ["#tech"], "date_from": "2024-01-01", "date_to": "2024-12-31", "mode": "rubert", "limit_per_channel": 100, "rubert_prompt": "Инновации в области искусственного интеллекта и машинного обучения", "threshold_direct": 0.3, # Direct match threshold "threshold_indirect": 0.2 # Indirect match threshold }) # ruBERT workflow: # 1. Collects all posts mechanically (without keyword filter) # 2. Generates embedding for rubert_prompt # 3. Generates embeddings for each post text # 4. Calculates cosine similarity # 5. Classifies as direct/indirect match based on thresholds for post in response.json()["results"]: print(f"\nPost: {post['link']}") print(f"Similarity: {post.get('rubert_similarity', 0):.3f}") print(f"Match type: {post.get('rubert_match_type', 'N/A')}") print(f"Topic: {post.get('rubert_topic', 'N/A')}") print(f"Views: {post['views']:,} | ERR: {post['err']:.2f}%") # Model details: # - Model: cointegrated/rubert-tiny2 (768-dim embeddings) # - Input: Max 512 tokens # - Processing: ~0.5s per post on CPU # - Cache: Models auto-cached to ~/.cache/huggingface ```