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
/
static
/
js
RSK World
news-summary-bot
News Summary Bot - Python + Flask + OpenAI + NewsAPI + AI Summarization + Real-time News + News Aggregation
js
  • script.js17.9 KB
GITHUB_RELEASE_GUIDE.mdChemistryCalculatorViewController.swiftCalculatorWidget.swiftgames_metadata.jsonexport.pyfavicon.icoscript.js
export.py
Raw Download
Find: Go to:
"""
Data Export and Reporting Features
Developer: Molla Samser
Design & Testing: Rima Khatun
Company: RSK World
Year: 2026
Website: https://rskworld.in
"""

import csv
import json
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
from typing import Dict, List, Optional
import sqlite3
from io import StringIO, BytesIO
import zipfile
from analytics import NewsAnalytics
from auth import UserPreferences
from search import advanced_search

class DataExporter:
    def __init__(self):
        self.analytics = NewsAnalytics()
        self.user_prefs = UserPreferences()
        self.search = advanced_search
    
    def export_user_data(self, user_id: int, format_type: str = 'json') -> bytes:
        """Export all user data in specified format."""
        # Gather user data
        user_preferences = self.user_prefs.get_all_preferences(user_id)
        reading_history = self.user_prefs.get_reading_history(user_id, limit=1000)
        reading_stats = self.user_prefs.get_reading_stats(user_id, days=365)
        
        user_data = {
            'user_id': user_id,
            'export_date': datetime.now().isoformat(),
            'preferences': user_preferences,
            'reading_history': reading_history,
            'reading_stats': reading_stats
        }
        
        if format_type.lower() == 'json':
            return self._export_json(user_data)
        elif format_type.lower() == 'csv':
            return self._export_user_csv(user_data)
        elif format_type.lower() == 'xml':
            return self._export_xml(user_data, 'user_data')
        else:
            raise ValueError(f"Unsupported format: {format_type}")
    
    def export_analytics_data(self, days: int = 30, format_type: str = 'json') -> bytes:
        """Export analytics data for specified period."""
        # Gather analytics data
        sentiment_trends = self.analytics.get_sentiment_trends(days)
        category_analytics = self.analytics.get_category_analytics()
        trending_topics = self.analytics.analyze_trending_topics(days)
        search_analytics = self.search.get_search_analytics(days)
        
        analytics_data = {
            'export_date': datetime.now().isoformat(),
            'period_days': days,
            'sentiment_trends': sentiment_trends,
            'category_analytics': category_analytics,
            'trending_topics': trending_topics,
            'search_analytics': search_analytics
        }
        
        if format_type.lower() == 'json':
            return self._export_json(analytics_data)
        elif format_type.lower() == 'csv':
            return self._export_analytics_csv(analytics_data)
        elif format_type.lower() == 'xml':
            return self._export_xml(analytics_data, 'analytics_data')
        else:
            raise ValueError(f"Unsupported format: {format_type}")
    
    def export_news_articles(self, category: Optional[str] = None, 
                          date_from: Optional[str] = None, 
                          date_to: Optional[str] = None,
                          format_type: str = 'json') -> bytes:
        """Export news articles with filters."""
        # This would typically query the database
        # For now, we'll create a mock export structure
        
        articles_data = {
            'export_date': datetime.now().isoformat(),
            'filters': {
                'category': category,
                'date_from': date_from,
                'date_to': date_to
            },
            'articles': []  # Would contain actual article data
        }
        
        if format_type.lower() == 'json':
            return self._export_json(articles_data)
        elif format_type.lower() == 'csv':
            return self._export_articles_csv(articles_data)
        elif format_type.lower() == 'xml':
            return self._export_xml(articles_data, 'articles_data')
        else:
            raise ValueError(f"Unsupported format: {format_type}")
    
    def create_full_backup(self, include_user_data: bool = False) -> bytes:
        """Create a complete backup of all system data."""
        backup_data = {
            'backup_date': datetime.now().isoformat(),
            'version': '1.0',
            'analytics': json.loads(self.export_analytics_data(365, 'json').decode()),
            'search_index': [],  # Would include search index data
            'cache_stats': {},   # Would include cache statistics
        }
        
        if include_user_data:
            # This would include all user data (with proper privacy considerations)
            backup_data['users'] = []  # Would contain anonymized user data
        
        # Create ZIP file with multiple formats
        zip_buffer = BytesIO()
        
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
            # Add JSON backup
            json_data = self._export_json(backup_data)
            zip_file.writestr('backup.json', json_data)
            
            # Add CSV exports
            analytics_csv = self._export_analytics_csv(backup_data['analytics'])
            zip_file.writestr('analytics.csv', analytics_csv)
            
            # Add XML export
            analytics_xml = self._export_xml(backup_data['analytics'], 'analytics')
            zip_file.writestr('analytics.xml', analytics_xml)
            
            # Add metadata
            metadata = {
                'created_by': 'News Summary Bot',
                'developer': 'Molla Samser',
                'company': 'RSK World',
                'website': 'https://rskworld.in',
                'export_date': datetime.now().isoformat(),
                'file_count': 3,
                'formats': ['json', 'csv', 'xml']
            }
            zip_file.writestr('metadata.json', json.dumps(metadata, indent=2))
        
        zip_buffer.seek(0)
        return zip_buffer.getvalue()
    
    def _export_json(self, data: Dict) -> bytes:
        """Export data as JSON."""
        return json.dumps(data, indent=2, default=str).encode('utf-8')
    
    def _export_xml(self, data: Dict, root_name: str) -> bytes:
        """Export data as XML."""
        root = ET.Element(root_name)
        
        def dict_to_xml(parent, data):
            if isinstance(data, dict):
                for key, value in data.items():
                    child = ET.SubElement(parent, key)
                    dict_to_xml(child, value)
            elif isinstance(data, list):
                for item in data:
                    child = ET.SubElement(parent, 'item')
                    dict_to_xml(child, item)
            else:
                parent.text = str(data)
        
        dict_to_xml(root, data)
        
        xml_str = ET.tostring(root, encoding='unicode')
        return xml_str.encode('utf-8')
    
    def _export_user_csv(self, user_data: Dict) -> bytes:
        """Export user data as CSV."""
        output = StringIO()
        
        # Export reading history
        if user_data.get('reading_history'):
            writer = csv.writer(output)
            writer.writerow(['Article ID', 'Title', 'Category', 'Read At', 'Reading Time'])
            
            for item in user_data['reading_history']:
                writer.writerow([
                    item.get('article_id'),
                    item.get('article_title'),
                    item.get('category'),
                    item.get('read_at'),
                    item.get('reading_time')
                ])
        
        return output.getvalue().encode('utf-8')
    
    def _export_analytics_csv(self, analytics_data: Dict) -> bytes:
        """Export analytics data as CSV."""
        output = StringIO()
        writer = csv.writer(output)
        
        # Export sentiment trends
        if analytics_data.get('sentiment_trends'):
            writer.writerow(['Date', 'Positive', 'Negative', 'Neutral'])
            for date, sentiments in analytics_data['sentiment_trends'].items():
                writer.writerow([
                    date,
                    sentiments.get('Positive', 0),
                    sentiments.get('Negative', 0),
                    sentiments.get('Neutral', 0)
                ])
        
        return output.getvalue().encode('utf-8')
    
    def _export_articles_csv(self, articles_data: Dict) -> bytes:
        """Export articles data as CSV."""
        output = StringIO()
        writer = csv.writer(output)
        
        writer.writerow(['ID', 'Title', 'Category', 'Source', 'Published At', 'Sentiment', 'Reliability'])
        
        for article in articles_data.get('articles', []):
            writer.writerow([
                article.get('id'),
                article.get('title'),
                article.get('category'),
                article.get('source'),
                article.get('published_at'),
                article.get('sentiment'),
                article.get('reliability_score')
            ])
        
        return output.getvalue().encode('utf-8')

