โ† ะะฐะทะฐะด
""" AlphaPulse Bot โ€” Card Templates & Visual Helpers All post formatting lives here. Every post goes through build_card() for consistent branding. """ import random from config import BASE_HASHTAGS, AFFILIATE_LINKS, PREMIUM_TEASERS # โ”€โ”€โ”€ Visual constants โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ HEADER_LINE = "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" FOOTER_LINE = "โ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆ" BRAND = "โšก <b>AlphaPulse</b> ยท @alphapulsexp" # Post counter for rotation logic _post_seq = {'count': 0} # Spark bar characters (index 0 = lowest, 7 = highest) SPARK_CHARS = "โ–โ–‚โ–ƒโ–„โ–…โ–†โ–‡โ–ˆ" def spark_bar(values: list[float]) -> str: """Convert a list of floats into a Unicode sparkline bar. E.g. [1, 3, 5, 2, 4] -> 'โ–‚โ–„โ–ˆโ–ƒโ–…' """ if not values or len(values) < 2: return "" mn, mx = min(values), max(values) spread = mx - mn if spread == 0: return SPARK_CHARS[4] * len(values) return "".join( SPARK_CHARS[min(int((v - mn) / spread * 7), 7)] for v in values ) def progress_bar(value: int, max_val: int = 100, width: int = 10) -> str: """Render a progress bar: [โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘] 78/100 """ filled = round(value / max_val * width) filled = max(0, min(width, filled)) empty = width - filled return f"[{'โ–ˆ' * filled}{'โ–‘' * empty}] {value}/{max_val}" def format_price_line(emoji: str, symbol: str, price: float, change_24h: float, sparkline: str = '') -> str: """Format a single price line with arrow and optional sparkline. E.g.: โ‚ฟ BTC $87,432 โ–ฒ +2.1% โ–โ–‚โ–ƒโ–…โ–‡ """ arrow = '๐ŸŸข' if change_24h > 0 else '๐Ÿ”ด' if change_24h < 0 else 'โšช' # Format price: no decimals for BTC, 0 decimals for ETH, 2 for SOL if price >= 1000: price_str = f"${price:,.0f}" elif price >= 10: price_str = f"${price:,.1f}" else: price_str = f"${price:,.2f}" line = f"{emoji} {symbol} <b>{price_str}</b> {arrow} {change_24h:+.1f}%" if sparkline: line += f" {sparkline}" return line def pick_affiliate() -> dict: """Weighted random affiliate link selection.""" weights = [a['weight'] for a in AFFILIATE_LINKS] return random.choices(AFFILIATE_LINKS, weights=weights, k=1)[0] def pick_premium_teaser() -> str: """Random premium teaser line.""" return random.choice(PREMIUM_TEASERS) def build_card(header_emoji: str, title: str, body_lines: list[str], hashtags: str = '', show_brand: bool = True, show_affiliate: bool = False, show_premium: bool = False) -> str: """Universal card wrapper. โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ๐Ÿง  MARKET ANALYSIS โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” body lines here... #hashtags ๐Ÿ’ผ Trade on Binance (optional affiliate) ๐Ÿ”’ Premium teaser (optional) โ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆโ”ˆ โšก AlphaPulse ยท @alphapulsexp """ _post_seq['count'] += 1 parts = [ HEADER_LINE, f"{header_emoji} <b>{title}</b>", HEADER_LINE, "", ] parts.extend(body_lines) if hashtags: parts += ["", hashtags] # Affiliate link (weighted rotation) if show_affiliate: aff = pick_affiliate() parts += ["", f"<a href='{aff['url']}'>{aff['cta']}</a>"] # Premium teaser if show_premium: parts += ["", f"<i>{pick_premium_teaser()}</i>"] if show_brand: parts += ["", FOOTER_LINE, BRAND] return "\n".join(parts) # โ”€โ”€โ”€ Specific card builders โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ def card_news(emoji: str, commentary: str, source: str, url: str, hashtags: str, add_affiliate: bool = False) -> str: """News post card with AI commentary.""" body = [commentary] body.append("") body.append(f"๐Ÿ“ฐ <i>{source}</i>") if url: body.append(f"๐Ÿ”— <a href='{url}'>Read full article</a>") return build_card(emoji, "CRYPTO NEWS", body, hashtags, show_affiliate=add_affiliate) def card_price_snapshot(prices: dict, fg: dict = None, dominance: dict = None, sparklines: dict = None) -> str: """Daily price snapshot card.""" sp = sparklines or {} body = [ format_price_line("โ‚ฟ", "BTC", prices['btc_price'], prices['btc_change'], sp.get('btc', '')), format_price_line("โŸ ", "ETH", prices['eth_price'], prices['eth_change'], sp.get('eth', '')), format_price_line("โ—Ž", "SOL", prices['sol_price'], prices.get('sol_change', 0), sp.get('sol', '')), ] if dominance: mcap_arrow = 'โ–ฒ' if dominance.get('mcap_change', 0) > 0 else 'โ–ผ' body += [ "", f"๐Ÿ”ต BTC.D: <b>{dominance['btc_dominance']}%</b> โ”‚ " f"ETH.D: {dominance['eth_dominance']}%", f"๐Ÿ“ฆ MCap: <b>${dominance['total_mcap'] / 1e12:.2f}T</b> " f"{mcap_arrow} {dominance['mcap_change']:+.1f}%", ] if fg: bar = progress_bar(fg['value']) body += [ "", f"๐Ÿ˜ฑ Fear & Greed: <b>{fg['value']}</b> โ€” {fg['classification']}", f"<code>{bar}</code>", ] # Show premium teaser on ~20% of price snapshots show_prem = random.random() < 0.2 return build_card("๐Ÿ’น", "PRICE SNAPSHOT", body, BASE_HASHTAGS, show_premium=show_prem) def card_trending(coins: list, date_str: str) -> str: """Trending coins card.""" medals = ['๐Ÿฅ‡', '๐Ÿฅˆ', '๐Ÿฅ‰', '4๏ธโƒฃ', '5๏ธโƒฃ', '6๏ธโƒฃ', '7๏ธโƒฃ'] body = [f"<i>{date_str} ยท CoinGecko</i>", ""] for i, c in enumerate(coins[:7]): rank = f"<i>#{c['rank']}</i>" if c['rank'] != '?' else '' body.append(f"{medals[i]} <b>{c['symbol']}</b> {c['name']} {rank}") return build_card("๐Ÿ”ฅ", "TRENDING NOW", body, "#crypto #trending #altcoins") def card_top_movers(gainers: list, losers: list) -> str: """Top movers card (gainers + losers).""" body = [] if gainers: body.append("๐ŸŸข <b>Top Gainers</b>") for c in gainers[:3]: sym = c['symbol'].upper() pct = c['price_change_percentage_24h'] or 0 price = c['current_price'] if price >= 1000: p = f"${price:,.0f}" elif price >= 1: p = f"${price:,.2f}" else: p = f"${price:,.4f}" body.append(f" โ–ฒ {sym} <b>{p}</b> <b>+{pct:.1f}%</b>") if gainers and losers: body.append("") if losers: body.append("๐Ÿ”ด <b>Top Losers</b>") for c in losers[:3]: sym = c['symbol'].upper() pct = c['price_change_percentage_24h'] or 0 price = c['current_price'] if price >= 1000: p = f"${price:,.0f}" elif price >= 1: p = f"${price:,.2f}" else: p = f"${price:,.4f}" body.append(f" โ–ผ {sym} <b>{p}</b> <b>{pct:.1f}%</b>") return build_card("๐Ÿ†", "24H TOP MOVERS", body, BASE_HASHTAGS) def card_weekly_digest(prices: dict, fg: dict = None, gainers: list = None, week_range: str = '') -> str: """Sunday weekly digest card.""" btc_arrow = 'โ–ฒ' if (prices.get('btc_7d') or 0) > 0 else 'โ–ผ' eth_arrow = 'โ–ฒ' if (prices.get('eth_7d') or 0) > 0 else 'โ–ผ' sol_arrow = 'โ–ฒ' if (prices.get('sol_7d') or 0) > 0 else 'โ–ผ' body = [ f"<i>{week_range}</i>", "", "๐Ÿ“ˆ <b>7-Day Performance</b>", f"โ‚ฟ BTC <b>${prices['btc_price']:,.0f}</b> {btc_arrow} {prices.get('btc_7d', 0):+.1f}%", f"โŸ  ETH <b>${prices['eth_price']:,.0f}</b> {eth_arrow} {prices.get('eth_7d', 0):+.1f}%", f"โ—Ž SOL <b>${prices['sol_price']:,.2f}</b> {sol_arrow} {prices.get('sol_7d', 0):+.1f}%", ] if fg: bar = progress_bar(fg['value']) body += [ "", f"๐Ÿ˜ฑ Sentiment: <b>{fg['classification']}</b> ({fg['value']})", f"<code>{bar}</code>", ] if gainers and len(gainers) >= 3: body += ["", "๐Ÿ† <b>Week's Top Gainers (24h)</b>"] for c in gainers[:3]: pct = c['price_change_percentage_24h'] or 0 body.append(f" โ–ฒ {c['symbol'].upper()} <b>+{pct:.1f}%</b>") body += [ "", "๐Ÿ’ฌ How was your week? Drop it below ๐Ÿ‘‡", ] return build_card("๐Ÿ“…", "WEEKLY DIGEST", body, BASE_HASHTAGS) def card_fear_greed(value: int, classification: str, comment: str) -> str: """Standalone Fear & Greed card.""" bar = progress_bar(value) # Emoji based on level if value <= 25: level_emoji = "๐Ÿ˜ฑ" elif value <= 45: level_emoji = "๐Ÿ˜ฐ" elif value <= 55: level_emoji = "๐Ÿ˜" elif value <= 75: level_emoji = "๐Ÿค‘" else: level_emoji = "๐Ÿš€" body = [ f"{level_emoji} <b>{classification}</b>", "", f"<code>{bar}</code>", "", comment, ] return build_card("๐ŸŽš๏ธ", "FEAR & GREED INDEX", body, BASE_HASHTAGS) def card_funding_rates(rates: list) -> str: """Funding rates card โ€” shows market long/short bias.""" body = [ "<i>Binance Futures ยท 8h Rate</i>", "", ] # Split into positive (longs pay) and negative (shorts pay) longs_pay = [r for r in rates if r['rate'] > 0][:5] shorts_pay = [r for r in rates if r['rate'] < 0][:5] if longs_pay: body.append("๐ŸŸข <b>Longs Pay (bullish crowd)</b>") for r in longs_pay: body.append(f" {r['symbol']} <b>{r['rate_pct']:+.4f}%</b>") if longs_pay and shorts_pay: body.append("") if shorts_pay: body.append("๐Ÿ”ด <b>Shorts Pay (bearish crowd)</b>") for r in shorts_pay: body.append(f" {r['symbol']} <b>{r['rate_pct']:+.4f}%</b>") # Summary line total_positive = len([r for r in rates if r['rate'] > 0]) total_negative = len([r for r in rates if r['rate'] < 0]) if total_positive + total_negative > 0: bias = "Longs dominant ๐Ÿ‚" if total_positive > total_negative else "Shorts dominant ๐Ÿป" body += ["", f"๐Ÿ“Š Market bias: <b>{bias}</b>"] return build_card("๐Ÿ“Š", "FUNDING RATES", body, "#crypto #futures #trading #BTC #funding", show_affiliate=True) def card_watchlist(coins: list, ai_commentary: str, date_str: str) -> str: """Monday watchlist card โ€” coins to watch this week.""" body = [ f"<i>Week of {date_str}</i>", "", ] for i, coin in enumerate(coins[:5], 1): emoji = ['1๏ธโƒฃ', '2๏ธโƒฃ', '3๏ธโƒฃ', '4๏ธโƒฃ', '5๏ธโƒฃ'][i - 1] sym = coin.get('symbol', '').upper() reason = coin.get('reason', '') body.append(f"{emoji} <b>{sym}</b> โ€” {reason}") if ai_commentary: body += ["", f"๐Ÿ’ก <i>{ai_commentary}</i>"] return build_card("๐ŸŽฏ", "WEEKLY WATCHLIST", body, "#crypto #watchlist #trading #altcoins") def card_history(events: list, date_str: str, ai_reflection: str = '') -> str: """'This Day in Crypto' historical card.""" body = [ f"<i>{date_str}</i>", "", ] for e in events: year = e['year'] icon = e.get('icon', '๐Ÿ“…') text = e['event'] nearby = e.get('_nearby', False) prefix = "~" if nearby else "" body.append(f"{icon} <b>{prefix}{year}</b> โ€” {text}") if ai_reflection: body += ["", f"๐Ÿ’ญ <i>{ai_reflection}</i>"] body += ["", "โ™ป๏ธ History doesn't repeat, but it rhymes"] return build_card("๐Ÿ•ฐ๏ธ", "THIS DAY IN CRYPTO", body, "#crypto #history #bitcoin #OnThisDay") def card_long_short(positions: list) -> str: """Market positioning card โ€” Long/Short ratios for top coins.""" # Count overall bias bulls = sum(1 for p in positions if p['ratio'] > 1.2) bears = sum(1 for p in positions if p['ratio'] < 0.8) if bulls > bears + 2: verdict = "๐Ÿ‚ Market heavily long โ€” watch for squeeze" elif bears > bulls + 2: verdict = "๐Ÿป Market heavily short โ€” squeeze risk high" else: verdict = "โš–๏ธ Mixed positioning" body = [ "<i>Binance Futures ยท Account Ratios</i>", "", ] for p in positions: long_pct = p['long_pct'] * 100 short_pct = p['short_pct'] * 100 # Visual bar: 10 chars total long_blocks = round(long_pct / 10) long_blocks = max(0, min(10, long_blocks)) short_blocks = 10 - long_blocks bar = f"๐ŸŸข{'โ–ˆ' * long_blocks}{'โ–‘' * short_blocks}๐Ÿ”ด" sym = f"{p['symbol']:>5}" body.append(f"<code>{sym}</code> {bar} L:{long_pct:.0f}% S:{short_pct:.0f}%") body += ["", f"๐Ÿ“Š <i>{verdict}</i>"] return build_card("๐Ÿ“", "MARKET POSITIONING", body, "#crypto #futures #longshort #trading", show_affiliate=True, show_premium=True) def card_whale_alerts(alerts: list) -> str: """Whale alert card โ€” large BTC transactions.""" body = [ "<i>Blockchair ยท Largest BTC Transactions (24h)</i>", "", ] for a in alerts: btc = a['amount_btc'] usd_approx = "" # would need BTC price body.append(f"๐Ÿ‹ <b>{btc:,.1f} BTC</b> tx: <code>{a['hash']}</code>") body += [ "", "๐Ÿ” <i>Large moves often precede volatility</i>", ] return build_card("๐Ÿณ", "WHALE WATCH", body, "#crypto #bitcoin #whales #onchain")