VOTE ULANG SEGERA never reached community channel though dashboard badge showed: - scan service lacked NFS config.env bind -> empty TELEGRAM_* -> send_text silent no-op - _notify_warn still record_notify'd -> fake dedup rows blocked future sends send_text/notify_warn now return delivery bool; record_notify only on True. scan service mounts config.env:/app/.env:ro.
175 lines
6.6 KiB
Python
175 lines
6.6 KiB
Python
"""Kirim notifikasi status distribusi + klaim + warn via Telegram Bot API (best-effort).
|
|
|
|
Dua kanal (V30/V36): KOMUNITAS (`TELEGRAM_COMMUNITY_CHAT_IDS`) hanya brief
|
|
mulai/selesai distribusi + VOTE ULANG SEGERA; INTERNAL (`TELEGRAM_INTERNAL_CHAT_IDS`)
|
|
detail distribusi + semua kegagalan + klaim reward (⊥ komunitas). Token tunggal
|
|
`TELEGRAM_BOT_TOKEN`. Tanpa token/daftar → kanal itu no-op senyap (⊥ ganggu
|
|
run). Gagal kirim hanya dicatat — ⊥ pernah crash distribusi/klaim.
|
|
|
|
V66: semua notifikasi kini punya emoticon prefix.
|
|
"""
|
|
|
|
import requests
|
|
|
|
from config import (TELEGRAM_BOT_TOKEN, TELEGRAM_COMMUNITY_CHAT_IDS,
|
|
TELEGRAM_INTERNAL_CHAT_IDS)
|
|
|
|
# Batas panjang pesan Telegram per kiriman (karakter)
|
|
_MAX_MSG_LEN = 4096
|
|
|
|
|
|
def send_text(text, chat_ids=None):
|
|
"""Kirim `text` ke daftar chat id. `chat_ids=None` → kanal komunitas.
|
|
|
|
Best-effort: tanpa token/daftar → no-op; gagal → log saja.
|
|
Return True bila berhasil ke SEMUA chat id (setidaknya satu terkirim),
|
|
False bila no-op (token/daftar kosong) atau ada yang gagal.
|
|
"""
|
|
if chat_ids is None:
|
|
chat_ids = TELEGRAM_COMMUNITY_CHAT_IDS
|
|
if not TELEGRAM_BOT_TOKEN or not chat_ids:
|
|
return False
|
|
url = f'https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage'
|
|
ok = True
|
|
for chat_id in chat_ids:
|
|
try:
|
|
resp = requests.post(url, json={'chat_id': chat_id, 'text': text},
|
|
timeout=10)
|
|
resp.raise_for_status()
|
|
except requests.RequestException as exc:
|
|
print(f'[Peringatan] Gagal kirim Telegram ke {chat_id}: {exc}',
|
|
flush=True)
|
|
ok = False
|
|
return ok
|
|
|
|
def send_internal(text):
|
|
"""Kirim ke kanal internal (detail + kegagalan + klaim)."""
|
|
send_text(text, TELEGRAM_INTERNAL_CHAT_IDS)
|
|
|
|
|
|
def notify_start(run_date, n_voters, total_share, balance):
|
|
"""Run nyata dimulai: komunitas brief + internal detail."""
|
|
send_text(f'🚀 DISTRIBUSI MULAI\n📅 Tanggal: {run_date}')
|
|
send_internal(
|
|
f'🚀 DISTRIBUSI MULAI\n'
|
|
f'📅 Tanggal: {run_date}\n'
|
|
f'👥 Pemilih: {n_voters}\n'
|
|
f'💎 Total share: {total_share} VEX\n'
|
|
f'💰 Saldo: {balance} VEX'
|
|
)
|
|
|
|
|
|
def notify_finish(status, sent, total, total_share, failed, run_date):
|
|
"""Run selesai: komunitas brief + internal detail (ok | partial)."""
|
|
if status == 'ok':
|
|
emoji = '✅'
|
|
label = 'Berhasil'
|
|
else:
|
|
emoji = '⚠️'
|
|
label = 'Sebagian'
|
|
send_text(
|
|
f'{emoji} DISTRIBUSI SELESAI\n'
|
|
f'📅 Tanggal: {run_date}\n'
|
|
f'📊 Status: {label}'
|
|
)
|
|
send_internal(
|
|
f'{emoji} DISTRIBUSI SELESAI\n'
|
|
f'📅 Tanggal: {run_date}\n'
|
|
f'📊 Status: {label}\n'
|
|
f'👥 Terkirim: {sent}/{total} pemilih\n'
|
|
f'💎 Total share: {total_share} VEX\n'
|
|
f'❌ Gagal: {len(failed)}'
|
|
)
|
|
|
|
|
|
def notify_failure(exc):
|
|
"""Run gagal total — kanal internal saja (ops detail)."""
|
|
send_internal(
|
|
f'🔴 DISTRIBUSI GAGAL\n'
|
|
f'❗ Error: {exc}'
|
|
)
|
|
|
|
|
|
def notify_claim(reward, fee, wallet):
|
|
"""Klaim reward BP sukses — kanal internal saja, ⊥ komunitas (V36)."""
|
|
send_internal(
|
|
f'💰 KLAIM REWARD SUKSES\n'
|
|
f'💎 Reward: {reward} VEX\n'
|
|
f'💸 Fee BP: {fee} VEX → {wallet}'
|
|
)
|
|
|
|
|
|
def notify_claim_unmeasured(txid_label):
|
|
"""Klaim mendarat tapi reward tak terukur (V45) — internal, ⊥ "sukses 0"."""
|
|
send_internal(
|
|
f'🔍 KLAIM MENDARAT · REWARD TAK TERUKUR\n'
|
|
f'⚠️ Periksa reward & fee BP manual (txid {txid_label})'
|
|
)
|
|
|
|
|
|
def notify_claim_failure(exc):
|
|
"""Kegagalan siklus klaim — kanal internal saja (V36)."""
|
|
send_internal(
|
|
f'🔴 KLAIM GAGAL\n'
|
|
f'❗ Error: {exc}'
|
|
)
|
|
|
|
|
|
# ——————————————————————————————————————————————————————————————————————
|
|
# V66: Notifikasi VOTE ULANG SEGERA — komunitas.
|
|
# voters = list of (owner, staked, urgency) di mana urgency 1=⚠️ 2=🔴 3=🚨.
|
|
# ——————————————————————————————————————————————————————————————————————
|
|
|
|
_URGENCY_EMOJI = {1: '⚠️', 2: '🔴', 3: '🚨'}
|
|
|
|
def notify_warn(voters):
|
|
"""VOTE ULANG SEGERA — komunitas, best-effort.
|
|
|
|
Kirim daftar akun yang vote-nya akan kedaluwarsa, diurutkan berdasarkan
|
|
urgensi (paling mendesak di atas). Maks 50 entri; sisanya arahkan ke
|
|
dashboard. Return True bila SEMUA pesan terkirim, False bila no-op/gagal.
|
|
"""
|
|
if not voters:
|
|
return True
|
|
# Urutkan: urgensi tinggi dulu (3 > 2 > 1)
|
|
sorted_voters = sorted(voters, key=lambda v: v[2], reverse=True)
|
|
truncated = len(sorted_voters) > 50
|
|
display = sorted_voters[:50]
|
|
# Emoji dari baris paling mendesak
|
|
top_emoji = _URGENCY_EMOJI.get(display[0][2], '⚠️')
|
|
lines = [f'{top_emoji} VOTE ULANG SEGERA', '',
|
|
f'{len(sorted_voters)} akun akan kedaluwarsa:', '']
|
|
for owner, staked, urgency in display:
|
|
emoji = _URGENCY_EMOJI.get(urgency, '⚠️')
|
|
lines.append(f'• {emoji} {owner} — {staked} VEX')
|
|
lines.append(f' ↳ https://mining.databisnis.id?q={owner}')
|
|
if truncated:
|
|
lines.append('')
|
|
lines.append('📋 Cek https://mining.databisnis.id untuk daftar lengkap')
|
|
lines.append('')
|
|
lines.append('Vote ulang sekarang agar tidak kehilangan hak bagian!')
|
|
text = '\n'.join(lines)
|
|
sent = True
|
|
# Telegram batas 4096 karakter — split bila perlu
|
|
if len(text) <= _MAX_MSG_LEN:
|
|
sent = send_text(text)
|
|
else:
|
|
# Kirim header dulu, lalu sisa per-chunk
|
|
header = f'{top_emoji} VOTE ULANG SEGERA\n\n{len(sorted_voters)} akun akan kedaluwarsa:\n'
|
|
sent = send_text(header) and sent
|
|
chunk = []
|
|
chunk_len = 0
|
|
for owner, staked, urgency in display:
|
|
emoji = _URGENCY_EMOJI.get(urgency, '⚠️')
|
|
line = f'• {emoji} {owner} — {staked} VEX\n ↳ https://mining.databisnis.id?q={owner}\n'
|
|
if chunk_len + len(line) > _MAX_MSG_LEN - 100:
|
|
sent = send_text('\n'.join(chunk)) and sent
|
|
chunk = []
|
|
chunk_len = 0
|
|
chunk.append(line.rstrip())
|
|
chunk_len += len(line)
|
|
if chunk:
|
|
sent = send_text('\n'.join(chunk)) and sent
|
|
sent = send_text('Vote ulang sekarang agar tidak kehilangan hak bagian!') and sent
|
|
return sent
|