class ReportGenerator:
    """Generate various reports from system data."""
    
    def __init__(self):
        self.analytics = NewsAnalytics()
        self.search = advanced_search
    
    def generate_usage_report(self, days: int = 30) -> Dict:
        """Generate comprehensive usage report."""
        # Gather usage statistics
        sentiment_trends = self.analytics.get_sentiment_trends(days)
        category_analytics = self.analytics.get_category_analytics()
        search_analytics = self.search.get_search_analytics(days)
        
        # Calculate summary statistics
        total_searches = search_analytics.get('total_searches', 0)
        top_category = max(category_analytics.items(), key=lambda x: x[1].get('total_articles', 0)) if category_analytics else None
        
        report = {
            'report_type': 'usage_report',
            'period_days': days,
            'generated_at': datetime.now().isoformat(),
            'summary': {
                'total_searches': total_searches,
                'top_category': top_category[0] if top_category else None,
                'total_categories': len(category_analytics),
                'avg_daily_searches': total_searches / days if days > 0 else 0
            },
            'detailed_analytics': {
                'sentiment_trends': sentiment_trends,
                'category_analytics': category_analytics,
                'search_analytics': search_analytics
            },
            'insights': self._generate_insights(sentiment_trends, category_analytics, search_analytics)
        }
        
        return report
    
    def generate_performance_report(self) -> Dict:
        """Generate system performance report."""
        # This would typically include performance metrics
        report = {
            'report_type': 'performance_report',
            'generated_at': datetime.now().isoformat(),
            'metrics': {
                'cache_hit_rate': 85.5,  # Mock data
                'avg_response_time': 0.234,  # seconds
                'api_calls_today': 1250,
                'error_rate': 0.02,  # 2%
                'uptime_percentage': 99.9
            },
            'recommendations': [
                'Consider increasing cache size for better performance',
                'Monitor API rate limits during peak hours',
                'Regular database maintenance recommended'
            ]
        }
        
        return report
    
    def generate_user_engagement_report(self, days: int = 30) -> Dict:
        """Generate user engagement report."""
        # This would typically analyze user engagement patterns
        report = {
            'report_type': 'user_engagement_report',
            'period_days': days,
            'generated_at': datetime.now().isoformat(),
            'metrics': {
                'active_users': 342,
                'new_users': 28,
                'avg_session_duration': 5.5,  # minutes
                'articles_per_user': 12.3,
                'summary_requests': 890,
                'search_queries': 456
            },
            'engagement_trends': {
                'daily_active_users': [12, 15, 18, 14, 20, 22, 19],  # Last 7 days
                'peak_hours': [9, 10, 14, 15, 20, 21],  # Most active hours
                'popular_categories': ['technology', 'business', 'health']
            }
        }
        
        return report
    
    def _generate_insights(self, sentiment_data: Dict, category_data: Dict, search_data: Dict) -> List[str]:
        """Generate insights from analytics data."""
        insights = []
        
        # Sentiment insights
        if sentiment_data:
            recent_sentiments = list(sentiment_data.values())[-7:] if len(sentiment_data) >= 7 else list(sentiment_data.values())
            if recent_sentiments:
                avg_positive = sum(s.get('Positive', 0) for s in recent_sentiments) / len(recent_sentiments)
                avg_negative = sum(s.get('Negative', 0) for s in recent_sentiments) / len(recent_sentiments)
                
                if avg_positive > avg_negative:
                    insights.append("News sentiment has been predominantly positive recently")
                elif avg_negative > avg_positive:
                    insights.append("News sentiment has been predominantly negative recently")
        
        # Category insights
        if category_data:
            top_category = max(category_data.items(), key=lambda x: x[1].get('total_articles', 0))
            insights.append(f"Most popular category: {top_category[0]} with {top_category[1].get('total_articles', 0)} articles")
        
        # Search insights
        if search_data.get('top_queries'):
            top_query = search_data['top_queries'][0]
            insights.append(f"Most searched topic: '{top_query['query']}' with {top_query['count']} searches")
        
        return insights

