/** * 🚀 API Integration āļŠāļģāļŦāļĢāļąāļšāđāļ”āļŠāļšāļ­āļĢāđŒāļ”āđāļ™āļ§āđ‚āļ™āđ‰āļĄāļāļēāļĢāđ€āļĄāļ·āļ­āļ‡āđ„āļ—āļĒ * āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļšāđāļŦāļĨāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļĢāļīāļ‡āļˆāļēāļāļŦāļ™āđˆāļ§āļĒāļ‡āļēāļ™āļĢāļēāļŠāļāļēāļĢāđāļĨāļ°āļŠāļ–āļēāļšāļąāļ™āļ•āđˆāļēāļ‡āđ† */ class PoliticalDataAPI { constructor() { this.baseURLs = { bot: 'https://api.bot.or.th/v1', nesdb: 'https://api.nesdc.go.th/v1', nso: 'https://api.nso.go.th/v1', ect: 'https://api.ect.go.th/v1' }; this.apiKeys = { bot: process.env.BOT_API_KEY, nesdb: process.env.NESDB_API_KEY, nso: process.env.NSO_API_KEY, ect: process.env.ECT_API_KEY }; this.cache = new Map(); this.cacheTimeout = 5 * 60 * 1000; // 5 āļ™āļēāļ—āļĩ } /** * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļĻāļĢāļĐāļāļāļīāļˆāļˆāļēāļāļ˜āļ™āļēāļ„āļēāļĢāđāļŦāđˆāļ‡āļ›āļĢāļ°āđ€āļ—āļĻāđ„āļ—āļĒ */ async getEconomicData(timeframe = '6months') { try { const cacheKey = `economic_${timeframe}`; if (this.isCacheValid(cacheKey)) { return this.cache.get(cacheKey); } const response = await fetch(`${this.baseURLs.bot}/economic-indicators`, { headers: { 'Authorization': `Bearer ${this.apiKeys.bot}`, 'Content-Type': 'application/json' }, params: { timeframe: timeframe, indicators: ['gdp', 'inflation', 'unemployment', 'consumer_confidence'] } }); if (!response.ok) { throw new Error(`BOT API Error: ${response.status}`); } const data = await response.json(); this.setCache(cacheKey, data); return data; } catch (error) { console.error('Error fetching economic data:', error); return this.getFallbackEconomicData(); } } /** * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāđ€āļĄāļ·āļ­āļ‡āļˆāļēāļāļŠāļģāļ™āļąāļāļ‡āļēāļ™āļ„āļ“āļ°āļāļĢāļĢāļĄāļāļēāļĢāļāļēāļĢāđ€āļĨāļ·āļ­āļāļ•āļąāđ‰āļ‡ */ async getPoliticalData(timeframe = '6months') { try { const cacheKey = `political_${timeframe}`; if (this.isCacheValid(cacheKey)) { return this.cache.get(cacheKey); } const response = await fetch(`${this.baseURLs.ect}/political-data`, { headers: { 'Authorization': `Bearer ${this.apiKeys.ect}`, 'Content-Type': 'application/json' }, params: { timeframe: timeframe, data_types: ['polls', 'elections', 'voter_registration'] } }); if (!response.ok) { throw new Error(`ECT API Error: ${response.status}`); } const data = await response.json(); this.setCache(cacheKey, data); return data; } catch (error) { console.error('Error fetching political data:', error); return this.getFallbackPoliticalData(); } } /** * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ›āļĢāļ°āļŠāļēāļāļĢāļˆāļēāļāļŠāļģāļ™āļąāļāļ‡āļēāļ™āļŠāļ–āļīāļ•āļīāđāļŦāđˆāļ‡āļŠāļēāļ•āļī */ async getDemographicData(region = 'all') { try { const cacheKey = `demographic_${region}`; if (this.isCacheValid(cacheKey)) { return this.cache.get(cacheKey); } const response = await fetch(`${this.baseURLs.nso}/demographics`, { headers: { 'Authorization': `Bearer ${this.apiKeys.nso}`, 'Content-Type': 'application/json' }, params: { region: region, indicators: ['population', 'age_distribution', 'education', 'income'] } }); if (!response.ok) { throw new Error(`NSO API Error: ${response.status}`); } const data = await response.json(); this.setCache(cacheKey, data); return data; } catch (error) { console.error('Error fetching demographic data:', error); return this.getFallbackDemographicData(); } } /** * āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļĄāļđāļĨ Social Media */ async getSocialMediaAnalysis(keywords = ['āļāļēāļĢāđ€āļĄāļ·āļ­āļ‡āđ„āļ—āļĒ', 'āļĢāļąāļāļšāļēāļĨ', 'āđ€āļĨāļ·āļ­āļāļ•āļąāđ‰āļ‡']) { try { // Twitter API const twitterData = await this.analyzeTwitter(keywords); // Facebook API const facebookData = await this.analyzeFacebook(keywords); // YouTube API const youtubeData = await this.analyzeYouTube(keywords); return { twitter: twitterData, facebook: facebookData, youtube: youtubeData, combined_sentiment: this.calculateCombinedSentiment([twitterData, facebookData, youtubeData]) }; } catch (error) { console.error('Error analyzing social media:', error); return this.getFallbackSocialMediaData(); } } /** * āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļĄāļđāļĨ Twitter */ async analyzeTwitter(keywords) { const response = await fetch('/api/twitter-analysis', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keywords: keywords, timeframe: '7days', language: 'th' }) }); return await response.json(); } /** * āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļĄāļđāļĨ Facebook */ async analyzeFacebook(keywords) { const response = await fetch('/api/facebook-analysis', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keywords: keywords, timeframe: '7days' }) }); return await response.json(); } /** * āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļĄāļđāļĨ YouTube */ async analyzeYouTube(keywords) { const response = await fetch('/api/youtube-analysis', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ keywords: keywords, timeframe: '7days', region: 'TH' }) }); return await response.json(); } /** * āļ„āļģāļ™āļ§āļ“ sentiment āļĢāļ§āļĄ */ calculateCombinedSentiment(dataSources) { const totalSentiment = dataSources.reduce((sum, source) => { return sum + (source.sentiment_score || 0); }, 0); return { score: totalSentiment / dataSources.length, label: this.getSentimentLabel(totalSentiment / dataSources.length), confidence: this.calculateConfidence(dataSources) }; } /** * āđāļ›āļĨāļ‡ sentiment score āđ€āļ›āđ‡āļ™ label */ getSentimentLabel(score) { if (score >= 0.6) return 'positive'; if (score <= 0.4) return 'negative'; return 'neutral'; } /** * āļ„āļģāļ™āļ§āļ“āļ„āļ§āļēāļĄāđ€āļŠāļ·āđˆāļ­āļĄāļąāđˆāļ™āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ */ calculateConfidence(dataSources) { const validSources = dataSources.filter(source => source.confidence); if (validSources.length === 0) return 0.5; const totalConfidence = validSources.reduce((sum, source) => { return sum + source.confidence; }, 0); return totalConfidence / validSources.length; } /** * āļĢāļ°āļšāļš Cache */ setCache(key, data) { this.cache.set(key, { data: data, timestamp: Date.now() }); } isCacheValid(key) { const cached = this.cache.get(key); if (!cached) return false; return (Date.now() - cached.timestamp) < this.cacheTimeout; } /** * āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļģāļĢāļ­āļ‡āđ€āļĄāļ·āđˆāļ­ API āđ„āļĄāđˆāļ—āļģāļ‡āļēāļ™ */ getFallbackEconomicData() { return { gdp_growth: 2.8, inflation_rate: 1.2, unemployment_rate: 1.1, consumer_confidence: 62.3, source: 'fallback_data', last_updated: new Date().toISOString() }; } getFallbackPoliticalData() { return { approval_rating: 62.3, party_support: { 'āļžāļĢāļĢāļ„āđ€āļžāļ·āđˆāļ­āđ„āļ—āļĒ': 30.1, 'āļžāļĢāļĢāļ„āļ āļđāļĄāļīāđƒāļˆāđ„āļ—āļĒ': 25.0, 'āļžāļĢāļĢāļ„āļāđ‰āļēāļ§āđ„āļāļĨ': 19.3, 'āļžāļĢāļĢāļ„āļ›āļĢāļ°āļŠāļēāļ˜āļīāļ›āļąāļ•āļĒāđŒ': 12.1 }, source: 'fallback_data', last_updated: new Date().toISOString() }; } getFallbackDemographicData() { return { total_population: 69799978, age_distribution: { '0-14': 16.2, '15-64': 67.8, '65+': 16.0 }, source: 'fallback_data', last_updated: new Date().toISOString() }; } getFallbackSocialMediaData() { return { twitter: { sentiment_score: 0.55, volume: 15000, trending_topics: ['āļāļēāļĢāđ€āļĄāļ·āļ­āļ‡āđ„āļ—āļĒ', 'āđ€āļĻāļĢāļĐāļāļāļīāļˆ', 'āļāļēāļĢāļĻāļķāļāļĐāļē'] }, facebook: { sentiment_score: 0.52, volume: 25000, engagement_rate: 0.08 }, youtube: { sentiment_score: 0.58, views: 500000, top_videos: [] }, combined_sentiment: { score: 0.55, label: 'neutral', confidence: 0.7 }, source: 'fallback_data', last_updated: new Date().toISOString() }; } } /** * āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāļ­āļąāļ›āđ€āļ”āļ•āđāļ”āļŠāļšāļ­āļĢāđŒāļ” */ class DashboardUpdater { constructor() { this.api = new PoliticalDataAPI(); this.charts = {}; this.updateInterval = 300000; // 5 āļ™āļēāļ—āļĩ } /** * āļ­āļąāļ›āđ€āļ”āļ•āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļąāđ‰āļ‡āļŦāļĄāļ” */ async updateAllData() { try { const [economicData, politicalData, demographicData, socialMediaData] = await Promise.all([ this.api.getEconomicData(), this.api.getPoliticalData(), this.api.getDemographicData(), this.api.getSocialMediaAnalysis() ]); this.updateEconomicCharts(economicData); this.updatePoliticalCharts(politicalData); this.updateDemographicCharts(demographicData); this.updateSocialMediaCharts(socialMediaData); this.updateStatistics(economicData, politicalData); this.updateLastUpdated(); this.showSuccessMessage('āļ­āļąāļ›āđ€āļ”āļ•āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļģāđ€āļĢāđ‡āļˆ'); } catch (error) { console.error('Error updating dashboard:', error); this.showErrorMessage('āđ€āļāļīāļ”āļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ”āđƒāļ™āļāļēāļĢāļ­āļąāļ›āđ€āļ”āļ•āļ‚āđ‰āļ­āļĄāļđāļĨ'); } } /** * āļ­āļąāļ›āđ€āļ”āļ•āļāļĢāļēāļŸāđ€āļĻāļĢāļĐāļāļāļīāļˆ */ updateEconomicCharts(data) { if (this.charts.economic) { this.charts.economic.data.datasets[0].data = [ data.gdp_growth, data.inflation_rate, data.unemployment_rate, data.consumer_confidence ]; this.charts.economic.update(); } } /** * āļ­āļąāļ›āđ€āļ”āļ•āļāļĢāļēāļŸāļāļēāļĢāđ€āļĄāļ·āļ­āļ‡ */ updatePoliticalCharts(data) { if (this.charts.political) { const partyData = Object.values(data.party_support); const partyLabels = Object.keys(data.party_support); this.charts.political.data.datasets[0].data = partyData; this.charts.political.data.labels = partyLabels; this.charts.political.update(); } } /** * āļ­āļąāļ›āđ€āļ”āļ•āļāļĢāļēāļŸāļ›āļĢāļ°āļŠāļēāļāļĢ */ updateDemographicCharts(data) { if (this.charts.demographic) { const ageData = Object.values(data.age_distribution); const ageLabels = Object.keys(data.age_distribution); this.charts.demographic.data.datasets[0].data = ageData; this.charts.demographic.data.labels = ageLabels; this.charts.demographic.update(); } } /** * āļ­āļąāļ›āđ€āļ”āļ•āļāļĢāļēāļŸ Social Media */ updateSocialMediaCharts(data) { if (this.charts.socialMedia) { const sentimentData = [ data.twitter.sentiment_score, data.facebook.sentiment_score, data.youtube.sentiment_score ]; this.charts.socialMedia.data.datasets[0].data = sentimentData; this.charts.socialMedia.update(); } } /** * āļ­āļąāļ›āđ€āļ”āļ•āļŠāļ–āļīāļ•āļīāļŦāļĨāļąāļ */ updateStatistics(economicData, politicalData) { // āļ­āļąāļ›āđ€āļ”āļ•āļ„āļ°āđāļ™āļ™āļ„āļ§āļēāļĄāđ€āļŠāļ·āđˆāļ­āļĄāļąāđˆāļ™āļĢāļąāļāļšāļēāļĨ const approvalElement = document.getElementById('approvalRating'); if (approvalElement) { approvalElement.textContent = politicalData.approval_rating.toFixed(1) + '%'; } // āļ­āļąāļ›āđ€āļ”āļ•āļāļēāļĢāđ€āļ•āļīāļšāđ‚āļ•āļ—āļēāļ‡āđ€āļĻāļĢāļĐāļāļāļīāļˆ const economicElement = document.getElementById('economicIndex'); if (economicElement) { economicElement.textContent = economicData.gdp_growth.toFixed(1) + '%'; } // āļ­āļąāļ›āđ€āļ”āļ•āļ”āļąāļŠāļ™āļĩāļ„āļ§āļēāļĄāļžāļ­āđƒāļˆāļ›āļĢāļ°āļŠāļēāļŠāļ™ const sentimentElement = document.getElementById('publicSentiment'); if (sentimentElement) { sentimentElement.textContent = economicData.consumer_confidence.toFixed(1) + '%'; } } /** * āļ­āļąāļ›āđ€āļ”āļ•āđ€āļ§āļĨāļēāļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ” */ updateLastUpdated() { const updateElement = document.querySelector('.update-info'); if (updateElement) { const now = new Date(); updateElement.innerHTML = ` āļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ”: ${now.toLocaleDateString('th-TH')} ${now.toLocaleTimeString('th-TH')} | āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļāļŦāļĨāļēāļĒāđāļŦāļĨāđˆāļ‡āļ—āļĩāđˆāđ€āļŠāļ·āđˆāļ­āļ–āļ·āļ­āđ„āļ”āđ‰ `; } } /** * āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļ„āļ§āļēāļĄāļŠāļģāđ€āļĢāđ‡āļˆ */ showSuccessMessage(message) { this.showNotification(message, 'success'); } /** * āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļ„āļ§āļēāļĄāļœāļīāļ”āļžāļĨāļēāļ” */ showErrorMessage(message) { this.showNotification(message, 'error'); } /** * āđāļŠāļ”āļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ */ showNotification(message, type) { const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); setTimeout(() => { notification.remove(); }, 3000); } /** * āđ€āļĢāļīāđˆāļĄāļāļēāļĢāļ­āļąāļ›āđ€āļ”āļ•āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī */ startAutoUpdate() { setInterval(() => { this.updateAllData(); }, this.updateInterval); } /** * āļŦāļĒāļļāļ”āļāļēāļĢāļ­āļąāļ›āđ€āļ”āļ•āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī */ stopAutoUpdate() { if (this.updateTimer) { clearInterval(this.updateTimer); } } } // āļŠāđˆāļ‡āļ­āļ­āļāļ„āļĨāļēāļŠāļŠāļģāļŦāļĢāļąāļšāđƒāļŠāđ‰āļ‡āļēāļ™ if (typeof module !== 'undefined' && module.exports) { module.exports = {PoliticalDataAPI, DashboardUpdater}; } else { window.PoliticalDataAPI = PoliticalDataAPI; window.DashboardUpdater = DashboardUpdater; }