Spaces:
Configuration error

Jiangxz commited on
Commit
2e274ff
·
verified ·
1 Parent(s): 263877a

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +576 -0
  2. requirements.txt +7 -0
app.py ADDED
@@ -0,0 +1,576 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ # 財政部財政資訊中心 江信宗
3
+
4
+ import gradio as gr
5
+ from openai import OpenAI
6
+ import requests
7
+ from bs4 import BeautifulSoup
8
+ import resend
9
+ import random
10
+ import lxml
11
+ import os
12
+ import time
13
+ import pytz
14
+ import datetime
15
+
16
+ headers = {
17
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
18
+ 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"',
19
+ 'sec-ch-ua-mobile': '?0',
20
+ 'sec-ch-ua-platform': '"Windows"',
21
+ 'sec-fetch-dest': 'document',
22
+ 'sec-fetch-mode': 'navigate',
23
+ 'sec-fetch-site': 'none',
24
+ 'Sec-Fetch-User': '?1',
25
+ 'sec-gpc': '1',
26
+ 'Cache-Control': 'no-cache',
27
+ 'Pragma': 'no-cache',
28
+ 'dnt': '1',
29
+ 'accept-language': 'zh-TW'
30
+ }
31
+
32
+ custom_css = """
33
+ .center-aligned {
34
+ text-align: center;
35
+ }
36
+ .text-background {
37
+ font-size: 20px !important;
38
+ padding: 5px !important;
39
+ border-radius: 10px !important;
40
+ border: 2px solid #B7E0FF !important;
41
+ margin: 0 !important;
42
+ }
43
+ .file-background {
44
+ background-color: #B7E0FF !important;
45
+ padding: 15px !important;
46
+ border-radius: 10px !important;
47
+ margin: 0 !important;
48
+ height: auto;
49
+ }
50
+ .script-background {
51
+ background-color: #FEF9D9 !important;
52
+ padding: 5px !important;
53
+ border-radius: 10px !important;
54
+ margin: 0 !important;
55
+ }
56
+ #submit-btn {
57
+ border-radius: 10px !important;
58
+ border: none !important;
59
+ background-color: #ff4081 !important;
60
+ color: white !important;
61
+ font-weight: bold !important;
62
+ transition: all 0.3s ease !important;
63
+ margin: 0 !important;
64
+ }
65
+ #submit-btn:hover {
66
+ background-color: #f50057 !important;
67
+ transform: scale(1.05);
68
+ }
69
+ """
70
+
71
+ def CheckConnect(url, ref):
72
+ global headers
73
+ RefererList = {'UdnSec':'https://udn.com/search/', 'CNA':'https://www.cna.com.tw/', 'LTN':'https://news.ltn.com.tw/list/breakingnews/'}
74
+ headers['referer'] = RefererList[ref]
75
+ try:
76
+ response = requests.get(url, headers=headers)
77
+ soup = BeautifulSoup(response.text, 'lxml')
78
+ checkSuccess = True
79
+ return soup, checkSuccess
80
+ except Exception as e:
81
+ print(f'※【爬取失敗】{url} ※')
82
+ soup = None
83
+ checkSuccess = False
84
+ return soup, checkSuccess
85
+
86
+ def is_peak_hours():
87
+ taiwan_tz = pytz.timezone('Asia/Taipei')
88
+ current_time = datetime.datetime.now(taiwan_tz).time()
89
+ peak_start = datetime.time(8, 0)
90
+ peak_end = datetime.time(20, 0)
91
+ return peak_start <= current_time <= peak_end
92
+
93
+ def UdnTax_Breaknews(num_news):
94
+ url = 'https://udn.com/api/more'
95
+ news_list = []
96
+ for page in range(1, 3):
97
+ params = {
98
+ 'page': page,
99
+ 'channelId': 1,
100
+ 'type': 'breaknews',
101
+ 'cate_id': 0,
102
+ 'totalRecNo': 10655
103
+ }
104
+ try:
105
+ response = requests.get(url, params=params, headers=headers)
106
+ response.raise_for_status()
107
+ data = response.json()
108
+ if data['state']:
109
+ for article in data['lists']:
110
+ try:
111
+ Title = article['title']
112
+ if any(substring in Title for substring in ["中職", "棒球", "MLB", "大選", "新聞周刊", "TPBL", "即時短評", "醫奉", "週運勢", "統一獅", "中信兄弟"]):
113
+ continue
114
+ Link = 'https://udn.com' + article['titleLink']
115
+ Link = Link.split('?')[0].strip()
116
+ AMP_Link = r'/news/amp/' + Link.split(r'news/')[1].strip()
117
+ AMP_Link = 'https://udn.com' + AMP_Link
118
+ checkSuccess = False
119
+ tryNums = 0
120
+ while not checkSuccess and tryNums < 2:
121
+ soup, checkSuccess = CheckConnect(AMP_Link, 'UdnSec')
122
+ if not checkSuccess:
123
+ tryNums += 1
124
+ time.sleep(2)
125
+ if not checkSuccess:
126
+ print(f'開啟連結累積失敗2次,跳過此文章:{Title}')
127
+ continue
128
+ if soup is None:
129
+ print(f'無法解析文章內容,跳過此文章:{Title}')
130
+ continue
131
+ Summary = soup.select_one('main.main')
132
+ if Summary is None:
133
+ print(f'無法找到文章主體,跳過此文章:{Title}')
134
+ continue
135
+ Section = Summary.select('p')
136
+ Article = ''
137
+ for context in Section:
138
+ context = context.get_text().strip()
139
+ context = context.replace('\n','')
140
+ Article = Article + context
141
+ Article = Article.split('《原文刊登')[0].strip()
142
+ news_list.append((Title, Link, Article))
143
+ if len(news_list) >= int(num_news):
144
+ return news_list
145
+ except Exception as e:
146
+ print(f"Error processing article in UdnTax_Breaknews: {e}")
147
+ else:
148
+ print(f"UdnTax_Breaknews: API response state is false for page {page}")
149
+ except requests.RequestException as e:
150
+ print(f"Error in UdnTax_Breaknews request for page {page}: {e}")
151
+ except ValueError as e:
152
+ print(f"Error parsing JSON in UdnTax_Breaknews for page {page}: {e}")
153
+ except Exception as e:
154
+ print(f"Unexpected error in UdnTax_Breaknews for page {page}: {e}")
155
+ return news_list
156
+
157
+ def CNA_Breaknews(num_news):
158
+ url = 'https://www.cna.com.tw/list/aall.aspx'
159
+ soup, checkSuccess = CheckConnect(url, 'CNA')
160
+ if not checkSuccess:
161
+ return []
162
+ news_list = []
163
+ try:
164
+ summary = soup.select_one('div.centralContent')
165
+ if summary:
166
+ section = summary.select('li')
167
+ for context in section:
168
+ try:
169
+ title = context.select_one('h2').get_text().strip()
170
+ if any(title.startswith(prefix) for prefix in [
171
+ "大樂透",
172
+ "今彩539",
173
+ "威力彩",
174
+ "一週大事",
175
+ "開箱老照片》"
176
+ ]):
177
+ continue
178
+ link = context.select_one('a')['href'].strip()
179
+ if any(substring in link for substring in ["/aspt/", "/amov/", "/ahel/", "netzero."]):
180
+ continue
181
+ link = 'https://www.cna.com.tw' + link
182
+ article_soup, _ = CheckConnect(link, 'CNA')
183
+ if article_soup:
184
+ article = article_soup.select_one('div.paragraph')
185
+ if article:
186
+ article = article.get_text().strip()
187
+ article = article.replace('\n','')
188
+ article = article.split('(編輯:')[0].split('(譯者:')[0]
189
+ else:
190
+ article = "無法取得文章內容"
191
+ else:
192
+ article = "無法取得文章內容"
193
+ news_list.append((title, link, article))
194
+ if len(news_list) >= int(num_news):
195
+ break
196
+ except Exception as e:
197
+ print(f"Error processing article in CNA_Breaknews: {e}")
198
+ except Exception as e:
199
+ print(f"Error in CNA_Breaknews: {e}")
200
+ return news_list
201
+
202
+ def LTN_Breaknews(num_news):
203
+ url = 'https://news.ltn.com.tw/rss/all.xml'
204
+ news_list = []
205
+ try:
206
+ response = requests.get(url, headers=headers)
207
+ soup = BeautifulSoup(response.text, 'lxml-xml')
208
+ items = soup.find_all('item')
209
+ for item in items:
210
+ try:
211
+ link = item.find('link').text.strip()
212
+ if any(link.startswith(prefix) for prefix in [
213
+ "https://health.ltn.com.tw/",
214
+ "https://news.ltn.com.tw/news/def/",
215
+ "https://def.ltn.com.tw/",
216
+ "https://sports.ltn.com.tw/",
217
+ "https://istyle.ltn.com.tw/",
218
+ "https://ent.ltn.com.tw/",
219
+ "https://talk.ltn.com.tw/"
220
+ ]):
221
+ continue
222
+ title = item.find('title').text.strip()
223
+ if any(title.startswith(prefix) for prefix in [
224
+ "自由爆新聞》",
225
+ "自由說新聞》"
226
+ ]):
227
+ continue
228
+ if any(substring in title for substring in ["各報重點新聞一覽", "大樂透", "威力彩", "今彩539", "統一獅", "中信兄弟", "TPBL"]):
229
+ continue
230
+ article = item.find('description').text.strip()
231
+ news_list.append((title, link, article))
232
+ if len(news_list) >= int(num_news):
233
+ break
234
+ except Exception as e:
235
+ print(f"Error processing article in LTN_Breaknews: {e}")
236
+ except Exception as e:
237
+ print(f"Error in LTN_Breaknews: {e}")
238
+ return news_list
239
+
240
+ def ETtoday_Breaknews(num_news):
241
+ url = 'https://feeds.feedburner.com/ettoday/realtime'
242
+ news_list = []
243
+ try:
244
+ response = requests.get(url, headers=headers)
245
+ soup = BeautifulSoup(response.content, 'xml')
246
+ items = soup.find_all('item')
247
+ for item in items:
248
+ title = item.find('title').text.strip()
249
+ link = item.find('link').text.strip()
250
+ if any(link.startswith(prefix) for prefix in [
251
+ "https://star.ettoday.net/",
252
+ "https://fashion.ettoday.net/",
253
+ "https://ezbuy.ettoday.net/",
254
+ "https://boba.ettoday.net/",
255
+ "https://events.ettoday.net/",
256
+ "https://forum.ettoday.net/",
257
+ "https://sports.ettoday.net/",
258
+ "https://pets.ettoday.net/",
259
+ "https://speed.ettoday.net/",
260
+ "https://travel.ettoday.net/",
261
+ "https://game.ettoday.net/",
262
+ "https://health.ettoday.net/",
263
+ "https://discovery.ettoday.net/",
264
+ "https://esg.ettoday.net/",
265
+ "https://ettoday.kaik.io/"
266
+ ]):
267
+ continue
268
+ description = item.find('description').text.strip()
269
+ content_start = description.find('<br/>')
270
+ if content_start != -1:
271
+ content_start += 5
272
+ article = description[content_start:].strip()
273
+ article = BeautifulSoup(article, 'html.parser').get_text()
274
+ if not article:
275
+ continue
276
+ else:
277
+ continue
278
+ news_list.append((title, link, article))
279
+ if len(news_list) >= int(num_news):
280
+ break
281
+ except Exception as e:
282
+ print(f"Error in ETtoday_Breaknews: {e}")
283
+ return news_list
284
+
285
+ def SETN_Breaknews(num_news):
286
+ url = 'https://www.setn.com/ViewAll.aspx'
287
+ params = {
288
+ 'p': 1
289
+ }
290
+ news_list = []
291
+ try:
292
+ response = requests.get(url, params=params, headers=headers)
293
+ soup = BeautifulSoup(response.text, 'html.parser')
294
+ news_items = soup.select('div.newsItems')
295
+ for item in news_items:
296
+ try:
297
+ title_elem = item.select_one('h3.view-li-title a')
298
+ if title_elem:
299
+ title = title_elem.text.strip()
300
+ if any(substring in title for substring in ["中職", "棒球", "MLB", "大選", "會員日", "TPBL", "統一獅", "中信兄弟"]):
301
+ continue
302
+ link = title_elem['href']
303
+ if not link.startswith('http'):
304
+ if link.startswith('//'):
305
+ link = 'https:' + link
306
+ elif link.startswith('/'):
307
+ link = 'https://www.setn.com' + link
308
+ else:
309
+ link = 'https://www.setn.com/' + link
310
+ if any(link.startswith(prefix) for prefix in [
311
+ "https://star.setn.com",
312
+ "https://health.setn.com"
313
+ ]):
314
+ continue
315
+ link = link.split('&utm_')[0]
316
+ article_response = requests.get(link, headers=headers)
317
+ article_soup = BeautifulSoup(article_response.text, 'html.parser')
318
+ article_content = article_soup.select_one('div#Content1')
319
+ if article_content:
320
+ article = article_content.get_text(strip=True)
321
+ else:
322
+ article = "無法獲取文章內容"
323
+ news_list.append((title, link, article))
324
+ if len(news_list) >= int(num_news):
325
+ break
326
+ except Exception as e:
327
+ print(f"Error processing news item in SETN_Breaknews: {e}")
328
+ except requests.RequestException as e:
329
+ print(f"Error in SETN_Breaknews request: {e}")
330
+ except Exception as e:
331
+ print(f"Unexpected error in SETN_Breaknews: {e}")
332
+ return news_list
333
+
334
+ def initial_news(num_news):
335
+ gr.Info("掌握焦點新聞中,請稍待片刻....")
336
+ start_time = time.time()
337
+ taiwan_tz = pytz.timezone('Asia/Taipei')
338
+ current_time = datetime.datetime.now(taiwan_tz).strftime("%Y-%m-%d %H:%M:%S")
339
+ all_news = []
340
+ total_news = 0
341
+ news_sources = [
342
+ ('UdnTax_Breaknews', UdnTax_Breaknews, int(num_news)),
343
+ ('CNA_Breaknews', CNA_Breaknews, int(num_news)),
344
+ ('LTN_Breaknews', LTN_Breaknews, int(num_news)),
345
+ ('ETtoday_Breaknews', ETtoday_Breaknews, int(num_news)),
346
+ ('SETN_Breaknews', SETN_Breaknews, int(num_news))
347
+ ]
348
+ for source_name, source_func, num_news in news_sources:
349
+ try:
350
+ all_news.extend(source_func(num_news))
351
+ except Exception as e:
352
+ print(f"Error fetching news from {source_name}: {e}")
353
+ total_news = len(all_news)
354
+ estimated_time = total_news * 7 # 每條新聞預估7秒
355
+ estimated_minutes = estimated_time // 60
356
+ estimated_seconds = estimated_time % 60
357
+ gr.Info(f"預估處理時間:約 {estimated_minutes} 分 {estimated_seconds} 秒")
358
+
359
+ client = OpenAI(
360
+ api_key=os.environ.get("YOUR_API_KEY"),
361
+ base_url="https://api.sambanova.ai/v1",
362
+ )
363
+ keywords = ["房屋稅","地價稅","牌照稅","土地增值稅","契稅","印花稅","娛樂稅","特別稅","囤房稅","財政部","財政資訊中心","財資中心","臺北市稅捐稽徵處","新北市政府稅捐稽徵處","桃園市政府地方稅務局","臺中市政府地方稅務局","臺南市政府財政稅務局","高雄市稅捐稽徵處","新竹市稅務局","新竹縣政府稅務局","苗栗縣政府稅務局","彰化縣地方稅務局","南投縣稅務局","雲林縣稅務局","嘉義市政府財政稅務局","嘉義縣財政稅務局","屏東縣政府財稅局","基隆市稅務局","宜蘭縣政府財政稅務局","花蓮縣地方稅務局","臺東縣稅務局","澎湖縣政府稅務局","金門縣稅務局","連江縣財政稅務局","生成式AI","人工智慧","AI智慧","AI模型","個資外洩","資安漏洞","隱私洩漏","稅務局","地方稅","稅捐機關","稅務入口網","地方稅網路申報"]
364
+ prompt = f"""
365
+ 請判斷以下新聞內容與這些關鍵字的相關性:```{', '.join(keywords)}```
366
+ 請給出一個0到100之間的相關性百分比,並說明理,理由說明不要提到keywords內容。
367
+ 回答格式為:
368
+ 相關性:XX%
369
+ 理由:(簡要說明)
370
+
371
+ 新聞內容:```
372
+ """
373
+ for i, (title, link, article) in enumerate(all_news):
374
+ full_prompt = prompt + article + "```"
375
+ max_retries = 2
376
+ retry_delay = 5
377
+ for attempt in range(max_retries):
378
+ try:
379
+ response = client.chat.completions.create(
380
+ model="Meta-Llama-3.1-405B-Instruct",
381
+ messages=[
382
+ {"role": "system", "content": "Must reply to user in Traditional Chinese."},
383
+ {"role": "user", "content": full_prompt}
384
+ ],
385
+ temperature=1
386
+ )
387
+ result = response.choices[0].message.content.strip()
388
+ break
389
+ except Exception as e:
390
+ if (attempt == 0) or (attempt == max_retries - 1):
391
+ try:
392
+ response = client.chat.completions.create(
393
+ model="Meta-Llama-3.1-70B-Instruct",
394
+ messages=[
395
+ {"role": "system", "content": "Must reply to user in Traditional Chinese."},
396
+ {"role": "user", "content": full_prompt}
397
+ ],
398
+ temperature=0.5
399
+ )
400
+ result = response.choices[0].message.content.strip()
401
+ print("Successfully used 70B model as fallback")
402
+ break
403
+ except Exception as e2:
404
+ print(f"Error with both models: {e2}")
405
+ result = "無法處理此新聞項目"
406
+ break
407
+ else:
408
+ print(f"Retrying in {retry_delay} seconds...")
409
+ time.sleep(retry_delay)
410
+ retry_delay *= 2
411
+ relevance_percentage = 0
412
+ reason = ""
413
+ for line in result.split('\n'):
414
+ if line.startswith("相關性:"):
415
+ try:
416
+ relevance_percentage = int(line.split(":")[1].strip().rstrip('%'))
417
+ except ValueError:
418
+ print(f"Warning: Could not parse relevance percentage from '{line}'")
419
+ relevance_percentage = 0
420
+ elif line.startswith("理由:"):
421
+ reason = line.split(":")[1].strip()
422
+ relevance = "高" if relevance_percentage >= 60 else "中" if relevance_percentage >= 40 else "無"
423
+ all_news[i] = (title, link, article, f"Relevance: '{relevance}', Rate: {relevance_percentage}%, Reason: {reason}")
424
+ print(f"News item {i} processed: {all_news[i]}")
425
+ # 在每次API調用之間增加隨機延遲
426
+ time.sleep(random.uniform(2, 5))
427
+ if total_news > 10 and (i + 1) % (total_news // 5) == 0:
428
+ progress_percentage = ((i + 1) / total_news) * 100
429
+ gr.Info(f"已處理 {progress_percentage:.0f}% 的新聞項目")
430
+
431
+ news_html = """
432
+ <style>
433
+ .high-relevance:hover {
434
+ color: red !important;
435
+ }
436
+ .news-list {
437
+ list-style-type: disc;
438
+ padding-left: 10px;
439
+ }
440
+ .news-item {
441
+ margin-bottom: 15px;
442
+ }
443
+ </style>
444
+ <ul class="news-list">
445
+ """
446
+ high_relevance_news = []
447
+ for title, link, article, relevance_info in all_news:
448
+ relevance_parts = relevance_info.split(', ')
449
+ relevance = relevance_parts[0].split("'")[1]
450
+ rate = int(relevance_parts[1].split(': ')[1].rstrip('%'))
451
+ reason = relevance_parts[2].split(': ')[1]
452
+ if rate >= 40:
453
+ high_relevance_news.append((title, link, rate, reason))
454
+ if rate >= 60:
455
+ title_style = 'color: #C62E2E; font-weight: bold;'
456
+ title_class = 'class="high-relevance"'
457
+ info_color = '#C62E2E'
458
+ elif 40 <= rate < 60:
459
+ title_style = 'color: #F95454; font-weight: bold;'
460
+ title_class = 'class="high-relevance"'
461
+ info_color = 'black'
462
+ elif 20 <= rate < 40:
463
+ title_style = 'color: #FFA24C; font-weight: bold;'
464
+ title_class = 'class="high-relevance"'
465
+ info_color = 'black'
466
+ else:
467
+ title_style = 'color: blue;'
468
+ title_class = ''
469
+ info_color = 'black'
470
+ news_html += f"""
471
+ <li class="news-item">
472
+ <a href='{link}' target='_blank' style='text-decoration: none; {title_style}' {title_class}>{title}</a><br>
473
+ <span style='color: {info_color};'>LLM解析結果:{relevance}財稅相關,關聯度為 {rate} %。</span><br>
474
+ <span style='color: {info_color};'>理由:{reason}</span>
475
+ </li>
476
+ """
477
+ news_html += "</ul>"
478
+ news_html += f"<p style='text-align: right; font-style: italic; color: #888;'>更新時間:{current_time}</p>"
479
+ if high_relevance_news:
480
+ resend.api_key = os.environ["YOUR_API_TOKEN"]
481
+ email_content = "<h2>財資焦點新聞摘要</h2><ul>"
482
+ for title, link, rate, reason in high_relevance_news:
483
+ email_content += f"<li><a href='{link}'>{title}</a>(相關性:{rate}%)<br>理由:{reason}</li>"
484
+ email_content += "</ul>"
485
+ params: resend.Emails.SendParams = {
486
+ "from": "News_Get <onboarding@resend.dev>",
487
+ "to": ["antivir7@gmail.com"],
488
+ "subject": "財資關鍵焦點新聞(閥值40%)",
489
+ "html": email_content,
490
+ }
491
+ try:
492
+ email_response = resend.Emails.send(params)
493
+ print(f"High relevance news email sent successfully. Response: {email_response}")
494
+ except Exception as e:
495
+ print(f"Failed to send high relevance news email: {str(e)}")
496
+ end_time = time.time()
497
+ gr.Info(f"焦點新聞已更新完成,執行時間: {(end_time - start_time):.2f} 秒。")
498
+ return news_html
499
+
500
+ last_update_time = 0
501
+ last_num_news = 0
502
+
503
+ def update_news(num_news):
504
+ global last_update_time, last_num_news
505
+ current_time = time.time()
506
+ num_news = int(num_news)
507
+ if num_news <= 10 and last_num_news <= 20:
508
+ cooldown = 180
509
+ elif 10 <= num_news <= 20 and last_num_news <= 10:
510
+ cooldown = 180
511
+ elif 10 <= num_news <= 20 and 10 < last_num_news <= 20:
512
+ cooldown = 300
513
+ elif 10 <= num_news <= 20 and last_num_news <= 30:
514
+ cooldown = 600
515
+ elif 10 <= num_news <= 20 and last_num_news <= 40:
516
+ cooldown = 1200
517
+ else:
518
+ cooldown = 1800
519
+ if current_time - last_update_time < cooldown:
520
+ remaining_time = int(cooldown - (current_time - last_update_time))
521
+ gr.Warning(f"為避免遭誤判為DDoS,請等待 {remaining_time} 秒後再次更新")
522
+ if last_update_time == 0:
523
+ return gr.update(value=f"為避免遭誤判為DDoS,請等待 {remaining_time} 秒後再次更新"), gr.update(visible=True)
524
+ else:
525
+ return gr.update(), gr.update(visible=True)
526
+ last_update_time = current_time
527
+ last_num_news = num_news
528
+ resend.api_key = os.environ["YOUR_API_TOKEN"]
529
+ params: resend.Emails.SendParams = {
530
+ "from": "News_API <onboarding@resend.dev>",
531
+ "to": ["antivir7@gmail.com"],
532
+ "subject": "掌握焦點新聞",
533
+ "html": f"""
534
+ <strong>焦點新聞內容更新</strong><br>
535
+ 每家獲取數量:{num_news}<br>
536
+ 更新時間:{datetime.datetime.fromtimestamp(last_update_time).strftime('%Y-%m-%d %H:%M:%S')}
537
+ """,
538
+ }
539
+ try:
540
+ email_response = resend.Emails.send(params)
541
+ print(f"Email sent successfully. Response:{email_response}")
542
+ except Exception as e:
543
+ print(f"Failed to send email:{str(e)}")
544
+ news_html = initial_news(num_news)
545
+ return news_html, gr.update(visible=True)
546
+
547
+ with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css) as iface:
548
+ gr.Markdown("""
549
+ # 🔥 最新焦點新聞 - 財政部財政資訊中心 🔥
550
+ > ### **※ 快速掌握關注之最新焦點新聞,為避免遭誤判為DDoS,請適當調整每家獲取數量。系統部署:江信宗,LLM:Llama-3.1-70B。**
551
+ """, elem_classes="center-aligned")
552
+ with gr.Row():
553
+ num_news = gr.Slider(
554
+ minimum=1,
555
+ maximum=40 if is_peak_hours() else 20,
556
+ value=5,
557
+ step=1,
558
+ label="每家獲取數量",
559
+ info="每家爬取新聞則數進行LLM解析",
560
+ elem_classes="file-background"
561
+ )
562
+ with gr.Column():
563
+ gr.HTML(
564
+ """
565
+ <span style="font-size: 18px; color: black;">為免相同新聞爬取過多,僅以5間新聞媒體作示範,取得新聞數量越多,LLM解析時間越長,敬請耐心等候。</span>
566
+ """, elem_classes="script-background"
567
+ )
568
+ update_button = gr.Button("掌握焦點新聞", elem_id="submit-btn")
569
+ opinion_news = gr.HTML(label="焦點新聞", elem_classes="text-background", visible=False)
570
+ update_button.click(fn=update_news, inputs=[num_news], outputs=[opinion_news, opinion_news])
571
+
572
+ if __name__ == "__main__":
573
+ if "SPACE_ID" in os.environ:
574
+ iface.launch()
575
+ else:
576
+ iface.launch(share=True, show_api=False)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio
2
+ openai
3
+ requests
4
+ resend
5
+ beautifulsoup4
6
+ lxml
7
+ pytz