# Initialize global exporter and report generator
data_exporter = DataExporter()
report_generator = ReportGenerator()

# Developer Details
# Created by Molla Samser (RSK World)
# 2026
352 lines•14.2 KB
python
static/js/script.js
Raw Download
Find: Go to:
/*
News Summary Bot - JavaScript Logic
Developer: Molla Samser
Design & Testing: Rima Khatun
Company: RSK World
Year: 2026
Website: https://rskworld.in
*/

let currentCategory = 'general';
let lastContent = ''; // For regeneration

document.addEventListener('DOMContentLoaded', () => {
    if (document.getElementById('news-grid')) {
        loadCategory('general');
        updateTrends();
    }
});

function loadCategory(category) {
    currentCategory = category;

    // Update UI
    document.querySelectorAll('.category-item').forEach(item => {
        item.classList.remove('active');
        if (item.getAttribute('onclick') && item.getAttribute('onclick').includes(category)) {
            item.classList.add('active');
        }
    });

    fetchNews(`/api/news?category=${category}`);
}

function searchNews() {
    const query = document.getElementById('newsQuery').value;
    if (query.trim()) {
        fetchNews(`/api/news?q=${encodeURIComponent(query)}`);
    }
}

async function fetchNews(url) {
    const grid = document.getElementById('news-grid');
    grid.innerHTML = `
        <div class="glass-card" style="grid-column: 1/-1; text-align: center;">
            <span class="loader"></span>
            <p>Fetching latest news headlines...</p>
        </div>
    `;

    try {
        const response = await fetch(url);
        const data = await response.json();

        if (data.error) {
            grid.innerHTML = `<div class="glass-card" style="grid-column: 1/-1; color: #ff4444;">Error: ${data.error}</div>`;
            return;
        }

        const articles = data.articles;
        if (!articles || articles.length === 0) {
            grid.innerHTML = `<div class="glass-card" style="grid-column: 1/-1;">No news articles found.</div>`;
            return;
        }

        grid.innerHTML = articles.map((article, index) => {
            const encodedContent = encodeURIComponent(article.description || article.title);
            return `
            <div class="news-card glass-card">
                <img src="${article.urlToImage || 'https://via.placeholder.com/300x200?text=News+Article'}" alt="News" class="news-image" onerror="this.src='https://via.placeholder.com/300x200?text=News+Article'">
                <div id="sentiment-${index}" class="badge-sentiment bg-neutral">Analyzing...</div>
                <h4>${article.title}</h4>
                <p style="font-size: 0.9rem; color: #888; margin: 0.5rem 0;">${article.source.name} • ${new Date(article.publishedAt).toLocaleDateString()}</p>
                <div style="margin-top: 1rem; display: flex; gap: 10px;">
                    <button onclick="showSummary('${index}', '${encodedContent}')" class="btn-premium" style="padding: 0.4rem 1rem; font-size: 0.8rem;">Summarize</button>
                    <a href="${article.url}" target="_blank" class="btn-premium" style="padding: 0.4rem 1rem; font-size: 0.8rem; background: var(--glass);">Read More</a>
                </div>
            </div>
            `;
        }).join('');

        // Trigger sentiment analysis for each card
        articles.forEach((article, index) => {
            updateSentiment(index, article.title);
        });

    } catch (err) {
        grid.innerHTML = `<div class="glass-card" style="grid-column: 1/-1; color: #ff4444;">Failed to fetch news. Please check your connection.</div>`;
    }
}

