refactor(bot): split config.py (405 lines) into settings.py + tiers.py
settings.py: env vars, bot token, descriptions, formatting functions tiers.py: subscription tiers, pricing, upgrade packs config.py: re-export shim for backward compat
This commit is contained in:
parent
b96a20d18c
commit
df877e7347
4 changed files with 479 additions and 405 deletions
|
|
@ -1,405 +1,3 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
CryptoRugMunch Bot - Configuration
|
||||
===================================
|
||||
Tiers, channels, API endpoints, DEX ref links, and constants.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from app.domains.referral.partners import DEX_REF_LINKS # noqa: F401
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# BOT
|
||||
# ══════════════════════════════════════════════
|
||||
BOT_TOKEN = os.getenv("RUGMUNCH_BOT_TOKEN", "")
|
||||
OWNER_IDS = {int(x.strip()) for x in os.getenv("TELEGRAM_ALLOWED_USERS", "7075336557").split(",") if x.strip()}
|
||||
BOT_USERNAME = "RugMunchBot"
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# BRANDING & SOCIAL
|
||||
# ══════════════════════════════════════════════
|
||||
WEBSITE_URL = "https://rugmunch.io"
|
||||
WEB_APP_URL = f"{WEBSITE_URL}/scanner"
|
||||
TWITTER_URL = "https://x.com/RugMunch"
|
||||
TELEGRAM_CHANNEL = "https://t.me/RugMunchAlerts"
|
||||
TELEGRAM_GROUP = "https://t.me/RugMunchChat"
|
||||
SUPPORT_EMAIL = "biz@rugmunch.io"
|
||||
ENTERPRISE_CONTACT = "biz@rugmunch.io"
|
||||
|
||||
SOCIAL_LINKS = {
|
||||
"website": {"emoji": "🌐", "name": "RugMunch.io", "url": WEBSITE_URL},
|
||||
"twitter": {"emoji": "𝕏", "name": "Twitter/X", "url": TWITTER_URL}, # noqa: RUF001
|
||||
"channel": {"emoji": "📢", "name": "Alerts Channel", "url": TELEGRAM_CHANNEL},
|
||||
"group": {"emoji": "💬", "name": "Community Chat", "url": TELEGRAM_GROUP},
|
||||
}
|
||||
|
||||
# Bot profile (set via BotFather API on startup)
|
||||
BOT_DESCRIPTION = (
|
||||
"🛡️ RugMunch Intelligence - The Bloomberg Terminal of Shitcoins\n\n"
|
||||
"Scan any token across 77+ chains for honeypots, rug pulls, bundles, "
|
||||
"fake volume, and dev wallet history. AI-powered risk scoring with "
|
||||
"real-time alerts.\n\n"
|
||||
"🔍 Multi-chain token scanning\n"
|
||||
"👛 Wallet forensics & smart money tracking\n"
|
||||
"📊 Technical analysis & trending tokens\n"
|
||||
"🚨 Real-time scam detection & alerts\n"
|
||||
"📚 Free crypto scam education\n\n"
|
||||
"🌐 rugmunch.io"
|
||||
)
|
||||
BOT_SHORT_DESCRIPTION = "🛡️ Crypto scam detection & token intelligence across 77+ chains. Free tier available."
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# API ENDPOINTS
|
||||
# ══════════════════════════════════════════════
|
||||
BACKEND_URL = os.getenv("RMI_BACKEND_URL", "http://localhost:8000")
|
||||
DATABUS_URL = f"{BACKEND_URL}/api/v1/databus"
|
||||
RAG_URL = f"{BACKEND_URL}/api/v1/rag"
|
||||
|
||||
# External APIs
|
||||
DEXSCREENER = "https://api.dexscreener.com/latest/dex"
|
||||
COINGECKO = "https://api.coingecko.com/api/v3"
|
||||
DEFILLAMA = "https://api.llama.fi"
|
||||
GOPLUS = "https://api.gopluslabs.com/api/v1"
|
||||
HONEYPOT = "https://api.honeypot.is/v2"
|
||||
BUBBLE_MAPS = "https://api.bubblemaps.io"
|
||||
DEXTOOLS = "https://public-api.dextools.io/trial/v2"
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# SUBSCRIPTION TIERS (Weekly Allotments)
|
||||
# ══════════════════════════════════════════════
|
||||
# Weekly model: resets every Monday 00:00 UTC.
|
||||
# Prevents day-one dump abuse and smooths API load.
|
||||
TIERS = {
|
||||
"free": {
|
||||
"name": "Free",
|
||||
"emoji": "🆓",
|
||||
"scans_per_week": 25,
|
||||
"ai_msgs_per_week": 15,
|
||||
"price_monthly": 0,
|
||||
"features": [
|
||||
"25 scans/week",
|
||||
"15 AI questions/week",
|
||||
"Basic risk score",
|
||||
"Contract safety check",
|
||||
"Liquidity overview",
|
||||
"Top 10 holders",
|
||||
"Community access",
|
||||
],
|
||||
},
|
||||
"scout": {
|
||||
"name": "Scout",
|
||||
"emoji": "🔍",
|
||||
"scans_per_week": 150,
|
||||
"ai_msgs_per_week": 75,
|
||||
"price_monthly": 29.99,
|
||||
"price_stars": 1500,
|
||||
"features": [
|
||||
"150 scans/week",
|
||||
"75 AI questions/week",
|
||||
"Advanced risk scoring",
|
||||
"Bundle detection",
|
||||
"Fake volume detection",
|
||||
"Dev wallet finder",
|
||||
"Holder trend analysis",
|
||||
"Smart money alerts",
|
||||
"Watchlist (10 tokens)",
|
||||
"Export reports",
|
||||
],
|
||||
},
|
||||
"hunter": {
|
||||
"name": "Hunter",
|
||||
"emoji": "🎯",
|
||||
"scans_per_week": 400,
|
||||
"ai_msgs_per_week": 250,
|
||||
"price_monthly": 49.99,
|
||||
"price_stars": 2500,
|
||||
"features": [
|
||||
"400 scans/week",
|
||||
"250 AI questions/week",
|
||||
"Everything in Scout, plus:",
|
||||
"Full wallet forensics",
|
||||
"First buyer analysis",
|
||||
"Exchange flow tracking",
|
||||
"Fresh wallet detection",
|
||||
"Shill detection",
|
||||
"Social sentiment scan",
|
||||
"Watchlist (50 tokens)",
|
||||
"Real-time alerts",
|
||||
"TA chart generation",
|
||||
],
|
||||
},
|
||||
"pro": {
|
||||
"name": "Pro",
|
||||
"emoji": "👑",
|
||||
"scans_per_week": 1000,
|
||||
"ai_msgs_per_week": 500,
|
||||
"price_monthly": 99.99,
|
||||
"price_stars": 5000,
|
||||
"features": [
|
||||
"1,000 scans/week",
|
||||
"500 AI questions/week",
|
||||
"Everything in Hunter, plus:",
|
||||
"Smart money watch (live)",
|
||||
"Cross-chain tracking",
|
||||
"Custom alert rules",
|
||||
"API access",
|
||||
"Priority support",
|
||||
"Early feature access",
|
||||
"Unlimited watchlists",
|
||||
"Multi-wallet tracking",
|
||||
"White-label reports",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# USER REFERRAL LINKS (Add your own!)
|
||||
# ══════════════════════════════════════════════
|
||||
# These are shown in scan reports and on the website.
|
||||
USER_REF_LINKS = {
|
||||
# "binance": {"name": "Binance", "url": "https://www.binance.com/en/register?ref=YOUR_CODE", "emoji": "🟡"},
|
||||
# "bybit": {"name": "Bybit", "url": "https://www.bybit.com/invite?ref=YOUR_CODE", "emoji": "🟠"},
|
||||
# "okx": {"name": "OKX", "url": "https://www.okx.com/join/YOUR_CODE", "emoji": "⚫"},
|
||||
# "coinbase": {"name": "Coinbase", "url": "https://coinbase.com/join/YOUR_CODE", "emoji": "🔵"},
|
||||
# "kucoin": {"name": "KuCoin", "url": "https://www.kucoin.com/r/YOUR_CODE", "emoji": "🟢"},
|
||||
# "gate": {"name": "Gate.io", "url": "https://www.gate.io/ref/YOUR_CODE", "emoji": "🔷"},
|
||||
# "mexc": {"name": "MEXC", "url": "https://www.mexc.com/register?inviteCode=YOUR_CODE", "emoji": "🔶"},
|
||||
# "bitget": {"name": "Bitget", "url": "https://www.bitget.com/referral?clacCode=YOUR_CODE", "emoji": "🌊"},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# TOP-UP PACKS (Buy extra when you run out)
|
||||
# ══════════════════════════════════════════════
|
||||
# One-time purchases via Telegram Stars. No expiry -
|
||||
# top-up balance carries over forever until used.
|
||||
TOP_UP_PACKS = {
|
||||
"scans_10": {
|
||||
"name": "10 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 10,
|
||||
"stars": 75,
|
||||
},
|
||||
"scans_50": {
|
||||
"name": "50 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 50,
|
||||
"stars": 300,
|
||||
},
|
||||
"scans_100": {
|
||||
"name": "100 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 100,
|
||||
"stars": 500,
|
||||
},
|
||||
"ai_10": {
|
||||
"name": "10 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 10,
|
||||
"stars": 100,
|
||||
},
|
||||
"ai_50": {
|
||||
"name": "50 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 50,
|
||||
"stars": 400,
|
||||
},
|
||||
"ai_100": {
|
||||
"name": "100 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 100,
|
||||
"stars": 700,
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# SCAN PACKS (One-time deep analysis purchases)
|
||||
# ══════════════════════════════════════════════
|
||||
SCAN_PACKS = {
|
||||
"deep_scan": {
|
||||
"name": "🔬 Deep Token Scan",
|
||||
"description": "Complete forensic analysis of ONE token",
|
||||
"includes": [
|
||||
"Price history",
|
||||
"Holder bubble map",
|
||||
"Wallet clusters",
|
||||
"Scam indicators",
|
||||
"AI prediction",
|
||||
"Dev history",
|
||||
],
|
||||
"stars": 250,
|
||||
},
|
||||
"wallet_forensic": {
|
||||
"name": "🕵️ Wallet Forensic Report",
|
||||
"description": "Full wallet investigation - P&L, connections, history",
|
||||
"includes": [
|
||||
"Transaction history",
|
||||
"Counterparty analysis",
|
||||
"P&L breakdown",
|
||||
"Bundle links",
|
||||
"Scam DB check",
|
||||
],
|
||||
"stars": 400,
|
||||
},
|
||||
"bundle_investigation": {
|
||||
"name": "🕸️ Bundle Investigation",
|
||||
"description": "Full cluster analysis - find every connected wallet",
|
||||
"includes": [
|
||||
"Funding source map",
|
||||
"Timing correlation",
|
||||
"Wash trade detection",
|
||||
"Holder overlap",
|
||||
"Exit strategy clues",
|
||||
],
|
||||
"stars": 500,
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# DEX REFERRAL LINKS
|
||||
# ══════════════════════════════════════════════
|
||||
# These generate revenue when users trade through our links.
|
||||
# Canonical definitions live in app.domains.referral.partners.
|
||||
|
||||
# Chain ID mapping for 1inch
|
||||
CHAIN_IDS = {
|
||||
"ethereum": 1,
|
||||
"bsc": 56,
|
||||
"polygon": 137,
|
||||
"arbitrum": 42161,
|
||||
"avalanche": 43114,
|
||||
"optimism": 10,
|
||||
"base": 8453,
|
||||
"fantom": 250,
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# CHANNELS
|
||||
# ══════════════════════════════════════════════
|
||||
CHANNELS = {
|
||||
"main": "-1002056885429",
|
||||
"news": "-1003982391578",
|
||||
"alerts": "-1003818352164",
|
||||
"alpha": "-1003762675055",
|
||||
"scans": "-1003937506770",
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# RISK VISUALIZATION
|
||||
# ══════════════════════════════════════════════
|
||||
RISK_COLORS = {
|
||||
"critical": "🔴",
|
||||
"high": "🟠",
|
||||
"medium": "🟡",
|
||||
"low": "🟢",
|
||||
"safe": "✅",
|
||||
"unknown": "⚪",
|
||||
}
|
||||
|
||||
|
||||
def risk_bar(score: float, length: int = 10) -> str:
|
||||
"""Generate a visual risk bar (0-100 scale)."""
|
||||
filled = int((score / 100) * length)
|
||||
if score >= 80:
|
||||
return "🔴" * filled + "⬜" * (length - filled) + f" {score}% DANGER"
|
||||
elif score >= 60:
|
||||
return "🟠" * filled + "⬜" * (length - filled) + f" {score}% RISKY"
|
||||
elif score >= 40:
|
||||
return "🟡" * filled + "⬜" * (length - filled) + f" {score}% CAUTION"
|
||||
elif score >= 20:
|
||||
return "🟢" * filled + "⬜" * (length - filled) + f" {score}% LOW"
|
||||
else:
|
||||
return "✅" * filled + "⬜" * (length - filled) + f" {score}% SAFE"
|
||||
|
||||
|
||||
def threat_indicator(label: str, found: bool) -> str:
|
||||
return f"{'🔴' if found else '✅'} {label}"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# FORMATTING HELPERS
|
||||
# ══════════════════════════════════════════════
|
||||
def fmt_number(n) -> str:
|
||||
"""Format large numbers: 1.2M, 450K, etc."""
|
||||
if n is None:
|
||||
return "N/A"
|
||||
try:
|
||||
n = float(n)
|
||||
except (ValueError, TypeError):
|
||||
return str(n)
|
||||
if n >= 1e9:
|
||||
return f"${n / 1e9:.2f}B"
|
||||
elif n >= 1e6:
|
||||
return f"${n / 1e6:.2f}M"
|
||||
elif n >= 1e3:
|
||||
return f"${n / 1e3:.1f}K"
|
||||
elif n >= 1:
|
||||
return f"${n:.4f}"
|
||||
elif n >= 0.01:
|
||||
return f"${n:.6f}"
|
||||
else:
|
||||
return f"${n:.10f}"
|
||||
|
||||
|
||||
def fmt_pct(n) -> str:
|
||||
if n is None:
|
||||
return "N/A"
|
||||
try:
|
||||
return f"{float(n):.2f}%"
|
||||
except (ValueError, TypeError):
|
||||
return str(n)
|
||||
|
||||
|
||||
def fmt_chain(chain: str) -> str:
|
||||
"""Return chain emoji + name."""
|
||||
emojis = {
|
||||
"solana": "◎",
|
||||
"ethereum": "⟠",
|
||||
"bsc": "🔶",
|
||||
"polygon": "🟣",
|
||||
"arbitrum": "🔵",
|
||||
"optimism": "🔴",
|
||||
"base": "🔷",
|
||||
"avalanche": "🔺",
|
||||
"fantom": "👻",
|
||||
"tron": "🔻",
|
||||
"bitcoin": "₿",
|
||||
}
|
||||
return f"{emojis.get(chain.lower(), '⛓️')} {chain.capitalize()}"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# SCAM SCHOOL TOPICS
|
||||
# ══════════════════════════════════════════════
|
||||
SCAM_SCHOOL_TOPICS = {
|
||||
"honeypot": {
|
||||
"title": "🍯 Honeypot Tokens",
|
||||
"content": "A honeypot lets you buy but prevents you from selling. The contract has hidden code that only allows whitelisted addresses to sell.\n\n🔍 *How to spot:*\n• Can buy but can't sell\n• Only 1-2 sellers ever\n• Contract has sell restrictions\n• Honeypot.is flags it\n\n🛡️ *Protection:* Always test with a small amount first, and use /scan before buying.",
|
||||
},
|
||||
"rugpull": {
|
||||
"title": "🏃 Rug Pulls",
|
||||
"content": "Developers drain all liquidity from a token, making it worthless. Can happen via:\n\n• Removing LP tokens\n• Minting infinite tokens\n• Hidden sell functions\n• Blacklisting buyers\n\n🔍 *How to spot:*\n• Unlocked liquidity\n• Unrenounced contract\n• Dev holds >20% supply\n• New wallet, no history\n\n🛡️ *Protection:* Check /scan for LP lock status and dev wallet analysis.",
|
||||
},
|
||||
"bundling": {
|
||||
"title": "📦 Bundle Scams",
|
||||
"content": "Devs use multiple wallets to create fake organic activity. They buy from 10-50 wallets funded from the same source.\n\n🔍 *How to spot:*\n• Multiple wallets funded from same CEX/bridge\n• Identical buy amounts\n• Same-timestamp transactions\n• Wallets only hold this one token\n\n🛡️ *Protection:* Use /scan bundle detection to trace funding sources.",
|
||||
},
|
||||
"wash_trading": {
|
||||
"title": "🔄 Wash Trading",
|
||||
"content": "Artificially inflating volume by buying and selling between own wallets.\n\n🔍 *How to spot:*\n• High volume, low unique traders\n• Same wallets trading back and forth\n• Round number trade amounts\n• Volume doesn't match market cap\n\n🛡️ *Protection:* /scan checks volume authenticity.",
|
||||
},
|
||||
"fresh_wallets": {
|
||||
"title": "🆕 Fresh Wallet Red Flags",
|
||||
"content": "When most holders are brand new wallets (created <7 days ago), it often means the dev created fake holders.\n\n🔍 *How to spot:*\n• >50% wallets <7 days old\n• Wallets only hold 1 token\n• No transaction history before buying\n• Similar naming patterns\n\n🛡️ *Protection:* /scan shows wallet age distribution.",
|
||||
},
|
||||
"exchange_flow": {
|
||||
"title": "🏦 Exchange Flow Analysis",
|
||||
"content": "Tracking when large holders move tokens to exchanges signals potential dumps.\n\n🔍 *How to spot:*\n• Top holders sending to CEX wallets\n• Increasing exchange deposits\n• Unusual transfer patterns\n• Pre-scheduled dumps\n\n🛡️ *Protection:* Hunter+ tiers get real-time exchange flow alerts.",
|
||||
},
|
||||
}
|
||||
"""Config — re-exports from settings + tiers."""
|
||||
from app.domains.telegram.rugmunchbot.settings import * # noqa: F403
|
||||
from app.domains.telegram.rugmunchbot.tiers import * # noqa: F403
|
||||
|
|
|
|||
71
app/domains/telegram/rugmunchbot/middleware.py
Normal file
71
app/domains/telegram/rugmunchbot/middleware.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Bot middleware — tier enforcement, rate limiting, metrics as decorators.
|
||||
|
||||
Usage: @require_tier("scan") on any command handler.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
from app.domains.telegram.rugmunchbot import db
|
||||
from app.domains.telegram.rugmunchbot.metrics import track_command, track_error
|
||||
|
||||
logger = logging.getLogger("rugmunchbot.middleware")
|
||||
|
||||
|
||||
def require_tier(action: str = "scan"):
|
||||
"""Decorator: check rate limit + tier before executing command.
|
||||
|
||||
Automatically calls db.check_rate_limit() and db.use_scan().
|
||||
Sends paywall message if limit exceeded.
|
||||
Tracks command + errors via Prometheus metrics.
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
async def wrapper(update, ctx, *args, **kwargs):
|
||||
user = update.effective_user
|
||||
if not user:
|
||||
return
|
||||
|
||||
# Check tier
|
||||
allowed, used, limit = db.check_rate_limit(user.id, action)
|
||||
if not allowed:
|
||||
from app.domains.telegram.rugmunchbot.config import paywall_text
|
||||
from app.domains.telegram.rugmunchbot.formatting import paywall_kb
|
||||
await update.message.reply_text(
|
||||
paywall_text(user.id, action),
|
||||
parse_mode="HTML",
|
||||
reply_markup=paywall_kb(),
|
||||
)
|
||||
track_error(func.__name__, "rate_limit")
|
||||
return
|
||||
|
||||
try:
|
||||
result = await func(update, ctx, *args, **kwargs)
|
||||
db.use_scan(user.id)
|
||||
track_command(func.__name__)
|
||||
return result
|
||||
except Exception as e:
|
||||
track_error(func.__name__, type(e).__name__)
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
return decorator
|
||||
|
||||
|
||||
def admin_only(func):
|
||||
"""Decorator: only allow bot owners to execute."""
|
||||
@wraps(func)
|
||||
async def wrapper(update, ctx, *args, **kwargs):
|
||||
user = update.effective_user
|
||||
if not user or not user.id:
|
||||
return
|
||||
from app.domains.telegram.rugmunchbot.formatting import is_owner
|
||||
if not is_owner(user.id):
|
||||
await update.message.reply_text("🔒 Admin only.")
|
||||
return
|
||||
return await func(update, ctx, *args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
__all__ = ["admin_only", "require_tier"]
|
||||
68
app/domains/telegram/rugmunchbot/settings.py
Normal file
68
app/domains/telegram/rugmunchbot/settings.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
CryptoRugMunch Bot - Configuration
|
||||
===================================
|
||||
Tiers, channels, API endpoints, DEX ref links, and constants.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from app.domains.referral.partners import DEX_REF_LINKS # noqa: F401
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# BOT
|
||||
# ══════════════════════════════════════════════
|
||||
BOT_TOKEN = os.getenv("RUGMUNCH_BOT_TOKEN", "")
|
||||
OWNER_IDS = {int(x.strip()) for x in os.getenv("TELEGRAM_ALLOWED_USERS", "7075336557").split(",") if x.strip()}
|
||||
BOT_USERNAME = "RugMunchBot"
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# BRANDING & SOCIAL
|
||||
# ══════════════════════════════════════════════
|
||||
WEBSITE_URL = "https://rugmunch.io"
|
||||
WEB_APP_URL = f"{WEBSITE_URL}/scanner"
|
||||
TWITTER_URL = "https://x.com/RugMunch"
|
||||
TELEGRAM_CHANNEL = "https://t.me/RugMunchAlerts"
|
||||
TELEGRAM_GROUP = "https://t.me/RugMunchChat"
|
||||
SUPPORT_EMAIL = "biz@rugmunch.io"
|
||||
ENTERPRISE_CONTACT = "biz@rugmunch.io"
|
||||
|
||||
SOCIAL_LINKS = {
|
||||
"website": {"emoji": "🌐", "name": "RugMunch.io", "url": WEBSITE_URL},
|
||||
"twitter": {"emoji": "𝕏", "name": "Twitter/X", "url": TWITTER_URL}, # noqa: RUF001
|
||||
"channel": {"emoji": "📢", "name": "Alerts Channel", "url": TELEGRAM_CHANNEL},
|
||||
"group": {"emoji": "💬", "name": "Community Chat", "url": TELEGRAM_GROUP},
|
||||
}
|
||||
|
||||
# Bot profile (set via BotFather API on startup)
|
||||
BOT_DESCRIPTION = (
|
||||
"🛡️ RugMunch Intelligence - The Bloomberg Terminal of Shitcoins\n\n"
|
||||
"Scan any token across 77+ chains for honeypots, rug pulls, bundles, "
|
||||
"fake volume, and dev wallet history. AI-powered risk scoring with "
|
||||
"real-time alerts.\n\n"
|
||||
"🔍 Multi-chain token scanning\n"
|
||||
"👛 Wallet forensics & smart money tracking\n"
|
||||
"📊 Technical analysis & trending tokens\n"
|
||||
"🚨 Real-time scam detection & alerts\n"
|
||||
"📚 Free crypto scam education\n\n"
|
||||
"🌐 rugmunch.io"
|
||||
)
|
||||
BOT_SHORT_DESCRIPTION = "🛡️ Crypto scam detection & token intelligence across 77+ chains. Free tier available."
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# API ENDPOINTS
|
||||
# ══════════════════════════════════════════════
|
||||
BACKEND_URL = os.getenv("RMI_BACKEND_URL", "http://localhost:8000")
|
||||
DATABUS_URL = f"{BACKEND_URL}/api/v1/databus"
|
||||
RAG_URL = f"{BACKEND_URL}/api/v1/rag"
|
||||
|
||||
# External APIs
|
||||
DEXSCREENER = "https://api.dexscreener.com/latest/dex"
|
||||
COINGECKO = "https://api.coingecko.com/api/v3"
|
||||
DEFILLAMA = "https://api.llama.fi"
|
||||
GOPLUS = "https://api.gopluslabs.com/api/v1"
|
||||
HONEYPOT = "https://api.honeypot.is/v2"
|
||||
BUBBLE_MAPS = "https://api.bubblemaps.io"
|
||||
DEXTOOLS = "https://public-api.dextools.io/trial/v2"
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
337
app/domains/telegram/rugmunchbot/tiers.py
Normal file
337
app/domains/telegram/rugmunchbot/tiers.py
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
# SUBSCRIPTION TIERS (Weekly Allotments)
|
||||
# ══════════════════════════════════════════════
|
||||
# Weekly model: resets every Monday 00:00 UTC.
|
||||
# Prevents day-one dump abuse and smooths API load.
|
||||
TIERS = {
|
||||
"free": {
|
||||
"name": "Free",
|
||||
"emoji": "🆓",
|
||||
"scans_per_week": 25,
|
||||
"ai_msgs_per_week": 15,
|
||||
"price_monthly": 0,
|
||||
"features": [
|
||||
"25 scans/week",
|
||||
"15 AI questions/week",
|
||||
"Basic risk score",
|
||||
"Contract safety check",
|
||||
"Liquidity overview",
|
||||
"Top 10 holders",
|
||||
"Community access",
|
||||
],
|
||||
},
|
||||
"scout": {
|
||||
"name": "Scout",
|
||||
"emoji": "🔍",
|
||||
"scans_per_week": 150,
|
||||
"ai_msgs_per_week": 75,
|
||||
"price_monthly": 29.99,
|
||||
"price_stars": 1500,
|
||||
"features": [
|
||||
"150 scans/week",
|
||||
"75 AI questions/week",
|
||||
"Advanced risk scoring",
|
||||
"Bundle detection",
|
||||
"Fake volume detection",
|
||||
"Dev wallet finder",
|
||||
"Holder trend analysis",
|
||||
"Smart money alerts",
|
||||
"Watchlist (10 tokens)",
|
||||
"Export reports",
|
||||
],
|
||||
},
|
||||
"hunter": {
|
||||
"name": "Hunter",
|
||||
"emoji": "🎯",
|
||||
"scans_per_week": 400,
|
||||
"ai_msgs_per_week": 250,
|
||||
"price_monthly": 49.99,
|
||||
"price_stars": 2500,
|
||||
"features": [
|
||||
"400 scans/week",
|
||||
"250 AI questions/week",
|
||||
"Everything in Scout, plus:",
|
||||
"Full wallet forensics",
|
||||
"First buyer analysis",
|
||||
"Exchange flow tracking",
|
||||
"Fresh wallet detection",
|
||||
"Shill detection",
|
||||
"Social sentiment scan",
|
||||
"Watchlist (50 tokens)",
|
||||
"Real-time alerts",
|
||||
"TA chart generation",
|
||||
],
|
||||
},
|
||||
"pro": {
|
||||
"name": "Pro",
|
||||
"emoji": "👑",
|
||||
"scans_per_week": 1000,
|
||||
"ai_msgs_per_week": 500,
|
||||
"price_monthly": 99.99,
|
||||
"price_stars": 5000,
|
||||
"features": [
|
||||
"1,000 scans/week",
|
||||
"500 AI questions/week",
|
||||
"Everything in Hunter, plus:",
|
||||
"Smart money watch (live)",
|
||||
"Cross-chain tracking",
|
||||
"Custom alert rules",
|
||||
"API access",
|
||||
"Priority support",
|
||||
"Early feature access",
|
||||
"Unlimited watchlists",
|
||||
"Multi-wallet tracking",
|
||||
"White-label reports",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# USER REFERRAL LINKS (Add your own!)
|
||||
# ══════════════════════════════════════════════
|
||||
# These are shown in scan reports and on the website.
|
||||
USER_REF_LINKS = {
|
||||
# "binance": {"name": "Binance", "url": "https://www.binance.com/en/register?ref=YOUR_CODE", "emoji": "🟡"},
|
||||
# "bybit": {"name": "Bybit", "url": "https://www.bybit.com/invite?ref=YOUR_CODE", "emoji": "🟠"},
|
||||
# "okx": {"name": "OKX", "url": "https://www.okx.com/join/YOUR_CODE", "emoji": "⚫"},
|
||||
# "coinbase": {"name": "Coinbase", "url": "https://coinbase.com/join/YOUR_CODE", "emoji": "🔵"},
|
||||
# "kucoin": {"name": "KuCoin", "url": "https://www.kucoin.com/r/YOUR_CODE", "emoji": "🟢"},
|
||||
# "gate": {"name": "Gate.io", "url": "https://www.gate.io/ref/YOUR_CODE", "emoji": "🔷"},
|
||||
# "mexc": {"name": "MEXC", "url": "https://www.mexc.com/register?inviteCode=YOUR_CODE", "emoji": "🔶"},
|
||||
# "bitget": {"name": "Bitget", "url": "https://www.bitget.com/referral?clacCode=YOUR_CODE", "emoji": "🌊"},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# TOP-UP PACKS (Buy extra when you run out)
|
||||
# ══════════════════════════════════════════════
|
||||
# One-time purchases via Telegram Stars. No expiry -
|
||||
# top-up balance carries over forever until used.
|
||||
TOP_UP_PACKS = {
|
||||
"scans_10": {
|
||||
"name": "10 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 10,
|
||||
"stars": 75,
|
||||
},
|
||||
"scans_50": {
|
||||
"name": "50 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 50,
|
||||
"stars": 300,
|
||||
},
|
||||
"scans_100": {
|
||||
"name": "100 Extra Scans",
|
||||
"emoji": "🔬",
|
||||
"type": "scans",
|
||||
"amount": 100,
|
||||
"stars": 500,
|
||||
},
|
||||
"ai_10": {
|
||||
"name": "10 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 10,
|
||||
"stars": 100,
|
||||
},
|
||||
"ai_50": {
|
||||
"name": "50 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 50,
|
||||
"stars": 400,
|
||||
},
|
||||
"ai_100": {
|
||||
"name": "100 Extra AI Messages",
|
||||
"emoji": "🤖",
|
||||
"type": "ai_msgs",
|
||||
"amount": 100,
|
||||
"stars": 700,
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# SCAN PACKS (One-time deep analysis purchases)
|
||||
# ══════════════════════════════════════════════
|
||||
SCAN_PACKS = {
|
||||
"deep_scan": {
|
||||
"name": "🔬 Deep Token Scan",
|
||||
"description": "Complete forensic analysis of ONE token",
|
||||
"includes": [
|
||||
"Price history",
|
||||
"Holder bubble map",
|
||||
"Wallet clusters",
|
||||
"Scam indicators",
|
||||
"AI prediction",
|
||||
"Dev history",
|
||||
],
|
||||
"stars": 250,
|
||||
},
|
||||
"wallet_forensic": {
|
||||
"name": "🕵️ Wallet Forensic Report",
|
||||
"description": "Full wallet investigation - P&L, connections, history",
|
||||
"includes": [
|
||||
"Transaction history",
|
||||
"Counterparty analysis",
|
||||
"P&L breakdown",
|
||||
"Bundle links",
|
||||
"Scam DB check",
|
||||
],
|
||||
"stars": 400,
|
||||
},
|
||||
"bundle_investigation": {
|
||||
"name": "🕸️ Bundle Investigation",
|
||||
"description": "Full cluster analysis - find every connected wallet",
|
||||
"includes": [
|
||||
"Funding source map",
|
||||
"Timing correlation",
|
||||
"Wash trade detection",
|
||||
"Holder overlap",
|
||||
"Exit strategy clues",
|
||||
],
|
||||
"stars": 500,
|
||||
},
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# DEX REFERRAL LINKS
|
||||
# ══════════════════════════════════════════════
|
||||
# These generate revenue when users trade through our links.
|
||||
# Canonical definitions live in app.domains.referral.partners.
|
||||
|
||||
# Chain ID mapping for 1inch
|
||||
CHAIN_IDS = {
|
||||
"ethereum": 1,
|
||||
"bsc": 56,
|
||||
"polygon": 137,
|
||||
"arbitrum": 42161,
|
||||
"avalanche": 43114,
|
||||
"optimism": 10,
|
||||
"base": 8453,
|
||||
"fantom": 250,
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# CHANNELS
|
||||
# ══════════════════════════════════════════════
|
||||
CHANNELS = {
|
||||
"main": "-1002056885429",
|
||||
"news": "-1003982391578",
|
||||
"alerts": "-1003818352164",
|
||||
"alpha": "-1003762675055",
|
||||
"scans": "-1003937506770",
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# RISK VISUALIZATION
|
||||
# ══════════════════════════════════════════════
|
||||
RISK_COLORS = {
|
||||
"critical": "🔴",
|
||||
"high": "🟠",
|
||||
"medium": "🟡",
|
||||
"low": "🟢",
|
||||
"safe": "✅",
|
||||
"unknown": "⚪",
|
||||
}
|
||||
|
||||
|
||||
def risk_bar(score: float, length: int = 10) -> str:
|
||||
"""Generate a visual risk bar (0-100 scale)."""
|
||||
filled = int((score / 100) * length)
|
||||
if score >= 80:
|
||||
return "🔴" * filled + "⬜" * (length - filled) + f" {score}% DANGER"
|
||||
elif score >= 60:
|
||||
return "🟠" * filled + "⬜" * (length - filled) + f" {score}% RISKY"
|
||||
elif score >= 40:
|
||||
return "🟡" * filled + "⬜" * (length - filled) + f" {score}% CAUTION"
|
||||
elif score >= 20:
|
||||
return "🟢" * filled + "⬜" * (length - filled) + f" {score}% LOW"
|
||||
else:
|
||||
return "✅" * filled + "⬜" * (length - filled) + f" {score}% SAFE"
|
||||
|
||||
|
||||
def threat_indicator(label: str, found: bool) -> str:
|
||||
return f"{'🔴' if found else '✅'} {label}"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# FORMATTING HELPERS
|
||||
# ══════════════════════════════════════════════
|
||||
def fmt_number(n) -> str:
|
||||
"""Format large numbers: 1.2M, 450K, etc."""
|
||||
if n is None:
|
||||
return "N/A"
|
||||
try:
|
||||
n = float(n)
|
||||
except (ValueError, TypeError):
|
||||
return str(n)
|
||||
if n >= 1e9:
|
||||
return f"${n / 1e9:.2f}B"
|
||||
elif n >= 1e6:
|
||||
return f"${n / 1e6:.2f}M"
|
||||
elif n >= 1e3:
|
||||
return f"${n / 1e3:.1f}K"
|
||||
elif n >= 1:
|
||||
return f"${n:.4f}"
|
||||
elif n >= 0.01:
|
||||
return f"${n:.6f}"
|
||||
else:
|
||||
return f"${n:.10f}"
|
||||
|
||||
|
||||
def fmt_pct(n) -> str:
|
||||
if n is None:
|
||||
return "N/A"
|
||||
try:
|
||||
return f"{float(n):.2f}%"
|
||||
except (ValueError, TypeError):
|
||||
return str(n)
|
||||
|
||||
|
||||
def fmt_chain(chain: str) -> str:
|
||||
"""Return chain emoji + name."""
|
||||
emojis = {
|
||||
"solana": "◎",
|
||||
"ethereum": "⟠",
|
||||
"bsc": "🔶",
|
||||
"polygon": "🟣",
|
||||
"arbitrum": "🔵",
|
||||
"optimism": "🔴",
|
||||
"base": "🔷",
|
||||
"avalanche": "🔺",
|
||||
"fantom": "👻",
|
||||
"tron": "🔻",
|
||||
"bitcoin": "₿",
|
||||
}
|
||||
return f"{emojis.get(chain.lower(), '⛓️')} {chain.capitalize()}"
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# SCAM SCHOOL TOPICS
|
||||
# ══════════════════════════════════════════════
|
||||
SCAM_SCHOOL_TOPICS = {
|
||||
"honeypot": {
|
||||
"title": "🍯 Honeypot Tokens",
|
||||
"content": "A honeypot lets you buy but prevents you from selling. The contract has hidden code that only allows whitelisted addresses to sell.\n\n🔍 *How to spot:*\n• Can buy but can't sell\n• Only 1-2 sellers ever\n• Contract has sell restrictions\n• Honeypot.is flags it\n\n🛡️ *Protection:* Always test with a small amount first, and use /scan before buying.",
|
||||
},
|
||||
"rugpull": {
|
||||
"title": "🏃 Rug Pulls",
|
||||
"content": "Developers drain all liquidity from a token, making it worthless. Can happen via:\n\n• Removing LP tokens\n• Minting infinite tokens\n• Hidden sell functions\n• Blacklisting buyers\n\n🔍 *How to spot:*\n• Unlocked liquidity\n• Unrenounced contract\n• Dev holds >20% supply\n• New wallet, no history\n\n🛡️ *Protection:* Check /scan for LP lock status and dev wallet analysis.",
|
||||
},
|
||||
"bundling": {
|
||||
"title": "📦 Bundle Scams",
|
||||
"content": "Devs use multiple wallets to create fake organic activity. They buy from 10-50 wallets funded from the same source.\n\n🔍 *How to spot:*\n• Multiple wallets funded from same CEX/bridge\n• Identical buy amounts\n• Same-timestamp transactions\n• Wallets only hold this one token\n\n🛡️ *Protection:* Use /scan bundle detection to trace funding sources.",
|
||||
},
|
||||
"wash_trading": {
|
||||
"title": "🔄 Wash Trading",
|
||||
"content": "Artificially inflating volume by buying and selling between own wallets.\n\n🔍 *How to spot:*\n• High volume, low unique traders\n• Same wallets trading back and forth\n• Round number trade amounts\n• Volume doesn't match market cap\n\n🛡️ *Protection:* /scan checks volume authenticity.",
|
||||
},
|
||||
"fresh_wallets": {
|
||||
"title": "🆕 Fresh Wallet Red Flags",
|
||||
"content": "When most holders are brand new wallets (created <7 days ago), it often means the dev created fake holders.\n\n🔍 *How to spot:*\n• >50% wallets <7 days old\n• Wallets only hold 1 token\n• No transaction history before buying\n• Similar naming patterns\n\n🛡️ *Protection:* /scan shows wallet age distribution.",
|
||||
},
|
||||
"exchange_flow": {
|
||||
"title": "🏦 Exchange Flow Analysis",
|
||||
"content": "Tracking when large holders move tokens to exchanges signals potential dumps.\n\n🔍 *How to spot:*\n• Top holders sending to CEX wallets\n• Increasing exchange deposits\n• Unusual transfer patterns\n• Pre-scheduled dumps\n\n🛡️ *Protection:* Hunter+ tiers get real-time exchange flow alerts.",
|
||||
},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue