{{ costEstimate.detail }}
Generates visual charts and tabular data directly in the browser.
Override what the top "active channels" bar counts. E.g. heatmap = all sentiments, but spike = only negatives.
Positive → green/blue, Negative → red/pink, mixed/neutral → grey. Needs enriched posts (AI Sentiment column).
Min = light end (low post count), Max = dark end (high count). Picks intensity within each sentiment.
Combine with sentiment mode to spotlight e.g. "negative posts about Тимошенко".
Only save posts matching this pattern.
Format: Category Name | Regex
Select a database and click Generate Dashboard.
Total Posts
{{ statsData.total_posts.toLocaleString() }}
Total Views
{{ statsData.total_views.toLocaleString() }}
Channels
{{ statsData.channels.length }}
Posting Timeline
AI Sentiment
Regex Categories
AI Categories
Channel Performance & Sentiment
| Channel Name | Posts | Views | Pos | Neg | Neu |
|---|---|---|---|---|---|
| {{ c.label }} | {{ c.posts.toLocaleString() }} | {{ c.views.toLocaleString() }} | {{ c.Positive || 0 }} | {{ c.Negative || 0 }} | {{ c.Neutral || 0 }} |
Comparison: {{ compareResult.db_a }} vs {{ compareResult.db_b }}
DB A Rows
{{ compareResult.rows_a.toLocaleString() }}
DB B Rows
{{ compareResult.rows_b.toLocaleString() }}
New in B
+{{ compareResult.new_in_b.toLocaleString() }}
Sentiment Shift
{{ s.label }}
{{ s.delta > 0 ? '+' : '' }}{{ s.delta }}%
New Channels in B
{{ compareResult.new_channels.join(', ') }}
| # | {{ col }} |
|---|---|
| {{ dbViewerPage * dbViewerPageSize + rIdx + 1 }} |
{{ row[cIdx] }}
|
Select a database and click Load to view data.
Double-click any cell to edit it.
General
All schedule times (scrape, enrich, digest, etc.) use this timezone.
Google Gemini
Get a key from Google AI Studio
OpenRouter
FREE MODELSSome models have mandatory reasoning. This hides it from the response to save tokens.
Get a key from openrouter.ai/keys. Free models available!
OpenAI
Get a key from platform.openai.com
Telegram Sessions for Monitoring
Add multiple Telegram sessions to distribute channel scraping and avoid rate limits. Channels are split evenly across active sessions.
Monitoring Schedule
All times in UTCWhen to scrape all channels. E.g.: 02:00,06:00,10:00,14:00,18:00,22:00
When to AI-enrich new posts (sentiment, categories). Set ~30 min after scrape.
When to generate AI summaries for all topics. Set before digest times.
When to scan for coordinated information attacks.
Restart the app for schedule changes to take effect.
Global AI Models
Default models for all topics (can be overridden per-topic)Enrichment model is used for sentiment analysis and categorization. Summary model is used for AI summaries and deep analysis.
Data Retention
Older posts will be automatically deleted during scheduled cleanup.
Change My Password
Users
Admin cannot view other users' API keys or files — only manage accounts. Passwords are hashed; API keys are encrypted at rest.
A new user gets a clone of admin's non-secret settings and a copy of admin's uploaded CSV files. API keys are NOT copied.
HOW TO GET TELEGRAM CREDENTIALS
- Go to my.telegram.org and log in with your phone number.
- Click on 'API development tools'.
- Create a new application (you can enter any random App Title and Short Name).
- Copy the App api_id and App api_hash into the Setup tab.
- First time you run the scraper, a popup will ask for the Telegram code sent to your app.
SERVER BLOCKED? (STRING SESSION BYPASS)
If you never receive a code, your VPS IP is blocked by Telegram's anti-bot system. Bypass it using a local QR code login:
- Install Python on your local Windows/Mac computer.
- Run
pip install telethon qrcodein your terminal. - Run the Python script below on your PC. It will print a QR code in your terminal.
- Open Telegram on your phone → Settings → Devices → Link Desktop Device, and scan the QR.
- Click the code box below (it will auto-select all), copy the generated
1BJWap...string, and paste it into the Setup tab.
import asyncio
import qrcode
from telethon import TelegramClient
from telethon.sessions import StringSession
from telethon.errors import SessionPasswordNeededError
api_id = YOUR_API_ID # CHANGE THIS
api_hash = 'YOUR_API_HASH' # CHANGE THIS
async def main():
client = TelegramClient(StringSession(), api_id, api_hash)
await client.connect()
if not await client.is_user_authorized():
qr_login = await client.qr_login()
qr = qrcode.QRCode(border=1)
qr.add_data(qr_login.url)
qr.make(fit=True)
qr.print_ascii(invert=True)
try:
await qr_login.wait()
except SessionPasswordNeededError:
password = input("Please type your Telegram 2FA Password: ")
await client.sign_in(password=password)
print("\n--- YOUR SESSION STRING (COPY THIS) ---")
print(client.session.save())
if __name__ == "__main__":
asyncio.run(main())
REGEX (REGULAR EXPRESSIONS) TUTORIAL
Regex is a powerful way to search for specific words or patterns.
- Exact word match:
\bapple\b(Matches 'apple' but not 'apples') - Ignore Capitalization:
(?i)apple(Matches 'Apple', 'aPpLe', 'apple') - OR operator:
apple|banana|orange(Matches any of those words) - Find a hashtag:
#ukraine - Contains numbers:
\d+
Examples for the Categorizer box:
Politics | (?i)biden|trump|putin|zelensky Military | (?i)\b(tank|missile|drone)\b Economy | (?i)inflation|taxes|\$
AI ANALYSIS (GEMINI)
- Get a free API key from Google AI Studio: https://aistudio.google.com/app/apikey
- Because AI reads the text conceptually, you don't need exact words. Just describe what 'Positive' means to you, or describe your desired categories in plain English!
- Note: Free tier strictly limits processing to ~15 messages per minute. The scraper automatically throttles itself to respect this limit.
{{ t.name }}
{{ t.active_alerts }} alertsNo topics configured yet
Go to the Topics tab to create your first monitoring topic.
Posts by Topic ({{ dashPeriodLabel }})
Sentiment Overview ({{ dashPeriodLabel }})
Background Activity
{{ monDashboard.timezone }}Channel Analytics
Size = views. Green = positive, Red = negative, Gray = neutral. Top 40 channels.
| Channel | Category | Posts | Views | + | ~ | - |
|---|---|---|---|---|---|---|
| @{{ ch.username }}{{ ch.title?.slice(0,25) }} | {{ ch.category || '' }} | {{ ch.post_count }} | {{ (ch.total_views||0).toLocaleString() }} | {{ ch.positive || 0 }} | {{ ch.neutral || 0 }} | {{ ch.negative || 0 }} |
Leave all unchecked = process all channels
{{ selectedTopicDash.topic.name }} All Posts Keyword
{{ selectedTopicDash.topic.description }}
{{ selectedTopicDash.topic.regex_display || selectedTopicDash.topic.regex_pattern }}
AI Summary
Post Volume Over Time
Sentiment Over Time
AI Categories
Regex Categories
Topic Seismograph
SVGPosts ({{ filteredSortedPosts.length }} / {{ selectedTopicDash.recent_posts.length }})
| Date | Channel | Sent. | AI Cat. | Regex Cat. | Text | Views | AI | |
|---|---|---|---|---|---|---|---|---|
| Clear filters | ||||||||
| {{ p.posted_at?.slice(5,16) }} | {{ p.channel_username }} |
{{ p.text?.slice(0, 150) }}
|
{{ (p.views||0).toLocaleString() }} | |||||
Channel Analytics
| Channel | Posts | Views | + | - |
|---|---|---|---|---|
| @{{ ch.username }} | {{ ch.post_count }} | {{ (ch.total_views||0).toLocaleString() }} | {{ ch.positive || 0 }} | {{ ch.negative || 0 }} |
Coordinated Post Groups
Sentiment Spikes
Saved Reports
Select a topic from the left panel
Manage Categories & Types
| Channel | Category | Type | Subs | Posts | Sent. | Active | Actions | |
|---|---|---|---|---|---|---|---|---|
|
@{{ ch.username }}
{{ ch.title }}
|
{{ (ch.subscriber_count||0).toLocaleString() }} | {{ ch.total_posts || 0 }} | {{ ch.positive||0 }}/{{ ch.negative||0 }} — |
No channels added yet
Click Load Defaults to import 279 Ukrainian media channels, or add manually.
Coordinated Post Detection
Posts with ≥ {{ detectionRules.coord_similarity }}% text similarity across ≥ {{ detectionRules.coord_min_channels }} channels within {{ detectionRules.coord_time_window }}h are flagged as coordinated.
Spike Detection
Flags a negative spike when volume is ≥ {{ detectionRules.attack_volume_mult }}× the {{ detectionRules.attack_lookback_days }}-day baseline AND negativity exceeds baseline + {{ detectionRules.attack_std_devs }}σ with ≥ {{ detectionRules.attack_min_posts }} same-sentiment posts from ≥ {{ detectionRules.attack_min_channels }} channels. A positive spike fires on the same volume rule when positivity > 70%.
Sentiment Spikes
Coordinated Post Groups
No active alerts
Run detection to scan for sentiment spikes and coordinated posts.
Dismissed items and everything older than 7 days. Kept for reference — won't trigger digests or auto-alerts.
Archived Spikes ({{ archivedAttacks.length }})
Archived Coordinated Groups ({{ archivedGroups.length }})
Archive is empty
Dismissed or auto-archived items will appear here.
Comma-separated. Post matches if it contains at least one of these words.
Comma-separated. Post must contain every single word listed here.
Comma-separated. Posts with any of these words will be excluded.
Examples & Recipes
This bot receives notifications for all topics.
No bots configured
Click "Add Bot" to set up notifications.
Digest Archive
Setup Guide
- Open Telegram, search for @BotFather
- Send /newbot and follow the prompts
- Copy the bot token and paste it into a bot card above
- For DMs: start a chat with the bot, send /start. Get your Chat ID from @userinfobot
- For groups: add the bot to the group as admin. Chat ID is the group ID (negative number)
- For forum topics: also enter the Thread ID (from the ?thread= URL param)
- You can use the same bot token for multiple destinations — just create multiple cards with different Chat IDs
{{ viewingPost.text }}
Edit Topic
Edit to define what "positive" means for this topic
Edit to define what "negative" means for this topic
{{ prompt.title }}
{{ prompt.desc }}