Forráskód Böngészése

Switch transport to the official connectrpc Python package (0.4.2.post2)

Move chattolib off hand-rolled Connect-JSON-over-httpx onto the same
connectrpc client that chatto-bot uses.

Codegen:
- scripts/generate_pb.sh now fetches every Chatto proto (api/v1,
  admin/v1, auth/v1, discovery/v1, realtime/v1) and runs both
  --python_out and --connect-python_out. Produces 39 *_pb2.py message
  modules and 25 *_connect.py service-stub modules under
  src/chattolib/_pb/.

Transport:
- _transport.py rewritten. build_service_clients(base_url) returns a
  typed ServiceClients bundle with one client per Chatto service, all
  sharing the google.protobuf binary codec. translate_connect_error
  maps ConnectError -> ChattoAuthError/ChattoConnectError. pb_to_dict
  bridges protobuf responses to the existing types.py dataclass
  parsers so the public dataclass surface doesn't churn.

Client:
- ChattoClient rewritten to build typed protobuf requests, call the
  generated stubs via a shared _rpc helper (auth headers + error
  translation), and normalise responses into dataclasses. .services
  is a public escape hatch for reaching the raw stubs directly. The
  old string-based client.call(service, method, dict) is gone.
- ActiveCallParticipant.user_id (stale, from the JSON-transport era)
  replaced with a full User field matching the current CallParticipant
  message.

Deps:
- Added connectrpc>=0.11 and protobuf>=5.28 as runtime deps.
  httpx>=0.27 remains for /auth/login (not a Connect endpoint).
  websockets stays under [realtime]. protoc-gen-connect-python is a
  build-time requirement for the code generator.

Tests:
- tests/test_client.py rewritten to mock at the service-stub level
  via unittest.mock.AsyncMock. 48 mocked tests, all passing. No more
  HTTP mocking.
- ruff and mypy --strict clean.

Verified live against Chatto HQ (0.4.2) with the RoboChatto account:
login, me(), list_rooms(), get_motd(), list_roles(),
has_notifications() all decode end-to-end through the new transport.
Realtime WebSocket is unchanged and still working.
TheCodemancer 3 hónapja
szülő
commit
1bd6b70c27
100 módosított fájl, 17326 hozzáadás és 845 törlés
  1. 27 0
      .beans/chattolib-psxw--move-chattolib-transport-to-the-connectrpc-python.md
  2. 13 8
      CLAUDE.md
  3. 203 0
      proto/chatto/admin/v1/diagnostics.proto
  4. 101 0
      proto/chatto/admin/v1/event_log.proto
  5. 239 0
      proto/chatto/admin/v1/members.proto
  6. 326 0
      proto/chatto/admin/v1/permissions.proto
  7. 135 0
      proto/chatto/admin/v1/roles.proto
  8. 261 0
      proto/chatto/admin/v1/room_layout.proto
  9. 165 0
      proto/chatto/admin/v1/server.proto
  10. 150 0
      proto/chatto/api/v1/account.proto
  11. 138 0
      proto/chatto/api/v1/asset_uploads.proto
  12. 91 0
      proto/chatto/api/v1/attachments.proto
  13. 53 0
      proto/chatto/api/v1/common.proto
  14. 77 0
      proto/chatto/api/v1/external_identities.proto
  15. 48 0
      proto/chatto/api/v1/link_previews.proto
  16. 150 0
      proto/chatto/api/v1/member_directory.proto
  17. 175 0
      proto/chatto/api/v1/message_types.proto
  18. 225 0
      proto/chatto/api/v1/messages.proto
  19. 223 0
      proto/chatto/api/v1/notifications.proto
  20. 26 0
      proto/chatto/api/v1/pagination.proto
  21. 64 0
      proto/chatto/api/v1/push_notifications.proto
  22. 53 0
      proto/chatto/api/v1/reactions.proto
  23. 44 0
      proto/chatto/api/v1/read_state.proto
  24. 74 0
      proto/chatto/api/v1/roles.proto
  25. 210 0
      proto/chatto/api/v1/room_directory.proto
  26. 183 0
      proto/chatto/api/v1/room_timeline.proto
  27. 399 0
      proto/chatto/api/v1/rooms.proto
  28. 31 0
      proto/chatto/api/v1/server.proto
  29. 49 0
      proto/chatto/api/v1/server_state.proto
  30. 112 0
      proto/chatto/api/v1/threads.proto
  31. 172 0
      proto/chatto/api/v1/voice_calls.proto
  32. 113 0
      proto/chatto/auth/v1/external_identity_auth.proto
  33. 25 0
      proto/chatto/discovery/v1/server.proto
  34. 3 2
      pyproject.toml
  35. 64 33
      scripts/generate_pb.sh
  36. 0 0
      src/chattolib/_pb/chatto/admin/__init__.py
  37. 0 0
      src/chattolib/_pb/chatto/admin/v1/__init__.py
  38. 123 0
      src/chattolib/_pb/chatto/admin/v1/diagnostics_connect.py
  39. 26 0
      src/chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py
  40. 253 0
      src/chattolib/_pb/chatto/admin/v1/event_log_connect.py
  41. 28 0
      src/chattolib/_pb/chatto/admin/v1/event_log_pb2.py
  42. 643 0
      src/chattolib/_pb/chatto/admin/v1/members_connect.py
  43. 32 0
      src/chattolib/_pb/chatto/admin/v1/members_pb2.py
  44. 578 0
      src/chattolib/_pb/chatto/admin/v1/permissions_connect.py
  45. 28 0
      src/chattolib/_pb/chatto/admin/v1/permissions_pb2.py
  46. 448 0
      src/chattolib/_pb/chatto/admin/v1/roles_connect.py
  47. 29 0
      src/chattolib/_pb/chatto/admin/v1/roles_pb2.py
  48. 773 0
      src/chattolib/_pb/chatto/admin/v1/room_layout_connect.py
  49. 29 0
      src/chattolib/_pb/chatto/admin/v1/room_layout_pb2.py
  50. 578 0
      src/chattolib/_pb/chatto/admin/v1/server_connect.py
  51. 29 0
      src/chattolib/_pb/chatto/admin/v1/server_pb2.py
  52. 906 0
      src/chattolib/_pb/chatto/api/v1/account_connect.py
  53. 33 0
      src/chattolib/_pb/chatto/api/v1/account_pb2.py
  54. 383 0
      src/chattolib/_pb/chatto/api/v1/asset_uploads_connect.py
  55. 29 0
      src/chattolib/_pb/chatto/api/v1/asset_uploads_pb2.py
  56. 188 0
      src/chattolib/_pb/chatto/api/v1/attachments_connect.py
  57. 30 0
      src/chattolib/_pb/chatto/api/v1/attachments_pb2.py
  58. 49 0
      src/chattolib/_pb/chatto/api/v1/common_pb2.py
  59. 56 0
      src/chattolib/_pb/chatto/api/v1/external_identities_pb2.py
  60. 43 0
      src/chattolib/_pb/chatto/api/v1/link_previews_pb2.py
  61. 253 0
      src/chattolib/_pb/chatto/api/v1/member_directory_connect.py
  62. 30 0
      src/chattolib/_pb/chatto/api/v1/member_directory_pb2.py
  63. 28 0
      src/chattolib/_pb/chatto/api/v1/message_types_pb2.py
  64. 710 0
      src/chattolib/_pb/chatto/api/v1/messages_connect.py
  65. 30 0
      src/chattolib/_pb/chatto/api/v1/messages_pb2.py
  66. 318 0
      src/chattolib/_pb/chatto/api/v1/notification_preferences_connect.py
  67. 578 0
      src/chattolib/_pb/chatto/api/v1/notifications_connect.py
  68. 31 0
      src/chattolib/_pb/chatto/api/v1/notifications_pb2.py
  69. 43 0
      src/chattolib/_pb/chatto/api/v1/pagination_pb2.py
  70. 188 0
      src/chattolib/_pb/chatto/api/v1/push_notifications_connect.py
  71. 55 0
      src/chattolib/_pb/chatto/api/v1/push_notifications_pb2.py
  72. 56 0
      src/chattolib/_pb/chatto/api/v1/reactions_pb2.py
  73. 50 0
      src/chattolib/_pb/chatto/api/v1/read_state_pb2.py
  74. 253 0
      src/chattolib/_pb/chatto/api/v1/roles_connect.py
  75. 55 0
      src/chattolib/_pb/chatto/api/v1/roles_pb2.py
  76. 448 0
      src/chattolib/_pb/chatto/api/v1/room_directory_connect.py
  77. 29 0
      src/chattolib/_pb/chatto/api/v1/room_directory_pb2.py
  78. 30 0
      src/chattolib/_pb/chatto/api/v1/room_timeline_pb2.py
  79. 1426 0
      src/chattolib/_pb/chatto/api/v1/rooms_connect.py
  80. 34 0
      src/chattolib/_pb/chatto/api/v1/rooms_pb2.py
  81. 39 0
      src/chattolib/_pb/chatto/api/v1/server_pb2.py
  82. 188 0
      src/chattolib/_pb/chatto/api/v1/server_state_connect.py
  83. 46 0
      src/chattolib/_pb/chatto/api/v1/server_state_pb2.py
  84. 450 0
      src/chattolib/_pb/chatto/api/v1/threads_connect.py
  85. 32 0
      src/chattolib/_pb/chatto/api/v1/threads_pb2.py
  86. 123 0
      src/chattolib/_pb/chatto/api/v1/viewer_connect.py
  87. 513 0
      src/chattolib/_pb/chatto/api/v1/voice_calls_connect.py
  88. 30 0
      src/chattolib/_pb/chatto/api/v1/voice_calls_pb2.py
  89. 0 0
      src/chattolib/_pb/chatto/auth/__init__.py
  90. 0 0
      src/chattolib/_pb/chatto/auth/v1/__init__.py
  91. 318 0
      src/chattolib/_pb/chatto/auth/v1/external_identity_auth_connect.py
  92. 28 0
      src/chattolib/_pb/chatto/auth/v1/external_identity_auth_pb2.py
  93. 0 0
      src/chattolib/_pb/chatto/discovery/__init__.py
  94. 0 0
      src/chattolib/_pb/chatto/discovery/v1/__init__.py
  95. 123 0
      src/chattolib/_pb/chatto/discovery/v1/server_connect.py
  96. 41 0
      src/chattolib/_pb/chatto/discovery/v1/server_pb2.py
  97. 161 67
      src/chattolib/_transport.py
  98. 465 228
      src/chattolib/client.py
  99. 4 2
      src/chattolib/types.py
  100. 384 505
      tests/test_client.py

+ 27 - 0
.beans/chattolib-psxw--move-chattolib-transport-to-the-connectrpc-python.md

@@ -0,0 +1,27 @@
+---
+# chattolib-psxw
+title: Move chattolib transport to the connectrpc Python package
+status: completed
+type: task
+priority: high
+created_at: 2026-07-09T10:03:55Z
+updated_at: 2026-07-09T10:20:09Z
+---
+
+Replace hand-rolled Connect-JSON-over-httpx with the official connectrpc Python library (chatto-bot uses this too). Generate connect-python service stubs for every Chatto service from the vendored .proto files, rewrite ChattoClient so its methods delegate to those stubs, drop _transport.py, and keep the Pythonic top-level API (client.post_message(...)) intact.
+
+## Summary of Changes
+
+Migrated the chattolib request/response transport from hand-rolled Connect-JSON-over-httpx to the official `connectrpc` Python package plus generated ConnectRPC service stubs (same transport chatto-bot uses).
+
+- Extended `scripts/generate_pb.sh` to fetch every Chatto proto (api/v1, admin/v1, auth/v1, discovery/v1, realtime/v1) and to invoke `protoc-gen-connect-python` alongside the standard `--python_out`. Now produces 39 `*_pb2.py` message modules and 25 `*_connect.py` service stub modules under `src/chattolib/_pb/`.
+- `_transport.py` rewritten: exposes `build_service_clients(base_url)` (returns a typed `ServiceClients` bundle with one client per Chatto service, all sharing the google.protobuf binary codec) plus `translate_connect_error` (`ConnectError` → `ChattoAuthError`/`ChattoConnectError`) and `pb_to_dict` (bridges protobuf responses to the dataclass `.parse()` methods).
+- `ChattoClient` fully rewritten: each method builds a protobuf request, calls the appropriate stub via a common `_rpc` helper that adds auth headers and translates errors, then normalises the response into the existing dataclass types. Added a public `.services` property so callers can reach the raw ConnectRPC clients directly.
+- Removed the `client.call(service, method, request)` string-based escape hatch (replaced by `.services`).
+- `pyproject.toml`: added `connectrpc>=0.11` and `protobuf>=5.28` as hard runtime deps; `httpx` stays for the `/auth/login` endpoint; `websockets` still under `[realtime]`.
+- Tests rewritten to mock at the service-client method level via `unittest.mock.AsyncMock` — 48 tests, all passing, no HTTP mocking.
+- `ruff` + `mypy --strict` clean.
+
+Verified live against Chatto HQ (0.4.2) with the RoboChatto account: login → `me()` → `list_rooms()` → `get_motd()` → `list_roles()` → `has_notifications()` all succeed end-to-end. Realtime WebSocket unchanged and still working.
+
+CLAUDE.md architecture section refreshed to describe the new transport and _pb layout.

+ 13 - 8
CLAUDE.md

@@ -34,23 +34,28 @@ mypy src/chattolib
 
 ## Architecture
 
-The library uses **httpx** for async HTTP. Realtime events use a separate
+The library uses the official **`connectrpc` Python package** for the
+request/response API surface: `_transport.build_service_clients(base_url)`
+instantiates one ConnectRPC service client per Chatto service, all sharing the
+same address and the `google.protobuf` binary codec. `httpx` remains for the
+one non-Connect endpoint (`/auth/login`). Realtime events use a separate
 binary-protobuf WebSocket protocol (`chatto.realtime.v1`) served at
 `/api/realtime`, implemented in `realtime.py` on top of `websockets` and
 generated protobuf bindings.
 
 ### Package layout: `src/chattolib/`
 
-- **client.py** — `ChattoClient` async class. Wraps every Connect service the client currently supports; the low-level `call(service, method, request)` method is a public escape hatch.
-- **_transport.py** — Connect JSON transport helpers (URL building, headers, error decoding).
+- **client.py** — `ChattoClient` async class. Each method builds a protobuf request, invokes the appropriate generated service stub via `_rpc`, and normalises the response into the library's dataclass types. `services` is a public escape hatch for reaching the raw ConnectRPC service clients directly.
+- **_transport.py** — instantiates the ConnectRPC service clients, translates `connectrpc.errors.ConnectError` into `ChattoConnectError`/`ChattoAuthError`, and exposes `pb_to_dict(message)` for the request/response ↔ dataclass bridge.
 - **types.py** — Dataclasses and `StrEnum` types mirroring the protobuf messages, with `.parse(dict)` classmethods that consume Connect JSON.
 - **exceptions.py** — `ChattoError`, `ChattoConnectError` (wraps Connect protocol errors), `ChattoAuthError`.
 - **realtime.py** — Protobuf realtime WebSocket client. `stream_events(client)` yields `RealtimeEvent(kind, payload, ...)` values. Errors surface as `ChattoRealtimeError` / `ChattoRealtimeCloseError`.
