Browse Source

Track Chatto 0.4.10

Regenerated proto bindings from upstream. The only wire change was
an additive RPC on PushNotificationService: SendTestNotification,
which sends a Web Push test notification to the caller. Exposed it as
ChattoClient.send_test_push_notification().

Bumped version 0.4.9.post1 → 0.4.10.
TheCodemancer 3 months ago
parent
commit
2d08208baa

+ 1 - 1
CLAUDE.md

@@ -91,7 +91,7 @@ generated protobuf bindings.
 | Threads | `ThreadService` | `FollowThread`, `UnfollowThread`, `ListFollowedThreads`, `GetThreadEvents`, `GetThreadEventsAround`, `MarkThreadAsRead` |
 | Notifications | `NotificationService` | `ListNotifications`, `GetNotification`, `BatchGetNotifications`, `ListRoomNotifications`, `ListRoomNotificationCounts`, `HasNotifications`, `DismissNotification`, `DismissAllNotifications` |
 | Notification prefs | `NotificationPreferencesService` | `Get`/`Update` × `Server`/`Room` |
-| Push | `PushNotificationService` | `Subscribe`, `Unsubscribe` |
+| Push | `PushNotificationService` | `Subscribe`, `Unsubscribe`, `SendTestNotification` |
 | Assets | `AssetService` | `GetAsset`, `BatchGetAssets` |
 | Asset uploads | `AssetUploadService` | `CreateUpload`, `UploadChunk`, `GetUpload`, `CompleteUpload`, `CancelUpload`. `upload_attachment(room, path)` helper computes SHA-256, chunks, and completes in one call. |
 | Admin: server | `chatto.admin.v1.AdminServerService` | `GetServerConfig`, `UpdateServerConfig`, `UploadServerLogo`, `DeleteServerLogo`, `UploadServerBanner`, `DeleteServerBanner`, `GetServerSecurityConfig`, `UpdateBlockedUsernames` |

+ 12 - 0
proto/chatto/api/v1/push_notifications.proto

@@ -19,6 +19,9 @@ service PushNotificationService {
   rpc Unsubscribe(UnsubscribePushRequest) returns (UnsubscribePushResponse) {
     option idempotency_level = IDEMPOTENT;
   }
+
+  // Sends a test notification to the caller's registered browser subscriptions.
+  rpc SendTestNotification(SendTestPushNotificationRequest) returns (SendTestPushNotificationResponse);
 }
 
 // Request to store a PushSubscription returned by the browser Push API.
@@ -65,3 +68,12 @@ message UnsubscribePushResponse {
   // True when the request completed.
   bool unsubscribed = 1;
 }
+
+// Request to test the current user's registered browser push subscriptions.
+message SendTestPushNotificationRequest {}
+
+// Result of sending a test Web Push notification.
+message SendTestPushNotificationResponse {
+  // True when the push provider accepted the notification.
+  bool sent = 1;
+}

+ 1 - 1
pyproject.toml

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "chattolib"
-version = "0.4.9.post1"
+version = "0.4.10"
 description = "Async Python client library for the Chatto webchat Connect API"
 requires-python = ">=3.11"
 license = "MPL-2.0 AND Apache-2.0"

+ 65 - 0
src/chattolib/_pb/chatto/api/v1/push_notifications_connect.py

