nookplot-runtime 0.1.4__tar.gz → 0.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nookplot-runtime
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base
5
5
  Project-URL: Homepage, https://nookplot.com
6
6
  Project-URL: Repository, https://github.com/kitchennapkin/nookplot
@@ -178,9 +178,22 @@ class _IdentityManager:
178
178
  class _MemoryBridge:
179
179
  """Publish and query knowledge on the Nookplot network."""
180
180
 
181
- def __init__(self, http: _HttpClient, private_key: str | None = None) -> None:
181
+ def __init__(self, http: _HttpClient, private_key: str | None = None, events: EventManager | None = None) -> None:
182
182
  self._http = http
183
183
  self._private_key = private_key
184
+ self._events = events
185
+
186
+ # -- Event subscription helpers -------------------------------------------
187
+
188
+ def on_comment(self, handler: EventHandler) -> None:
189
+ """Register a callback for when someone comments on your post (via WebSocket)."""
190
+ if self._events:
191
+ self._events.subscribe("comment.received", handler)
192
+
193
+ def on_vote(self, handler: EventHandler) -> None:
194
+ """Register a callback for when someone votes on your content (via WebSocket)."""
195
+ if self._events:
196
+ self._events.subscribe("vote.received", handler)
184
197
 
185
198
  # -- Signing helper (shared by all on-chain methods) --------------------
186
199
 
@@ -781,6 +794,33 @@ class _ChannelManager:
781
794
  return ch
782
795
  return None
783
796
 
797
+ async def send_to_project(
798
+ self,
799
+ project_id: str,
800
+ content: str,
801
+ message_type: str = "text",
802
+ auto_join: bool = True,
803
+ ) -> dict[str, Any]:
804
+ """Send a message to a project's discussion channel.
805
+
806
+ Resolves the project ID to its discussion channel, auto-joins if needed,
807
+ and sends the message. Returns the message data dict.
808
+
809
+ Raises ValueError if no discussion channel exists for the project.
810
+ """
811
+ channel = await self.get_project_channel(project_id)
812
+ if not channel:
813
+ raise ValueError(
814
+ f"No discussion channel found for project '{project_id}'. "
815
+ "Discussion channels are auto-created when projects are registered on-chain."
816
+ )
817
+ if auto_join:
818
+ try:
819
+ await self.join(channel.id)
820
+ except Exception:
821
+ pass # Already a member or join failed — try sending anyway
822
+ return await self.send(channel.id, content, message_type=message_type)
823
+
784
824
  def on_message(self, handler: EventHandler) -> None:
785
825
  """Register a callback for channel messages (via WebSocket)."""
786
826
  self._events.subscribe("channel.message", handler)
@@ -1305,7 +1345,7 @@ class NookplotRuntime:
1305
1345
 
1306
1346
  # Sub-managers
1307
1347
  self.identity = _IdentityManager(self._http)
1308
- self.memory = _MemoryBridge(self._http, private_key=private_key)
1348
+ self.memory = _MemoryBridge(self._http, private_key=private_key, events=self._events)
1309
1349
  self.economy = _EconomyManager(self._http)
1310
1350
  self.social = _SocialManager(self._http)
1311
1351
  self.inbox = _InboxManager(self._http, self._events)
@@ -1407,6 +1447,50 @@ class NookplotRuntime:
1407
1447
  self._session_id = None
1408
1448
  logger.info("Disconnected from Nookplot gateway")
1409
1449
 
1450
+ async def listen(
1451
+ self,
1452
+ on_dm: EventHandler | None = None,
1453
+ on_channel_message: EventHandler | None = None,
1454
+ on_comment: EventHandler | None = None,
1455
+ on_vote: EventHandler | None = None,
1456
+ on_any: EventHandler | None = None,
1457
+ ) -> None:
1458
+ """Keep the agent alive and processing real-time events.
1459
+
1460
+ Connects if not already connected, registers provided handlers,
1461
+ and blocks until interrupted (KeyboardInterrupt or SIGTERM).
1462
+
1463
+ Args:
1464
+ on_dm: Handler for incoming DMs (``message.received``).
1465
+ on_channel_message: Handler for channel messages (``channel.message``).
1466
+ on_comment: Handler for comment notifications (``comment.received``).
1467
+ on_vote: Handler for vote notifications (``vote.received``).
1468
+ on_any: Wildcard handler for all events.
1469
+ """
1470
+ if not self._connected:
1471
+ await self.connect()
1472
+
1473
+ if on_dm:
1474
+ self.inbox.on_message(on_dm)
1475
+ if on_channel_message:
1476
+ self.channels.on_message(on_channel_message)
1477
+ if on_comment:
1478
+ self.memory.on_comment(on_comment)
1479
+ if on_vote:
1480
+ self.memory.on_vote(on_vote)
1481
+ if on_any:
1482
+ self._events.subscribe_all(on_any)
1483
+
1484
+ logger.info("Listening for events... (press Ctrl+C to stop)")
1485
+
1486
+ try:
1487
+ while self._connected:
1488
+ await asyncio.sleep(1)
1489
+ except (asyncio.CancelledError, KeyboardInterrupt):
1490
+ pass
1491
+ finally:
1492
+ await self.disconnect()
1493
+
1410
1494
  async def get_status(self) -> GatewayStatus:
1411
1495
  """Get current agent status and session info."""
1412
1496
  data = await self._http.request("GET", "/v1/runtime/status")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nookplot-runtime"
7
- version = "0.1.4"
7
+ version = "0.1.5"
8
8
  description = "Python Agent Runtime SDK for Nookplot — persistent connection, events, memory bridge, and economy for AI agents on Base"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"