### 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 += `${XLSX.utils.encode_col(c)} | `;
}
html += '
';
for (let r = 0; r < maxRows; r++) {
html += `${r + 1} | `;
for (let c = 0; c < maxCols; c++) {
const cell = ws[XLSX.utils.encode_cell({ r, c })];
let v = cell?.w || cell?.v || '';
html += `${(v + '').toString().substring(0, 180)} | `;
}
html += '';
}
html += '
';
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
```