notifications.proto 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. syntax = "proto3";
  2. package chatto.api.v1;
  3. import "buf/validate/validate.proto";
  4. import "chatto/api/v1/pagination.proto";
  5. import "chatto/api/v1/rooms.proto";
  6. import "chatto/api/v1/users.proto";
  7. import "google/protobuf/descriptor.proto";
  8. import "google/protobuf/empty.proto";
  9. import "google/protobuf/field_mask.proto";
  10. import "google/protobuf/timestamp.proto";
  11. // Delivery behavior for one notification signal class.
  12. enum NotificationDeliveryMode {
  13. option allow_alias = true;
  14. // No delivery mode was specified. This value is not a valid override.
  15. NOTIFICATION_DELIVERY_MODE_UNSPECIFIED = 0;
  16. // Matching activity does not create user attention.
  17. NOTIFICATION_DELIVERY_MODE_OFF = 1;
  18. // Deprecated alias for NOTIFICATION_DELIVERY_MODE_IN_APP_NOTIFICATION.
  19. NOTIFICATION_DELIVERY_MODE_SILENT = 2 [deprecated = true];
  20. // Matching activity creates an in-app notification but does not send push.
  21. NOTIFICATION_DELIVERY_MODE_IN_APP_NOTIFICATION = 2;
  22. // Deprecated alias for NOTIFICATION_DELIVERY_MODE_PUSH_NOTIFICATION.
  23. NOTIFICATION_DELIVERY_MODE_ALERT = 3 [deprecated = true];
  24. // Matching activity creates an in-app notification and may send push.
  25. NOTIFICATION_DELIVERY_MODE_PUSH_NOTIFICATION = 3;
  26. // Matching activity adds a neutral unread indicator without creating a
  27. // notification occurrence, playing a sound, or sending push.
  28. NOTIFICATION_DELIVERY_MODE_UNREAD_BADGE = 4;
  29. }
  30. // Delivery modes for Chatto's finite built-in notification signal classes.
  31. // Presence is significant: absent fields inherit in a stored policy.
  32. message NotificationDeliveryModes {
  33. optional NotificationDeliveryMode direct_messages = 1;
  34. optional NotificationDeliveryMode direct_mentions = 2;
  35. optional NotificationDeliveryMode replies = 3;
  36. optional NotificationDeliveryMode role_mentions = 4;
  37. optional NotificationDeliveryMode here_mentions = 5;
  38. optional NotificationDeliveryMode all_mentions = 6;
  39. optional NotificationDeliveryMode followed_threads = 7;
  40. // Deprecated compatibility slot. Use room_messages at room scope.
  41. optional NotificationDeliveryMode followed_rooms = 8 [deprecated = true];
  42. optional NotificationDeliveryMode reactions = 9;
  43. // Delivery mode for root messages posted directly to joined channel rooms.
  44. // Thread messages and direct messages use their own signal classes.
  45. optional NotificationDeliveryMode room_messages = 10;
  46. }
  47. // Visual importance of an unread notification. This is independent from
  48. // whether the notification is eligible for sound or push delivery.
  49. enum NotificationAttentionLevel {
  50. // No attention level was provided.
  51. NOTIFICATION_ATTENTION_LEVEL_UNSPECIFIED = 0;
  52. // Ambient activity appears with a neutral unread indicator.
  53. NOTIFICATION_ATTENTION_LEVEL_AMBIENT = 1;
  54. // Important activity appears with Chatto's notification orange.
  55. NOTIFICATION_ATTENTION_LEVEL_IMPORTANT = 2;
  56. }
  57. // Exact visible message destination carried by a notification signal.
  58. message NotificationMessageReference {
  59. // Room containing the source activity.
  60. RoomSummary room = 1;
  61. // Exact source or reacted-to message event to reveal.
  62. string event_id = 2;
  63. // Thread root when the target is inside a thread.
  64. optional string thread_root_event_id = 3;
  65. }
  66. // A direct message sent to the viewer.
  67. message DirectMessageReceived {
  68. NotificationMessageReference message = 1;
  69. }
  70. // A message that mentions the viewer directly.
  71. message DirectMentionReceived {
  72. NotificationMessageReference message = 1;
  73. }
  74. // A reply to one of the viewer's messages.
  75. message ReplyReceived {
  76. NotificationMessageReference message = 1;
  77. }
  78. // A message mentioning a role held by the viewer.
  79. message RoleMentionReceived {
  80. NotificationMessageReference message = 1;
  81. // Source-time role handles that selected the viewer. Multiple matching role
  82. // mentions are consolidated into one occurrence for the source message.
  83. repeated string role_names = 2;
  84. }
  85. // An `@here` message that included the viewer.
  86. message HereMentionReceived {
  87. NotificationMessageReference message = 1;
  88. }
  89. // An `@all` message that included the viewer.
  90. message AllMentionReceived {
  91. NotificationMessageReference message = 1;
  92. }
  93. // Activity in a thread followed by the viewer.
  94. message FollowedThreadActivity {
  95. NotificationMessageReference message = 1;
  96. }
  97. // Deprecated compatibility payload. Chatto does not derive this signal.
  98. message FollowedRoomActivity {
  99. option deprecated = true;
  100. NotificationMessageReference message = 1;
  101. }
  102. // A root message posted directly to a channel room that the viewer belongs to.
  103. message RoomMessageReceived {
  104. NotificationMessageReference message = 1;
  105. }
  106. // A reaction added to one of the viewer's messages.
  107. message ReactionReceived {
  108. NotificationMessageReference message = 1;
  109. // Exact reaction supplied by the actor.
  110. string emoji = 2;
  111. }
  112. // Immutable, typed cause and destination of one notification. Clients must
  113. // preserve unknown variants as generic dismissible rows and must not infer
  114. // navigation semantics for them.
  115. message NotificationSignal {
  116. oneof kind {
  117. DirectMessageReceived direct_message_received = 1;
  118. DirectMentionReceived direct_mention_received = 2;
  119. ReplyReceived reply_received = 3;
  120. RoleMentionReceived role_mention_received = 4;
  121. HereMentionReceived here_mention_received = 5;
  122. AllMentionReceived all_mention_received = 6;
  123. FollowedThreadActivity followed_thread_activity = 7;
  124. FollowedRoomActivity followed_room_activity = 8 [deprecated = true];
  125. ReactionReceived reaction_received = 9;
  126. RoomMessageReceived room_message_received = 10;
  127. }
  128. }
  129. // One exact Notifications 2.0 source occurrence.
  130. message NotificationOccurrence {
  131. // Stable occurrence ID.
  132. string id = 1;
  133. // Time of the source activity.
  134. google.protobuf.Timestamp created_at = 2;
  135. // User who caused the source activity, when still visible.
  136. User actor = 3;
  137. // Immutable cause and destination represented by this occurrence.
  138. NotificationSignal signal = 4;
  139. // Whether this occurrence still contributes unread attention.
  140. bool unread = 5;
  141. // Absolute expiry, 90 days after the source activity.
  142. google.protobuf.Timestamp expires_at = 6;
  143. // Source-time visual importance of this occurrence.
  144. NotificationAttentionLevel attention_level = 7;
  145. }
  146. // Request for one page of notification occurrences.
  147. message ListNotificationOccurrencesRequest {
  148. // Page request. Defaults to 50 results when absent or limit is zero.
  149. PageRequest page = 1;
  150. }
  151. // One page of exact notification occurrences.
  152. message ListNotificationOccurrencesResponse {
  153. // Occurrences newest activity first.
  154. repeated NotificationOccurrence occurrences = 1;
  155. // Page metadata.
  156. PageInfo page = 2;
  157. // Total unread occurrence count, independent of presentation grouping.
  158. int32 unread_count = 3;
  159. // Earliest expiry in the complete list, including occurrences outside this page.
  160. // Clients refresh authoritative notification state at this boundary.
  161. google.protobuf.Timestamp next_expiry_at = 4;
  162. // Complete unread-occurrence counts by target room. This is independent of
  163. // the bounded page so room indicators remain authoritative.
  164. repeated NotificationRoomUnreadCount room_unread_counts = 5;
  165. // Exact unread occurrences with Important attention.
  166. int32 important_unread_count = 6;
  167. }
  168. // Request one exact notification occurrence by stable ID.
  169. message GetNotificationOccurrenceRequest {
  170. // Required stable occurrence ID.
  171. string notification_id = 1 [(buf.validate.field).string.min_len = 1];
  172. }
  173. // One exact notification occurrence.
  174. message GetNotificationOccurrenceResponse {
  175. // Visible occurrence. Absence is reported as NOT_FOUND.
  176. NotificationOccurrence occurrence = 1;
  177. }
  178. // Request an explicit bounded set of notification occurrences.
  179. message BatchGetNotificationOccurrencesRequest {
  180. // Stable occurrence IDs. Unknown, deleted, and currently invisible IDs are
  181. // omitted. Repeated IDs are de-duplicated in first-seen request order.
  182. repeated string notification_ids = 1 [(buf.validate.field).repeated = {
  183. min_items: 1
  184. max_items: 100
  185. items: {
  186. string: {min_len: 1}
  187. }
  188. }];
  189. }
  190. // Exact visible notification occurrences requested by stable ID.
  191. message BatchGetNotificationOccurrencesResponse {
  192. // Found occurrences in first-seen request order.
  193. repeated NotificationOccurrence occurrences = 1;
  194. }
  195. // Unread notification occurrences currently targeting one room.
  196. message NotificationRoomUnreadCount {
  197. // Target room ID.
  198. string room_id = 1;
  199. // Number of unread occurrences targeting this room.
  200. int32 unread_count = 2;
  201. // Unread occurrences in this room with Important attention.
  202. int32 important_unread_count = 3;
  203. }
  204. // Request to mark one notification occurrence read.
  205. message MarkNotificationReadRequest {
  206. // Required stable occurrence ID.
  207. string notification_id = 1 [(buf.validate.field).string.min_len = 1];
  208. }
  209. // Notification occurrence after marking it read.
  210. message MarkNotificationReadResponse {
  211. // Updated occurrence.
  212. NotificationOccurrence occurrence = 1;
  213. }
  214. // Request permanent deletion of one notification occurrence.
  215. message DeleteNotificationOccurrenceRequest {
  216. // Required stable occurrence ID.
  217. string notification_id = 1 [(buf.validate.field).string.min_len = 1];
  218. }
  219. // Result of deleting one notification occurrence.
  220. message DeleteNotificationOccurrenceResponse {
  221. // True when this call replaced a visible occurrence with a deletion tombstone.
  222. // False when the occurrence was already absent or deleted.
  223. bool deleted = 1;
  224. }
  225. // Request permanent deletion of an explicit bounded set of occurrences.
  226. message BatchDeleteNotificationOccurrencesRequest {
  227. // Stable occurrence IDs to delete. Duplicates and already absent IDs are
  228. // ignored. The server accepts at most 100 IDs.
  229. repeated string notification_ids = 1 [(buf.validate.field).repeated = {
  230. min_items: 1
  231. max_items: 100
  232. items: {
  233. string: {min_len: 1}
  234. }
  235. }];
  236. }
  237. // Result of deleting exact notification occurrences.
  238. message BatchDeleteNotificationOccurrencesResponse {
  239. // Number of visible occurrences replaced by deletion tombstones.
  240. int32 deleted_count = 1;
  241. }
  242. // Request permanent deletion of every currently visible notification occurrence.
  243. message DeleteAllNotificationOccurrencesRequest {}
  244. // Result of deleting every currently visible notification occurrence.
  245. message DeleteAllNotificationOccurrencesResponse {
  246. // Number of visible occurrences replaced by deletion tombstones.
  247. int32 deleted_count = 1;
  248. }
  249. // Explicit and effective delivery modes for one notification policy scope.
  250. message NotificationPolicy {
  251. // Explicit modes at the requested scope. Missing fields mean Inherit.
  252. NotificationDeliveryModes overrides = 1;
  253. // Complete modes after applying product, server, and room inheritance.
  254. NotificationDeliveryModes effective = 2;
  255. }
  256. // Request the authenticated viewer's notification policy.
  257. message GetNotificationPolicyRequest {
  258. // Empty returns server-scoped preferences. A room ID returns the inherited
  259. // effective policy for that room and requires current membership.
  260. optional string room_id = 1 [(buf.validate.field).string.min_len = 1];
  261. }
  262. // Complete supported notification policy for one scope.
  263. message GetNotificationPolicyResponse {
  264. // Room scope when requested; absent for server scope.
  265. optional string room_id = 1;
  266. // Explicit and effective policy for the requested scope.
  267. NotificationPolicy policy = 2;
  268. }
  269. // Sparsely update notification policy overrides.
  270. message UpdateNotificationPolicyRequest {
  271. // Room scope to change; absent changes the server scope.
  272. optional string room_id = 1 [(buf.validate.field).string.min_len = 1];
  273. // Sparse override values. Fields selected by update_mask but absent here are
  274. // cleared to Inherit.
  275. NotificationDeliveryModes overrides = 2;
  276. // Fields to set or clear, relative to overrides. The mask must contain at
  277. // least one supported delivery-mode field.
  278. google.protobuf.FieldMask update_mask = 3;
  279. }
  280. // Complete supported notification policy after a sparse update.
  281. message UpdateNotificationPolicyResponse {
  282. // Room scope when changed; absent for server scope.
  283. optional string room_id = 1;
  284. // Explicit and effective policy after the update.
  285. NotificationPolicy policy = 2;
  286. }
  287. // Explicit target for one notification policy. Clients must select exactly
  288. // one scope. Direct-message rooms use room scope and never inherit from a room
  289. // group.
  290. message NotificationPolicyScope {
  291. oneof scope {
  292. option (buf.validate.oneof).required = true;
  293. // Selects the authenticated viewer's server-wide policy.
  294. google.protobuf.Empty server = 1;
  295. // Selects one existing room group's policy.
  296. string room_group_id = 2 [(buf.validate.field).string.min_len = 1];
  297. // Selects one room's policy and requires current room membership.
  298. string room_id = 3 [(buf.validate.field).string.min_len = 1];
  299. }
  300. }
  301. // One policy together with the explicit scope that produced it.
  302. message ScopedNotificationPolicy {
  303. // Exact policy scope.
  304. NotificationPolicyScope scope = 1;
  305. // Explicit and effective modes for the scope.
  306. NotificationPolicy policy = 2;
  307. }
  308. // Request one notification policy for the authenticated viewer.
  309. message NotificationPolicyServiceGetNotificationPolicyRequest {
  310. // Required policy scope.
  311. NotificationPolicyScope scope = 1 [(buf.validate.field).required = true];
  312. }
  313. // One resolved notification policy.
  314. message NotificationPolicyServiceGetNotificationPolicyResponse {
  315. // Requested policy and its explicit scope.
  316. ScopedNotificationPolicy policy = 1;
  317. }
  318. // Request an explicit bounded set of notification policies.
  319. message BatchGetNotificationPoliciesRequest {
  320. // Required scopes. Missing or inaccessible scopes are omitted. Repeated
  321. // scopes are de-duplicated in first-seen request order.
  322. repeated NotificationPolicyScope scopes = 1 [(buf.validate.field).repeated = {
  323. min_items: 1
  324. max_items: 100
  325. }];
  326. }
  327. // Resolved notification policies requested by scope.
  328. message BatchGetNotificationPoliciesResponse {
  329. // Found policies in first-seen request order.
  330. repeated ScopedNotificationPolicy policies = 1;
  331. }
  332. // Sparsely update notification policy overrides at one explicit scope.
  333. message NotificationPolicyServiceUpdateNotificationPolicyRequest {
  334. // Required policy scope.
  335. NotificationPolicyScope scope = 1 [(buf.validate.field).required = true];
  336. // Sparse override values. Fields selected by update_mask but absent here are
  337. // cleared to Inherit.
  338. NotificationDeliveryModes overrides = 2;
  339. // Fields to set or clear, relative to overrides. The mask must contain at
  340. // least one supported delivery-mode field.
  341. google.protobuf.FieldMask update_mask = 3;
  342. }
  343. // Complete supported notification policy after a scoped sparse update.
  344. message NotificationPolicyServiceUpdateNotificationPolicyResponse {
  345. // Updated policy and its explicit scope.
  346. ScopedNotificationPolicy policy = 1;
  347. }
  348. // Reads, marks read, and deletes notifications for the authenticated viewer.
  349. service NotificationService {
  350. // Gets one exact visible occurrence. Message-derived occurrences require
  351. // current room membership. Channel-room occurrences also require message.read
  352. // or a matching thread relationship with message.read-interactions. DM
  353. // membership authorizes DM occurrences. Returns NOT_FOUND when the occurrence
  354. // is absent, deleted, expired, or no longer visible to the authenticated
  355. // viewer. Returns UNIMPLEMENTED when this server cannot validate its signal
  356. // kind.
  357. rpc GetNotificationOccurrence(GetNotificationOccurrenceRequest) returns (GetNotificationOccurrenceResponse);
  358. // Gets an explicit bounded set of visible occurrences. Missing or
  359. // inaccessible IDs are omitted and duplicate IDs are de-duplicated. Returns
  360. // UNIMPLEMENTED rather than partially answering when this server cannot
  361. // validate one requested signal kind.
  362. rpc BatchGetNotificationOccurrences(BatchGetNotificationOccurrencesRequest) returns (BatchGetNotificationOccurrencesResponse);
  363. // Lists exact Notifications 2.0 occurrences. Clients may derive temporary
  364. // presentation groups without changing occurrence identity or counts.
  365. // Message-derived occurrences require current room membership. Channel-room
  366. // occurrences also require message.read or a matching thread relationship
  367. // with message.read-interactions. DM membership authorizes DM occurrences.
  368. // Returns UNIMPLEMENTED rather than silently omitting an occurrence whose
  369. // signal kind this server version cannot validate and assemble.
  370. rpc ListNotificationOccurrences(ListNotificationOccurrencesRequest) returns (ListNotificationOccurrencesResponse);
  371. // Marks one occurrence read. Repeating the call is safe. Returns
  372. // UNIMPLEMENTED when this server cannot validate the occurrence signal kind.
  373. rpc MarkNotificationRead(MarkNotificationReadRequest) returns (MarkNotificationReadResponse) {
  374. option idempotency_level = IDEMPOTENT;
  375. }
  376. // Permanently deletes one occurrence while retaining its anti-recreation
  377. // tombstone through the original expiry. Repeating the call is safe. Returns
  378. // UNIMPLEMENTED when this server cannot validate the occurrence signal kind.
  379. rpc DeleteNotificationOccurrence(DeleteNotificationOccurrenceRequest) returns (DeleteNotificationOccurrenceResponse) {
  380. option idempotency_level = IDEMPOTENT;
  381. }
  382. // Permanently deletes an exact set of occurrences. Repeating the call is
  383. // safe because later activity receives different occurrence IDs. Returns
  384. // UNIMPLEMENTED rather than partially deleting a signal kind this server
  385. // cannot validate.
  386. rpc BatchDeleteNotificationOccurrences(BatchDeleteNotificationOccurrencesRequest) returns (BatchDeleteNotificationOccurrencesResponse) {
  387. option idempotency_level = IDEMPOTENT;
  388. }
  389. // Permanently deletes every visible occurrence current at the server's
  390. // authoritative mutation boundary. Callers must not retry this mutation
  391. // automatically because later activity may arrive after that boundary.
  392. // Returns UNIMPLEMENTED rather than partially deleting a signal kind this
  393. // server cannot validate.
  394. rpc DeleteAllNotificationOccurrences(DeleteAllNotificationOccurrencesRequest) returns (DeleteAllNotificationOccurrencesResponse);
  395. // Gets explicit and effective modes for every supported signal class.
  396. rpc GetNotificationPolicy(GetNotificationPolicyRequest) returns (GetNotificationPolicyResponse);
  397. // Atomically sets or clears selected server- or room-scoped signal-class overrides.
  398. rpc UpdateNotificationPolicy(UpdateNotificationPolicyRequest) returns (UpdateNotificationPolicyResponse) {
  399. option idempotency_level = IDEMPOTENT;
  400. }
  401. }
  402. // Reads and updates the authenticated viewer's server, room-group, and room
  403. // notification delivery policies.
  404. service NotificationPolicyService {
  405. // Gets explicit and effective modes for one policy scope. Returns NOT_FOUND
  406. // for a missing room group or room, and PERMISSION_DENIED when the viewer is
  407. // not a current member of a requested room.
  408. rpc GetNotificationPolicy(NotificationPolicyServiceGetNotificationPolicyRequest) returns (NotificationPolicyServiceGetNotificationPolicyResponse);
  409. // Gets an explicit bounded set of policies. Missing and inaccessible scopes
  410. // are omitted, and duplicate scopes are de-duplicated in first-seen order.
  411. rpc BatchGetNotificationPolicies(BatchGetNotificationPoliciesRequest) returns (BatchGetNotificationPoliciesResponse);
  412. // Atomically sets or clears selected overrides at one explicit scope.
  413. rpc UpdateNotificationPolicy(NotificationPolicyServiceUpdateNotificationPolicyRequest) returns (NotificationPolicyServiceUpdateNotificationPolicyResponse) {
  414. option idempotency_level = IDEMPOTENT;
  415. }
  416. }