-- **_pb/** — Vendored, generated Python protobuf bindings for
-  `chatto.realtime.v1` and its transitive imports. Regenerate with
-  `scripts/generate_pb.sh` (needs `protoc` on PATH); the script fetches the
-  latest `.proto` sources from `chattocorp/chatto` and rewrites the bindings.
-  The `_pb/__init__.py` inserts its own directory onto `sys.path` so the
+- **_pb/** — Vendored, generated Python protobuf message classes (`*_pb2.py`)
+  and ConnectRPC service stubs (`*_connect.py`) for every Chatto service.
+  Regenerate with `scripts/generate_pb.sh` (needs `protoc` and
+  `protoc-gen-connect-python` on PATH). The script fetches the `.proto`
+  sources from `chattocorp/chatto` and rewrites the bindings. The
+  `_pb/__init__.py` inserts its own directory onto `sys.path` so the
   generated `from chatto.api.v1 import ...` imports resolve without polluting
   the top-level namespace of dependent projects.
 

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

@@ -0,0 +1,203 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+// Request to read owner-only server diagnostics.
+message GetSystemInfoRequest {}
+
+// Owner-only operational diagnostics for the Chatto server.
+message GetSystemInfoResponse {
+  // Broker connection, account, and JetStream state.
+  AdminSystemInfo system_info = 1;
+  // Event-sourced projection health and memory estimates.
+  repeated AdminProjectionState projections = 2;
+}
+
+// Broker and application diagnostics shown on the server admin system page.
+message AdminSystemInfo {
+  // Current NATS server connection metadata.
+  AdminConnectionInfo connection = 1;
+  // JetStream account limits and usage.
+  AdminAccountInfo account = 2;
+  // JetStream stream and consumer diagnostics.
+  AdminNatsStats nats = 3;
+  // Aggregate Chatto object counts.
+  AdminServerStats stats = 4;
+}
+
+// Current NATS server connection metadata.
+message AdminConnectionInfo {
+  // Whether Chatto is currently connected to NATS.
+  bool connected = 1;
+  // Connected NATS server ID.
+  string server_id = 2;
+  // Connected NATS server name.
+  string server_name = 3;
+  // Connected NATS server version.
+  string version = 4;
+  // Maximum payload accepted by the connected NATS server.
+  int64 max_payload = 5;
+  // Round-trip latency to the connected NATS server.
+  string rtt = 6;
+}
+
+// JetStream account limits and usage.
+message AdminAccountInfo {
+  // Maximum memory storage in bytes. Values less than or equal to zero mean unlimited.
+  int64 memory = 1;
+  // Used memory storage in bytes.
+  int64 memory_used = 2;
+  // Maximum file storage in bytes. Values less than or equal to zero mean unlimited.
+  int64 storage = 3;
+  // Used file storage in bytes.
+  int64 storage_used = 4;
+  // Maximum number of streams. Values less than or equal to zero mean unlimited.
+  int32 streams = 5;
+  // Current number of streams.
+  int32 streams_used = 6;
+  // Maximum number of consumers. Values less than or equal to zero mean unlimited.
+  int32 consumers = 7;
+  // Current number of consumers.
+  int32 consumers_used = 8;
+}
+
+// Aggregate Chatto object counts.
+message AdminServerStats {
+  // Registered user count.
+  int32 user_count = 1;
+  // Channel room count.
+  int32 channel_room_count = 2;
+  // Direct-message room count.
+  int32 dm_room_count = 3;
+}
+
+// JetStream stream and consumer diagnostics.
+message AdminNatsStats {
+  // Total messages stored across all streams.
+  int64 total_messages = 1;
+  // Total bytes stored across all streams.
+  int64 total_bytes = 2;
+  // Total pending messages across all consumers.
+  int64 total_consumer_pending = 3;
+  // Total messages currently waiting for acknowledgement.
+  int32 total_ack_pending = 4;
+  // Streams sorted by name.
+  repeated AdminNatsStreamInfo streams = 5;
+  // Consumers sorted by stream and name.
+  repeated AdminNatsConsumerInfo consumers = 6;
+}
+
+// JetStream stream state.
+message AdminNatsStreamInfo {
+  // Stream name.
+  string name = 1;
+  // Stream description.
+  string description = 2;
+  // Subjects stored in the stream.
+  repeated string subjects = 3;
+  // Storage backend name.
+  string storage = 4;
+  // Number of messages in the stream.
+  int64 messages = 5;
+  // Number of bytes in the stream.
+  int64 bytes = 6;
+  // First stream sequence.
+  string first_sequence = 7;
+  // Last stream sequence.
+  string last_sequence = 8;
+  // Number of stream consumers.
+  int32 consumer_count = 9;
+  // Configured stream replica count.
+  int32 replicas = 10;
+  // Current cluster leader, when reported by NATS.
+  string cluster_leader = 11;
+}
+
+// JetStream consumer state.
+message AdminNatsConsumerInfo {
+  // Stream that owns the consumer.
+  string stream = 1;
+  // Consumer name.
+  string name = 2;
+  // Durable name, when the consumer is durable.
+  string durable = 3;
+  // Single filter subject, when configured.
+  string filter_subject = 4;
+  // Multiple filter subjects, when configured.
+  repeated string filter_subjects = 5;
+  // NATS acknowledgement policy.
+  string ack_policy = 6;
+  // Whether this is a pull consumer.
+  bool pull_based = 7;
+  // Whether this push consumer has an active subscription.
+  bool push_bound = 8;
+  // Pending messages for this consumer.
+  int64 pending = 9;
+  // Messages waiting for acknowledgement.
+  int32 ack_pending = 10;
+  // Redelivered message count.
+  int32 redelivered = 11;
+  // Pull requests waiting for messages.
+  int32 waiting = 12;
+  // Delivered consumer sequence.
+  string delivered_consumer_sequence = 13;
+  // Delivered stream sequence.
+  string delivered_stream_sequence = 14;
+  // Acknowledgement floor consumer sequence.
+  string ack_floor_consumer_sequence = 15;
+  // Acknowledgement floor stream sequence.
+  string ack_floor_stream_sequence = 16;
+}
+
+// Event-sourced projection health and memory estimates.
+message AdminProjectionState {
+  // Stable projection key.
+  string key = 1;
+  // Display name.
+  string name = 2;
+  // EVT subjects consumed by this projection.
+  repeated string subjects = 3;
+  // Whether the projection projector is running.
+  bool started = 4;
+  // Startup duration in seconds, set after startup completes.
+  optional double startup_duration_seconds = 5;
+  // Last event sequence applied by the projection.
+  string last_applied_sequence = 6;
+  // Target sequence among the projection's matching subjects.
+  string matching_stream_sequence = 7;
+  // Current last sequence of the EVT stream.
+  string stream_last_sequence = 8;
+  // Matching-subject lag between target and applied sequence.
+  int64 lag = 9;
+  // Whether the projection failed.
+  bool failed = 10;
+  // Sequence that failed, when failed is true.
+  string failed_sequence = 11;
+  // Failure message, when failed is true.
+  string failure = 12;
+  // Estimated number of entries held by the projection.
+  int64 entry_count = 13;
+  // Estimated memory used by the projection in bytes.
+  int64 estimated_bytes = 14;
+  // Estimated average memory per entry in bytes.
+  int64 average_entry_bytes = 15;
+  // Projection-specific diagnostic metrics.
+  repeated AdminProjectionMetric metrics = 16;
+}
+
+// Projection-specific diagnostic metric.
+message AdminProjectionMetric {
+  // Metric name.
+  string name = 1;
+  // Metric value.
+  int64 value = 2;
+  // Estimated memory represented by this metric in bytes.
+  int64 bytes = 3;
+}
+
+// Provides server diagnostics for the admin system page.
+service AdminDiagnosticsService {
+  // Returns broker, JetStream, and projection diagnostics. Requires the
+  // server owner role.
+  rpc GetSystemInfo(GetSystemInfoRequest) returns (GetSystemInfoResponse);
+}

+ 101 - 0
proto/chatto/admin/v1/event_log.proto

@@ -0,0 +1,101 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "google/protobuf/timestamp.proto";
+
+// Filter for owner/auditor event-log inspection.
+message AdminEventLogFilter {
+  // Event oneof payload type, such as MessagePostedEvent.
+  string event_type = 1;
+  // Actor user ID.
+  string actor_id = 2;
+  // Inclusive lower creation timestamp bound.
+  google.protobuf.Timestamp created_at_from = 3;
+  // Inclusive upper creation timestamp bound.
+  google.protobuf.Timestamp created_at_to = 4;
+}
+
+// Request to list durable EVT entries newest-first.
+message ListEventsRequest {
+  // Maximum entries to return. The server clamps this to its diagnostic limit.
+  int32 limit = 1 [(buf.validate.field).int32.gte = 0];
+  // Exclusive sequence cursor. Returned entries are older than this sequence.
+  optional string before = 2;
+  // Optional filter. Filtered scans inspect a bounded window per request.
+  AdminEventLogFilter filter = 3;
+}
+
+// Newest-first page of durable EVT entries.
+message ListEventsResponse {
+  // Event-log rows.
+  repeated AdminEventLogEntry entries = 1;
+  // Whether older rows may be fetched with end_cursor.
+  bool has_older = 2;
+  // Sequence cursor for loading older rows.
+  optional string end_cursor = 3;
+  // Total EVT message count reported by JetStream.
+  int64 total_count = 4;
+  // Number of stream sequences inspected while building this page.
+  int32 scanned_count = 5;
+  // Maximum number of stream sequences inspected for this page.
+  int32 scan_limit = 6;
+  // True when a filtered request reached scan_limit before finding a full page.
+  bool scan_limited = 7;
+}
+
+// Request to list filterable event types.
+message ListEventTypesRequest {}
+
+// Finite snapshot of event types accepted by event-log filters.
+message ListEventTypesResponse {
+  // Sorted event type names.
+  repeated string event_types = 1;
+}
+
+// Request to read one durable EVT entry by stream sequence.
+message GetEventRequest {
+  // Required EVT stream sequence.
+  string sequence = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of reading one durable EVT entry.
+message GetEventResponse {
+  // Requested event-log entry.
+  AdminEventLogEntry entry = 1;
+}
+
+// One durable EVT entry decoded for operator inspection.
+message AdminEventLogEntry {
+  // EVT stream sequence.
+  string sequence = 1;
+  // NATS subject.
+  string subject = 2;
+  // Aggregate type parsed from the subject.
+  string aggregate_type = 3;
+  // Aggregate ID parsed from the subject.
+  string aggregate_id = 4;
+  // Event oneof payload type, or decode-error when decoding failed.
+  string event_type = 5;
+  // Durable event ID.
+  string event_id = 6;
+  // Actor user ID stored on the event.
+  string actor_id = 7;
+  // Event creation timestamp.
+  google.protobuf.Timestamp created_at = 8;
+  // Pretty-printed JSON representation of the event payload.
+  string payload_json = 9;
+}
+
+// Provides server-admin audit/event-log diagnostics.
+service AdminEventLogService {
+  // Lists durable EVT entries newest-first. Requires admin.view-audit.
+  rpc ListEvents(ListEventsRequest) returns (ListEventsResponse);
+  // Lists filterable event type names as a finite snapshot. Requires
+  // admin.view-audit.
+  rpc ListEventTypes(ListEventTypesRequest) returns (ListEventTypesResponse);
+  // Reads one durable EVT entry by stream sequence. Requires admin.view-audit.
+  // Returns NOT_FOUND when the requested sequence does not exist.
+  rpc GetEvent(GetEventRequest) returns (GetEventResponse);
+}

+ 239 - 0
proto/chatto/admin/v1/members.proto

@@ -0,0 +1,239 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/pagination.proto";
+import "chatto/api/v1/roles.proto";
+import "chatto/api/v1/users.proto";
+import "chatto/admin/v1/roles.proto";
+import "google/protobuf/timestamp.proto";
+
+// User row returned by server-admin member management reads.
+//
+// The embedded user record carries public identity fields; remaining fields are
+// admin/member metadata with visibility controlled by the service.
+message AdminMember {
+  reserved 1 to 4;
+  reserved "id", "login", "display_name", "avatar_url";
+
+  // Explicit role assignments. The implicit everyone role is omitted.
+  repeated string roles = 5;
+  // Account creation time, when known.
+  google.protobuf.Timestamp created_at = 6;
+  reserved 7;
+  reserved "deleted";
+  // Whether the caller may see that the user has a verified email and one exists.
+  bool has_verified_email = 8;
+  // Verified email addresses visible to the caller.
+  repeated string verified_emails = 9;
+  // Whether the caller may delete this account.
+  bool viewer_can_delete_account = 10;
+  // Last self-service username change, when visible and known.
+  google.protobuf.Timestamp last_login_change = 11;
+  // Public identity fields for this user.
+  chatto.api.v1.User user = 12;
+}
+
+// Request server-admin member rows.
+message ListMembersRequest {
+  reserved 2, 3;
+  reserved "limit", "offset";
+
+  // Case-insensitive login/display-name search term. Empty disables search.
+  string search = 1;
+  // Page request. Defaults to 20 results when absent or limit is zero.
+  chatto.api.v1.PageRequest page = 4;
+}
+
+// Server-admin member rows plus role summaries.
+message ListMembersResponse {
+  reserved 3, 4;
+  reserved "total_count", "has_more";
+
+  // Matching members.
+  repeated AdminMember members = 1;
+  // Public roles for display-name lookup.
+  repeated chatto.api.v1.Role roles = 2;
+  // Page metadata.
+  chatto.api.v1.PageInfo page = 5;
+}
+
+// Request one server-admin member detail record.
+message GetMemberRequest {
+  oneof target {
+    // Target stable user ID.
+    string user_id = 1 [(buf.validate.field).string.min_len = 1];
+    // Target login identifier.
+    string login = 2 [(buf.validate.field).string.min_len = 1];
+  }
+}
+
+// Server-admin member detail payload.
+message GetMemberResponse {
+  // Member record.
+  AdminMember member = 1;
+  // Roles for assignment UI.
+  repeated AdminRole roles = 2;
+  // Permissions available for per-user overrides.
+  repeated string available_permissions = 3;
+  // Whether the caller may assign/revoke roles.
+  bool viewer_can_assign_roles = 4;
+  // Whether the caller may edit role definitions.
+  bool viewer_can_manage_roles = 5;
+  // Whether the caller may edit direct user permissions.
+  bool viewer_can_manage_user_permissions = 6;
+}
+
+// Request server-admin member rows for a set of stable user IDs.
+message BatchGetMembersRequest {
+  // Required target user IDs. Unknown IDs are omitted from the response.
+  repeated string user_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Batch server-admin member response.
+message BatchGetMembersResponse {
+  // Found members. The server preserves first-seen request order and
+  // de-duplicates repeated IDs.
+  repeated AdminMember members = 1;
+  // Public roles for display-name lookup.
+  repeated chatto.api.v1.Role roles = 2;
+}
+
+// Request to assign a role to a user.
+message AssignRoleRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Role name to assign.
+  string role_name = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of assigning a role.
+message AssignRoleResponse {
+  reserved 1;
+  reserved "assigned";
+
+  // Updated admin member row.
+  AdminMember member = 2;
+}
+
+// Request to revoke a role from a user.
+message RevokeRoleRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Role name to revoke.
+  string role_name = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of revoking a role.
+message RevokeRoleResponse {
+  reserved 1;
+  reserved "revoked";
+
+  // Updated admin member row.
+  AdminMember member = 2;
+}
+
+// Request to update a user's identity as a server-admin action.
+message UpdateUserRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // New display name, when changing it. Empty clears the explicit display
+  // name. The server also rejects control and confusing invisible characters.
+  optional string display_name = 2 [(buf.validate.field).string.max_len = 32];
+  // New login identifier, when changing it. The server accepts ASCII letters,
+  // digits, period, underscore, and hyphen, starting with a letter or digit.
+  optional string login = 3 [(buf.validate.field).string = {
+    min_len: 2
+    max_len: 32
+  }];
+}
+
+// Result of an admin user update.
+message UpdateUserResponse {
+  // Updated user profile.
+  chatto.api.v1.User user = 1;
+  // Updated admin member row.
+  AdminMember member = 2;
+}
+
+// Request to update a user's password as a server-admin action.
+message UpdateUserPasswordRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // New password. The server applies the same password policy as registration
+  // and password reset.
+  string password = 2 [(buf.validate.field).string = {
+    min_len: 8
+    max_len: 128
+  }];
+}
+
+// Result of an admin password update.
+message UpdateUserPasswordResponse {
+  // Updated admin member row.
+  AdminMember member = 1;
+}
+
+// Request to clear a user's self-service username-change cooldown.
+message ClearUsernameCooldownRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of clearing a user's username-change cooldown.
+message ClearUsernameCooldownResponse {
+  // True when the request completed.
+  bool cleared = 1;
+}
+
+// Request to delete a user account as a server-admin action.
+message DeleteUserRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Current password proof for accounts with a password when the active
+  // runtime credential is no longer fresh.
+  string current_password = 2;
+}
+
+// Result of deleting a user account.
+message DeleteUserResponse {
+  // True when the user was deleted.
+  bool deleted = 1;
+}
+
+// Server-admin user management commands and member-detail reads.
+service AdminUserService {
+  // Lists server members for the admin members screen. Requires
+  // admin.view-users.
+  rpc ListMembers(ListMembersRequest) returns (ListMembersResponse);
+  // Gets one server member plus role/permission metadata for admin details.
+  // Requires admin.view-users. Returns NOT_FOUND when the user does not exist.
+  rpc GetMember(GetMemberRequest) returns (GetMemberResponse);
+  // Gets server member rows for multiple users. Requires admin.view-users.
+  rpc BatchGetMembers(BatchGetMembersRequest) returns (BatchGetMembersResponse);
+  // Assigns a role to a user. Requires role.assign.
+  rpc AssignRole(AssignRoleRequest) returns (AssignRoleResponse);
+  // Revokes a role from a user. Requires role.assign.
+  rpc RevokeRole(RevokeRoleRequest) returns (RevokeRoleResponse);
+  // Updates another user's login and/or display name as an admin action.
+  // Requires user.manage-accounts; the caller cannot target their own account.
+  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);
+  // Updates another user's password as an admin action. Requires
+  // user.manage-accounts and a fresh credential for the caller; the caller
+  // cannot target their own account.
+  rpc UpdateUserPassword(UpdateUserPasswordRequest) returns (UpdateUserPasswordResponse);
+  // Clears the target user's self-service username-change cooldown. Requires
+  // user.manage-accounts.
+  rpc ClearUsernameCooldown(ClearUsernameCooldownRequest) returns (ClearUsernameCooldownResponse);
+  // Deletes a user account as an admin action. Requires user.delete-any for
+  // other users, user.delete-self for the caller, and a fresh credential for
+  // the caller.
+  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
+}

+ 326 - 0
proto/chatto/admin/v1/permissions.proto

@@ -0,0 +1,326 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/roles.proto";
+
+// Trinary permission decision used by permission matrices and write requests.
+enum PermissionDecision {
+  // Unspecified decision; rejected for writes.
+  PERMISSION_DECISION_UNSPECIFIED = 0;
+  // Explicit allow.
+  PERMISSION_DECISION_ALLOW = 1;
+  // Explicit deny.
+  PERMISSION_DECISION_DENY = 2;
+  // No explicit decision; clears the permission state on writes.
+  PERMISSION_DECISION_NONE = 3;
+}
+
+// Scope tier for permission matrices and write requests.
+enum PermissionScopeKind {
+  // Unspecified scope; treated as server scope where older clients omit it.
+  PERMISSION_SCOPE_KIND_UNSPECIFIED = 0;
+  // Server tier.
+  PERMISSION_SCOPE_KIND_SERVER = 1;
+  // Room-group tier.
+  PERMISSION_SCOPE_KIND_GROUP = 2;
+  // Room tier.
+  PERMISSION_SCOPE_KIND_ROOM = 3;
+}
+
+// Permission scope reference.
+message PermissionScope {
+  // Scope tier.
+  PermissionScopeKind kind = 1 [(buf.validate.field).enum.defined_only = true];
+  // Scope ID for group or room scope. Empty for server scope.
+  string id = 2;
+}
+
+// Permission grants and denials at a single tier.
+message TierPermissions {
+  // Explicitly granted permissions.
+  repeated string permissions = 1;
+  // Explicitly denied permissions.
+  repeated string permission_denials = 2;
+}
+
+// One role's permission state at a single tier.
+message TierRole {
+  reserved 1 to 5;
+  reserved "role_name", "display_name", "description", "is_system", "position";
+
+  // Explicit state at this tier.
+  TierPermissions override = 6;
+  // Allows inherited from broader tiers.
+  repeated string inherited_allows = 7;
+  // Denials inherited from broader tiers.
+  repeated string inherited_denials = 8;
+  // Public role metadata.
+  chatto.api.v1.Role role = 9;
+}
+
+// Role permission matrix for one tier.
+message TierRoles {
+  // Permissions configurable at this tier.
+  repeated string applicable_permissions = 1;
+  // Roles sorted by position.
+  repeated TierRole roles = 2;
+}
+
+// Request a role-permission tier matrix.
+message GetRolePermissionTierMatrixRequest {
+  // Target scope. Omit or use SERVER for server scope.
+  PermissionScope scope = 1;
+}
+
+// Response containing a role-permission tier matrix.
+message GetRolePermissionTierMatrixResponse {
+  // Requested matrix.
+  TierRoles matrix = 1;
+}
+
+// Matrix column scope.
+message PermissionMatrixScope {
+  // Stable scope ID for the server, a group, or a room.
+  string id = 1;
+  // Human-readable label.
+  string label = 2;
+  // Scope tier.
+  PermissionScopeKind kind = 3;
+  // Parent group ID for room scopes.
+  string parent_group_id = 4;
+}
+
+// Matrix cell for one permission and scope.
+message PermissionMatrixCell {
+  // Permission identifier.
+  string permission = 1;
+  // Scope ID matching PermissionMatrixScope.id.
+  string scope_id = 2;
+  // Explicit decision at this scope.
+  PermissionDecision override = 3;
+  // Effective decision at this scope.
+  PermissionDecision effective = 4;
+}
+
+// Permission matrix for one role across all scopes.
+message RolePermissionMatrix {
+  // Role name.
+  string role_name = 1;
+  // Permissions rendered as rows.
+  repeated string applicable_permissions = 2;
+  // Scopes rendered as columns.
+  repeated PermissionMatrixScope scopes = 3;
+  // Sparse matrix cells.
+  repeated PermissionMatrixCell cells = 4;
+}
+
+// Request one role's permission matrix.
+message GetRolePermissionMatrixRequest {
+  // Stable role name.
+  string role_name = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response containing one role's permission matrix.
+message GetRolePermissionMatrixResponse {
+  // Requested matrix.
+  RolePermissionMatrix matrix = 1;
+}
+
+// Permission matrix for one user across all scopes.
+message UserPermissionMatrix {
+  // User ID.
+  string user_id = 1;
+  // Permissions rendered as rows.
+  repeated string applicable_permissions = 2;
+  // Scopes rendered as columns.
+  repeated PermissionMatrixScope scopes = 3;
+  // Sparse matrix cells.
+  repeated PermissionMatrixCell cells = 4;
+}
+
+// One resource-oriented permission decision at a concrete scope.
+//
+// This is the row-oriented counterpart to PermissionMatrixCell. Prefer this
+// shape when building integrations that do not need matrix column joins.
+message ScopedPermissionDecision {
+  // Permission identifier.
+  string permission = 1;
+  // Server, group, or room scope this decision applies to.
+  PermissionScope scope = 2;
+  // Explicit decision stored at this scope.
+  PermissionDecision override = 3;
+  // Effective decision after inherited broader scopes are applied.
+  PermissionDecision effective = 4;
+}
+
+// Stored permission decision returned after a write.
+message PermissionDecisionUpdate {
+  // Permission identifier.
+  string permission = 1;
+  // Server, group, or room scope this decision applies to.
+  PermissionScope scope = 2;
+  // Stored decision after the write. NONE means no explicit decision remains.
+  PermissionDecision decision = 3;
+}
+
+// Request explicit/effective permission decisions for one role.
+message ListRolePermissionDecisionsRequest {
+  // Stable role name.
+  string role_name = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Resource-oriented permission decisions for one role.
+message ListRolePermissionDecisionsResponse {
+  // Stable role name.
+  string role_name = 1;
+  // Permission decisions keyed by permission and typed scope.
+  repeated ScopedPermissionDecision decisions = 2;
+}
+
+// Request explicit/effective permission decisions for one user.
+message ListUserPermissionDecisionsRequest {
+  // User ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Resource-oriented permission decisions for one user.
+message ListUserPermissionDecisionsResponse {
+  // User ID.
+  string user_id = 1;
+  // Permission decisions keyed by permission and typed scope.
+  repeated ScopedPermissionDecision decisions = 2;
+}
+
+// Level at which a permission explanation found a decision.
+enum PermissionDecisionLevel {
+  // No decision level was reached.
+  PERMISSION_DECISION_LEVEL_UNSPECIFIED = 0;
+  // Server-level role or user decision.
+  PERMISSION_DECISION_LEVEL_SERVER = 1;
+  // Room-group-level role or user decision.
+  PERMISSION_DECISION_LEVEL_GROUP = 2;
+  // Room-level role or user decision.
+  PERMISSION_DECISION_LEVEL_ROOM = 3;
+}
+
+// One explicit decision encountered while explaining a permission.
+message PermissionTraceEntry {
+  // Level at which this decision was observed.
+  PermissionDecisionLevel level = 1;
+  // Role name or policy marker that produced the decision.
+  string role_name = 2;
+  // Decision observed at this trace step.
+  PermissionDecision decision = 3;
+  // Whether this entry is the winning decision.
+  bool applied = 4;
+}
+
+// Full decision trace for one permission and target user.
+message PermissionExplanation {
+  // Permission identifier.
+  string permission = 1;
+  // Overall decision after applying deny-wins resolution.
+  PermissionDecision state = 2;
+  // Level of the winning decision, when state is not NONE.
+  PermissionDecisionLevel decided_at = 3;
+  // Role name or policy marker that produced the winning decision.
+  string decided_by_role = 4;
+  // Ordered decision trace. The first entry is the winning decision.
+  repeated PermissionTraceEntry trace = 5;
+}
+
+// Request permission explanations for a user at server or room scope.
+message ExplainPermissionsRequest {
+  // User whose permissions should be explained.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Optional room scope. Omit for server-scoped permissions.
+  string room_id = 2;
+}
+
+// Permission explanations for all permissions applicable at the requested scope.
+message ExplainPermissionsResponse {
+  // Explanation rows sorted by permission metadata order.
+  repeated PermissionExplanation explanations = 1;
+}
+
+// Request one user's permission matrix.
+message GetUserPermissionMatrixRequest {
+  // User ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response containing one user's permission matrix.
+message GetUserPermissionMatrixResponse {
+  // Requested matrix.
+  UserPermissionMatrix matrix = 1;
+}
+
+// Request to set one role permission state.
+message SetRolePermissionRequest {
+  // Role name.
+  string role_name = 1 [(buf.validate.field).string.min_len = 1];
+  // Permission identifier.
+  string permission = 2 [(buf.validate.field).string.min_len = 1];
+  // New decision. NONE clears the explicit state.
+  PermissionDecision decision = 3 [(buf.validate.field).enum = {
+    defined_only: true
+    not_in: [0]
+  }];
+  // Target scope. Omit or use SERVER for server scope.
+  PermissionScope scope = 4;
+}
+
+// Result of setting one role permission state.
+message SetRolePermissionResponse {
+  // Updated permission decision for the requested role/scope cell.
+  PermissionDecisionUpdate decision = 1;
+}
+
+// Request to set one user permission state.
+message SetUserPermissionRequest {
+  // Target user ID.
+  string user_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Permission identifier.
+  string permission = 2 [(buf.validate.field).string.min_len = 1];
+  // New decision. NONE clears the explicit state.
+  PermissionDecision decision = 3 [(buf.validate.field).enum = {
+    defined_only: true
+    not_in: [0]
+  }];
+  // Target scope. Omit or use SERVER for server scope.
+  PermissionScope scope = 4;
+}
+
+// Result of setting one user permission state.
+message SetUserPermissionResponse {
+  // Updated permission decision for the requested user/scope cell.
+  PermissionDecisionUpdate decision = 1;
+}
+
+// Provides permission matrix reads and admin permission writes.
+service AdminPermissionService {
+  // Gets the role-permission matrix for a single tier. Requires role.manage
+  // for server/group scope; room scope requires role.manage or room.manage in
+  // that room.
+  rpc GetRolePermissionTierMatrix(GetRolePermissionTierMatrixRequest) returns (GetRolePermissionTierMatrixResponse);
+  // Gets one role's full permission matrix. Requires role.manage. Returns
+  // NOT_FOUND when the role does not exist.
+  rpc GetRolePermissionMatrix(GetRolePermissionMatrixRequest) returns (GetRolePermissionMatrixResponse);
+  // Lists one role's permission decisions as resource-oriented rows. Requires
+  // role.manage. Returns NOT_FOUND when the role does not exist.
+  rpc ListRolePermissionDecisions(ListRolePermissionDecisionsRequest) returns (ListRolePermissionDecisionsResponse);
+  // Gets one user's full permission matrix. Requires user.manage-permissions.
+  // Returns NOT_FOUND when the user does not exist.
+  rpc GetUserPermissionMatrix(GetUserPermissionMatrixRequest) returns (GetUserPermissionMatrixResponse);
+  // Lists one user's permission decisions as resource-oriented rows. Requires
+  // user.manage-permissions. Returns NOT_FOUND when the user does not exist.
+  rpc ListUserPermissionDecisions(ListUserPermissionDecisionsRequest) returns (ListUserPermissionDecisionsResponse);
+  // Explains permission resolution for another user. Requires role.manage.
+  rpc ExplainPermissions(ExplainPermissionsRequest) returns (ExplainPermissionsResponse);
+  // Sets one role permission decision. Requires role.manage or scoped room.manage for room scope.
+  rpc SetRolePermission(SetRolePermissionRequest) returns (SetRolePermissionResponse);
+  // Sets one user permission decision. Requires user.manage-permissions.
+  rpc SetUserPermission(SetUserPermissionRequest) returns (SetUserPermissionResponse);
+}

+ 135 - 0
proto/chatto/admin/v1/roles.proto

@@ -0,0 +1,135 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/roles.proto";
+import "chatto/api/v1/users.proto";
+
+// Role metadata plus administrative permission state.
+message AdminRole {
+  // Public role metadata shared with ordinary role catalog reads.
+  chatto.api.v1.Role role = 1;
+  // Permissions granted by this role.
+  repeated string permissions = 2;
+  // Permissions denied by this role.
+  repeated string permission_denials = 3;
+}
+
+// Request the role catalog.
+message ListRolesRequest {}
+
+// Finite role catalog snapshot plus viewer role-management capabilities.
+message ListRolesResponse {
+  // Roles sorted by position.
+  repeated AdminRole roles = 1;
+  // Whether the caller may create/update/delete role definitions.
+  bool viewer_can_manage_roles = 2;
+  // Whether the caller may assign/revoke roles and view role rosters.
+  bool viewer_can_assign_roles = 3;
+}
+
+// Request one role.
+message GetRoleRequest {
+  // Stable role name.
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// One role plus role-management metadata.
+message GetRoleResponse {
+  // Requested role.
+  AdminRole role = 1;
+  // Explicit users with this role. Empty unless the caller may assign roles.
+  repeated chatto.api.v1.User users = 2;
+  // Whether the caller may create/update/delete role definitions.
+  bool viewer_can_manage_roles = 3;
+  // Whether the caller may assign/revoke roles and view role rosters.
+  bool viewer_can_assign_roles = 4;
+}
+
+// Request to create a role.
+message CreateRoleRequest {
+  // Stable role name.
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+  // Display name shown in admin and mention UIs.
+  string display_name = 2 [(buf.validate.field).string.max_len = 80];
+  // Optional role description.
+  string description = 3 [(buf.validate.field).string.max_len = 500];
+  // Whether messages may notify users assigned to this role.
+  bool pingable = 4;
+}
+
+// Result of creating a role.
+message CreateRoleResponse {
+  // Created role.
+  AdminRole role = 1;
+}
+
+// Request to update role metadata.
+message UpdateRoleRequest {
+  // Stable role name.
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+  // Display name shown in admin and mention UIs, when changing it.
+  optional string display_name = 2 [(buf.validate.field).string.max_len = 80];
+  // Role description, when changing it. Empty clears the description.
+  optional string description = 3 [(buf.validate.field).string.max_len = 500];
+  // Whether messages may notify users assigned to this role, when changing it.
+  optional bool pingable = 4;
+}
+
+// Result of updating a role.
+message UpdateRoleResponse {
+  // Updated role.
+  AdminRole role = 1;
+}
+
+// Request to delete a role.
+message DeleteRoleRequest {
+  // Stable role name.
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of deleting a role.
+message DeleteRoleResponse {
+  // True when the request completed.
+  bool deleted = 1;
+}
+
+// Request to replace custom role order.
+message ReorderRolesRequest {
+  // Ordered custom role names. The list must include every custom role exactly
+  // once; system roles keep fixed positions and must not be included.
+  repeated string role_names = 1 [(buf.validate.field).repeated = {
+    max_items: 1000
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Result of replacing custom role order.
+message ReorderRolesResponse {
+  // Full role catalog after reordering.
+  repeated AdminRole roles = 1;
+}
+
+// Provides authenticated role reads and administrative role definition changes.
+service AdminRoleService {
+  // Lists the role catalog including permission arrays as a finite snapshot.
+  // Requires an authenticated user. This intentionally includes permission
+  // arrays for clients that need to show a user's assigned roles and their
+  // effects; use chatto.api.v1.RoleService for lightweight catalog rendering.
+  rpc ListRoles(ListRolesRequest) returns (ListRolesResponse);
+  // Gets one role plus admin detail metadata. Returns NOT_FOUND when the
+  // role does not exist. Requires an authenticated user; the assigned-user
+  // roster is empty unless the caller may assign roles.
+  rpc GetRole(GetRoleRequest) returns (GetRoleResponse);
+  // Creates a custom role. Requires role.manage.
+  rpc CreateRole(CreateRoleRequest) returns (CreateRoleResponse);
+  // Updates role metadata. Requires role.manage.
+  rpc UpdateRole(UpdateRoleRequest) returns (UpdateRoleResponse);
+  // Deletes a custom role. Requires role.manage.
+  rpc DeleteRole(DeleteRoleRequest) returns (DeleteRoleResponse);
+  // Replaces the custom role order. Requires role.manage.
+  rpc ReorderRoles(ReorderRolesRequest) returns (ReorderRolesResponse);
+}

+ 261 - 0
proto/chatto/admin/v1/room_layout.proto

@@ -0,0 +1,261 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/room_directory.proto";
+import "chatto/api/v1/rooms.proto";
+
+// One ordered sidebar item in an admin room group layout.
+message AdminRoomLayoutItem {
+  oneof item {
+    // Channel room entry.
+    chatto.api.v1.Room room = 1;
+    // Sidebar link entry.
+    chatto.api.v1.SidebarLink sidebar_link = 2;
+  }
+}
+
+// Ordered room group returned after admin layout mutations.
+message AdminRoomLayoutGroup {
+  reserved 4;
+  reserved "rooms";
+
+  // Stable room group ID.
+  string id = 1;
+  // Display name.
+  string name = 2;
+  // Public group description, when set.
+  string description = 3;
+  // Mixed room/sidebar-link entries in sidebar order.
+  repeated AdminRoomLayoutItem items = 5;
+  // Whether the viewer can create rooms in this group.
+  bool can_create_room = 6;
+}
+
+// Request for the server-admin room group layout.
+message ListRoomGroupsRequest {}
+
+// Server-admin room group layout response.
+message ListRoomGroupsResponse {
+  // Ordered room groups, including archived rooms the caller can manage.
+  repeated AdminRoomLayoutGroup groups = 1;
+}
+
+// Sidebar item kind used in reorder requests.
+enum AdminRoomLayoutItemKind {
+  // The item kind was not specified.
+  ADMIN_ROOM_LAYOUT_ITEM_KIND_UNSPECIFIED = 0;
+  // Channel room entry.
+  ADMIN_ROOM_LAYOUT_ITEM_KIND_ROOM = 1;
+  // Sidebar link entry.
+  ADMIN_ROOM_LAYOUT_ITEM_KIND_SIDEBAR_LINK = 2;
+}
+
+// One item reference in a sidebar order replacement.
+message AdminRoomLayoutItemInput {
+  // Item kind.
+  AdminRoomLayoutItemKind kind = 1 [(buf.validate.field).enum = {
+    defined_only: true
+    not_in: [0]
+  }];
+  // Room ID when kind is ROOM, sidebar link ID when kind is SIDEBAR_LINK.
+  string id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Request to create a room group.
+message CreateRoomGroupRequest {
+  // Required group display name.
+  string name = 1 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 80
+  }];
+  // Optional public group description.
+  string description = 2 [(buf.validate.field).string.max_len = 500];
+}
+
+// Result of creating a room group.
+message CreateRoomGroupResponse {
+  // Created room group.
+  AdminRoomLayoutGroup group = 1;
+}
+
+// Request to update a room group.
+message UpdateRoomGroupRequest {
+  // Required room group ID.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+  // New group display name, when changing it.
+  optional string name = 2 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 80
+  }];
+  // New public group description, when changing it. Empty clears the
+  // description.
+  optional string description = 3 [(buf.validate.field).string.max_len = 500];
+}
+
+// Result of updating a room group.
+message UpdateRoomGroupResponse {
+  // Updated room group.
+  AdminRoomLayoutGroup group = 1;
+}
+
+// Request to delete an empty room group.
+message DeleteRoomGroupRequest {
+  // Required room group ID.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of deleting a room group.
+message DeleteRoomGroupResponse {
+  // True when the room group was deleted.
+  bool deleted = 1;
+}
+
+// Request to replace the global room group order.
+message ReorderRoomGroupsRequest {
+  // Ordered room group IDs. The list must include every existing group exactly
+  // once.
+  repeated string ordered_group_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 1000
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Result of replacing the global room group order.
+message ReorderRoomGroupsResponse {
+  // Room groups in their new order.
+  repeated AdminRoomLayoutGroup groups = 1;
+}
+
+// Request to move a channel room to another room group.
+message MoveRoomToGroupRequest {
+  // Required channel room ID.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required destination room group ID.
+  string group_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of moving a channel room.
+message MoveRoomToGroupResponse {
+  // Moved room after the operation.
+  chatto.api.v1.Room room = 1;
+}
+
+// Request to replace the mixed sidebar item order in one group.
+message ReorderSidebarItemsInGroupRequest {
+  // Required room group ID.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Ordered room/sidebar-link item references.
+  repeated AdminRoomLayoutItemInput items = 2 [(buf.validate.field).repeated = {max_items: 1000}];
+}
+
+// Result of replacing the mixed sidebar item order.
+message ReorderSidebarItemsInGroupResponse {
+  // Updated room group.
+  AdminRoomLayoutGroup group = 1;
+}
+
+// Request to create a sidebar link in one room group.
+message CreateSidebarLinkRequest {
+  // Required destination room group ID.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required display label.
+  string label = 2 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 80
+  }];
+  // Required absolute http(s) URL or server-local path.
+  string url = 3 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 2048
+  }];
+}
+
+// Result of creating a sidebar link.
+message CreateSidebarLinkResponse {
+  // Created sidebar link.
+  chatto.api.v1.SidebarLink sidebar_link = 1;
+}
+
+// Request to update a sidebar link.
+message UpdateSidebarLinkRequest {
+  // Required sidebar link ID.
+  string link_id = 1 [(buf.validate.field).string.min_len = 1];
+  // New display label, when changing it.
+  optional string label = 2 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 80
+  }];
+  // New absolute http(s) URL or server-local path, when changing it.
+  optional string url = 3 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 2048
+  }];
+}
+
+// Result of updating a sidebar link.
+message UpdateSidebarLinkResponse {
+  // Updated sidebar link.
+  chatto.api.v1.SidebarLink sidebar_link = 1;
+}
+
+// Request to delete a sidebar link.
+message DeleteSidebarLinkRequest {
+  // Required sidebar link ID.
+  string link_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of deleting a sidebar link.
+message DeleteSidebarLinkResponse {
+  // True when the sidebar link was deleted.
+  bool deleted = 1;
+}
+
+// Request to move a sidebar link to another room group.
+message MoveSidebarLinkToGroupRequest {
+  // Required sidebar link ID.
+  string link_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required destination room group ID.
+  string group_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of moving a sidebar link.
+message MoveSidebarLinkToGroupResponse {
+  // Moved sidebar link.
+  chatto.api.v1.SidebarLink sidebar_link = 1;
+}
+
+// Reads and mutates the server-admin room layout.
+service AdminRoomLayoutService {
+  // Lists the editable room group layout. Requires role.manage and returns
+  // visible room entries, including archived rooms for administrative actions
+  // such as unarchiving.
+  rpc ListRoomGroups(ListRoomGroupsRequest) returns (ListRoomGroupsResponse);
+  // Creates a room group. Requires role.manage.
+  rpc CreateRoomGroup(CreateRoomGroupRequest) returns (CreateRoomGroupResponse);
+  // Updates a room group's metadata. Requires role.manage.
+  rpc UpdateRoomGroup(UpdateRoomGroupRequest) returns (UpdateRoomGroupResponse);
+  // Deletes an empty room group. Requires role.manage.
+  rpc DeleteRoomGroup(DeleteRoomGroupRequest) returns (DeleteRoomGroupResponse);
+  // Replaces the global room group order. Requires role.manage.
+  rpc ReorderRoomGroups(ReorderRoomGroupsRequest) returns (ReorderRoomGroupsResponse);
+  // Moves a channel room to another room group. Requires room.manage in both
+  // source and destination groups.
+  rpc MoveRoomToGroup(MoveRoomToGroupRequest) returns (MoveRoomToGroupResponse);
+  // Replaces the mixed room/link sidebar order in one group. Requires
+  // room.manage in that group.
+  rpc ReorderSidebarItemsInGroup(ReorderSidebarItemsInGroupRequest) returns (ReorderSidebarItemsInGroupResponse);
+  // Creates a sidebar link. Requires room.manage in the destination group.
+  rpc CreateSidebarLink(CreateSidebarLinkRequest) returns (CreateSidebarLinkResponse);
+  // Updates a sidebar link. Requires room.manage in the link's current group.
+  rpc UpdateSidebarLink(UpdateSidebarLinkRequest) returns (UpdateSidebarLinkResponse);
+  // Deletes a sidebar link. Requires room.manage in the link's current group.
+  rpc DeleteSidebarLink(DeleteSidebarLinkRequest) returns (DeleteSidebarLinkResponse);
+  // Moves a sidebar link to another group. Requires room.manage in both source
+  // and destination groups.
+  rpc MoveSidebarLinkToGroup(MoveSidebarLinkToGroupRequest) returns (MoveSidebarLinkToGroupResponse);
+}

+ 165 - 0
proto/chatto/admin/v1/server.proto

@@ -0,0 +1,165 @@
+syntax = "proto3";
+
+package chatto.admin.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/common.proto";
+import "chatto/api/v1/server.proto";
+
+// Runtime-editable server profile settings.
+message ServerConfig {
+  // Server name for page titles. Empty means the default is used.
+  string server_name = 1;
+  // Short server description for OG link-preview metadata.
+  string description = 2;
+  // Message of the day for the authenticated header.
+  string motd = 3;
+  // Welcome message shown on the login page.
+  string welcome_message = 4;
+}
+
+// Request server profile settings.
+message GetServerConfigRequest {}
+
+// Server profile settings plus effective public branding.
+message GetServerConfigResponse {
+  reserved 2;
+  reserved "profile";
+
+  // Runtime-editable server profile settings.
+  ServerConfig config = 1;
+  // Effective public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 3;
+}
+
+// Request to update runtime-editable server profile settings.
+message UpdateServerConfigRequest {
+  // Optional server name for page titles. Set to empty string to use default.
+  optional string server_name = 1 [(buf.validate.field).string.max_len = 80];
+  // Optional short server description for OG link-preview metadata. Set to
+  // empty string to clear.
+  optional string description = 2 [(buf.validate.field).string.max_len = 500];
+  // Optional message of the day for the header. Set to empty string to clear.
+  optional string motd = 3 [(buf.validate.field).string.max_len = 1000];
+  // Optional welcome message shown on the login page. Set to empty string to
+  // clear.
+  optional string welcome_message = 4 [(buf.validate.field).string.max_len = 10000];
+}
+
+// Result of updating runtime-editable server profile settings.
+message UpdateServerConfigResponse {
+  reserved 1;
+  reserved "profile";
+
+  // Updated public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 3;
+  // Updated runtime-editable server profile settings.
+  ServerConfig config = 2;
+}
+
+// Request to upload and set the public server logo.
+message UploadServerLogoRequest {
+  reserved 1 to 3;
+  reserved "filename", "content_type";
+
+  // Image payload. The server validates, resizes, and stores a WebP logo.
+  chatto.api.v1.ImageUpload image = 4;
+}
+
+// Result of uploading the public server logo.
+message UploadServerLogoResponse {
+  reserved 1;
+  reserved "profile";
+
+  // Updated public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 2;
+}
+
+// Request to clear the public server logo.
+message DeleteServerLogoRequest {}
+
+// Result of clearing the public server logo.
+message DeleteServerLogoResponse {
+  reserved 1;
+  reserved "profile";
+
+  // Updated public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 2;
+}
+
+// Request to upload and set the public server banner.
+message UploadServerBannerRequest {
+  reserved 1 to 3;
+  reserved "filename", "content_type";
+
+  // Image payload. The server validates, resizes, and stores a WebP banner.
+  chatto.api.v1.ImageUpload image = 4;
+}
+
+// Result of uploading the public server banner.
+message UploadServerBannerResponse {
+  reserved 1;
+  reserved "profile";
+
+  // Updated public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 2;
+}
+
+// Request to clear the public server banner.
+message DeleteServerBannerRequest {}
+
+// Result of clearing the public server banner.
+message DeleteServerBannerResponse {
+  reserved 1;
+  reserved "profile";
+
+  // Updated public server profile and branding.
+  chatto.api.v1.ServerPublicProfile public_profile = 2;
+}
+
+// Request for server security configuration.
+message GetServerSecurityConfigRequest {}
+
+// Security-sensitive server configuration. The caller must be allowed to
+// manage server configuration.
+message GetServerSecurityConfigResponse {
+  // Blocked usernames. Entries are normalized by the server.
+  repeated string blocked_usernames = 1;
+}
+
+// Request to update the server blocked-username list.
+message UpdateBlockedUsernamesRequest {
+  // Blocked usernames. Empty entries are ignored and entries are normalized by
+  // the server.
+  repeated string blocked_usernames = 1 [(buf.validate.field).repeated = {max_items: 1000}];
+}
+
+// Result of updating the server blocked-username list.
+message UpdateBlockedUsernamesResponse {
+  // Effective blocked usernames after the update.
+  repeated string blocked_usernames = 1;
+}
+
+// Provides server-administration commands for profile, branding, and security
+// settings.
+service AdminServerService {
+  // Gets runtime-editable server profile settings. This RPC requires
+  // server.manage.
+  rpc GetServerConfig(GetServerConfigRequest) returns (GetServerConfigResponse);
+  // Updates runtime-editable server profile settings. This RPC requires
+  // server.manage.
+  rpc UpdateServerConfig(UpdateServerConfigRequest) returns (UpdateServerConfigResponse);
+  // Uploads and sets the public server logo. This RPC requires server.manage.
+  rpc UploadServerLogo(UploadServerLogoRequest) returns (UploadServerLogoResponse);
+  // Clears the public server logo. This RPC requires server.manage.
+  rpc DeleteServerLogo(DeleteServerLogoRequest) returns (DeleteServerLogoResponse);
+  // Uploads and sets the public server banner. This RPC requires server.manage.
+  rpc UploadServerBanner(UploadServerBannerRequest) returns (UploadServerBannerResponse);
+  // Clears the public server banner. This RPC requires server.manage.
+  rpc DeleteServerBanner(DeleteServerBannerRequest) returns (DeleteServerBannerResponse);
+  // Returns security-sensitive server configuration. This RPC requires
+  // server.manage.
+  rpc GetServerSecurityConfig(GetServerSecurityConfigRequest) returns (GetServerSecurityConfigResponse);
+  // Updates the blocked-username list. This RPC requires server.manage.
+  rpc UpdateBlockedUsernames(UpdateBlockedUsernamesRequest) returns (UpdateBlockedUsernamesResponse);
+}

+ 150 - 0
proto/chatto/api/v1/account.proto

@@ -0,0 +1,150 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/common.proto";
+import "chatto/api/v1/external_identities.proto";
+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";
+
+// Request to update the authenticated user's profile. At least one field must
+// be present.
+message UpdateProfileRequest {
+  // New display name, when changing it. Empty clears the explicit display
+  // name. The server also rejects control and confusing invisible characters.
+  optional string display_name = 1 [(buf.validate.field).string.max_len = 32];
+  // New login identifier, when changing it. The server accepts ASCII letters,
+  // digits, period, underscore, and hyphen, starting with a letter or digit.
+  optional string login = 2 [(buf.validate.field).string = {
+    min_len: 2
+    max_len: 32
+  }];
+}
+
+// Result of a profile update.
+message UpdateProfileResponse {
+  // Updated user profile.
+  User user = 1;
+}
+
+// Request to upload and set the authenticated user's avatar.
+message UploadAvatarRequest {
+  reserved 1 to 3;
+  reserved "filename", "content_type";
+
+  // Image payload. The server validates, resizes, and stores a WebP avatar.
+  ImageUpload image = 4;
+}
+
+// Result of uploading the authenticated user's avatar.
+message UploadAvatarResponse {
+  // Updated user profile.
+  User user = 1;
+}
+
+// Request to delete the authenticated user's avatar.
+message DeleteAvatarRequest {}
+
+// Result of deleting the authenticated user's avatar.
+message DeleteAvatarResponse {
+  // Updated user profile.
+  User user = 1;
+}
+
+// Request to update or add the authenticated user's password.
+message UpdatePasswordRequest {
+  // New password. The server applies the same password policy as registration
+  // and password reset.
+  string password = 1 [(buf.validate.field).string = {
+    min_len: 8
+    max_len: 128
+  }];
+  // Current password. Required when the account already has a password; omitted
+  // when adding the first password to a passwordless account.
+  string current_password = 2 [(buf.validate.field).string.max_len = 128];
+}
+
+// Result of updating or adding the authenticated account password.
+message UpdatePasswordResponse {
+  // Current authenticated user profile after the password update.
+  User user = 1;
+}
+
+// Request to update the authenticated user's display preferences. Omitted
+// fields are left unchanged. An empty timezone clears the explicit timezone.
+message UpdateSettingsRequest {
+  // IANA timezone override. Empty clears the override.
+  optional string timezone = 1;
+  // Preferred time format.
+  optional TimeFormat time_format = 2;
+}
+
+// Result of updating display preferences.
+message UpdateSettingsResponse {
+  // Stored settings after merging the request.
+  UserSettings settings = 1;
+}
+
+// Request a short-lived confirmation token for deleting the authenticated
+// account.
+message RequestAccountDeletionRequest {}
+
+// Result of issuing an account deletion confirmation token.
+message RequestAccountDeletionResponse {
+  // Confirmation token. Clients must pass this to DeleteMyAccount.
+  string confirmation_token = 1;
+}
+
+// Request to permanently delete the authenticated account.
+message DeleteMyAccountRequest {
+  // Confirmation token obtained from RequestAccountDeletion.
+  string confirmation_token = 1;
+}
+
+// Result of deleting the authenticated account.
+message DeleteMyAccountResponse {
+  // True when the account was deleted.
+  bool deleted = 1;
+}
+
+// Self-service account, profile, avatar, display preference, presence,
+// custom-status, external identity, and account lifecycle commands for the
+// authenticated user.
+service MyAccountService {
+  // Updates the authenticated user's login and/or display name.
+  rpc UpdateProfile(UpdateProfileRequest) returns (UpdateProfileResponse);
+  // 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);
+  // Updates or adds the authenticated user's password.
+  rpc UpdatePassword(UpdatePasswordRequest) returns (UpdatePasswordResponse);
+  // Updates the authenticated user's display preferences.
+  rpc UpdateSettings(UpdateSettingsRequest) returns (UpdateSettingsResponse);
+  // Lists configured external identity providers and identities linked to the
+  // authenticated account.
+  rpc ListExternalIdentities(ListExternalIdentitiesRequest) returns (ListExternalIdentitiesResponse);
+  // Creates a short-lived browser handoff URL for linking a provider identity
+  // to the authenticated account. The provider callback completes through
+  // chatto.auth.v1.ExternalIdentityAuthService.ConfirmExternalIdentityLink.
+  rpc StartExternalIdentityLink(StartExternalIdentityLinkRequest) returns (StartExternalIdentityLinkResponse);
+  // Disconnects a provider identity from the authenticated account.
+  rpc DisconnectExternalIdentity(DisconnectExternalIdentityRequest) returns (DisconnectExternalIdentityResponse);
+  // Updates the current user's live presence status. This state is transient:
+  // clients should refresh it periodically while visible, and should stop
+  // calling this RPC when the user chooses to appear offline.
+  rpc UpdatePresence(UpdatePresenceRequest) returns (UpdatePresenceResponse);
+  // Updates or replaces the current user's custom status. Emoji and text are
+  // required, and expires_at must be omitted or in the future.
+  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);
+  // Issues a short-lived token used to confirm self-deletion.
+  rpc RequestAccountDeletion(RequestAccountDeletionRequest) returns (RequestAccountDeletionResponse);
+  // Permanently deletes the authenticated account after token validation.
+  rpc DeleteMyAccount(DeleteMyAccountRequest) returns (DeleteMyAccountResponse);
+}

+ 138 - 0
proto/chatto/api/v1/asset_uploads.proto

@@ -0,0 +1,138 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/attachments.proto";
+import "google/protobuf/timestamp.proto";
+
+// Upload lifecycle state for a room-scoped attachment upload.
+enum AssetUploadStatus {
+  // The upload status was not specified.
+  ASSET_UPLOAD_STATUS_UNSPECIFIED = 0;
+  // The upload accepts more chunks.
+  ASSET_UPLOAD_STATUS_OPEN = 1;
+  // The upload has been completed and produced an attachment asset.
+  ASSET_UPLOAD_STATUS_COMPLETED = 2;
+  // The upload was cancelled or expired.
+  ASSET_UPLOAD_STATUS_CANCELLED = 3;
+}
+
+// Current server-side state for one room-scoped attachment upload.
+message AssetUpload {
+  // Upload session ID.
+  string upload_id = 1;
+  // Room the resulting attachment asset will belong to.
+  string room_id = 2;
+  // Current upload status.
+  AssetUploadStatus status = 3;
+  // Number of bytes committed so far.
+  int64 committed_offset = 4;
+  // Total declared file size in bytes.
+  int64 size = 5;
+  // Maximum accepted chunk size in bytes.
+  int32 max_chunk_size = 6;
+  // SHA-256 digest of the complete file, lowercase hexadecimal.
+  string sha256 = 7;
+  // Upload expiry. Clients should create a new upload after this time.
+  google.protobuf.Timestamp expires_at = 8;
+  // Asset ID after completion.
+  string asset_id = 9;
+}
+
+// Request to start a room-scoped attachment upload.
+message CreateUploadRequest {
+  reserved 6, 7;
+  reserved "thread_root_event_id", "also_send_to_channel";
+
+  // Required room ID.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Original filename supplied by the client.
+  string filename = 2 [(buf.validate.field).string.min_len = 1];
+  // MIME content type. Empty values are treated as application/octet-stream.
+  string content_type = 3;
+  // Required total file size in bytes.
+  int64 size = 4 [(buf.validate.field).int64.gte = 0];
+  // Required SHA-256 digest of the complete file, lowercase hexadecimal.
+  string sha256 = 5 [(buf.validate.field).string = {
+    pattern: "^[a-f0-9]{64}$"
+  }];
+}
+
+// Response after creating an upload.
+message CreateUploadResponse {
+  // Created upload session.
+  AssetUpload upload = 1;
+}
+
+// Request to append one chunk to an upload.
+message UploadChunkRequest {
+  // Required upload session ID.
+  string upload_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Byte offset where this chunk starts.
+  int64 offset = 2 [(buf.validate.field).int64.gte = 0];
+  // Chunk bytes. The server enforces a per-RPC size limit.
+  bytes content = 3 [(buf.validate.field).bytes.min_len = 1];
+  // SHA-256 digest of this chunk, lowercase hexadecimal.
+  string chunk_sha256 = 4 [(buf.validate.field).string = {
+    pattern: "^[a-f0-9]{64}$"
+  }];
+}
+
+// Response after committing an upload chunk.
+message UploadChunkResponse {
+  // Updated upload session.
+  AssetUpload upload = 1;
+}
+
+// Request to fetch upload state.
+message GetUploadRequest {
+  // Required upload session ID.
+  string upload_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response containing upload state.
+message GetUploadResponse {
+  // Current upload session.
+  AssetUpload upload = 1;
+}
+
+// Request to complete an upload and create its attachment asset.
+message CompleteUploadRequest {
+  // Required upload session ID.
+  string upload_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response after an upload completes.
+message CompleteUploadResponse {
+  // Completed upload session.
+  AssetUpload upload = 1;
+  // Attachment asset produced by this upload.
+  Asset asset = 2;
+}
+
+// Request to cancel an upload.
+message CancelUploadRequest {
+  // Required upload session ID.
+  string upload_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response after cancelling an upload.
+message CancelUploadResponse {
+  // Updated upload session.
+  AssetUpload upload = 1;
+}
+
+// Creates room-scoped attachment assets through chunked ConnectRPC uploads.
+service AssetUploadService {
+  // Starts a room-scoped attachment upload for the authenticated user.
+  rpc CreateUpload(CreateUploadRequest) returns (CreateUploadResponse);
+  // Appends one chunk at the upload's current committed offset.
+  rpc UploadChunk(UploadChunkRequest) returns (UploadChunkResponse);
+  // Reads current upload state for retry or resume.
+  rpc GetUpload(GetUploadRequest) returns (GetUploadResponse);
+  // Verifies the completed upload and creates the attachment asset.
+  rpc CompleteUpload(CompleteUploadRequest) returns (CompleteUploadResponse);
+  // Cancels an incomplete upload and releases temporary storage.
+  rpc CancelUpload(CancelUploadRequest) returns (CancelUploadResponse);
+}

+ 91 - 0
proto/chatto/api/v1/attachments.proto

@@ -0,0 +1,91 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/common.proto";
+import "chatto/api/v1/message_types.proto";
+import "google/protobuf/timestamp.proto";
+
+// Room-scoped binary asset metadata and freshly signed URLs.
+message Asset {
+  // Stable asset ID.
+  string id = 1;
+  // Original filename.
+  string filename = 2;
+  // MIME content type.
+  string content_type = 3;
+  // Stored file size in bytes.
+  int64 size = 4;
+  // Media width when known.
+  int32 width = 5;
+  // Media height when known.
+  int32 height = 6;
+  // Signed URL for the original asset bytes.
+  MessageAssetUrl asset_url = 7;
+  // Signed URL for a transformed image thumbnail.
+  MessageAssetUrl thumbnail_asset_url = 8;
+  // Video processing state when this asset is a video attachment.
+  MessageVideoProcessing video_processing = 9;
+}
+
+// One current room attachment and its message anchor.
+message RoomAttachmentListItem {
+  // Attachment metadata and signed URLs.
+  Asset attachment = 1;
+  // Message event containing the attachment.
+  string message_event_id = 2;
+  // Thread root event ID when the containing message is a thread reply.
+  string thread_root_event_id = 3;
+  // Message creation timestamp.
+  google.protobuf.Timestamp created_at = 4;
+}
+
+// Request to read one room-scoped asset with freshly signed URLs.
+message GetAssetRequest {
+  // Required. Room that owns the asset.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Asset ID.
+  string asset_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Thumbnail URL options. Defaults are applied when absent.
+  ImageTransformOptions thumbnail = 3;
+}
+
+// Response containing one room-scoped asset.
+message GetAssetResponse {
+  // Asset metadata and signed URLs.
+  Asset asset = 1;
+}
+
+// Request to read many room-scoped assets with freshly signed URLs.
+message BatchGetAssetsRequest {
+  // Required. Room that owns the assets.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Asset IDs. Missing, deleted, and wrong-room asset IDs are omitted.
+  repeated string asset_ids = 2 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+  // Thumbnail URL options. Defaults are applied when absent.
+  ImageTransformOptions thumbnail = 3;
+}
+
+// Response containing room-scoped assets.
+message BatchGetAssetsResponse {
+  // Assets in first-seen request order.
+  repeated Asset assets = 1;
+}
+
+// Reads room-scoped asset metadata and freshly signed asset URLs.
+service AssetService {
+  // Reads one asset. Authentication and room membership are required. Returns
+  // NOT_FOUND when the asset is missing, deleted, or owned by a different room.
+  rpc GetAsset(GetAssetRequest) returns (GetAssetResponse);
+  // Reads many assets in one room. Authentication and room membership are
+  // required. Missing, deleted, and wrong-room asset IDs are omitted. Results
+  // preserve first-seen request order and repeated asset IDs are de-duplicated.
+  rpc BatchGetAssets(BatchGetAssetsRequest) returns (BatchGetAssetsResponse);
+}

+ 53 - 0
proto/chatto/api/v1/common.proto

@@ -0,0 +1,53 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+
+// Fit mode used when generating transformed image URLs.
+enum ImageFitMode {
+  // The fit mode was not specified.
+  IMAGE_FIT_MODE_UNSPECIFIED = 0;
+  // Preserve the whole source image within the requested bounds.
+  IMAGE_FIT_MODE_CONTAIN = 1;
+  // Fill the requested bounds, cropping when needed.
+  IMAGE_FIT_MODE_COVER = 2;
+}
+
+// Image transform parameters for generated image URLs.
+message ImageTransformOptions {
+  // Target image width in pixels.
+  int32 width = 1 [(buf.validate.field).int32 = {
+    gte: 1
+    lte: 4096
+  }];
+  // Target image height in pixels.
+  int32 height = 2 [(buf.validate.field).int32 = {
+    gte: 1
+    lte: 4096
+  }];
+  // Image crop/fit behavior.
+  ImageFitMode fit = 3 [(buf.validate.field).enum.defined_only = true];
+}
+
+// Browser-supplied image upload payload.
+message ImageUpload {
+  // Raw image bytes.
+  bytes image = 1;
+  // Original browser filename, for diagnostics and future compatibility.
+  string filename = 2;
+  // Browser-provided content type, for diagnostics and future compatibility.
+  string content_type = 3;
+}
+
+// Public login/provider metadata shared by discovery and account-linking APIs.
+message ProviderMetadata {
+  // Stable configured provider ID.
+  string id = 1;
+  // Provider type, such as "password", "oidc", "github", or "discord".
+  string type = 2;
+  // Human-readable provider label.
+  string label = 3;
+  // URL that starts login for this provider.
+  string login_url = 4;
+}

+ 77 - 0
proto/chatto/api/v1/external_identities.proto

@@ -0,0 +1,77 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/common.proto";
+
+// Public metadata for a configured external login provider.
+message ExternalIdentityProvider {
+  reserved 1 to 4;
+  reserved "id", "type", "label", "login_url";
+
+  // URL that starts authenticated account linking for this provider. Clients
+  // should use StartExternalIdentityLink instead of navigating here directly.
+  string link_url = 5;
+  // True when this provider is already linked to the authenticated user.
+  bool linked = 6;
+  // Linked identity subject hash for this provider, when linked.
+  string linked_identity_subject_hash = 7;
+  // Shared provider metadata.
+  ProviderMetadata provider = 8;
+}
+
+// Linked provider identity metadata for the authenticated user's settings UI.
+message LinkedExternalIdentity {
+  // Provider ID recorded when the identity was linked.
+  string provider_id = 1;
+  // Provider type recorded when the identity was linked.
+  string provider_type = 2;
+  // Current or fallback provider label.
+  string provider_label = 3;
+  // Stable one-way subject hash, useful only as a UI/list key.
+  string subject_hash = 4;
+}
+
+// Request to list configured and linked external identities for the current user.
+message ListExternalIdentitiesRequest {}
+
+// Finite snapshot of configured providers and linked external identities.
+message ListExternalIdentitiesResponse {
+  // Configured providers the user may link.
+  repeated ExternalIdentityProvider providers = 1;
+  // Provider identities already linked to the authenticated user.
+  repeated LinkedExternalIdentity linked_identities = 2;
+}
+
+// Request to start linking a configured provider to the current user.
+message StartExternalIdentityLinkRequest {
+  // Provider ID to link.
+  string provider_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Internal path to return to after confirmation.
+  string redirect_path = 2;
+  // Current password proof for accounts with a password when the active
+  // runtime credential is no longer fresh.
+  string current_password = 3;
+}
+
+// Result of preparing an external identity link flow.
+message StartExternalIdentityLinkResponse {
+  // Browser URL that starts provider authorization on the target server origin.
+  string start_url = 1;
+}
+
+// Request to disconnect a linked provider identity from the authenticated user.
+message DisconnectExternalIdentityRequest {
+  // Stable one-way subject hash of the linked identity to disconnect.
+  string subject_hash = 1 [(buf.validate.field).string.min_len = 1];
+  // Current password proof for accounts with a password when the active
+  // runtime credential is no longer fresh.
+  string current_password = 2;
+}
+
+// Result of disconnecting a provider identity.
+message DisconnectExternalIdentityResponse {
+  // True when the identity was disconnected.
+  bool disconnected = 1;
+}

+ 48 - 0
proto/chatto/api/v1/link_previews.proto

@@ -0,0 +1,48 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+
+// Request to fetch server-side link preview metadata for a URL.
+message FetchLinkPreviewRequest {
+  // URL to preview.
+  string url = 1 [
+    (buf.validate.field).string.min_len = 1,
+    (buf.validate.field).string.max_len = 2048
+  ];
+}
+
+// Link preview metadata used by message composers and room timeline events.
+//
+// Clients should treat optional metadata as unavailable when absent. Message
+// creation accepts only the preview_token returned by FetchLinkPreview, not
+// client-provided metadata fields.
+message LinkPreview {
+  // Previewed URL.
+  string url = 1;
+  // Page or embed title.
+  optional string title = 2;
+  // Page or embed description.
+  optional string description = 3;
+  // Preview image URL, when available.
+  optional string image_url = 4;
+  // Existing server asset ID for the preview image, when cached or stored with
+  // the message.
+  optional string image_asset_id = 5;
+  // Site name, when known.
+  optional string site_name = 6;
+  // Embed provider or type, when recognized.
+  optional string embed_type = 7;
+  // Provider-specific embed ID, when recognized.
+  optional string embed_id = 8;
+}
+
+// Result of fetching link preview metadata.
+message FetchLinkPreviewResponse {
+  // Preview metadata, or absent when the URL cannot be previewed.
+  LinkPreview preview = 1;
+  // Short-lived opaque token to pass to CreateMessage.link_preview_token when
+  // the user posts this preview.
+  string preview_token = 2;
+}

+ 150 - 0
proto/chatto/api/v1/member_directory.proto

@@ -0,0 +1,150 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/pagination.proto";
+import "chatto/api/v1/users.proto";
+import "google/protobuf/timestamp.proto";
+
+// Public user/member row used by user directory, room membership, and mention
+// surfaces.
+message DirectoryMember {
+  // Public user fields.
+  User user = 1;
+  // Explicit roles assigned to the user. Member listings include the virtual
+  // `everyone` role for parity with Chatto's permission model.
+  repeated string roles = 2;
+  // Account creation time when known.
+  google.protobuf.Timestamp created_at = 3;
+}
+
+// Request for users visible to the authenticated user.
+message ListUsersRequest {
+  reserved 2, 3;
+  reserved "limit", "offset";
+
+  // Optional case-insensitive search against login and display name.
+  string search = 1 [(buf.validate.field).string.max_len = 100];
+  // Page request. Defaults to 20 results when absent or limit is zero.
+  PageRequest page = 4;
+}
+
+// User directory page.
+message ListUsersResponse {
+  reserved 2, 3;
+  reserved "total_count", "has_more";
+
+  // Users in the requested page.
+  repeated DirectoryMember users = 1;
+  // Page metadata.
+  PageInfo page = 4;
+}
+
+// Request one visible user by stable user ID or login.
+message GetUserRequest {
+  oneof target {
+    // Target stable user ID.
+    string user_id = 1 [(buf.validate.field).string.min_len = 1];
+    // Target login identifier.
+    string login = 2 [(buf.validate.field).string.min_len = 1];
+  }
+}
+
+// User directory response.
+message GetUserResponse {
+  // Requested user.
+  DirectoryMember user = 1;
+}
+
+// Request user directory rows for a set of stable user IDs.
+message BatchGetUsersRequest {
+  // Required target user IDs. Unknown IDs are omitted from the response.
+  repeated string user_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Batch user directory response.
+message BatchGetUsersResponse {
+  // Found users. The server preserves first-seen request order and
+  // de-duplicates repeated IDs.
+  repeated DirectoryMember users = 1;
+}
+
+// Request for members of one room.
+message ListRoomMembersRequest {
+  reserved 3, 4;
+  reserved "limit", "offset";
+
+  // Required. Room whose explicit members should be listed.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Optional case-insensitive search against login and display name.
+  string search = 2 [(buf.validate.field).string.max_len = 100];
+  // Page request. Defaults to 20 results when absent or limit is zero.
+  PageRequest page = 5;
+}
+
+// Room member page.
+message ListRoomMembersResponse {
+  reserved 2, 3;
+  reserved "total_count", "has_more";
+
+  // Members in the requested page.
+  repeated DirectoryMember members = 1;
+  // Page metadata.
+  PageInfo page = 4;
+}
+
+// Request one member of one room by stable user ID.
+message GetRoomMemberRequest {
+  // Required room ID.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required target user ID.
+  string user_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Room member response.
+message GetRoomMemberResponse {
+  // Requested room member.
+  DirectoryMember member = 1;
+}
+
+// Request room member rows for a set of stable user IDs.
+message BatchGetRoomMembersRequest {
+  // Required room ID.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required target user IDs. Unknown IDs and users that are not members of the
+  // room are omitted from the response.
+  repeated string user_ids = 2 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Batch room member response.
+message BatchGetRoomMembersResponse {
+  // Found members. The server preserves first-seen request order and
+  // de-duplicates repeated IDs.
+  repeated DirectoryMember members = 1;
+}
+
+// Reads the authenticated server-wide user directory.
+service UserService {
+  // Lists users visible to the authenticated user. Admin-sensitive account
+  // fields stay out of this public row shape.
+  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
+  // Gets one visible user by user ID or login. Returns NOT_FOUND when the
+  // target is unknown.
+  rpc GetUser(GetUserRequest) returns (GetUserResponse);
+  // Gets visible user rows for multiple stable user IDs. Unknown IDs are
+  // omitted from the response.
+  rpc BatchGetUsers(BatchGetUsersRequest) returns (BatchGetUsersResponse);
+}

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

@@ -0,0 +1,175 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "chatto/api/v1/link_previews.proto";
+import "google/protobuf/timestamp.proto";
+
+// Time-limited URL for an asset attached to a message.
+//
+// Clients should expect these URLs to expire and refresh the asset through
+// AssetService when a URL is no longer usable.
+message MessageAssetUrl {
+  // Signed asset URL.
+  string url = 1;
+  // Time when the signed URL expires.
+  google.protobuf.Timestamp expires_at = 2;
+}
+
+// Processing state for a video attachment.
+enum MessageVideoProcessingStatus {
+  // The processing status was not specified.
+  MESSAGE_VIDEO_PROCESSING_STATUS_UNSPECIFIED = 0;
+  // Video processing is still running.
+  MESSAGE_VIDEO_PROCESSING_STATUS_PROCESSING = 1;
+  // Video processing completed successfully.
+  MESSAGE_VIDEO_PROCESSING_STATUS_COMPLETED = 2;
+  // Video processing failed.
+  MESSAGE_VIDEO_PROCESSING_STATUS_FAILED = 3;
+}
+
+// One transcoded video rendition.
+message MessageVideoVariant {
+  // Quality label for the rendition.
+  string quality = 1;
+  // Video width in pixels.
+  int32 width = 2;
+  // Video height in pixels.
+  int32 height = 3;
+  // Rendition size in bytes.
+  int64 size = 4;
+  // Signed URL for the rendition.
+  MessageAssetUrl asset_url = 5;
+}
+
+// Processing metadata for a video attachment.
+//
+// Clients can use this object to show upload/transcoding progress and decide
+// whether to play a processed variant, show a thumbnail, or display a failure
+// state.
+message MessageVideoProcessing {
+  // Current processing status.
+  MessageVideoProcessingStatus status = 1;
+  // Video duration in milliseconds.
+  int64 duration_ms = 2;
+  // Source video width in pixels.
+  int32 width = 3;
+  // Source video height in pixels.
+  int32 height = 4;
+  // True when the original source asset is currently available.
+  bool source_available = 5;
+  // Stable reason code for a failed or incomplete processing state.
+  string reason_code = 6;
+  // Signed URL for the generated thumbnail, when available.
+  MessageAssetUrl thumbnail_asset_url = 7;
+  // Available transcoded renditions.
+  repeated MessageVideoVariant variants = 8;
+}
+
+// Attachment metadata included with a message.
+//
+// Image and video dimensions are best-effort metadata for layout. Asset URLs can
+// be absent while processing is pending or when the source is unavailable.
+message MessageAttachment {
+  // Stable attachment ID.
+  string id = 1;
+  // Original filename.
+  string filename = 2;
+  // MIME content type.
+  string content_type = 3;
+  // Image or video width in pixels, when known.
+  int32 width = 4;
+  // Image or video height in pixels, when known.
+  int32 height = 5;
+  // Signed URL for the original asset, when available.
+  MessageAssetUrl asset_url = 6;
+  // Signed URL for a thumbnail image, when available.
+  MessageAssetUrl thumbnail_asset_url = 7;
+  // Video-specific processing metadata, when this attachment is a video.
+  MessageVideoProcessing video_processing = 8;
+}
+
+// Aggregated reaction state for one emoji on one message.
+//
+// This state is scoped to the current message and includes whether the current
+// user has reacted with the same emoji.
+message MessageReaction {
+  // Emoji or reaction key.
+  string emoji = 1;
+  // Number of users who reacted with this emoji.
+  int32 count = 2;
+  // True when the current user reacted with this emoji.
+  bool has_reacted = 3;
+  // Preview of up to five user IDs that reacted with this emoji.
+  repeated string preview_user_ids = 4;
+}
+
+// Viewer-specific state for one message thread.
+message ThreadViewerState {
+  // Whether the current user follows this message's thread, when known.
+  optional bool is_following = 1;
+  // True when the thread has unread replies for the current user, when known.
+  optional bool has_unread = 2;
+}
+
+// Aggregated state for one message thread.
+message ThreadSummary {
+  // Event ID of the root message for the thread.
+  string thread_root_event_id = 1;
+  // Number of replies in this message's thread.
+  int32 reply_count = 2;
+  // Creation time of the most recent reply in this message's thread.
+  google.protobuf.Timestamp last_reply_at = 3;
+  // Preview of up to five user IDs that have participated in this message's
+  // thread.
+  repeated string participant_preview_user_ids = 4;
+  // Total number of distinct users that have participated in this message's
+  // thread.
+  int32 participant_count = 5;
+  // State resolved for the current user.
+  ThreadViewerState viewer_state = 6;
+}
+
+// Renderable message data.
+//
+// The same shape is used for top-level room messages, thread replies, and
+// thread echo entries. Thread-related fields let clients render reply counts,
+// thread participants, and follow state without additional per-message
+// requests.
+message Message {
+  reserved 14 to 18;
+  reserved "reply_count", "last_reply_at", "thread_participant_preview_user_ids", "thread_participant_count", "viewer_is_following_thread";
+
+  // Stable message event ID.
+  string id = 1;
+  // Room containing the message.
+  string room_id = 2;
+  // Time when the message was created.
+  google.protobuf.Timestamp created_at = 3;
+  // User ID of the message actor.
+  string actor_id = 4;
+  // Message body text, when available. A present empty string is distinct from
+  // an absent body.
+  optional string body = 5;
+  // Attachments sent with the message.
+  repeated MessageAttachment attachments = 6;
+  // Link preview extracted for the message, when available.
+  LinkPreview link_preview = 7;
+  // Time when the message was last edited.
+  google.protobuf.Timestamp updated_at = 8;
+  // Event ID this message directly replies to, when this is a reply.
+  string in_reply_to = 9;
+  // Event ID of the root message for the thread this message belongs to.
+  string thread_root_event_id = 10;
+  // Event ID this event echoes into the current view, when applicable. Echoes
+  // allow thread activity to appear in another timeline context.
+  string echo_of_event_id = 11;
+  // Thread root ID of the echoed event, when applicable.
+  string echo_from_thread_root_event_id = 12;
+  // Channel timeline event ID for a thread echo, when applicable.
+  string channel_echo_event_id = 13;
+  // Reaction summaries for this message.
+  repeated MessageReaction reactions = 19;
+  // Aggregated thread state, when known for a thread root message.
+  ThreadSummary thread = 20;
+}

+ 225 - 0
proto/chatto/api/v1/messages.proto

@@ -0,0 +1,225 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/link_previews.proto";
+import "chatto/api/v1/message_types.proto";
+import "chatto/api/v1/reactions.proto";
+
+// Request to create a message in a room or thread.
+message CreateMessageRequest {
+  // Field 8 was `link_preview`, a client-provided metadata message used before
+  // CreateMessage accepted only server-issued link preview tokens.
+  reserved 8;
+  reserved "link_preview";
+  // Field 9 was `attachments`, inline attachment bytes accepted before
+  // AssetUploadService moved uploads into a dedicated chunked API.
+  reserved 9;
+  reserved "attachments";
+  // Field 7 was `mention_confirmation_token`, used by a server-enforced large
+  // mention confirmation flow before role mention confirmation became a
+  // client-only composer affordance.
+  reserved 7;
+  reserved "mention_confirmation_token";
+
+  // Required. Room where the message should be created.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // 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;
+  // 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.
+  string in_reply_to = 5;
+  // True to also echo a thread reply into the main room timeline.
+  bool also_send_to_channel = 6;
+  // Short-lived token returned by FetchLinkPreview for the selected URL. The
+  // server resolves the token to cached, server-fetched metadata during post.
+  string link_preview_token = 10;
+}
+
+// Result of creating a message.
+message CreateMessageResponse {
+  // Field 2 was `mention_confirmation`, used by a server-enforced large
+  // mention confirmation flow before role mention confirmation became a
+  // client-only composer affordance.
+  reserved 2;
+  reserved "mention_confirmation";
+  // Field 3 was `includes`, used before create responses were narrowed to the
+  // generated message.
+  reserved 3;
+  reserved "includes";
+
+  // Renderable message created by the request.
+  Message message = 1;
+}
+
+// Request to patch a message.
+message UpdateMessageRequest {
+  // Required. Room containing the message.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message to edit.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // New message body text. Omit to preserve the current body.
+  optional string body = 3 [(buf.validate.field).string.max_len = 10000];
+  // For thread replies, whether a channel echo should exist after saving.
+  // Omit to preserve the current echo state.
+  optional bool also_send_to_channel = 4;
+}
+
+// Result of editing a message.
+message UpdateMessageResponse {
+  reserved 1;
+  reserved "updated";
+
+  // Field 3 was `includes`, used before update responses were narrowed to the
+  // edited message.
+  reserved 3;
+  reserved "includes";
+
+  // Renderable message after the edit.
+  Message message = 2;
+}
+
+// Request to retract a message.
+message DeleteMessageRequest {
+  // Required. Room containing the message.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message to retract.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of retracting a message.
+message DeleteMessageResponse {
+  // True when the delete/retract request was accepted.
+  bool deleted = 1;
+}
+
+// Request to remove one attachment from a message.
+message DeleteAttachmentRequest {
+  // Required. Room containing the message.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message containing the attachment.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required. Attachment ID to remove from the message.
+  string attachment_id = 3 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of removing one attachment from a message.
+message DeleteAttachmentResponse {
+  // True when the attachment removal was accepted.
+  bool deleted = 1;
+}
+
+// Request to remove the accepted link preview from a message.
+message DeleteLinkPreviewRequest {
+  // Required. Room containing the message.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message containing the link preview.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required. URL of the link preview to remove.
+  string url = 3 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of removing a link preview from a message.
+message DeleteLinkPreviewResponse {
+  // True when the link preview removal was accepted.
+  bool deleted = 1;
+}
+
+// Request to read one visible message.
+message GetMessageRequest {
+  // Field 3 was `thumbnail`, used before custom attachment URL hydration moved
+  // to AssetService.
+  reserved 3;
+  reserved "thumbnail";
+
+  // Required. Room containing the message.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Message event ID.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response containing one renderable message.
+message GetMessageResponse {
+  // Field 2 was `includes`, used before message read responses were narrowed
+  // to messages.
+  reserved 2;
+  reserved "includes";
+
+  // Renderable message.
+  Message message = 1;
+}
+
+// Request to read many visible messages in one room.
+message BatchGetMessagesRequest {
+  // Field 3 was `thumbnail`, used before custom attachment URL hydration moved
+  // to AssetService.
+  reserved 3;
+  reserved "thumbnail";
+
+  // Required. Room containing the messages.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Message event IDs. Missing, retracted, non-message, and
+  // wrong-room event IDs are omitted from the response.
+  repeated string event_ids = 2 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Response containing renderable messages.
+message BatchGetMessagesResponse {
+  // Field 2 was `includes`, used before message read responses were narrowed
+  // to messages.
+  reserved 2;
+  reserved "includes";
+
+  // Renderable messages in first-seen request order.
+  repeated Message messages = 1;
+}
+
+// Creates messages in room and thread timelines.
+service MessageService {
+  // Fetches and caches metadata for a composer URL. Authentication is required
+  // to avoid exposing the preview fetcher as an unauthenticated network proxy.
+  // Successful responses include a short-lived token accepted by CreateMessage.
+  rpc FetchLinkPreview(FetchLinkPreviewRequest) returns (FetchLinkPreviewResponse);
+  // Creates a message for the current user. The user must be a room member and
+  // must have message.post for room messages or message.post-in-thread for
+  // thread replies. Echoing a thread reply also requires message.echo and
+  // message.post.
+  rpc CreateMessage(CreateMessageRequest) returns (CreateMessageResponse);
+  // Edits a message body. Authors can edit their own messages within the edit
+  // window. Non-authors need message.manage and cannot change channel echo
+  // state.
+  rpc UpdateMessage(UpdateMessageRequest) returns (UpdateMessageResponse);
+  // Retracts a message. Authors can delete their own messages. Non-authors need
+  // message.manage.
+  rpc DeleteMessage(DeleteMessageRequest) returns (DeleteMessageResponse);
+  // Removes one attachment from the author's own message.
+  rpc DeleteAttachment(DeleteAttachmentRequest) returns (DeleteAttachmentResponse);
+  // Removes the accepted link preview from the author's own message.
+  rpc DeleteLinkPreview(DeleteLinkPreviewRequest) returns (DeleteLinkPreviewResponse);
+  // Reads one renderable message, including current body, attachment metadata,
+  // link preview, reactions, and thread metadata. Authentication and room
+  // membership are required. Returns NOT_FOUND when the event does not exist,
+  // is not a message, has been retracted, or belongs to a different room.
+  rpc GetMessage(GetMessageRequest) returns (GetMessageResponse);
+  // Reads many renderable messages in one room. Authentication and room
+  // membership are required. Missing, retracted, non-message, and wrong-room
+  // event IDs are omitted. Results preserve first-seen request order and
+  // repeated event IDs are de-duplicated.
+  rpc BatchGetMessages(BatchGetMessagesRequest) returns (BatchGetMessagesResponse);
+  // Adds a reaction to a message. The user must be a room member and have
+  // message.react in the target room.
+  rpc AddReaction(AddReactionRequest) returns (AddReactionResponse);
+  // Removes a reaction from a message. The user must be a room member and have
+  // message.react in the target room.
+  rpc RemoveReaction(RemoveReactionRequest) returns (RemoveReactionResponse);
+}

+ 223 - 0
proto/chatto/api/v1/notifications.proto

@@ -0,0 +1,223 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+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/timestamp.proto";
+
+// Direct-message notification payload.
+message DirectMessageNotification {
+  reserved 1;
+  reserved "room_id";
+
+  // Message event ID.
+  string event_id = 2;
+  // DM room where the message was posted.
+  RoomSummary room = 3;
+}
+
+// Mention notification payload.
+message MentionNotification {
+  // Room where the mention occurred.
+  RoomSummary room = 1;
+  // Message event ID.
+  string event_id = 2;
+  // Thread root event ID when the mention happened inside a thread.
+  optional string thread_root_event_id = 3;
+}
+
+// Reply notification payload.
+message ReplyNotification {
+  // Room where the reply occurred.
+  RoomSummary room = 1;
+  // Reply event ID.
+  string event_id = 2;
+  // Event ID of the message being replied to.
+  string in_reply_to_id = 3;
+  // Thread root event ID when the reply happened inside a thread.
+  optional string thread_root_event_id = 4;
+}
+
+// All-messages room notification payload.
+message RoomMessageNotification {
+  // Room where the message was posted.
+  RoomSummary room = 1;
+  // Message event ID.
+  string event_id = 2;
+}
+
+// One pending notification for the authenticated viewer.
+message NotificationItem {
+  reserved 4;
+  reserved "summary";
+
+  // Stable notification ID.
+  string id = 1;
+  // Creation time.
+  google.protobuf.Timestamp created_at = 2;
+  // User who triggered the notification, when still resolvable.
+  User actor = 3;
+
+  oneof kind {
+    // Direct-message notification.
+    DirectMessageNotification direct_message = 10;
+    // Mention notification.
+    MentionNotification mention = 11;
+    // Reply notification.
+    ReplyNotification reply = 12;
+    // All-messages room notification.
+    RoomMessageNotification room_message = 13;
+  }
+}
+
+// Request for the authenticated viewer's pending notifications.
+message ListNotificationsRequest {
+  reserved 1, 2;
+  reserved "limit", "offset";
+
+  // Page request. Defaults to 50 results when absent or limit is zero.
+  PageRequest page = 3;
+}
+
+// Request for pending notifications scoped to one room.
+message ListRoomNotificationsRequest {
+  reserved 2, 3;
+  reserved "limit", "offset";
+
+  // Required. Room whose notifications should be listed.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Page request. Defaults to 50 results when absent or limit is zero.
+  PageRequest page = 4;
+}
+
+// Pending notification page.
+message ListNotificationsResponse {
+  reserved 2, 3, 4;
+  reserved "total_count", "has_more", "server_name";
+
+  // Page notifications, newest first.
+  repeated NotificationItem notifications = 1;
+  // Page metadata.
+  PageInfo page = 5;
+}
+
+// Request one pending notification for the authenticated viewer.
+message GetNotificationRequest {
+  // Required notification ID.
+  string notification_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Pending notification response.
+message GetNotificationResponse {
+  reserved 2;
+  reserved "server_name";
+
+  // Requested notification.
+  NotificationItem notification = 1;
+}
+
+// Request pending notifications for a set of stable notification IDs.
+message BatchGetNotificationsRequest {
+  // Required notification IDs. Unknown or dismissed IDs are omitted from the
+  // response.
+  repeated string notification_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Batch pending notification response.
+message BatchGetNotificationsResponse {
+  reserved 2;
+  reserved "server_name";
+
+  // Found notifications. The server preserves first-seen request order and
+  // de-duplicates repeated IDs.
+  repeated NotificationItem notifications = 1;
+}
+
+// Pending notification page scoped to one room.
+message ListRoomNotificationsResponse {
+  reserved 2, 3, 4;
+  reserved "total_count", "has_more", "server_name";
+
+  // Page notifications, newest first.
+  repeated NotificationItem notifications = 1;
+  // Page metadata.
+  PageInfo page = 5;
+}
+
+// Request for a lightweight pending notification check.
+message HasNotificationsRequest {}
+
+// Lightweight pending notification check.
+message HasNotificationsResponse {
+  // True when the authenticated viewer has at least one pending notification.
+  bool has_notifications = 1;
+}
+
+// Pending notification count for one room.
+message RoomNotificationCount {
+  // Room ID.
+  string room_id = 1;
+  // Pending notification count for the authenticated viewer in this room.
+  int32 total_count = 2;
+}
+
+// Request for pending notification counts grouped by room.
+message ListRoomNotificationCountsRequest {}
+
+// Finite snapshot of pending notification counts grouped by room.
+message ListRoomNotificationCountsResponse {
+  // Counts for rooms with at least one pending notification.
+  repeated RoomNotificationCount room_counts = 1;
+}
+
+// Request to dismiss one pending notification.
+message DismissNotificationRequest {
+  // Required. Notification to dismiss.
+  string notification_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of dismissing one pending notification.
+message DismissNotificationResponse {
+  // True when the notification is no longer pending.
+  bool dismissed = 1;
+}
+
+// Request to dismiss all pending notifications.
+message DismissAllNotificationsRequest {}
+
+// Result of dismissing all pending notifications.
+message DismissAllNotificationsResponse {
+  // Number of notifications dismissed.
+  int32 dismissed_count = 1;
+}
+
+// Reads and dismisses pending notifications for the authenticated viewer.
+service NotificationService {
+  // Lists the authenticated viewer's pending notifications.
+  rpc ListNotifications(ListNotificationsRequest) returns (ListNotificationsResponse);
+  // Gets one pending notification. Returns NOT_FOUND when the notification is
+  // unknown or has been dismissed.
+  rpc GetNotification(GetNotificationRequest) returns (GetNotificationResponse);
+  // Gets pending notifications by ID.
+  rpc BatchGetNotifications(BatchGetNotificationsRequest) returns (BatchGetNotificationsResponse);
+  // Lists pending notifications for one room. Non-members receive an empty page.
+  rpc ListRoomNotifications(ListRoomNotificationsRequest) returns (ListRoomNotificationsResponse);
+  // Lists pending notification counts grouped by room as a finite snapshot.
+  rpc ListRoomNotificationCounts(ListRoomNotificationCountsRequest) returns (ListRoomNotificationCountsResponse);
+  // Checks whether the authenticated viewer has any pending notifications.
+  rpc HasNotifications(HasNotificationsRequest) returns (HasNotificationsResponse);
+  // Dismisses one pending notification. Already-dismissed notifications are
+  // treated as idempotent success.
+  rpc DismissNotification(DismissNotificationRequest) returns (DismissNotificationResponse);
+  // Dismisses all pending notifications.
+  rpc DismissAllNotifications(DismissAllNotificationsRequest) returns (DismissAllNotificationsResponse);
+}

+ 26 - 0
proto/chatto/api/v1/pagination.proto

@@ -0,0 +1,26 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+
+// Offset-based page request for list RPCs whose result order is stable enough
+// for simple list browsing.
+message PageRequest {
+  // Maximum number of items to request. Each RPC defines its default and
+  // effective maximum; this shared request shape accepts values up to 500.
+  int32 limit = 1 [(buf.validate.field).int32 = {
+    gte: 0,
+    lte: 500
+  }];
+  // Zero-based number of matching items to skip.
+  int32 offset = 2 [(buf.validate.field).int32.gte = 0];
+}
+
+// Offset-based page metadata returned by list RPCs.
+message PageInfo {
+  // Total matching item count before pagination.
+  int64 total_count = 1;
+  // True when another page exists after this response.
+  bool has_more = 2;
+}

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

@@ -0,0 +1,64 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+
+// Browser Web Push subscription commands for the current user.
+service PushNotificationService {
+  // Stores or updates the caller's browser push subscription.
+  //
+  // The server must have Web Push configured. Clients normally call this after
+  // the browser grants notification permission and returns a PushSubscription.
+  rpc Subscribe(SubscribePushRequest) returns (SubscribePushResponse);
+
+  // Removes the caller's browser push subscription by endpoint.
+  //
+  // The call is idempotent: removing an unknown endpoint still succeeds.
+  rpc Unsubscribe(UnsubscribePushRequest) returns (UnsubscribePushResponse);
+}
+
+// Request to store a PushSubscription returned by the browser Push API.
+message SubscribePushRequest {
+  // Push service endpoint URL.
+  string endpoint = 1 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 4096
+  }];
+
+  // Client P-256 ECDH public key from PushSubscription.keys.p256dh.
+  string p256dh = 2 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 256
+  }];
+
+  // Authentication secret from PushSubscription.keys.auth.
+  string auth = 3 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 128
+  }];
+
+  // Optional browser user-agent string for device identification.
+  optional string user_agent = 4 [(buf.validate.field).string.max_len = 512];
+}
+
+// Response from storing a browser push subscription.
+message SubscribePushResponse {
+  // True when the subscription was stored.
+  bool subscribed = 1;
+}
+
+// Request to remove a browser push subscription.
+message UnsubscribePushRequest {
+  // Push service endpoint URL to remove.
+  string endpoint = 1 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 4096
+  }];
+}
+
+// Response from removing a browser push subscription.
+message UnsubscribePushResponse {
+  // True when the request completed.
+  bool unsubscribed = 1;
+}

+ 53 - 0
proto/chatto/api/v1/reactions.proto

@@ -0,0 +1,53 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/message_types.proto";
+
+// Request to add the current user's reaction to a message.
+//
+// When `message_event_id` names a channel echo of a thread reply, the server
+// treats it as an alias for the original thread reply and stores the reaction
+// on that original event.
+message AddReactionRequest {
+  // Required. Room containing the message event.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message being reacted to, or a channel echo of
+  // the message.
+  string message_event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required. Emoji shortcode name, such as "thumbsup" or "heart".
+  string emoji = 3 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of adding a reaction.
+message AddReactionResponse {
+  // True when the reaction was newly added, false when it already existed.
+  bool added = 1;
+  // Updated aggregate reaction state for this emoji.
+  MessageReaction reaction = 2;
+}
+
+// Request to remove the current user's reaction from a message.
+//
+// When `message_event_id` names a channel echo of a thread reply, the server
+// treats it as an alias for the original thread reply and removes the reaction
+// from that original event.
+message RemoveReactionRequest {
+  // Required. Room containing the message event.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the message whose reaction should be removed, or a
+  // channel echo of the message.
+  string message_event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required. Emoji shortcode name, such as "thumbsup" or "heart".
+  string emoji = 3 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of removing a reaction.
+message RemoveReactionResponse {
+  // True when the reaction was removed, false when it did not exist.
+  bool removed = 1;
+  // Updated aggregate reaction state for this emoji. Empty when no reactions for
+  // the emoji remain.
+  MessageReaction reaction = 2;
+}

+ 44 - 0
proto/chatto/api/v1/read_state.proto

@@ -0,0 +1,44 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "google/protobuf/timestamp.proto";
+
+// Request to mark a room timeline as read.
+message MarkRoomAsReadRequest {
+  // Required. Room whose timeline should be marked read.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Highest room event ID the current user has read. When set, the event must
+  // exist as a root event in the room timeline. Leave empty to mark through the
+  // room's current latest root event.
+  string up_to_event_id = 2;
+}
+
+// Result of marking a room timeline as read.
+//
+// Clients can use the previous timestamp to decide whether unread badges or
+// local notification state need to be reconciled.
+message MarkRoomAsReadResponse {
+  // New room read timestamp stored for the current user.
+  google.protobuf.Timestamp last_read_at = 1;
+  // Previous room read timestamp, when one existed.
+  google.protobuf.Timestamp previous_last_read_at = 2;
+}
+
+// Request to mark a message thread as read.
+message MarkThreadAsReadRequest {
+  // Required. Room containing the thread.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the root message for the thread.
+  string thread_root_event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Highest thread event ID the current user has read. The event should belong
+  // to the thread identified by thread_root_event_id.
+  string up_to_event_id = 3;
+}
+
+// Result of marking a message thread as read.
+message MarkThreadAsReadResponse {
+  // Previous thread read timestamp, when one existed.
+  google.protobuf.Timestamp previous_read_at = 1;
+}

+ 74 - 0
proto/chatto/api/v1/roles.proto

@@ -0,0 +1,74 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+
+// Public role metadata used for rendering role mentions, labels, and catalogs.
+message Role {
+  // Stable role name used in permission and assignment records.
+  string name = 1;
+  // Display name shown in user-facing role UIs.
+  string display_name = 2;
+  // Optional role description.
+  string description = 3;
+  // Whether this is a built-in role.
+  bool is_system = 4;
+  // Display/order position.
+  int32 position = 5;
+  // Whether messages may notify users assigned to this role.
+  bool pingable = 6;
+}
+
+// Request the current role catalog.
+message ListRolesRequest {}
+
+// Snapshot of the role catalog visible to the authenticated viewer.
+message ListRolesResponse {
+  // Roles sorted by position.
+  repeated Role roles = 1;
+}
+
+// Request one public role by stable role name.
+message GetRoleRequest {
+  // Required stable role name.
+  string name = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Public role lookup response.
+message GetRoleResponse {
+  // Requested role.
+  Role role = 1;
+}
+
+// Request public role records for a set of stable role names.
+message BatchGetRolesRequest {
+  // Required role names. Unknown names are omitted from the response.
+  repeated string names = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Batch public role response.
+message BatchGetRolesResponse {
+  // Found roles. The server preserves first-seen request order and
+  // de-duplicates repeated names.
+  repeated Role roles = 1;
+}
+
+// Provides authenticated reads for public role metadata.
+service RoleService {
+  // Lists the current role catalog as a finite snapshot. Requires an
+  // authenticated user.
+  rpc ListRoles(ListRolesRequest) returns (ListRolesResponse);
+  // Gets one public role by stable name. Returns NOT_FOUND when the role is
+  // unknown.
+  rpc GetRole(GetRoleRequest) returns (GetRoleResponse);
+  // Gets public role records for multiple role names referenced by member rows,
+  // mentions, or integration clients.
+  rpc BatchGetRoles(BatchGetRolesRequest) returns (BatchGetRolesResponse);
+}

+ 210 - 0
proto/chatto/api/v1/room_directory.proto

@@ -0,0 +1,210 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/permissions.proto";
+import "chatto/api/v1/rooms.proto";
+
+// Viewer-specific state and permission decisions for one room.
+message RoomViewerState {
+  // True when the current user is an effective room member.
+  bool is_member = 1;
+  // True when the room has unread root messages for the current user.
+  bool has_unread = 2;
+  // Effective room-scoped permission decisions after room state constraints.
+  repeated PermissionGrant permissions = 3;
+}
+
+// Room kinds to include in directory responses.
+enum RoomDirectoryScope {
+  // Include both visible channel rooms and the caller's active DM rooms.
+  ROOM_DIRECTORY_SCOPE_UNSPECIFIED = 0;
+  // Include both visible channel rooms and the caller's active DM rooms.
+  ROOM_DIRECTORY_SCOPE_ALL = 1;
+  // Include visible channel rooms only.
+  ROOM_DIRECTORY_SCOPE_CHANNELS = 2;
+  // Include the caller's active DM rooms only.
+  ROOM_DIRECTORY_SCOPE_DMS = 3;
+}
+
+// Room metadata plus state and capabilities resolved for the authenticated
+// viewer.
+message RoomWithViewerState {
+  reserved 2 to 13;
+  reserved "is_member", "has_unread", "can_list_room", "can_join_room", "can_post_message", "can_post_in_thread", "can_attach", "can_react", "can_echo_message", "can_manage_others_message", "can_manage_room", "can_ban_room_members";
+
+  // Public room metadata.
+  Room room = 1;
+  // State and permission decisions resolved for the current user.
+  RoomViewerState viewer_state = 14;
+}
+
+// Sidebar link metadata for room group navigation.
+message SidebarLink {
+  // Stable sidebar link ID.
+  string id = 1;
+  // Display label.
+  string label = 2;
+  // Absolute http(s) URL or server-local path.
+  string url = 3;
+}
+
+// One ordered item in a room group.
+message RoomGroupItem {
+  oneof item {
+    // Visible room entry.
+    RoomWithViewerState room = 1;
+    // Sidebar link entry.
+    SidebarLink sidebar_link = 2;
+  }
+}
+
+// Viewer-specific state for one room group.
+message RoomGroupViewerState {
+  reserved 1;
+  reserved "can_create_room";
+
+  // Effective group-scoped permission decisions after group state constraints.
+  repeated PermissionGrant permissions = 2;
+}
+
+// Ordered group of channel rooms and sidebar links.
+message RoomGroup {
+  reserved 4;
+  reserved "rooms";
+
+  // Stable room group ID.
+  string id = 1;
+  // Display name.
+  string name = 2;
+  // Public group description, when set.
+  string description = 3;
+  // Mixed room/sidebar-link entries in sidebar order.
+  repeated RoomGroupItem items = 5;
+  // State and permissions resolved for the current user.
+  RoomGroupViewerState viewer_state = 6;
+}
+
+// Request for rooms visible to the current user.
+message ListRoomsRequest {
+  // Which room kinds to include. Defaults to all.
+  RoomDirectoryScope scope = 1 [(buf.validate.field).enum.defined_only = true];
+}
+
+// Finite snapshot of rooms visible to the current user.
+message ListRoomsResponse {
+  // Rooms matching the requested scope.
+  repeated RoomWithViewerState rooms = 1;
+}
+
+// Request for ordered room groups visible to the current user.
+message ListRoomGroupsRequest {
+  reserved 1;
+  reserved "include_archived_rooms";
+}
+
+// Finite snapshot of ordered room groups visible to the current user.
+message ListRoomGroupsResponse {
+  // Channel room groups in sidebar order.
+  repeated RoomGroup groups = 1;
+}
+
+// Request for one ordered room group visible to the current user.
+message GetRoomGroupRequest {
+  reserved 2;
+  reserved "include_archived_rooms";
+
+  // Required. Room group to resolve.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// One ordered room group visible to the current user.
+message GetRoomGroupResponse {
+  // Resolved room group. Hidden and archived room entries are omitted.
+  RoomGroup group = 1;
+}
+
+// Request visible room groups by stable group ID.
+message BatchGetRoomGroupsRequest {
+  reserved 2;
+  reserved "include_archived_rooms";
+
+  // Required room group IDs. Unknown groups are omitted from the response.
+  repeated string group_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Visible room group batch response for direct group hydration.
+message BatchGetRoomGroupsResponse {
+  // Resolved room groups in first-seen request order.
+  repeated RoomGroup groups = 1;
+}
+
+// Request for one room visible to the current user.
+message GetRoomRequest {
+  // Required. Room to resolve.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Room visible to the current user.
+message GetRoomResponse {
+  // Resolved room and viewer state.
+  RoomWithViewerState room = 1;
+}
+
+// Request visible rooms by stable room ID.
+message BatchGetRoomsRequest {
+  // Required room IDs. Unknown rooms and rooms hidden from the caller are
+  // omitted from the response. Archived rooms may be returned when the caller
+  // can still refresh them directly.
+  repeated string room_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Visible room batch response for direct room hydration.
+message BatchGetRoomsResponse {
+  // Resolved rooms in first-seen request order.
+  repeated RoomWithViewerState rooms = 1;
+}
+
+// Reads room directory data.
+service RoomDirectoryService {
+  // Lists rooms visible to the current user. Channel rooms are non-archived
+  // rooms visible through membership or room.list. DM rooms are membership-only
+  // and empty DM conversations are omitted. Results are returned as a finite
+  // navigation snapshot.
+  rpc ListRooms(ListRoomsRequest) returns (ListRoomsResponse);
+  // Lists ordered channel room groups and sidebar items visible to the current
+  // user as a finite navigation snapshot. Hidden room entries are omitted.
+  // Archived room entries are omitted.
+  rpc ListRoomGroups(ListRoomGroupsRequest) returns (ListRoomGroupsResponse);
+  // Returns one ordered room group by ID. Hidden room entries are omitted.
+  // Archived room entries are omitted.
+  // Returns NOT_FOUND when the room group does not exist.
+  rpc GetRoomGroup(GetRoomGroupRequest) returns (GetRoomGroupResponse);
+  // Returns ordered room groups for a set of stable group IDs. Hidden room
+  // entries are omitted. Archived room entries are omitted. Unknown groups are
+  // omitted. Results preserve first-seen request order and repeated IDs are
+  // de-duplicated.
+  rpc BatchGetRoomGroups(BatchGetRoomGroupsRequest) returns (BatchGetRoomGroupsResponse);
+  // Returns one visible room by ID. DM rooms require membership. Returns
+  // NOT_FOUND when the room does not exist and PERMISSION_DENIED when the room
+  // exists but is hidden from the caller.
+  rpc GetRoom(GetRoomRequest) returns (GetRoomResponse);
+  // Returns visible rooms for a set of stable room IDs. This is a direct
+  // hydration API, so visible archived rooms may be returned. Unknown rooms and
+  // rooms hidden from the caller are omitted. Results preserve first-seen
+  // request order and repeated IDs are de-duplicated.
+  rpc BatchGetRooms(BatchGetRoomsRequest) returns (BatchGetRoomsResponse);
+}

+ 183 - 0
proto/chatto/api/v1/room_timeline.proto

@@ -0,0 +1,183 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/message_types.proto";
+import "chatto/api/v1/users.proto";
+import "google/protobuf/timestamp.proto";
+
+// Related entities included beside timeline/feed events.
+//
+// Includes are reserved for hot paginated feed paths where many events may
+// repeatedly reference the same render data. Other APIs should return resources
+// directly and rely on BatchGet-style follow-up hydration instead of adding
+// includes maps.
+message RoomTimelineIncludes {
+  // Users keyed by user ID.
+  map<string, User> users = 1;
+}
+
+// Payload for room lifecycle and membership timeline events.
+message RoomTimelineRoomEvent {
+  // Room affected by the event.
+  string room_id = 1;
+}
+
+// Payload for a message-posted timeline event.
+message RoomMessagePosted {
+  // Renderable message created by this timeline event.
+  Message message = 1;
+}
+
+// One event in a room or thread timeline.
+//
+// Clients should inspect the event oneof to choose the renderer for the event.
+message RoomTimelineEvent {
+  // Stable event ID.
+  string id = 1;
+  // Time when the event was created.
+  google.protobuf.Timestamp created_at = 2;
+  // User ID of the event actor.
+  string actor_id = 3;
+
+  // Concrete event payload.
+  oneof event {
+    // A message was posted.
+    RoomMessagePosted message_posted = 10;
+    // The room was created.
+    RoomTimelineRoomEvent room_created = 20;
+    // The room metadata was updated.
+    RoomTimelineRoomEvent room_updated = 21;
+    // The room was deleted.
+    RoomTimelineRoomEvent room_deleted = 22;
+    // The room was archived.
+    RoomTimelineRoomEvent room_archived = 23;
+    // The room was unarchived.
+    RoomTimelineRoomEvent room_unarchived = 24;
+    // A user joined the room.
+    RoomTimelineRoomEvent user_joined_room = 30;
+    // A user left the room.
+    RoomTimelineRoomEvent user_left_room = 31;
+  }
+}
+
+// Cursor page of room or thread timeline events.
+//
+// Use opaque start_cursor and end_cursor values with before/after requests to
+// continue paging in either direction. Clients must treat cursor values as
+// server-owned tokens and must not parse or construct them. The has_older and
+// has_newer flags tell clients whether another request can extend the current
+// window.
+message RoomTimelinePage {
+  // Events in display order.
+  repeated RoomTimelineEvent events = 1;
+  // Opaque cursor for the first event in the page.
+  string start_cursor = 2;
+  // Opaque cursor for the last event in the page.
+  string end_cursor = 3;
+  // True when older events are available before start_cursor.
+  bool has_older = 4;
+  // True when newer events are available after end_cursor.
+  bool has_newer = 5;
+  // Hot-path related entities needed to render the page without per-event hydration.
+  RoomTimelineIncludes includes = 6;
+}
+
+// Request for a page of room timeline events.
+//
+// Omit the cursor to load the initial page. Set before to a previously returned
+// start_cursor to page toward older events, or after to a previously returned
+// end_cursor to page toward newer events.
+message GetRoomEventsRequest {
+  // Required. Room whose timeline should be loaded.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Maximum number of events to return. The server may clamp very large limits.
+  int32 limit = 2;
+  // Cursor direction for paging.
+  oneof cursor {
+    // Return events older than this opaque cursor.
+    string before = 3;
+    // Return events newer than this opaque cursor.
+    string after = 4;
+  }
+}
+
+// Response containing one room timeline page.
+message GetRoomEventsResponse {
+  // Loaded timeline page.
+  RoomTimelinePage page = 1;
+}
+
+// Request for room timeline events around a specific event.
+//
+// Use this when a client needs to jump to a known event and render enough
+// context around it.
+message GetRoomEventsAroundRequest {
+  // Required. Room whose timeline should be loaded.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Anchor event ID that should appear in the returned page.
+  string event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Maximum number of events to return around the anchor.
+  int32 limit = 3;
+}
+
+// Response containing a room timeline window around a specific event.
+message GetRoomEventsAroundResponse {
+  // Loaded timeline page.
+  RoomTimelinePage page = 1;
+  // Zero-based index of the anchor event within page.events.
+  int32 target_index = 2;
+}
+
+// Request for a page of events in one thread.
+//
+// Omit the cursor to load the latest visible part of the thread, including the
+// root message. Set before to a previously returned start_cursor to page toward
+// older replies, or after to a previously returned end_cursor to page toward
+// newer replies without repeating the root message.
+message GetThreadEventsRequest {
+  // Required. Room containing the thread.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the root message for the thread.
+  string thread_root_event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Maximum number of events to return. The server may clamp very large limits.
+  int32 limit = 3;
+  // Cursor direction for paging.
+  oneof cursor {
+    // Return thread events older than this opaque cursor.
+    string before = 4;
+    // Return thread events newer than this opaque cursor.
+    string after = 5;
+  }
+}
+
+// Response containing one thread timeline page.
+message GetThreadEventsResponse {
+  // Loaded timeline page.
+  RoomTimelinePage page = 1;
+}
+
+// Request for thread events around a specific event.
+//
+// Use this when a client needs to jump to a known reply and render surrounding
+// thread context.
+message GetThreadEventsAroundRequest {
+  // Required. Room containing the thread.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the root message for the thread.
+  string thread_root_event_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required. Anchor event ID inside the thread. The event should belong to the
+  // requested thread.
+  string event_id = 3 [(buf.validate.field).string.min_len = 1];
+  // Maximum number of events to return around the anchor.
+  int32 limit = 4;
+}
+
+// Response containing a thread timeline window around a specific event.
+message GetThreadEventsAroundResponse {
+  // Loaded timeline page.
+  RoomTimelinePage page = 1;
+  // Zero-based index of the anchor event within page.events.
+  int32 target_index = 2;
+}

+ 399 - 0
proto/chatto/api/v1/rooms.proto

@@ -0,0 +1,399 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/attachments.proto";
+import "chatto/api/v1/common.proto";
+import "chatto/api/v1/member_directory.proto";
+import "chatto/api/v1/pagination.proto";
+import "chatto/api/v1/read_state.proto";
+import "chatto/api/v1/room_timeline.proto";
+import "google/protobuf/timestamp.proto";
+
+// Kind of room represented by the public API.
+enum RoomKind {
+  // The room kind was not specified.
+  ROOM_KIND_UNSPECIFIED = 0;
+  // A regular channel governed by server and room permissions.
+  ROOM_KIND_CHANNEL = 1;
+  // A direct-message conversation between members.
+  ROOM_KIND_DM = 2;
+}
+
+// Public room metadata returned by room commands.
+message Room {
+  // Stable room ID.
+  string id = 1;
+  // Room kind.
+  RoomKind kind = 2;
+  // Room name. Direct-message rooms may have an empty name because clients
+  // derive their display label from participants.
+  string name = 3;
+  // Optional room description.
+  string description = 4;
+  // True when the room is archived and hidden from active room lists.
+  bool archived = 5;
+  // Room group ID for channel rooms. Empty for direct-message rooms.
+  string group_id = 6;
+  // True when a channel grants effective membership to eligible server members.
+  bool universal = 7;
+}
+
+// Lightweight room reference for cross-resource rows.
+message RoomSummary {
+  // Stable room ID.
+  string id = 1;
+  // Room kind.
+  RoomKind kind = 2;
+  // Room name. Direct-message rooms may have an empty name because clients
+  // derive their display label from participants.
+  string name = 3;
+}
+
+// Request to create a channel room.
+message CreateRoomRequest {
+  // Required. Name of the new channel room.
+  string name = 1 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 30
+  }];
+  // Optional room description.
+  string description = 2 [(buf.validate.field).string.max_len = 500];
+  // Required. Room group that should contain the new channel.
+  string group_id = 3 [(buf.validate.field).string.min_len = 1];
+  // Whether the new channel should grant effective membership to eligible
+  // server members.
+  bool universal = 4;
+}
+
+// Result of creating a room.
+message CreateRoomResponse {
+  // Created room.
+  Room room = 1;
+}
+
+// Request to update a room's editable metadata.
+message UpdateRoomRequest {
+  // Required. Room to update.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // New room name, when changing it.
+  optional string name = 2 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 30
+  }];
+  // New room description, when changing it. Empty clears the description.
+  optional string description = 3 [(buf.validate.field).string.max_len = 500];
+  // New universal membership state, when changing it. Direct-message rooms
+  // cannot be universal.
+  optional bool universal = 4;
+}
+
+// Result of updating a room.
+message UpdateRoomResponse {
+  // Updated room.
+  Room room = 1;
+}
+
+// Request to archive a room.
+message ArchiveRoomRequest {
+  // Required. Room to archive.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of archiving a room.
+message ArchiveRoomResponse {
+  // Archived room.
+  Room room = 1;
+}
+
+// Request to unarchive a room.
+message UnarchiveRoomRequest {
+  // Required. Room to unarchive.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of unarchiving a room.
+message UnarchiveRoomResponse {
+  // Unarchived room.
+  Room room = 1;
+}
+
+// Request to join a room as the current user.
+message JoinRoomRequest {
+  // Required. Room to join.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of joining a room.
+message JoinRoomResponse {
+  // Joined room.
+  Room room = 1;
+}
+
+// Request to join all joinable rooms in one room group.
+message JoinRoomGroupRequest {
+  // Required. Room group whose rooms should be joined.
+  string group_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of joining all joinable rooms in one room group.
+message JoinRoomGroupResponse {
+  // Room IDs that transitioned from not joined to joined.
+  repeated string joined_room_ids = 1;
+}
+
+// Request to start or fetch a direct-message room.
+message StartDMRequest {
+  // Other participants to include in the direct-message room. The current user
+  // is always included by the server. An empty list creates or fetches the
+  // caller's self-DM.
+  repeated string participant_ids = 1 [(buf.validate.field).repeated = {
+    max_items: 9
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Result of starting or fetching a direct-message room.
+message StartDMResponse {
+  // Direct-message room for the participant set.
+  Room room = 1;
+}
+
+// Request to leave a room as the current user.
+message LeaveRoomRequest {
+  // Required. Room to leave.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of leaving a room.
+message LeaveRoomResponse {
+  // True when the current user is no longer an explicit member after the call.
+  bool left = 1;
+}
+
+// Request to add a user to a channel room.
+message AddMemberRequest {
+  // Required. Channel room to add the user to.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. User to add as an explicit room member.
+  string user_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of adding a user to a room.
+message AddMemberResponse {
+  // Added room member.
+  DirectoryMember member = 1;
+}
+
+// Request to remove a user from a channel room.
+message RemoveMemberRequest {
+  // Required. Channel room to remove the user from.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. User to remove from the room's explicit members.
+  string user_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of removing a user from a room.
+message RemoveMemberResponse {
+  // True when an explicit room membership was removed by this call.
+  bool removed = 1;
+}
+
+// Request to ban a member from a channel room.
+message BanMemberRequest {
+  // Required. Channel room to ban the user from.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. User to ban from the room.
+  string user_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required moderator-entered reason stored for audit.
+  string reason = 3 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 1000
+  }];
+  // Optional future time when the ban expires.
+  google.protobuf.Timestamp expires_at = 4;
+}
+
+// Result of banning a room member.
+message BanMemberResponse {
+  // True when the ban operation completed.
+  bool banned = 1;
+}
+
+// Request to remove a channel room ban.
+message UnbanMemberRequest {
+  // Required. Channel room to unban the user from.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. User to unban.
+  string user_id = 2 [(buf.validate.field).string.min_len = 1];
+  // Required moderator-entered reason stored for audit.
+  string reason = 3 [(buf.validate.field).string = {
+    min_len: 1
+    max_len: 1000
+  }];
+}
+
+// Result of removing a room ban.
+message UnbanMemberResponse {
+  // True when the unban operation completed.
+  bool unbanned = 1;
+}
+
+// Active channel room ban with optional hydrated room and user references.
+message RoomBan {
+  // Stable ban event ID.
+  string id = 1;
+  // Channel room ID.
+  string room_id = 2;
+  // Channel room metadata, when the referenced room still exists.
+  Room room = 3;
+  // Banned user ID.
+  string user_id = 4;
+  // Banned user profile, when the referenced user still exists.
+  DirectoryMember user = 5;
+  // Moderator user ID that created the ban.
+  string moderator_id = 6;
+  // Moderator profile, when the referenced user still exists.
+  DirectoryMember moderator = 7;
+  // Moderator-entered ban reason.
+  string reason = 8;
+  // Time the ban was created.
+  google.protobuf.Timestamp created_at = 9;
+  // Optional future time when the ban expires.
+  google.protobuf.Timestamp expires_at = 10;
+}
+
+// Request to list active room bans.
+message ListBansRequest {
+  // Optional channel room filter. Empty lists active bans across all rooms.
+  string room_id = 1;
+  // Page request. Defaults are applied when absent or limit is zero.
+  PageRequest page = 2;
+}
+
+// Active room bans visible to the current moderator.
+message ListBansResponse {
+  // Active bans, newest first.
+  repeated RoomBan bans = 1;
+  // Page metadata.
+  PageInfo page = 2;
+}
+
+// Request for room-scoped attachment list pages.
+message ListRoomAttachmentsRequest {
+  reserved 2, 3;
+  reserved "limit", "offset";
+
+  // Required room ID.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Thumbnail URL options. Defaults are applied when absent.
+  ImageTransformOptions thumbnail = 4;
+  // Page request. Defaults are applied when absent or limit is zero.
+  PageRequest page = 5;
+}
+
+// Room-scoped attachment list response.
+message ListRoomAttachmentsResponse {
+  reserved 2, 3;
+  reserved "total_count", "has_more";
+
+  // Current attachments in newest message order.
+  repeated RoomAttachmentListItem attachments = 1;
+  // Page metadata.
+  PageInfo page = 4;
+}
+
+// Request to refresh the current user's live-only typing indicator.
+message UpdateTypingIndicatorRequest {
+  // Required. Room where the current user is typing.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Event ID of the thread root when typing inside a thread.
+  string thread_root_event_id = 2;
+}
+
+// Result of refreshing a typing indicator.
+message UpdateTypingIndicatorResponse {
+  // True when the typing indicator was accepted for publish.
+  bool updated = 1;
+}
+
+// Manages room-scoped operations for the current user.
+//
+// RoomService owns operations whose authorization and state are primarily scoped
+// to one room, including lifecycle, membership, moderation, room timeline reads,
+// room read state, attachments, and live typing. Resource-specific services
+// should still be preferred when an operation is not naturally room-scoped or
+// when the resource needs an independent CRUD/batch surface.
+service RoomService {
+  // Creates a new channel room in a room group. The caller must be allowed to
+  // create rooms in the target group.
+  rpc CreateRoom(CreateRoomRequest) returns (CreateRoomResponse);
+  // Updates a room's editable metadata. The caller must be allowed to manage
+  // rooms. Direct-message rooms cannot be universal.
+  rpc UpdateRoom(UpdateRoomRequest) returns (UpdateRoomResponse);
+  // Archives a room so it is hidden from active room lists. The caller must be
+  // allowed to manage rooms.
+  rpc ArchiveRoom(ArchiveRoomRequest) returns (ArchiveRoomResponse);
+  // Restores an archived room to active room lists. The caller must be allowed
+  // to manage rooms.
+  rpc UnarchiveRoom(UnarchiveRoomRequest) returns (UnarchiveRoomResponse);
+  // Joins the room as the current user when room permissions allow it.
+  rpc JoinRoom(JoinRoomRequest) returns (JoinRoomResponse);
+  // Joins every unarchived room in a group that the current user can join.
+  // Already-joined and non-joinable rooms are skipped.
+  rpc JoinRoomGroup(JoinRoomGroupRequest) returns (JoinRoomGroupResponse);
+  // Starts or fetches a direct-message room for the current user and the
+  // requested participant set. The caller must be allowed to start DMs.
+  rpc StartDM(StartDMRequest) returns (StartDMResponse);
+  // Leaves the room as the current user. Direct-message and universal rooms
+  // cannot be left.
+  rpc LeaveRoom(LeaveRoomRequest) returns (LeaveRoomResponse);
+  // Lists explicit members of a room. The caller must be a member of the room.
+  rpc ListMembers(ListRoomMembersRequest) returns (ListRoomMembersResponse);
+  // Gets one explicit member of a room. The caller must be a member of the
+  // room. Returns NOT_FOUND when the target is unknown or not a room member.
+  rpc GetMember(GetRoomMemberRequest) returns (GetRoomMemberResponse);
+  // Gets explicit room member rows for multiple users. The caller must be a
+  // member of the room.
+  rpc BatchGetMembers(BatchGetRoomMembersRequest) returns (BatchGetRoomMembersResponse);
+  // Adds a user as an explicit member of a channel room. The caller must be
+  // allowed to manage the room. Direct-message and universal rooms cannot be
+  // managed this way.
+  rpc AddMember(AddMemberRequest) returns (AddMemberResponse);
+  // Removes a user from a channel room's explicit members. The caller must be
+  // allowed to manage the room. Direct-message and universal rooms cannot be
+  // managed this way.
+  rpc RemoveMember(RemoveMemberRequest) returns (RemoveMemberResponse);
+  // Lists active channel room bans. The caller must be allowed to moderate room
+  // membership bans.
+  rpc ListBans(ListBansRequest) returns (ListBansResponse);
+  // Lists current message-owned room attachments. Authentication and room
+  // membership are required. Returns PERMISSION_DENIED when the room is
+  // inaccessible to the caller.
+  rpc ListRoomAttachments(ListRoomAttachmentsRequest) returns (ListRoomAttachmentsResponse);
+  // Refreshes the current user's live-only typing indicator for a room or
+  // thread. Room membership is required; message posting permission is not.
+  rpc UpdateTypingIndicator(UpdateTypingIndicatorRequest) returns (UpdateTypingIndicatorResponse);
+  // Returns one page of room timeline events, including related user data needed
+  // to render the page.
+  rpc GetRoomEvents(GetRoomEventsRequest) returns (GetRoomEventsResponse);
+  // Returns a room timeline window centered around a specific event. Use this to
+  // open a permalink, search result, or notification target in context. Returns
+  // NOT_FOUND when the anchor event is missing or not visible in the room
+  // timeline and PERMISSION_DENIED when the room is inaccessible.
+  rpc GetRoomEventsAround(GetRoomEventsAroundRequest) returns (GetRoomEventsAroundResponse);
+  // Marks a room timeline as read through the supplied event. If no event is
+  // supplied, the server marks through the room's latest root event. Clients
+  // usually call this after the user has viewed the latest visible event in the
+  // room.
+  rpc MarkRoomAsRead(MarkRoomAsReadRequest) returns (MarkRoomAsReadResponse);
+  // Bans a member from a channel room. Direct-message rooms cannot be moderated
+  // this way, and the target must currently be a room member.
+  rpc BanMember(BanMemberRequest) returns (BanMemberResponse);
+  // Removes an active channel room ban. Calling this when no active ban exists
+  // is allowed and still returns success.
+  rpc UnbanMember(UnbanMemberRequest) returns (UnbanMemberResponse);
+}

+ 31 - 0
proto/chatto/api/v1/server.proto

@@ -0,0 +1,31 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "chatto/api/v1/common.proto";
+
+// Public server profile, identity, and branding fields.
+message ServerPublicProfile {
+  // Display name of the Chatto server.
+  string name = 1;
+  // Server software version.
+  string version = 2;
+  // Optional server logo URL.
+  optional string logo_url = 3;
+  // Optional server banner URL.
+  optional string banner_url = 4;
+  // Optional welcome message.
+  optional string welcome_message = 5;
+  // Optional server description.
+  optional string description = 6;
+}
+
+// Login and registration options exposed before authentication.
+message ServerLogin {
+  // Whether users can create accounts through the public UI.
+  bool direct_registration_enabled = 1;
+  // Configured login providers.
+  repeated ProviderMetadata providers = 2;
+  // URL for the legacy authorization flow, when enabled.
+  string authorize_url = 3;
+}

+ 49 - 0
proto/chatto/api/v1/server_state.proto

@@ -0,0 +1,49 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+// Request for the authenticated message of the day.
+message GetMotdRequest {}
+
+// Authenticated message of the day response.
+message GetMotdResponse {
+  // Optional message of the day shown to authenticated members.
+  optional string motd = 1;
+}
+
+// Authenticated server runtime settings used by clients.
+message ServerRuntimeConfig {
+  // Whether Web Push notifications are fully configured.
+  bool push_notifications_enabled = 1;
+  // Optional VAPID public key for Web Push registration.
+  optional string vapid_public_key = 2;
+  // Optional LiveKit URL for voice and video calls.
+  optional string livekit_url = 3;
+  reserved 4;
+  reserved "direct_registration_enabled";
+  // Whether video processing is enabled.
+  bool video_processing_enabled = 5;
+  // Maximum general upload size in bytes.
+  int64 max_upload_size = 6;
+  // Maximum video upload size in bytes.
+  int64 max_video_upload_size = 7;
+  // Message edit window in seconds.
+  int32 message_edit_window_seconds = 8;
+}
+
+// Request for authenticated server runtime configuration.
+message GetRuntimeConfigRequest {}
+
+// Authenticated server runtime configuration response.
+message GetRuntimeConfigResponse {
+  // Authenticated runtime settings used by clients.
+  ServerRuntimeConfig runtime = 1;
+}
+
+// Provides authenticated server-wide reads.
+service ServerService {
+  // Returns the authenticated message of the day.
+  rpc GetMotd(GetMotdRequest) returns (GetMotdResponse);
+  // Returns authenticated runtime settings used by clients.
+  rpc GetRuntimeConfig(GetRuntimeConfigRequest) returns (GetRuntimeConfigResponse);
+}

+ 112 - 0
proto/chatto/api/v1/threads.proto

@@ -0,0 +1,112 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/message_types.proto";
+import "chatto/api/v1/pagination.proto";
+import "chatto/api/v1/read_state.proto";
+import "chatto/api/v1/room_timeline.proto";
+import "chatto/api/v1/rooms.proto";
+
+// Current follow state for one thread and viewer.
+message ThreadFollowState {
+  // Room containing the thread.
+  string room_id = 1;
+  // Event ID of the root message for the thread.
+  string thread_root_event_id = 2;
+  // True when the current user follows the thread.
+  bool following = 3;
+}
+
+// Request to follow one message thread.
+message FollowThreadRequest {
+  // Required. Room containing the thread.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the root message for the thread.
+  string thread_root_event_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of following a thread.
+message FollowThreadResponse {
+  // True when the current user follows the thread after the operation.
+  bool following = 1;
+  // Current follow state after the operation.
+  ThreadFollowState state = 2;
+}
+
+// Request to stop following one message thread.
+message UnfollowThreadRequest {
+  // Required. Room containing the thread.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+  // Required. Event ID of the root message for the thread.
+  string thread_root_event_id = 2 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of unfollowing a thread.
+message UnfollowThreadResponse {
+  // True when the current user follows the thread after the operation.
+  bool following = 1;
+  // Current follow state after the operation.
+  ThreadFollowState state = 2;
+}
+
+// One followed thread for the current user.
+message FollowedThread {
+  reserved 1 to 3, 5 to 7;
+  reserved "room_id", "room_name", "thread_root_event_id", "reply_count", "last_reply_at", "has_unread";
+
+  // Renderable root message, when the root is still visible.
+  Message root_message = 4;
+  // Room containing the thread.
+  RoomSummary room = 8;
+  // Aggregated thread state.
+  ThreadSummary thread = 9;
+}
+
+// Request for a page of followed threads for the current user.
+message ListFollowedThreadsRequest {
+  reserved 1, 2;
+  reserved "limit", "offset";
+
+  // Page request. Defaults to 20 results when absent or limit is zero.
+  PageRequest page = 3;
+}
+
+// Response containing one followed-thread page.
+message ListFollowedThreadsResponse {
+  reserved 2, 3;
+  reserved "total_count", "has_more";
+
+  // Followed threads in newest-activity-first order.
+  repeated FollowedThread threads = 1;
+  // Hot-path related entities needed to render this feed page without
+  // per-thread hydration.
+  RoomTimelineIncludes includes = 4;
+  // Page metadata.
+  PageInfo page = 5;
+}
+
+// Manages thread follow state for the current user.
+service ThreadService {
+  // Returns followed threads for the current user, including enough root-message
+  // data for clients to render the list without extra per-field fetches.
+  rpc ListFollowedThreads(ListFollowedThreadsRequest) returns (ListFollowedThreadsResponse);
+  // Follows a thread for the current user. Followed threads can be surfaced in
+  // clients and can participate in thread notification behavior.
+  rpc FollowThread(FollowThreadRequest) returns (FollowThreadResponse);
+  // Stops following a thread for the current user. The response reports the
+  // resulting follow state so clients can update local UI immediately.
+  rpc UnfollowThread(UnfollowThreadRequest) returns (UnfollowThreadResponse);
+  // Returns one page of events in a message thread. Initial pages include the
+  // thread root message; cursor pages return replies in the requested direction.
+  rpc GetThreadEvents(GetThreadEventsRequest) returns (GetThreadEventsResponse);
+  // Returns a thread timeline window centered around a specific event. Use this
+  // to open a reply from a notification or search result in context. Returns
+  // NOT_FOUND when the thread root or anchor event is missing or hidden and
+  // PERMISSION_DENIED when the room is inaccessible.
+  rpc GetThreadEventsAround(GetThreadEventsAroundRequest) returns (GetThreadEventsAroundResponse);
+  // Marks a thread timeline as read through the supplied event without changing
+  // the room-level read marker.
+  rpc MarkThreadAsRead(MarkThreadAsReadRequest) returns (MarkThreadAsReadResponse);
+}

+ 172 - 0
proto/chatto/api/v1/voice_calls.proto

@@ -0,0 +1,172 @@
+syntax = "proto3";
+
+package chatto.api.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/rooms.proto";
+import "chatto/api/v1/users.proto";
+import "google/protobuf/timestamp.proto";
+
+// Voice-call request/response APIs backed by Chatto call-state projections.
+service VoiceCallService {
+  // Lists member channel rooms that currently have active calls as a finite
+  // runtime snapshot. Rooms the caller is not a member of are omitted.
+  //
+  // Returns an empty list when LiveKit is not configured.
+  rpc ListActiveCalls(ListActiveCallsRequest) returns (ListActiveCallsResponse);
+
+  // Gets the current active call snapshot for one room.
+  //
+  // The caller must be a member of the room. Returns NOT_FOUND when the room
+  // does not exist, currently has no active call, or voice calls are not
+  // configured. The returned call_id lets clients ignore stale realtime call
+  // events from previous calls in the same room.
+  rpc GetActiveCall(GetActiveCallRequest) returns (GetActiveCallResponse);
+
+  // Gets current active call snapshots for many rooms.
+  //
+  // Rooms that do not exist, are inaccessible, or currently have no active call
+  // are omitted. Results preserve first-seen request order and repeated room
+  // IDs are de-duplicated. Returns an empty list when LiveKit is not configured.
+  rpc BatchGetActiveCalls(BatchGetActiveCallsRequest) returns (BatchGetActiveCallsResponse);
+
+  // Lists participants currently observed in a room's active call as a finite
+  // runtime snapshot.
+  //
+  // The caller must be a member of the room.
+  // Returns NOT_FOUND when the room does not exist and PERMISSION_DENIED when
+  // the caller is not a room member.
+  rpc ListCallParticipants(ListCallParticipantsRequest) returns (ListCallParticipantsResponse);
+
+  // Records the caller's intent to join a room call.
+  //
+  // Returns joined=false when LiveKit is not configured.
+  rpc JoinCall(JoinCallRequest) returns (JoinCallResponse);
+
+  // Issues a LiveKit token for joining the room's active call.
+  //
+  // The caller must be a member of the room and a call must already be active.
+  // Returns NOT_FOUND when the room does not exist, PERMISSION_DENIED when the
+  // caller is not a room member, and FAILED_PRECONDITION when no call is active
+  // or voice and video calls are not configured.
+  rpc GetCallToken(GetCallTokenRequest) returns (GetCallTokenResponse);
+
+  // Records the caller's intent to leave a room call.
+  //
+  // Returns left=false when LiveKit is not configured.
+  rpc LeaveCall(LeaveCallRequest) returns (LeaveCallResponse);
+}
+
+// Request for active channel call snapshots.
+message ListActiveCallsRequest {}
+
+// Finite runtime snapshot of active channel calls.
+message ListActiveCallsResponse {
+  // Active calls in room order returned by the call-state projection.
+  repeated ActiveCall calls = 1;
+}
+
+// Current active call snapshot for one room.
+message ActiveCall {
+  reserved 1;
+  reserved "room_id";
+
+  // Room containing the active call.
+  RoomSummary room = 4;
+  // Active call session ID.
+  string call_id = 2;
+  // Participants currently projected for this call.
+  repeated CallParticipant participants = 3;
+}
+
+// Request for one room's active call snapshot.
+message GetActiveCallRequest {
+  // Required. Room whose active call should be inspected.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response for one room's active call snapshot.
+message GetActiveCallResponse {
+  // Current active call.
+  ActiveCall call = 1;
+}
+
+// Request active call snapshots for many rooms.
+message BatchGetActiveCallsRequest {
+  // Required room IDs. Unknown, inaccessible, and inactive rooms are omitted.
+  repeated string room_ids = 1 [(buf.validate.field).repeated = {
+    min_items: 1
+    max_items: 100
+    items: {
+      string: {min_len: 1}
+    }
+  }];
+}
+
+// Active call batch response.
+message BatchGetActiveCallsResponse {
+  // Active calls in first-seen request order.
+  repeated ActiveCall calls = 1;
+}
+
+// Request for participants in a room call.
+message ListCallParticipantsRequest {
+  // Required. Room whose active call should be inspected.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Finite runtime snapshot of participants in one room call.
+message ListCallParticipantsResponse {
+  // Participants currently projected for the room call.
+  repeated CallParticipant participants = 1;
+}
+
+// User currently participating in a room call.
+message CallParticipant {
+  // Participant user.
+  User user = 1;
+  // When the user joined this call.
+  google.protobuf.Timestamp joined_at = 2;
+  // Active call session ID.
+  string call_id = 3;
+}
+
+// Request to record joining a room call.
+message JoinCallRequest {
+  // Required. Room whose call is being joined.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response from recording a join intent.
+message JoinCallResponse {
+  // True when a join fact was recorded.
+  bool joined = 1;
+}
+
+// Request for a LiveKit token for a room call.
+message GetCallTokenRequest {
+  // Required. Room whose active call should be joined.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// LiveKit token details for joining a room call.
+message GetCallTokenResponse {
+  // LiveKit JWT token.
+  string token = 1;
+  // Shared E2EE key for this active call.
+  string e2ee_key = 2;
+  // Active call session ID.
+  string call_id = 3;
+}
+
+// Request to record leaving a room call.
+message LeaveCallRequest {
+  // Required. Room whose call is being left.
+  string room_id = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Response from recording a leave intent.
+message LeaveCallResponse {
+  // True when a leave fact was recorded.
+  bool left = 1;
+}

+ 113 - 0
proto/chatto/auth/v1/external_identity_auth.proto

@@ -0,0 +1,113 @@
+syntax = "proto3";
+
+package chatto.auth.v1;
+
+import "buf/validate/validate.proto";
+import "chatto/api/v1/external_identities.proto";
+
+// Mode of a pending external identity confirmation flow.
+enum ExternalIdentityFlowKind {
+  // The flow kind was not specified.
+  EXTERNAL_IDENTITY_FLOW_KIND_UNSPECIFIED = 0;
+  // Create a new passwordless account and link the provider identity.
+  EXTERNAL_IDENTITY_FLOW_KIND_CREATE_ACCOUNT = 1;
+  // Link the provider identity to the authenticated account.
+  EXTERNAL_IDENTITY_FLOW_KIND_LINK_ACCOUNT = 2;
+}
+
+// Metadata about a pending external identity flow. The provider subject is not
+// exposed; clients only need user-facing provider/profile hints.
+message PendingExternalIdentity {
+  // Pending flow kind.
+  ExternalIdentityFlowKind kind = 1;
+  // Provider ID that produced the identity.
+  string provider_id = 2;
+  // Provider type that produced the identity.
+  string provider_type = 3;
+  // Provider label shown to the user.
+  string provider_label = 4;
+  // Verified email supplied by the provider, when available.
+  string verified_email = 5;
+  // Suggested username derived from provider profile hints.
+  string login_hint = 6;
+  // Suggested display name derived from provider profile hints.
+  string display_name_hint = 7;
+  // User ID this link flow is bound to. Empty for create flows.
+  string bound_user_id = 8;
+  // Internal path the client should return to after confirmation.
+  string redirect_path = 9;
+}
+
+// Request to inspect a pending external identity flow.
+message GetPendingExternalIdentityRequest {
+  // Raw pending flow token from the provider callback redirect.
+  string token = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of inspecting a pending external identity flow.
+message GetPendingExternalIdentityResponse {
+  // Pending flow metadata.
+  PendingExternalIdentity pending = 1;
+}
+
+// Request to create a new passwordless account from a pending provider identity.
+message CreateExternalIdentityAccountRequest {
+  // Raw pending create token from the provider callback redirect.
+  string token = 1 [(buf.validate.field).string.min_len = 1];
+  // Desired username.
+  string login = 2 [(buf.validate.field).string = {
+    min_len: 2
+    max_len: 32
+  }];
+}
+
+// Result of creating an account from a pending provider identity.
+message CreateExternalIdentityAccountResponse {
+  // Newly created user ID.
+  string user_id = 1;
+  // Newly created username.
+  string login = 2;
+  // Bearer token for the new session.
+  string token = 3;
+}
+
+// Request to cancel a pending external identity flow.
+message CancelExternalIdentityFlowRequest {
+  // Raw pending flow token.
+  string token = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of cancelling a pending external identity flow.
+message CancelExternalIdentityFlowResponse {
+  // True when a pending flow was removed or already absent.
+  bool cancelled = 1;
+}
+
+// Request to confirm a pending provider link using the flow capability token.
+message ConfirmExternalIdentityLinkRequest {
+  // Raw pending link token from the provider callback redirect.
+  string token = 1 [(buf.validate.field).string.min_len = 1];
+}
+
+// Result of confirming a provider identity link.
+message ConfirmExternalIdentityLinkResponse {
+  // Linked identity metadata.
+  chatto.api.v1.LinkedExternalIdentity linked_identity = 1;
+}
+
+// Public external identity authentication operations.
+service ExternalIdentityAuthService {
+  // Reads safe metadata for a pending create/link flow. This public RPC
+  // requires a valid pending-flow capability token and returns NOT_FOUND when
+  // the token is unknown or expired.
+  rpc GetPendingExternalIdentity(GetPendingExternalIdentityRequest) returns (GetPendingExternalIdentityResponse);
+  // Creates a passwordless account from a pending provider identity. This
+  // public RPC requires a valid create-flow capability token.
+  rpc CreateExternalIdentityAccount(CreateExternalIdentityAccountRequest) returns (CreateExternalIdentityAccountResponse);
+  // Links a provider identity using a pending link-flow capability token. This
+  // public RPC requires a valid link-flow capability token.
+  rpc ConfirmExternalIdentityLink(ConfirmExternalIdentityLinkRequest) returns (ConfirmExternalIdentityLinkResponse);
+  // Cancels a pending provider identity flow. This public RPC requires a valid
+  // pending-flow capability token.
+  rpc CancelExternalIdentityFlow(CancelExternalIdentityFlowRequest) returns (CancelExternalIdentityFlowResponse);
+}

+ 25 - 0
proto/chatto/discovery/v1/server.proto

@@ -0,0 +1,25 @@
+syntax = "proto3";
+
+package chatto.discovery.v1;
+
+import "chatto/api/v1/server.proto";
+
+// Request for public server metadata.
+message GetServerRequest {}
+
+// Public metadata clients need before they can authenticate or render the
+// initial server view.
+message GetServerResponse {
+  // Public server profile and branding.
+  chatto.api.v1.ServerPublicProfile profile = 1;
+  // Login and registration options.
+  chatto.api.v1.ServerLogin login = 2;
+}
+
+// Provides unauthenticated server discovery metadata.
+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);
+}

+ 3 - 2
pyproject.toml

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "chattolib"
-version = "0.4.2.post1"
+version = "0.4.2.post2"
 description = "Async Python client library for the Chatto webchat Connect API"
 requires-python = ">=3.11"
 license = "MIT"
@@ -24,6 +24,8 @@ classifiers = [
     "Framework :: AsyncIO",
 ]
 dependencies = [
+    "connectrpc>=0.11",
+    "protobuf>=5.28",
     "httpx>=0.27",
 ]
 
@@ -33,7 +35,6 @@ Homepage = "https://chat.chatto.run"
 [project.optional-dependencies]
 realtime = [
     "websockets>=13.0",
-    "protobuf>=5.28",
 ]
 dev = [
     "pytest>=8.0",

+ 64 - 33
scripts/generate_pb.sh

@@ -1,10 +1,17 @@
 #!/usr/bin/env bash
-# Regenerate the vendored Python protobuf bindings under src/chattolib/_pb.
+# Regenerate the vendored Python protobuf + ConnectRPC bindings under
+# src/chattolib/_pb.
 #
 # Fetches the required .proto sources into proto/ (from chattocorp/chatto's
-# main branch and bufbuild/protovalidate) and runs protoc.
+# main branch and bufbuild/protovalidate) and runs protoc with both the
+# built-in Python code generator and the connect-python plugin.
 #
-# Requirements: protoc on PATH, curl on PATH.
+# Requirements:
+#   - protoc on PATH
+#   - protoc-gen-connect-python on PATH (install via
+#     `pip install protoc-gen-connect-python`, which chattolib pins under
+#     the `[realtime]` extra)
+#   - curl on PATH
 #
 # Run from the repo root:  ./scripts/generate_pb.sh
 
@@ -18,26 +25,51 @@ fi
 repo_root=$(cd "$(dirname "$0")/.." && pwd)
 cd "$repo_root"
 
-mkdir -p proto/chatto/realtime/v1 proto/chatto/api/v1 proto/buf/validate
+mkdir -p proto/chatto/{api,admin,auth,discovery,realtime}/v1 proto/buf/validate
 
 chatto_base="https://raw.githubusercontent.com/chattocorp/chatto/main/proto/chatto"
 
 fetch() {
     local rel=$1
-    local dest="proto/$rel"
-    curl -sfL "$chatto_base/${rel#chatto/}" -o "$dest"
+    curl -sfL "$chatto_base/${rel#chatto/}" -o "proto/$rel"
     echo "  $rel"
 }
 
 echo "Fetching proto sources ..."
+# chatto.api.v1
+for f in \
+    account.proto attachments.proto asset_uploads.proto common.proto \
+    external_identities.proto link_previews.proto member_directory.proto \
+    messages.proto message_types.proto notification_preferences.proto \
+    notifications.proto pagination.proto permissions.proto presence.proto \
+    push_notifications.proto reactions.proto read_state.proto \
+    room_directory.proto rooms.proto room_timeline.proto roles.proto \
+    server.proto server_state.proto threads.proto users.proto \
+    user_status.proto viewer.proto voice_calls.proto
+do
+    fetch "chatto/api/v1/$f"
+done
+
+# chatto.admin.v1
+for f in \
+    diagnostics.proto event_log.proto members.proto permissions.proto \
+    roles.proto room_layout.proto server.proto
+do
+    fetch "chatto/admin/v1/$f"
+done
+
+# chatto.auth.v1
+fetch chatto/auth/v1/external_identity_auth.proto
+
+# chatto.discovery.v1
+fetch chatto/discovery/v1/server.proto
+
+# chatto.realtime.v1
 fetch chatto/realtime/v1/realtime.proto
-fetch chatto/api/v1/viewer.proto
-fetch chatto/api/v1/notification_preferences.proto
-fetch chatto/api/v1/presence.proto
-fetch chatto/api/v1/users.proto
-fetch chatto/api/v1/user_status.proto
-fetch chatto/api/v1/permissions.proto
-curl -sfL "https://raw.githubusercontent.com/bufbuild/protovalidate/main/proto/protovalidate/buf/validate/validate.proto" \
+
+# buf.validate — sourced from bufbuild/protovalidate
+curl -sfL \
+    "https://raw.githubusercontent.com/bufbuild/protovalidate/main/proto/protovalidate/buf/validate/validate.proto" \
     -o proto/buf/validate/validate.proto
 echo "  buf/validate/validate.proto"
 
@@ -45,27 +77,26 @@ echo "Regenerating Python bindings ..."
 rm -rf src/chattolib/_pb/chatto src/chattolib/_pb/buf
 mkdir -p src/chattolib/_pb
 
-protoc --proto_path=proto --python_out=src/chattolib/_pb \
-    proto/chatto/realtime/v1/realtime.proto \
-    proto/chatto/api/v1/viewer.proto \
-    proto/chatto/api/v1/notification_preferences.proto \
-    proto/chatto/api/v1/presence.proto \
-    proto/chatto/api/v1/users.proto \
-    proto/chatto/api/v1/user_status.proto \
-    proto/chatto/api/v1/permissions.proto \
-    proto/buf/validate/validate.proto
+# Collect every input .proto so protoc's plugins visit each service.
+mapfile -t proto_files < <(find proto -type f -name '*.proto' | sort)
+
+protoc --proto_path=proto \
+    --python_out=src/chattolib/_pb \
+    "${proto_files[@]}"
+
+# Only run the connect-python plugin when it is available. The realtime
+# WebSocket path only needs the pb2 files; every request/response service is
+# strongly typed via the connect stubs.
+if command -v protoc-gen-connect-python >/dev/null; then
+    protoc --proto_path=proto \
+        --connect-python_out=src/chattolib/_pb \
+        "${proto_files[@]}"
+else
+    echo "protoc-gen-connect-python not on PATH; skipped connect stubs" >&2
+fi
 
 # Add __init__.py at every level so the generated modules form a package.
-for dir in \
-    src/chattolib/_pb/chatto \
-    src/chattolib/_pb/chatto/api \
-    src/chattolib/_pb/chatto/api/v1 \
-    src/chattolib/_pb/chatto/realtime \
-    src/chattolib/_pb/chatto/realtime/v1 \
-    src/chattolib/_pb/buf \
-    src/chattolib/_pb/buf/validate
-do
-    touch "$dir/__init__.py"
-done
+find src/chattolib/_pb \( -path '*/chatto*' -o -path '*/buf*' \) -type d \
+    | while read -r dir; do touch "$dir/__init__.py"; done
 
 echo "Done."