async function updateSentiment(index, text) {
    const badge = document.getElementById(`sentiment-${index}`);
    try {
        const response = await fetch('/api/analyze', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ content: text })
        });
        const data = await response.json();
        const sentiment = data.sentiment || 'Neutral';

        badge.innerText = sentiment;
        badge.className = `badge-sentiment bg-${sentiment.toLowerCase()}`;
    } catch {
        badge.innerText = 'Neutral';
    }
}

async function showSummary(index, content) {
    lastContent = decodeURIComponent(content);
    const modal = document.getElementById('summaryModal');
    const summaryText = document.getElementById('summary-text');

    modal.style.display = 'flex';
    requestSummary();
}

async function requestSummary() {
    const summaryText = document.getElementById('summary-text');
    const language = document.getElementById('summaryLanguage').value;
    summaryText.innerHTML = '<span class="loader"></span> Generating AI Summary...';

    try {
        const response = await fetch('/api/summarize', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ content: lastContent, language: language })
        });
        const data = await response.json();

        if (data.summary) {
            summaryText.innerText = data.summary;
        } else {
            summaryText.innerText = data.error || "Could not generate summary.";
        }
    } catch (err) {
        summaryText.innerText = "Error connecting to summarization service.";
    }
}

function regenerateSummary() {
    requestSummary();
}

function speakSummary() {
    const text = document.getElementById('summary-text').innerText;
    if ('speechSynthesis' in window) {
        // Cancel existing speech
        window.speechSynthesis.cancel();
        const utterance = new SpeechSynthesisUtterance(text);
        const language = document.getElementById('summaryLanguage').value;

        // Basic mapping for major languages
        const langMap = {
            'English': 'en-US',
            'Hindi': 'hi-IN',
            'Spanish': 'es-ES',
            'French': 'fr-FR',
            'German': 'de-DE'
        };
        utterance.lang = langMap[language] || 'en-US';
        window.speechSynthesis.speak(utterance);
    } else {
        alert("Sorry, your browser doesn't support text to speech!");
    }
}

async function updateTrends() {
    const container = document.getElementById('trending-topics');
    if (!container) return;
    
    try {
        const response = await fetch('/api/trending?days=7');
        const data = await response.json();
        
        if (data.trending && data.trending.length > 0) {
            const topics = data.trending.slice(0, 7).flatMap(t => t.keywords || []).slice(0, 7);
            container.innerHTML = topics.map(topic => 
                `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${topic}'; searchNews();">${topic}</span>`
            ).join('');
        } else {
            // Fallback to default trends
            const trends = ['AI Chatbots', 'Global Economy', 'SpaceX', 'Web3', 'Blockchain', 'Sustainability', 'Tech Jobs'];
            container.innerHTML = trends.map(t => 
                `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${t}'; searchNews();">${t}</span>`
            ).join('');
        }
    } catch (error) {
        console.error('Error fetching trends:', error);
        // Fallback to default trends
        const trends = ['AI Chatbots', 'Global Economy', 'SpaceX', 'Web3', 'Blockchain', 'Sustainability', 'Tech Jobs'];
        container.innerHTML = trends.map(t => 
            `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${t}'; searchNews();">${t}</span>`
        ).join('');
    }
}

function closeModal() {
    window.speechSynthesis.cancel();
    document.getElementById('summaryModal').style.display = 'none';
}

// Close modal when clicking outside
window.onclick = function (event) {
    const modal = document.getElementById('summaryModal');
    if (event.target == modal) {
        closeModal();
    }
}

// Voice Search Implementation
let recognition = null;
let isListening = false;

function initVoiceRecognition() {
    if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
        recognition = new SpeechRecognition();
        recognition.continuous = false;
        recognition.interimResults = false;
        recognition.lang = 'en-US';

        recognition.onresult = function(event) {
            const transcript = event.results[0][0].transcript;
            document.getElementById('newsQuery').value = transcript;
            searchNews();
            toggleVoiceSearch(); // Stop listening
        };

        recognition.onerror = function(event) {
            console.error('Speech recognition error:', event.error);
            alert('Voice recognition error. Please try again.');
            toggleVoiceSearch(); // Stop listening
        };

        recognition.onend = function() {
            if (isListening) {
                const btn = document.getElementById('voiceSearchBtn');
                if (btn) {
                    btn.classList.remove('listening');
                    isListening = false;
                }
            }
        };
    }
}

function toggleVoiceSearch() {
    if (!recognition) {
        initVoiceRecognition();
    }

    if (!recognition) {
        alert('Your browser does not support voice recognition. Please use Chrome or Edge.');
        return;
    }

    const btn = document.getElementById('voiceSearchBtn');
    
    if (isListening) {
        recognition.stop();
        btn.classList.remove('listening');
        isListening = false;
    } else {
        recognition.start();
        btn.classList.add('listening');
        isListening = true;
    }
}

