Spaces:
Sleeping
Sleeping
Create app.py
Browse files
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)
|