+ 0 - 0
src/chattolib/_pb/chatto/admin/__init__.py


+ 0 - 0
src/chattolib/_pb/chatto/admin/v1/__init__.py


+ 123 - 0
src/chattolib/_pb/chatto/admin/v1/diagnostics_connect.py

@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/diagnostics.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.diagnostics_pb2 as chatto_dot_admin_dot_v1_dot_diagnostics__pb2
+
+
+class AdminDiagnosticsService(Protocol):
+    async def get_system_info(self, request: chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminDiagnosticsServiceASGIApplication(ConnectASGIApplication[AdminDiagnosticsService]):
+    def __init__(self, service: AdminDiagnosticsService | AsyncGenerator[AdminDiagnosticsService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminDiagnosticsService/GetSystemInfo": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetSystemInfo",
+                        service_name="chatto.admin.v1.AdminDiagnosticsService",
+                        input=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_system_info,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminDiagnosticsService"
+
+
+class AdminDiagnosticsServiceClient(ConnectClient):
+    async def get_system_info(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetSystemInfo",
+                service_name="chatto.admin.v1.AdminDiagnosticsService",
+                input=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+                output=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminDiagnosticsServiceSync(Protocol):
+    def get_system_info(self, request: chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminDiagnosticsServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminDiagnosticsServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminDiagnosticsService/GetSystemInfo": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetSystemInfo",
+                        service_name="chatto.admin.v1.AdminDiagnosticsService",
+                        input=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_system_info,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminDiagnosticsService"
+
+
+class AdminDiagnosticsServiceClientSync(ConnectClientSync):
+    def get_system_info(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetSystemInfo",
+                service_name="chatto.admin.v1.AdminDiagnosticsService",
+                input=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoRequest,
+                output=chatto_dot_admin_dot_v1_dot_diagnostics__pb2.GetSystemInfoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 26 - 0
src/chattolib/_pb/chatto/admin/v1/diagnostics_pb2.py


+ 253 - 0
src/chattolib/_pb/chatto/admin/v1/event_log_connect.py

@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/event_log.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.event_log_pb2 as chatto_dot_admin_dot_v1_dot_event__log__pb2
+
+
+class AdminEventLogService(Protocol):
+    async def list_events(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_event_types(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_event(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminEventLogServiceASGIApplication(ConnectASGIApplication[AdminEventLogService]):
+    def __init__(self, service: AdminEventLogService | AsyncGenerator[AdminEventLogService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminEventLogService/ListEvents": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListEvents",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_events,
+                ),
+                "/chatto.admin.v1.AdminEventLogService/ListEventTypes": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListEventTypes",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_event_types,
+                ),
+                "/chatto.admin.v1.AdminEventLogService/GetEvent": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetEvent",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_event,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminEventLogService"
+
+
+class AdminEventLogServiceClient(ConnectClient):
+    async def list_events(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListEvents",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_event_types(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListEventTypes",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_event(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetEvent",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminEventLogServiceSync(Protocol):
+    def list_events(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_event_types(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_event(self, request: chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminEventLogServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminEventLogServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminEventLogService/ListEvents": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListEvents",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_events,
+                ),
+                "/chatto.admin.v1.AdminEventLogService/ListEventTypes": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListEventTypes",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_event_types,
+                ),
+                "/chatto.admin.v1.AdminEventLogService/GetEvent": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetEvent",
+                        service_name="chatto.admin.v1.AdminEventLogService",
+                        input=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+                        output=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_event,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminEventLogService"
+
+
+class AdminEventLogServiceClientSync(ConnectClientSync):
+    def list_events(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListEvents",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_event_types(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListEventTypes",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.ListEventTypesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_event(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetEvent",
+                service_name="chatto.admin.v1.AdminEventLogService",
+                input=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventRequest,
+                output=chatto_dot_admin_dot_v1_dot_event__log__pb2.GetEventResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 28 - 0
src/chattolib/_pb/chatto/admin/v1/event_log_pb2.py


+ 643 - 0
src/chattolib/_pb/chatto/admin/v1/members_connect.py

@@ -0,0 +1,643 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/members.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.members_pb2 as chatto_dot_admin_dot_v1_dot_members__pb2
+
+
+class AdminUserService(Protocol):
+    async def list_members(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_member(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_members(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def assign_role(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def revoke_role(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_user(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_user_password(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def clear_username_cooldown(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_user(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminUserServiceASGIApplication(ConnectASGIApplication[AdminUserService]):
+    def __init__(self, service: AdminUserService | AsyncGenerator[AdminUserService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminUserService/ListMembers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListMembers",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_members,
+                ),
+                "/chatto.admin.v1.AdminUserService/GetMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetMember",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_member,
+                ),
+                "/chatto.admin.v1.AdminUserService/BatchGetMembers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetMembers",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_members,
+                ),
+                "/chatto.admin.v1.AdminUserService/AssignRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="AssignRole",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.assign_role,
+                ),
+                "/chatto.admin.v1.AdminUserService/RevokeRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="RevokeRole",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.revoke_role,
+                ),
+                "/chatto.admin.v1.AdminUserService/UpdateUser": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateUser",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_user,
+                ),
+                "/chatto.admin.v1.AdminUserService/UpdateUserPassword": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateUserPassword",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_user_password,
+                ),
+                "/chatto.admin.v1.AdminUserService/ClearUsernameCooldown": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ClearUsernameCooldown",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.clear_username_cooldown,
+                ),
+                "/chatto.admin.v1.AdminUserService/DeleteUser": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteUser",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_user,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminUserService"
+
+
+class AdminUserServiceClient(ConnectClient):
+    async def list_members(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListMembers",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_member(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMember",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_members(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMembers",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def assign_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AssignRole",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def revoke_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RevokeRole",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_user(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateUser",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_user_password(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateUserPassword",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def clear_username_cooldown(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ClearUsernameCooldown",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_user(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteUser",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminUserServiceSync(Protocol):
+    def list_members(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_member(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_members(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def assign_role(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def revoke_role(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_user(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_user_password(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def clear_username_cooldown(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_user(self, request: chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminUserServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminUserServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminUserService/ListMembers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListMembers",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_members,
+                ),
+                "/chatto.admin.v1.AdminUserService/GetMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetMember",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_member,
+                ),
+                "/chatto.admin.v1.AdminUserService/BatchGetMembers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetMembers",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_members,
+                ),
+                "/chatto.admin.v1.AdminUserService/AssignRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="AssignRole",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.assign_role,
+                ),
+                "/chatto.admin.v1.AdminUserService/RevokeRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="RevokeRole",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.revoke_role,
+                ),
+                "/chatto.admin.v1.AdminUserService/UpdateUser": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateUser",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_user,
+                ),
+                "/chatto.admin.v1.AdminUserService/UpdateUserPassword": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateUserPassword",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_user_password,
+                ),
+                "/chatto.admin.v1.AdminUserService/ClearUsernameCooldown": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ClearUsernameCooldown",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.clear_username_cooldown,
+                ),
+                "/chatto.admin.v1.AdminUserService/DeleteUser": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteUser",
+                        service_name="chatto.admin.v1.AdminUserService",
+                        input=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+                        output=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_user,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminUserService"
+
+
+class AdminUserServiceClientSync(ConnectClientSync):
+    def list_members(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListMembers",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.ListMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_member(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMember",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.GetMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_members(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMembers",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.BatchGetMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def assign_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AssignRole",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.AssignRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def revoke_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RevokeRole",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.RevokeRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_user(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateUser",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_user_password(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateUserPassword",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.UpdateUserPasswordResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def clear_username_cooldown(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ClearUsernameCooldown",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.ClearUsernameCooldownResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_user(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteUser",
+                service_name="chatto.admin.v1.AdminUserService",
+                input=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserRequest,
+                output=chatto_dot_admin_dot_v1_dot_members__pb2.DeleteUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 32 - 0
src/chattolib/_pb/chatto/admin/v1/members_pb2.py


+ 578 - 0
src/chattolib/_pb/chatto/admin/v1/permissions_connect.py

@@ -0,0 +1,578 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/permissions.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.permissions_pb2 as chatto_dot_admin_dot_v1_dot_permissions__pb2
+
+
+class AdminPermissionService(Protocol):
+    async def get_role_permission_tier_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_role_permission_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_role_permission_decisions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_user_permission_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_user_permission_decisions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def explain_permissions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def set_role_permission(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def set_user_permission(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminPermissionServiceASGIApplication(ConnectASGIApplication[AdminPermissionService]):
+    def __init__(self, service: AdminPermissionService | AsyncGenerator[AdminPermissionService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminPermissionService/GetRolePermissionTierMatrix": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRolePermissionTierMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_role_permission_tier_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/GetRolePermissionMatrix": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRolePermissionMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_role_permission_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ListRolePermissionDecisions": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRolePermissionDecisions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_role_permission_decisions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/GetUserPermissionMatrix": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetUserPermissionMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_user_permission_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ListUserPermissionDecisions": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListUserPermissionDecisions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_user_permission_decisions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ExplainPermissions": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ExplainPermissions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.explain_permissions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/SetRolePermission": Endpoint.unary(
+                    method=MethodInfo(
+                        name="SetRolePermission",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.set_role_permission,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/SetUserPermission": Endpoint.unary(
+                    method=MethodInfo(
+                        name="SetUserPermission",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.set_user_permission,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminPermissionService"
+
+
+class AdminPermissionServiceClient(ConnectClient):
+    async def get_role_permission_tier_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRolePermissionTierMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_role_permission_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRolePermissionMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_role_permission_decisions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRolePermissionDecisions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_user_permission_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUserPermissionMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_user_permission_decisions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListUserPermissionDecisions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def explain_permissions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ExplainPermissions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def set_role_permission(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SetRolePermission",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def set_user_permission(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SetUserPermission",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminPermissionServiceSync(Protocol):
+    def get_role_permission_tier_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_role_permission_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_role_permission_decisions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_user_permission_matrix(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_user_permission_decisions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def explain_permissions(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def set_role_permission(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def set_user_permission(self, request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminPermissionServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminPermissionServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminPermissionService/GetRolePermissionTierMatrix": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRolePermissionTierMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_role_permission_tier_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/GetRolePermissionMatrix": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRolePermissionMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_role_permission_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ListRolePermissionDecisions": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRolePermissionDecisions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_role_permission_decisions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/GetUserPermissionMatrix": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetUserPermissionMatrix",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_user_permission_matrix,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ListUserPermissionDecisions": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListUserPermissionDecisions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_user_permission_decisions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/ExplainPermissions": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ExplainPermissions",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.explain_permissions,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/SetRolePermission": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="SetRolePermission",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.set_role_permission,
+                ),
+                "/chatto.admin.v1.AdminPermissionService/SetUserPermission": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="SetUserPermission",
+                        service_name="chatto.admin.v1.AdminPermissionService",
+                        input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+                        output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.set_user_permission,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminPermissionService"
+
+
+class AdminPermissionServiceClientSync(ConnectClientSync):
+    def get_role_permission_tier_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRolePermissionTierMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionTierMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_role_permission_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRolePermissionMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetRolePermissionMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_role_permission_decisions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRolePermissionDecisions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListRolePermissionDecisionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_user_permission_matrix(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUserPermissionMatrix",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.GetUserPermissionMatrixResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_user_permission_decisions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListUserPermissionDecisions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ListUserPermissionDecisionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def explain_permissions(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ExplainPermissions",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.ExplainPermissionsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def set_role_permission(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SetRolePermission",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetRolePermissionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def set_user_permission(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="SetUserPermission",
+                service_name="chatto.admin.v1.AdminPermissionService",
+                input=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionRequest,
+                output=chatto_dot_admin_dot_v1_dot_permissions__pb2.SetUserPermissionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 28 - 0
src/chattolib/_pb/chatto/admin/v1/permissions_pb2.py


+ 448 - 0
src/chattolib/_pb/chatto/admin/v1/roles_connect.py

@@ -0,0 +1,448 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/roles.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.roles_pb2 as chatto_dot_admin_dot_v1_dot_roles__pb2
+
+
+class AdminRoleService(Protocol):
+    async def list_roles(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def create_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def reorder_roles(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminRoleServiceASGIApplication(ConnectASGIApplication[AdminRoleService]):
+    def __init__(self, service: AdminRoleService | AsyncGenerator[AdminRoleService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminRoleService/ListRoles": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoles",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_roles,
+                ),
+                "/chatto.admin.v1.AdminRoleService/GetRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/CreateRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/UpdateRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/DeleteRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/ReorderRoles": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ReorderRoles",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.reorder_roles,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminRoleService"
+
+
+class AdminRoleServiceClient(ConnectClient):
+    async def list_roles(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoles",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def create_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def reorder_roles(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderRoles",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminRoleServiceSync(Protocol):
+    def list_roles(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def create_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_role(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def reorder_roles(self, request: chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminRoleServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminRoleServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminRoleService/ListRoles": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoles",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_roles,
+                ),
+                "/chatto.admin.v1.AdminRoleService/GetRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/CreateRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/UpdateRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/DeleteRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteRole",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_role,
+                ),
+                "/chatto.admin.v1.AdminRoleService/ReorderRoles": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ReorderRoles",
+                        service_name="chatto.admin.v1.AdminRoleService",
+                        input=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.reorder_roles,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminRoleService"
+
+
+class AdminRoleServiceClientSync(ConnectClientSync):
+    def list_roles(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoles",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.ListRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.GetRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def create_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.CreateRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.UpdateRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_role(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteRole",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.DeleteRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def reorder_roles(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderRoles",
+                service_name="chatto.admin.v1.AdminRoleService",
+                input=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesRequest,
+                output=chatto_dot_admin_dot_v1_dot_roles__pb2.ReorderRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29 - 0
src/chattolib/_pb/chatto/admin/v1/roles_pb2.py


+ 773 - 0
src/chattolib/_pb/chatto/admin/v1/room_layout_connect.py

@@ -0,0 +1,773 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/room_layout.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.room_layout_pb2 as chatto_dot_admin_dot_v1_dot_room__layout__pb2
+
+
+class AdminRoomLayoutService(Protocol):
+    async def list_room_groups(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def create_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def reorder_room_groups(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def move_room_to_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def reorder_sidebar_items_in_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def create_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def move_sidebar_link_to_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminRoomLayoutServiceASGIApplication(ConnectASGIApplication[AdminRoomLayoutService]):
+    def __init__(self, service: AdminRoomLayoutService | AsyncGenerator[AdminRoomLayoutService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminRoomLayoutService/ListRoomGroups": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoomGroups",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_room_groups,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/CreateRoomGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/UpdateRoomGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/DeleteRoomGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/ReorderRoomGroups": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ReorderRoomGroups",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.reorder_room_groups,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/MoveRoomToGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="MoveRoomToGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.move_room_to_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/ReorderSidebarItemsInGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ReorderSidebarItemsInGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.reorder_sidebar_items_in_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/CreateSidebarLink": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/UpdateSidebarLink": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/DeleteSidebarLink": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/MoveSidebarLinkToGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="MoveSidebarLinkToGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.move_sidebar_link_to_group,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminRoomLayoutService"
+
+
+class AdminRoomLayoutServiceClient(ConnectClient):
+    async def list_room_groups(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomGroups",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def create_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def reorder_room_groups(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderRoomGroups",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def move_room_to_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveRoomToGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def reorder_sidebar_items_in_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderSidebarItemsInGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def create_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def move_sidebar_link_to_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveSidebarLinkToGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminRoomLayoutServiceSync(Protocol):
+    def list_room_groups(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def create_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_room_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def reorder_room_groups(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def move_room_to_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def reorder_sidebar_items_in_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def create_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_sidebar_link(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def move_sidebar_link_to_group(self, request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminRoomLayoutServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminRoomLayoutServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminRoomLayoutService/ListRoomGroups": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoomGroups",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_room_groups,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/CreateRoomGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/UpdateRoomGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/DeleteRoomGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteRoomGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_room_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/ReorderRoomGroups": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ReorderRoomGroups",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.reorder_room_groups,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/MoveRoomToGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="MoveRoomToGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.move_room_to_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/ReorderSidebarItemsInGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ReorderSidebarItemsInGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.reorder_sidebar_items_in_group,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/CreateSidebarLink": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/UpdateSidebarLink": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/DeleteSidebarLink": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteSidebarLink",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_sidebar_link,
+                ),
+                "/chatto.admin.v1.AdminRoomLayoutService/MoveSidebarLinkToGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="MoveSidebarLinkToGroup",
+                        service_name="chatto.admin.v1.AdminRoomLayoutService",
+                        input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+                        output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.move_sidebar_link_to_group,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminRoomLayoutService"
+
+
+class AdminRoomLayoutServiceClientSync(ConnectClientSync):
+    def list_room_groups(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomGroups",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ListRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def create_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_room_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteRoomGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def reorder_room_groups(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderRoomGroups",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def move_room_to_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveRoomToGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveRoomToGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def reorder_sidebar_items_in_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ReorderSidebarItemsInGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.ReorderSidebarItemsInGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def create_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.CreateSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.UpdateSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_sidebar_link(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteSidebarLink",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.DeleteSidebarLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def move_sidebar_link_to_group(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MoveSidebarLinkToGroup",
+                service_name="chatto.admin.v1.AdminRoomLayoutService",
+                input=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupRequest,
+                output=chatto_dot_admin_dot_v1_dot_room__layout__pb2.MoveSidebarLinkToGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29 - 0
src/chattolib/_pb/chatto/admin/v1/room_layout_pb2.py


+ 578 - 0
src/chattolib/_pb/chatto/admin/v1/server_connect.py

@@ -0,0 +1,578 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/admin/v1/server.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.admin.v1.server_pb2 as chatto_dot_admin_dot_v1_dot_server__pb2
+
+
+class AdminServerService(Protocol):
+    async def get_server_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_server_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def upload_server_logo(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_server_logo(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def upload_server_banner(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_server_banner(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_server_security_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_blocked_usernames(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminServerServiceASGIApplication(ConnectASGIApplication[AdminServerService]):
+    def __init__(self, service: AdminServerService | AsyncGenerator[AdminServerService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.admin.v1.AdminServerService/GetServerConfig": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetServerConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_server_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UpdateServerConfig": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateServerConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_server_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UploadServerLogo": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UploadServerLogo",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.upload_server_logo,
+                ),
+                "/chatto.admin.v1.AdminServerService/DeleteServerLogo": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteServerLogo",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_server_logo,
+                ),
+                "/chatto.admin.v1.AdminServerService/UploadServerBanner": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UploadServerBanner",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.upload_server_banner,
+                ),
+                "/chatto.admin.v1.AdminServerService/DeleteServerBanner": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteServerBanner",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_server_banner,
+                ),
+                "/chatto.admin.v1.AdminServerService/GetServerSecurityConfig": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetServerSecurityConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_server_security_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UpdateBlockedUsernames": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateBlockedUsernames",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_blocked_usernames,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminServerService"
+
+
+class AdminServerServiceClient(ConnectClient):
+    async def get_server_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_server_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateServerConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def upload_server_logo(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadServerLogo",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_server_logo(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteServerLogo",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def upload_server_banner(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadServerBanner",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_server_banner(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteServerBanner",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_server_security_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerSecurityConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_blocked_usernames(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateBlockedUsernames",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AdminServerServiceSync(Protocol):
+    def get_server_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_server_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def upload_server_logo(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_server_logo(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def upload_server_banner(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_server_banner(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_server_security_config(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_blocked_usernames(self, request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest, ctx: RequestContext) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AdminServerServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AdminServerServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.admin.v1.AdminServerService/GetServerConfig": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetServerConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_server_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UpdateServerConfig": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateServerConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_server_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UploadServerLogo": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UploadServerLogo",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.upload_server_logo,
+                ),
+                "/chatto.admin.v1.AdminServerService/DeleteServerLogo": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteServerLogo",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_server_logo,
+                ),
+                "/chatto.admin.v1.AdminServerService/UploadServerBanner": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UploadServerBanner",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.upload_server_banner,
+                ),
+                "/chatto.admin.v1.AdminServerService/DeleteServerBanner": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteServerBanner",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_server_banner,
+                ),
+                "/chatto.admin.v1.AdminServerService/GetServerSecurityConfig": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetServerSecurityConfig",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_server_security_config,
+                ),
+                "/chatto.admin.v1.AdminServerService/UpdateBlockedUsernames": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateBlockedUsernames",
+                        service_name="chatto.admin.v1.AdminServerService",
+                        input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+                        output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_blocked_usernames,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.admin.v1.AdminServerService"
+
+
+class AdminServerServiceClientSync(ConnectClientSync):
+    def get_server_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_server_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateServerConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateServerConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def upload_server_logo(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadServerLogo",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerLogoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_server_logo(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteServerLogo",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerLogoResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def upload_server_banner(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadServerBanner",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UploadServerBannerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_server_banner(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteServerBanner",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.DeleteServerBannerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_server_security_config(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerSecurityConfig",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.GetServerSecurityConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_blocked_usernames(
+        self,
+        request: chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateBlockedUsernames",
+                service_name="chatto.admin.v1.AdminServerService",
+                input=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesRequest,
+                output=chatto_dot_admin_dot_v1_dot_server__pb2.UpdateBlockedUsernamesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29 - 0
src/chattolib/_pb/chatto/admin/v1/server_pb2.py


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

@@ -0,0 +1,906 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/account.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.account_pb2 as chatto_dot_api_dot_v1_dot_account__pb2
+import chatto.api.v1.external_identities_pb2 as chatto_dot_api_dot_v1_dot_external__identities__pb2
+import chatto.api.v1.presence_pb2 as chatto_dot_api_dot_v1_dot_presence__pb2
+import chatto.api.v1.user_status_pb2 as chatto_dot_api_dot_v1_dot_user__status__pb2
+
+
+class MyAccountService(Protocol):
+    async def update_profile(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def upload_avatar(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_avatar(self, request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_password(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_settings(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_external_identities(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def start_external_identity_link(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def disconnect_external_identity(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_presence(self, request: chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_custom_status(self, request: chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_custom_status(self, request: chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def request_account_deletion(self, request: chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_my_account(self, request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class MyAccountServiceASGIApplication(ConnectASGIApplication[MyAccountService]):
+    def __init__(self, service: MyAccountService | AsyncGenerator[MyAccountService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.MyAccountService/UpdateProfile": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateProfile",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_profile,
+                ),
+                "/chatto.api.v1.MyAccountService/UploadAvatar": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UploadAvatar",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.upload_avatar,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteAvatar": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteAvatar",
+                        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,
+                    ),
+                    function=svc.delete_avatar,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdatePassword": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdatePassword",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_password,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdateSettings": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateSettings",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_settings,
+                ),
+                "/chatto.api.v1.MyAccountService/ListExternalIdentities": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListExternalIdentities",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_external_identities,
+                ),
+                "/chatto.api.v1.MyAccountService/StartExternalIdentityLink": Endpoint.unary(
+                    method=MethodInfo(
+                        name="StartExternalIdentityLink",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.start_external_identity_link,
+                ),
+                "/chatto.api.v1.MyAccountService/DisconnectExternalIdentity": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DisconnectExternalIdentity",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.disconnect_external_identity,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdatePresence": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdatePresence",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_presence,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdateCustomStatus": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateCustomStatus",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+                        output=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_custom_status,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteCustomStatus": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteCustomStatus",
+                        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,
+                    ),
+                    function=svc.delete_custom_status,
+                ),
+                "/chatto.api.v1.MyAccountService/RequestAccountDeletion": Endpoint.unary(
+                    method=MethodInfo(
+                        name="RequestAccountDeletion",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.request_account_deletion,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteMyAccount": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteMyAccount",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_my_account,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.MyAccountService"
+
+
+class MyAccountServiceClient(ConnectClient):
+    async def update_profile(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateProfile",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def upload_avatar(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadAvatar",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_avatar(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteAvatar",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_password(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdatePassword",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_settings(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateSettings",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_external_identities(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListExternalIdentities",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def start_external_identity_link(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="StartExternalIdentityLink",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def disconnect_external_identity(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DisconnectExternalIdentity",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_presence(
+        self,
+        request: chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdatePresence",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+                output=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_custom_status(
+        self,
+        request: chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateCustomStatus",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+                output=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_custom_status(
+        self,
+        request: chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteCustomStatus",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def request_account_deletion(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RequestAccountDeletion",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_my_account(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteMyAccount",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class MyAccountServiceSync(Protocol):
+    def update_profile(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def upload_avatar(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_avatar(self, request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_password(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_settings(self, request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_external_identities(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def start_external_identity_link(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def disconnect_external_identity(self, request: chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_presence(self, request: chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_custom_status(self, request: chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_custom_status(self, request: chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def request_account_deletion(self, request: chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_my_account(self, request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class MyAccountServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: MyAccountServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.MyAccountService/UpdateProfile": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateProfile",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_profile,
+                ),
+                "/chatto.api.v1.MyAccountService/UploadAvatar": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UploadAvatar",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.upload_avatar,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteAvatar": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteAvatar",
+                        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,
+                    ),
+                    function=service.delete_avatar,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdatePassword": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdatePassword",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_password,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdateSettings": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateSettings",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_settings,
+                ),
+                "/chatto.api.v1.MyAccountService/ListExternalIdentities": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListExternalIdentities",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_external_identities,
+                ),
+                "/chatto.api.v1.MyAccountService/StartExternalIdentityLink": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="StartExternalIdentityLink",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.start_external_identity_link,
+                ),
+                "/chatto.api.v1.MyAccountService/DisconnectExternalIdentity": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DisconnectExternalIdentity",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+                        output=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.disconnect_external_identity,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdatePresence": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdatePresence",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_presence,
+                ),
+                "/chatto.api.v1.MyAccountService/UpdateCustomStatus": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateCustomStatus",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+                        output=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_custom_status,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteCustomStatus": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteCustomStatus",
+                        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,
+                    ),
+                    function=service.delete_custom_status,
+                ),
+                "/chatto.api.v1.MyAccountService/RequestAccountDeletion": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="RequestAccountDeletion",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.request_account_deletion,
+                ),
+                "/chatto.api.v1.MyAccountService/DeleteMyAccount": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteMyAccount",
+                        service_name="chatto.api.v1.MyAccountService",
+                        input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+                        output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_my_account,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.MyAccountService"
+
+
+class MyAccountServiceClientSync(ConnectClientSync):
+    def update_profile(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateProfile",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateProfileResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def upload_avatar(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadAvatar",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UploadAvatarResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_avatar(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteAvatarResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteAvatar",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_password(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdatePassword",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdatePasswordResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_settings(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateSettings",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.UpdateSettingsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_external_identities(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListExternalIdentities",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.ListExternalIdentitiesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def start_external_identity_link(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="StartExternalIdentityLink",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.StartExternalIdentityLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def disconnect_external_identity(
+        self,
+        request: chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DisconnectExternalIdentity",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityRequest,
+                output=chatto_dot_api_dot_v1_dot_external__identities__pb2.DisconnectExternalIdentityResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_presence(
+        self,
+        request: chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdatePresence",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceRequest,
+                output=chatto_dot_api_dot_v1_dot_presence__pb2.UpdatePresenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_custom_status(
+        self,
+        request: chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateCustomStatus",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusRequest,
+                output=chatto_dot_api_dot_v1_dot_user__status__pb2.UpdateCustomStatusResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_custom_status(
+        self,
+        request: chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_user__status__pb2.DeleteCustomStatusResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteCustomStatus",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def request_account_deletion(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RequestAccountDeletion",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.RequestAccountDeletionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_my_account(
+        self,
+        request: chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteMyAccount",
+                service_name="chatto.api.v1.MyAccountService",
+                input=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountRequest,
+                output=chatto_dot_api_dot_v1_dot_account__pb2.DeleteMyAccountResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 33 - 0
src/chattolib/_pb/chatto/api/v1/account_pb2.py


+ 383 - 0
src/chattolib/_pb/chatto/api/v1/asset_uploads_connect.py

@@ -0,0 +1,383 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/asset_uploads.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.asset_uploads_pb2 as chatto_dot_api_dot_v1_dot_asset__uploads__pb2
+
+
+class AssetUploadService(Protocol):
+    async def create_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def upload_chunk(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def complete_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def cancel_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AssetUploadServiceASGIApplication(ConnectASGIApplication[AssetUploadService]):
+    def __init__(self, service: AssetUploadService | AsyncGenerator[AssetUploadService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.AssetUploadService/CreateUpload": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/UploadChunk": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UploadChunk",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.upload_chunk,
+                ),
+                "/chatto.api.v1.AssetUploadService/GetUpload": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/CompleteUpload": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CompleteUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.complete_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/CancelUpload": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CancelUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.cancel_upload,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.AssetUploadService"
+
+
+class AssetUploadServiceClient(ConnectClient):
+    async def create_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def upload_chunk(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadChunk",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def complete_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CompleteUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def cancel_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CancelUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AssetUploadServiceSync(Protocol):
+    def create_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def upload_chunk(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def complete_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def cancel_upload(self, request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AssetUploadServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AssetUploadServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.AssetUploadService/CreateUpload": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/UploadChunk": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UploadChunk",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.upload_chunk,
+                ),
+                "/chatto.api.v1.AssetUploadService/GetUpload": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/CompleteUpload": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CompleteUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.complete_upload,
+                ),
+                "/chatto.api.v1.AssetUploadService/CancelUpload": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CancelUpload",
+                        service_name="chatto.api.v1.AssetUploadService",
+                        input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+                        output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.cancel_upload,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.AssetUploadService"
+
+
+class AssetUploadServiceClientSync(ConnectClientSync):
+    def create_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CreateUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def upload_chunk(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UploadChunk",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.UploadChunkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.GetUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def complete_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CompleteUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CompleteUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def cancel_upload(
+        self,
+        request: chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CancelUpload",
+                service_name="chatto.api.v1.AssetUploadService",
+                input=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadRequest,
+                output=chatto_dot_api_dot_v1_dot_asset__uploads__pb2.CancelUploadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29 - 0
src/chattolib/_pb/chatto/api/v1/asset_uploads_pb2.py


+ 188 - 0
src/chattolib/_pb/chatto/api/v1/attachments_connect.py

@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/attachments.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.attachments_pb2 as chatto_dot_api_dot_v1_dot_attachments__pb2
+
+
+class AssetService(Protocol):
+    async def get_asset(self, request: chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_assets(self, request: chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AssetServiceASGIApplication(ConnectASGIApplication[AssetService]):
+    def __init__(self, service: AssetService | AsyncGenerator[AssetService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.AssetService/GetAsset": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetAsset",
+                        service_name="chatto.api.v1.AssetService",
+                        input=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+                        output=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_asset,
+                ),
+                "/chatto.api.v1.AssetService/BatchGetAssets": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetAssets",
+                        service_name="chatto.api.v1.AssetService",
+                        input=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+                        output=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_assets,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.AssetService"
+
+
+class AssetServiceClient(ConnectClient):
+    async def get_asset(
+        self,
+        request: chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetAsset",
+                service_name="chatto.api.v1.AssetService",
+                input=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+                output=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_assets(
+        self,
+        request: chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetAssets",
+                service_name="chatto.api.v1.AssetService",
+                input=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+                output=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class AssetServiceSync(Protocol):
+    def get_asset(self, request: chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_assets(self, request: chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class AssetServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: AssetServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.AssetService/GetAsset": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetAsset",
+                        service_name="chatto.api.v1.AssetService",
+                        input=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+                        output=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_asset,
+                ),
+                "/chatto.api.v1.AssetService/BatchGetAssets": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetAssets",
+                        service_name="chatto.api.v1.AssetService",
+                        input=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+                        output=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_assets,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.AssetService"
+
+
+class AssetServiceClientSync(ConnectClientSync):
+    def get_asset(
+        self,
+        request: chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetAsset",
+                service_name="chatto.api.v1.AssetService",
+                input=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetRequest,
+                output=chatto_dot_api_dot_v1_dot_attachments__pb2.GetAssetResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_assets(
+        self,
+        request: chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetAssets",
+                service_name="chatto.api.v1.AssetService",
+                input=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsRequest,
+                output=chatto_dot_api_dot_v1_dot_attachments__pb2.BatchGetAssetsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
src/chattolib/_pb/chatto/api/v1/attachments_pb2.py


+ 49 - 0
src/chattolib/_pb/chatto/api/v1/common_pb2.py

@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/common.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/common.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x63hatto/api/v1/common.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\"\x82\x01\n\x15ImageTransformOptions\x12\x19\n\x05width\x18\x01 \x01(\x05\x42\n\xbaH\x07\x1a\x05\x18\x80 (\x01\x12\x1a\n\x06height\x18\x02 \x01(\x05\x42\n\xbaH\x07\x1a\x05\x18\x80 (\x01\x12\x32\n\x03\x66it\x18\x03 \x01(\x0e\x32\x1b.chatto.api.v1.ImageFitModeB\x08\xbaH\x05\x82\x01\x02\x10\x01\"D\n\x0bImageUpload\x12\r\n\x05image\x18\x01 \x01(\x0c\x12\x10\n\x08\x66ilename\x18\x02 \x01(\t\x12\x14\n\x0c\x63ontent_type\x18\x03 \x01(\t\"N\n\x10ProviderMetadata\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04type\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x11\n\tlogin_url\x18\x04 \x01(\t*d\n\x0cImageFitMode\x12\x1e\n\x1aIMAGE_FIT_MODE_UNSPECIFIED\x10\x00\x12\x1a\n\x16IMAGE_FIT_MODE_CONTAIN\x10\x01\x12\x18\n\x14IMAGE_FIT_MODE_COVER\x10\x02\x62\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.common_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['width']._loaded_options = None
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['width']._serialized_options = b'\272H\007\032\005\030\200 (\001'
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['height']._loaded_options = None
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['height']._serialized_options = b'\272H\007\032\005\030\200 (\001'
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['fit']._loaded_options = None
+  _globals['_IMAGETRANSFORMOPTIONS'].fields_by_name['fit']._serialized_options = b'\272H\005\202\001\002\020\001'
+  _globals['_IMAGEFITMODE']._serialized_start=357
+  _globals['_IMAGEFITMODE']._serialized_end=457
+  _globals['_IMAGETRANSFORMOPTIONS']._serialized_start=75
+  _globals['_IMAGETRANSFORMOPTIONS']._serialized_end=205
+  _globals['_IMAGEUPLOAD']._serialized_start=207
+  _globals['_IMAGEUPLOAD']._serialized_end=275
+  _globals['_PROVIDERMETADATA']._serialized_start=277
+  _globals['_PROVIDERMETADATA']._serialized_end=355
+# @@protoc_insertion_point(module_scope)

+ 56 - 0
src/chattolib/_pb/chatto/api/v1/external_identities_pb2.py

@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/external_identities.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/external_identities.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+from chatto.api.v1 import common_pb2 as chatto_dot_api_dot_v1_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\'chatto/api/v1/external_identities.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\x1a\x1a\x63hatto/api/v1/common.proto\"\xb7\x01\n\x18\x45xternalIdentityProvider\x12\x10\n\x08link_url\x18\x05 \x01(\t\x12\x0e\n\x06linked\x18\x06 \x01(\x08\x12$\n\x1clinked_identity_subject_hash\x18\x07 \x01(\t\x12\x31\n\x08provider\x18\x08 \x01(\x0b\x32\x1f.chatto.api.v1.ProviderMetadataJ\x04\x08\x01\x10\x05R\x02idR\x04typeR\x05labelR\tlogin_url\"r\n\x16LinkedExternalIdentity\x12\x13\n\x0bprovider_id\x18\x01 \x01(\t\x12\x15\n\rprovider_type\x18\x02 \x01(\t\x12\x16\n\x0eprovider_label\x18\x03 \x01(\t\x12\x14\n\x0csubject_hash\x18\x04 \x01(\t\"\x1f\n\x1dListExternalIdentitiesRequest\"\x9e\x01\n\x1eListExternalIdentitiesResponse\x12:\n\tproviders\x18\x01 \x03(\x0b\x32\'.chatto.api.v1.ExternalIdentityProvider\x12@\n\x11linked_identities\x18\x02 \x03(\x0b\x32%.chatto.api.v1.LinkedExternalIdentity\"q\n StartExternalIdentityLinkRequest\x12\x1c\n\x0bprovider_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x15\n\rredirect_path\x18\x02 \x01(\t\x12\x18\n\x10\x63urrent_password\x18\x03 \x01(\t\"6\n!StartExternalIdentityLinkResponse\x12\x11\n\tstart_url\x18\x01 \x01(\t\"\\\n!DisconnectExternalIdentityRequest\x12\x1d\n\x0csubject_hash\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x18\n\x10\x63urrent_password\x18\x02 \x01(\t\":\n\"DisconnectExternalIdentityResponse\x12\x14\n\x0c\x64isconnected\x18\x01 \x01(\x08\x62\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.external_identities_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_STARTEXTERNALIDENTITYLINKREQUEST'].fields_by_name['provider_id']._loaded_options = None
+  _globals['_STARTEXTERNALIDENTITYLINKREQUEST'].fields_by_name['provider_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_DISCONNECTEXTERNALIDENTITYREQUEST'].fields_by_name['subject_hash']._loaded_options = None
+  _globals['_DISCONNECTEXTERNALIDENTITYREQUEST'].fields_by_name['subject_hash']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_EXTERNALIDENTITYPROVIDER']._serialized_start=116
+  _globals['_EXTERNALIDENTITYPROVIDER']._serialized_end=299
+  _globals['_LINKEDEXTERNALIDENTITY']._serialized_start=301
+  _globals['_LINKEDEXTERNALIDENTITY']._serialized_end=415
+  _globals['_LISTEXTERNALIDENTITIESREQUEST']._serialized_start=417
+  _globals['_LISTEXTERNALIDENTITIESREQUEST']._serialized_end=448
+  _globals['_LISTEXTERNALIDENTITIESRESPONSE']._serialized_start=451
+  _globals['_LISTEXTERNALIDENTITIESRESPONSE']._serialized_end=609
+  _globals['_STARTEXTERNALIDENTITYLINKREQUEST']._serialized_start=611
+  _globals['_STARTEXTERNALIDENTITYLINKREQUEST']._serialized_end=724
+  _globals['_STARTEXTERNALIDENTITYLINKRESPONSE']._serialized_start=726
+  _globals['_STARTEXTERNALIDENTITYLINKRESPONSE']._serialized_end=780
+  _globals['_DISCONNECTEXTERNALIDENTITYREQUEST']._serialized_start=782
+  _globals['_DISCONNECTEXTERNALIDENTITYREQUEST']._serialized_end=874
+  _globals['_DISCONNECTEXTERNALIDENTITYRESPONSE']._serialized_start=876
+  _globals['_DISCONNECTEXTERNALIDENTITYRESPONSE']._serialized_end=934
+# @@protoc_insertion_point(module_scope)

+ 43 - 0
src/chattolib/_pb/chatto/api/v1/link_previews_pb2.py

@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/link_previews.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/link_previews.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!chatto/api/v1/link_previews.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\"2\n\x17\x46\x65tchLinkPreviewRequest\x12\x17\n\x03url\x18\x01 \x01(\tB\n\xbaH\x07r\x05\x10\x01\x18\x80\x10\"\xaa\x02\n\x0bLinkPreview\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\x05title\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x16\n\timage_url\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0eimage_asset_id\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x16\n\tsite_name\x18\x06 \x01(\tH\x04\x88\x01\x01\x12\x17\n\nembed_type\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08\x65mbed_id\x18\x08 \x01(\tH\x06\x88\x01\x01\x42\x08\n\x06_titleB\x0e\n\x0c_descriptionB\x0c\n\n_image_urlB\x11\n\x0f_image_asset_idB\x0c\n\n_site_nameB\r\n\x0b_embed_typeB\x0b\n\t_embed_id\"^\n\x18\x46\x65tchLinkPreviewResponse\x12+\n\x07preview\x18\x01 \x01(\x0b\x32\x1a.chatto.api.v1.LinkPreview\x12\x15\n\rpreview_token\x18\x02 \x01(\tb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.link_previews_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_FETCHLINKPREVIEWREQUEST'].fields_by_name['url']._loaded_options = None
+  _globals['_FETCHLINKPREVIEWREQUEST'].fields_by_name['url']._serialized_options = b'\272H\007r\005\020\001\030\200\020'
+  _globals['_FETCHLINKPREVIEWREQUEST']._serialized_start=81
+  _globals['_FETCHLINKPREVIEWREQUEST']._serialized_end=131
+  _globals['_LINKPREVIEW']._serialized_start=134
+  _globals['_LINKPREVIEW']._serialized_end=432
+  _globals['_FETCHLINKPREVIEWRESPONSE']._serialized_start=434
+  _globals['_FETCHLINKPREVIEWRESPONSE']._serialized_end=528
+# @@protoc_insertion_point(module_scope)

+ 253 - 0
src/chattolib/_pb/chatto/api/v1/member_directory_connect.py

@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/member_directory.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.member_directory_pb2 as chatto_dot_api_dot_v1_dot_member__directory__pb2
+
+
+class UserService(Protocol):
+    async def list_users(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_user(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_users(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class UserServiceASGIApplication(ConnectASGIApplication[UserService]):
+    def __init__(self, service: UserService | AsyncGenerator[UserService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.UserService/ListUsers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListUsers",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_users,
+                ),
+                "/chatto.api.v1.UserService/GetUser": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetUser",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_user,
+                ),
+                "/chatto.api.v1.UserService/BatchGetUsers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetUsers",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_users,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.UserService"
+
+
+class UserServiceClient(ConnectClient):
+    async def list_users(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListUsers",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_user(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUser",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_users(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetUsers",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class UserServiceSync(Protocol):
+    def list_users(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_user(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_users(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class UserServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: UserServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.UserService/ListUsers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListUsers",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_users,
+                ),
+                "/chatto.api.v1.UserService/GetUser": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetUser",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_user,
+                ),
+                "/chatto.api.v1.UserService/BatchGetUsers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetUsers",
+                        service_name="chatto.api.v1.UserService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_users,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.UserService"
+
+
+class UserServiceClientSync(ConnectClientSync):
+    def list_users(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListUsers",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListUsersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_user(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetUser",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetUserResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_users(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetUsers",
+                service_name="chatto.api.v1.UserService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetUsersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
src/chattolib/_pb/chatto/api/v1/member_directory_pb2.py


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 28 - 0
src/chattolib/_pb/chatto/api/v1/message_types_pb2.py


+ 710 - 0
src/chattolib/_pb/chatto/api/v1/messages_connect.py

@@ -0,0 +1,710 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/messages.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.link_previews_pb2 as chatto_dot_api_dot_v1_dot_link__previews__pb2
+import chatto.api.v1.messages_pb2 as chatto_dot_api_dot_v1_dot_messages__pb2
+import chatto.api.v1.reactions_pb2 as chatto_dot_api_dot_v1_dot_reactions__pb2
+
+
+class MessageService(Protocol):
+    async def fetch_link_preview(self, request: chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def create_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_attachment(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def delete_link_preview(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_messages(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def add_reaction(self, request: chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def remove_reaction(self, request: chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class MessageServiceASGIApplication(ConnectASGIApplication[MessageService]):
+    def __init__(self, service: MessageService | AsyncGenerator[MessageService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.MessageService/FetchLinkPreview": Endpoint.unary(
+                    method=MethodInfo(
+                        name="FetchLinkPreview",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+                        output=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.fetch_link_preview,
+                ),
+                "/chatto.api.v1.MessageService/CreateMessage": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_message,
+                ),
+                "/chatto.api.v1.MessageService/UpdateMessage": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_message,
+                ),
+                "/chatto.api.v1.MessageService/DeleteMessage": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_message,
+                ),
+                "/chatto.api.v1.MessageService/DeleteAttachment": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteAttachment",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_attachment,
+                ),
+                "/chatto.api.v1.MessageService/DeleteLinkPreview": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DeleteLinkPreview",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.delete_link_preview,
+                ),
+                "/chatto.api.v1.MessageService/GetMessage": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_message,
+                ),
+                "/chatto.api.v1.MessageService/BatchGetMessages": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetMessages",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_messages,
+                ),
+                "/chatto.api.v1.MessageService/AddReaction": Endpoint.unary(
+                    method=MethodInfo(
+                        name="AddReaction",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+                        output=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.add_reaction,
+                ),
+                "/chatto.api.v1.MessageService/RemoveReaction": Endpoint.unary(
+                    method=MethodInfo(
+                        name="RemoveReaction",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+                        output=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.remove_reaction,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.MessageService"
+
+
+class MessageServiceClient(ConnectClient):
+    async def fetch_link_preview(
+        self,
+        request: chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="FetchLinkPreview",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+                output=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def create_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_attachment(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteAttachment",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def delete_link_preview(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteLinkPreview",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_messages(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMessages",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def add_reaction(
+        self,
+        request: chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AddReaction",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+                output=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def remove_reaction(
+        self,
+        request: chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RemoveReaction",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+                output=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class MessageServiceSync(Protocol):
+    def fetch_link_preview(self, request: chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def create_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_attachment(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def delete_link_preview(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_message(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_messages(self, request: chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def add_reaction(self, request: chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def remove_reaction(self, request: chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class MessageServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: MessageServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.MessageService/FetchLinkPreview": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="FetchLinkPreview",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+                        output=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.fetch_link_preview,
+                ),
+                "/chatto.api.v1.MessageService/CreateMessage": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_message,
+                ),
+                "/chatto.api.v1.MessageService/UpdateMessage": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_message,
+                ),
+                "/chatto.api.v1.MessageService/DeleteMessage": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_message,
+                ),
+                "/chatto.api.v1.MessageService/DeleteAttachment": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteAttachment",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_attachment,
+                ),
+                "/chatto.api.v1.MessageService/DeleteLinkPreview": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DeleteLinkPreview",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.delete_link_preview,
+                ),
+                "/chatto.api.v1.MessageService/GetMessage": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetMessage",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_message,
+                ),
+                "/chatto.api.v1.MessageService/BatchGetMessages": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetMessages",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+                        output=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_messages,
+                ),
+                "/chatto.api.v1.MessageService/AddReaction": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="AddReaction",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+                        output=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.add_reaction,
+                ),
+                "/chatto.api.v1.MessageService/RemoveReaction": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="RemoveReaction",
+                        service_name="chatto.api.v1.MessageService",
+                        input=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+                        output=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.remove_reaction,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.MessageService"
+
+
+class MessageServiceClientSync(ConnectClientSync):
+    def fetch_link_preview(
+        self,
+        request: chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="FetchLinkPreview",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewRequest,
+                output=chatto_dot_api_dot_v1_dot_link__previews__pb2.FetchLinkPreviewResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def create_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.CreateMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.UpdateMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_attachment(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteAttachment",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteAttachmentResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def delete_link_preview(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DeleteLinkPreview",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.DeleteLinkPreviewResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_message(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMessage",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.GetMessageResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_messages(
+        self,
+        request: chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMessages",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesRequest,
+                output=chatto_dot_api_dot_v1_dot_messages__pb2.BatchGetMessagesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def add_reaction(
+        self,
+        request: chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AddReaction",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionRequest,
+                output=chatto_dot_api_dot_v1_dot_reactions__pb2.AddReactionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def remove_reaction(
+        self,
+        request: chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RemoveReaction",
+                service_name="chatto.api.v1.MessageService",
+                input=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionRequest,
+                output=chatto_dot_api_dot_v1_dot_reactions__pb2.RemoveReactionResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
src/chattolib/_pb/chatto/api/v1/messages_pb2.py


+ 318 - 0
src/chattolib/_pb/chatto/api/v1/notification_preferences_connect.py

@@ -0,0 +1,318 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/notification_preferences.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.notification_preferences_pb2 as chatto_dot_api_dot_v1_dot_notification__preferences__pb2
+
+
+class NotificationPreferencesService(Protocol):
+    async def get_server_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_server_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_room_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_room_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class NotificationPreferencesServiceASGIApplication(ConnectASGIApplication[NotificationPreferencesService]):
+    def __init__(self, service: NotificationPreferencesService | AsyncGenerator[NotificationPreferencesService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.NotificationPreferencesService/GetServerNotificationPreference": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetServerNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_server_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/UpdateServerNotificationPreference": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateServerNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_server_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/GetRoomNotificationPreference": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRoomNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_room_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/UpdateRoomNotificationPreference": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateRoomNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_room_notification_preference,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.NotificationPreferencesService"
+
+
+class NotificationPreferencesServiceClient(ConnectClient):
+    async def get_server_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_server_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateServerNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_room_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_room_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoomNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class NotificationPreferencesServiceSync(Protocol):
+    def get_server_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_server_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_room_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_room_notification_preference(self, request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class NotificationPreferencesServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: NotificationPreferencesServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.NotificationPreferencesService/GetServerNotificationPreference": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetServerNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_server_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/UpdateServerNotificationPreference": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateServerNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_server_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/GetRoomNotificationPreference": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRoomNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_room_notification_preference,
+                ),
+                "/chatto.api.v1.NotificationPreferencesService/UpdateRoomNotificationPreference": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateRoomNotificationPreference",
+                        service_name="chatto.api.v1.NotificationPreferencesService",
+                        input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+                        output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_room_notification_preference,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.NotificationPreferencesService"
+
+
+class NotificationPreferencesServiceClientSync(ConnectClientSync):
+    def get_server_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServerNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetServerNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_server_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateServerNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateServerNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_room_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetRoomNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.GetNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_room_notification_preference(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoomNotificationPreference",
+                service_name="chatto.api.v1.NotificationPreferencesService",
+                input=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateRoomNotificationPreferenceRequest,
+                output=chatto_dot_api_dot_v1_dot_notification__preferences__pb2.UpdateNotificationPreferenceResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

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

@@ -0,0 +1,578 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/notifications.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.notifications_pb2 as chatto_dot_api_dot_v1_dot_notifications__pb2
+
+
+class NotificationService(Protocol):
+    async def list_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_notification(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_room_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_room_notification_counts(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def has_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def dismiss_notification(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def dismiss_all_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class NotificationServiceASGIApplication(ConnectASGIApplication[NotificationService]):
+    def __init__(self, service: NotificationService | AsyncGenerator[NotificationService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.NotificationService/ListNotifications": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/GetNotification": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetNotification",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_notification,
+                ),
+                "/chatto.api.v1.NotificationService/BatchGetNotifications": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/ListRoomNotifications": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoomNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_room_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/ListRoomNotificationCounts": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoomNotificationCounts",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_room_notification_counts,
+                ),
+                "/chatto.api.v1.NotificationService/HasNotifications": Endpoint.unary(
+                    method=MethodInfo(
+                        name="HasNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.has_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/DismissNotification": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DismissNotification",
+                        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,
+                    ),
+                    function=svc.dismiss_notification,
+                ),
+                "/chatto.api.v1.NotificationService/DismissAllNotifications": Endpoint.unary(
+                    method=MethodInfo(
+                        name="DismissAllNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.dismiss_all_notifications,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.NotificationService"
+
+
+class NotificationServiceClient(ConnectClient):
+    async def list_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetNotification",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_room_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_room_notification_counts(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomNotificationCounts",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def has_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="HasNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def dismiss_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DismissNotification",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def dismiss_all_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DismissAllNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class NotificationServiceSync(Protocol):
+    def list_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_notification(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_room_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_room_notification_counts(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def has_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def dismiss_notification(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def dismiss_all_notifications(self, request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class NotificationServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: NotificationServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.NotificationService/ListNotifications": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/GetNotification": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetNotification",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_notification,
+                ),
+                "/chatto.api.v1.NotificationService/BatchGetNotifications": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/ListRoomNotifications": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoomNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_room_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/ListRoomNotificationCounts": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoomNotificationCounts",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_room_notification_counts,
+                ),
+                "/chatto.api.v1.NotificationService/HasNotifications": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="HasNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.has_notifications,
+                ),
+                "/chatto.api.v1.NotificationService/DismissNotification": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DismissNotification",
+                        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,
+                    ),
+                    function=service.dismiss_notification,
+                ),
+                "/chatto.api.v1.NotificationService/DismissAllNotifications": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="DismissAllNotifications",
+                        service_name="chatto.api.v1.NotificationService",
+                        input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+                        output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.dismiss_all_notifications,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.NotificationService"
+
+
+class NotificationServiceClientSync(ConnectClientSync):
+    def list_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetNotification",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.GetNotificationResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.BatchGetNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_room_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_room_notification_counts(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomNotificationCounts",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.ListRoomNotificationCountsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def has_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="HasNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.HasNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def dismiss_notification(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissNotificationResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DismissNotification",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def dismiss_all_notifications(
+        self,
+        request: chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="DismissAllNotifications",
+                service_name="chatto.api.v1.NotificationService",
+                input=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsRequest,
+                output=chatto_dot_api_dot_v1_dot_notifications__pb2.DismissAllNotificationsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 31 - 0
src/chattolib/_pb/chatto/api/v1/notifications_pb2.py


+ 43 - 0
src/chattolib/_pb/chatto/api/v1/pagination_pb2.py

@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/pagination.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/pagination.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63hatto/api/v1/pagination.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\"A\n\x0bPageRequest\x12\x19\n\x05limit\x18\x01 \x01(\x05\x42\n\xbaH\x07\x1a\x05\x18\xf4\x03(\x00\x12\x17\n\x06offset\x18\x02 \x01(\x05\x42\x07\xbaH\x04\x1a\x02(\x00\"1\n\x08PageInfo\x12\x13\n\x0btotal_count\x18\x01 \x01(\x03\x12\x10\n\x08has_more\x18\x02 \x01(\x08\x62\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.pagination_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_PAGEREQUEST'].fields_by_name['limit']._loaded_options = None
+  _globals['_PAGEREQUEST'].fields_by_name['limit']._serialized_options = b'\272H\007\032\005\030\364\003(\000'
+  _globals['_PAGEREQUEST'].fields_by_name['offset']._loaded_options = None
+  _globals['_PAGEREQUEST'].fields_by_name['offset']._serialized_options = b'\272H\004\032\002(\000'
+  _globals['_PAGEREQUEST']._serialized_start=78
+  _globals['_PAGEREQUEST']._serialized_end=143
+  _globals['_PAGEINFO']._serialized_start=145
+  _globals['_PAGEINFO']._serialized_end=194
+# @@protoc_insertion_point(module_scope)

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

@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/push_notifications.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.push_notifications_pb2 as chatto_dot_api_dot_v1_dot_push__notifications__pb2
+
+
+class PushNotificationService(Protocol):
+    async 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")
+
+    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")
+
+
+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:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.PushNotificationService/Subscribe": Endpoint.unary(
+                    method=MethodInfo(
+                        name="Subscribe",
+                        service_name="chatto.api.v1.PushNotificationService",
+                        input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+                        output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.subscribe,
+                ),
+                "/chatto.api.v1.PushNotificationService/Unsubscribe": Endpoint.unary(
+                    method=MethodInfo(
+                        name="Unsubscribe",
+                        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,
+                    ),
+                    function=svc.unsubscribe,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.PushNotificationService"
+
+
+class PushNotificationServiceClient(ConnectClient):
+    async def subscribe(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="Subscribe",
+                service_name="chatto.api.v1.PushNotificationService",
+                input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+                output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def unsubscribe(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="Unsubscribe",
+                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,
+            ),
+            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")
+
+
+class PushNotificationServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: PushNotificationServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.PushNotificationService/Subscribe": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="Subscribe",
+                        service_name="chatto.api.v1.PushNotificationService",
+                        input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+                        output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.subscribe,
+                ),
+                "/chatto.api.v1.PushNotificationService/Unsubscribe": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="Unsubscribe",
+                        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,
+                    ),
+                    function=service.unsubscribe,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.PushNotificationService"
+
+
+class PushNotificationServiceClientSync(ConnectClientSync):
+    def subscribe(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="Subscribe",
+                service_name="chatto.api.v1.PushNotificationService",
+                input=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushRequest,
+                output=chatto_dot_api_dot_v1_dot_push__notifications__pb2.SubscribePushResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def unsubscribe(
+        self,
+        request: chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_push__notifications__pb2.UnsubscribePushResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="Unsubscribe",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 55 - 0
src/chattolib/_pb/chatto/api/v1/push_notifications_pb2.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/push_notifications.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/push_notifications.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__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')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.push_notifications_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['endpoint']._loaded_options = None
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['endpoint']._serialized_options = b'\272H\007r\005\020\001\030\200 '
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['p256dh']._loaded_options = None
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['p256dh']._serialized_options = b'\272H\007r\005\020\001\030\200\002'
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['auth']._loaded_options = None
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['auth']._serialized_options = b'\272H\007r\005\020\001\030\200\001'
+  _globals['_SUBSCRIBEPUSHREQUEST'].fields_by_name['user_agent']._loaded_options = None
+  _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
+# @@protoc_insertion_point(module_scope)

+ 56 - 0
src/chattolib/_pb/chatto/api/v1/reactions_pb2.py

@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/reactions.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/reactions.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+from chatto.api.v1 import message_types_pb2 as chatto_dot_api_dot_v1_dot_message__types__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x63hatto/api/v1/reactions.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\x1a!chatto/api/v1/message_types.proto\"i\n\x12\x41\x64\x64ReactionRequest\x12\x18\n\x07room_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12!\n\x10message_event_id\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x16\n\x05\x65moji\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\"V\n\x13\x41\x64\x64ReactionResponse\x12\r\n\x05\x61\x64\x64\x65\x64\x18\x01 \x01(\x08\x12\x30\n\x08reaction\x18\x02 \x01(\x0b\x32\x1e.chatto.api.v1.MessageReaction\"l\n\x15RemoveReactionRequest\x12\x18\n\x07room_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12!\n\x10message_event_id\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x16\n\x05\x65moji\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\"[\n\x16RemoveReactionResponse\x12\x0f\n\x07removed\x18\x01 \x01(\x08\x12\x30\n\x08reaction\x18\x02 \x01(\x0b\x32\x1e.chatto.api.v1.MessageReactionb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.reactions_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['room_id']._loaded_options = None
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['room_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['message_event_id']._loaded_options = None
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['message_event_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['emoji']._loaded_options = None
+  _globals['_ADDREACTIONREQUEST'].fields_by_name['emoji']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['room_id']._loaded_options = None
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['room_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['message_event_id']._loaded_options = None
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['message_event_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['emoji']._loaded_options = None
+  _globals['_REMOVEREACTIONREQUEST'].fields_by_name['emoji']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_ADDREACTIONREQUEST']._serialized_start=112
+  _globals['_ADDREACTIONREQUEST']._serialized_end=217
+  _globals['_ADDREACTIONRESPONSE']._serialized_start=219
+  _globals['_ADDREACTIONRESPONSE']._serialized_end=305
+  _globals['_REMOVEREACTIONREQUEST']._serialized_start=307
+  _globals['_REMOVEREACTIONREQUEST']._serialized_end=415
+  _globals['_REMOVEREACTIONRESPONSE']._serialized_start=417
+  _globals['_REMOVEREACTIONRESPONSE']._serialized_end=508
+# @@protoc_insertion_point(module_scope)

+ 50 - 0
src/chattolib/_pb/chatto/api/v1/read_state_pb2.py

@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/read_state.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/read_state.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x63hatto/api/v1/read_state.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"I\n\x15MarkRoomAsReadRequest\x12\x18\n\x07room_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x16\n\x0eup_to_event_id\x18\x02 \x01(\t\"\x85\x01\n\x16MarkRoomAsReadResponse\x12\x30\n\x0clast_read_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\x15previous_last_read_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"r\n\x17MarkThreadAsReadRequest\x12\x18\n\x07room_id\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12%\n\x14thread_root_event_id\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\x12\x16\n\x0eup_to_event_id\x18\x03 \x01(\t\"P\n\x18MarkThreadAsReadResponse\x12\x34\n\x10previous_read_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestampb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.read_state_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_MARKROOMASREADREQUEST'].fields_by_name['room_id']._loaded_options = None
+  _globals['_MARKROOMASREADREQUEST'].fields_by_name['room_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_MARKTHREADASREADREQUEST'].fields_by_name['room_id']._loaded_options = None
+  _globals['_MARKTHREADASREADREQUEST'].fields_by_name['room_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_MARKTHREADASREADREQUEST'].fields_by_name['thread_root_event_id']._loaded_options = None
+  _globals['_MARKTHREADASREADREQUEST'].fields_by_name['thread_root_event_id']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_MARKROOMASREADREQUEST']._serialized_start=111
+  _globals['_MARKROOMASREADREQUEST']._serialized_end=184
+  _globals['_MARKROOMASREADRESPONSE']._serialized_start=187
+  _globals['_MARKROOMASREADRESPONSE']._serialized_end=320
+  _globals['_MARKTHREADASREADREQUEST']._serialized_start=322
+  _globals['_MARKTHREADASREADREQUEST']._serialized_end=436
+  _globals['_MARKTHREADASREADRESPONSE']._serialized_start=438
+  _globals['_MARKTHREADASREADRESPONSE']._serialized_end=518
+# @@protoc_insertion_point(module_scope)

+ 253 - 0
src/chattolib/_pb/chatto/api/v1/roles_connect.py

@@ -0,0 +1,253 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/roles.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.roles_pb2 as chatto_dot_api_dot_v1_dot_roles__pb2
+
+
+class RoleService(Protocol):
+    async def list_roles(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_role(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_roles(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoleServiceASGIApplication(ConnectASGIApplication[RoleService]):
+    def __init__(self, service: RoleService | AsyncGenerator[RoleService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.RoleService/ListRoles": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoles",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_roles,
+                ),
+                "/chatto.api.v1.RoleService/GetRole": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRole",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_role,
+                ),
+                "/chatto.api.v1.RoleService/BatchGetRoles": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetRoles",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_roles,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoleService"
+
+
+class RoleServiceClient(ConnectClient):
+    async def list_roles(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoles",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_role(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRole",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_roles(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRoles",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class RoleServiceSync(Protocol):
+    def list_roles(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_role(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_roles(self, request: chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoleServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: RoleServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.RoleService/ListRoles": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoles",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_roles,
+                ),
+                "/chatto.api.v1.RoleService/GetRole": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRole",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_role,
+                ),
+                "/chatto.api.v1.RoleService/BatchGetRoles": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetRoles",
+                        service_name="chatto.api.v1.RoleService",
+                        input=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+                        output=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_roles,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoleService"
+
+
+class RoleServiceClientSync(ConnectClientSync):
+    def list_roles(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoles",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.ListRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_role(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRole",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.GetRoleResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_roles(
+        self,
+        request: chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRoles",
+                service_name="chatto.api.v1.RoleService",
+                input=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesRequest,
+                output=chatto_dot_api_dot_v1_dot_roles__pb2.BatchGetRolesResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 55 - 0
src/chattolib/_pb/chatto/api/v1/roles_pb2.py

@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/roles.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/roles.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from buf.validate import validate_pb2 as buf_dot_validate_dot_validate__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x63hatto/api/v1/roles.proto\x12\rchatto.api.v1\x1a\x1b\x62uf/validate/validate.proto\"v\n\x04Role\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x64isplay_name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x11\n\tis_system\x18\x04 \x01(\x08\x12\x10\n\x08position\x18\x05 \x01(\x05\x12\x10\n\x08pingable\x18\x06 \x01(\x08\"\x12\n\x10ListRolesRequest\"7\n\x11ListRolesResponse\x12\"\n\x05roles\x18\x01 \x03(\x0b\x32\x13.chatto.api.v1.Role\"\'\n\x0eGetRoleRequest\x12\x15\n\x04name\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01\"4\n\x0fGetRoleResponse\x12!\n\x04role\x18\x01 \x01(\x0b\x32\x13.chatto.api.v1.Role\"7\n\x14\x42\x61tchGetRolesRequest\x12\x1f\n\x05names\x18\x01 \x03(\tB\x10\xbaH\r\x92\x01\n\x08\x01\x10\x64\"\x04r\x02\x10\x01\";\n\x15\x42\x61tchGetRolesResponse\x12\"\n\x05roles\x18\x01 \x03(\x0b\x32\x13.chatto.api.v1.Role2\x83\x02\n\x0bRoleService\x12N\n\tListRoles\x12\x1f.chatto.api.v1.ListRolesRequest\x1a .chatto.api.v1.ListRolesResponse\x12H\n\x07GetRole\x12\x1d.chatto.api.v1.GetRoleRequest\x1a\x1e.chatto.api.v1.GetRoleResponse\x12Z\n\rBatchGetRoles\x12#.chatto.api.v1.BatchGetRolesRequest\x1a$.chatto.api.v1.BatchGetRolesResponseb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.roles_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_GETROLEREQUEST'].fields_by_name['name']._loaded_options = None
+  _globals['_GETROLEREQUEST'].fields_by_name['name']._serialized_options = b'\272H\004r\002\020\001'
+  _globals['_BATCHGETROLESREQUEST'].fields_by_name['names']._loaded_options = None
+  _globals['_BATCHGETROLESREQUEST'].fields_by_name['names']._serialized_options = b'\272H\r\222\001\n\010\001\020d\"\004r\002\020\001'
+  _globals['_ROLE']._serialized_start=73
+  _globals['_ROLE']._serialized_end=191
+  _globals['_LISTROLESREQUEST']._serialized_start=193
+  _globals['_LISTROLESREQUEST']._serialized_end=211
+  _globals['_LISTROLESRESPONSE']._serialized_start=213
+  _globals['_LISTROLESRESPONSE']._serialized_end=268
+  _globals['_GETROLEREQUEST']._serialized_start=270
+  _globals['_GETROLEREQUEST']._serialized_end=309
+  _globals['_GETROLERESPONSE']._serialized_start=311
+  _globals['_GETROLERESPONSE']._serialized_end=363
+  _globals['_BATCHGETROLESREQUEST']._serialized_start=365
+  _globals['_BATCHGETROLESREQUEST']._serialized_end=420
+  _globals['_BATCHGETROLESRESPONSE']._serialized_start=422
+  _globals['_BATCHGETROLESRESPONSE']._serialized_end=481
+  _globals['_ROLESERVICE']._serialized_start=484
+  _globals['_ROLESERVICE']._serialized_end=743
+# @@protoc_insertion_point(module_scope)

+ 448 - 0
src/chattolib/_pb/chatto/api/v1/room_directory_connect.py

@@ -0,0 +1,448 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/room_directory.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.room_directory_pb2 as chatto_dot_api_dot_v1_dot_room__directory__pb2
+
+
+class RoomDirectoryService(Protocol):
+    async def list_rooms(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_room_groups(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_room_group(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_room_groups(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_room(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_rooms(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoomDirectoryServiceASGIApplication(ConnectASGIApplication[RoomDirectoryService]):
+    def __init__(self, service: RoomDirectoryService | AsyncGenerator[RoomDirectoryService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.RoomDirectoryService/ListRooms": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRooms",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_rooms,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/ListRoomGroups": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoomGroups",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_room_groups,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/GetRoomGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRoomGroup",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_room_group,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/BatchGetRoomGroups": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetRoomGroups",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_room_groups,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/GetRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRoom",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_room,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/BatchGetRooms": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetRooms",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_rooms,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoomDirectoryService"
+
+
+class RoomDirectoryServiceClient(ConnectClient):
+    async def list_rooms(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRooms",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_room_groups(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomGroups",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_room_group(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomGroup",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_room_groups(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRoomGroups",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoom",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_rooms(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRooms",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class RoomDirectoryServiceSync(Protocol):
+    def list_rooms(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_room_groups(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_room_group(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_room_groups(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_room(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_rooms(self, request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoomDirectoryServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: RoomDirectoryServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.RoomDirectoryService/ListRooms": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRooms",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_rooms,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/ListRoomGroups": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoomGroups",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_room_groups,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/GetRoomGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRoomGroup",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_room_group,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/BatchGetRoomGroups": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetRoomGroups",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_room_groups,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/GetRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRoom",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_room,
+                ),
+                "/chatto.api.v1.RoomDirectoryService/BatchGetRooms": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetRooms",
+                        service_name="chatto.api.v1.RoomDirectoryService",
+                        input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_rooms,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoomDirectoryService"
+
+
+class RoomDirectoryServiceClientSync(ConnectClientSync):
+    def list_rooms(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRooms",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_room_groups(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomGroups",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.ListRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_room_group(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomGroup",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_room_groups(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRoomGroups",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomGroupsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoom",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.GetRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_rooms(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetRooms",
+                service_name="chatto.api.v1.RoomDirectoryService",
+                input=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__directory__pb2.BatchGetRoomsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 29 - 0
src/chattolib/_pb/chatto/api/v1/room_directory_pb2.py


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
src/chattolib/_pb/chatto/api/v1/room_timeline_pb2.py


+ 1426 - 0
src/chattolib/_pb/chatto/api/v1/rooms_connect.py

@@ -0,0 +1,1426 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/rooms.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.member_directory_pb2 as chatto_dot_api_dot_v1_dot_member__directory__pb2
+import chatto.api.v1.read_state_pb2 as chatto_dot_api_dot_v1_dot_read__state__pb2
+import chatto.api.v1.room_timeline_pb2 as chatto_dot_api_dot_v1_dot_room__timeline__pb2
+import chatto.api.v1.rooms_pb2 as chatto_dot_api_dot_v1_dot_rooms__pb2
+
+
+class RoomService(Protocol):
+    async def create_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def archive_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def unarchive_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def join_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def join_room_group(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def start_d_m(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def leave_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_members(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_member(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_members(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def add_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def remove_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_bans(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_room_attachments(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def update_typing_indicator(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_room_events(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_room_events_around(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def mark_room_as_read(self, request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def ban_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def unban_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoomServiceASGIApplication(ConnectASGIApplication[RoomService]):
+    def __init__(self, service: RoomService | AsyncGenerator[RoomService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.RoomService/CreateRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_room,
+                ),
+                "/chatto.api.v1.RoomService/UpdateRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_room,
+                ),
+                "/chatto.api.v1.RoomService/ArchiveRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ArchiveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.archive_room,
+                ),
+                "/chatto.api.v1.RoomService/UnarchiveRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UnarchiveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.unarchive_room,
+                ),
+                "/chatto.api.v1.RoomService/JoinRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="JoinRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.join_room,
+                ),
+                "/chatto.api.v1.RoomService/JoinRoomGroup": Endpoint.unary(
+                    method=MethodInfo(
+                        name="JoinRoomGroup",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.join_room_group,
+                ),
+                "/chatto.api.v1.RoomService/StartDM": Endpoint.unary(
+                    method=MethodInfo(
+                        name="StartDM",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.start_d_m,
+                ),
+                "/chatto.api.v1.RoomService/LeaveRoom": Endpoint.unary(
+                    method=MethodInfo(
+                        name="LeaveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.leave_room,
+                ),
+                "/chatto.api.v1.RoomService/ListMembers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListMembers",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_members,
+                ),
+                "/chatto.api.v1.RoomService/GetMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_member,
+                ),
+                "/chatto.api.v1.RoomService/BatchGetMembers": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetMembers",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_members,
+                ),
+                "/chatto.api.v1.RoomService/AddMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="AddMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.add_member,
+                ),
+                "/chatto.api.v1.RoomService/RemoveMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="RemoveMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.remove_member,
+                ),
+                "/chatto.api.v1.RoomService/ListBans": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListBans",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_bans,
+                ),
+                "/chatto.api.v1.RoomService/ListRoomAttachments": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListRoomAttachments",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_room_attachments,
+                ),
+                "/chatto.api.v1.RoomService/UpdateTypingIndicator": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UpdateTypingIndicator",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.update_typing_indicator,
+                ),
+                "/chatto.api.v1.RoomService/GetRoomEvents": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRoomEvents",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_room_events,
+                ),
+                "/chatto.api.v1.RoomService/GetRoomEventsAround": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRoomEventsAround",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_room_events_around,
+                ),
+                "/chatto.api.v1.RoomService/MarkRoomAsRead": Endpoint.unary(
+                    method=MethodInfo(
+                        name="MarkRoomAsRead",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+                        output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.mark_room_as_read,
+                ),
+                "/chatto.api.v1.RoomService/BanMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BanMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.ban_member,
+                ),
+                "/chatto.api.v1.RoomService/UnbanMember": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UnbanMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.unban_member,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoomService"
+
+
+class RoomServiceClient(ConnectClient):
+    async def create_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def archive_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ArchiveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def unarchive_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnarchiveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def join_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def join_room_group(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinRoomGroup",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def start_d_m(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="StartDM",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def leave_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="LeaveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_members(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListMembers",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_members(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMembers",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def add_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AddMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def remove_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RemoveMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_bans(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListBans",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_room_attachments(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomAttachments",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def update_typing_indicator(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateTypingIndicator",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_room_events(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomEvents",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_room_events_around(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomEventsAround",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def mark_room_as_read(
+        self,
+        request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MarkRoomAsRead",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+                output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def ban_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BanMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def unban_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnbanMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class RoomServiceSync(Protocol):
+    def create_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def archive_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def unarchive_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def join_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def join_room_group(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def start_d_m(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def leave_room(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_members(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_member(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_members(self, request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def add_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def remove_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_bans(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_room_attachments(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def update_typing_indicator(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_room_events(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_room_events_around(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def mark_room_as_read(self, request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def ban_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def unban_member(self, request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class RoomServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: RoomServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.RoomService/CreateRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_room,
+                ),
+                "/chatto.api.v1.RoomService/UpdateRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_room,
+                ),
+                "/chatto.api.v1.RoomService/ArchiveRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ArchiveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.archive_room,
+                ),
+                "/chatto.api.v1.RoomService/UnarchiveRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UnarchiveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.unarchive_room,
+                ),
+                "/chatto.api.v1.RoomService/JoinRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="JoinRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.join_room,
+                ),
+                "/chatto.api.v1.RoomService/JoinRoomGroup": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="JoinRoomGroup",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.join_room_group,
+                ),
+                "/chatto.api.v1.RoomService/StartDM": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="StartDM",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.start_d_m,
+                ),
+                "/chatto.api.v1.RoomService/LeaveRoom": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="LeaveRoom",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.leave_room,
+                ),
+                "/chatto.api.v1.RoomService/ListMembers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListMembers",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_members,
+                ),
+                "/chatto.api.v1.RoomService/GetMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_member,
+                ),
+                "/chatto.api.v1.RoomService/BatchGetMembers": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetMembers",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+                        output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_members,
+                ),
+                "/chatto.api.v1.RoomService/AddMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="AddMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.add_member,
+                ),
+                "/chatto.api.v1.RoomService/RemoveMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="RemoveMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.remove_member,
+                ),
+                "/chatto.api.v1.RoomService/ListBans": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListBans",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_bans,
+                ),
+                "/chatto.api.v1.RoomService/ListRoomAttachments": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListRoomAttachments",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_room_attachments,
+                ),
+                "/chatto.api.v1.RoomService/UpdateTypingIndicator": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UpdateTypingIndicator",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.update_typing_indicator,
+                ),
+                "/chatto.api.v1.RoomService/GetRoomEvents": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRoomEvents",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_room_events,
+                ),
+                "/chatto.api.v1.RoomService/GetRoomEventsAround": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRoomEventsAround",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_room_events_around,
+                ),
+                "/chatto.api.v1.RoomService/MarkRoomAsRead": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="MarkRoomAsRead",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+                        output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.mark_room_as_read,
+                ),
+                "/chatto.api.v1.RoomService/BanMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BanMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.ban_member,
+                ),
+                "/chatto.api.v1.RoomService/UnbanMember": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UnbanMember",
+                        service_name="chatto.api.v1.RoomService",
+                        input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+                        output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.unban_member,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.RoomService"
+
+
+class RoomServiceClientSync(ConnectClientSync):
+    def create_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.CreateRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def archive_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ArchiveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ArchiveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def unarchive_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnarchiveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnarchiveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def join_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def join_room_group(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinRoomGroup",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.JoinRoomGroupResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def start_d_m(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="StartDM",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.StartDMResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def leave_room(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="LeaveRoom",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.LeaveRoomResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_members(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListMembers",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.ListRoomMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.GetRoomMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_members(
+        self,
+        request: chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetMembers",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersRequest,
+                output=chatto_dot_api_dot_v1_dot_member__directory__pb2.BatchGetRoomMembersResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def add_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="AddMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.AddMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def remove_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="RemoveMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.RemoveMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_bans(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListBans",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListBansResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_room_attachments(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListRoomAttachments",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.ListRoomAttachmentsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def update_typing_indicator(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UpdateTypingIndicator",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UpdateTypingIndicatorResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_room_events(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomEvents",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_room_events_around(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRoomEventsAround",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetRoomEventsAroundResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def mark_room_as_read(
+        self,
+        request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MarkRoomAsRead",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadRequest,
+                output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkRoomAsReadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def ban_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BanMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.BanMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def unban_member(
+        self,
+        request: chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnbanMember",
+                service_name="chatto.api.v1.RoomService",
+                input=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberRequest,
+                output=chatto_dot_api_dot_v1_dot_rooms__pb2.UnbanMemberResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 34 - 0
src/chattolib/_pb/chatto/api/v1/rooms_pb2.py


+ 39 - 0
src/chattolib/_pb/chatto/api/v1/server_pb2.py

@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/server.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/server.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from chatto.api.v1 import common_pb2 as chatto_dot_api_dot_v1_dot_common__pb2
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1a\x63hatto/api/v1/server.proto\x12\rchatto.api.v1\x1a\x1a\x63hatto/api/v1/common.proto\"\xdc\x01\n\x13ServerPublicProfile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x15\n\x08logo_url\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x17\n\nbanner_url\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1c\n\x0fwelcome_message\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x06 \x01(\tH\x03\x88\x01\x01\x42\x0b\n\t_logo_urlB\r\n\x0b_banner_urlB\x12\n\x10_welcome_messageB\x0e\n\x0c_description\"}\n\x0bServerLogin\x12#\n\x1b\x64irect_registration_enabled\x18\x01 \x01(\x08\x12\x32\n\tproviders\x18\x02 \x03(\x0b\x32\x1f.chatto.api.v1.ProviderMetadata\x12\x15\n\rauthorize_url\x18\x03 \x01(\tb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.server_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_SERVERPUBLICPROFILE']._serialized_start=74
+  _globals['_SERVERPUBLICPROFILE']._serialized_end=294
+  _globals['_SERVERLOGIN']._serialized_start=296
+  _globals['_SERVERLOGIN']._serialized_end=421
+# @@protoc_insertion_point(module_scope)

+ 188 - 0
src/chattolib/_pb/chatto/api/v1/server_state_connect.py

@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/server_state.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.server_state_pb2 as chatto_dot_api_dot_v1_dot_server__state__pb2
+
+
+class ServerService(Protocol):
+    async def get_motd(self, request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_runtime_config(self, request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ServerServiceASGIApplication(ConnectASGIApplication[ServerService]):
+    def __init__(self, service: ServerService | AsyncGenerator[ServerService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.ServerService/GetMotd": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetMotd",
+                        service_name="chatto.api.v1.ServerService",
+                        input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+                        output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_motd,
+                ),
+                "/chatto.api.v1.ServerService/GetRuntimeConfig": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetRuntimeConfig",
+                        service_name="chatto.api.v1.ServerService",
+                        input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+                        output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_runtime_config,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ServerService"
+
+
+class ServerServiceClient(ConnectClient):
+    async def get_motd(
+        self,
+        request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMotd",
+                service_name="chatto.api.v1.ServerService",
+                input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+                output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_runtime_config(
+        self,
+        request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRuntimeConfig",
+                service_name="chatto.api.v1.ServerService",
+                input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+                output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class ServerServiceSync(Protocol):
+    def get_motd(self, request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_runtime_config(self, request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ServerServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: ServerServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.ServerService/GetMotd": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetMotd",
+                        service_name="chatto.api.v1.ServerService",
+                        input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+                        output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_motd,
+                ),
+                "/chatto.api.v1.ServerService/GetRuntimeConfig": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetRuntimeConfig",
+                        service_name="chatto.api.v1.ServerService",
+                        input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+                        output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_runtime_config,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ServerService"
+
+
+class ServerServiceClientSync(ConnectClientSync):
+    def get_motd(
+        self,
+        request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetMotd",
+                service_name="chatto.api.v1.ServerService",
+                input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdRequest,
+                output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetMotdResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_runtime_config(
+        self,
+        request: chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetRuntimeConfig",
+                service_name="chatto.api.v1.ServerService",
+                input=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigRequest,
+                output=chatto_dot_api_dot_v1_dot_server__state__pb2.GetRuntimeConfigResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 46 - 0
src/chattolib/_pb/chatto/api/v1/server_state_pb2.py

@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/api/v1/server_state.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/api/v1/server_state.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+
+
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n chatto/api/v1/server_state.proto\x12\rchatto.api.v1\"\x10\n\x0eGetMotdRequest\"-\n\x0fGetMotdResponse\x12\x11\n\x04motd\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_motd\"\xb9\x02\n\x13ServerRuntimeConfig\x12\"\n\x1apush_notifications_enabled\x18\x01 \x01(\x08\x12\x1d\n\x10vapid_public_key\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0blivekit_url\x18\x03 \x01(\tH\x01\x88\x01\x01\x12 \n\x18video_processing_enabled\x18\x05 \x01(\x08\x12\x17\n\x0fmax_upload_size\x18\x06 \x01(\x03\x12\x1d\n\x15max_video_upload_size\x18\x07 \x01(\x03\x12#\n\x1bmessage_edit_window_seconds\x18\x08 \x01(\x05\x42\x13\n\x11_vapid_public_keyB\x0e\n\x0c_livekit_urlJ\x04\x08\x04\x10\x05R\x1b\x64irect_registration_enabled\"\x19\n\x17GetRuntimeConfigRequest\"O\n\x18GetRuntimeConfigResponse\x12\x33\n\x07runtime\x18\x01 \x01(\x0b\x32\".chatto.api.v1.ServerRuntimeConfig2\xbe\x01\n\rServerService\x12H\n\x07GetMotd\x12\x1d.chatto.api.v1.GetMotdRequest\x1a\x1e.chatto.api.v1.GetMotdResponse\x12\x63\n\x10GetRuntimeConfig\x12&.chatto.api.v1.GetRuntimeConfigRequest\x1a\'.chatto.api.v1.GetRuntimeConfigResponseb\x06proto3')
+
+_globals = globals()
+_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
+_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'chatto.api.v1.server_state_pb2', _globals)
+if not _descriptor._USE_C_DESCRIPTORS:
+  DESCRIPTOR._loaded_options = None
+  _globals['_GETMOTDREQUEST']._serialized_start=51
+  _globals['_GETMOTDREQUEST']._serialized_end=67
+  _globals['_GETMOTDRESPONSE']._serialized_start=69
+  _globals['_GETMOTDRESPONSE']._serialized_end=114
+  _globals['_SERVERRUNTIMECONFIG']._serialized_start=117
+  _globals['_SERVERRUNTIMECONFIG']._serialized_end=430
+  _globals['_GETRUNTIMECONFIGREQUEST']._serialized_start=432
+  _globals['_GETRUNTIMECONFIGREQUEST']._serialized_end=457
+  _globals['_GETRUNTIMECONFIGRESPONSE']._serialized_start=459
+  _globals['_GETRUNTIMECONFIGRESPONSE']._serialized_end=538
+  _globals['_SERVERSERVICE']._serialized_start=541
+  _globals['_SERVERSERVICE']._serialized_end=731
+# @@protoc_insertion_point(module_scope)

+ 450 - 0
src/chattolib/_pb/chatto/api/v1/threads_connect.py

@@ -0,0 +1,450 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/threads.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.read_state_pb2 as chatto_dot_api_dot_v1_dot_read__state__pb2
+import chatto.api.v1.room_timeline_pb2 as chatto_dot_api_dot_v1_dot_room__timeline__pb2
+import chatto.api.v1.threads_pb2 as chatto_dot_api_dot_v1_dot_threads__pb2
+
+
+class ThreadService(Protocol):
+    async def list_followed_threads(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def follow_thread(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def unfollow_thread(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_thread_events(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_thread_events_around(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def mark_thread_as_read(self, request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ThreadServiceASGIApplication(ConnectASGIApplication[ThreadService]):
+    def __init__(self, service: ThreadService | AsyncGenerator[ThreadService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.ThreadService/ListFollowedThreads": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListFollowedThreads",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_followed_threads,
+                ),
+                "/chatto.api.v1.ThreadService/FollowThread": Endpoint.unary(
+                    method=MethodInfo(
+                        name="FollowThread",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.follow_thread,
+                ),
+                "/chatto.api.v1.ThreadService/UnfollowThread": Endpoint.unary(
+                    method=MethodInfo(
+                        name="UnfollowThread",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.unfollow_thread,
+                ),
+                "/chatto.api.v1.ThreadService/GetThreadEvents": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetThreadEvents",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_thread_events,
+                ),
+                "/chatto.api.v1.ThreadService/GetThreadEventsAround": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetThreadEventsAround",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_thread_events_around,
+                ),
+                "/chatto.api.v1.ThreadService/MarkThreadAsRead": Endpoint.unary(
+                    method=MethodInfo(
+                        name="MarkThreadAsRead",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+                        output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.mark_thread_as_read,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ThreadService"
+
+
+class ThreadServiceClient(ConnectClient):
+    async def list_followed_threads(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListFollowedThreads",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def follow_thread(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="FollowThread",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def unfollow_thread(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnfollowThread",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_thread_events(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetThreadEvents",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_thread_events_around(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetThreadEventsAround",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def mark_thread_as_read(
+        self,
+        request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MarkThreadAsRead",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+                output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class ThreadServiceSync(Protocol):
+    def list_followed_threads(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def follow_thread(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def unfollow_thread(self, request: chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_thread_events(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_thread_events_around(self, request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def mark_thread_as_read(self, request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ThreadServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: ThreadServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.ThreadService/ListFollowedThreads": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListFollowedThreads",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_followed_threads,
+                ),
+                "/chatto.api.v1.ThreadService/FollowThread": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="FollowThread",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.follow_thread,
+                ),
+                "/chatto.api.v1.ThreadService/UnfollowThread": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="UnfollowThread",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+                        output=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.unfollow_thread,
+                ),
+                "/chatto.api.v1.ThreadService/GetThreadEvents": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetThreadEvents",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_thread_events,
+                ),
+                "/chatto.api.v1.ThreadService/GetThreadEventsAround": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetThreadEventsAround",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+                        output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_thread_events_around,
+                ),
+                "/chatto.api.v1.ThreadService/MarkThreadAsRead": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="MarkThreadAsRead",
+                        service_name="chatto.api.v1.ThreadService",
+                        input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+                        output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.mark_thread_as_read,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ThreadService"
+
+
+class ThreadServiceClientSync(ConnectClientSync):
+    def list_followed_threads(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListFollowedThreads",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.ListFollowedThreadsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def follow_thread(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="FollowThread",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.FollowThreadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def unfollow_thread(
+        self,
+        request: chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="UnfollowThread",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadRequest,
+                output=chatto_dot_api_dot_v1_dot_threads__pb2.UnfollowThreadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_thread_events(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetThreadEvents",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_thread_events_around(
+        self,
+        request: chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetThreadEventsAround",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundRequest,
+                output=chatto_dot_api_dot_v1_dot_room__timeline__pb2.GetThreadEventsAroundResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def mark_thread_as_read(
+        self,
+        request: chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="MarkThreadAsRead",
+                service_name="chatto.api.v1.ThreadService",
+                input=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadRequest,
+                output=chatto_dot_api_dot_v1_dot_read__state__pb2.MarkThreadAsReadResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 32 - 0
src/chattolib/_pb/chatto/api/v1/threads_pb2.py


+ 123 - 0
src/chattolib/_pb/chatto/api/v1/viewer_connect.py

@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/viewer.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.viewer_pb2 as chatto_dot_api_dot_v1_dot_viewer__pb2
+
+
+class ViewerService(Protocol):
+    async def get_viewer(self, request: chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ViewerServiceASGIApplication(ConnectASGIApplication[ViewerService]):
+    def __init__(self, service: ViewerService | AsyncGenerator[ViewerService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.ViewerService/GetViewer": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetViewer",
+                        service_name="chatto.api.v1.ViewerService",
+                        input=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+                        output=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_viewer,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ViewerService"
+
+
+class ViewerServiceClient(ConnectClient):
+    async def get_viewer(
+        self,
+        request: chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetViewer",
+                service_name="chatto.api.v1.ViewerService",
+                input=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+                output=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class ViewerServiceSync(Protocol):
+    def get_viewer(self, request: chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ViewerServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: ViewerServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.ViewerService/GetViewer": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetViewer",
+                        service_name="chatto.api.v1.ViewerService",
+                        input=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+                        output=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_viewer,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.ViewerService"
+
+
+class ViewerServiceClientSync(ConnectClientSync):
+    def get_viewer(
+        self,
+        request: chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetViewer",
+                service_name="chatto.api.v1.ViewerService",
+                input=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerRequest,
+                output=chatto_dot_api_dot_v1_dot_viewer__pb2.GetViewerResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 513 - 0
src/chattolib/_pb/chatto/api/v1/voice_calls_connect.py

@@ -0,0 +1,513 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/api/v1/voice_calls.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.api.v1.voice_calls_pb2 as chatto_dot_api_dot_v1_dot_voice__calls__pb2
+
+
+class VoiceCallService(Protocol):
+    async def list_active_calls(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_active_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def batch_get_active_calls(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def list_call_participants(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def join_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def get_call_token(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def leave_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class VoiceCallServiceASGIApplication(ConnectASGIApplication[VoiceCallService]):
+    def __init__(self, service: VoiceCallService | AsyncGenerator[VoiceCallService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.api.v1.VoiceCallService/ListActiveCalls": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListActiveCalls",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_active_calls,
+                ),
+                "/chatto.api.v1.VoiceCallService/GetActiveCall": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetActiveCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_active_call,
+                ),
+                "/chatto.api.v1.VoiceCallService/BatchGetActiveCalls": Endpoint.unary(
+                    method=MethodInfo(
+                        name="BatchGetActiveCalls",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.batch_get_active_calls,
+                ),
+                "/chatto.api.v1.VoiceCallService/ListCallParticipants": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ListCallParticipants",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.list_call_participants,
+                ),
+                "/chatto.api.v1.VoiceCallService/JoinCall": Endpoint.unary(
+                    method=MethodInfo(
+                        name="JoinCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.join_call,
+                ),
+                "/chatto.api.v1.VoiceCallService/GetCallToken": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetCallToken",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_call_token,
+                ),
+                "/chatto.api.v1.VoiceCallService/LeaveCall": Endpoint.unary(
+                    method=MethodInfo(
+                        name="LeaveCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.leave_call,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.VoiceCallService"
+
+
+class VoiceCallServiceClient(ConnectClient):
+    async def list_active_calls(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListActiveCalls",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_active_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetActiveCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def batch_get_active_calls(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetActiveCalls",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def list_call_participants(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListCallParticipants",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def join_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def get_call_token(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetCallToken",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def leave_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="LeaveCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class VoiceCallServiceSync(Protocol):
+    def list_active_calls(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_active_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def batch_get_active_calls(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def list_call_participants(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def join_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def get_call_token(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def leave_call(self, request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest, ctx: RequestContext) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class VoiceCallServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: VoiceCallServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.api.v1.VoiceCallService/ListActiveCalls": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListActiveCalls",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_active_calls,
+                ),
+                "/chatto.api.v1.VoiceCallService/GetActiveCall": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetActiveCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_active_call,
+                ),
+                "/chatto.api.v1.VoiceCallService/BatchGetActiveCalls": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="BatchGetActiveCalls",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.batch_get_active_calls,
+                ),
+                "/chatto.api.v1.VoiceCallService/ListCallParticipants": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ListCallParticipants",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.list_call_participants,
+                ),
+                "/chatto.api.v1.VoiceCallService/JoinCall": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="JoinCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.join_call,
+                ),
+                "/chatto.api.v1.VoiceCallService/GetCallToken": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetCallToken",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_call_token,
+                ),
+                "/chatto.api.v1.VoiceCallService/LeaveCall": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="LeaveCall",
+                        service_name="chatto.api.v1.VoiceCallService",
+                        input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+                        output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.leave_call,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.api.v1.VoiceCallService"
+
+
+class VoiceCallServiceClientSync(ConnectClientSync):
+    def list_active_calls(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListActiveCalls",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListActiveCallsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_active_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetActiveCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetActiveCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def batch_get_active_calls(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="BatchGetActiveCalls",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.BatchGetActiveCallsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def list_call_participants(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ListCallParticipants",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.ListCallParticipantsResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def join_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="JoinCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.JoinCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def get_call_token(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetCallToken",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.GetCallTokenResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def leave_call(
+        self,
+        request: chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="LeaveCall",
+                service_name="chatto.api.v1.VoiceCallService",
+                input=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallRequest,
+                output=chatto_dot_api_dot_v1_dot_voice__calls__pb2.LeaveCallResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 30 - 0
src/chattolib/_pb/chatto/api/v1/voice_calls_pb2.py


+ 0 - 0
src/chattolib/_pb/chatto/auth/__init__.py


+ 0 - 0
src/chattolib/_pb/chatto/auth/v1/__init__.py


+ 318 - 0
src/chattolib/_pb/chatto/auth/v1/external_identity_auth_connect.py

@@ -0,0 +1,318 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/auth/v1/external_identity_auth.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.auth.v1.external_identity_auth_pb2 as chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2
+
+
+class ExternalIdentityAuthService(Protocol):
+    async def get_pending_external_identity(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def create_external_identity_account(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def confirm_external_identity_link(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+    async def cancel_external_identity_flow(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ExternalIdentityAuthServiceASGIApplication(ConnectASGIApplication[ExternalIdentityAuthService]):
+    def __init__(self, service: ExternalIdentityAuthService | AsyncGenerator[ExternalIdentityAuthService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.auth.v1.ExternalIdentityAuthService/GetPendingExternalIdentity": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetPendingExternalIdentity",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.get_pending_external_identity,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/CreateExternalIdentityAccount": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CreateExternalIdentityAccount",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.create_external_identity_account,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/ConfirmExternalIdentityLink": Endpoint.unary(
+                    method=MethodInfo(
+                        name="ConfirmExternalIdentityLink",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.confirm_external_identity_link,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/CancelExternalIdentityFlow": Endpoint.unary(
+                    method=MethodInfo(
+                        name="CancelExternalIdentityFlow",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=svc.cancel_external_identity_flow,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.auth.v1.ExternalIdentityAuthService"
+
+
+class ExternalIdentityAuthServiceClient(ConnectClient):
+    async def get_pending_external_identity(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetPendingExternalIdentity",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def create_external_identity_account(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateExternalIdentityAccount",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def confirm_external_identity_link(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ConfirmExternalIdentityLink",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    async def cancel_external_identity_flow(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CancelExternalIdentityFlow",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class ExternalIdentityAuthServiceSync(Protocol):
+    def get_pending_external_identity(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def create_external_identity_account(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def confirm_external_identity_link(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+    def cancel_external_identity_flow(self, request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest, ctx: RequestContext) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ExternalIdentityAuthServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: ExternalIdentityAuthServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.auth.v1.ExternalIdentityAuthService/GetPendingExternalIdentity": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetPendingExternalIdentity",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.get_pending_external_identity,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/CreateExternalIdentityAccount": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CreateExternalIdentityAccount",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.create_external_identity_account,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/ConfirmExternalIdentityLink": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="ConfirmExternalIdentityLink",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.confirm_external_identity_link,
+                ),
+                "/chatto.auth.v1.ExternalIdentityAuthService/CancelExternalIdentityFlow": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="CancelExternalIdentityFlow",
+                        service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                        input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+                        output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse,
+                        idempotency_level=IdempotencyLevel.UNKNOWN,
+                    ),
+                    function=service.cancel_external_identity_flow,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.auth.v1.ExternalIdentityAuthService"
+
+
+class ExternalIdentityAuthServiceClientSync(ConnectClientSync):
+    def get_pending_external_identity(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetPendingExternalIdentity",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.GetPendingExternalIdentityResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def create_external_identity_account(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CreateExternalIdentityAccount",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CreateExternalIdentityAccountResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def confirm_external_identity_link(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="ConfirmExternalIdentityLink",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.ConfirmExternalIdentityLinkResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+    def cancel_external_identity_flow(
+        self,
+        request: chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="CancelExternalIdentityFlow",
+                service_name="chatto.auth.v1.ExternalIdentityAuthService",
+                input=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowRequest,
+                output=chatto_dot_auth_dot_v1_dot_external__identity__auth__pb2.CancelExternalIdentityFlowResponse,
+                idempotency_level=IdempotencyLevel.UNKNOWN,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 28 - 0
src/chattolib/_pb/chatto/auth/v1/external_identity_auth_pb2.py


+ 0 - 0
src/chattolib/_pb/chatto/discovery/__init__.py


+ 0 - 0
src/chattolib/_pb/chatto/discovery/v1/__init__.py


+ 123 - 0
src/chattolib/_pb/chatto/discovery/v1/server_connect.py

@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# Generated by https://github.com/connectrpc/connect-python.  DO NOT EDIT!
+# source: chatto/discovery/v1/server.proto
+
+from collections.abc import AsyncGenerator, AsyncIterator, Iterable, Iterator, Mapping
+from typing import Protocol
+
+from connectrpc.client import ConnectClient, ConnectClientSync
+from connectrpc.code import Code
+from connectrpc.compression import Compression
+from connectrpc.errors import ConnectError
+from connectrpc.interceptor import Interceptor, InterceptorSync
+from connectrpc.method import IdempotencyLevel, MethodInfo
+from connectrpc.request import Headers, RequestContext
+from connectrpc.server import ConnectASGIApplication, ConnectWSGIApplication, Endpoint, EndpointSync
+import chatto.discovery.v1.server_pb2 as chatto_dot_discovery_dot_v1_dot_server__pb2
+
+
+class ServerDiscoveryService(Protocol):
+    async def get_server(self, request: chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest, ctx: RequestContext) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ServerDiscoveryServiceASGIApplication(ConnectASGIApplication[ServerDiscoveryService]):
+    def __init__(self, service: ServerDiscoveryService | AsyncGenerator[ServerDiscoveryService], *, interceptors: Iterable[Interceptor]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            service=service,
+            endpoints=lambda svc: {
+                "/chatto.discovery.v1.ServerDiscoveryService/GetServer": Endpoint.unary(
+                    method=MethodInfo(
+                        name="GetServer",
+                        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,
+                    ),
+                    function=svc.get_server,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.discovery.v1.ServerDiscoveryService"
+
+
+class ServerDiscoveryServiceClient(ConnectClient):
+    async def get_server(
+        self,
+        request: chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
+        return await self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServer",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )
+
+
+class ServerDiscoveryServiceSync(Protocol):
+    def get_server(self, request: chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest, ctx: RequestContext) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
+        raise ConnectError(Code.UNIMPLEMENTED, "Not implemented")
+
+
+class ServerDiscoveryServiceWSGIApplication(ConnectWSGIApplication):
+    def __init__(self, service: ServerDiscoveryServiceSync, interceptors: Iterable[InterceptorSync]=(), read_max_bytes: int | None = None, compressions: Iterable[Compression] | None = None) -> None:
+        super().__init__(
+            endpoints={
+                "/chatto.discovery.v1.ServerDiscoveryService/GetServer": EndpointSync.unary(
+                    method=MethodInfo(
+                        name="GetServer",
+                        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,
+                    ),
+                    function=service.get_server,
+                ),
+            },
+            interceptors=interceptors,
+            read_max_bytes=read_max_bytes,
+            compressions=compressions,
+        )
+
+    @property
+    def path(self) -> str:
+        """Returns the URL path to mount the application to when serving multiple applications."""
+        return "/chatto.discovery.v1.ServerDiscoveryService"
+
+
+class ServerDiscoveryServiceClientSync(ConnectClientSync):
+    def get_server(
+        self,
+        request: chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerRequest,
+        *,
+        headers: Headers | Mapping[str, str] | None = None,
+        timeout_ms: int | None = None,
+    ) -> chatto_dot_discovery_dot_v1_dot_server__pb2.GetServerResponse:
+        return self.execute_unary(
+            request=request,
+            method=MethodInfo(
+                name="GetServer",
+                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,
+            ),
+            headers=headers,
+            timeout_ms=timeout_ms,
+        )

+ 41 - 0
src/chattolib/_pb/chatto/discovery/v1/server_pb2.py

@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Generated by the protocol buffer compiler.  DO NOT EDIT!
+# NO CHECKED-IN PROTOBUF GENCODE
+# source: chatto/discovery/v1/server.proto
+# Protobuf Python Version: 7.34.1
+"""Generated protocol buffer code."""
+from google.protobuf import descriptor as _descriptor
+from google.protobuf import descriptor_pool as _descriptor_pool
+from google.protobuf import runtime_version as _runtime_version
+from google.protobuf import symbol_database as _symbol_database
+from google.protobuf.internal import builder as _builder
+_runtime_version.ValidateProtobufRuntimeVersion(
+    _runtime_version.Domain.PUBLIC,
+    7,
+    34,
+    1,
+    '',
+    'chatto/discovery/v1/server.proto'
+)
+# @@protoc_insertion_point(imports)
+
+_sym_db = _symbol_database.Default()
+
+
+from chatto.api.v1 import server_pb2 as chatto_dot_api_dot_v1_dot_server__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')
+
+_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
+# @@protoc_insertion_point(module_scope)

+ 161 - 67
src/chattolib/_transport.py

@@ -1,85 +1,179 @@
-"""Connect JSON transport for chattolib.
-
-Chatto exposes its public API as ConnectRPC services under ``/api/connect/``.
-Each service method is invoked with an HTTP ``POST`` to::
-
-    /api/connect/<full.service.Name>/<Method>
-
-with a JSON-encoded request body and returns a JSON-encoded response body. This
-module provides the low-level helpers; the high-level surface lives in
-``chattolib.client``.
+# mypy: disable-error-code="no-any-return"
+"""Shared plumbing for the ConnectRPC-based transport.
+
+Chatto's public API is a ConnectRPC service surface. ``chattolib.client``
+speaks to it through generated service stubs (see ``chattolib._pb``) driven
+by the official ``connectrpc`` Python package.
+
+This module exposes:
+
+* :func:`build_service_clients` — one call, one ``base_url`` argument,
+  returns a ``ServiceClients`` object with a typed field per Chatto service.
+* :func:`translate_connect_error` — translates a
+  ``connectrpc.errors.ConnectError`` into the library's public exception
+  hierarchy (:class:`chattolib.exceptions.ChattoAuthError` /
+  :class:`chattolib.exceptions.ChattoConnectError`).
+* :func:`pb_to_dict` — turns a protobuf response into the camelCase JSON
+  shape that the existing ``types.py`` dataclass parsers already accept.
+
+Keeping the parsers dict-driven means the migration from Connect-JSON to
+Connect-binary transport doesn't ripple through the entire public API.
 """
 
 from __future__ import annotations
 
+from dataclasses import dataclass
 from typing import Any
 
-import httpx
-
+from connectrpc.code import Code
+from connectrpc.compat import google_protobuf_binary_codec
+from connectrpc.errors import ConnectError
+from google.protobuf.json_format import MessageToDict
+from google.protobuf.message import Message
+
+from chattolib._pb.chatto.admin.v1.diagnostics_connect import (
+    AdminDiagnosticsServiceClient,
+)
+from chattolib._pb.chatto.admin.v1.event_log_connect import AdminEventLogServiceClient
+from chattolib._pb.chatto.admin.v1.members_connect import AdminUserServiceClient
+from chattolib._pb.chatto.admin.v1.permissions_connect import (
+    AdminPermissionServiceClient,
+)
+from chattolib._pb.chatto.admin.v1.roles_connect import AdminRoleServiceClient
+from chattolib._pb.chatto.admin.v1.room_layout_connect import (
+    AdminRoomLayoutServiceClient,
+)
+from chattolib._pb.chatto.admin.v1.server_connect import AdminServerServiceClient
+from chattolib._pb.chatto.api.v1.account_connect import MyAccountServiceClient
+from chattolib._pb.chatto.api.v1.asset_uploads_connect import AssetUploadServiceClient
+from chattolib._pb.chatto.api.v1.attachments_connect import AssetServiceClient
+from chattolib._pb.chatto.api.v1.member_directory_connect import UserServiceClient
+from chattolib._pb.chatto.api.v1.messages_connect import MessageServiceClient
+from chattolib._pb.chatto.api.v1.notification_preferences_connect import (
+    NotificationPreferencesServiceClient,
+)
+from chattolib._pb.chatto.api.v1.notifications_connect import (
+    NotificationServiceClient,
+)
+from chattolib._pb.chatto.api.v1.push_notifications_connect import (
+    PushNotificationServiceClient,
+)
+from chattolib._pb.chatto.api.v1.roles_connect import RoleServiceClient
+from chattolib._pb.chatto.api.v1.room_directory_connect import (
+    RoomDirectoryServiceClient,
+)
+from chattolib._pb.chatto.api.v1.rooms_connect import RoomServiceClient
+from chattolib._pb.chatto.api.v1.server_state_connect import ServerServiceClient
+from chattolib._pb.chatto.api.v1.threads_connect import ThreadServiceClient
+from chattolib._pb.chatto.api.v1.viewer_connect import ViewerServiceClient
+from chattolib._pb.chatto.api.v1.voice_calls_connect import VoiceCallServiceClient
+from chattolib._pb.chatto.auth.v1.external_identity_auth_connect import (
+    ExternalIdentityAuthServiceClient,
+)
+from chattolib._pb.chatto.discovery.v1.server_connect import (
+    ServerDiscoveryServiceClient,
+)
 from chattolib.exceptions import ChattoAuthError, ChattoConnectError
 
 CONNECT_PREFIX = "/api/connect"
 
 
-def _headers(token: str | None, session_cookie: str | None) -> dict[str, str]:
-    headers: dict[str, str] = {"Content-Type": "application/json"}
-    if token:
-        headers["Authorization"] = f"Bearer {token}"
-    if session_cookie:
-        headers["Cookie"] = f"chatto_session={session_cookie}"
-    return headers
-
-
-def _raise_for_error(response: httpx.Response) -> None:
-    if 200 <= response.status_code < 300:
-        return
-
-    if response.status_code == 401:
-        raise ChattoAuthError("Authentication failed")
-
-    code = "unknown"
-    message = response.text
-    details: list[dict[str, Any]] = []
-    if response.headers.get("content-type", "").startswith("application/json"):
-        try:
-            body = response.json()
-            code = body.get("code") or code
-            message = body.get("message") or message
-            details = body.get("details") or details
-        except ValueError:
-            pass
-
-    raise ChattoConnectError(
-        code=code,
-        message=message,
-        status_code=response.status_code,
-        details=details,
+@dataclass
+class ServiceClients:
+    """Typed bundle of ConnectRPC service clients used by ``ChattoClient``."""
+
+    server_discovery: ServerDiscoveryServiceClient
+    server: ServerServiceClient
+    viewer: ViewerServiceClient
+    account: MyAccountServiceClient
+    users: UserServiceClient
+    roles: RoleServiceClient
+    room_directory: RoomDirectoryServiceClient
+    rooms: RoomServiceClient
+    messages: MessageServiceClient
+    threads: ThreadServiceClient
+    notifications: NotificationServiceClient
+    notification_prefs: NotificationPreferencesServiceClient
+    push: PushNotificationServiceClient
+    assets: AssetServiceClient
+    asset_uploads: AssetUploadServiceClient
+    voice_calls: VoiceCallServiceClient
+    external_auth: ExternalIdentityAuthServiceClient
+    admin_server: AdminServerServiceClient
+    admin_room_layout: AdminRoomLayoutServiceClient
+    admin_users: AdminUserServiceClient
+    admin_roles: AdminRoleServiceClient
+    admin_event_log: AdminEventLogServiceClient
+    admin_diagnostics: AdminDiagnosticsServiceClient
+    admin_permissions: AdminPermissionServiceClient
+
+    async def close(self) -> None:
+        for name in self.__dataclass_fields__:
+            client = getattr(self, name)
+            await client.close()
+
+
+def build_service_clients(base_url: str) -> ServiceClients:
+    """Instantiate one service client per Chatto Connect service.
+
+    ``base_url`` is the server root (e.g. ``https://chat.chatto.run``); the
+    ConnectRPC prefix is appended by this function.
+    """
+    address = f"{base_url.rstrip('/')}{CONNECT_PREFIX}"
+    codec = google_protobuf_binary_codec()
+
+    def make(cls: Any) -> Any:
+        return cls(address, codec=codec)
+
+    return ServiceClients(
+        server_discovery=make(ServerDiscoveryServiceClient),
+        server=make(ServerServiceClient),
+        viewer=make(ViewerServiceClient),
+        account=make(MyAccountServiceClient),
+        users=make(UserServiceClient),
+        roles=make(RoleServiceClient),
+        room_directory=make(RoomDirectoryServiceClient),
+        rooms=make(RoomServiceClient),
+        messages=make(MessageServiceClient),
+        threads=make(ThreadServiceClient),
+        notifications=make(NotificationServiceClient),
+        notification_prefs=make(NotificationPreferencesServiceClient),
+        push=make(PushNotificationServiceClient),
+        assets=make(AssetServiceClient),
+        asset_uploads=make(AssetUploadServiceClient),
+        voice_calls=make(VoiceCallServiceClient),
+        external_auth=make(ExternalIdentityAuthServiceClient),
+        admin_server=make(AdminServerServiceClient),
+        admin_room_layout=make(AdminRoomLayoutServiceClient),
+        admin_users=make(AdminUserServiceClient),
+        admin_roles=make(AdminRoleServiceClient),
+        admin_event_log=make(AdminEventLogServiceClient),
+        admin_diagnostics=make(AdminDiagnosticsServiceClient),
+        admin_permissions=make(AdminPermissionServiceClient),
     )
 
 
-async def call(
-    http: httpx.AsyncClient,
-    base_url: str,
-    service: str,
-    method: str,
-    request: dict[str, Any] | None,
-    *,
-    token: str | None,
-    session_cookie: str | None,
-) -> dict[str, Any]:
-    """Invoke a Connect unary RPC and return the decoded response JSON."""
-    url = f"{base_url}{CONNECT_PREFIX}/{service}/{method}"
-    response = await http.post(
-        url,
-        content=b"{}" if not request else _to_json(request),
-        headers=_headers(token, session_cookie),
+def translate_connect_error(exc: ConnectError) -> Exception:
+    """Convert a ``connectrpc`` error into chattolib's exception hierarchy."""
+    if exc.code == Code.UNAUTHENTICATED:
+        return ChattoAuthError(str(exc))
+    return ChattoConnectError(
+        code=exc.code.name.lower(),
+        message=str(exc),
     )
-    _raise_for_error(response)
-    body: dict[str, Any] = response.json()
-    return body
 
 
-def _to_json(payload: dict[str, Any]) -> bytes:
-    import json
+def pb_to_dict(message: Message | None) -> dict[str, Any]:
+    """Convert a protobuf message to the camelCase dict shape the parsers accept.
 
-    return json.dumps(payload, separators=(",", ":")).encode("utf-8")
+    ``preserving_proto_field_name=False`` gives us JSON-mapping camelCase
+    keys (e.g. ``created_at`` → ``createdAt``), matching what the
+    ``types.py`` dataclass parsers already consume.
+    """
+    if message is None:
+        return {}
+    return MessageToDict(
+        message,
+        preserving_proto_field_name=False,
+        use_integers_for_enums=False,
+    )

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 465 - 228
src/chattolib/client.py


+ 4 - 2
src/chattolib/types.py

@@ -884,14 +884,16 @@ class ImageTransformOptions:
 
 @dataclass
 class ActiveCallParticipant:
-    user_id: str = ""
+    user: User | None = None
     joined_at: datetime | None = None
+    call_id: str = ""
 
     @classmethod
     def parse(cls, data: dict[str, Any]) -> ActiveCallParticipant:
         return cls(
-            user_id=data.get("userId", ""),
+            user=User.parse(data.get("user")),
             joined_at=parse_datetime(data.get("joinedAt")),
+            call_id=data.get("callId", ""),
         )
 
 

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 384 - 505
tests/test_client.py


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott