Riy777 commited on
Commit
662698c
·
1 Parent(s): 781de6c

Update sentiment_news.py

Browse files
Files changed (1) hide show
  1. sentiment_news.py +86 -8
sentiment_news.py CHANGED
@@ -2,14 +2,23 @@ import os, asyncio
2
  import httpx
3
  from gnews import GNews
4
  import feedparser
5
- from datetime import datetime
 
6
 
 
 
 
7
  CRYPTO_RSS_FEEDS = {
8
  "Cointelegraph": "https://cointelegraph.com/rss",
9
  "CoinDesk": "https://www.coindesk.com/arc/outboundfeeds/rss/",
10
  "CryptoSlate": "https://cryptoslate.com/feed/",
11
  "NewsBTC": "https://www.newsbtc.com/feed/",
12
- "Bitcoin.com": "https://news.bitcoin.com/feed/"
 
 
 
 
 
13
  }
14
 
15
  class NewsFetcher:
@@ -23,14 +32,33 @@ class NewsFetcher:
23
  'Cache-Control': 'no-cache'
24
  }
25
  )
 
26
  self.gnews = GNews(language='en', country='US', period='3h', max_results=8)
27
 
28
  async def _fetch_from_gnews(self, symbol: str) -> list:
29
  try:
30
  base_symbol = symbol.split("/")[0]
 
31
  query = f'"{base_symbol}" cryptocurrency -bitcoin -ethereum -BTC -ETH'
 
 
32
  news_items = await asyncio.to_thread(self.gnews.get_news, query)
33
- return news_items
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  except Exception as e:
35
  print(f"Failed to fetch specific news from GNews for {symbol}: {e}")
36
  return []
@@ -40,6 +68,8 @@ class NewsFetcher:
40
  base_symbol = symbol.split('/')[0]
41
  max_redirects = 2
42
  current_url = feed_url
 
 
43
  for attempt in range(max_redirects):
44
  try:
45
  response = await self.http_client.get(current_url)
@@ -56,16 +86,41 @@ class NewsFetcher:
56
  news_items = []
57
  search_term = base_symbol.lower()
58
 
59
- for entry in feed.entries[:15]:
 
 
 
 
 
 
 
 
60
  title = entry.title.lower() if hasattr(entry, 'title') else ''
61
  summary = entry.summary.lower() if hasattr(entry, 'summary') else entry.description.lower() if hasattr(entry, 'description') else ''
62
- if search_term in title or search_term in summary:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  news_items.append({
64
  'title': entry.title,
65
  'description': summary,
66
  'source': source_name,
67
- 'published': entry.get('published', '')
68
  })
 
69
  return news_items
70
  except Exception as e:
71
  print(f"Failed to fetch specific news from {source_name} RSS for {symbol}: {e}")
@@ -73,6 +128,8 @@ class NewsFetcher:
73
 
74
  async def get_news_for_symbol(self, symbol: str) -> str:
75
  base_symbol = symbol.split("/")[0]
 
 
76
  tasks = [self._fetch_from_gnews(symbol)]
77
  for name, url in CRYPTO_RSS_FEEDS.items():
78
  tasks.append(self._fetch_from_rss_feed(url, name, symbol))
@@ -83,14 +140,24 @@ class NewsFetcher:
83
  for result in results:
84
  if isinstance(result, Exception):
85
  continue
 
86
  for item in result:
 
87
  if self._is_directly_relevant_to_symbol(item, base_symbol):
88
  title = item.get('title', 'No Title')
89
  description = item.get('description', 'No Description')
90
  source = item.get('source', 'Unknown Source')
91
- published = item.get('published', '')
 
 
 
 
92
 
93
  news_entry = f"[{source}] {title}. {description}"
 
 
 
 
94
  if published:
95
  news_entry += f" (Published: {published})"
96
 
@@ -99,25 +166,36 @@ class NewsFetcher:
99
  if not all_news_text:
100
  return f"No specific news found for {base_symbol} in the last 3 hours."
101
 
 
102
  important_news = all_news_text[:5]
103
  return " | ".join(important_news)
104
 
105
  def _is_directly_relevant_to_symbol(self, news_item, base_symbol):
 
 
 
 
106
  title = news_item.get('title', '').lower()
107
  description = news_item.get('description', '').lower()
108
  symbol_lower = base_symbol.lower()
109
 
 
110
  if symbol_lower not in title and symbol_lower not in description:
111
  return False
