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
@@ -16,6 +16,7 @@ import requests
|
||||
|
||||
import db
|
||||
import get_voters as gv
|
||||
import telegram
|
||||
|
||||
|
||||
def _fresh_weight(staked_raw, days_ago=5):
|
||||
@@ -257,6 +258,115 @@ def main():
|
||||
check('V41 first_seen idempoten (record ulang ⊥ overwrite)',
|
||||
fs['aaa1'] == scanned_a and fs['aaa1'] != '2000-01-01T00:00:00')
|
||||
|
||||
# V66: voter_notify — tabel lacak notifikasi VOTE ULANG SEGERA
|
||||
db.ensure_notify_schema()
|
||||
conn = sqlite3.connect(db.DB_PATH)
|
||||
cols = [r[1] for r in conn.execute(
|
||||
'PRAGMA table_info(voter_notify)').fetchall()]
|
||||
pk = [r[1] for r in conn.execute(
|
||||
'PRAGMA table_info(voter_notify)').fetchall() if r[5]]
|
||||
conn.close()
|
||||
check('V66 voter_notify tabel ada', 'owner' in cols)
|
||||
check('V66 voter_notify PK = owner', pk == ['owner'])
|
||||
check('V66 urgency kolom ada', 'urgency' in cols)
|
||||
check('V66 notified_at kolom ada', 'notified_at' in cols)
|
||||
|
||||
# record_notify: insert baru
|
||||
db.record_notify('aaa1', 1, '2026-08-27T08:00:00')
|
||||
check('V66 get_notify_urgency(aaa1) = 1', db.get_notify_urgency('aaa1') == 1)
|
||||
check('V66 get_notify_urgency(unk) = None', db.get_notify_urgency('unk9') is None)
|
||||
|
||||
# record_notify: idempoten (INSERT OR IGNORE) — urgency tetap 1
|
||||
db.record_notify('aaa1', 1, '2026-08-27T08:00:00')
|
||||
check('V66 record_notify idempoten', db.get_notify_urgency('aaa1') == 1)
|
||||
|
||||
# record_notify: urgency naik → update
|
||||
db.record_notify('aaa1', 2, '2026-08-28T08:00:00')
|
||||
check('V66 urgency naik 1→2', db.get_notify_urgency('aaa1') == 2)
|
||||
# urgency turun → ⊥ update
|
||||
db.record_notify('aaa1', 1, '2026-08-29T08:00:00')
|
||||
check('V66 urgency turun 2→1 ⊥ update', db.get_notify_urgency('aaa1') == 2)
|
||||
|
||||
# cleanup_notify: hapus baris tua
|
||||
db.record_notify('old1', 1, '2026-08-01T00:00:00')
|
||||
db.record_notify('new1', 1, '2026-08-27T00:00:00')
|
||||
db.cleanup_notify('2026-08-15T00:00:00')
|
||||
check('V66 cleanup hapus baris tua', db.get_notify_urgency('old1') is None)
|
||||
check('V66 cleanup simpan baris baru', db.get_notify_urgency('new1') == 1)
|
||||
|
||||
# V66: _notify_warn — integrasi dengan telegram.notify_warn
|
||||
# Siapkan pemilih dalam jendela VOTE ULANG SEGERA (last_vote 26 hari lalu)
|
||||
warn_now = datetime(2026, 8, 27, 8, 0, 0)
|
||||
warn_vote = (warn_now - timedelta(days=26)).isoformat(timespec='seconds')
|
||||
safe_vote = (warn_now - timedelta(days=20)).isoformat(timespec='seconds')
|
||||
# Masukkan pemilih langsung ke DB (bypass scan)
|
||||
wrn2_vote = (warn_now - timedelta(days=27)).isoformat(timespec='seconds')
|
||||
db.replace_snapshot([
|
||||
{'owner': 'wrn1', 'weight': '1.0', 'staked': 5000.0,
|
||||
'last_vote': warn_vote},
|
||||
{'owner': 'wrn2', 'weight': '2.0', 'staked': 3000.0,
|
||||
'last_vote': wrn2_vote},
|
||||
{'owner': 'safe', 'weight': '3.0', 'staked': 8000.0,
|
||||
'last_vote': safe_vote},
|
||||
], warn_now.isoformat(timespec='seconds'))
|
||||
|
||||
sent = []
|
||||
_orig_warn = telegram.notify_warn
|
||||
telegram.notify_warn = lambda v: sent.append(v)
|
||||
try:
|
||||
gv._notify_warn(warn_now)
|
||||
check('V66 _notify_warn dipanggil', len(sent) == 1)
|
||||
voters_notified = sent[0]
|
||||
owners_notified = [v[0] for v in voters_notified]
|
||||
check('V66 wrn2 (27h, ≤2d) masuk', 'wrn2' in owners_notified)
|
||||
check('V66 wrn1 (26h, >2d) masuk', 'wrn1' in owners_notified)
|
||||
check('V66 safe (20h, ⊥ window) tak masuk', 'safe' not in owners_notified)
|
||||
# Cek urgensi
|
||||
by_owner = {v[0]: v[2] for v in voters_notified}
|
||||
check('V66 wrn2 urgensi = 3 (≤1d)', by_owner['wrn2'] == 3)
|
||||
check('V66 wrn1 urgensi = 2 (≤2d)', by_owner['wrn1'] == 2)
|
||||
# Cek voter_notify tercatat
|
||||
check('V66 voter_notify wrn2 = 3', db.get_notify_urgency('wrn2') == 3)
|
||||
check('V66 voter_notify wrn1 = 2', db.get_notify_urgency('wrn1') == 2)
|
||||
finally:
|
||||
telegram.notify_warn = _orig_warn
|
||||
|
||||
# V66: _notify_warn — skip bila sudah diberi tahu pada urgensi ≥ saat ini
|
||||
sent.clear()
|
||||
telegram.notify_warn = lambda v: sent.append(v)
|
||||
try:
|
||||
gv._notify_warn(warn_now) # wrn1 sudah urgensi 2, wrn2 sudah urgensi 3
|
||||
check('V66 _notify_warn skip redundancy', len(sent) == 0)
|
||||
finally:
|
||||
telegram.notify_warn = _orig_warn
|
||||
|
||||
# V66: _notify_warn — escalation trigger (wrn1 dari 2→3)
|
||||
sent.clear()
|
||||
# Hapus lacak utk wrn1 supaya bisa test escalation dratch
|
||||
conn = sqlite3.connect(db.DB_PATH)
|
||||
conn.execute('DELETE FROM voter_notify WHERE owner = ?', ('wrn1',))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
# Update wrn1 last_vote ke 27 hari lalu (≤1d → urgensi 3)
|
||||
closer_vote = (warn_now - timedelta(days=27)).isoformat(
|
||||
timespec='seconds')
|
||||
db.replace_snapshot([
|
||||
{'owner': 'wrn1', 'weight': '1.0', 'staked': 5000.0,
|
||||
'last_vote': closer_vote},
|
||||
{'owner': 'wrn2', 'weight': '2.0', 'staked': 3000.0,
|
||||
'last_vote': wrn2_vote},
|
||||
], warn_now.isoformat(timespec='seconds'))
|
||||
telegram.notify_warn = lambda v: sent.append(v)
|
||||
try:
|
||||
gv._notify_warn(warn_now)
|
||||
check('V66 escalation trigger (2→3)', len(sent) == 1)
|
||||
by_owner = {v[0]: v[2] for v in sent[0]}
|
||||
check('V66 wrn1 escalation ke urgensi 3', by_owner['wrn1'] == 3)
|
||||
check('V66 voter_notify wrn1 updated ke 3',
|
||||
db.get_notify_urgency('wrn1') == 3)
|
||||
finally:
|
||||
telegram.notify_warn = _orig_warn
|
||||
|
||||
print('\nSEMUA UJI PASS')
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user