// Initialize voice recognition on page load
document.addEventListener('DOMContentLoaded', () => {
    initVoiceRecognition();
});

// PDF Export Implementation
function exportSummaryPDF() {
    const title = document.getElementById('modal-title').innerText;
    const summary = document.getElementById('summary-text').innerText;
    const language = document.getElementById('summaryLanguage').value;
    
    if (!summary || summary.includes('Generating') || summary.includes('Error')) {
        alert('Please wait for the summary to be generated.');
        return;
    }

    try {
        const { jsPDF } = window.jspdf;
        const doc = new jsPDF();
        
        // Add title
        doc.setFontSize(18);
        doc.text(title, 20, 20);
        
        // Add language info
        doc.setFontSize(12);
        doc.text(`Language: ${language}`, 20, 30);
        doc.text(`Generated: ${new Date().toLocaleString()}`, 20, 37);
        
        // Add summary text with word wrap
        doc.setFontSize(11);
        const lines = doc.splitTextToSize(summary, 170);
        doc.text(lines, 20, 50);
        
        // Add footer
        const pageCount = doc.internal.getNumberOfPages();
        for (let i = 1; i <= pageCount; i++) {
            doc.setPage(i);
            doc.setFontSize(8);
            doc.text(`Page ${i} of ${pageCount} | RSK World News Bot`, 20, doc.internal.pageSize.height - 10);
        }
        
        // Save the PDF
        doc.save(`news-summary-${Date.now()}.pdf`);
    } catch (error) {
        console.error('PDF export error:', error);
        alert('Error generating PDF. Please make sure jsPDF library is loaded.');
    }
}

// Bookmarks/Favorites System
let bookmarks = JSON.parse(localStorage.getItem('newsBookmarks') || '[]');

function toggleBookmark(articleIndex, article) {
    const bookmarkKey = `bookmark_${article.url || article.title}`;
    const existingIndex = bookmarks.findIndex(b => b.url === article.url);
    
    if (existingIndex >= 0) {
        // Remove bookmark
        bookmarks.splice(existingIndex, 1);
        updateBookmarkButton(articleIndex, false);
    } else {
        // Add bookmark
        bookmarks.push({
            title: article.title,
            url: article.url,
            description: article.description,
            source: article.source?.name,
            publishedAt: article.publishedAt,
            urlToImage: article.urlToImage,
            savedAt: new Date().toISOString()
        });
        updateBookmarkButton(articleIndex, true);
    }
    
    localStorage.setItem('newsBookmarks', JSON.stringify(bookmarks));
}

function updateBookmarkButton(index, isBookmarked) {
    const card = document.querySelectorAll('.news-card')[index];
    if (card) {
        let btn = card.querySelector('.bookmark-btn');
        if (!btn) {
            // Create bookmark button if it doesn't exist
            const btnContainer = card.querySelector('div[style*="margin-top: 1rem"]');
            if (btnContainer) {
                btn = document.createElement('button');
                btn.className = 'bookmark-btn';
                btn.innerHTML = '<i class="fas fa-bookmark"></i>';
                btn.onclick = () => {
                    const articles = JSON.parse(sessionStorage.getItem('currentArticles') || '[]');
                    if (articles[index]) {
                        toggleBookmark(index, articles[index]);
                    }
                };
                btnContainer.insertBefore(btn, btnContainer.firstChild);
            }
        }
        if (btn) {
            btn.classList.toggle('active', isBookmarked);
        }
    }
}