112
 
 
113
  crypto_keywords = [
114
  'crypto', 'cryptocurrency', 'token', 'blockchain',
115
  'price', 'market', 'trading', 'exchange', 'defi',
116
- 'coin', 'digital currency', 'altcoin'
 
117
  ]
118
 
119
  return any(keyword in title or keyword in description for keyword in crypto_keywords)
120
 
 
 
 
121
  class SentimentAnalyzer:
122
  def __init__(self, data_manager):
123
  self.data_manager = data_manager
 
2
  import httpx
3
  from gnews import GNews
4
  import feedparser
5
+ from datetime import datetime, timedelta, timezone
6
+ import time
7
 
8
+ #
9
+ # 🔴 تم التعديل: توسيع قائمة مصادر RSS لتشمل تغطية أوسع للعملات البديلة
10
+ #
11
  CRYPTO_RSS_FEEDS = {
12
  "Cointelegraph": "https://cointelegraph.com/rss",
13
  "CoinDesk": "https://www.coindesk.com/arc/outboundfeeds/rss/",
14
  "CryptoSlate": "https://cryptoslate.com/feed/",
15
  "NewsBTC": "https://www.newsbtc.com/feed/",
16
+ "Bitcoin.com": "https://news.bitcoin.com/feed/",
17
+ "The Block": "https://www.theblock.co/rss.xml",
18
+ "Decrypt": "https://decrypt.co/feed",
19
+ "AMBCrypto": "https://ambcrypto.com/feed/",
20
+ "CryptoPotato": "https://cryptopotato.com/feed/",
21
+ "U.Today": "https://u.today/rss"
22
  }
23
 
24
  class NewsFetcher:
 
32
  'Cache-Control': 'no-cache'
33
  }
34
  )
35
+ # GNews مفلترة مسبقاً لـ 3 ساعات
36
  self.gnews = GNews(language='en', country='US', period='3h', max_results=8)
37
 
38
  async def _fetch_from_gnews(self, symbol: str) -> list:
39
  try:
40
  base_symbol = symbol.split("/")[0]
41
+ # نستخدم الكلمات المفتاحية السلبية لمحاولة عزل أخبار العملة نفسها
42
  query = f'"{base_symbol}" cryptocurrency -bitcoin -ethereum -BTC -ETH'
43
+
44
+ # GNews تعمل بشكل متزامن، لذا نستخدم to_thread
45
  news_items = await asyncio.to_thread(self.gnews.get_news, query)
46
+
47
+ #
48
+ # 🔴 تم التعديل: توحيد تنسيق الإخراج ليتضمن مفتاح 'published'
49
+ #
50
+ formatted_items = []
51
+ for item in news_items:
52
+ # GNews توفر تاريخ النشر كنص (وهو مفلتر مسبقاً لـ 3 ساعات)
53
+ published_text = item.get('published date', 'Recent')
54
+ formatted_items.append({
55
+ 'title': item.get('title', 'No Title'),
56
+ 'description': item.get('description', 'No Description'),
57
+ 'source': item.get('source', {}).get('title', 'GNews'),
58
+ 'published': published_text # تمرير التاريخ/الوقت
59
+ })
60
+ return formatted_items
61
+
62
  except Exception as e:
63
  print(f"Failed to fetch specific news from GNews for {symbol}: {e}")
64
  return []
 
68
  base_symbol = symbol.split('/')[0]
69
  max_redirects = 2
70
  current_url = feed_url
71
+
72
+ # منطق معالجة إعادة التوجيه
73
  for attempt in range(max_redirects):
74
  try:
75
  response = await self.http_client.get(current_url)
 
86
  news_items = []
87
  search_term = base_symbol.lower()
88
 
89
+ #
90
+ # 🔴 تم التعديل: حساب الوقت قبل 3 ساعات (باستخدام التوقيت العالمي UTC)
91
+ #
92
+ three_hours_ago = datetime.now(timezone.utc) - timedelta(hours=3)
93
+
94
+ #
95
+ # 🔴 تم التعديل: إزالة قيد '[:15]' والفلترة حسب الوقت
96
+ #
97
+ for entry in feed.entries:
98
  title = entry.title.lower() if hasattr(entry, 'title') else ''
99
  summary = entry.summary.lower() if hasattr(entry, 'summary') else entry.description.lower() if hasattr(entry, 'description') else ''
