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:
1 parent
eaefad6c1d
commit
1e93437a90
7 files changed
+345
-31
No files matched your search
+46
-1
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user