help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • Blog
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
news-summary-bot
RSK World
news-summary-bot
News Summary Bot - Python + Flask + OpenAI + NewsAPI + AI Summarization + Real-time News + News Aggregation
news-summary-bot
  • __pycache__
  • static
  • templates
  • .env459 B
  • .gitignore761 B
  • GITHUB_RELEASE_SUMMARY.md3.7 KB
  • INSTALLATION.md2.7 KB
  • PROJECT_SUMMARY.md9.5 KB
  • README.md11.4 KB
  • RELEASE_NOTES_v1.0.0.md6.7 KB
  • admin.py7.6 KB
  • analytics.py11 KB
  • app.py14.1 KB
  • auth.py15 KB
  • cache.py11.1 KB
  • export.py14.2 KB
  • news_bot.py4.7 KB
  • requirements.txt286 B
  • search.py16.8 KB
  • security.py14.4 KB
dataset_manifest.jsonRELEASE_NOTES.mdstyle.css.gitignorecache.py
static/css/style.css
Raw Download
Find: Go to:
/*
News Summary Bot - Custom Styles
Developer: Molla Samser
Design & Testing: Rima Khatun
Company: RSK World
Year: 2026
Website: https://rskworld.in
*/

:root {
    --primary: #dc3545;
    /* text-danger */
    --primary-dark: #b02a37;
    --secondary: #0d6efd;
    --dark: #1a1a1a;
    --light: #f8f9fa;
    --gray: #6c757d;
    --glass: rgba(255, 255, 255, 0.1);
    --glass-border: rgba(255, 255, 255, 0.2);
    --shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Outfit', sans-serif;
    background: #0f0f0f;
    color: var(--light);
    line-height: 1.6;
    overflow-x: hidden;
}

.glass-card {
    background: var(--glass);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--glass-border);
    border-radius: 15px;
    padding: 2rem;
    box-shadow: var(--shadow);
}

.btn-premium {
    background: linear-gradient(45deg, var(--primary), var(--secondary));
    border: none;
    color: white;
    padding: 0.8rem 2rem;
    border-radius: 50px;
    font-weight: 600;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
    text-decoration: none;
    display: inline-block;
    cursor: pointer;
}

.btn-premium:hover {
    transform: translateY(-3px);
    box-shadow: 0 10px 20px rgba(220, 53, 69, 0.4);
    color: white;
}

/* Navbar */
nav {
    padding: 1.5rem 5%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    position: fixed;
    width: 100%;
    top: 0;
    z-index: 1000;
    background: rgba(15, 15, 15, 0.8);
    backdrop-filter: blur(10px);
}

.logo {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--primary);
    text-transform: uppercase;
    letter-spacing: 2px;
}

/* Hero Section */
.hero {
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 0 10%;
    background: radial-gradient(circle at top right, rgba(220, 53, 69, 0.2), transparent),
        radial-gradient(circle at bottom left, rgba(13, 110, 253, 0.2), transparent);
}

.hero h1 {
    font-size: 4rem;
    margin-bottom: 1.5rem;
    background: linear-gradient(to right, #fff, #888);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.hero p {
    font-size: 1.2rem;
    max-width: 700px;
    margin-bottom: 2rem;
    color: var(--gray);
}

/* Dashboard Style */
.dashboard-container {
    margin-top: 100px;
    padding: 2rem 5%;
    display: grid;
    grid-template-columns: 250px 1fr;
    gap: 2rem;
    min-height: calc(100vh - 100px);
}

.sidebar {
    height: fit-content;
}

.category-list {
    list-style: none;
}

.category-item {
    padding: 1rem;
    margin-bottom: 0.5rem;
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.3s;
    display: flex;
    align-items: center;
    gap: 10px;
}

.category-item:hover,
.category-item.active {
    background: var(--glass);
    color: var(--primary);
}

.news-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 2rem;
}

.news-card {
    transition: transform 0.3s ease;
}

.news-card:hover {
    transform: translateY(-10px);
}

.news-image {
    width: 100%;
    height: 180px;
    object-fit: cover;
    border-radius: 10px;
    margin-bottom: 1rem;
}

.summary-modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.8);
    z-index: 2000;
    justify-content: center;
    align-items: center;
}

.modal-content {
    max-width: 600px;
    width: 90%;
}

