diff --git a/chat.go b/chat.go index 4d2abf7..d27f3d0 100644 --- a/chat.go +++ b/chat.go @@ -2,6 +2,8 @@ package keybase import ( "bufio" + "encoding/base64" + "encoding/binary" "encoding/json" "errors" "os/exec" @@ -9,6 +11,36 @@ import ( "time" ) +// Returns a string representation of a message id suitable for use in a +// pagination struct +func getID(id int) string { + var b []byte + switch { + case id < 128: + // 7-bit int + b = make([]byte, 1) + b = []byte{byte(id)} + + case id <= 255: + // uint8 + b = make([]byte, 2) + b = []byte{204, byte(id)} + + case id > 255 && id <= 65535: + // uint16 + b = make([]byte, 2) + binary.BigEndian.PutUint16(b, uint16(id)) + b = append([]byte{205}, b...) + + case id > 65535 && id <= 4294967295: + // uint32 + b = make([]byte, 4) + binary.BigEndian.PutUint32(b, uint32(id)) + b = append([]byte{206}, b...) + } + return base64.StdEncoding.EncodeToString(b) +} + // Creates a string of a json-encoded channel to pass to keybase chat api-listen --filter-channel func createFilterString(channel Channel) string { if channel.Name == "" { @@ -223,6 +255,29 @@ func (k *Keybase) ChatList(opts ...Channel) (ChatAPI, error) { return r, err } +// ReadMessage fetches the chat message with the specified message id from a conversation. +func (c Chat) ReadMessage(messageID int) (*ChatAPI, error) { + m := ChatAPI{ + Params: ¶ms{}, + } + m.Params.Options = options{ + Pagination: &pagination{}, + } + + m.Method = "read" + m.Params.Options.Channel = &c.Channel + m.Params.Options.Pagination.Num = 1 + + m.Params.Options.Pagination.Previous = getID(messageID - 1) + + r, err := chatAPIOut(c.keybase, m) + if err != nil { + return &ChatAPI{}, err + } + r.keybase = *c.keybase + return &r, nil +} + // Read fetches chat messages from a conversation. By default, 10 messages will // be fetched at a time. However, if count is passed, then that is the number of // messages that will be fetched. diff --git a/types.go b/types.go index 98eea8d..ad279a1 100644 --- a/types.go +++ b/types.go @@ -22,10 +22,10 @@ type ChatAPI struct { Message string `json:"message,omitempty"` ID int `json:"id,omitempty"` Ratelimits []rateLimits `json:"ratelimits,omitempty"` - Notification *notification `json:"notification"` + Notification *notification `json:"notification,omitempty"` Result *result `json:"result,omitempty"` - Pagination *pagination `json:"pagination"` - Error *Error `json:"error"` + Pagination *pagination `json:"pagination,omitempty"` + Error *Error `json:"error,omitempty"` keybase Keybase // Some methods will need this, so I'm passing it but keeping it unexported } @@ -217,7 +217,7 @@ type notification struct { // Channel holds information about a conversation type Channel struct { - Name string `json:"name"` + Name string `json:"name,omitempty"` Public bool `json:"public,omitempty"` MembersType string `json:"members_type,omitempty"` TopicType string `json:"topic_type,omitempty"` @@ -241,7 +241,7 @@ type options struct { MsgID int `json:"msg_id,omitempty"` GameID string `json:"game_id,omitempty"` - Name string `json:"name"` + Name string `json:"name,omitempty"` Public bool `json:"public,omitempty"` MembersType string `json:"members_type,omitempty"` TopicType string `json:"topic_type,omitempty"`