GoshawkVortexAI commited on
Commit
601c03e
·
verified ·
1 Parent(s): e13524e

Upload app_fixed.py

Browse files
Files changed (1) hide show
  1. app_fixed.py +1181 -0
app_fixed.py ADDED
@@ -0,0 +1,1181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import time
4
+ import sqlite3
5
+ import hashlib
6
+ import secrets
7
+ from datetime import date
8
+
9
+ import gradio as gr
10
+ import torch
11
+
12
+ from transformers import AutoTokenizer
13
+ from peft import AutoPeftModelForCausalLM
14
+
15
+
16
+ # ============================================================
17
+ # CONFIG
18
+ # ============================================================
19
+
20
+ MODEL_PATH = "."
21
+
22
+ DB_PATH = "users.db"
23
+
24
+ FREE_DAILY_LIMIT = 10
25
+ PRO_DAILY_LIMIT = 1000
26
+
27
+ ADMIN_EMAIL = os.getenv(
28
+ "ADMIN_EMAIL",
29
+ "admin@example.com"
30
+ )
31
+
32
+ ADMIN_PASSWORD = os.getenv(
33
+ "ADMIN_PASSWORD",
34
+ "CHANGE_THIS_PASSWORD"
35
+ )
36
+
37
+
38
+ # ============================================================
39
+ # DATABASE
40
+ # ============================================================
41
+
42
+ def db():
43
+ conn = sqlite3.connect(
44
+ DB_PATH,
45
+ check_same_thread=False
46
+ )
47
+ conn.row_factory = sqlite3.Row
48
+ return conn
49
+
50
+
51
+ def init_db():
52
+
53
+ conn = db()
54
+
55
+ conn.execute("""
56
+ CREATE TABLE IF NOT EXISTS users (
57
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
58
+ email TEXT UNIQUE NOT NULL,
59
+ password_hash TEXT NOT NULL,
60
+ salt TEXT NOT NULL,
61
+ plan TEXT DEFAULT 'free',
62
+ active INTEGER DEFAULT 1,
63
+ daily_usage INTEGER DEFAULT 0,
64
+ usage_date TEXT,
65
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
66
+ )
67
+ """)
68
+
69
+ conn.commit()
70
+
71
+ existing = conn.execute(
72
+ "SELECT id FROM users WHERE email = ?",
73
+ (ADMIN_EMAIL.lower().strip(),)
74
+ ).fetchone()
75
+
76
+ if not existing:
77
+
78
+ password_hash, salt = hash_password(
79
+ ADMIN_PASSWORD
80
+ )
81
+
82
+ conn.execute("""
83
+ INSERT INTO users
84
+ (email, password_hash, salt, plan, active)
85
+ VALUES (?, ?, ?, 'admin', 1)
86
+ """, (
87
+ ADMIN_EMAIL.lower().strip(),
88
+ password_hash,
89
+ salt
90
+ ))
91
+
92
+ conn.commit()
93
+
94
+ conn.close()
95
+
96
+
97
+ # ============================================================
98
+ # PASSWORD
99
+ # ============================================================
100
+
101
+ def hash_password(password, salt=None):
102
+
103
+ if salt is None:
104
+ salt = secrets.token_hex(16)
105
+
106
+ password_hash = hashlib.pbkdf2_hmac(
107
+ "sha256",
108
+ password.encode("utf-8"),
109
+ salt.encode("utf-8"),
110
+ 200_000
111
+ ).hex()
112
+
113
+ return password_hash, salt
114
+
115
+
116
+ def verify_password(
117
+ password,
118
+ stored_hash,
119
+ salt
120
+ ):
121
+
122
+ calculated, _ = hash_password(
123
+ password,
124
+ salt
125
+ )
126
+
127
+ return secrets.compare_digest(
128
+ calculated,
129
+ stored_hash
130
+ )
131
+
132
+
133
+ # ============================================================
134
+ # USERS
135
+ # ============================================================
136
+
137
+ def get_user(email):
138
+
139
+ conn = db()
140
+
141
+ user = conn.execute(
142
+ "SELECT * FROM users WHERE email = ?",
143
+ (email.lower().strip(),)
144
+ ).fetchone()
145
+
146
+ conn.close()
147
+
148
+ return user
149
+
150
+
151
+ def create_user(email, password):
152
+
153
+ email = email.lower().strip()
154
+
155
+ if not re.match(
156
+ r"^[^@\s]+@[^@\s]+\.[^@\s]+$",
157
+ email
158
+ ):
159
+ return False, "Geçerli bir e-posta adresi gir."
160
+
161
+ if len(password) < 8:
162
+ return False, "Şifre en az 8 karakter olmalı."
163
+
164
+ if get_user(email):
165
+ return False, "Bu e-posta zaten kayıtlı."
166
+
167
+ password_hash, salt = hash_password(password)
168
+
169
+ conn = db()
170
+
171
+ conn.execute("""
172
+ INSERT INTO users
173
+ (email, password_hash, salt, plan, active)
174
+ VALUES (?, ?, ?, 'free', 1)
175
+ """, (
176
+ email,
177
+ password_hash,
178
+ salt
179
+ ))
180
+
181
+ conn.commit()
182
+ conn.close()
183
+
184
+ return True, "Hesap oluşturuldu."
185
+
186
+
187
+ def login_user(email, password):
188
+
189
+ user = get_user(email)
190
+
191
+ if not user:
192
+ return None, "E-posta veya şifre hatalı."
193
+
194
+ if not verify_password(
195
+ password,
196
+ user["password_hash"],
197
+ user["salt"]
198
+ ):
199
+ return None, "E-posta veya şifre hatalı."
200
+
201
+ if not user["active"]:
202
+ return None, "Hesabın pasif durumda."
203
+
204
+ today = str(date.today())
205
+
206
+ if user["usage_date"] != today:
207
+
208
+ conn = db()
209
+
210
+ conn.execute("""
211
+ UPDATE users
212
+ SET daily_usage = 0,
213
+ usage_date = ?
214
+ WHERE id = ?
215
+ """, (
216
+ today,
217
+ user["id"]
218
+ ))
219
+
220
+ conn.commit()
221
+ conn.close()
222
+
223
+ user = get_user(email)
224
+
225
+ return user, "Giriş başarılı."
226
+
227
+
228
+ # ============================================================
229
+ # LIMIT
230
+ # ============================================================
231
+
232
+ def get_limit(plan):
233
+
234
+ if plan == "admin":
235
+ return 999999
236
+
237
+ if plan == "pro":
238
+ return PRO_DAILY_LIMIT
239
+
240
+ return FREE_DAILY_LIMIT
241
+
242
+
243
+ def can_use(user):
244
+
245
+ today = str(date.today())
246
+
247
+ if user["usage_date"] != today:
248
+ return True
249
+
250
+ return (
251
+ user["daily_usage"]
252
+ <
253
+ get_limit(user["plan"])
254
+ )
255
+
256
+
257
+ def increment_usage(user_id):
258
+
259
+ conn = db()
260
+
261
+ conn.execute("""
262
+ UPDATE users
263
+ SET daily_usage = daily_usage + 1,
264
+ usage_date = ?
265
+ WHERE id = ?
266
+ """, (
267
+ str(date.today()),
268
+ user_id
269
+ ))
270
+
271
+ conn.commit()
272
+ conn.close()
273
+
274
+
275
+ # ============================================================
276
+ # MODEL
277
+ # ============================================================
278
+
279
+ print("Loading model...")
280
+
281
+ DEVICE = (
282
+ "cuda"
283
+ if torch.cuda.is_available()
284
+ else "cpu"
285
+ )
286
+
287
+ print("Device:", DEVICE)
288
+
289
+ adapter_file = os.path.join(
290
+ MODEL_PATH,
291
+ "adapter_model.safetensors"
292
+ )
293
+
294
+ if not os.path.exists(adapter_file):
295
+
296
+ raise RuntimeError(
297
+ "adapter_model.safetensors bulunamadı. "
298
+ "PEFT adapter ağırlığını Space'e yükle."
299
+ )
300
+
301
+
302
+ tokenizer = AutoTokenizer.from_pretrained(
303
+ MODEL_PATH
304
+ )
305
+
306
+ model = AutoPeftModelForCausalLM.from_pretrained(
307
+ MODEL_PATH,
308
+ dtype=(
309
+ torch.float16
310
+ if DEVICE == "cuda"
311
+ else torch.float32
312
+ )
313
+ )
314
+
315
+ model.to(DEVICE)
316
+ model.eval()
317
+
318
+ print("Model loaded.")
319
+
320
+
321
+ # ============================================================
322
+ # CRYPTO ASSET DETECTION
323
+ # ============================================================
324
+
325
+ KNOWN_ASSETS = {
326
+
327
+ "BTC": [
328
+ "bitcoin",
329
+ "btc"
330
+ ],
331
+
332
+ "ETH": [
333
+ "ethereum",
334
+ "eth"
335
+ ],
336
+
337
+ "SOL": [
338
+ "solana",
339
+ "sol"
340
+ ],
341
+
342
+ "BNB": [
343
+ "binance coin",
344
+ "bnb"
345
+ ],
346
+
347
+ "XRP": [
348
+ "ripple",
349
+ "xrp"
350
+ ],
351
+
352
+ "ADA": [
353
+ "cardano",
354
+ "ada"
355
+ ],
356
+
357
+ "DOGE": [
358
+ "dogecoin",
359
+ "doge"
360
+ ],
361
+
362
+ "AVAX": [
363
+ "avalanche",
364
+ "avax"
365
+ ],
366
+
367
+ "LINK": [
368
+ "chainlink",
369
+ "link"
370
+ ],
371
+
372
+ "DOT": [
373
+ "polkadot",
374
+ "dot"
375
+ ]
376
+ }
377
+
378
+
379
+ def detect_assets(text):
380
+
381
+ lower = text.lower()
382
+
383
+ detected = []
384
+
385
+ for symbol, names in KNOWN_ASSETS.items():
386
+
387
+ for name in names:
388
+
389
+ pattern = (
390
+ r"(?<![a-z])"
391
+ + re.escape(name)
392
+ + r"(?![a-z])"
393
+ )
394
+
395
+ if re.search(pattern, lower):
396
+
397
+ detected.append(symbol)
398
+ break
399
+
400
+ return detected
401
+
402
+
403
+ # ============================================================
404
+ # SENTIMENT EXTRACTION
405
+ # ============================================================
406
+
407
+ def extract_sentiment(text):
408
+
409
+ lower = text.lower()
410
+
411
+ # Strong explicit labels
412
+ if re.search(
413
+ r"\bbullish\b|\bpositive\b",
414
+ lower
415
+ ):
416
+ return "Bullish"
417
+
418
+ if re.search(
419
+ r"\bbearish\b|\bnegative\b",
420
+ lower
421
+ ):
422
+ return "Bearish"
423
+
424
+ if re.search(
425
+ r"\bneutral\b|\bsideways\b",
426
+ lower
427
+ ):
428
+ return "Neutral"
429
+
430
+ # Keyword fallback
431
+ bullish_words = [
432
+ "rise",
433
+ "rising",
434
+ "surge",
435
+ "surging",
436
+ "gain",
437
+ "gains",
438
+ "growth",
439
+ "breakout",
440
+ "bull",
441
+ "buy",
442
+ "buying",
443
+ "inflow",
444
+ "inflows",
445
+ "adoption",
446
+ "strong",
447
+ "record high",
448
+ "higher"
449
+ ]
450
+
451
+ bearish_words = [
452
+ "fall",
453
+ "falling",
454
+ "drop",
455
+ "dropping",
456
+ "crash",
457
+ "loss",
458
+ "losses",
459
+ "sell",
460
+ "selling",
461
+ "outflow",
462
+ "outflows",
463
+ "bear",
464
+ "resistance",
465
+ "hack",
466
+ "liquidation",
467
+ "lower",
468
+ "weak"
469
+ ]
470
+
471
+ bullish_score = sum(
472
+ word in lower
473
+ for word in bullish_words
474
+ )
475
+
476
+ bearish_score = sum(
477
+ word in lower
478
+ for word in bearish_words
479
+ )
480
+
481
+ if bullish_score > bearish_score:
482
+ return "Bullish"
483
+
484
+ if bearish_score > bullish_score:
485
+ return "Bearish"
486
+
487
+ return "Neutral"
488
+
489
+
490
+ # ============================================================
491
+ # SENTIMENT SCORE
492
+ # ============================================================
493
+
494
+ def sentiment_score(
495
+ sentiment,
496
+ text
497
+ ):
498
+
499
+ lower = text.lower()
500
+
501
+ bullish_words = [
502
+ "surge",
503
+ "breakout",
504
+ "bullish",
505
+ "strong",
506
+ "inflow",
507
+ "growth",
508
+ "rally",
509
+ "higher",
510
+ "buy"
511
+ ]
512
+
513
+ bearish_words = [
514
+ "crash",
515
+ "bearish",
516
+ "weak",
517
+ "outflow",
518
+ "liquidation",
519
+ "hack",
520
+ "lower",
521
+ "sell"
522
+ ]
523
+
524
+ bull = sum(
525
+ x in lower
526
+ for x in bullish_words
527
+ )
528
+
529
+ bear = sum(
530
+ x in lower
531
+ for x in bearish_words
532
+ )
533
+
534
+ raw = bull - bear
535
+
536
+ if sentiment == "Bullish":
537
+ raw += 1
538
+
539
+ elif sentiment == "Bearish":
540
+ raw -= 1
541
+
542
+ score = max(
543
+ -1,
544
+ min(
545
+ 1,
546
+ raw / 5
547
+ )
548
+ )
549
+
550
+ return score
551
+
552
+
553
+ # ============================================================
554
+ # STRENGTH
555
+ # ============================================================
556
+
557
+ def get_strength(score):
558
+
559
+ absolute = abs(score)
560
+
561
+ if absolute >= 0.70:
562
+ return "STRONG"
563
+
564
+ if absolute >= 0.35:
565
+ return "MODERATE"
566
+
567
+ return "WEAK"
568
+
569
+
570
+ # ============================================================
571
+ # RISK
572
+ # ============================================================
573
+
574
+ def get_risk(
575
+ text,
576
+ score
577
+ ):
578
+
579
+ lower = text.lower()
580
+
581
+ high_risk_words = [
582
+ "hack",
583
+ "exploit",
584
+ "crash",
585
+ "liquidation",
586
+ "lawsuit",
587
+ "ban",
588
+ "bankruptcy",
589
+ "rug"
590
+ ]
591
+
592
+ medium_risk_words = [
593
+ "volatility",
594
+ "resistance",
595
+ "uncertain",
596
+ "uncertainty",
597
+ "regulation"
598
+ ]
599
+
600
+ if any(
601
+ word in lower
602
+ for word in high_risk_words
603
+ ):
604
+ return "HIGH"
605
+
606
+ if any(
607
+ word in lower
608
+ for word in medium_risk_words
609
+ ):
610
+ return "MEDIUM"
611
+
612
+ if abs(score) > 0.7:
613
+ return "MEDIUM"
614
+
615
+ return "LOW"
616
+
617
+
618
+ # ============================================================
619
+ # MARKET IMPACT
620
+ # ============================================================
621
+
622
+ def get_market_impact(
623
+ sentiment,
624
+ score
625
+ ):
626
+
627
+ if abs(score) >= 0.70:
628
+ strength = "High"
629
+
630
+ elif abs(score) >= 0.35:
631
+ strength = "Moderate"
632
+
633
+ else:
634
+ strength = "Low"
635
+
636
+ if sentiment == "Bullish":
637
+ return f"Positive ({strength})"
638
+
639
+ if sentiment == "Bearish":
640
+ return f"Negative ({strength})"
641
+
642
+ return f"Neutral ({strength})"
643
+
644
+
645
+ # ============================================================
646
+ # SIGNAL BAR
647
+ # ============================================================
648
+
649
+ def bar(value):
650
+
651
+ value = max(
652
+ 0,
653
+ min(
654
+ 100,
655
+ int(value)
656
+ )
657
+ )
658
+
659
+ filled = int(
660
+ value / 5
661
+ )
662
+
663
+ return (
664
+ "█" * filled
665
+ +
666
+ "░" * (20 - filled)
667
+ )
668
+
669
+
670
+ # ============================================================
671
+ # DETAILED ANALYSIS
672
+ # ============================================================
673
+
674
+ def analyze_crypto(
675
+ text,
676
+ user
677
+ ):
678
+
679
+ start = time.perf_counter()
680
+
681
+ if not text or not text.strip():
682
+
683
+ return "Lütfen analiz edilecek metni gir."
684
+
685
+ if user is None:
686
+
687
+ return "Önce giriş yapmalısın."
688
+
689
+ current_user = get_user(
690
+ user["email"]
691
+ )
692
+
693
+ if not current_user:
694
+
695
+ return "Oturum bulunamadı."
696
+
697
+ if not current_user["active"]:
698
+
699
+ return "Hesabın aktif değil."
700
+
701
+ if not can_use(current_user):
702
+
703
+ limit = get_limit(
704
+ current_user["plan"]
705
+ )
706
+
707
+ return (
708
+ f"## ⚠️ Günlük Limit Doldu\n\n"
709
+ f"**Plan:** "
710
+ f"{current_user['plan'].upper()}\n\n"
711
+ f"**Limit:** {limit} analiz/gün"
712
+ )
713
+
714
+
715
+ # --------------------------------------------------------
716
+ # MODEL
717
+ # --------------------------------------------------------
718
+
719
+ prompt = f"""
720
+ You are a cryptocurrency sentiment analysis model.
721
+
722
+ Analyze the following crypto market text.
723
+
724
+ Classify the sentiment as exactly one:
725
+ Bullish
726
+ Neutral
727
+ Bearish
728
+
729
+ Return the sentiment label clearly.
730
+
731
+ Text:
732
+ {text}
733
+
734
+ Sentiment:
735
+ """
736
+
737
+
738
+ inputs = tokenizer(
739
+ prompt,
740
+ return_tensors="pt",
741
+ truncation=True,
742
+ max_length=512
743
+ )
744
+
745
+ inputs = {
746
+ k: v.to(DEVICE)
747
+ for k, v in inputs.items()
748
+ }
749
+
750
+
751
+ with torch.no_grad():
752
+
753
+ output = model.generate(
754
+ **inputs,
755
+ max_new_tokens=20,
756
+ do_sample=False
757
+ )
758
+
759
+
760
+ generated = output[
761
+ 0
762
+ ][
763
+ inputs["input_ids"].shape[1]:
764
+ ]
765
+
766
+
767
+ raw_result = tokenizer.decode(
768
+ generated,
769
+ skip_special_tokens=True
770
+ ).strip()
771
+
772
+
773
+ # --------------------------------------------------------
774
+ # ANALYSIS
775
+ # --------------------------------------------------------
776
+
777
+ sentiment = extract_sentiment(
778
+ raw_result
779
+ +
780
+ " "
781
+ +
782
+ text
783
+ )
784
+
785
+ score = sentiment_score(
786
+ sentiment,
787
+ text
788
+ )
789
+
790
+ strength = get_strength(
791
+ score
792
+ )
793
+
794
+ risk = get_risk(
795
+ text,
796
+ score
797
+ )
798
+
799
+ impact = get_market_impact(
800
+ sentiment,
801
+ score
802
+ )
803
+
804
+ assets = detect_assets(
805
+ text
806
+ )
807
+
808
+
809
+ # --------------------------------------------------------
810
+ # DISPLAY SCORES
811
+ # --------------------------------------------------------
812
+
813
+ if sentiment == "Bullish":
814
+
815
+ bullish = 70 + abs(score) * 25
816
+ bearish = 10
817
+ neutral = 100 - bullish - bearish
818
+
819
+ elif sentiment == "Bearish":
820
+
821
+ bearish = 70 + abs(score) * 25
822
+ bullish = 10
823
+ neutral = 100 - bearish - bullish
824
+
825
+ else:
826
+
827
+ neutral = 60 + (1 - abs(score)) * 20
828
+ bullish = (100 - neutral) / 2
829
+ bearish = bullish
830
+
831
+
832
+ bullish = max(
833
+ 0,
834
+ min(
835
+ 100,
836
+ bullish
837
+ )
838
+ )
839
+
840
+ bearish = max(
841
+ 0,
842
+ min(
843
+ 100,
844
+ bearish
845
+ )
846
+ )
847
+
848
+ neutral = max(
849
+ 0,
850
+ 100 - bullish - bearish
851
+ )
852
+
853
+
854
+ # --------------------------------------------------------
855
+ # DIRECTION ICON
856
+ # --------------------------------------------------------
857
+
858
+ if sentiment == "Bullish":
859
+ icon = "🟢"
860
+
861
+ elif sentiment == "Bearish":
862
+ icon = "🔴"
863
+
864
+ else:
865
+ icon = "🟡"
866
+
867
+
868
+ elapsed = (
869
+ time.perf_counter()
870
+ - start
871
+ )
872
+
873
+
874
+ # --------------------------------------------------------
875
+ # USAGE
876
+ # --------------------------------------------------------
877
+
878
+ increment_usage(
879
+ current_user["id"]
880
+ )
881
+
882
+ new_user = get_user(
883
+ current_user["email"]
884
+ )
885
+
886
+
887
+ # --------------------------------------------------------
888
+ # ASSET TEXT
889
+ # --------------------------------------------------------
890
+
891
+ asset_text = (
892
+ ", ".join(assets)
893
+ if assets
894
+ else "Belirlenmedi"
895
+ )
896
+
897
+
898
+ # --------------------------------------------------------
899
+ # SCORE TEXT
900
+ # --------------------------------------------------------
901
+
902
+ score_text = (
903
+ f"{score:+.2f}"
904
+ )
905
+
906
+
907
+ # --------------------------------------------------------
908
+ # FINAL REPORT
909
+ # --------------------------------------------------------
910
+
911
+ report = f"""
912
+ # {icon} CRYPTO SENTIMENT ANALYSIS
913
+
914
+ ## Genel Sinyal
915
+
916
+ ### **{sentiment.upper()}**
917
+
918
+ | Metrik | Sonuç |
919
+ |---|---|
920
+ | **Sentiment** | {sentiment} |
921
+ | **Sentiment Score** | `{score_text}` |
922
+ | **Signal Strength** | **{strength}** |
923
+ | **Market Impact** | **{impact}** |
924
+ | **Risk Level** | **{risk}** |
925
+ | **Detected Asset** | **{asset_text}** |
926
+
927
+ ---
928
+
929
+ ## 📊 Sentiment Distribution
930
+
931
+ ### 🟢 Bullish — **{bullish:.1f}%**
932
+
933
+ `{bar(bullish)}`
934
+
935
+ ### 🟡 Neutral — **{neutral:.1f}%**
936
+
937
+ `{bar(neutral)}`
938
+
939
+ ### 🔴 Bearish — **{bearish:.1f}%**
940
+
941
+ `{bar(bearish)}`
942
+
943
+ > **Not:** Bu yüzdeler mevcut generative modelin gerçek
944
+ > softmax sınıf olasılıkları değildir; model çıktısı ve metin
945
+ > sinyallerinden oluşturulan yaklaşık dağılımdır.
946
+
947
+ ---
948
+
949
+ ## 🎯 Signal Interpretation
950
+
951
+ **Direction:** `{sentiment}`
952
+
953
+ **Strength:** `{strength}`
954
+
955
+ **Risk:** `{risk}`
956
+
957
+ **Market Impact:** `{impact}`
958
+
959
+ ---
960
+
961
+ ## 🪙 Asset Detection
962
+
963
+ Detected assets:
964
+
965
+ **{asset_text}**
966
+
967
+ ---
968
+
969
+ ## 🤖 Model Output
970
+
971
+ ```text
972
+ {raw_result if raw_result else "No explicit label returned."}
973
+
974
+ ```
975
+
976
+ """
977
+ # Keep the report as the function result.
978
+ return report
979
+
980
+
981
+ # ============================================================
982
+ # AUTHENTICATION / UI
983
+ # ============================================================
984
+
985
+ def signup_action(email, password):
986
+ ok, message = create_user(email, password)
987
+ return message
988
+
989
+
990
+ def login_action(email, password):
991
+ user, message = login_user(email, password)
992
+ if user is None:
993
+ return None, f"❌ {message}"
994
+ return dict(user), f"✅ {message} — {user['email']} ({user['plan'].upper()})"
995
+
996
+
997
+ def logout_action():
998
+ return None, "Çıkış yapıldı."
999
+
1000
+
1001
+ def user_info(user):
1002
+ if not user:
1003
+ return "Giriş yapılmadı."
1004
+ current = get_user(user["email"])
1005
+ if not current:
1006
+ return "Oturum bulunamadı."
1007
+ limit = get_limit(current["plan"])
1008
+ usage = current["daily_usage"] if current["usage_date"] == str(date.today()) else 0
1009
+ return (
1010
+ f"**{current['email']}** \\n"
1011
+ f"Plan: **{current['plan'].upper()}** \\n"
1012
+ f"Günlük kullanım: **{usage}/{limit}**"
1013
+ )
1014
+
1015
+
1016
+ def admin_users_action(user):
1017
+ if not user or user.get("plan") != "admin":
1018
+ return "❌ Admin yetkisi gerekli."
1019
+ conn = db()
1020
+ rows = conn.execute(
1021
+ "SELECT id, email, plan, active, daily_usage, usage_date, created_at FROM users ORDER BY id"
1022
+ ).fetchall()
1023
+ conn.close()
1024
+ if not rows:
1025
+ return "Kullanıcı yok."
1026
+ lines = ["| ID | Email | Plan | Active | Usage | Date |", "|---:|---|---|---:|---:|---|"]
1027
+ for r in rows:
1028
+ lines.append(
1029
+ f"| {r['id']} | {r['email']} | {r['plan']} | {r['active']} | {r['daily_usage']} | {r['usage_date'] or '-'} |"
1030
+ )
1031
+ return "\\n".join(lines)
1032
+
1033
+
1034
+ def admin_change_plan(user, email, plan):
1035
+ if not user or user.get("plan") != "admin":
1036
+ return "❌ Admin yetkisi gerekli."
1037
+ plan = (plan or "").lower().strip()
1038
+ if plan not in {"free", "pro", "admin"}:
1039
+ return "❌ Plan free, pro veya admin olmalı."
1040
+ target = get_user(email)
1041
+ if not target:
1042
+ return "❌ Kullanıcı bulunamadı."
1043
+ conn = db()
1044
+ conn.execute("UPDATE users SET plan = ? WHERE id = ?", (plan, target["id"]))
1045
+ conn.commit()
1046
+ conn.close()
1047
+ return f"✅ {email} → {plan}"
1048
+
1049
+
1050
+ def admin_set_active(user, email, active):
1051
+ if not user or user.get("plan") != "admin":
1052
+ return "❌ Admin yetkisi gerekli."
1053
+ target = get_user(email)
1054
+ if not target:
1055
+ return "❌ Kullanıcı bulunamadı."
1056
+ conn = db()
1057
+ conn.execute("UPDATE users SET active = ? WHERE id = ?", (1 if active else 0, target["id"]))
1058
+ conn.commit()
1059
+ conn.close()
1060
+ return f"✅ {email} → {'aktif' if active else 'pasif'}"
1061
+
1062
+
1063
+ def admin_delete(user, email):
1064
+ if not user or user.get("plan") != "admin":
1065
+ return "❌ Admin yetkisi gerekli."
1066
+ email = (email or "").lower().strip()
1067
+ if email == ADMIN_EMAIL.lower().strip():
1068
+ return "❌ Ana admin hesabı silinemez."
1069
+ target = get_user(email)
1070
+ if not target:
1071
+ return "❌ Kullanıcı bulunamadı."
1072
+ conn = db()
1073
+ conn.execute("DELETE FROM users WHERE id = ?", (target["id"],))
1074
+ conn.commit()
1075
+ conn.close()
1076
+ return f"✅ {email} silindi."
1077
+
1078
+
1079
+ # Initialize database before serving the UI.
1080
+ init_db()
1081
+
1082
+ with gr.Blocks(title="Crypto Sentiment AI") as demo:
1083
+ session = gr.State(None)
1084
+
1085
+ gr.Markdown("# 🧠 Crypto Sentiment AI")
1086
+ gr.Markdown("Kripto metinlerini analiz et ve model tabanlı sentiment sinyalini görüntüle.")
1087
+
1088
+ with gr.Tab("🔐 Hesap"):
1089
+ with gr.Row():
1090
+ with gr.Column():
1091
+ gr.Markdown("### Giriş")
1092
+ login_email = gr.Textbox(label="E-posta")
1093
+ login_password = gr.Textbox(label="Şifre", type="password")
1094
+ login_btn = gr.Button("Giriş Yap", variant="primary")
1095
+ logout_btn = gr.Button("Çıkış Yap")
1096
+ with gr.Column():
1097
+ gr.Markdown("### Kayıt")
1098
+ signup_email = gr.Textbox(label="E-posta")
1099
+ signup_password = gr.Textbox(label="Şifre", type="password")
1100
+ signup_btn = gr.Button("Hesap Oluştur")
1101
+
1102
+ auth_status = gr.Markdown("Giriş yapılmadı.")
1103
+ account_info = gr.Markdown("Giriş yapılmadı.")
1104
+
1105
+ login_btn.click(
1106
+ login_action,
1107
+ inputs=[login_email, login_password],
1108
+ outputs=[session, auth_status],
1109
+ ).then(user_info, inputs=session, outputs=account_info)
1110
+
1111
+ logout_btn.click(
1112
+ logout_action,
1113
+ outputs=[session, auth_status],
1114
+ ).then(user_info, inputs=session, outputs=account_info)
1115
+
1116
+ signup_btn.click(
1117
+ signup_action,
1118
+ inputs=[signup_email, signup_password],
1119
+ outputs=auth_status,
1120
+ )
1121
+
1122
+ with gr.Tab("📊 Sentiment Analysis"):
1123
+ analysis_text = gr.Textbox(
1124
+ label="Crypto market text",
1125
+ placeholder="Örn: Bitcoin breaks resistance as institutional inflows increase...",
1126
+ lines=8,
1127
+ )
1128
+ analyze_btn = gr.Button("🔎 Analiz Et", variant="primary")
1129
+ report_output = gr.Markdown()
1130
+ analyze_btn.click(
1131
+ analyze_crypto,
1132
+ inputs=[analysis_text, session],
1133
+ outputs=report_output,
1134
+ )
1135
+
1136
+ with gr.Tab("👑 Admin"):
1137
+ gr.Markdown("Admin işlemleri yalnızca admin planındaki oturumla çalışır.")
1138
+ refresh_btn = gr.Button("Kullanıcıları Yenile")
1139
+ users_output = gr.Markdown()
1140
+ refresh_btn.click(admin_users_action, inputs=session, outputs=users_output)
1141
+
1142
+ with gr.Row():
1143
+ admin_email_input = gr.Textbox(label="Hedef e-posta")
1144
+ admin_plan = gr.Dropdown(["free", "pro", "admin"], value="free", label="Yeni plan")
1145
+ change_plan_btn = gr.Button("Planı Değiştir")
1146
+ activate_btn = gr.Button("Aktif Et")
1147
+ deactivate_btn = gr.Button("Pasif Et")
1148
+ delete_btn = gr.Button("Kullanıcıyı Sil")
1149
+ admin_status = gr.Markdown()
1150
+
1151
+ change_plan_btn.click(
1152
+ admin_change_plan,
1153
+ inputs=[session, admin_email_input, admin_plan],
1154
+ outputs=admin_status,
1155
+ ).then(admin_users_action, inputs=session, outputs=users_output)
1156
+
1157
+ activate_btn.click(
1158
+ admin_set_active,
1159
+ inputs=[session, admin_email_input, gr.State(True)],
1160
+ outputs=admin_status,
1161
+ ).then(admin_users_action, inputs=session, outputs=users_output)
1162
+
1163
+ deactivate_btn.click(
1164
+ admin_set_active,
1165
+ inputs=[session, admin_email_input, gr.State(False)],
1166
+ outputs=admin_status,
1167
+ ).then(admin_users_action, inputs=session, outputs=users_output)
1168
+
1169
+ delete_btn.click(
1170
+ admin_delete,
1171
+ inputs=[session, admin_email_input],
1172
+ outputs=admin_status,
1173
+ ).then(admin_users_action, inputs=session, outputs=users_output)
1174
+
1175
+ gr.Markdown(
1176
+ "⚠️ Sentiment dağılımı mevcut generative PEFT modelinden gerçek softmax sınıf olasılığı değildir."
1177
+ )
1178
+
1179
+
1180
+ if __name__ == "__main__":
1181
+ demo.launch()