.loader {
    width: 48px;
    height: 48px;
    border: 5px solid #FFF;
    border-bottom-color: var(--primary);
    border-radius: 50%;
    display: inline-block;
    box-sizing: border-box;
    animation: rotation 1s linear infinite;
}

@keyframes rotation {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

/* Sentiment Badges */
.badge-sentiment {
    padding: 0.3rem 0.8rem;
    border-radius: 20px;
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    display: inline-block;
    margin-bottom: 0.5rem;
}

.bg-positive {
    background: rgba(40, 167, 69, 0.2);
    color: #28a745;
    border: 1px solid #28a745;
}

.bg-negative {
    background: rgba(220, 53, 69, 0.2);
    color: #dc3545;
    border: 1px solid #dc3545;
}

.bg-neutral {
    background: rgba(108, 117, 125, 0.2);
    color: #6c757d;
    border: 1px solid #6c757d;
}

/* Trending Topics */
.trend-tag {
    display: inline-block;
    padding: 0.4rem 0.8rem;
    background: var(--glass);
    border: 1px solid var(--glass-border);
    border-radius: 15px;
    margin: 0.3rem;
    font-size: 0.85rem;
    cursor: pointer;
    transition: all 0.3s ease;
}

.trend-tag:hover {
    border-color: var(--primary);
    color: var(--primary);
    transform: scale(1.05);
}

.live-dot {
    height: 8px;
    width: 8px;
    background-color: #dc3545;
    border-radius: 50%;
    display: inline-block;
    margin-right: 5px;
    animation: pulse 1.5s infinite;
}

@keyframes pulse {
    0% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgba(220, 53, 69, 0.7);
    }

    70% {
        transform: scale(1);
        box-shadow: 0 0 0 5px rgba(220, 53, 69, 0);
    }

    100% {
        transform: scale(0.95);
        box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
    }
}

/* Pro Features Styles */
.reliability-indicator {
    height: 4px;
    width: 100%;
    background: #444;
    border-radius: 2px;
    margin: 10px 0;
    overflow: hidden;
}

