Poe255M commited on
Commit
d480ffe
·
verified ·
1 Parent(s): 2a29d02

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +643 -0
app.py ADDED
@@ -0,0 +1,643 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ==============================================================================
2
+ # 🛡️ FraudGuard Myanmar AI - Final Master Pipeline
3
+ # ==============================================================================
4
+ import gradio as gr
5
+ import requests
6
+ import whois
7
+ import re
8
+ import feedparser
9
+ import os
10
+
11
+ import numpy as np
12
+ from datetime import datetime
13
+ from difflib import SequenceMatcher
14
+ from PIL import Image, ImageChops
15
+ from sentence_transformers import SentenceTransformer, util
16
+ import torch
17
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
18
+ from io import BytesIO
19
+ from bs4 import BeautifulSoup
20
+
21
+ # --- 1. MODEL LOADING ---
22
+ print("System Initializing... Loading All AI Components.")
23
+
24
+
25
+ MODEL_NAME = "Poe255M/myanmar-fraud-detection-final"
26
+
27
+ try:
28
+ # Hugging Face မှ Model နှင့် Tokenizer ကို တိုက်ရိုက်ယူခြင်း
29
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
30
+ nlp_model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME)
31
+ print(f"✅ Custom Myanmar AI Model Loaded from Hugging Face: {MODEL_NAME}")
32
+ except Exception as e:
33
+ print(f"⚠️ သတိပေးချက်: Online Model ကို မတွေ့ပါ။ Base Model ကို ယာယီသုံးထားပါမည်။ Error: {e}")
34
+ tokenizer = AutoTokenizer.from_pretrained("xlm-roberta-base")
35
+ nlp_model = AutoModelForSequenceClassification.from_pretrained("xlm-roberta-base", num_labels=2)
36
+
37
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
38
+ nlp_model.to(device)
39
+ nlp_model.eval()
40
+
41
+
42
+ print("Loading CLIP model for multi-modal verification...")
43
+ clip_model = SentenceTransformer('clip-ViT-B-32')
44
+
45
+ # --- 2. CORE LOGIC MODULES ---
46
+
47
+ class VerificationSystem:
48
+ @staticmethod
49
+ def translate_to_en(text):
50
+ """CLIP Model ဖြင့် ပုံကိုစစ်ဆေးရန်အတွက်သာ သုံးမည်"""
51
+ try:
52
+ url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=en&dt=t&q={text}"
53
+ res = requests.get(url, timeout=5).json()
54
+ return "".join([s[0] for s in res[0]])
55
+ except:
56
+ return text
57
+
58
+ @staticmethod
59
+ def verify_headline_match(user_text, url):
60
+ """User ရိုက်တဲ့စာနဲ့ URL ထဲက မူရင်းခေါင်းစဉ် တူ၊ မတူ စစ်ဆေးရန်"""
61
+ if not url or not url.startswith("http"):
62
+ return 100, "N/A"
63
+ try:
64
+ headers = {'User-Agent': 'Mozilla/5.0'}
65
+ res = requests.get(url, headers=headers, timeout=10)
66
+ soup = BeautifulSoup(res.text, 'html.parser')
67
+
68
+ original_title = ""
69
+ h1 = soup.find('h1')
70
+ if h1:
71
+ original_title = h1.get_text().strip()
72
+ elif soup.title:
73
+ original_title = soup.title.get_text().strip()
74
+
75
+ if not original_title:
76
+ return 50, "Could not extract title from source"
77
+
78
+ similarity = SequenceMatcher(None, user_text.lower(), original_title.lower()).ratio()
79
+ match_score = similarity * 100
80
+
81
+ msg = f"✅ Original Title: {original_title[:60]}..." if match_score > 60 else f"⚠️ Mismatch! Source Title: {original_title[:60]}..."
82
+ return match_score, msg
83
+ except:
84
+ return 50, "Error Fetching Source Title"
85
+
86
+ @staticmethod
87
+ def perform_ela(image_path, quality=90):
88
+ if not image_path: return 100, "No image"
89
+ try:
90
+ original = Image.open(image_path).convert('RGB')
91
+ resaved_path = "temp_forensic.jpg"
92
+ original.save(resaved_path, 'JPEG', quality=quality)
93
+ resaved = Image.open(resaved_path)
94
+
95
+ ela_diff = ImageChops.difference(original, resaved)
96
+ stat = np.array(ela_diff).mean()
97
+ if os.path.exists(resaved_path): os.remove(resaved_path)
98
+
99
+ if stat > 1.2:
100
+ score = max(10, 100 - (stat * 40))
101
+ return score, "⚠️ Tampering Detected"
102
+ elif stat > 0.8:
103
+ score = max(50, 100 - (stat * 20))
104
+ return score, "🟡 Minor Edits/Low Quality"
105
+ else:
106
+ score = min(100, 100 - (stat * 5))
107
+ return score, "✅ Consistent Pixels"
108
+ except: return 50, "Scan Error"
109
+
110
+ @staticmethod
111
+ def extract_image_from_url(url):
112
+ if not url: return None
113
+ try:
114
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
115
+ if any(url.lower().endswith(ext) for ext in ['.jpg', '.jpeg', '.png', '.webp']):
116
+ res = requests.get(url, headers=headers, timeout=10)
117
+ img = Image.open(BytesIO(res.content)).convert('RGB')
118
+ path = "temp_url_image.jpg"
119
+ img.save(path)
120
+ return path
121
+
122
+ response = requests.get(url, headers=headers, timeout=10)
123
+ soup = BeautifulSoup(response.text, 'html.parser')
124
+ img_tag = soup.find("meta", property="og:image") or soup.find("meta", attrs={"name": "twitter:image"})
125
+ img_url = img_tag["content"] if img_tag and img_tag.has_attr("content") else None
126
+
127
+ if not img_url:
128
+ first_img = soup.find("img")
129
+ img_url = first_img["src"] if first_img and first_img.has_attr("src") else None
130
+
131
+ if img_url:
132
+ if img_url.startswith('//'): img_url = 'https:' + img_url
133
+ elif img_url.startswith('/'):
134
+ from urllib.parse import urljoin
135
+ img_url = urljoin(url, img_url)
136
+
137
+ img_res = requests.get(img_url, headers=headers, timeout=10)
138
+ img = Image.open(BytesIO(img_res.content)).convert('RGB')
139
+ path = "temp_extracted_image.jpg"
140
+ img.save(path)
141
+ return path
142
+ except: return None
143
+ return None
144
+
145
+ @staticmethod
146
+ def get_image_text_similarity(image_path, text, url="", url_image_path=None):
147
+ if not image_path or not text: return 50, "Incomplete Data"
148
+ try:
149
+ is_myanmar = bool(re.search(r'[\u1000-\u109F]', text))
150
+ processed_text = VerificationSystem.translate_to_en(text) if is_myanmar else text
151
+
152
+ img_obj = Image.open(image_path)
153
+ img_emb = clip_model.encode(img_obj)
154
+ text_emb = clip_model.encode([processed_text])
155
+ similarity = util.cos_sim(img_emb, text_emb).item()
156
+
157
+ is_trusted_source = False
158
+ trusted_domains = ["bbc.com", "reuters.com", "apnews.com", "voanews.com", "rfa.org", "nytimes.com"]
159
+ if url:
160
+ for d in trusted_domains:
161
+ if d in url.lower():
162
+ is_trusted_source = True
163
+ break
164
+
165
+ is_text_match = similarity >= 0.22
166
+
167
+ if is_text_match:
168
+ final_score = min(100, similarity * 240)
169
+ msg = "✅ စာသားနှင့် ပုံ ကိုက်ညီမှုရှိသည်" if is_myanmar else "✅ Context Matches"
170
+ else:
171
+ if is_trusted_source:
172
+ final_score = 55.0
173
+ msg = "ℹ️ သတင်းရင်းမြစ်မှာ ယုံကြည်ရသော်လည်း ပုံမှာ သရုပ်ပြပုံ (Illustrative) သာ ဖြစ်နိုင်ပါသည်" if is_myanmar else "ℹ️ Trusted source but image may be illustrative"
174
+ else:
175
+ final_score = max(10, similarity * 130)
176
+ msg = "❌ စာသားနှင့် ပုံ မကိုက်ညီပါ" if is_myanmar else "❌ Content Mismatch"
177
+
178
+ img_match_pct = 0
179
+ has_url_img = False
180
+
181
+ if url_image_path and os.path.exists(url_image_path):
182
+ has_url_img = True
183
+ url_img_emb = clip_model.encode(Image.open(url_image_path))
184
+ img_similarity = util.cos_sim(img_emb, url_img_emb).item()
185
+ img_match_pct = img_similarity * 100
186
+
187
+ if img_match_pct > 80:
188
+ if not is_text_match:
189
+ final_score = 65.0 if is_trusted_source else 45.0
190
+ msg = "⚠️ Link ပါပုံနှင့် တူသော်လည်း ခေါင်းစဉ်နှင့် တိုက်ရိုက်မသက်ဆိုင်ပါ" if is_myanmar else "⚠️ Image matches Source but Mismatches Headline"
191
+ else:
192
+ final_score = min(100, final_score + 15)
193
+ msg = "✅ မူရင်းသတင်းပါပုံဖြစ်ပြီး စာသားနှင့်လည်း ကိုက်ညီပါသည်" if is_myanmar else "✅ Verified Source Image Match"
194
+ elif img_match_pct < 50:
195
+ final_score = max(10, final_score - 25)
196
+ msg += " | ⚠️ Link ထဲမှ မူရင်းပုံမဟုတ်ပါ" if is_myanmar else " | ⚠️ Not the Original Image from Link"
197
+
198
+ if has_url_img and not is_text_match and img_match_pct < 50 and not is_trusted_source:
199
+ final_score = 10.5
200
+ msg = "🚨 သတင်းအချက်အလက်အားလုံး လွဲမှားနေပါသည် (High Risk)" if is_myanmar else "🚨 High Risk: Complete Information Mismatch"
201
+
202
+ return round(max(5, final_score), 2), msg
203
+ except Exception as e:
204
+ return 50, f"Verification Error: {str(e)}"
205
+
206
+
207
+ @staticmethod
208
+ def get_source_score(url):
209
+ if not url or not url.startswith("http"):
210
+ return 30, "Missing/Invalid URL Source"
211
+
212
+ try:
213
+ domain_search = re.search(r'https?://([A-Za-z0-9.-]+)', url)
214
+ if not domain_search: return 30, "Invalid Domain"
215
+ domain = domain_search.group(1).lower()
216
+
217
+ # --- ၁။ တရားဝင် သတင်းဌာနကြီးများ (Hard News) ---
218
+ trusted_news = ["bbc.com", "reuters.com", "rfa.org", "voanews.com", "dvb.no", "irrawaddy.com", "myanmar-now.org", "khitthitnews.com", "mizzima.com"]
219
+
220
+ # --- ၂။ နာမည်ကြီး အနုပညာ/ဆယ်လီ မီဒီယာများ (Cele Media) ---
221
+ trusted_cele = ["myanmarcelebrity.com", "popularmyanmar.com", "celegabar.com", "shwemon.com", "celeyatkwat.com"]
222
+
223
+ # (က) သတင်းဌာနကြီးများ စစ်ဆေးခြင်း
224
+ for m in trusted_news:
225
+ if domain == m or domain.endswith("." + m):
226
+ return 100, f"Verified News Media ({domain})"
227
+
228
+ # (ခ) အနုပညာသတင်းဌာနများ စစ်ဆေးခြင်း
229
+ for c in trusted_cele:
230
+ if domain == c or domain.endswith("." + c):
231
+ return 90, f"Verified Entertainment Media ({domain})"
232
+
233
+ # --- ၃။ Social Media Links (Facebook, Instagram) စစ်ဆေးခြင်း ---
234
+ # ဆယ်လီသတင်းအများစုသည် Social Media ပေါ်တွင်သာ ရှိတတ်သဖြင့် သီးသန့်စစ်ဆေးမည်
235
+ if "facebook.com" in domain or "instagram.com" in domain:
236
+ path = url.split(domain)[-1].lower()
237
+
238
+ # Official Page ဟု ယူဆနိုင်သော လက္ခဏာများ (ဥပမာ - facebook.com/naytoe.official)
239
+ if "official" in path or "original" in path or "verified" in path:
240
+ return 85, "Likely Official Social Media Account"
241
+
242
+ # Facebook Group သို့မဟုတ် Video Link သီးသန့်ဖြစ်နေလျှင် (သတင်းတုဖြန့်ရန် အသုံးများသော နေရာများ)
243
+ elif "/groups/" in path or "/watch/" in path or "reel" in path:
244
+ return 40, "Social Media Group/Video (Unverified Origin)"
245
+
246
+ # သာမန် Page သို့မဟုတ် Profile ဖြစ်လျှင် (ကြားနေအမှတ်ပေးမည်)
247
+ else:
248
+ return 60, "General Social Media Source (Needs Cross-check)"
249
+
250
+ # --- ၄။ အမည်မသိ Domain များကို WHOIS ဖြင့် သက်တမ်းစစ်ခြင်း (Fake Sites များကို ဖမ်းရန်) ---
251
+ w = whois.whois(domain)
252
+ creation_date = w.creation_date
253
+ if isinstance(creation_date, list):
254
+ creation_date = creation_date[0]
255
+
256
+ if creation_date:
257
+ from datetime import datetime
258
+ age_days = (datetime.now() - creation_date).days
259
+ age_months = age_days // 30
260
+
261
+ if age_days < 180:
262
+ return 15, f"⚠️ Very New/Suspicious Site (Age: {age_months} months)"
263
+ elif age_days < 730:
264
+ return 50, f"Neutral Site (Age: {age_months} months)"
265
+ else:
266
+ return 80, f"Established Site (Age: {age_months // 12} years)"
267
+ else:
268
+ return 30, "Unknown Identity (No Creation Date)"
269
+
270
+ except Exception as e:
271
+ return 20, "Hidden/Suspicious Source Identity"
272
+
273
+ @staticmethod
274
+ def get_nlp_prediction(text):
275
+ """🌟 AI Accuracy ကို ၉၉% ထိ တိုးမြှင့်လိုက်သော logic 🌟"""
276
+ if not text or len(text.split()) < 3: return 10.0
277
+
278
+ # ၁။ AI Model မှ ရလဒ်ယူခြင်း
279
+ inputs = tokenizer(text, max_length=512, padding="max_length", truncation=True, return_tensors="pt").to(device)
280
+ with torch.no_grad():
281
+ outputs = nlp_model(**inputs)
282
+ probs = torch.nn.functional.softmax(outputs.logits, dim=-1)[0]
283
+
284
+ real_score = probs[0].item() * 100
285
+
286
+ # ၂။ 🚨 Case 2 & 3 အတွက် အထူး Red Flags (Heuristics)
287
+ # သတင်းအတုတွေမှာ အမြဲသုံးတဲ့ 'ခြိမ်းခြောက်မှု' နဲ့ 'လောဘဆွမှု' စကားလုံးများ
288
+ urgency_patterns = [
289
+ "ပိတ်သိမ်းသွားမည်", "ပိတ်သိမ်းတော့မယ်", "အမြန်ဆုံး", "လက်ဆင့်ကမ်း",
290
+ "အတွင်းသတင်း", "Update ပြုလုပ်ရပါမည်", "Personal Information",
291
+ "အကောင့်ပိတ်သိမ်း", "ယာယီပိတ်သိမ်း", "မယုံနိုင်စရာ", "၁၀၀% အမှန်"
292
+ ]
293
+
294
+ penalty = 0
295
+ for pattern in urgency_patterns:
296
+ if pattern in text:
297
+ penalty += 20 # တစ်ခုပါတိုင်း ၂၀% လျှော့ချမည်
298
+
299
+ # ၃။ AI က ၉၀% ကျော် အစစ်လို့ပြောရင်တောင် Penalty ပါရင် Score ကို ချက်ချင်းချမည်
300
+ final_score = real_score - penalty
301
+
302
+ # ၄။ Logic Correction (Case 2/3 Fix)
303
+ # AI က သိပ်မသေချာဘူး (၇၅% အောက်) ဆိုရင် 'သံသယဖြစ်ဖွယ်' ဘက်ကို ပိုပို့မည်
304
+ if final_score < 75:
305
+ final_score = final_score * 0.6 # Score ကို ထပ်လျှော့ချခြင်း
306
+
307
+ return round(max(5, final_score), 2)
308
+
309
+ @staticmethod
310
+ def check_rss_similarity(headline, url=""):
311
+ import requests
312
+ if not headline or len(headline) < 10: return 0
313
+
314
+ trusted_domains = [
315
+ "bbc.com/burmese", "rfa.org/burmese", "burmese.voanews.com",
316
+ "mizzima.com", "khitthitnews.org", "dvb.no", "myanmar-now.org",
317
+ "reuters.com", "apnews.com", "nytimes.com", "cnn.com",
318
+ "theguardian.com", "aljazeera.com", "dw.com", "france24.com",
319
+ "bloomberg.com", "wsj.com", "forbes.com","myanmarcelebrity.com"
320
+ ]
321
+
322
+ base_bonus = 0
323
+ if url:
324
+ for domain in trusted_domains:
325
+ if domain in url.lower():
326
+ base_bonus = 90
327
+ break
328
+
329
+ feeds = [
330
+ "https://feeds.bbci.co.uk/news/world/rss.xml",
331
+ "https://www.bbc.com/burmese/index.xml",
332
+ "https://www.rfa.org/burmese/RSS",
333
+ "https://burmese.voanews.com/api/zuy_pv-m_i",
334
+ "https://www.theguardian.com/world/rss",
335
+ "https://rss.nytimes.com/services/xml/rss/nyt/World.xml"
336
+ "https://www.myanmarcelebrity.com/"
337
+ ]
338
+
339
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
340
+ max_sim = 0
341
+
342
+ clean_headline = re.sub(r'[^\\u1000-\\u109F a-zA-Z0-9]', ' ', headline).lower()
343
+ headline_keywords = set([w for w in clean_headline.split() if len(w) > 2])
344
+
345
+ for f_url in feeds:
346
+ try:
347
+ response = requests.get(f_url, headers=headers, timeout=5)
348
+ if response.status_code == 200:
349
+ feed = feedparser.parse(response.content)
350
+ for entry in feed.entries:
351
+ seq_sim = SequenceMatcher(None, headline.lower(), entry.title.lower()).ratio()
352
+ entry_clean = re.sub(r'[^\\u1000-\\u109F a-zA-Z0-9]', ' ', entry.title).lower()
353
+ entry_keywords = set([w for w in entry_clean.split() if len(w) > 2])
354
+ common = headline_keywords.intersection(entry_keywords)
355
+ keyword_sim = len(common) / len(headline_keywords) if headline_keywords else 0
356
+
357
+ max_sim = max(max_sim, seq_sim, keyword_sim)
358
+ if max_sim > 0.85: break
359
+ except: continue
360
+
361
+ rss_val = min(100, max_sim * 160)
362
+ final_score = max(base_bonus, rss_val)
363
+
364
+ return final_score
365
+
366
+
367
+
368
+
369
+ # --- 3. MASTER ENGINE (Final Presentation Version) ---
370
+ def master_detector_v12(text, url, image):
371
+ try:
372
+ v = VerificationSystem()
373
+
374
+ # ၁။ 🚨 Empty Input Check (စာသားမပါလျှင်)
375
+ if not text or not text.strip():
376
+ return "<div style='color:#dc2626; padding:20px; text-align:center; background:#fef2f2; border-radius:10px; border:1px solid #fca5a5;'><b>⚠️ ကျေးဇူးပြု၍ စစ်ဆေးလိုသော သတင်းစာသားကို ထည့်သွင်းပါ။</b></div>"
377
+
378
+ # ၂။ 🚨 Short Text Check ("I don't know" Logic - စာသားတိုလွန်းလျှင်)
379
+ words = text.split()
380
+ if len(words) < 10:
381
+ return f"""
382
+ <div style='color:#d97706; padding:20px; text-align:center; background:#fffbeb; border-radius:10px; border:1px solid #fcd34d;'>
383
+ <b style='font-size:1.1rem;'>⚠️ အချက်အလက် မလုံလောက်ပါ (Insufficient Information)</b><br><br>
384
+ သင်ထည့်သွင်းထားသော စာသားမှာ <b>({len(words)} လုံးသာ)</b> ရှိပြီး တိုတောင်းလွန်းပါသည်။ <br>
385
+ AI မှ တိကျစွာ ဆန်းစစ်နိုင်ရန်အတွက် အနည်းဆုံး စကားလုံး (၁၀) လုံးနှင့်အထက် ပါဝင်သော သတင်းအပြည့်အစုံကို ထည့်သွင်းပေးပါ။
386
+ </div>
387
+ """
388
+
389
+ # URL ပါ၊ မပါ စစ်ဆေးခြင်း
390
+ has_url = bool(url and url.strip() != "")
391
+ is_myanmar = bool(re.search(r'[\u1000-\u109F]', text))
392
+
393
+ # --- 📊 3. UI Progress Bar ဖန်တီးပေးသော Helper Function ---
394
+ def create_bar(label, value, bar_color, is_mm, nlp_label_local, custom_msg=None):
395
+ if custom_msg:
396
+ explainer = custom_msg
397
+ else:
398
+ if label == "Source Trust":
399
+ if not has_url:
400
+ explainer = "ℹ️ URL လင့်ခ် မပါဝင်သဖြင့် သတင်းရင်းမြစ်ကို အတည်ပြု၍ မရပါ။" if is_mm else "ℹ️ No URL provided. Source unverified."
401
+ bar_color = "#94a3b8" # URL မပါလျှင် ခဲရောင်ပြမည်
402
+ else:
403
+ explainer = ("✅ ယုံကြည်ရသော ရင်းမြစ်ဖြစ်သည်။" if value > 75 else "⚠️ ရင်းမြစ် မတည်ငြိမ်ခြင်း (သို့) ဒိုမိန်းသက်တမ်း နုနယ်ခြင်း။") if is_mm else ("✅ Verified source." if value > 75 else "⚠️ Low authority source.")
404
+ elif label == "Global Consistency":
405
+ explainer = ("✅ အခြားမီဒီယာများတွင်လည်း ဖော်ပြထားသည်။" if value > 60 else "ℹ️ အခြားသတင်းဌာနများတွင် အတည်ပြုချက် မတွေ့ရသေးပါ။") if is_mm else ("✅ Corroborated." if value > 60 else "ℹ️ Not corroborated yet.")
406
+ elif label == "AI Pattern Analysis":
407
+ explainer = ("✅ AI မှ သတင်းမှန် အရေးအသားဟု ဆုံးဖြတ်သည်။" if nlp_label_local == "Real" else "⚠️ AI မှ သတင်းတု/Clickbait ဟု သတ်မှတ်သည်။" if nlp_label_local == "Fake" else "ℹ️ AI အတွက် ဆုံးဖြတ်ရန် ခက်ခဲသော ရောထွေးနေသည့် အရေးအသားဖြစ်သည်။") if is_mm else ("✅ Legitimate pattern." if nlp_label_local == "Real" else "⚠️ Misinformation pattern." if nlp_label_local == "Fake" else "ℹ️ Neutral/Mixed pattern.")
408
+ elif label == "Image Integrity":
409
+ explainer = ("✅ ပုံရိပ်မှာ မူရင်းအတိုင်းဖြစ်ပြီး ပြင်ဆင်မှု မတွေ့ရပါ။" if value > 85 else "⚠️ ပုံရိပ်ကို ပြုပြင်ထားသော လက္ခဏာရှိသည်။") if is_mm else ("✅ No tampering." if value > 85 else "⚠️ Potential tampering.")
410
+ elif label == "Visual Context":
411
+ explainer = ("✅ ပုံနှင့်စာသား ကိုက်ညီမှုရှိသည်။" if value > 72 else "⚠️ ပုံနှင့်စာသား တစ်ခြားစီဖြစ်နေသည်။") if is_mm else ("✅ Context matches." if value > 72 else "⚠️ Context mismatch.")
412
+ else: explainer = ""
413
+
414
+ return f"""
415
+ <div style="margin-bottom: 12px;">
416
+ <div style="display: flex; justify-content: space-between; font-size: 0.8rem; color: #475569; margin-bottom: 3px;">
417
+ <span style="font-weight:600;">{label}</span><span>{value:.1f}%</span>
418
+ </div>
419
+ <div style="width: 100%; background: #e2e8f0; border-radius: 10px; height: 7px; overflow: hidden;">
420
+ <div style="width: {value}%; background: {bar_color}; height: 100%; border-radius: 10px;"></div>
421
+ </div>
422
+ <div style="font-size: 0.72rem; color: #1e293b; margin-top: 3px; line-height: 1.3; font-weight: 500;">{explainer}</div>
423
+ </div>"""
424
+
425
+ # --- 🔍 4. Core Metrics Calculations ---
426
+ url_image = v.extract_image_from_url(url) if has_url else None
427
+ src_score, src_msg = v.get_source_score(url) if has_url else (0, "No URL")
428
+ rss_score = v.check_rss_similarity(text, url=url)
429
+ nlp_score = v.get_nlp_prediction(text)
430
+
431
+ # NLP Score Labeling
432
+ if nlp_score >= 65:
433
+ nlp_label = "Real"; nlp_val = nlp_score; ai_c = "#10b981"
434
+ elif nlp_score <= 40:
435
+ nlp_label = "Fake"; nlp_val = 100 - nlp_score; ai_c = "#dc2626"
436
+ else:
437
+ nlp_label = "Neutral"; nlp_val = nlp_score; ai_c = "#94a3b8"
438
+
439
+ # --- 🖼️ 5. Image Processing & Zero-shot Classification ---
440
+ image_bars_html = ""
441
+ img_msg_extra = ""
442
+ sim_msg = ""
443
+
444
+ if image:
445
+ # ပြည်တွင်း/ပြည်ပ မြင်ကွင်းခွဲခြားခြင်း (Zero-shot CLIP)
446
+ try:
447
+ img_obj = Image.open(image)
448
+ loc_prompts = [
449
+ "a photo taken in Myanmar, Burmese streets, pagodas, Asian people, Myanmar culture",
450
+ "a photo taken in a foreign country, Western people, foreign streets, Europe, America, Africa, Middle East"
451
+ ]
452
+ loc_embs = clip_model.encode(loc_prompts)
453
+ img_emb_local = clip_model.encode(img_obj)
454
+ loc_scores = util.cos_sim(img_emb_local, loc_embs)[0]
455
+
456
+ if loc_scores[1] > loc_scores[0] + 0.02:
457
+ img_msg_extra = "<br><span style='color:#dc2626; font-weight:600;'>🌍 AI Visual Scan: ဤပုံသည် ပြည်ပနိုင်ငံမှ မြင်ကွင်းဖြစ်နိုင်ခြေများပါသည်။ (Foreign Image Detected)</span>"
458
+ elif loc_scores[0] > loc_scores[1] + 0.02:
459
+ img_msg_extra = "<br><span style='color:#059669; font-weight:600;'>🇲🇲 AI Visual Scan: ဤပုံသည် ပြည်တွင်းမှ မြင်ကွင်းဖြစ်နိုင်ခြေများပါသည်။ (Domestic Image)</span>"
460
+ except Exception as e:
461
+ pass # Error တက်လျှင် ကျော်သွားမည်
462
+
463
+ img_ela_score, _ = v.perform_ela(image)
464
+ img_sim_score, sim_msg_original = v.get_image_text_similarity(image, text, url=url, url_image_path=url_image)
465
+
466
+ # Combine image similarity message with location detection
467
+ sim_msg = sim_msg_original + img_msg_extra
468
+
469
+ # ⚖️ Dynamic Weighting for Image Case
470
+ if has_url:
471
+ weights = {'source': 0.20, 'nlp': 0.35, 'rss': 0.15, 'ela': 0.10, 'sim': 0.20}
472
+ else:
473
+ # URL မပါလျှင် Source ကို 0 ထားပြီး AI နှင့် Image ကို အလေးပေးမည်
474
+ weights = {'source': 0.0, 'nlp': 0.45, 'rss': 0.15, 'ela': 0.15, 'sim': 0.25}
475
+
476
+ final = (src_score * weights['source']) + (nlp_score * weights['nlp']) + \
477
+ (rss_score * weights['rss']) + (img_ela_score * weights['ela']) + \
478
+ (img_sim_score * weights['sim'])
479
+
480
+ image_bars_html = f"""
481
+ {create_bar("Image Integrity", img_ela_score, "#f59e0b", is_myanmar, nlp_label)}
482
+ {create_bar("Visual Context", img_sim_score, "#06b6d4", is_myanmar, nlp_label, custom_msg=sim_msg)}
483
+ """
484
+ else:
485
+ # ⚖️ Dynamic Weighting for Text-Only Case
486
+ if has_url:
487
+ weights = {'source': 0.35, 'nlp': 0.50, 'rss': 0.15}
488
+ else:
489
+ # ပုံရော၊ URL ရော မပါလျှင် NLP ကို 80% အထိ အလေးပေးမည်
490
+ weights = {'source': 0.0, 'nlp': 0.80, 'rss': 0.20}
491
+
492
+ final = (src_score * weights['source']) + (nlp_score * weights['nlp']) + (rss_score * weights['rss'])
493
+ img_msg = "ℹ️ ပုံမပါဝင်သည့်အတွက် Visual Analysis မပြုလုပ်ပါ။" if is_myanmar else "ℹ️ No image for visual analysis."
494
+ image_bars_html = f'<div style="padding:12px; background:#f1f5f9; border-radius:8px; font-size:0.75rem; color:#475569; text-align:center;">{img_msg}</div>'
495
+
496
+ # --- 🗂️ 6. Final Status & Dynamic Color Logic ---
497
+ if final >= 75:
498
+ main_bg, accent_c, status = "#ecfdf5", "#059669", "✅ ယုံကြည်စိတ်ချရသော သတင်း (RELIABLE VERDICT)"
499
+ elif final >= 45 and final < 75:
500
+ main_bg, accent_c, status = "#f8fafc", "#64748b", "⚖️ အတည်ပြုရန်ခက်ခဲသော သတင်း (INCONCLUSIVE / NEUTRAL)"
501
+ else:
502
+ main_bg, accent_c, status = "#fef2f2", "#dc2626", "🚨 သတင်းတု / အန္တရာယ်ရှိသောသတင်း (FAKE / HIGH RISK)"
503
+
504
+ # --- 📝 7. Analysis & Recommendations ---
505
+ analysis_header = "🔎 အသေးစိတ် ဆန်းစစ်ချက်" if is_myanmar else "🔎 Detailed Reasoning"
506
+ tips_header = "🛡️ အကြံပြုချက်နှင့် သတိပြုရန်" if is_myanmar else "🛡️ Recommendations"
507
+ reasons = []
508
+ tips = []
509
+
510
+ if final >= 75:
511
+ reasons.append("✅ သတင်းရင်းမြစ်နှင့် အချက်အလက်များ ခိုင်မာမှုရှိသည်။")
512
+ tips.append("💡 ဤသတင်းသည် ယုံကြည်စိတ်ချရသဖြင့် ဝေမျှနိုင်ပါသည်။")
513
+ elif final >= 45 and final < 75:
514
+ reasons.append("⚖️ အချက်အလက်များမှာ အမှန်နှင့် အမှား ရောထွေးနေနိုင်ပါသည်။ (သို့) လုံလောက်သော သက်သေအထောက်အထား မတွေ့ရသေးပါ။")
515
+ tips.append("💡 ဤသတင်းကို ချက်ချင်းမယုံကြည်ဘဲ အခြားတရားဝင် မီဒီယာကြီးများတွင် ထပ်မံစစ်ဆေးရန် အကြံပြုအပ်ပါသည်။")
516
+ else:
517
+ if nlp_label == "Fake": reasons.append("⚠️ AI စနစ်မှ ဤစာသားသည် သတင်းအတု/Clickbait ပုံစံဖြစ်နေကြောင်း တွေ့ရှိရသည်။")
518
+
519
+ if not has_url:
520
+ reasons.append("ℹ️ သတင်းရင်းမြစ် (URL) ထည့်သွင်းထားခြင်း မရှိသဖြင့် မူရင်းရင်းမြစ်ကို အတည်ပြုရန် ခက်ခဲပါသည်။")
521
+ elif src_score < 40:
522
+ reasons.append(f"❌ သတင်းရင်းမြစ် ({src_msg}) သည် စိတ်မချရပါ။")
523
+
524
+ tips.append("💡 သတင်းအမှားဖြစ်နိုင်ခြေ အလွန်များသဖြင့် အခြားသူများထံ ဆက်လက်မဝေမျှရန် အသိပေးအပ်ပါသည်။")
525
+
526
+ reasons_html = "".join([f"<li style='margin-bottom:5px;'>{r}</li>" for r in reasons])
527
+ tips_html = "".join([f"<li style='margin-bottom:6px;'>{t}</li>" for t in tips])
528
+
529
+ # --- 🌐 8. Final HTML Output ---
530
+ return f"""
531
+ <div style="background: {main_bg}; padding: 22px; border-radius: 15px; border: 1px solid {accent_c}33; font-family: sans-serif; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
532
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
533
+ <h3 style="margin: 0; color: {accent_c}; font-size: 1.15rem; font-weight: 800;">{status}</h3>
534
+ <span style="background: {accent_c}; color: white; padding: 5px 15px; border-radius: 25px; font-weight: 800;">{final:.1f}%</span>
535
+ </div>
536
+
537
+ <div style="background: white; padding: 18px; border-radius: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.03);">
538
+ <b style="font-size: 0.85rem; color: #1e293b; display: block; margin-bottom: 15px; text-transform: uppercase;">📊 Verification Metrics:</b>
539
+ {create_bar("Source Trust", src_score, "#3b82f6", is_myanmar, nlp_label)}
540
+ {create_bar("Global Consistency", rss_score, "#8b5cf6", is_myanmar, nlp_label)}
541
+ {create_bar("AI Pattern Analysis", nlp_val, ai_c, is_myanmar, nlp_label)}
542
+ <hr style="border: 0; border-top: 1px solid #f1f5f9; margin: 18px 0;">
543
+ {image_bars_html}
544
+ </div>
545
+
546
+ <div style="background: {("#f0fdf4" if final >= 75 else "#f8fafc" if final >= 45 else "#fef2f2")}; padding: 15px; border-radius: 10px; border-left: 5px solid {accent_c}; margin-top: 15px;">
547
+ <b style="color: {accent_c}; display: block; margin-bottom: 8px;">{analysis_header}</b>
548
+ <ul style="margin: 0; padding-left: 20px; color: #1e293b; font-size: 0.95rem;">{reasons_html if reasons_html else "<li>Analysis complete.</li>"}</ul>
549
+ </div>
550
+
551
+ <div style="background: #eff6ff; padding: 15px; border-radius: 10px; border-left: 5px solid #2563eb; margin-top: 12px;">
552
+ <b style="color: #1e40af; display: block; margin-bottom: 8px;">{tips_header}</b>
553
+ <ul style="margin: 0; padding-left: 20px; color: #1e3a8a; font-size: 0.95rem;">{tips_html}</ul>
554
+ </div>
555
+
556
+ </div>
557
+ """
558
+ except Exception as e:
559
+ import traceback
560
+ return f"<div style='color:red; padding:20px; border:1px solid red; border-radius:10px;'><b>System Error:</b> {str(e)}<br><pre style='font-size:0.7rem;'>{traceback.format_exc()}</pre></div>"
561
+
562
+
563
+ # --- 4. MODERN UI DESIGN ---
564
+ custom_css = """
565
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
566
+ @import url('https://mmwebfonts.comquas.com/fonts/?font=pyidaungsu');
567
+ .gradio-container { background-color: #f9fafb !important; font-family: 'Inter', 'Pyidaungsu' !important; }
568
+ .card { background: white !important; border-radius: 20px !important; border: 1px solid #e5e7eb !important; padding: 30px !important; }
569
+ textarea { font-family: 'Pyidaungsu', sans-serif !important; font-size: 16px !important; }
570
+ .btn-primary { background: #111827 !important; color: white !important; border-radius: 10px !important; font-weight: 600 !important; }
571
+ """
572
+
573
+ with gr.Blocks(css=custom_css) as demo:
574
+ gr.HTML("<div style='text-align: center; padding: 20px;'><h1>🛡️ Fake News Detection (Powered by Myanmar AI)</h1></div>")
575
+ with gr.Tabs():
576
+ with gr.TabItem("🔍 Verifier Dashboard"):
577
+ with gr.Row():
578
+ with gr.Column(scale=5, elem_classes="card"):
579
+ txt = gr.Textbox(label="News Content", lines=8, placeholder="သတင်းစာသားကို ဤနေရာတွင် ထည့်ပါ...")
580
+ url = gr.Textbox(label="Source URL (Optional)")
581
+ img = gr.Image(label="Image Attachment", type="filepath")
582
+ with gr.Row():
583
+ clear = gr.Button("Reset Fields")
584
+ submit = gr.Button("Analyze News", variant="primary")
585
+ with gr.Column(scale=5):
586
+ empty_html = "<div style='text-align: center; padding: 100px; color: #9ca3af;'>Ready for analysis...</div>"
587
+ output = gr.HTML(value=empty_html)
588
+
589
+ with gr.TabItem("ℹ️ About System"):
590
+ with gr.Column(elem_classes="card"):
591
+ gr.Markdown(r"""
592
+ # 🛡️ Fake News Detection System Methodology
593
+ ### 📄 1. Project Overview & System Rationale
594
+ ဤ Project သည် သတင်းတု (Fake News) များကို ရှာဖွေရာတွင် စာသားတင်မကဘဲ Context အားလုံးကို ခြုံငုံကြည့်သည့် **Multi-modal Framework** တစ်ခုဖြစ်သည်။ ဤစနစ်သည် သတင်းတစ်ခု၏ စစ်မှန်မှုကို အချက် (၄) ချက်ဖြင့် တိုင်းတာပါသည်။
595
+ 1. **Linguistic Style:** အရေးအသားပုံစံမှာ ဝါဒဖြန့်စာသားဖြစ်နေသလား?
596
+ 2. **Metadata & Source:** သတင်းလာရာ ရင်းမြစ်က ယုံကြည်ရသလား?
597
+ 3. **Visual Integrity:** သတင်းတွင်ပါသော ပုံသည် ပြုပြင်ထားသလား သို့မဟုတ် စာသားနှင့် ကိုက်ညီမှုရှိသလား?
598
+ 4. **Global consistency:** အခြားသော မီဒီယာကြီးများမှာ ဖော်ပြထားခြင်းရှိသလား?
599
+
600
+ ---
601
+
602
+ ### 🧠 2. Core Algorithms & Methodology
603
+
604
+ #### **A. Transformer-based Classification (XLM-RoBERTa)**
605
+ * **Algorithm:** *XLM-RoBERTa (Cross-lingual Language Model)*
606
+ * **Implementation:** ဤ Model သည် မြန်မာစာ သတင်းမှန်နှင့် သတင်းအတု Data ထောင်ပေါင်းများစွာကို ကိုယ်တိုင် သင်ယူ (Fine-tuned) ထားသော ကိုယ်ပိုင် AI စနစ်ဖြစ်သည်။ ဘာသာပြန်စရာမလိုဘဲ မြန်မာစာကို တိုက်ရိုက် နားလည်စစ်ဆေးနိုင်သည်။
607
+
608
+ #### **B. Visual Forensics (ELA & CLIP)**
609
+ * **Error Level Analysis (ELA):** JPEG ပုံရိပ်တစ်ခုကို ပြန်သိမ်းသည့်အခါ ပြုပြင်ထားသော Pixel များသည် Error Level ကွဲပြားသွားခြင်းကို အခြေခံ၍ ပုံပြင်/မပြင်ကို စစ်ဆေးသည်။
610
+ * **CLIP (Contrastive Language-Image Pre-training):** NLP နည်းပညာကို အသုံးပြု၍ စာသားထဲတွင် ပါဝင်သော "အကြောင်းအရာ" နှင့် ရုပ်ပုံထဲတွင် မြင်တွေ့ရသော "အမြင်အာရုံဆိုင်ရာ သဘောတရား (Visual Concept)" တို့၏ ကိုက်ညီမှုကို Text-Image Embedding Alignment နည်းလမ်းဖြင့် တိုင်းတာသည်။
611
+
612
+ #### **C. Source Verification & RSS Matching**
613
+ * **Whois Analysis:** Domain ၏ သက်တမ်းကို စစ်ဆေးသည်။ သတင်းအတုဆိုဒ်အများစုမှာ သက်တမ်း (၆) လအောက်သာ ရှိတတ်သည်။
614
+ * **RSS Feed Comparison:** BBC, RFA, VOA စသည့် ယုံကြည်ရသော သတင်းဌာနကြီးများ၏ လက်ရှိသတင်းခေါင်းစဉ်များနှင့် သင့်သတင်းကို တိုက်ဆိုင်စစ်ဆေးပြီး ���ခြားမီဒီယာတွင် ပါ၊ မပါ ဆုံးဖြတ်သည်။
615
+
616
+ ---
617
+
618
+ ### ⚙️ 3. Mathematical Scoring Model
619
+ စနစ်မှ ရရှိလာသော Metrics တစ်ခုချင်းစီကို အောက်ပါ **Weighted Average Formula** ဖြင့် ပေါင်းစပ်ကာ Confidence Score ထုတ်ပေးပါသည်။
620
+
621
+ $$Score = (W_{nlp} \cdot NLP) + (W_{src} \cdot Source) + (W_{rss} \cdot RSS) + (W_{vis} \cdot Visual)$$
622
+
623
+ | Metric | Weight (With Image) | Weight (Text Only) |
624
+ | :--- | :--- | :--- |
625
+ | **AI Pattern (NLP)** | 30% | 45% |
626
+ | **Source Authority** | 25% | 45% |
627
+ | **Visual Forensics** | 10% | - |
628
+ | **Content Consistency**| 20% | - |
629
+ | **Global Consensus** | 15% | 10% |
630
+
631
+ ---
632
+
633
+ ### 🎯 4. Project Deliverables
634
+ * ✅ **Hybrid Detection:** စာသားရော ပုံပါ စစ်ဆေးနိုင်ခြင်း။
635
+ * ✅ **Evidence-Based Reasoning:** အဖြေတစ်ခုတည်း မဟုတ်ဘဲ အကြောင်းပြချက်ပါ ဖော်ပြခြင်း။
636
+ * ✅ **Burmese Language Support:** မြန်မာစာသားများကို တိုက်ရိုက် နားလည်ထောက်ပံ့ပေးခြင်း။
637
+ """)
638
+
639
+ submit.click(master_detector_v12, inputs=[txt, url, img], outputs=output)
640
+ clear.click(lambda: ["", "", None, empty_html], outputs=[txt, url, img, output])
641
+
642
+ if __name__ == "__main__":
643
+ demo.launch(share=True)