diff --git a/workspace/timmy-config/nostr-bridge/bridge_mvp.py b/workspace/timmy-config/nostr-bridge/bridge_mvp.py index ee126dde..48e48690 100644 --- a/workspace/timmy-config/nostr-bridge/bridge_mvp.py +++ b/workspace/timmy-config/nostr-bridge/bridge_mvp.py @@ -13,7 +13,7 @@ from urllib.request import Request, urlopen # nostr_sdk imports try: - from nostr_sdk import Keys, Client, Filter, Kind, NostrSigner, Timestamp, RelayUrl + from nostr_sdk import Keys, Client, Filter, Kind, NostrSigner, Timestamp, RelayUrl, PublicKey except ImportError as e: print(f"[ERROR] nostr_sdk import failed: {e}") sys.exit(1) @@ -51,6 +51,7 @@ def load_allowed_pubkeys(): # Alexander's pubkey is the primary operator if "alexander" in keystore: allowed.append(keystore["alexander"].get("pubkey", "")) + allowed.append(keystore["alexander"].get("hex_public", "")) return [p for p in allowed if p] # Gitea API helpers @@ -188,9 +189,11 @@ async def poll_dms(client, signer, since_ts): try: events = await client.fetch_events(filter_dm, timedelta(seconds=5)) - for event in events: + # Convert Events object to list for iteration + event_list = events.to_vec() + for event in event_list: author = event.author().to_hex() - author_npub = event.author().to_bech32() + author_npub = event.author().to_npub()[:32] # Verify sovereign identity if author not in allowed_pubkeys: @@ -202,9 +205,10 @@ async def poll_dms(client, signer, since_ts): # Decrypt content (requires NIP-44 or NIP-04 decryption) try: - # Try to decrypt - this is a simplified placeholder - # Real implementation would use signer.decrypt or similar - content = event.content() # May be encrypted + # Try to decrypt using signer's decrypt method + # Note: This is for NIP-04, NIP-44 may need different handling + decrypted = signer.decrypt(author, event.content()) + content = decrypted print(f" Content preview: {content[:80]}...") # Parse and execute command @@ -213,6 +217,24 @@ async def poll_dms(client, signer, since_ts): result = execute_command(cmd, author_npub) commands_executed += 1 print(f" ✅ {result.get('action', 'unknown')}: {result.get('message', '')[:60]}...") + + # Send acknowledgement DM back + try: + reply_content = f"ACK: {result.get('message', 'Command processed')[:200]}" + recipient = PublicKey.parse(author) + + # Build and send NIP-04 encrypted DM + from nostr_sdk import EventBuilder + dm_event = EventBuilder.encrypt_direct_msg( + signer=signer, + receiver=recipient, + content=reply_content + ) + # Publish to relay + await client.send_event_builder(dm_event) + print(f" [ACK SENT] DM reply delivered to {author_npub[:20]}...") + except Exception as ack_err: + print(f" [ACK ERROR] Failed to send acknowledgement: {ack_err}") else: print(f" [PARSE] Unrecognized command format") @@ -286,12 +308,16 @@ def main(): # Load allowed pubkeys allowed = load_allowed_pubkeys() print(f"[INIT] Allowed operators: {len(allowed)}") + for pk in allowed: + print(f" - {pk[:32]}...") # Run bridge loop try: asyncio.run(run_bridge_loop()) except Exception as e: print(f"\n[ERROR] Bridge crashed: {e}") + import traceback + traceback.print_exc() sys.exit(1) if __name__ == "__main__":