function loadBookmarks() {
    const grid = document.getElementById('news-grid');
    if (bookmarks.length === 0) {
        grid.innerHTML = '<div class="glass-card" style="grid-column: 1/-1; text-align: center;">No bookmarks saved yet.</div>';
        return;
    }
    
    grid.innerHTML = bookmarks.map((bookmark, index) => {
        return `
        <div class="news-card glass-card">
            <img src="${bookmark.urlToImage || 'https://via.placeholder.com/300x200?text=News+Article'}" alt="News" class="news-image" onerror="this.src='https://via.placeholder.com/300x200?text=News+Article'">
            <div class="badge-sentiment bg-neutral">Bookmarked</div>
            <h4>${bookmark.title}</h4>
            <p style="font-size: 0.9rem; color: #888; margin: 0.5rem 0;">${bookmark.source || 'Unknown'} • ${new Date(bookmark.publishedAt).toLocaleDateString()}</p>
            <p style="font-size: 0.85rem; color: #aaa; margin: 0.5rem 0;">${bookmark.description || ''}</p>
            <div style="margin-top: 1rem; display: flex; gap: 10px;">
                <button onclick="toggleBookmark(${index}, ${JSON.stringify(bookmark).replace(/"/g, '&quot;')})" class="bookmark-btn active"><i class="fas fa-bookmark"></i></button>
                <button onclick="showSummary('${index}', '${encodeURIComponent(bookmark.description || bookmark.title)}')" class="btn-premium" style="padding: 0.4rem 1rem; font-size: 0.8rem;">Summarize</button>
                <a href="${bookmark.url}" target="_blank" class="btn-premium" style="padding: 0.4rem 1rem; font-size: 0.8rem; background: var(--glass);">Read More</a>
            </div>
        </div>
        `;
    }).join('');
}

// Update fetchNews to store articles and add bookmark buttons
const originalFetchNews = fetchNews;
fetchNews = async function(url) {
    const result = await originalFetchNews(url);
    
    // Store articles in sessionStorage for bookmark functionality
    try {
        const response = await fetch(url);
        const data = await response.json();
        if (data.articles) {
            sessionStorage.setItem('currentArticles', JSON.stringify(data.articles));
            
            // Add bookmark buttons to articles
            data.articles.forEach((article, index) => {
                const isBookmarked = bookmarks.some(b => b.url === article.url);
                setTimeout(() => updateBookmarkButton(index, isBookmarked), 100);
            });
        }
    } catch (e) {
        console.error('Error storing articles:', e);
    }
    
    return result;
};

// Update loadCategory to handle bookmarks
const originalLoadCategory = loadCategory;
loadCategory = function(category) {
    if (category === 'bookmarks') {
        loadBookmarks();
        // Update UI
        document.querySelectorAll('.category-item').forEach(item => {
            item.classList.remove('active');
            if (item.getAttribute('onclick') && item.getAttribute('onclick').includes('bookmarks')) {
                item.classList.add('active');
            }
        });
    } else {
        originalLoadCategory(category);
    }
};

// Update trending topics to use API
async function updateTrends() {
    const container = document.getElementById('trending-topics');
    if (!container) return;
    
    try {
        const response = await fetch('/api/trending?days=7');
        const data = await response.json();
        
        if (data.trending && data.trending.length > 0) {
            const topics = data.trending.slice(0, 7).flatMap(t => t.keywords || []);
            container.innerHTML = topics.map(topic => 
                `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${topic}'; searchNews();">${topic}</span>`
            ).join('');
        } else {
            // Fallback to default trends
            const trends = ['AI Chatbots', 'Global Economy', 'SpaceX', 'Web3', 'Blockchain', 'Sustainability', 'Tech Jobs'];
            container.innerHTML = trends.map(t => 
                `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${t}'; searchNews();">${t}</span>`
            ).join('');
        }
    } catch (error) {
        console.error('Error fetching trends:', error);
        // Fallback to default trends
        const trends = ['AI Chatbots', 'Global Economy', 'SpaceX', 'Web3', 'Blockchain', 'Sustainability', 'Tech Jobs'];
        container.innerHTML = trends.map(t => 
            `<span class="trend-tag" onclick="document.getElementById('newsQuery').value='${t}'; searchNews();">${t}</span>`
        ).join('');
    }
}

// Developed by Molla Samser | 2026 | RSK World
474 lines•17.9 KB
javascript
🚀 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