100
+
101
+ # التحقق من تاريخ النشر
102
+ published_tuple = entry.get('published_parsed')
103
+ if not published_tuple:
104
+ continue # تخطي الخبر إذا لم يكن له تاريخ نشر
105
+
106
+ # تحويل تاريخ النشر إلى كائن datetime مع دعم التوقيت العالمي (UTC)
107
+ try:
108
+ published_time = datetime.fromtimestamp(time.mktime(published_tuple), timezone.utc)
109
+ except Exception:
110
+ # في حال فشل التحويل، نفترض أنه قديم
111
+ continue
112
+
113
+ #
114
+ # 🔴 تم التعديل: تطبيق الفلتر الزمني (آخر 3 ساعات) وفلتر اسم العملة
115
+ #
116
+ if (search_term in title or search_term in summary) and (published_time >= three_hours_ago):
117
  news_items.append({
118
  'title': entry.title,
119
  'description': summary,
120
  'source': source_name,
121
+ 'published': published_time.isoformat() # إرسال التاريخ بتنسيق ISO
122
  })
123
+
124
  return news_items
125
  except Exception as e:
126
  print(f"Failed to fetch specific news from {source_name} RSS for {symbol}: {e}")
 
128
 
129
  async def get_news_for_symbol(self, symbol: str) -> str:
130
  base_symbol = symbol.split("/")[0]
131
+
132
+ # إنشاء قائمة المهام (GNews + جميع مصادر RSS)
133
  tasks = [self._fetch_from_gnews(symbol)]
134
  for name, url in CRYPTO_RSS_FEEDS.items():
135
  tasks.append(self._fetch_from_rss_feed(url, name, symbol))
 
140
  for result in results:
141
  if isinstance(result, Exception):
142
  continue
143
+
144
  for item in result:
145
+ # نستخدم الفلتر الثانوي للتأكد من أن الخبر يركز فعلاً على العملة
146
  if self._is_directly_relevant_to_symbol(item, base_symbol):
147
  title = item.get('title', 'No Title')
148
  description = item.get('description', 'No Description')
149
  source = item.get('source', 'Unknown Source')
150
+
151
+ #
152
+ # 🔴 تم التعديل: هذا المنطق سيعمل الآن بفضل التعديلات السابقة
153
+ #
154
+ published = item.get('published', '') # الحصول على التاريخ/الوقت
155
 
156
  news_entry = f"[{source}] {title}. {description}"
157
+
158
+ #
159
+ # 🔴 تم التعديل: إضافة وقت النشر إلى النص النهائي (تنفيذاً لطلبك)
160
+ #
161
  if published:
162
  news_entry += f" (Published: {published})"
163
 
 
166
  if not all_news_text:
167
  return f"No specific news found for {base_symbol} in the last 3 hours."
168
 
169
+ # أخذ أهم 5 أخبار (الأكثر حداثة أو صلة)
170
  important_news = all_news_text[:5]
171
  return " | ".join(important_news)
172
 
173
  def _is_directly_relevant_to_symbol(self, news_item, base_symbol):
174
+ """
175
+ فلتر ثانوي للتأكد من أن الخبر ليس مجرد ذكر عابر للعملة،
176
+ بل يتعلق فعلاً بجوانب التداول أو السوق.
177
+ """
178
  title = news_item.get('title', '').lower()
179
  description = news_item.get('description', '').lower()
180
  symbol_lower = base_symbol.lower()
181
 
182
+ # يجب أن يكون الرمز موجوداً في العنوان أو الوصف
183
  if symbol_lower not in title and symbol_lower not in description:
184
  return False
185
 
186
+ # يجب أن يحتوي الخبر على كلمات مفتاحية تدل على أنه خبر مالي/تداولي
187
  crypto_keywords = [
188
  'crypto', 'cryptocurrency', 'token', 'blockchain',
189
  'price', 'market', 'trading', 'exchange', 'defi',
190
+ 'coin', 'digital currency', 'altcoin', 'airdrop', 'listing',
191
+ 'partnership', 'update', 'mainnet', 'protocol'
192
  ]
193
 
194
  return any(keyword in title or keyword in description for keyword in crypto_keywords)
195
 
196
+ # --- (باقي الملف: SentimentAnalyzer و format_whale_analysis) ---
197
+ # --- (لم يتم المساس بهذه الأجزاء لأنها خارج نطاق الطلب) ---
198
+
199
  class SentimentAnalyzer:
200
  def __init__(self, data_manager):
201
  self.data_manager = data_manager