bep40 commited on
Commit
39c3d67
·
verified ·
1 Parent(s): c95a3a0

World Cup RSS all articles + persistent AI rewrite wall APIs

Browse files
Files changed (1) hide show
  1. main.py +65 -1
main.py CHANGED
@@ -72,6 +72,20 @@ def _save_short_db(db):
72
  except:pass
73
 
74
  def _short_default():return {"views":0,"likes":0,"shares":0,"comments":[]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  PRIORITY_LEAGUES = ["Ngoại Hạng Anh","FA Cup","Champions League","LaLiga","Copa del Rey","Serie A","Bundesliga","Ligue 1","V-League"]
76
  LEAGUE_IDS = {"nha":27110,"laliga":27233,"seriea":27044,"bundesliga":26891,"ligue1":27212}
77
  HL_LEAGUES = {"premier-league":{"path":"anh/premier-league","name":"Premier League","emoji":"🏴󠁧󠁢󠁥󠁮󠁧󠁿"},"fa-cup":{"path":"anh/fa-cup","name":"FA Cup","emoji":"🏆"},"bundesliga":{"path":"duc/bundesliga","name":"Bundesliga","emoji":"🇩🇪"},"serie-a":{"path":"italy/serie-a","name":"Serie A","emoji":"🇮🇹"},"la-liga":{"path":"tay-ban-nha/la-liga","name":"La Liga","emoji":"🇪🇸"},"champions-league":{"path":"cup-chau-au/uefa-champions-league","name":"Champions League","emoji":"⭐"},"europa-league":{"path":"cup-chau-au/uefa-europa-league","name":"Europa League","emoji":"🟠"},"world-cup":{"path":"the-gioi/world-cup-qualifiers","name":"World Cup 2026","emoji":"🌍"}}
@@ -541,7 +555,20 @@ def scrape_bbc_article(url):
541
  except:return None
542
 
543
  def scrape_ttvh_worldcup():
544
- """Scrape World Cup 2026 articles from The Thao Van Hoa."""
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  try:
546
  soup=_get("https://thethaovanhoa.vn/world-cup-2026.htm");arts=[];seen=set()
547
  for a in soup.find_all("a",href=True):
@@ -646,6 +673,43 @@ def api_article(url:str=Query(...)):
646
  elif"thethaovanhoa.vn" in url:data=scrape_ttvh_article(url)
647
  else:data=None
648
  return JSONResponse(data if data else{"error":"not supported"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  @app.get("/v")
650
  async def video_share(url:str=Query(default=""),title:str=Query(default="VNEWS Video"),img:str=Query(default=""),type:str=Query(default="highlights")):
651
  decoded_url=unquote(url);decoded_title=unquote(title)
 
72
  except:pass
73
 
74
  def _short_default():return {"views":0,"likes":0,"shares":0,"comments":[]}
75
+ WALL_FILE = "/data/wall_posts.json" if os.path.isdir("/data") else "/app/wall_posts.json"
76
+ def _load_wall():
77
+ try:
78
+ if os.path.exists(WALL_FILE):
79
+ with open(WALL_FILE,"r",encoding="utf-8") as f:return json.load(f)
80
+ except:pass
81
+ return []
82
+ def _save_wall(posts):
83
+ try:
84
+ os.makedirs(os.path.dirname(WALL_FILE),exist_ok=True)
85
+ tmp=WALL_FILE+".tmp"
86
+ with open(tmp,"w",encoding="utf-8") as f:json.dump(posts[:100],f,ensure_ascii=False)
87
+ os.replace(tmp,WALL_FILE)
88
+ except:pass
89
  PRIORITY_LEAGUES = ["Ngoại Hạng Anh","FA Cup","Champions League","LaLiga","Copa del Rey","Serie A","Bundesliga","Ligue 1","V-League"]
90
  LEAGUE_IDS = {"nha":27110,"laliga":27233,"seriea":27044,"bundesliga":26891,"ligue1":27212}
91
  HL_LEAGUES = {"premier-league":{"path":"anh/premier-league","name":"Premier League","emoji":"🏴󠁧󠁢󠁥󠁮󠁧󠁿"},"fa-cup":{"path":"anh/fa-cup","name":"FA Cup","emoji":"🏆"},"bundesliga":{"path":"duc/bundesliga","name":"Bundesliga","emoji":"🇩🇪"},"serie-a":{"path":"italy/serie-a","name":"Serie A","emoji":"🇮🇹"},"la-liga":{"path":"tay-ban-nha/la-liga","name":"La Liga","emoji":"🇪🇸"},"champions-league":{"path":"cup-chau-au/uefa-champions-league","name":"Champions League","emoji":"⭐"},"europa-league":{"path":"cup-chau-au/uefa-europa-league","name":"Europa League","emoji":"🟠"},"world-cup":{"path":"the-gioi/world-cup-qualifiers","name":"World Cup 2026","emoji":"🌍"}}
 
555
  except:return None
556
 
557
  def scrape_ttvh_worldcup():
558
+ """Scrape all World Cup 2026 articles from The Thao Van Hoa RSS."""
559
+ try:
560
+ r=requests.get("https://thethaovanhoa.vn/rss/world-cup-2026.rss",headers=HEADERS,timeout=15);r.encoding="utf-8"
561
+ soup=BeautifulSoup(r.text,"xml");arts=[];seen=set()
562
+ for it in soup.find_all("item"):
563
+ title=(it.find("title").get_text(strip=True) if it.find("title") else "")
564
+ link=(it.find("link").get_text(strip=True) if it.find("link") else "")
565
+ desc=(it.find("description").get_text(" ",strip=True) if it.find("description") else "")
566
+ img="";ds=BeautifulSoup(desc,"lxml");im=ds.find("img")
567
+ if im:img=im.get("src","") or im.get("data-src","")
568
+ if title and link and link not in seen:
569
+ seen.add(link);arts.append({"title":title,"link":link,"img":img,"source":"ttvh"})
570
+ if arts:return arts
571
+ except:pass
572
  try:
573
  soup=_get("https://thethaovanhoa.vn/world-cup-2026.htm");arts=[];seen=set()
574
  for a in soup.find_all("a",href=True):
 
673
  elif"thethaovanhoa.vn" in url:data=scrape_ttvh_article(url)
674
  else:data=None
675
  return JSONResponse(data if data else{"error":"not supported"})
676
+ def _article_by_url(url):
677
+ if "vnexpress.net" in url:return scrape_vne_article(url)
678
+ if "bbc.com" in url:return scrape_bbc_article(url)
679
+ if "dantri.com.vn" in url:return scrape_dantri_article(url)
680
+ if "genk.vn" in url:return scrape_genk_article(url)
681
+ if "thethaovanhoa.vn" in url:return scrape_ttvh_article(url)
682
+ return None
683
+
684
+ def _ai_rewrite_article(data):
685
+ title=(data or {}).get("title","");summary=(data or {}).get("summary","")
686
+ ps=[b.get("text","") for b in (data or {}).get("body",[]) if b.get("type")=="p" and len(b.get("text",""))>30]
687
+ lead=summary or (ps[0] if ps else "")
688
+ points=[]
689
+ for p in ps[:5]:
690
+ t=p.strip()
691
+ if len(t)>180:t=t[:177]+"..."
692
+ points.append(t)
693
+ rewritten="Bản tin AI viết lại: "+title+"\n\n"+lead
694
+ if points:rewritten += "\n\nĐiểm chính:\n" + "\n".join(["• "+x for x in points])
695
+ return rewritten
696
+
697
+ @app.get("/api/wall")
698
+ def api_wall():return JSONResponse({"posts":_load_wall()[:50]})
699
+
700
+ @app.post("/api/rewrite_share")
701
+ async def api_rewrite_share(request:Request):
702
+ try:body=await request.json()
703
+ except:body={}
704
+ url=str(body.get("url","")).strip()
705
+ if not url:return JSONResponse({"error":"missing url"},status_code=400)
706
+ data=_article_by_url(url)
707
+ if not data or not data.get("title") or not data.get("body"):
708
+ return JSONResponse({"error":"Không đọc được bài viết"},status_code=422)
709
+ post={"id":hashlib.md5((url+str(time.time())).encode()).hexdigest()[:12],"url":url,"title":data.get("title",""),"img":data.get("og_image","") or "","text":_ai_rewrite_article(data),"ts":int(time.time()),"source":data.get("source","")}
710
+ posts=_load_wall();posts.insert(0,post);_save_wall(posts)
711
+ return JSONResponse({"post":post})
712
+
713
  @app.get("/v")
714
  async def video_share(url:str=Query(default=""),title:str=Query(default="VNEWS Video"),img:str=Query(default=""),type:str=Query(default="highlights")):
715
  decoded_url=unquote(url);decoded_title=unquote(title)