feat: VOTE ULANG SEGERA Telegram notification + emoticon on all notifications

- telegram.py: emoticons on all existing notif (🚀✅⚠️🔴💰🔍) + new notify_warn for expiring voters
- db.py: voter_notify table (owner/urgency/notified_at) for state-change dedup
- get_voters.py: _notify_warn() after persist() — urgency escalation ⚠️/🔴/🚨 by days remaining
- Dockerfile.scan: add telegram.py to COPY
- test_get_voters.py: 15 new V66 assertions (schema, record, cleanup, integration, escalation)
- test_distribute.py: updated notification format assertions
- SPEC.md: V66 + B26
This commit is contained in:
proitlab committed 2026-08-27 16:12:53 +07:00
1 parent eaefad6c1d
commit 1e93437a90
7 files changed
+345 -31

No files matched your search

+46 -1
View File
@@ -15,7 +15,9 @@ from datetime import datetime, timedelta, timezone
import requests
import db
from config import API_NODE, MIN_STAKED_VEX, TARGET_BP
import telegram
from config import (API_NODE, MIN_STAKED_VEX, TARGET_BP,
VEX_EXPIRED_DAYS, VEX_STALE_DAYS, VEX_WARN_DAYS)
# Kontrak & scope sistem Vexanium = vexcore (BUKAN vexio sesuai konvensi EOS)
CODE = 'vexcore'
@@ -175,6 +177,48 @@ def summarize(rows, scanned_at):
f"{row['staked']:,.4f} VEX - Bobot: {row['weight']}")
def _notify_warn(now):
"""V66: kirim notifikasi VOTE ULANG SEGERA ke komunitas.
Query pemilih dalam jendela `now − STALE < last_vote ≤ now − (STALE − WARN)`,
hitung urgensi berdasarkan sisa hari (1=⚠️ >2h, 2=🔴 ≤2h, 3=🚨 ≤1h),
filter yang sudah diberi tahu pada level ≥ urgensi saat ini, kirim
via Telegram, catat ke `voter_notify`. Bersihkan baris stale.
"""
stale_cutoff = (now - timedelta(days=VEX_STALE_DAYS)).isoformat(timespec='seconds')
warn_cutoff = (now - timedelta(days=VEX_STALE_DAYS - VEX_WARN_DAYS)).isoformat(
timespec='seconds')
rows = db.query(
'SELECT v.owner, v.staked, v.last_vote FROM voters v '
'WHERE v.last_vote > %s AND v.last_vote <= %s',
(stale_cutoff, warn_cutoff))
if not rows:
db.cleanup_notify((now - timedelta(days=VEX_EXPIRED_DAYS)).isoformat(
timespec='seconds'))
return
to_notify = []
for owner, staked, last_vote in rows:
vote_dt = datetime.fromisoformat(last_vote)
days_left = VEX_STALE_DAYS - (now - vote_dt).days
if days_left <= 1:
urgency = 3
elif days_left <= 2:
urgency = 2
else:
urgency = 1
prev = db.get_notify_urgency(owner)
if prev is not None and prev >= urgency:
continue
to_notify.append((owner, f'{staked:,.4f}', urgency))
if to_notify:
telegram.notify_warn(to_notify)
for owner, _, urgency in to_notify:
db.record_notify(owner, urgency,
now.isoformat(timespec='seconds'))
db.cleanup_notify((now - timedelta(days=VEX_EXPIRED_DAYS)).isoformat(
timespec='seconds'))
def main():
"""Titik masuk CLI."""
print(f'Mulai memindai voters untuk BP: "{TARGET_BP}"...')
@@ -186,6 +230,7 @@ def main():
if item is not None:
filtered.append(item)
scanned_at = persist(filtered)
_notify_warn(now)
summarize(filtered, scanned_at)
return 0