@@ -23,6 +23,9 @@ class PushNotificationService(Protocol):
     async def unsubscribe(self, request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse:
         raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
 
+    async def send_test_notification(self, request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
 
 class PushNotificationServiceASGIApplication(ConnectASGIApplication[PushNotificationService]):
     def __init__(self, service: PushNotificationService | AsyncGenerator[PushNotificationService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
@@ -49,6 +52,16 @@ class PushNotificationServiceASGIApplication(ConnectASGIApplication[PushNotifica
                     ),
                     function=svc.unsubscribe,
                 ),
+                "/chatto.api.v1.PushNotificationService/SendTestNotification": Endpoint.unary(
+                    method=MethodInfo(
+                        name="SendTestNotification",
+                        service_name="chatto.api.v1.PushNotificationService",
+                        input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+                        output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.send_test_notification,
+                ),
             },
             interceptors=interceptors,
             read_max_bytes=read_max_bytes,
@@ -102,12 +115,34 @@ class PushNotificationServiceClient(ConnectClient):
             timeout_ms=timeout_ms,
         )
 
+    async def send_test_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SendTestNotification",
+                service_name="chatto.api.v1.PushNotificationService",
+                input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+                output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
 
 class PushNotificationServiceSync(Protocol):
     def subscribe(self, request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse:
         raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
     def unsubscribe(self, request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse:
         raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def send_test_notification(self, request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
 
 
 class PushNotificationServiceWSGIApplication(ConnectWSGIApplication):
@@ -134,6 +169,16 @@ class PushNotificationServiceWSGIApplication(ConnectWSGIApplication):
                     ),
                     function=service.unsubscribe,
                 ),
+                "/chatto.api.v1.PushNotificationService/SendTestNotification": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="SendTestNotification",
+                        service_name="chatto.api.v1.PushNotificationService",
+                        input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+                        output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.send_test_notification,
+                ),
             },
             interceptors=interceptors,
             read_max_bytes=read_max_bytes,
@@ -186,3 +231,23 @@ class PushNotificationServiceClientSync(ConnectClientSync):
             headers=headers,
             timeout_ms=timeout_ms,
         )
+
+    def send_test_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SendTestNotification",
+                service_name="chatto.api.v1.PushNotificationService",
+                input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationRequest,
+                output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SendTestPushNotificationResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 7 - 3
src/chattolib/_pb/chatto/api/v1/push_notifications_pb2.py

@@ -26,7 +26,7 @@ from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
 from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&chatto/api/v1/push_notifications.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\x1a google/protobuf/descriptor.proto\"\x9c\x01\n\x14SubscribePushRequest\x12\x1c\n\x08\x65ndpoint\x18\x01 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80 \x12\x1a\n\x06p256dh\x18\x02 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80\x02\x12\x18\n\x04\x61uth\x18\x03 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80\x01\x12!\n\nuser_agent\x18\x04 \x01(\tB\x08\xbaH\x05r\x03\x18\x80\x04H\x00\x88\x01\x01\x42\r\n\x0b_user_agent\"+\n\x15SubscribePushResponse\x12\x12\n\nsubscribed\x18\x01 \x01(\x08\"6\n\x16UnsubscribePushRequest\x12\x1c\n\x08\x65ndpoint\x18\x01 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80 \"/\n\x17UnsubscribePushResponse\x12\x14\n\x0cunsubscribed\x18\x01 \x01(\x08\x32\xd4\x01\n\x17PushNotificationService\x12V\n\tSubscribe\x12#.chatto.api.v1.SubscribePushRequest\x1a$.chatto.api.v1.SubscribePushResponse\x12\x61\n\x0bUnsubscribe\x12%.chatto.api.v1.UnsubscribePushRequest\x1a&.chatto.api.v1.UnsubscribePushResponse\"\x03\x90\x02\x02\x62\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&chatto/api/v1/push_notifications.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\x1a google/protobuf/descriptor.proto\"\x9c\x01\n\x14SubscribePushRequest\x12\x1c\n\x08\x65ndpoint\x18\x01 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80 \x12\x1a\n\x06p256dh\x18\x02 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80\x02\x12\x18\n\x04\x61uth\x18\x03 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80\x01\x12!\n\nuser_agent\x18\x04 \x01(\tB\x08\xbaH\x05r\x03\x18\x80\x04H\x00\x88\x01\x01\x42\r\n\x0b_user_agent\"+\n\x15SubscribePushResponse\x12\x12\n\nsubscribed\x18\x01 \x01(\x08\"6\n\x16UnsubscribePushRequest\x12\x1c\n\x08\x65ndpoint\x18\x01 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80 \"/\n\x17UnsubscribePushResponse\x12\x14\n\x0cunsubscribed\x18\x01 \x01(\x08\"!\n\x1fSendTestPushNotificationRequest\"0\n SendTestPushNotificationResponse\x12\x0c\n\x04sent\x18\x01 \x01(\x08\x32\xcd\x02\n\x17PushNotificationService\x12V\n\tSubscribe\x12#.chatto.api.v1.SubscribePushRequest\x1a$.chatto.api.v1.SubscribePushResponse\x12\x61\n\x0bUnsubscribe\x12%.chatto.api.v1.UnsubscribePushRequest\x1a&.chatto.api.v1.UnsubscribePushResponse\"\x03\x90\x02\x02\x12w\n\x14SendTestNotification\x12..chatto.api.v1.SendTestPushNotificationRequest\x1a/.chatto.api.v1.SendTestPushNotificationResponseb\x06proto3')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -53,6 +53,10 @@ if not _descriptor._USE_C_DESCRIPTORS:
   _globals['_UNSUBSCRIBEPUSHREQUEST']._serialized_end=378
   _globals['_UNSUBSCRIBEPUSHRESPONSE']._serialized_start=380
   _globals['_UNSUBSCRIBEPUSHRESPONSE']._serialized_end=427
-  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_start=430
-  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_end=642
+  _globals['_SENDTESTPUSHNOTIFICATIONREQUEST']._serialized_start=429
+  _globals['_SENDTESTPUSHNOTIFICATIONREQUEST']._serialized_end=462
+  _globals['_SENDTESTPUSHNOTIFICATIONRESPONSE']._serialized_start=464
+  _globals['_SENDTESTPUSHNOTIFICATIONRESPONSE']._serialized_end=512
+  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_start=515
+  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_end=848
 # @@protoc_insertion_point(module_scope)

+ 9 - 0
src/chattolib/client.py

@@ -1386,6 +1386,15 @@ class ChattoClient:
         )
         return resp.unsubscribed
 
+    async def send_test_push_notification(self) -> bool:
+        resp = await self._rpc(
+            self._svc.push.send_test_notification(
+                push_notifications_pb2.SendTestPushNotificationRequest(),
+                headers=self._headers(),
+            )
+        )
+        return resp.sent
+
     # --- Assets ---------------------------------------------------------
 
     async def get_asset(