Explorar o código

Track Chatto 0.4.9

Chatto HQ jumped from 0.4.3 to 0.4.9. Per the version-tracks-Chatto
policy, chattolib bumps to match.

Upstream proto deltas:

- Additive: Message.deleted_at (Timestamp). Surfaced on the Message
  dataclass and its parser.
- Additive: GetSystemInfoResponse.asset_cleanup (AdminAssetCleanupStatus)
  with a new AdminAssetCleanupHealth enum. Passes through the existing
  raw-dict admin_get_system_info() return; no client code change.
- Metadata-only: DeleteAvatar, DeleteCustomStatus, DismissNotification,
  Unsubscribe, and GetServer now advertise idempotency_level. No wire
  change.
- Server-side validation: CreateMessageRequest.attachment_asset_ids
  now capped at 10 items with per-item length limits. Server-enforced;
  no client change.

Live-verified against chat.chatto.run (0.4.9) with the RoboChatto
account: login, me(), list_rooms(), get_motd() all decode correctly.
ruff + mypy --strict + pytest all clean.
TheCodemancer hai 3 meses
pai
achega
941d1ded5f

+ 14 - 12
proto/buf/validate/validate.proto

@@ -88,7 +88,7 @@ extend google.protobuf.FieldOptions {
   //
   // ```proto
   // extend buf.validate.Int32Rules {
-  //   bool is_zero [(buf.validate.predefined).cel = {
+  //   bool is_zero = 1001 [(buf.validate.predefined).cel = {
   //     id: "int32.is_zero",
   //     message: "must be zero",
   //     expression: "!rule || this == 0",
@@ -1747,7 +1747,7 @@ message UInt64Rules {
   // message MyUInt64 {
   //   uint64 value = 1 [
   //     (buf.validate.field).uint64.example = 1,
-  //     (buf.validate.field).uint64.example = -10
+  //     (buf.validate.field).uint64.example = 10
   //   ];
   // }
   // ```
@@ -3028,8 +3028,8 @@ message BoolRules {
   // ```proto
   // message MyBool {
   //   bool value = 1 [
-  //     (buf.validate.field).bool.example = 1,
-  //     (buf.validate.field).bool.example = 2
+  //     (buf.validate.field).bool.example = true,
+  //     (buf.validate.field).bool.example = false
   //   ];
   // }
   // ```
@@ -3239,7 +3239,7 @@ message StringRules {
   // ```proto
   // message MyString {
   //   // must be in list ["apple", "banana"]
-  //   string value = 1 [(buf.validate.field).string.in = "apple", (buf.validate.field).string.in = "banana"];
+  //   string value = 1 [(buf.validate.field).string = { in: ["apple", "banana"] }];
   // }
   // ```
   repeated string in = 10 [(predefined).cel = {
@@ -3253,7 +3253,7 @@ message StringRules {
   // ```proto
   // message MyString {
   //   // value must not be in list ["orange", "grape"]
-  //   string value = 1 [(buf.validate.field).string.not_in = "orange", (buf.validate.field).string.not_in = "grape"];
+  //   string value = 1 [(buf.validate.field).string = { not_in: ["orange", "grape"] }];
   // }
   // ```
   repeated string not_in = 11 [(predefined).cel = {
@@ -4025,7 +4025,7 @@ message BytesRules {
   // ```proto
   // message MyBytes {
   //   // value must in ["\x01\x02", "\x02\x03", "\x03\x04"]
-  //   optional bytes value = 1 [(buf.validate.field).bytes.in = {"\x01\x02", "\x02\x03", "\x03\x04"}];
+  //   optional bytes value = 1 [(buf.validate.field).bytes = { in: ["\x01\x02", "\x02\x03", "\x03\x04"] }];
   // }
   // ```
   repeated bytes in = 8 [(predefined).cel = {
@@ -4041,7 +4041,7 @@ message BytesRules {
   // ```proto
   // message MyBytes {
   //   // value must not in ["\x01\x02", "\x02\x03", "\x03\x04"]
-  //   optional bytes value = 1 [(buf.validate.field).bytes.not_in = {"\x01\x02", "\x02\x03", "\x03\x04"}];
+  //   optional bytes value = 1 [(buf.validate.field).bytes = { not_in: ["\x01\x02", "\x02\x03", "\x03\x04"] }];
   // }
   // ```
   repeated bytes not_in = 9 [(predefined).cel = {
@@ -4258,8 +4258,10 @@ message EnumRules {
   // }
   //
   // message MyMessage {
+  //   MyEnum value = 1 [
   //     (buf.validate.field).enum.example = 1,
   //     (buf.validate.field).enum.example = 2
+  //   ];
   // }
   // ```
   repeated int32 example = 5 [(predefined).cel = {
@@ -4647,7 +4649,7 @@ message DurationRules {
   // message MyDuration {
   //   google.protobuf.Duration value = 1 [
   //     (buf.validate.field).duration.example = { seconds: 1 },
-  //     (buf.validate.field).duration.example = { seconds: 2 },
+  //     (buf.validate.field).duration.example = { seconds: 2 }
   //   ];
   // }
   // ```
@@ -4727,7 +4729,7 @@ message FieldMaskRules {
   // message MyFieldMask {
   //   google.protobuf.FieldMask value = 1 [
   //     (buf.validate.field).field_mask.example = { paths: ["a", "b"] },
-  //     (buf.validate.field).field_mask.example = { paths: ["c.a", "d"] },
+  //     (buf.validate.field).field_mask.example = { paths: ["c.a", "d"] }
   //   ];
   // }
   // ```
@@ -4938,7 +4940,7 @@ message TimestampRules {
   // message MyTimestamp {
   //   google.protobuf.Timestamp value = 1 [
   //     (buf.validate.field).timestamp.example = { seconds: 1672444800 },
-  //     (buf.validate.field).timestamp.example = { seconds: 1672531200 },
+  //     (buf.validate.field).timestamp.example = { seconds: 1672531200 }
   //   ];
   // }
   // ```
@@ -5038,7 +5040,7 @@ message Violation {
   //   bool b = 2 [(buf.validate.field).cel = {
   //     id: "custom_rule",
   //     expression: "!this ? 'b must be true': ''"
-  //   }]
+  //   }];
   // }
   // ```
   //

+ 40 - 0
proto/chatto/admin/v1/diagnostics.proto

@@ -2,6 +2,8 @@ syntax = "proto3";
 
 package chatto.admin.v1;
 
+import "google/protobuf/timestamp.proto";
+
 // Request to read owner-only server diagnostics.
 message GetSystemInfoRequest {}
 
@@ -11,6 +13,44 @@ message GetSystemInfoResponse {
   AdminSystemInfo system_info = 1;
   // Event-sourced projection health and memory estimates.
   repeated AdminProjectionState projections = 2;
+  // Recoverable physical deletion worker health.
+  AdminAssetCleanupStatus asset_cleanup = 3;
+}
+
+// Current health of recoverable message-owned asset deletion.
+message AdminAssetCleanupStatus {
+  // Operator-facing worker state derived from lease and heartbeat state.
+  AdminAssetCleanupHealth health = 1;
+  // Deletion effects currently waiting for a successful retry.
+  int64 pending_count = 2;
+  // Creation time of the oldest pending deletion, when known.
+  google.protobuf.Timestamp oldest_pending_at = 3;
+  // Whether the elected worker is currently running a cleanup pass.
+  bool pass_in_progress = 4;
+  // Completion time of the most recent pass.
+  google.protobuf.Timestamp last_pass_at = 5;
+  // Completion time of the most recent fully successful pass.
+  google.protobuf.Timestamp last_successful_pass_at = 6;
+  // Time of the latest elected-worker heartbeat.
+  google.protobuf.Timestamp updated_at = 7;
+  // Whether the most recent completed pass had one or more failures.
+  bool last_pass_failed = 8;
+  // Global EVT sequence through which deletion facts were inspected.
+  string last_inspected_sequence = 9;
+  // Current latest global EVT sequence matching asset deletion facts.
+  string latest_deletion_sequence = 10;
+}
+
+// Operator-facing state for the elected asset cleanup worker.
+enum AdminAssetCleanupHealth {
+  ADMIN_ASSET_CLEANUP_HEALTH_UNSPECIFIED = 0;
+  ADMIN_ASSET_CLEANUP_HEALTH_INACTIVE = 1;
+  ADMIN_ASSET_CLEANUP_HEALTH_INITIALIZING = 2;
+  ADMIN_ASSET_CLEANUP_HEALTH_HEALTHY = 3;
+  ADMIN_ASSET_CLEANUP_HEALTH_RETRYING = 4;
+  ADMIN_ASSET_CLEANUP_HEALTH_STALLED = 5;
+  // Cleanup diagnostics could not be read; other system diagnostics remain valid.
+  ADMIN_ASSET_CLEANUP_HEALTH_UNAVAILABLE = 6;
 }
 
 // Broker and application diagnostics shown on the server admin system page.

+ 7 - 2
proto/chatto/api/v1/account.proto

@@ -9,6 +9,7 @@ import "chatto/api/v1/presence.proto";
 import "chatto/api/v1/user_status.proto";
 import "chatto/api/v1/users.proto";
 import "chatto/api/v1/viewer.proto";
+import "google/protobuf/descriptor.proto";
 
 // Request to update the authenticated user's profile. At least one field must
 // be present.
@@ -119,7 +120,9 @@ service MyAccountService {
   // Uploads and sets the authenticated user's avatar.
   rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse);
   // Deletes the authenticated user's avatar. The call is idempotent.
-  rpc DeleteAvatar(DeleteAvatarRequest) returns (DeleteAvatarResponse);
+  rpc DeleteAvatar(DeleteAvatarRequest) returns (DeleteAvatarResponse) {
+    option idempotency_level = IDEMPOTENT;
+  }
   // Updates or adds the authenticated user's password.
   rpc UpdatePassword(UpdatePasswordRequest) returns (UpdatePasswordResponse);
   // Updates the authenticated user's display preferences.
@@ -142,7 +145,9 @@ service MyAccountService {
   rpc UpdateCustomStatus(UpdateCustomStatusRequest) returns (UpdateCustomStatusResponse);
   // Deletes the current user's custom status. The call is idempotent and returns
   // the resulting empty status state.
-  rpc DeleteCustomStatus(DeleteCustomStatusRequest) returns (DeleteCustomStatusResponse);
+  rpc DeleteCustomStatus(DeleteCustomStatusRequest) returns (DeleteCustomStatusResponse) {
+    option idempotency_level = IDEMPOTENT;
+  }
   // Issues a short-lived token used to confirm self-deletion.
   rpc RequestAccountDeletion(RequestAccountDeletionRequest) returns (RequestAccountDeletionResponse);
   // Permanently deletes the authenticated account after token validation.

+ 3 - 0
proto/chatto/api/v1/message_types.proto

@@ -172,4 +172,7 @@ message Message {
   repeated MessageReaction reactions = 19;
   // Aggregated thread state, when known for a thread root message.
   ThreadSummary thread = 20;
+  // Time when the message content was deleted through retraction or account
+  // crypto-shredding. Absent when unavailable body content is not a deletion.
+  google.protobuf.Timestamp deleted_at = 21;
 }

+ 10 - 1
proto/chatto/api/v1/messages.proto

@@ -28,7 +28,16 @@ message CreateMessageRequest {
   // Message body text. Required unless attachment_asset_ids is non-empty.
   string body = 2;
   // Existing room-scoped attachment asset IDs to include with the message.
-  repeated string attachment_asset_ids = 3;
+  // At most 10 IDs may be supplied.
+  repeated string attachment_asset_ids = 3 [(buf.validate.field).repeated = {
+    max_items: 10
+    items: {
+      string: {
+        min_len: 1
+        max_bytes: 15
+      }
+    }
+  }];
   // Event ID of the thread root message when posting a thread reply.
   string thread_root_event_id = 4;
   // Event ID this message replies to for attribution.

+ 4 - 1
proto/chatto/api/v1/notifications.proto

@@ -6,6 +6,7 @@ import "buf/validate/validate.proto";
 import "chatto/api/v1/pagination.proto";
 import "chatto/api/v1/rooms.proto";
 import "chatto/api/v1/users.proto";
+import "google/protobuf/descriptor.proto";
 import "google/protobuf/timestamp.proto";
 
 // Direct-message notification payload.
@@ -217,7 +218,9 @@ service NotificationService {
   rpc HasNotifications(HasNotificationsRequest) returns (HasNotificationsResponse);
   // Dismisses one pending notification. Already-dismissed notifications are
   // treated as idempotent success.
-  rpc DismissNotification(DismissNotificationRequest) returns (DismissNotificationResponse);
+  rpc DismissNotification(DismissNotificationRequest) returns (DismissNotificationResponse) {
+    option idempotency_level = IDEMPOTENT;
+  }
   // Dismisses all pending notifications.
   rpc DismissAllNotifications(DismissAllNotificationsRequest) returns (DismissAllNotificationsResponse);
 }

+ 4 - 1
proto/chatto/api/v1/push_notifications.proto

@@ -3,6 +3,7 @@ syntax = "proto3";
 package chatto.api.v1;
 
 import "buf/validate/validate.proto";
+import "google/protobuf/descriptor.proto";
 
 // Browser Web Push subscription commands for the current user.
 service PushNotificationService {
@@ -15,7 +16,9 @@ service PushNotificationService {
   // Removes the caller's browser push subscription by endpoint.
   //
   // The call is idempotent: removing an unknown endpoint still succeeds.
-  rpc Unsubscribe(UnsubscribePushRequest) returns (UnsubscribePushResponse);
+  rpc Unsubscribe(UnsubscribePushRequest) returns (UnsubscribePushResponse) {
+    option idempotency_level = IDEMPOTENT;
+  }
 }
 
 // Request to store a PushSubscription returned by the browser Push API.

+ 4 - 1
proto/chatto/discovery/v1/server.proto

@@ -3,6 +3,7 @@ syntax = "proto3";
 package chatto.discovery.v1;
 
 import "chatto/api/v1/server.proto";
+import "google/protobuf/descriptor.proto";
 
 // Request for public server metadata.
 message GetServerRequest {}
@@ -21,5 +22,7 @@ service ServerDiscoveryService {
   // Returns public server metadata, branding, registration status, and login
   // provider information. This fully public RPC is available before login so
   // clients can render the first server screen and authentication choices.
-  rpc GetServer(GetServerRequest) returns (GetServerResponse);
+  rpc GetServer(GetServerRequest) returns (GetServerResponse) {
+    option idempotency_level = NO_SIDE_EFFECTS;
+  }
 }

+ 1 - 1
pyproject.toml

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

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py


+ 8 - 8
src/chattolib/_pb/chatto/api/v1/account_connect.py

@@ -91,7 +91,7 @@ class MyAccountServiceASGIApplication(ConnectASGIApplication[MyAccountService]):
                         service_name="chatto.api.v1.MyAccountService",
                         input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
                         output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=svc.delete_avatar,
                 ),
@@ -171,7 +171,7 @@ class MyAccountServiceASGIApplication(ConnectASGIApplication[MyAccountService]):
                         service_name="chatto.api.v1.MyAccountService",
                         input=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
                         output=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=svc.delete_custom_status,
                 ),
@@ -262,7 +262,7 @@ class MyAccountServiceClient(ConnectClient):
                 service_name="chatto.api.v1.MyAccountService",
                 input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
                 output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
@@ -422,7 +422,7 @@ class MyAccountServiceClient(ConnectClient):
                 service_name="chatto.api.v1.MyAccountService",
                 input=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
                 output=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
@@ -528,7 +528,7 @@ class MyAccountServiceWSGIApplication(ConnectWSGIApplication):
                         service_name="chatto.api.v1.MyAccountService",
                         input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
                         output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=service.delete_avatar,
                 ),
@@ -608,7 +608,7 @@ class MyAccountServiceWSGIApplication(ConnectWSGIApplication):
                         service_name="chatto.api.v1.MyAccountService",
                         input=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
                         output=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=service.delete_custom_status,
                 ),
@@ -699,7 +699,7 @@ class MyAccountServiceClientSync(ConnectClientSync):
                 service_name="chatto.api.v1.MyAccountService",
                 input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
                 output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
@@ -859,7 +859,7 @@ class MyAccountServiceClientSync(ConnectClientSync):
                 service_name="chatto.api.v1.MyAccountService",
                 input=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
                 output=chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/chattolib/_pb/chatto/api/v1/account_pb2.py


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/chattolib/_pb/chatto/api/v1/message_types_pb2.py


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
src/chattolib/_pb/chatto/api/v1/messages_pb2.py


+ 4 - 4
src/chattolib/_pb/chatto/api/v1/notifications_connect.py

@@ -113,7 +113,7 @@ class NotificationServiceASGIApplication(ConnectASGIApplication[NotificationServ
                         service_name="chatto.api.v1.NotificationService",
                         input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
                         output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=svc.dismiss_notification,
                 ),
@@ -274,7 +274,7 @@ class NotificationServiceClient(ConnectClient):
                 service_name="chatto.api.v1.NotificationService",
                 input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
                 output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
@@ -390,7 +390,7 @@ class NotificationServiceWSGIApplication(ConnectWSGIApplication):
                         service_name="chatto.api.v1.NotificationService",
                         input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
                         output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=service.dismiss_notification,
                 ),
@@ -551,7 +551,7 @@ class NotificationServiceClientSync(ConnectClientSync):
                 service_name="chatto.api.v1.NotificationService",
                 input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
                 output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
src/chattolib/_pb/chatto/api/v1/notifications_pb2.py


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

@@ -45,7 +45,7 @@ class PushNotificationServiceASGIApplication(ConnectASGIApplication[PushNotifica
                         service_name="chatto.api.v1.PushNotificationService",
                         input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
                         output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=svc.unsubscribe,
                 ),
@@ -96,7 +96,7 @@ class PushNotificationServiceClient(ConnectClient):
                 service_name="chatto.api.v1.PushNotificationService",
                 input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
                 output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
@@ -130,7 +130,7 @@ class PushNotificationServiceWSGIApplication(ConnectWSGIApplication):
                         service_name="chatto.api.v1.PushNotificationService",
                         input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
                         output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.IDEMPOTENT,
                     ),
                     function=service.unsubscribe,
                 ),
@@ -181,7 +181,7 @@ class PushNotificationServiceClientSync(ConnectClientSync):
                 service_name="chatto.api.v1.PushNotificationService",
                 input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
                 output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.IDEMPOTENT,
             ),
             headers=headers,
             timeout_ms=timeout_ms,

+ 14 - 11
src/chattolib/_pb/chatto/api/v1/push_notifications_pb2.py

@@ -23,9 +23,10 @@ _sym_db = _symbol_database.Default()
 
 
 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\"\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\xcf\x01\n\x17PushNotificationService\x12V\n\tSubscribe\x12#.chatto.api.v1.SubscribePushRequest\x1a$.chatto.api.v1.SubscribePushResponse\x12\\\n\x0bUnsubscribe\x12%.chatto.api.v1.UnsubscribePushRequest\x1a&.chatto.api.v1.UnsubscribePushResponseb\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\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')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -42,14 +43,16 @@ if not _descriptor._USE_C_DESCRIPTORS:
   _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['user_agent']._serialized_options = b'\272H\005r\003\030\200\004'
   _globals['_UNSUBSCRIBEPUSHREQUEST'].fields_by_name['endpoint']._loaded_options = None
   _globals['_UNSUBSCRIBEPUSHREQUEST'].fields_by_name['endpoint']._serialized_options = b'\272H\007r\005\020\001\030\200 '
-  _globals['_SUBSCRIBEPUSHREQUEST']._serialized_start=87
-  _globals['_SUBSCRIBEPUSHREQUEST']._serialized_end=243
-  _globals['_SUBSCRIBEPUSHRESPONSE']._serialized_start=245
-  _globals['_SUBSCRIBEPUSHRESPONSE']._serialized_end=288
-  _globals['_UNSUBSCRIBEPUSHREQUEST']._serialized_start=290
-  _globals['_UNSUBSCRIBEPUSHREQUEST']._serialized_end=344
-  _globals['_UNSUBSCRIBEPUSHRESPONSE']._serialized_start=346
-  _globals['_UNSUBSCRIBEPUSHRESPONSE']._serialized_end=393
-  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_start=396
-  _globals['_PUSHNOTIFICATIONSERVICE']._serialized_end=603
+  _globals['_PUSHNOTIFICATIONSERVICE'].methods_by_name['Unsubscribe']._loaded_options = None
+  _globals['_PUSHNOTIFICATIONSERVICE'].methods_by_name['Unsubscribe']._serialized_options = b'\220\002\002'
+  _globals['_SUBSCRIBEPUSHREQUEST']._serialized_start=121
+  _globals['_SUBSCRIBEPUSHREQUEST']._serialized_end=277
+  _globals['_SUBSCRIBEPUSHRESPONSE']._serialized_start=279
+  _globals['_SUBSCRIBEPUSHRESPONSE']._serialized_end=322
+  _globals['_UNSUBSCRIBEPUSHREQUEST']._serialized_start=324
+  _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
 # @@protoc_insertion_point(module_scope)

+ 8 - 4
src/chattolib/_pb/chatto/discovery/v1/server_connect.py

@@ -32,7 +32,7 @@ class ServerDiscoveryServiceASGIApplication(ConnectASGIApplication[ServerDiscove
                         service_name="chatto.discovery.v1.ServerDiscoveryService",
                         input=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
                         output=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.NO_SIDE_EFFECTS,
                     ),
                     function=svc.get_server,
                 ),
@@ -55,6 +55,7 @@ class ServerDiscoveryServiceClient(ConnectClient):
         *,
         headers: Headers | Mapping[str, str] | None = None,
         timeout_ms: int | None = None,
+        use_get: bool = False,
     ) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
         return await self.execute_unary(
             request=request,
@@ -63,10 +64,11 @@ class ServerDiscoveryServiceClient(ConnectClient):
                 service_name="chatto.discovery.v1.ServerDiscoveryService",
                 input=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
                 output=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.NO_SIDE_EFFECTS,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
+            use_get=use_get,
         )
 
 
@@ -85,7 +87,7 @@ class ServerDiscoveryServiceWSGIApplication(ConnectWSGIApplication):
                         service_name="chatto.discovery.v1.ServerDiscoveryService",
                         input=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
                         output=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse,
-                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                        idempotency_level=IdempotencyLevel.NO_SIDE_EFFECTS,
                     ),
                     function=service.get_server,
                 ),
@@ -108,6 +110,7 @@ class ServerDiscoveryServiceClientSync(ConnectClientSync):
         *,
         headers: Headers | Mapping[str, str] | None = None,
         timeout_ms: int | None = None,
+        use_get: bool = False,
     ) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
         return self.execute_unary(
             request=request,
@@ -116,8 +119,9 @@ class ServerDiscoveryServiceClientSync(ConnectClientSync):
                 service_name="chatto.discovery.v1.ServerDiscoveryService",
                 input=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
                 output=chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse,
-                idempotency_level=IdempotencyLevel.UNKNOWN,
+                idempotency_level=IdempotencyLevel.NO_SIDE_EFFECTS,
             ),
             headers=headers,
             timeout_ms=timeout_ms,
+            use_get=use_get,
         )

+ 10 - 7
src/chattolib/_pb/chatto/discovery/v1/server_pb2.py

@@ -23,19 +23,22 @@ _sym_db = _symbol_database.Default()
 
 
 from chatto.api.v1 import server_pb2 as chatto_dot_api_dot_v1_dot_server__pb2
+from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n chatto/discovery/v1/server.proto\x12\x13\x63hatto.discovery.v1\x1a\x1a\x63hatto/api/v1/server.proto\"\x12\n\x10GetServerRequest\"s\n\x11GetServerResponse\x12\x33\n\x07profile\x18\x01 \x01(\x0b\x32\".chatto.api.v1.ServerPublicProfile\x12)\n\x05login\x18\x02 \x01(\x0b\x32\x1a.chatto.api.v1.ServerLogin2t\n\x16ServerDiscoveryService\x12Z\n\tGetServer\x12%.chatto.discovery.v1.GetServerRequest\x1a&.chatto.discovery.v1.GetServerResponseb\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n chatto/discovery/v1/server.proto\x12\x13\x63hatto.discovery.v1\x1a\x1a\x63hatto/api/v1/server.proto\x1a google/protobuf/descriptor.proto\"\x12\n\x10GetServerRequest\"s\n\x11GetServerResponse\x12\x33\n\x07profile\x18\x01 \x01(\x0b\x32\".chatto.api.v1.ServerPublicProfile\x12)\n\x05login\x18\x02 \x01(\x0b\x32\x1a.chatto.api.v1.ServerLogin2y\n\x16ServerDiscoveryService\x12_\n\tGetServer\x12%.chatto.discovery.v1.GetServerRequest\x1a&.chatto.discovery.v1.GetServerResponse\"\x03\x90\x02\x01\x62\x06proto3')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
 _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.discovery.v1.server_pb2', _globals)
 if not _descriptor._USE_C_DESCRIPTORS:
   DESCRIPTOR._loaded_options = None
-  _globals['_GETSERVERREQUEST']._serialized_start=85
-  _globals['_GETSERVERREQUEST']._serialized_end=103
-  _globals['_GETSERVERRESPONSE']._serialized_start=105
-  _globals['_GETSERVERRESPONSE']._serialized_end=220
-  _globals['_SERVERDISCOVERYSERVICE']._serialized_start=222
-  _globals['_SERVERDISCOVERYSERVICE']._serialized_end=338
+  _globals['_SERVERDISCOVERYSERVICE'].methods_by_name['GetServer']._loaded_options = None
+  _globals['_SERVERDISCOVERYSERVICE'].methods_by_name['GetServer']._serialized_options = b'\220\002\001'
+  _globals['_GETSERVERREQUEST']._serialized_start=119
+  _globals['_GETSERVERREQUEST']._serialized_end=137
+  _globals['_GETSERVERRESPONSE']._serialized_start=139
+  _globals['_GETSERVERRESPONSE']._serialized_end=254
+  _globals['_SERVERDISCOVERYSERVICE']._serialized_start=256
+  _globals['_SERVERDISCOVERYSERVICE']._serialized_end=377
 # @@protoc_insertion_point(module_scope)

+ 2 - 0
src/chattolib/types.py

@@ -646,6 +646,7 @@ class Message:
     channel_echo_event_id: str = ""
     reactions: list[MessageReaction] = field(default_factory=list)
     thread: ThreadSummary | None = None
+    deleted_at: datetime | None = None
 
     @classmethod
     def parse(cls, data: dict[str, Any] | None) -> Message | None:
@@ -667,6 +668,7 @@ class Message:
             channel_echo_event_id=data.get("channelEchoEventId", ""),
             reactions=[MessageReaction.parse(r) for r in data.get("reactions") or []],
             thread=ThreadSummary.parse(data.get("thread")),
+            deleted_at=parse_datetime(data.get("deletedAt")),
         )
 
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio