Spaces:
Sleeping
Sleeping
Commit ·
fa9bd1a
1
Parent(s): 255a427
flask-themed UI with desktop/mobile tabs
Browse files
app.py
CHANGED
|
@@ -26,10 +26,29 @@ print("Loading SHAP explainer...")
|
|
| 26 |
explainer = SmishingExplainer(predictor)
|
| 27 |
print("Model loaded. Ready.")
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
@spaces.GPU(duration=120)
|
| 30 |
def analyze(message):
|
| 31 |
if not message or not message.strip():
|
| 32 |
-
return "⚠️ Please enter a message.", None, None
|
| 33 |
|
| 34 |
result = predictor.predict(message)
|
| 35 |
|
|
@@ -114,36 +133,79 @@ examples = [
|
|
| 114 |
"Mom, I landed safely. Will call from the hotel in an hour.",
|
| 115 |
]
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
|
| 149 |
if __name__ == "__main__":
|
|
|
|
| 26 |
explainer = SmishingExplainer(predictor)
|
| 27 |
print("Model loaded. Ready.")
|
| 28 |
|
| 29 |
+
CSS = """
|
| 30 |
+
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap');
|
| 31 |
+
.gradio-container { font-family: 'Plus Jakarta Sans', sans-serif !important; max-width: 920px !important; margin: 0 auto !important; }
|
| 32 |
+
.ss-hero { text-align: center; padding: 2rem 0 1.2rem; }
|
| 33 |
+
.ss-hero h1 { font-size: 2.3rem; font-weight: 800; letter-spacing: -0.5px; margin: 0; }
|
| 34 |
+
.ss-hero h1 span { color: #3b82f6; }
|
| 35 |
+
.ss-hero p { opacity: 0.65; font-size: 0.92rem; margin-top: 0.4rem; }
|
| 36 |
+
.ss-badge { display: inline-flex; align-items: center; gap: 0.35rem; background: rgba(59,130,246,.12);
|
| 37 |
+
border: 1px solid rgba(59,130,246,.35); color: #3b82f6; font-size: 0.72rem; font-weight: 700;
|
| 38 |
+
padding: 0.25rem 0.7rem; border-radius: 20px; margin-bottom: 0.8rem; }
|
| 39 |
+
.phone-frame { max-width: 392px !important; margin: 0 auto !important; border: 10px solid #0b0e13;
|
| 40 |
+
border-radius: 34px; padding: 1rem 0.9rem 1.3rem !important; box-shadow: 0 14px 44px rgba(0,0,0,.35); }
|
| 41 |
+
.phone-notch { width: 120px; height: 22px; background: #0b0e13; border-radius: 0 0 12px 12px; margin: -1rem auto 0.7rem; }
|
| 42 |
+
.phone-title { text-align: center; font-weight: 800; font-size: 1rem; margin-bottom: 0.2rem; }
|
| 43 |
+
.phone-sub { text-align: center; font-size: 0.72rem; opacity: 0.6; margin-bottom: 0.8rem; }
|
| 44 |
+
.ss-footer { text-align: center; font-size: 0.75rem; opacity: 0.6; margin-top: 1.6rem; line-height: 1.7; }
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
|
| 48 |
@spaces.GPU(duration=120)
|
| 49 |
def analyze(message):
|
| 50 |
if not message or not message.strip():
|
| 51 |
+
return "⚠️ Please enter a message.", None, None, None
|
| 52 |
|
| 53 |
result = predictor.predict(message)
|
| 54 |
|
|
|
|
| 133 |
"Mom, I landed safely. Will call from the hotel in an hour.",
|
| 134 |
]
|
| 135 |
|
| 136 |
+
seed_labels = ["Seed 1 · EN spam (KYC)", "Seed 2 · EN ham (bank)", "Seed 3 · Hinglish spam (bijli)",
|
| 137 |
+
"Seed 4 · EN ham (recharge)", "Seed 5 · Hinglish spam (CBI)", "Seed 6 · EN ham (UPI)",
|
| 138 |
+
"Seed 7 · Hindi spam (lottery)", "Seed 8 · EN ham (OTP)", "Seed 9 · EN spam (UPI)",
|
| 139 |
+
"Seed 10 · EN ham (personal)"]
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def _pick_seed(choice):
|
| 143 |
+
try:
|
| 144 |
+
idx = seed_labels.index(choice)
|
| 145 |
+
except ValueError:
|
| 146 |
+
idx = 0
|
| 147 |
+
return examples[idx]
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
with gr.Blocks(title="ScamShield — Smishing Detector", css=CSS,
|
| 151 |
+
theme=gr.themes.Soft(primary_hue="blue", neutral_hue="slate")) as demo:
|
| 152 |
+
gr.HTML("""<div class="ss-hero">
|
| 153 |
+
<div class="ss-badge">🛡️ AI · Cybersecurity — Smishing Detection</div>
|
| 154 |
+
<h1>Detect <span>Scams</span> Before They Strike</h1>
|
| 155 |
+
<p>XLM-RoBERTa + URL fusion + SHAP · English · Hindi · Hinglish · Threshold 0.55</p>
|
| 156 |
+
</div>""")
|
| 157 |
+
|
| 158 |
+
with gr.Tabs():
|
| 159 |
+
with gr.TabItem("Desktop"):
|
| 160 |
+
with gr.Row():
|
| 161 |
+
with gr.Column(scale=2):
|
| 162 |
+
inp = gr.Textbox(label="SMS Message", placeholder="Paste the SMS message here...",
|
| 163 |
+
lines=4, max_lines=10)
|
| 164 |
+
btn = gr.Button("🔍 Analyze", variant="primary", size="lg")
|
| 165 |
+
gr.Examples(examples=examples, inputs=inp, label="Try these examples")
|
| 166 |
+
|
| 167 |
+
with gr.Column(scale=3):
|
| 168 |
+
verdict = gr.Markdown(label="Verdict")
|
| 169 |
+
with gr.Tabs():
|
| 170 |
+
with gr.TabItem("URL Analysis"):
|
| 171 |
+
url_box = gr.Markdown()
|
| 172 |
+
with gr.TabItem("Text Signals"):
|
| 173 |
+
text_box = gr.Markdown()
|
| 174 |
+
with gr.TabItem("SHAP Explainability"):
|
| 175 |
+
shap_box = gr.Markdown()
|
| 176 |
+
|
| 177 |
+
btn.click(fn=analyze, inputs=inp,
|
| 178 |
+
outputs=[verdict, url_box, text_box, shap_box], api_name="predict")
|
| 179 |
+
inp.submit(fn=analyze, inputs=inp,
|
| 180 |
+
outputs=[verdict, url_box, text_box, shap_box])
|
| 181 |
+
|
| 182 |
+
with gr.TabItem("Mobile"):
|
| 183 |
+
gr.Markdown("Phone-style scanner — same live model. Full app adds the 100-message seeded inbox + Scan All; "
|
| 184 |
+
"pick a seed below or paste any SMS.")
|
| 185 |
+
with gr.Column(elem_classes="phone-frame"):
|
| 186 |
+
gr.HTML('<div class="phone-notch"></div><div class="phone-title">🛡️ ScamShield</div>'
|
| 187 |
+
'<div class="phone-sub">Seeded inbox preview · live analysis</div>')
|
| 188 |
+
seed = gr.Dropdown(choices=seed_labels, value=seed_labels[0], label="Seeded message")
|
| 189 |
+
minp = gr.Textbox(label="SMS Message", lines=3, max_lines=6)
|
| 190 |
+
mbtn = gr.Button("🔍 Scan", variant="primary")
|
| 191 |
+
mverdict = gr.Markdown(label="Verdict")
|
| 192 |
+
with gr.Tabs():
|
| 193 |
+
with gr.TabItem("URLs"):
|
| 194 |
+
murl = gr.Markdown()
|
| 195 |
+
with gr.TabItem("Signals"):
|
| 196 |
+
mtext = gr.Markdown()
|
| 197 |
+
with gr.TabItem("SHAP"):
|
| 198 |
+
mshap = gr.Markdown()
|
| 199 |
+
|
| 200 |
+
seed.change(fn=_pick_seed, inputs=seed, outputs=minp, api_name=False)
|
| 201 |
+
mbtn.click(fn=analyze, inputs=minp,
|
| 202 |
+
outputs=[mverdict, murl, mtext, mshap], api_name="predict_mobile")
|
| 203 |
+
|
| 204 |
+
gr.HTML("""<div class="ss-footer">
|
| 205 |
+
<b>Model:</b> XLM-RoBERTa (xlm-roberta-base) · <b>Threshold:</b> 0.55 ·
|
| 206 |
+
<b>Languages:</b> English · Hindi · Hinglish · <b>Features:</b> 9 URL + 8 text signals · <b>Spam F1:</b> 0.94<br/>
|
| 207 |
+
Full Android app (.apk) via EAS build — same API underneath.
|
| 208 |
+
</div>""")
|
| 209 |
|
| 210 |
|
| 211 |
if __name__ == "__main__":
|