.reliability-bar {
    height: 100%;
    background: linear-gradient(90deg, #dc3545, #ffc107, #28a745);
    transition: width 0.5s ease;
}

.bookmark-btn {
    position: absolute;
    top: 10px;
    right: 10px;
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: white;
    width: 35px;
    height: 35px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.3s;
    z-index: 5;
}

.bookmark-btn.active {
    background: var(--primary);
    border-color: var(--primary);
}

.voice-btn {
    background: var(--glass);
    border: 1px solid var(--glass-border);
    color: white;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 10px;
    cursor: pointer;
    transition: all 0.3s;
}

.voice-btn.listening {
    background: var(--primary);
    animation: pulse 1s infinite;
}

.trending-container {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
}

footer {
    padding: 3rem 5%;
    text-align: center;
    border-top: 1px solid var(--glass-border);
    margin-top: 4rem;
    background: #0a0a0a;
}

@media (max-width: 768px) {
    .dashboard-container {
        grid-template-columns: 1fr;
    }

    .hero h1 {
        font-size: 2.5rem;
    }
}
366 lines•7.2 KB
css
.gitignore
Raw Download
Find: Go to:
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Virtual Environment
venv/
env/
ENV/
.venv

# Environment variables
.env
.env.local
.env.*.local

# Database files
*.db
*.sqlite
*.sqlite3
users.db
news_analytics.db
cache.db
search_index.db
rate_limits.db

# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# Logs
*.log
logs/

# OS
Thumbs.db
.DS_Store

# Flask
instance/
.webassets-cache

# Testing
.pytest_cache/
.coverage
htmlcov/

# Temporary files
*.tmp
*.temp
*.bak

# Backup files
*.backup
backup/

# Export files (temporary)
exports/
downloads/
81 lines•761 B
text
cache.py
Raw Download
Find: Go to:
"""
Caching System for News Summary Bot
Developer: Molla Samser
Design & Testing: Rima Khatun
Company: RSK World
Year: 2026
Website: https://rskworld.in
"""

import time
import json
import hashlib
import sqlite3
from datetime import datetime, timedelta
from threading import Lock
import os

class CacheManager:
    def __init__(self, db_path='cache.db', default_ttl=300):
        self.db_path = db_path
        self.default_ttl = default_ttl
        self.lock = Lock()
        self.init_database()
    
    def init_database(self):
        """Initialize SQLite database for caching."""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS cache_entries (
                key TEXT PRIMARY KEY,
                value TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                expires_at TIMESTAMP,
                access_count INTEGER DEFAULT 0,
                last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS cache_stats (
                id INTEGER PRIMARY KEY,
                total_hits INTEGER DEFAULT 0,
                total_misses INTEGER DEFAULT 0,
                total_entries INTEGER DEFAULT 0,
                last_cleanup TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        # Initialize stats if not exists
        cursor.execute('SELECT COUNT(*) FROM cache_stats')
        if cursor.fetchone()[0] == 0:
            cursor.execute('INSERT INTO cache_stats (id) VALUES (1)')
        
        conn.commit()
        conn.close()
    
    def _generate_key(self, prefix, *args, **kwargs):
        """Generate a unique cache key."""
        key_data = f"{prefix}:{str(args)}:{str(sorted(kwargs.items()))}"
        return hashlib.md5(key_data.encode()).hexdigest()
    
    def get(self, key):
        """Get value from cache."""
        with self.lock:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('''
                SELECT value, expires_at FROM cache_entries 
                WHERE key = ? AND (expires_at IS NULL OR expires_at > ?)
            ''', (key, datetime.now()))
            
            result = cursor.fetchone()
            conn.close()
            
            if result:
                # Update access statistics
                self._update_access_stats(key, hit=True)
                return json.loads(result[0])
            else:
                self._update_access_stats(key, hit=False)
                return None
    
    def set(self, key, value, ttl=None):
        """Set value in cache with optional TTL."""
        if ttl is None:
            ttl = self.default_ttl
        
        expires_at = datetime.now() + timedelta(seconds=ttl) if ttl > 0 else None
        
        with self.lock:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('''
                INSERT OR REPLACE INTO cache_entries 
                (key, value, expires_at, created_at, last_accessed)
                VALUES (?, ?, ?, ?, ?)
            ''', (key, json.dumps(value), expires_at, datetime.now(), datetime.now()))
            
            conn.commit()
            conn.close()
    
    def delete(self, key):
        """Delete specific key from cache."""
        with self.lock:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('DELETE FROM cache_entries WHERE key = ?', (key,))
            
            conn.commit()
            conn.close()
    
    def clear(self):
        """Clear all cache entries."""
        with self.lock:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('DELETE FROM cache_entries')
            
            conn.commit()
            conn.close()
    
    def cleanup_expired(self):
        """Remove expired entries from cache."""
        with self.lock:
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()
            
            cursor.execute('''
                DELETE FROM cache_entries 
                WHERE expires_at IS NOT NULL AND expires_at <= ?
            ''', (datetime.now(),))
            
            # Update cleanup timestamp
            cursor.execute('''
                UPDATE cache_stats SET last_cleanup = ?
                WHERE id = 1
            ''', (datetime.now(),))
            
            conn.commit()
            conn.close()
    
    def get_stats(self):
        """Get cache statistics."""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # Get overall stats
        cursor.execute('''
            SELECT total_hits, total_misses, total_entries, last_cleanup
            FROM cache_stats WHERE id = 1
        ''')
        stats = cursor.fetchone()
        
        # Get current entry count
        cursor.execute('SELECT COUNT(*) FROM cache_entries')
        current_entries = cursor.fetchone()[0]
        
        # Get cache size
        cursor.execute('''
            SELECT SUM(LENGTH(value)) FROM cache_entries
        ''')
        cache_size = cursor.fetchone()[0] or 0
        
        conn.close()
        
        if stats:
            hit_rate = (stats[0] / (stats[0] + stats[1]) * 100) if (stats[0] + stats[1]) > 0 else 0
            
            return {
                'total_hits': stats[0],
                'total_misses': stats[1],
                'total_entries': stats[2],
                'current_entries': current_entries,
                'cache_size_bytes': cache_size,
                'cache_size_mb': round(cache_size / (1024 * 1024), 2),
                'hit_rate': round(hit_rate, 2),
                'last_cleanup': stats[3]
            }
        
        return None
    
    def _update_access_stats(self, key, hit=True):
        """Update access statistics."""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        if hit:
            # Update hit count and last accessed for the entry
            cursor.execute('''
                UPDATE cache_entries 
                SET access_count = access_count + 1, last_accessed = ?
                WHERE key = ?
            ''', (datetime.now(), key))
            
            # Update global hit count
            cursor.execute('''
                UPDATE cache_stats 
                SET total_hits = total_hits + 1
                WHERE id = 1
            ''')
        else:
            # Update global miss count
            cursor.execute('''
                UPDATE cache_stats 
                SET total_misses = total_misses + 1
                WHERE id = 1
            ''')
        
        # Update total entries count
        cursor.execute('SELECT COUNT(*) FROM cache_entries')
        current_count = cursor.fetchone()[0]
        cursor.execute('''
            UPDATE cache_stats 
            SET total_entries = ?
            WHERE id = 1
        ''', (current_count,))
        
        conn.commit()
        conn.close()

class NewsCache:
    """Specialized cache for news data."""
    
    def __init__(self, cache_manager):
        self.cache = cache_manager
    
    def get_news(self, category='general', query=None, country='us'):
        """Get cached news data."""
        key = self.cache._generate_key('news', category, query, country)
        return self.cache.get(key)
    
    def set_news(self, category='general', query=None, country='us', data=None, ttl=300):
        """Cache news data."""
        if data is None:
            return
        
        key = self.cache._generate_key('news', category, query, country)
        self.cache.set(key, data, ttl)
    
    def get_summary(self, content_hash, language='English'):
        """Get cached summary."""
        key = self.cache._generate_key('summary', content_hash, language)
        return self.cache.get(key)
    
    def set_summary(self, content_hash, language='English', summary=None, ttl=3600):
        """Cache summary."""
        if summary is None:
            return
        
        key = self.cache._generate_key('summary', content_hash, language)
        self.cache.set(key, summary, ttl)
    
    def get_sentiment(self, content_hash):
        """Get cached sentiment analysis."""
        key = self.cache._generate_key('sentiment', content_hash)
        return self.cache.get(key)
    
    def set_sentiment(self, content_hash, sentiment=None, ttl=3600):
        """Cache sentiment analysis."""
        if sentiment is None:
            return
        
        key = self.cache._generate_key('sentiment', content_hash)
        self.cache.set(key, sentiment, ttl)
    
    def get_trending_topics(self, days=7):
        """Get cached trending topics."""
        key = self.cache._generate_key('trending', days)
        return self.cache.get(key)
    
    def set_trending_topics(self, days=7, topics=None, ttl=1800):
        """Cache trending topics."""
        if topics is None:
            return
        
        key = self.cache._generate_key('trending', days)
        self.cache.set(key, topics, ttl)
    
    def invalidate_category(self, category):
        """Invalidate all cache entries for a specific category."""
        # This is a simplified approach - in production, you might want
        # more sophisticated invalidation strategies
        patterns = [
            f"news:{category}:",
            f"trending:",
        ]
        
        conn = sqlite3.connect(self.cache.db_path)
        cursor = conn.cursor()
        
        for pattern in patterns:
            cursor.execute('DELETE FROM cache_entries WHERE key LIKE ?', (f'%{pattern}%',))
        
        conn.commit()
        conn.close()

# Cache decorator for functions
def cached(ttl=300, key_prefix=None):
    """Decorator to cache function results."""
    def decorator(func):
        def wrapper(*args, **kwargs):
            cache_manager = getattr(wrapper, '_cache_manager', None)
            if not cache_manager:
                return func(*args, **kwargs)
            
            # Generate cache key
            prefix = key_prefix or func.__name__
            key = cache_manager._generate_key(prefix, *args, **kwargs)
            
            # Try to get from cache
            result = cache_manager.get(key)
            if result is not None:
                return result
            
            # Execute function and cache result
            result = func(*args, **kwargs)
            cache_manager.set(key, result, ttl)
            
            return result
        
        return wrapper
    return decorator

# Initialize global cache manager
cache_manager = CacheManager()
news_cache = NewsCache(cache_manager)

# Developer Details
# Created by Molla Samser (RSK World)
# 2026
337 lines•11.1 KB
python
🚀 Support RSK World

Subscribe to our YouTube channel for latest tutorials & updates!



Click subscribe & support our work ❤️

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer