Riy777 commited on
Commit
e424c99
·
1 Parent(s): 10eef0b

Update sentiment_news.py

Browse files
Files changed (1) hide show
  1. sentiment_news.py +85 -32
sentiment_news.py CHANGED
@@ -1,4 +1,4 @@
1
- # sentiment_news.py (محدث بالكامل)
2
  import os, asyncio
3
  import httpx
4
  from gnews import GNews
@@ -35,6 +35,12 @@ class NewsFetcher:
35
  )
36
  # GNews مفلترة مسبقاً لـ 3 ساعات
37
  self.gnews = GNews(language='en', country='US', period='3h', max_results=8)
 
 
 
 
 
 
38
 
39
  async def _fetch_from_gnews(self, symbol: str) -> list:
40
  try:
@@ -64,38 +70,88 @@ class NewsFetcher:
64
  print(f"Failed to fetch specific news from GNews for {symbol}: {e}")
65
  return []
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  async def _fetch_from_rss_feed(self, feed_url: str, source_name: str, symbol: str) -> list:
68
  try:
69
  base_symbol = symbol.split('/')[0]
70
- max_redirects = 2
71
- current_url = feed_url
72
 
73
- # منطق معالجة إعادة التوجيه
74
- for attempt in range(max_redirects):
75
- try:
76
- response = await self.http_client.get(current_url)
77
- response.raise_for_status()
78
- break
79
- except httpx.HTTPStatusError as e:
80
- if e.response.status_code in [301, 302, 307, 308] and 'Location' in e.response.headers:
81
- current_url = e.response.headers['Location']
82
- continue
83
- else:
84
- raise
85
 
86
- feed = feedparser.parse(response.text)
87
  news_items = []
88
  search_term = base_symbol.lower()
89
 
90
- #
91
- # 🔴 تم التعديل: حساب الوقت قبل 3 ساعات (باستخدام التوقيت العالمي UTC)
92
- #
93
  three_hours_ago = datetime.now(timezone.utc) - timedelta(hours=3)
94
 
95
- #
96
- # 🔴 تم التعديل: إزالة قيد '[:15]' والفلترة حسب الوقت
97
- #
98
- for entry in feed.entries:
99
  title = entry.title.lower() if hasattr(entry, 'title') else ''
100
  summary = entry.summary.lower() if hasattr(entry, 'summary') else entry.description.lower() if hasattr(entry, 'description') else ''
101
 
@@ -111,9 +167,7 @@ class NewsFetcher:
111
  # في حال فشل التحويل، نفترض أنه قديم
112
  continue
113
 
114
- #
115
- # 🔴 تم التعديل: تطبيق الفلتر الزمني (آخر 3 ساعات) وفلتر اسم العملة
116
- #
117
  if (search_term in title or search_term in summary) and (published_time >= three_hours_ago):
118
  news_items.append({
119
  'title': entry.title,
@@ -124,6 +178,7 @@ class NewsFetcher:
124
 
125
  return news_items
126
  except Exception as e:
 
127
  print(f"Failed to fetch specific news from {source_name} RSS for {symbol}: {e}")
128
  return []
129
 
@@ -149,16 +204,12 @@ class NewsFetcher:
149
  description = item.get('description', 'No Description')
150
  source = item.get('source', 'Unknown Source')
151
 
152
- #
153
- # 🔴 تم التعديل: هذا المنطق سيعمل الآن بفضل التعديلات السابقة
154
- #
155
  published = item.get('published', '') # الحصول على التاريخ/الوقت
156
 
157
  news_entry = f"[{source}] {title}. {description}"
158
 
159
- #
160
- # 🔴 تم التعديل: إضافة وقت النشر إلى النص النهائي (تنفيذاً لطلبك)
161
- #
162
  if published:
163
  news_entry += f" (Published: {published})"
164
 
@@ -226,3 +277,5 @@ class SentimentAnalyzer:
226
  'market_trend': 'UNKNOWN',
227
  'data_quality': 'LOW'
228
  }
 
 
 
1
+ # sentiment_news.py (محدث V3.1 - إصلاح 429 بالتخزين المؤقت الذكي)
2
  import os, asyncio
3
  import httpx
4
  from gnews import GNews
 
35
  )
36
  # GNews مفلترة مسبقاً لـ 3 ساعات
37
  self.gnews = GNews(language='en', country='US', period='3h', max_results=8)
38
+
39
+ # 🔴 --- START OF CHANGE (V3.1 - Caching) --- 🔴
40
+ self.rss_cache = {}
41
+ self.cache_lock = asyncio.Lock()
42
+ self.cache_duration = timedelta(seconds=300) # (5 دقائق)
43
+ # 🔴 --- END OF CHANGE --- 🔴
44
 
45
  async def _fetch_from_gnews(self, symbol: str) -> list:
46
  try:
 
70
  print(f"Failed to fetch specific news from GNews for {symbol}: {e}")
71
  return []
72
 
73
+ # 🔴 --- START OF CHANGE (V3.1 - Caching Logic) --- 🔴
74
+ async def _get_cached_rss_feed(self, feed_url: str, source_name: str):
75
+ """
76
+ (جديد V3.1)
77
+ دالة مساعدة لجلب ملف RSS مع تخزين مؤقت (لمدة 5 دقائق).
78
+ هذا يمنع خطأ 429 عند طلب نفس المصدر لعدة عملات.
79
+ """
80
+ async with self.cache_lock:
81
+ current_time = datetime.now(timezone.utc)
82
+
83
+ # 1. التحقق من الذاكرة المؤقتة
84
+ if feed_url in self.rss_cache:
85
+ cached_data, cache_time = self.rss_cache[feed_url]
86
+ if (current_time - cache_time) < self.cache_duration:
87
+ # print(f" [NewsCache] Using cached data for {source_name}")
88
+ return cached_data # (إرجاع البيانات المخزنة)
89
+
90
+ # 2. (إذا لم يكن في الذاكرة أو كان قديماً) الجلب من المصدر
91
+ # print(f" [NewsCache] Fetching fresh data for {source_name}...")
92
+ max_redirects = 2
93
+ current_url = feed_url
94
+ response_text = None
95
+
96
+ try:
97
+ for attempt in range(max_redirects):
98
+ try:
99
+ response = await self.http_client.get(current_url)
100
+ response.raise_for_status()
101
+ response_text = response.text
102
+ break
103
+ except httpx.HTTPStatusError as e:
104
+ if e.response.status_code in [301, 302, 307, 308] and 'Location' in e.response.headers:
105
+ current_url = e.response.headers['Location']
106
+ continue
107
+ else:
108
+ # (التعامل مع 429 هنا أيضاً، لا تخزن الخطأ)
109
+ if e.response.status_code == 429:
110
+ print(f" ⚠️ [NewsCache] Rate limited (429) by {source_name}. Skipping for this cycle.")
111
+ # (سنخزن "فارغ" لمنع إعادة المحاولة الفورية)
112
+ self.rss_cache[feed_url] = ([], current_time)
113
+ return []
114
+ raise
115
+
116
+ if response_text is None:
117
+ raise ValueError("Failed to fetch RSS data after redirects")
118
+
119
+ feed = feedparser.parse(response_text)
120
+ entries = feed.entries
121
+
122
+ # 3. تخزين النتيجة الجديدة
123
+ self.rss_cache[feed_url] = (entries, current_time)
124
+ return entries
125
+
126
+ except Exception as e:
127
+ print(f" ❌ [NewsCache] Failed to fetch/parse {source_name}: {e}")
128
+ # (لا تخزن الخطأ، للسماح بإعادة المحاولة في الدورة القادمة)
129
+ if feed_url in self.rss_cache:
130
+ del self.rss_cache[feed_url]
131
+ return []
132
+ # 🔴 --- END OF CHANGE --- 🔴
133
+
134
+
135
  async def _fetch_from_rss_feed(self, feed_url: str, source_name: str, symbol: str) -> list:
136
  try:
137
  base_symbol = symbol.split('/')[0]
 
 
138
 
139
+ # 🔴 --- START OF CHANGE (V3.1 - Caching Logic) --- 🔴
140
+ # (الجلب من الذاكرة المؤقتة أو المصدر)
141
+ feed_entries = await self._get_cached_rss_feed(feed_url, source_name)
142
+ if not feed_entries:
143
+ return []
144
+ # 🔴 --- END OF CHANGE --- 🔴
 
 
 
 
 
 
145
 
 
146
  news_items = []
147
  search_term = base_symbol.lower()
148
 
149
+ # (حساب الوقت قبل 3 ساعات (باستخدام التوقيت العالمي UTC))
 
 
150
  three_hours_ago = datetime.now(timezone.utc) - timedelta(hours=3)
151
 
152
+ # (إزالة قيد '[:15]' والفلترة حسب الوقت)
153
+ # (نبحث الآن في 'feed_entries' بدلاً من 'feed.entries')
154
+ for entry in feed_entries:
 
155
  title = entry.title.lower() if hasattr(entry, 'title') else ''
156
  summary = entry.summary.lower() if hasattr(entry, 'summary') else entry.description.lower() if hasattr(entry, 'description') else ''
157
 
 
167
  # في حال فشل التحويل، نفترض أنه قديم
168
  continue
169
 
170
+ # (تطبيق الفلتر الزمني (آخر 3 ساعات) وفلتر اسم العملة)
 
 
171
  if (search_term in title or search_term in summary) and (published_time >= three_hours_ago):
172
  news_items.append({
173
  'title': entry.title,
 
178
 
179
  return news_items
180
  except Exception as e:
181
+ # (تم نقل أخطاء 429، لذا هذا للأخطاء غير المتوقعة فقط)
182
  print(f"Failed to fetch specific news from {source_name} RSS for {symbol}: {e}")
183
  return []
184
 
 
204
  description = item.get('description', 'No Description')
205
  source = item.get('source', 'Unknown Source')
206
 
207
+ # (هذا المنطق سيعمل الآن بفضل التعديلات السابقة)
 
 
208
  published = item.get('published', '') # الحصول على التاريخ/الوقت
209
 
210
  news_entry = f"[{source}] {title}. {description}"
211
 
212
+ # (إضافة وقت النشر إلى النص النهائي (تنفيذاً لطلبك))
 
 
213
  if published:
214
  news_entry += f" (Published: {published})"
215
 
 
277
  'market_trend': 'UNKNOWN',
278
  'data_quality': 'LOW'
279
  }
280
+
281
+ print("✅ Sentiment News loaded - V3.1 (Intelligent Caching)")