agus1111 commited on
Commit
e18ad7a
Β·
verified Β·
1 Parent(s): 34ba6be

Update botsignal.py

Browse files
Files changed (1) hide show
  1. botsignal.py +97 -23
botsignal.py CHANGED
@@ -10,12 +10,13 @@ from datetime import datetime, timedelta, timezone
10
  from mimetypes import guess_extension
11
  from typing import List, Tuple, Optional, Dict
12
 
 
13
  from rapidfuzz import fuzz
14
  from telethon import TelegramClient, events
15
  from telethon.sessions import StringSession, MemorySession
16
  from telethon.errors.rpcerrorlist import FloodWaitError
17
 
18
-
19
  from autotrack import setup_autotrack
20
 
21
 
@@ -180,7 +181,6 @@ RELEVANCE_THRESHOLD = float(os.environ.get("RELEVANCE_THRESHOLD", "0.6"))
180
  EXCLUDE_PHRASES = [p.strip().lower() for p in os.environ.get(
181
  "EXCLUDE_PHRASES",
182
  "achievement unlocked,call profit:,achieving +"
183
-
184
  ).split(",") if p.strip()]
185
 
186
  # ========= Client bootstrap =========
@@ -192,8 +192,10 @@ def build_client() -> TelegramClient:
192
  return TelegramClient(MemorySession(), API_ID, API_HASH)
193
 
194
  client = build_client()
195
- # Attach autotrack to the same Telethon client
 
196
  setup_autotrack(client)
 
197
  recent_hashes: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE)
198
  recent_content_hashes: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE) # content-only dedup
199
  recent_entity_keys: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE) # entity-based dedup
@@ -472,30 +474,101 @@ def _ca_from_key(keyword: str) -> str:
472
  return keyword.split("ca:sol:", 1)[1]
473
  return ""
474
 
475
- def build_midas_message_for_ca(ca: str, tier_label: str, body_snippet: Optional[str] = None) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  """
477
- Brand style (English) for Contract Address posts.
478
- body_snippet: optional original cleaned text to append as context (quoted).
479
  """
480
- parts = [
481
- "πŸ’Ž [MidasTouch Signal] πŸ’Ž",
482
- "New contract detected:",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  "",
484
- f"CA: `{ca}`",
 
 
485
  "",
486
- f"Current Tier: {tier_label}",
487
- "Auto-track milestone β†’ Target 2Γ— active 🎯",
 
488
  "",
489
- "Remember: This is a signal, not financial advice.",
490
- "Stay safe, stay golden ✨",
 
491
  ]
492
- msg = "\n".join(parts)
 
 
493
  if body_snippet:
494
- # append a minimal quoted context block (optional)
495
- snippet = body_snippet.strip()
496
  if snippet:
497
- snippet = re.sub(r"\n{3,}", "\n\n", snippet)
498
- msg += "\n\nβ€”\n" + snippet
499
  return msg
500
 
501
  def format_body_with_spacing(body: str, tier_label: str) -> str:
@@ -559,8 +632,8 @@ async def post_or_update(keyword: str, body: str, new_tier: str, src_msg, *, upd
559
  """
560
  Modified:
561
  - If `keyword` represents a Contract Address (ca:evm:/ca:sol:),
562
- we format with brand style (English) via build_midas_message_for_ca,
563
- bypassing the default spacing formatter.
564
  """
565
  prev = last_posted.get(keyword)
566
  now_ts = datetime.now().timestamp()
@@ -568,7 +641,8 @@ async def post_or_update(keyword: str, body: str, new_tier: str, src_msg, *, upd
568
  # Choose text based on whether this is a CA entity or not
569
  if _is_ca_key(keyword):
570
  ca_val = _ca_from_key(keyword)
571
- text_to_send = build_midas_message_for_ca(ca_val, new_tier, body_snippet=None)
 
572
  else:
573
  text_to_send = format_body_with_spacing(body, new_tier)
574
 
@@ -858,7 +932,7 @@ async def process_message(msg, source_chat_id: int) -> None:
858
  core_u, sup_u = _unique_counts_by_role(topic_key)
859
  if core_u < 1 and sup_u < SUPPORT_MIN_UNIQUE:
860
  debug_log(
861
- f"Support ditahan (core_u={core_u}, sup_u={sup_u} < {SUPPORT_MIN_UNIQUE})",
862
  orig_text,
863
  )
864
  return
 
10
  from mimetypes import guess_extension
11
  from typing import List, Tuple, Optional, Dict
12
 
13
+ import aiohttp
14
  from rapidfuzz import fuzz
15
  from telethon import TelegramClient, events
16
  from telethon.sessions import StringSession, MemorySession
17
  from telethon.errors.rpcerrorlist import FloodWaitError
18
 
19
+ # Attach autotrack (silent start, reply >=1.5x) β€” pastikan autotrack.py kamu versi terbaru
20
  from autotrack import setup_autotrack
21
 
22
 
 
181
  EXCLUDE_PHRASES = [p.strip().lower() for p in os.environ.get(
182
  "EXCLUDE_PHRASES",
183
  "achievement unlocked,call profit:,achieving +"
 
184
  ).split(",") if p.strip()]
185
 
186
  # ========= Client bootstrap =========
 
192
  return TelegramClient(MemorySession(), API_ID, API_HASH)
193
 
194
  client = build_client()
195
+
196
+ # Attach autotrack to the same Telethon client (silent start, reply >= 1.5x default)
197
  setup_autotrack(client)
198
+
199
  recent_hashes: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE)
200
  recent_content_hashes: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE) # content-only dedup
201
  recent_entity_keys: deque[str] = deque(maxlen=DEDUP_BUFFER_SIZE) # entity-based dedup
 
474
  return keyword.split("ca:sol:", 1)[1]
475
  return ""
476
 
477
+ # ===== Number format for MCAP line =====
478
+ def _fmt_big_usd(x):
479
+ if x is None:
480
+ return "β€”"
481
+ try:
482
+ x = float(x)
483
+ except:
484
+ return "β€”"
485
+ if x >= 1_000_000_000:
486
+ return f"${x/1_000_000_000:.2f}B"
487
+ if x >= 1_000_000:
488
+ return f"${x/1_000_000:.2f}M"
489
+ if x >= 1_000:
490
+ return f"${x/1_000:.2f}K"
491
+ return f"${x:.0f}"
492
+
493
+ # ===== Dexscreener fetch (MCAP/FDV) =====
494
+ DEXSCREENER_TOKEN_URL = "https://api.dexscreener.com/latest/dex/tokens/"
495
+
496
+ async def _fetch_initial_mcap(ca: str):
497
  """
498
+ Ambil perkiraan MCAP (marketCap atau FDV) sekali dari Dexscreener.
499
+ Return float atau None kalau gak ada.
500
  """
