1@namespace("chat.1")
2
3protocol local {
4  import idl "../gregor1" as gregor1;
5  import idl "../keybase1" as keybase1;
6  import idl "../stellar1" as stellar1;
7  import idl "common.avdl";
8  import idl "chat_ui.avdl";
9  import idl "unfurl.avdl";
10  import idl "commands.avdl";
11
12  @typedef("string") record VersionKind {}
13
14  enum TextPaymentResultTyp {
15    SENT_0,
16    ERROR_1
17  }
18
19  variant TextPaymentResult switch (TextPaymentResultTyp resultTyp) {
20  case ERROR: string;
21  case SENT: stellar1.PaymentID;
22  }
23
24  record TextPayment {
25    string username;
26    string paymentText;
27    TextPaymentResult result;
28  }
29
30  record KnownUserMention {
31    string text;
32    gregor1.UID uid;
33  }
34
35  record KnownTeamMention {
36    string name;
37    string channel;
38  }
39
40  record MaybeMention {
41    string name;
42    string channel;
43  }
44
45  record Coordinate {
46    double lat;
47    double lon;
48    double accuracy;
49  }
50
51  record LiveLocation {
52    gregor1.Time endTime;
53  }
54
55  record MessageText {
56    string body;
57    array<TextPayment> payments;
58    union { null, MessageID } replyTo;
59    union { null, gregor1.UID } replyToUID;
60    array<KnownUserMention> userMentions;
61    array<KnownTeamMention> teamMentions;
62    union { null, LiveLocation } liveLocation;
63    map<string, HarvestedEmoji> emojis;
64  }
65
66  record MessageConversationMetadata {
67    string conversationTitle;
68  }
69
70  record MessageEdit {
71    MessageID messageID;
72    string body;
73    array<KnownUserMention> userMentions;
74    array<KnownTeamMention> teamMentions;
75    map<string, HarvestedEmoji> emojis;
76  }
77
78  record MessageDelete {
79    array<MessageID> messageIDs;
80  }
81
82  record MessageHeadline {
83    string headline;
84    map<string, HarvestedEmoji> emojis;
85  }
86
87  record MessageFlip {
88    string text;
89    FlipGameID gameID;
90    ConversationID flipConvID;
91    array<KnownUserMention> userMentions;
92    array<KnownTeamMention> teamMentions;
93  }
94
95  record MessagePin {
96    MessageID msgID;
97  }
98
99  enum MessageSystemType {
100    ADDEDTOTEAM_0,
101    INVITEADDEDTOTEAM_1,
102    COMPLEXTEAM_2,
103    CREATETEAM_3,
104    GITPUSH_4,
105    CHANGEAVATAR_5,
106    CHANGERETENTION_6,
107    BULKADDTOCONV_7,
108    SBSRESOLVE_8,
109    NEWCHANNEL_9
110  }
111
112  record MessageSystemAddedToTeam {
113    string team;
114    string adder;
115    string addee;
116    keybase1.TeamRole role;
117    array<string> bulkAdds;
118  }
119
120  record MessageSystemInviteAddedToTeam {
121    string team;
122    string inviter;
123    string invitee;
124    string adder;
125    keybase1.TeamInviteCategory inviteType;
126    keybase1.TeamRole role;
127  }
128
129  record MessageSystemComplexTeam {
130    string team;
131  }
132
133  record MessageSystemCreateTeam {
134    string team;
135    string creator;
136  }
137
138  record MessageSystemGitPush {
139    string team;
140    string pusher;
141    string repoName;
142    keybase1.RepoID repoID;
143    array<keybase1.GitRefMetadata> refs;
144    keybase1.GitPushType pushType;
145    string previousRepoName;
146  }
147
148  record MessageSystemChangeAvatar {
149    string team;
150    string user;
151  }
152
153  record MessageSystemChangeRetention {
154    boolean isTeam;
155    boolean isInherit;
156    ConversationMembersType membersType;
157    RetentionPolicy policy;
158    string user;
159  }
160
161  record MessageSystemBulkAddToConv {
162    array<string> usernames;
163  }
164
165  record MessageSystemSbsResolve {
166    string assertionService;
167    string assertionUsername;
168    string prover;
169  }
170
171  record MessageSystemNewChannel {
172    string creator;
173    // CLI renders the name at creation
174    string nameAtCreation;
175    ConversationID convID;
176    // Grouped channel created at the same time
177    array<ConversationID> convIDs;
178  }
179
180  variant MessageSystem switch (MessageSystemType systemType) {
181    case ADDEDTOTEAM: MessageSystemAddedToTeam;
182    case INVITEADDEDTOTEAM: MessageSystemInviteAddedToTeam;
183    case COMPLEXTEAM: MessageSystemComplexTeam;
184    case CREATETEAM: MessageSystemCreateTeam;
185    case GITPUSH: MessageSystemGitPush;
186    case CHANGEAVATAR: MessageSystemChangeAvatar;
187    case CHANGERETENTION: MessageSystemChangeRetention;
188    case BULKADDTOCONV: MessageSystemBulkAddToConv;
189    case SBSRESOLVE: MessageSystemSbsResolve;
190    case NEWCHANNEL: MessageSystemNewChannel;
191  }
192
193  record MessageDeleteHistory {
194    // Delete messages up to this ID (exclusive).
195    MessageID upto;
196  }
197
198  record MessageAttachment {
199    Asset object;                // the primary attachment object (can be empty)
200    union {null, Asset} preview; // the (optional) preview of object    (V1)
201    array<Asset> previews;       // the previews of object              (V2)
202    bytes metadata;              // generic metadata (msgpack)
203    boolean uploaded;            // true if assets have been uploaded   (V2)
204    array<KnownUserMention> userMentions;
205    array<KnownTeamMention> teamMentions;
206    map<string, HarvestedEmoji> emojis;
207  }
208
209  record MessageAttachmentUploaded {
210    MessageID messageID;
211    Asset object;                // the primary attachment object
212    array<Asset> previews;       // the previews of object
213    bytes metadata;              // generic metadata (msgpack)
214  }
215
216  record MessageJoin {
217    array<string> joiners;
218    array<string> leavers;
219  }
220
221  record MessageLeave { }
222
223  record MessageReaction {
224     @mpackkey("m") @jsonkey("m")
225     MessageID messageID;
226     @mpackkey("b") @jsonkey("b")
227     string body; // emoji reaction :+1:
228     @mpackkey("t") @jsonkey("t")
229     union { null, gregor1.UID } targetUID;
230     @mpackkey("e") @jsonkey("e")
231    map<string, HarvestedEmoji> emojis;
232  }
233
234  record MessageSendPayment {
235    stellar1.PaymentID paymentID;
236  }
237
238  record MessageRequestPayment {
239    stellar1.KeybaseRequestID requestID;
240    string note;
241  }
242
243  record MessageUnfurl {
244    UnfurlResult unfurl;
245    MessageID messageID;
246  }
247
248  variant MessageBody switch (MessageType messageType) {
249    case TEXT: MessageText;
250    case ATTACHMENT: MessageAttachment;
251    case EDIT: MessageEdit;
252    case DELETE: MessageDelete;
253    case METADATA: MessageConversationMetadata;
254    case HEADLINE: MessageHeadline;
255    case ATTACHMENTUPLOADED: MessageAttachmentUploaded;
256    case JOIN: MessageJoin;
257    case LEAVE: MessageLeave;
258    case SYSTEM: MessageSystem;
259    case DELETEHISTORY: MessageDeleteHistory;
260    case REACTION: MessageReaction;
261    case SENDPAYMENT: MessageSendPayment;
262    case REQUESTPAYMENT: MessageRequestPayment;
263    case UNFURL: MessageUnfurl;
264    case FLIP: MessageFlip;
265    case PIN: MessagePin;
266  }
267
268  record SenderPrepareOptions {
269    boolean skipTopicNameState;
270     union { null, MessageID } replyTo;
271  }
272
273  record SenderSendOptions {
274     union { null, ConversationMemberStatus } joinMentionsAs;
275  }
276
277  enum OutboxStateType {
278    SENDING_0,
279    ERROR_1
280  }
281
282
283  // NOTE When adding a type here consider if it should be included in
284  // chat1/extras.go#IsBadgableError
285  enum OutboxErrorType {
286    MISC_0,
287    OFFLINE_1,
288    IDENTIFY_2,
289    TOOLONG_3,
290    DUPLICATE_4,
291    EXPIRED_5,
292    TOOMANYATTEMPTS_6,
293    ALREADY_DELETED_7,
294    UPLOADFAILED_8,
295    RESTRICTEDBOT_9,
296    MINWRITER_10
297  }
298
299  record OutboxStateError {
300    string message;
301    OutboxErrorType typ;
302  }
303
304  variant OutboxState switch (OutboxStateType state) {
305    case SENDING: int; // # of attempts
306    case ERROR: OutboxStateError; // error
307  }
308
309  record OutboxRecord {
310    OutboxState state;
311    OutboxID outboxID;
312    ConversationID convID;
313    gregor1.Time ctime;
314    @lint("ignore")
315    MessagePlaintext Msg;
316    keybase1.TLFIdentifyBehavior identifyBehavior;
317    union { null, SenderPrepareOptions } prepareOpts;
318    union { null, SenderSendOptions } sendOpts;
319    int ordinal; // the position of this outbox record behind the clientPrev in Msg
320    union { null, MakePreviewRes } preview;
321    union { null, MessageUnboxed } replyTo; // only used for display
322  }
323
324  enum HeaderPlaintextVersion {
325    V1_1,
326    V2_2,
327    V3_3,
328    V4_4,
329    V5_5,
330    V6_6,
331    V7_7,
332    V8_8,
333    V9_9,
334    V10_10
335  }
336
337  record HeaderPlaintextMetaInfo {
338    boolean crit; // whether it is critical to support this message
339  }
340
341  record HeaderPlaintextUnsupported {
342    HeaderPlaintextMetaInfo mi;
343  }
344
345  // HeaderPlaintextV1 is version 1 of HeaderPlaintext.
346  // Non-nullable fields may not be changed.
347  // Only nullable fields may be added.
348  // This is because unboxing MessageBoxedV1 reserializes
349  // using this struct and checks for equality of the reserialized form
350  // with the signature.
351  record HeaderPlaintextV1 {
352    ConversationIDTriple conv;
353    string tlfName;
354    boolean tlfPublic;
355    MessageType messageType;
356    array<MessagePreviousPointer> prev;
357    gregor1.UID sender;
358    gregor1.DeviceID senderDevice;
359    union { null, boolean } kbfsCryptKeysUsed;
360
361    // MessageBoxed.V1: Hash of the encrypted body ciphertext.
362    // MessageBoxed.V2: Hash of encrypted body (.v || .n || .e)
363    //                  Where V is a big-endian int32
364    Hash bodyHash;
365
366    union { null, OutboxInfo } outboxInfo;
367    union { null, OutboxID } outboxID;
368
369    // MessageBoxed.V1: Signature over the serialized HeaderPlaintextV1 (with headerSignature set to null).
370    // MessageBoxed.V2: Null (because the header is signencrypted outside)
371    union {null, SignatureInfo} headerSignature;
372
373    // Latest merkle root when sent.
374    // Nil in MBv1 messages. Non-nil in MBv2 messages.
375    union { null, MerkleRoot } merkleRoot;
376
377    // Extra ephemeral key metadata, if the message is exploding. Never
378    // supplied in V1. When supplied with V2, the message is encoded as V3.
379    // This is the only difference between V2 and V3.
380    @mpackkey("em") @jsonkey("em")
381    union { null, MsgEphemeralMetadata } ephemeralMetadata;
382
383    @mpackkey("b") @jsonkey("b")
384    union { null, gregor1.UID } botUID;
385  }
386
387  // HeaderPlaintext is a variant container for all the
388  // versions of HeaderPlaintext.
389  variant HeaderPlaintext switch (HeaderPlaintextVersion version) {
390    case V1 : HeaderPlaintextV1;
391    case V2 : HeaderPlaintextUnsupported;
392    case V3 : HeaderPlaintextUnsupported;
393    case V4 : HeaderPlaintextUnsupported;
394    case V5 : HeaderPlaintextUnsupported;
395    case V6 : HeaderPlaintextUnsupported;
396    case V7 : HeaderPlaintextUnsupported;
397    case V8 : HeaderPlaintextUnsupported;
398    case V9 : HeaderPlaintextUnsupported;
399    case V10: HeaderPlaintextUnsupported;
400  }
401
402  enum BodyPlaintextVersion {
403    V1_1,
404    V2_2,
405    V3_3,
406    V4_4,
407    V5_5,
408    V6_6,
409    V7_7,
410    V8_8,
411    V9_9,
412    V10_10
413  }
414
415  record BodyPlaintextMetaInfo {
416    boolean crit; // whether it's critical to support this message
417  }
418
419  // Every future BodyPlaintextVX needs to be a superset of this structure.
420  record BodyPlaintextUnsupported {
421    BodyPlaintextMetaInfo mi;
422  }
423
424  // BodyPlaintextV1 is version 1 of BodyPlaintext.
425  // The fields here cannot change.  To modify,
426  // create a new record type with a new version.
427  record BodyPlaintextV1 {
428    MessageBody messageBody;
429  }
430
431  record BodyPlaintextV2 {
432    MessageBody messageBody;
433    BodyPlaintextMetaInfo mi;
434  }
435
436  // BodyPlaintext is a variant container for all the
437  // versions of BodyPlaintext.
438  variant BodyPlaintext switch (BodyPlaintextVersion version) {
439    case V1: BodyPlaintextV1;
440    case V2: BodyPlaintextV2;
441    case V3: BodyPlaintextUnsupported;
442    case V4: BodyPlaintextUnsupported;
443    case V5: BodyPlaintextUnsupported;
444    case V6: BodyPlaintextUnsupported;
445    case V7: BodyPlaintextUnsupported;
446    case V8: BodyPlaintextUnsupported;
447    case V9: BodyPlaintextUnsupported;
448    case V10: BodyPlaintextUnsupported;
449  }
450
451  record MessagePlaintext {
452    MessageClientHeader clientHeader;
453    MessageBody messageBody;
454    union { null, OutboxID } supersedesOutboxID;
455    array<HarvestedEmoji> emojis;
456  }
457
458  record MessageUnboxedValid {
459    MessageClientHeaderVerified clientHeader;
460    MessageServerHeader serverHeader;
461    MessageBody messageBody;
462    string senderUsername;
463    string senderDeviceName;
464    keybase1.DeviceTypeV2 senderDeviceType;
465    Hash bodyHash;
466
467    // MessageBoxed.V1: Hash of the encrypted header ciphertext.
468    // MessageBoxed.V2: Hash of MessageBoxed.headerSealed (.v || .n || .b)
469    //                  Where V is a big-endian int32
470    Hash headerHash;
471
472    // TOOD Maybe get rid of this field in favor of verificationKey.
473    //      If so, bump-nuke the caches in storage_blockengine and any other persistent
474    //      storage of MessageUnboxedValid.
475    // MessageBoxed.V1: Header signature. Included for the verification key.
476    // MessageBoxed.V2: Null
477    union {null, SignatureInfo} headerSignature;
478
479    // MessageBoxed.V1: Null
480    // MessageBoxed.V2: The verification key used to unbox.
481    //                  See MessageBoxed.verifyKey
482    union {null, bytes} verificationKey;
483
484    // Whether the message was sent by a device that is now revoked.
485    // We aren't sure whether the device was revoked when the message was sent.
486    // Evaluated when unboxed. Not updated thereafter.
487    union {null, gregor1.Time} senderDeviceRevokedAt;
488
489    array<string> atMentionUsernames;
490    array<gregor1.UID> atMentions;
491    ChannelMention channelMention;
492    array<MaybeMention> maybeMentions;
493    array<ChannelNameMention> channelNameMentions;
494    // reactionText -> [Reaction(username, reactionMsgID)...]
495    ReactionMap reactions;
496    map<MessageID, UnfurlResult> unfurls;
497    array<HarvestedEmoji> emojis;
498
499    // The message this replied to (if any)
500    union { null, MessageUnboxed } replyTo;
501    // Non-empty if this message is keyed for a bot
502    string botUsername;
503  }
504
505  enum MessageUnboxedErrorType {
506    MISC_0,
507    BADVERSION_CRITICAL_1,
508    BADVERSION_2,
509    IDENTIFY_3,
510    EPHEMERAL_4,
511    PAIRWISE_MISSING_5
512  }
513
514  record MessageUnboxedError {
515    MessageUnboxedErrorType errType;
516    string errMsg;
517    // verbose error info for debugging but not
518    // user display
519    string internalErrMsg;
520    VersionKind versionKind;
521    int versionNumber;
522    boolean isCritical;
523    string senderUsername;
524    string senderDeviceName;
525    keybase1.DeviceTypeV2 senderDeviceType;
526    MessageID messageID;
527    MessageType messageType;
528    gregor1.Time ctime;
529    boolean isEphemeral;
530    union { null, string } explodedBy;
531    gregor1.Time etime;
532    // Non-empty if this message is keyed for a bot
533    string botUsername;
534  }
535
536  record MessageUnboxedPlaceholder {
537    MessageID messageID;
538    boolean hidden;
539  }
540
541  enum JourneycardType {
542    WELCOME_0,
543    POPULAR_CHANNELS_1,
544    ADD_PEOPLE_2,
545    CREATE_CHANNELS_3,
546    MSG_ATTENTION_4,
547    UNUSED_5, // Was going to be USER_AWAY_FOR_LONG but never was. Can be re-purposed for something else.
548    CHANNEL_INACTIVE_6,
549    MSG_NO_ANSWER_7
550  }
551
552  record MessageUnboxedJourneycard {
553    MessageID prevID; // ID of the message after which this card appears.
554    int ordinal; // sub-position after prevID. Ought to be >=1. See computeOutboxOrdinal for some context.
555    JourneycardType cardType;
556    MessageID highlightMsgID; // Message ID to highlight for MSG_ATTENTION
557    boolean openTeam; // Whether the team is open. Can be erroneously false due to caching. Only filled for ADD_PEOPLE.
558  }
559
560  // If a new case is needed here, make sure to update at least:
561  // - variant UIMessage in chat_ui.avdl
562  // - func PresentMessageUnboxed
563  variant MessageUnboxed switch (MessageUnboxedState state) {
564    case VALID: MessageUnboxedValid;
565    case ERROR: MessageUnboxedError;
566    case OUTBOX: OutboxRecord;
567    case PLACEHOLDER: MessageUnboxedPlaceholder;
568    case JOURNEYCARD: MessageUnboxedJourneycard;
569  }
570
571  // This causes fetching to return N items, where N = IdeallyGetUnreadPlus +
572  // Unread, if AtLeast <= N <= AtMost, or one of the bounds if there are too
573  // many / too few unread items. i.e. (derived from chris's comment)
574  // collar(AtLeast, (IdeallyGetUnreadPlus + Unread), AtMost)
575  //
576  // By definition, one could use a same non-zero number for both AtLeast and
577  // AtMost to precisely control the number of items returned.
578  record UnreadFirstNumLimit {
579    @lint("ignore")
580    int NumRead;
581
582    @lint("ignore")
583    int AtLeast;
584    @lint("ignore")
585    int AtMost;
586  }
587
588  record ConversationLocalParticipant {
589    string username;
590    boolean inConvName;
591    union { null, string } fullname;
592    union { null, string } contactName;
593  }
594
595  record ConversationPinnedMessage {
596    MessageUnboxed message;
597    string pinnerUsername;
598  }
599
600  record ConversationInfoLocal {
601    ConversationID id;
602    ConversationIDTriple triple;
603
604    string tlfName;
605    string topicName;
606    string headline;
607    array<HarvestedEmoji> headlineEmojis;
608    union { null, MessageUnboxed } snippetMsg;
609    union { null, ConversationPinnedMessage } pinnedMsg;
610    union { null, string } draft;
611    keybase1.TLFVisibility visibility;
612    boolean isDefaultConv;
613    ConversationStatus status;
614    ConversationMembersType membersType;
615    ConversationMemberStatus memberStatus;
616    TeamType teamType;
617    ConversationExistence existence;
618    ConversationVers version;
619    LocalConversationVers localVersion;
620
621    // Lists of usernames, always complete, optionally sorted by activity.
622    array<ConversationLocalParticipant> participants;
623
624    // Only ever set for KBFS conversations
625    union { null, ConversationFinalizeInfo } finalizeInfo;
626    // Only ever set for TEAM and IMPTEAM conversations
627    array<string> resetNames;
628  }
629
630  enum ConversationErrorType {
631    PERMANENT_0,
632    MISSINGINFO_1,
633    SELFREKEYNEEDED_2,
634    OTHERREKEYNEEDED_3,
635    IDENTIFY_4,
636    TRANSIENT_5,
637    NONE_6
638  }
639
640  record ConversationErrorLocal {
641    ConversationErrorType typ;
642    string message;
643    Conversation remoteConv;
644    string unverifiedTLFName;
645    // Only set if typ is for rekeying.
646    union { null, ConversationErrorRekey} rekeyInfo;
647  }
648
649  record ConversationErrorRekey {
650    // All of this stuff is server trust. Don't use it to send messages.
651    string tlfName;
652    boolean tlfPublic;
653    // Users who could rekey this conv.
654    array<string> rekeyers;
655    // Lists of usernames in the conv. Untrusted.
656    array<string> writerNames;
657    array<string> readerNames;
658  }
659
660  record ConversationMinWriterRoleInfoLocal {
661    string changedBy;
662    boolean cannotWrite;
663    keybase1.TeamRole role;
664  }
665
666  record ConversationSettingsLocal {
667    union { null, ConversationMinWriterRoleInfoLocal} minWriterRoleInfo;
668  }
669
670  // ConversationLocal, whenever present, has a valid `identifyFailures` field that
671  // faithfully represent identify result. If identify information is not
672  // available, we should use a different type.
673  record ConversationLocal {
674    union { null, ConversationErrorLocal } error;
675    ConversationInfoLocal info;
676    ConversationReaderInfo readerInfo;
677    union { null, ConversationCreatorInfoLocal } creatorInfo;
678    union { null, ConversationNotificationInfo } notifications;
679    array<ConversationMetadata> supersedes;
680    array<ConversationMetadata> supersededBy;
681
682    array<MessageSummary> maxMessages; // the latest message for each message type
683
684    // Whether this conversation has no content-ful messages.
685    boolean isEmpty;
686
687    // This field, if null or empty, indicates identify succeeded without any
688    // break.
689    array<keybase1.TLFIdentifyFailure> identifyFailures;
690
691    Expunge expunge; // The latest history deletion. Defaults to zeroes.
692    union { null, RetentionPolicy } convRetention;
693    union { null, RetentionPolicy } teamRetention;
694    union { null, ConversationSettingsLocal } convSettings;
695
696    // Commands for auto complete and bot comm
697    ConversationCommandGroups commands;
698    ConversationCommandGroups botCommands;
699    map<string, string> botAliases;
700  }
701
702  record NonblockFetchRes {
703    boolean offline;
704    array<RateLimit> rateLimits;
705    array<keybase1.TLFIdentifyFailure> identifyFailures;
706  }
707
708  record ThreadView {
709    array<MessageUnboxed> messages;
710    union { null, Pagination } pagination;
711  }
712
713  enum MessageIDControlMode {
714    OLDERMESSAGES_0,
715    NEWERMESSAGES_1,
716    CENTERED_2,
717    UNREADLINE_3
718  }
719
720  record MessageIDControl {
721    union { null, MessageID } pivot;
722    MessageIDControlMode mode;
723    int num;
724  }
725
726  GetThreadLocalRes getThreadLocal(ConversationID conversationID, GetThreadReason reason, union { null, GetThreadQuery} query, union { null, Pagination } pagination, keybase1.TLFIdentifyBehavior identifyBehavior);
727  record GetThreadQuery {
728    boolean markAsRead;
729    array<MessageType> messageTypes;
730    boolean disableResolveSupersedes;
731    boolean enableDeletePlaceholders;
732    boolean disablePostProcessThread;
733
734    union { null, gregor1.Time } before;
735    union { null, gregor1.Time } after;
736    union { null, MessageIDControl } messageIDControl;
737
738  }
739  record GetThreadLocalRes {
740    ThreadView thread;
741
742    boolean offline;
743    array<RateLimit> rateLimits;
744    array<keybase1.TLFIdentifyFailure> identifyFailures;
745  }
746
747  enum GetThreadNonblockCbMode {
748    FULL_0,
749    INCREMENTAL_1
750  }
751  enum GetThreadNonblockPgMode {
752    DEFAULT_0,
753    SERVER_1
754  }
755  @timeout_msec(30000) // 30 seconds
756  NonblockFetchRes getThreadNonblock(int sessionID, ConversationID conversationID, GetThreadNonblockCbMode cbMode, GetThreadReason reason, GetThreadNonblockPgMode pgmode, union { null, GetThreadQuery} query, array<string> knownRemotes, union { null, UIPagination } pagination, keybase1.TLFIdentifyBehavior identifyBehavior);
757
758
759  record UnreadlineRes {
760    boolean offline;
761    array<RateLimit> rateLimits;
762    array<keybase1.TLFIdentifyFailure> identifyFailures;
763    union { null, MessageID } unreadlineID;
764  }
765  UnreadlineRes getUnreadline(int sessionID, ConversationID convID,
766    MessageID readMsgID, keybase1.TLFIdentifyBehavior identifyBehavior);
767
768  record NameQuery {
769    string name;
770    union { null, TLFID } tlfID; // pass this if we already know the TLFID
771    ConversationMembersType membersType;
772  }
773
774  GetInboxAndUnboxLocalRes getInboxAndUnboxLocal(union { null, GetInboxLocalQuery} query, keybase1.TLFIdentifyBehavior identifyBehavior);
775  GetInboxAndUnboxUILocalRes getInboxAndUnboxUILocal(union { null, GetInboxLocalQuery} query, keybase1.TLFIdentifyBehavior identifyBehavior);
776  record GetInboxLocalQuery {
777    // Local analog of common:GetInboxQuery
778
779    union { null, NameQuery } name;
780    union { null, string } topicName;
781
782    array<ConversationID> convIDs;
783    union { null, TopicType } topicType;
784    union { null, keybase1.TLFVisibility } tlfVisibility;
785    union { null, gregor1.Time } before;
786    union { null, gregor1.Time } after;
787    union { null, boolean } oneChatTypePerTLF;
788
789    // If left empty, default is to show all.
790    array<ConversationStatus> status;
791    // If left empty, default is to return active, preview, and reset status
792    array<ConversationMemberStatus> memberStatus;
793
794    boolean unreadOnly;
795    boolean readOnly;
796    boolean computeActiveList;
797  }
798  record GetInboxAndUnboxLocalRes {
799    array<ConversationLocal> conversations;
800
801    boolean offline;
802    array<RateLimit> rateLimits;
803    array<keybase1.TLFIdentifyFailure> identifyFailures;
804  }
805   record GetInboxAndUnboxUILocalRes {
806    array<InboxUIItem> conversations;
807
808    boolean offline;
809    array<RateLimit> rateLimits;
810    array<keybase1.TLFIdentifyFailure> identifyFailures;
811  }
812
813  enum InboxLayoutReselectMode {
814    DEFAULT_0,
815    FORCE_1
816  }
817  void requestInboxLayout(InboxLayoutReselectMode reselectMode);
818  void requestInboxUnbox(array<ConversationID> convIDs);
819  void requestInboxSmallIncrease();
820  void requestInboxSmallReset();
821  NonblockFetchRes getInboxNonblockLocal(int sessionID, union { null, int } maxUnbox, boolean skipUnverified, union { null, GetInboxLocalQuery} query, keybase1.TLFIdentifyBehavior identifyBehavior);
822
823  PostLocalRes postLocal(int sessionID, ConversationID conversationID, MessagePlaintext msg, union { null, MessageID }  replyTo, keybase1.TLFIdentifyBehavior identifyBehavior, boolean skipInChatPayments);
824  record PostLocalRes {
825    array<RateLimit> rateLimits;
826    MessageID messageID;
827    array<keybase1.TLFIdentifyFailure> identifyFailures;
828  }
829
830  OutboxID generateOutboxID();
831  @timeout_msec(30000) // 30 seconds
832  PostLocalNonblockRes postLocalNonblock(int sessionID, ConversationID conversationID, MessagePlaintext msg, MessageID clientPrev, union { null, OutboxID } outboxID, union { null, MessageID }  replyTo, keybase1.TLFIdentifyBehavior identifyBehavior, boolean skipInChatPayments);
833  record PostLocalNonblockRes {
834    array<RateLimit> rateLimits;
835    OutboxID outboxID;
836    array<keybase1.TLFIdentifyFailure> identifyFailures;
837  }
838
839  PostLocalRes forwardMessage(int sessionID, ConversationID srcConvID, ConversationID dstConvID, MessageID msgID, keybase1.TLFIdentifyBehavior identifyBehavior);
840  @timeout_msec(30000) // 30 seconds
841  PostLocalNonblockRes forwardMessageNonblock(int sessionID, ConversationID srcConvID, ConversationID dstConvID, MessageID msgID, keybase1.TLFIdentifyBehavior identifyBehavior);
842
843  @timeout_msec(30000) // 30 seconds
844  PostLocalNonblockRes postTextNonblock(int sessionID, ConversationID conversationID, string tlfName, boolean tlfPublic, string body, MessageID clientPrev, union { null, MessageID } replyTo, union { null, OutboxID } outboxID, keybase1.TLFIdentifyBehavior identifyBehavior, union {null, gregor1.DurationSec} ephemeralLifetime);
845
846  @timeout_msec(30000) // 30 seconds
847  PostLocalNonblockRes postDeleteNonblock(ConversationID conversationID, string tlfName, boolean tlfPublic, MessageID supersedes,MessageID clientPrev, union { null, OutboxID } outboxID,  keybase1.TLFIdentifyBehavior identifyBehavior);
848
849  record EditTarget {
850    union { null, MessageID } messageID;
851    union { null, OutboxID } outboxID;
852  }
853
854  @timeout_msec(30000) // 30 seconds
855  PostLocalNonblockRes postEditNonblock(ConversationID conversationID, string tlfName, boolean tlfPublic, EditTarget target, string body, union { null, OutboxID } outboxID, MessageID clientPrev, keybase1.TLFIdentifyBehavior identifyBehavior);
856
857  @timeout_msec(30000) // 30 seconds
858  PostLocalNonblockRes postReactionNonblock(ConversationID conversationID, string tlfName, boolean tlfPublic, MessageID supersedes, string body, union { null, OutboxID } outboxID, MessageID clientPrev, keybase1.TLFIdentifyBehavior identifyBehavior);
859
860  @timeout_msec(30000) // 30 seconds
861  PostLocalNonblockRes postHeadlineNonblock(ConversationID conversationID, string tlfName, boolean tlfPublic,  string headline, union { null, OutboxID } outboxID, MessageID clientPrev, keybase1.TLFIdentifyBehavior identifyBehavior);
862  PostLocalRes postHeadline(ConversationID conversationID, string tlfName, boolean tlfPublic, string headline, keybase1.TLFIdentifyBehavior identifyBehavior);
863
864  @timeout_msec(30000) // 30 seconds
865  PostLocalNonblockRes postMetadataNonblock(ConversationID conversationID, string tlfName, boolean tlfPublic,  string channelName, union { null, OutboxID } outboxID, MessageID clientPrev, keybase1.TLFIdentifyBehavior identifyBehavior);
866  PostLocalRes postMetadata(ConversationID conversationID, string tlfName, boolean tlfPublic, string channelName, keybase1.TLFIdentifyBehavior identifyBehavior);
867
868  // Delete from the beginning upto a certain message (exclusive)
869  PostLocalRes postDeleteHistoryUpto(ConversationID conversationID, string tlfName, boolean tlfPublic,
870                   keybase1.TLFIdentifyBehavior identifyBehavior, MessageID upto);
871  // Delete from the beginning through a certain message (inclusive)
872  PostLocalRes postDeleteHistoryThrough(ConversationID conversationID, string tlfName, boolean tlfPublic,
873                    keybase1.TLFIdentifyBehavior identifyBehavior, MessageID through);
874  // Delete all messages older than `age`
875  PostLocalRes postDeleteHistoryByAge(ConversationID conversationID, string tlfName, boolean tlfPublic,
876                    keybase1.TLFIdentifyBehavior identifyBehavior, gregor1.DurationSec age);
877
878  @lint("ignore")
879  SetConversationStatusLocalRes SetConversationStatusLocal(ConversationID conversationID, ConversationStatus status, keybase1.TLFIdentifyBehavior identifyBehavior);
880  record SetConversationStatusLocalRes {
881    array<RateLimit> rateLimits;
882    array<keybase1.TLFIdentifyFailure> identifyFailures;
883  }
884
885  // newConversationsLocal tries to make many conversations at once. If the conversation triple already exists,
886  // it is not considered an error. Any errors are returned in the results.
887  NewConversationsLocalRes newConversationsLocal(array<NewConversationLocalArgument> newConversationLocalArguments, keybase1.TLFIdentifyBehavior identifyBehavior);
888  record NewConversationsLocalRes {
889    array<NewConversationsLocalResult> results;
890    array<RateLimit> rateLimits;
891    array<keybase1.TLFIdentifyFailure> identifyFailures;
892  }
893  record NewConversationsLocalResult {
894    union { null, NewConversationLocalRes } result;
895    union { null, string } err;
896  }
897  record NewConversationLocalArgument {
898    string tlfName;
899    TopicType topicType;
900    keybase1.TLFVisibility tlfVisibility;
901    union { null, string } topicName;
902    ConversationMembersType membersType;
903  }
904
905  NewConversationLocalRes newConversationLocal(string tlfName, TopicType topicType, keybase1.TLFVisibility tlfVisibility, union { null, string } topicName, ConversationMembersType membersType, keybase1.TLFIdentifyBehavior identifyBehavior);
906  record NewConversationLocalRes {
907    ConversationLocal conv;
908    InboxUIItem uiConv;
909    array<RateLimit> rateLimits;
910    array<keybase1.TLFIdentifyFailure> identifyFailures;
911  }
912
913  // if since is given, limit is ignored
914  GetInboxSummaryForCLILocalRes getInboxSummaryForCLILocal(GetInboxSummaryForCLILocalQuery query);
915  record GetInboxSummaryForCLILocalQuery {
916    TopicType topicType;
917    string after;
918    string before;
919    keybase1.TLFVisibility visibility;
920
921    // If left empty, default is to show all.
922    array<ConversationStatus> status;
923    array<ConversationID> convIDs;
924
925    boolean unreadFirst;
926    UnreadFirstNumLimit unreadFirstLimit;
927    int activitySortedLimit;
928  }
929  record GetInboxSummaryForCLILocalRes {
930    array<ConversationLocal> conversations;
931    boolean offline;
932    array<RateLimit> rateLimits;
933  }
934
935
936  GetConversationForCLILocalRes getConversationForCLILocal(GetConversationForCLILocalQuery query);
937  record GetConversationForCLILocalQuery {
938    boolean markAsRead;
939    @lint("ignore")
940    array<MessageType> MessageTypes;
941
942    @lint("ignore")
943    union { null, string } Since;
944
945    UnreadFirstNumLimit limit;
946
947    @lint("ignore")
948    ConversationLocal conv;
949  }
950  record GetConversationForCLILocalRes {
951    ConversationLocal conversation;
952    array<MessageUnboxed> messages;
953    boolean offline;
954    array<RateLimit> rateLimits;
955  }
956
957  // Get messages by ID.
958  @lint("ignore")
959  GetMessagesLocalRes GetMessagesLocal(ConversationID conversationID, array<MessageID> messageIDs, boolean disableResolveSupersedes, keybase1.TLFIdentifyBehavior identifyBehavior);
960  record GetMessagesLocalRes {
961    array<MessageUnboxed> messages;
962    boolean offline;
963    array<RateLimit> rateLimits;
964    array<keybase1.TLFIdentifyFailure> identifyFailures;
965  }
966
967  record PostFileAttachmentArg {
968    ConversationID conversationID;
969    string tlfName;
970    keybase1.TLFVisibility visibility;
971    string filename;
972    string title;
973    bytes metadata;
974    keybase1.TLFIdentifyBehavior identifyBehavior;
975
976    union {null, MakePreviewRes } callerPreview;
977    union {null, OutboxID } outboxID;
978    union {null, gregor1.DurationSec} ephemeralLifetime;
979  }
980
981  // Post an attachment from file source to conversationID.
982  PostLocalRes postFileAttachmentLocal(int sessionID, PostFileAttachmentArg arg);
983
984  @timeout_msec(30000) // 30 seconds
985  PostLocalNonblockRes postFileAttachmentLocalNonblock(int sessionID, PostFileAttachmentArg arg, MessageID clientPrev);
986
987  record GetNextAttachmentMessageLocalRes  {
988    union { null, UIMessage } message;
989    boolean offline;
990    array<RateLimit> rateLimits;
991    array<keybase1.TLFIdentifyFailure> identifyFailures;
992  }
993  GetNextAttachmentMessageLocalRes getNextAttachmentMessageLocal(ConversationID convID, MessageID messageID, boolean backInTime, array<AssetMetadataType> assetTypes, keybase1.TLFIdentifyBehavior identifyBehavior);
994
995  record DownloadAttachmentLocalRes {
996    array<RateLimit> rateLimits;
997    array<keybase1.TLFIdentifyFailure> identifyFailures;
998  }
999
1000  // Download an attachment from a message into sink stream.
1001  @lint("ignore")
1002  DownloadAttachmentLocalRes DownloadAttachmentLocal(int sessionID, ConversationID conversationID, MessageID messageID, keybase1.Stream sink, boolean preview, keybase1.TLFIdentifyBehavior identifyBehavior);
1003
1004  // Download an attachment from a message into a local file.
1005  // Filename must be writable by the service.
1006  record DownloadFileAttachmentLocalRes {
1007    string filePath;
1008    array<RateLimit> rateLimits;
1009    array<keybase1.TLFIdentifyFailure> identifyFailures;
1010  }
1011  @lint("ignore")
1012  DownloadFileAttachmentLocalRes DownloadFileAttachmentLocal(int sessionID, ConversationID conversationID, MessageID messageID, boolean downloadToCache, boolean preview, keybase1.TLFIdentifyBehavior identifyBehavior);
1013
1014  @lint("ignore")
1015  void ConfigureFileAttachmentDownloadLocal(string cacheDirOverride, string downloadDirOverride);
1016
1017  enum PreviewLocationTyp {
1018    URL_0,
1019    FILE_1,
1020    BYTES_2
1021  }
1022
1023  variant PreviewLocation switch (PreviewLocationTyp ltyp) {
1024    case URL: string;
1025    case FILE: string;
1026    case BYTES: bytes;
1027  }
1028
1029  record MakePreviewRes {
1030    string mimeType;        // this will always be populated
1031    union {null, string} previewMimeType ; // this will be populated if we made a preview
1032    union {null, PreviewLocation} location;    // will exist if service is able to make a preview
1033    union {null, AssetMetadata} metadata; // will exist if service is able to make a preview
1034    union {null, AssetMetadata} baseMetadata; // will exist if service is able to get base metadata
1035  }
1036  MakePreviewRes makePreview(int sessionID, string filename, OutboxID outboxID);
1037  MakePreviewRes makeAudioPreview(array<double> amps, int duration);
1038  string getUploadTempFile(OutboxID outboxID, string filename);
1039  string makeUploadTempFile(OutboxID outboxID, string filename, bytes data);
1040  void cancelUploadTempFile(OutboxID outboxID);
1041
1042  @lint("ignore")
1043  void CancelPost(OutboxID outboxID);
1044  @lint("ignore")
1045  void RetryPost(OutboxID outboxID, union { null, keybase1.TLFIdentifyBehavior } identifyBehavior);
1046
1047  record MarkAsReadLocalRes {
1048     boolean offline;
1049     array<RateLimit> rateLimits;
1050  }
1051  MarkAsReadLocalRes markAsReadLocal(int sessionID, ConversationID conversationID, union { null, MessageID } msgID);
1052
1053  record MarkTLFAsReadLocalRes {
1054     boolean offline;
1055     array<RateLimit> rateLimits;
1056  }
1057  MarkTLFAsReadLocalRes markTLFAsReadLocal(int sessionID, TLFID tlfID);
1058
1059  record FindConversationsLocalRes {
1060    array<ConversationLocal> conversations; // for the cli / json api
1061    array<InboxUIItem> uiConversations; // for the gui
1062
1063    boolean offline;
1064    array<RateLimit> rateLimits;
1065    array<keybase1.TLFIdentifyFailure> identifyFailures;
1066  }
1067
1068  FindConversationsLocalRes findConversationsLocal(string tlfName, ConversationMembersType membersType, keybase1.TLFVisibility visibility, TopicType topicType, string topicName, union { null, boolean } oneChatPerTLF, keybase1.TLFIdentifyBehavior identifyBehavior);
1069  InboxUIItem findGeneralConvFromTeamID(keybase1.TeamID teamID);
1070
1071  // Typing API
1072  void updateTyping(ConversationID conversationID, boolean typing); // throttled
1073  void updateUnsentText(ConversationID conversationID, string tlfName, string text); // debounced
1074
1075  // Channel management
1076  record JoinLeaveConversationLocalRes {
1077    boolean offline;
1078    array<RateLimit> rateLimits;
1079  }
1080  JoinLeaveConversationLocalRes joinConversationLocal(string tlfName, TopicType topicType, keybase1.TLFVisibility visibility, string topicName);
1081  JoinLeaveConversationLocalRes joinConversationByIDLocal(ConversationID convID);
1082  JoinLeaveConversationLocalRes leaveConversationLocal(ConversationID convID);
1083  record PreviewConversationLocalRes {
1084    InboxUIItem conv;
1085    boolean offline;
1086    array<RateLimit> rateLimits;
1087  }
1088  PreviewConversationLocalRes previewConversationByIDLocal(ConversationID convID);
1089  record DeleteConversationLocalRes {
1090    boolean offline;
1091    array<RateLimit> rateLimits;
1092  }
1093  DeleteConversationLocalRes deleteConversationLocal(int sessionID, ConversationID convID, string channelName, boolean confirmed);
1094  record RemoveFromConversationLocalRes {
1095    array<RateLimit> rateLimits;
1096  }
1097  RemoveFromConversationLocalRes removeFromConversationLocal(ConversationID convID, array<string> usernames);
1098
1099  record GetTLFConversationsLocalRes {
1100    array<InboxUIItem> convs;
1101    boolean offline;
1102    array<RateLimit> rateLimits;
1103  }
1104  GetTLFConversationsLocalRes getTLFConversationsLocal(string tlfName, TopicType topicType, ConversationMembersType membersType);
1105
1106  record GetChannelMembershipsLocalRes {
1107    array<ChannelNameMention> channels;
1108    boolean offline;
1109    array<RateLimit> rateLimits;
1110  }
1111  GetChannelMembershipsLocalRes getChannelMembershipsLocal(keybase1.TeamID teamID, gregor1.UID uid);
1112
1113
1114  record GetMutualTeamsLocalRes {
1115    array<keybase1.TeamID> teamIDs;
1116    boolean offline;
1117    array<RateLimit> rateLimits;
1118  }
1119  GetMutualTeamsLocalRes getMutualTeamsLocal(array<string> usernames);
1120
1121  // Chat notification configuration endpoint. Does not need to be complete, just a delta on the
1122  // currently configured settings.
1123  record SetAppNotificationSettingsLocalRes {
1124    boolean offline;
1125    array<RateLimit> rateLimits;
1126  }
1127  record AppNotificationSettingLocal {
1128    keybase1.DeviceType deviceType;
1129    NotificationKind kind;
1130    boolean enabled;
1131  }
1132  SetAppNotificationSettingsLocalRes setAppNotificationSettingsLocal(ConversationID convID, boolean channelWide, array<AppNotificationSettingLocal> settings);
1133  void setGlobalAppNotificationSettingsLocal(map<string, bool> settings);
1134  GlobalAppNotificationSettings getGlobalAppNotificationSettingsLocal();
1135
1136  // Unpack message from a push notification
1137  string unboxMobilePushNotification(string payload, string convID, ConversationMembersType membersType, array<string> pushIDs, boolean shouldAck);
1138
1139  // Convenience interface for adding someone back to a reset team convo
1140  void addTeamMemberAfterReset(string username, ConversationID convID);
1141  record ResetConvMember {
1142    string username;
1143    gregor1.UID uid;
1144    ConversationID conv;
1145  }
1146  record GetAllResetConvMembersRes {
1147    array<ResetConvMember> members;
1148    array<RateLimit> rateLimits;
1149  }
1150  GetAllResetConvMembersRes getAllResetConvMembers();
1151
1152  void setConvRetentionLocal(ConversationID convID, RetentionPolicy policy);
1153  void setTeamRetentionLocal(keybase1.TeamID teamID, RetentionPolicy policy);
1154  union { null, RetentionPolicy } getTeamRetentionLocal(keybase1.TeamID teamID);
1155
1156  void setConvMinWriterRoleLocal(ConversationID convID, keybase1.TeamRole role);
1157  void upgradeKBFSConversationToImpteam(ConversationID convID);
1158
1159  record SearchRegexpRes {
1160    boolean offline;
1161    array<ChatSearchHit> hits;
1162    array<RateLimit> rateLimits;
1163    array<keybase1.TLFIdentifyFailure> identifyFailures;
1164  }
1165
1166  SearchRegexpRes searchRegexp(int sessionID, ConversationID convID, string query,
1167    SearchOpts opts, keybase1.TLFIdentifyBehavior identifyBehavior);
1168  void cancelActiveInboxSearch();
1169
1170  record SearchInboxRes {
1171    boolean offline;
1172    union { null, ChatSearchInboxResults } res;
1173    array<RateLimit> rateLimits;
1174    array<keybase1.TLFIdentifyFailure> identifyFailures;
1175  }
1176
1177  SearchInboxRes searchInbox(int sessionID, string query,
1178    SearchOpts opts, boolean namesOnly, keybase1.TLFIdentifyBehavior identifyBehavior);
1179  record SimpleSearchInboxConvNamesHit {
1180    string name;
1181    ConversationID convID;
1182    boolean isTeam;
1183    array<string> parts;
1184    string tlfName;
1185  }
1186  array<SimpleSearchInboxConvNamesHit> simpleSearchInboxConvNames(string query);
1187
1188  void cancelActiveSearch();
1189
1190  record ProfileSearchConvStats {
1191    string err;
1192    string convName;
1193    // min/max messages we care about for the index.
1194    MessageID minConvID;
1195    MessageID maxConvID;
1196    // number of missing ids from the index
1197    int numMissing;
1198    // number of messages in the conversation that were indexed
1199    int numMessages;
1200    // number of bytes in the index on disk
1201    int indexSizeDisk;
1202    // number of bytes in the index in memory
1203    int64 indexSizeMem;
1204    // total time to index the conv
1205    gregor1.DurationMsec durationMsec;
1206    int percentIndexed;
1207  }
1208
1209  map<ConvIDStr, ProfileSearchConvStats> profileChatSearch(keybase1.TLFIdentifyBehavior identifyBehavior);
1210
1211  // Static data that changes only as often as the code.
1212  // TODO: consolidate this RPC with the wallet `getStaticConfig` in the future
1213  StaticConfig getStaticConfig();
1214  record BuiltinCommandGroup {
1215    ConversationBuiltinCommandTyp typ;
1216    array<ConversationCommand> commands;
1217  }
1218  record StaticConfig {
1219    array<MessageType> deletableByDeleteHistory;
1220    array<BuiltinCommandGroup> builtinCommands;
1221  }
1222
1223  // Respond to an unfurl prompt
1224  enum UnfurlPromptAction {
1225    ALWAYS_0,
1226    NEVER_1,
1227    ACCEPT_2,
1228    NOTNOW_3,
1229    ONETIME_4
1230  }
1231  variant UnfurlPromptResult switch (UnfurlPromptAction actionType) {
1232    case ALWAYS: void;
1233    case NEVER: void;
1234    case NOTNOW: void;
1235    case ACCEPT: string;
1236    case ONETIME: string;
1237  }
1238  void resolveUnfurlPrompt(ConversationID convID, MessageID msgID, UnfurlPromptResult result, keybase1.TLFIdentifyBehavior identifyBehavior);
1239  UnfurlSettingsDisplay getUnfurlSettings();
1240  void saveUnfurlSettings(UnfurlMode mode, array<string> whitelist);
1241
1242  void toggleMessageCollapse(ConversationID convID, MessageID msgID, boolean collapse);
1243
1244  void bulkAddToConv(ConversationID convID, array<string> usernames);
1245  void bulkAddToManyConvs(array<ConversationID> conversations, array<string> usernames);
1246
1247  keybase1.UserReacjis putReacjiSkinTone(keybase1.ReacjiSkinTone skinTone);
1248
1249  void resolveMaybeMention(MaybeMention mention);
1250
1251  // Gallery support
1252  enum GalleryItemTyp {
1253    MEDIA_0,
1254    LINK_1,
1255    DOC_2
1256  }
1257  record LoadGalleryRes {
1258    array<UIMessage> messages;
1259    boolean last;
1260    array<RateLimit> rateLimits;
1261    array<keybase1.TLFIdentifyFailure> identifyFailures;
1262  }
1263  LoadGalleryRes loadGallery(int sessionID, ConversationID convID, GalleryItemTyp typ, int num, union { null, MessageID } fromMsgID);
1264
1265  // Flips in the API
1266  record LoadFlipRes {
1267    UICoinFlipStatus status;
1268    array<RateLimit> rateLimits;
1269    array<keybase1.TLFIdentifyFailure> identifyFailures;
1270  }
1271  LoadFlipRes loadFlip(ConversationID hostConvID, MessageID hostMsgID, ConversationID flipConvID, FlipGameID gameID);
1272
1273  // Location updates
1274  void locationUpdate(Coordinate coord);
1275
1276  // Bot commands
1277  record UserBotExtendedDescription {
1278    string title;
1279    @jsonkey("desktop_body")
1280    string desktopBody;
1281    @jsonkey("mobile_body")
1282    string mobileBody;
1283  }
1284  record UserBotCommandOutput {
1285    string name;
1286    string description;
1287    string usage;
1288    @jsonkey("extended_description")
1289    union { null, UserBotExtendedDescription } extendedDescription;
1290    string username;
1291  }
1292  record UserBotCommandInput {
1293    string name;
1294    string description;
1295    string usage;
1296    @jsonkey("extended_description")
1297    union { null, UserBotExtendedDescription } extendedDescription;
1298  }
1299  record AdvertiseCommandsParam {
1300    BotCommandsAdvertisementTyp typ;
1301    array<UserBotCommandInput> commands;
1302    union { null, string } teamName;
1303    union { null, ConversationID } convID;
1304  }
1305  record AdvertiseBotCommandsLocalRes {
1306    array<RateLimit> rateLimits;
1307  }
1308
1309  AdvertiseBotCommandsLocalRes advertiseBotCommandsLocal(union { null, string } alias,
1310    array<AdvertiseCommandsParam> advertisements);
1311  record ListBotCommandsLocalRes {
1312    array<UserBotCommandOutput> commands;
1313    array<RateLimit> rateLimits;
1314  }
1315  ListBotCommandsLocalRes listBotCommandsLocal(ConversationID convID);
1316
1317  ListBotCommandsLocalRes listPublicBotCommandsLocal(string username);
1318
1319  record ClearBotCommandsFilter {
1320    BotCommandsAdvertisementTyp typ;
1321    union { null, string } teamName;
1322    union { null, ConversationID } convID;
1323  }
1324  record ClearBotCommandsLocalRes {
1325    array<RateLimit> rateLimits;
1326  }
1327  ClearBotCommandsLocalRes clearBotCommandsLocal(union { null, ClearBotCommandsFilter } filter);
1328
1329  // Pinning
1330  record PinMessageRes {
1331    array<RateLimit> rateLimits;
1332  }
1333  PinMessageRes pinMessage(ConversationID convID, MessageID msgID);
1334  PinMessageRes unpinMessage(ConversationID convID);
1335  void ignorePinnedMessage(ConversationID convID);
1336
1337  // Bot interface
1338  void addBotMember(ConversationID convID, string username, union { null, keybase1.TeamBotSettings } botSettings, keybase1.TeamRole role);
1339  void editBotMember(ConversationID convID, string username, union { null, keybase1.TeamBotSettings } botSettings, keybase1.TeamRole role);
1340  void removeBotMember(ConversationID convID, string username);
1341  void setBotMemberSettings(ConversationID convID, string username, keybase1.TeamBotSettings botSettings);
1342  keybase1.TeamBotSettings getBotMemberSettings(ConversationID convID, string username);
1343  keybase1.TeamRole getTeamRoleInConversation(ConversationID convID, string username);
1344  record ConvSearchHit {
1345    string name;
1346    ConversationID convID;
1347    boolean isTeam;
1348    array<string> parts;
1349  }
1350  array<ConvSearchHit> addBotConvSearch(string term);
1351  array<ConvSearchHit> forwardMessageConvSearch(string term);
1352
1353  keybase1.TeamID teamIDFromTLFName(string tlfName, ConversationMembersType membersType, boolean tlfPublic);
1354
1355  void dismissJourneycard(ConversationID convID, JourneycardType cardType);
1356
1357  record LocalMtimeUpdate {
1358    ConversationID convID;
1359    gregor1.Time mtime;
1360  }
1361
1362  enum SnippetDecoration {
1363    NONE_0,
1364    PENDING_MESSAGE_1,
1365    FAILED_PENDING_MESSAGE_2,
1366    EXPLODING_MESSAGE_3,
1367    EXPLODED_MESSAGE_4,
1368    AUDIO_ATTACHMENT_5,
1369    VIDEO_ATTACHMENT_6,
1370    PHOTO_ATTACHMENT_7,
1371    FILE_ATTACHMENT_8,
1372    STELLAR_RECEIVED_9,
1373    STELLAR_SENT_10,
1374    PINNED_MESSAGE_11
1375  }
1376
1377  record WelcomeMessageDisplay{
1378    boolean set;
1379    // display is text suitable for displaying in Kb.Markdown (e.g., it
1380    // has KB-escaped URLs)
1381    string display;
1382    // raw is the raw user-input plaintext
1383    string raw;
1384  }
1385  record WelcomeMessage {
1386    boolean set;
1387    // raw is the raw user-input plaintext
1388    string raw;
1389  }
1390  void setWelcomeMessage(keybase1.TeamID teamID, WelcomeMessage message);
1391  WelcomeMessageDisplay getWelcomeMessage(keybase1.TeamID teamID);
1392
1393  record GetDefaultTeamChannelsLocalRes {
1394    array<InboxUIItem> convs;
1395    union { null, RateLimit } rateLimit;
1396  }
1397  GetDefaultTeamChannelsLocalRes getDefaultTeamChannelsLocal(keybase1.TeamID teamID);
1398
1399  record SetDefaultTeamChannelsLocalRes {
1400    union { null, RateLimit } rateLimit;
1401  }
1402  SetDefaultTeamChannelsLocalRes setDefaultTeamChannelsLocal(keybase1.TeamID teamID, array<ConvIDStr> convs);
1403
1404  LastActiveStatus getLastActiveForTLF(TLFIDStr tlfID);
1405  record LastActiveTimeAll {
1406    map<TLFIDStr, gregor1.Time> teams;
1407    map<ConvIDStr, gregor1.Time> channels;
1408  }
1409  record LastActiveStatusAll {
1410    map<TLFIDStr, LastActiveStatus> teams; // top level active status for teams
1411    map<ConvIDStr, LastActiveStatus> channels; // active status for all team channels
1412  }
1413  LastActiveStatusAll getLastActiveForTeams();
1414
1415  int getRecentJoinsLocal(ConversationID convID);
1416
1417  // queue up a refresh of participants, this will result in notifications to the UI with the data
1418  // from local and remote sources
1419  void refreshParticipants(ConversationID convID);
1420  gregor1.Time getLastActiveAtLocal(keybase1.TeamID teamID, string username);
1421  map<keybase1.TeamID, gregor1.Time> getLastActiveAtMultiLocal(array<keybase1.TeamID> teamIDs, string username);
1422
1423  array<ConversationLocalParticipant> getParticipants(ConversationID convID);
1424
1425  // Emoji
1426  record EmojiError {
1427    string clidisplay;
1428    string uidisplay;
1429  }
1430  record AddEmojiRes {
1431    union { null, RateLimit } rateLimit;
1432    union {null, EmojiError} error;
1433  }
1434  AddEmojiRes addEmoji(ConversationID convID, string alias, string filename, boolean allowOverwrite);
1435
1436  record AddEmojisRes {
1437    union { null, RateLimit } rateLimit;
1438    array<string> successFilenames;
1439    map<string, EmojiError> failedFilenames; // filename => error message
1440  }
1441  AddEmojisRes addEmojis(ConversationID convID, array<string> aliases, array<string> filenames, array<boolean> allowOverwrite);
1442
1443  record AddEmojiAliasRes {
1444    union { null, RateLimit } rateLimit;
1445    union {null, EmojiError} error;
1446  }
1447  AddEmojiAliasRes addEmojiAlias(ConversationID convID, string newAlias, string existingAlias);
1448
1449  record RemoveEmojiRes {
1450    union { null, RateLimit } rateLimit;
1451  }
1452  RemoveEmojiRes removeEmoji(ConversationID convID, string alias);
1453
1454  record UserEmojiRes {
1455    UserEmojis emojis;
1456    union { null, RateLimit } rateLimit;
1457  }
1458
1459  record EmojiFetchOpts {
1460    boolean getCreationInfo;
1461    boolean getAliases;
1462    boolean onlyInTeam;
1463  }
1464  UserEmojiRes userEmojis(EmojiFetchOpts opts, union { null, ConversationID } convID);
1465  void toggleEmojiAnimations(boolean enabled);
1466}
1467