notifications.proto 17 KB

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