501
+ try:
502
+ timeout = aiohttp.ClientTimeout(total=8)
503
+ async with aiohttp.ClientSession(timeout=timeout) as sess:
504
+ async with sess.get(DEXSCREENER_TOKEN_URL + ca) as r:
505
+ if r.status != 200:
506
+ return None
507
+ data = await r.json()
508
+ pairs = (data or {}).get("pairs") or []
509
+ if not pairs:
510
+ return None
511
+ # pilih pair dengan USD liquidity terbesar
512
+ best = max(pairs, key=lambda p: (p.get("liquidity", {}) or {}).get("usd", 0))
513
+ mc = best.get("marketCap")
514
+ fdv = best.get("fdv")
515
+ if isinstance(mc, (int, float)) and mc > 0:
516
+ return float(mc)
517
+ if isinstance(fdv, (int, float)) and fdv > 0:
518
+ return float(fdv)
519
+ return None
520
+ except:
521
+ return None
522
+
523
+ # ===== Milestones label (sinkron dengan env; default 1.5Γ— β€’ 2Γ—) =====
524
+ _M_RAW = os.environ.get("MILESTONES", "1.5,2")
525
+ try:
526
+ _M_LIST = [x.strip() for x in _M_RAW.split(",") if x.strip()]
527
+ if not _M_LIST:
528
+ _M_LIST = ["1.5", "2"]
529
+ MILESTONES_LABEL = " β€’ ".join(f"{m}Γ—" for m in _M_LIST)
530
+ except Exception:
531
+ _M_LIST = ["1.5", "2"]
532
+ MILESTONES_LABEL = "1.5Γ— β€’ 2Γ—"
533
+
534
+ # ===== Creative CA message with MCAP + links =====
535
+ def build_midas_message_for_ca(ca: str, tier_label: str, *, mcap_value=None, body_snippet: Optional[str] = None) -> str:
536
+ """
537
+ Creative brand style untuk CA:
538
+ - Tampilkan MCAP kalau tersedia
539
+ - Sisipkan Dexscreener & Axiom
540
+ - Copy 'first alert 1.5Γ—' menuju discovery (no ceilings)
541
+ """
542
+ dexs_link = f"https://dexscreener.com/token/{ca}"
543
+ axiom_link = "https://axiom.trade/@1144321"
544
+ mcap_line = f"MCAP (est.): {_fmt_big_usd(mcap_value)}"
545
+
546
+ first_alert = _M_LIST[0] if _M_LIST else "1.5"
547
+
548
+ lines = [
549
+ "✨ **MidasTouch β€” Fresh Alpha Drop**",
550
+ f"**Tier Now:** {tier_label}",
551
  "",
552
+ "Here’s a fresh contract worth a look:",
553
+ f"**CA**: `{ca}`",
554
+ mcap_line,
555
  "",
556
+ "Quick actions:",
557
+ f"β€’ πŸ”Ž [Open on Dexscreener]({dexs_link})",
558
+ f"β€’ πŸ›’ [Trade with Axiom]({axiom_link})",
559
  "",
560
+ f"Auto-track armed β†’ first alert at **{first_alert}Γ—**; we hunt momentum to price discovery β€” no ceilings. 🎯",
561
+ "Plan the trade, trade the plan. Cut losers quick, compound the wins.",
562
+ "β€” **MidasTouch**",
563
  ]
564
+ msg = "\n".join(lines)
565
+
566
+ # (opsional) sisipkan snippet sumber
567
  if body_snippet:
568
+ snippet = re.sub(r"\n{3,}", "\n\n", body_snippet.strip())
 
569
  if snippet:
570
+ msg += "\n\n> " + snippet.replace("\n", "\n> ")
571
+
572
  return msg
573
 
574
  def format_body_with_spacing(body: str, tier_label: str) -> str:
 
632
  """
633
  Modified:
634
  - If `keyword` represents a Contract Address (ca:evm:/ca:sol:),
635
+ we format with brand style via build_midas_message_for_ca
636
+ and menambahkan MCAP (fetch sekali sebelum kirim).
637
  """
638
  prev = last_posted.get(keyword)
639
  now_ts = datetime.now().timestamp()
 
641
  # Choose text based on whether this is a CA entity or not
642
  if _is_ca_key(keyword):
643
  ca_val = _ca_from_key(keyword)
644
+ mcap_val = await _fetch_initial_mcap(ca_val) # ambil MCAP sekali
645
+ text_to_send = build_midas_message_for_ca(ca_val, new_tier, mcap_value=mcap_val, body_snippet=None)
646
  else:
647
  text_to_send = format_body_with_spacing(body, new_tier)
648
 
 
932
  core_u, sup_u = _unique_counts_by_role(topic_key)
933
  if core_u < 1 and sup_u < SUPPORT_MIN_UNIQUE:
934
  debug_log(
935
+ f"Support ditahan (core_u={core_u}, sup_u={SUPPORT_MIN_UNIQUE})",
936
  orig_text,
937
  )
938
  return