Spaces:
Sleeping
Sleeping
Update botsignal.py
Browse files- botsignal.py +70 -3
botsignal.py
CHANGED
|
@@ -542,18 +542,81 @@ try:
|
|
| 542 |
_M_LIST = ["1.5", "2"]
|
| 543 |
MILESTONES_LABEL = " • ".join(f"{m}×" for m in _M_LIST)
|
| 544 |
except Exception:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 545 |
_M_LIST = ["1.5", "2"]
|
| 546 |
MILESTONES_LABEL = "1.5× • 2×"
|
| 547 |
|
| 548 |
# ===== Creative CA message with MCAP + links =====
|
| 549 |
-
def build_midas_message_for_ca(ca: str, tier_label: str, *, mcap_value=None, body_snippet: Optional[str] = None) -> str:
|
| 550 |
"""
|
| 551 |
Creative brand style untuk CA:
|
| 552 |
- Tampilkan MCAP kalau tersedia
|
| 553 |
- Sisipkan Dexscreener & Axiom
|
| 554 |
- Copy 'first alert 1.5×' menuju discovery (no ceilings)
|
| 555 |
"""
|
| 556 |
-
dexs_link = f"https://dexscreener.com/token/{ca}"
|
| 557 |
axiom_link = "https://axiom.trade/@1144321"
|
| 558 |
mcap_line = f"MCAP (est.): {_fmt_big_usd(mcap_value)}"
|
| 559 |
|
|
@@ -893,6 +956,10 @@ async def process_message(msg, source_chat_id: int) -> None:
|
|
| 893 |
return
|
| 894 |
|
| 895 |
entity_key = extract_entity_key(orig_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 896 |
duplicate_entity = bool(entity_key and entity_key in recent_entity_keys)
|
| 897 |
|
| 898 |
UPDATE_HINTS = [
|
|
@@ -930,7 +997,7 @@ async def process_message(msg, source_chat_id: int) -> None:
|
|
| 930 |
all_kws = _extract_all_keywords(text_norm)
|
| 931 |
main_kw = _choose_dominant_keyword(text_norm, all_kws)
|
| 932 |
|
| 933 |
-
topic_key = entity_key
|
| 934 |
if not topic_key:
|
| 935 |
debug_log("Tak ada keyword/entitas cocok, dilewati", orig_text)
|
| 936 |
return
|
|
|
|
| 542 |
_M_LIST = ["1.5", "2"]
|
| 543 |
MILESTONES_LABEL = " • ".join(f"{m}×" for m in _M_LIST)
|
| 544 |
except Exception:
|
| 545 |
+
# ===== Universal Dexscreener link (guess chain from context or API) =====
|
| 546 |
+
CHAIN_HINTS = {
|
| 547 |
+
"bsc": "bsc", "bnb": "bsc", "binance": "bsc",
|
| 548 |
+
"eth": "ethereum", "ethereum": "ethereum",
|
| 549 |
+
"base": "base", "coinbase": "base",
|
| 550 |
+
"sol": "solana", "solana": "solana", "pump.fun": "solana"
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
def _guess_chain_from_text(t: str) -> Optional[str]:
|
| 554 |
+
t = (t or "").lower()
|
| 555 |
+
for k, v in CHAIN_HINTS.items():
|
| 556 |
+
if re.search(rf"(^|\W){re.escape(k)}(\W|$)", t):
|
| 557 |
+
return v
|
| 558 |
+
return None
|
| 559 |
+
|
| 560 |
+
async def _fetch_best_chain_for_ca(ca: str) -> Optional[str]:
|
| 561 |
+
"""
|
| 562 |
+
Query Dexscreener token endpoint to pick the chainId with highest USD liquidity.
|
| 563 |
+
"""
|
| 564 |
+
try:
|
| 565 |
+
timeout = aiohttp.ClientTimeout(total=8)
|
| 566 |
+
async with aiohttp.ClientSession(timeout=timeout) as sess:
|
| 567 |
+
async with sess.get(DEXSCREENER_TOKEN_URL + ca) as r:
|
| 568 |
+
if r.status != 200:
|
| 569 |
+
return None
|
| 570 |
+
data = await r.json()
|
| 571 |
+
pairs = (data or {}).get("pairs") or []
|
| 572 |
+
if not pairs:
|
| 573 |
+
return None
|
| 574 |
+
best = max(pairs, key=lambda p: (p.get("liquidity", {}) or {}).get("usd", 0))
|
| 575 |
+
chain_id = (best.get("chainId") or "").lower().strip()
|
| 576 |
+
# Dexscreener uses "ethereum", "bsc", "base", "solana", etc.
|
| 577 |
+
return chain_id or None
|
| 578 |
+
except:
|
| 579 |
+
return None
|
| 580 |
+
|
| 581 |
+
async def _dexs_link_universal(ca: str, context_text: Optional[str] = None) -> str:
|
| 582 |
+
"""
|
| 583 |
+
Build the proper Dexscreener URL for a CA:
|
| 584 |
+
- Solana: /solana/<ca>
|
| 585 |
+
- EVM: /{ethereum|bsc|base}/<ca> (heuristic from text, fallback API, then default ethereum)
|
| 586 |
+
- Fallback: /token/<ca>
|
| 587 |
+
"""
|
| 588 |
+
# Solana
|
| 589 |
+
if CA_SOL_RE.fullmatch(ca):
|
| 590 |
+
return f"https://dexscreener.com/solana/{ca}"
|
| 591 |
+
|
| 592 |
+
# EVM
|
| 593 |
+
if CA_EVM_RE.fullmatch(ca):
|
| 594 |
+
# (1) Heuristic from context text
|
| 595 |
+
hint = _guess_chain_from_text(context_text or "")
|
| 596 |
+
if hint:
|
| 597 |
+
return f"https://dexscreener.com/{hint}/{ca.lower()}"
|
| 598 |
+
# (2) Fallback query API to pick best chain by liquidity
|
| 599 |
+
chain = await _fetch_best_chain_for_ca(ca)
|
| 600 |
+
if chain:
|
| 601 |
+
return f"https://dexscreener.com/{chain}/{ca.lower()}"
|
| 602 |
+
# (3) Default
|
| 603 |
+
return f"https://dexscreener.com/ethereum/{ca.lower()}"
|
| 604 |
+
|
| 605 |
+
# Non-CA
|
| 606 |
+
return f"https://dexscreener.com/token/{ca}"
|
| 607 |
+
|
| 608 |
_M_LIST = ["1.5", "2"]
|
| 609 |
MILESTONES_LABEL = "1.5× • 2×"
|
| 610 |
|
| 611 |
# ===== Creative CA message with MCAP + links =====
|
| 612 |
+
def build_midas_message_for_ca(ca: str, tier_label: str, *, mcap_value=None, body_snippet: Optional[str] = None, dexs_link: Optional[str] = None) -> str:
|
| 613 |
"""
|
| 614 |
Creative brand style untuk CA:
|
| 615 |
- Tampilkan MCAP kalau tersedia
|
| 616 |
- Sisipkan Dexscreener & Axiom
|
| 617 |
- Copy 'first alert 1.5×' menuju discovery (no ceilings)
|
| 618 |
"""
|
| 619 |
+
dexs_link = dexs_link or f\"https://dexscreener.com/token/{ca}\"
|
| 620 |
axiom_link = "https://axiom.trade/@1144321"
|
| 621 |
mcap_line = f"MCAP (est.): {_fmt_big_usd(mcap_value)}"
|
| 622 |
|
|
|
|
| 956 |
return
|
| 957 |
|
| 958 |
entity_key = extract_entity_key(orig_text)
|
| 959 |
+
# STRICT: Only process messages that contain a Contract Address (CA)
|
| 960 |
+
if not (entity_key and entity_key.startswith("ca:")):
|
| 961 |
+
debug_log("Bukan pesan CA, dilewati", orig_text)
|
| 962 |
+
return
|
| 963 |
duplicate_entity = bool(entity_key and entity_key in recent_entity_keys)
|
| 964 |
|
| 965 |
UPDATE_HINTS = [
|
|
|
|
| 997 |
all_kws = _extract_all_keywords(text_norm)
|
| 998 |
main_kw = _choose_dominant_keyword(text_norm, all_kws)
|
| 999 |
|
| 1000 |
+
topic_key = entity_key
|
| 1001 |
if not topic_key:
|
| 1002 |
debug_log("Tak ada keyword/entitas cocok, dilewati", orig_text)
|
| 1003 |
return
|