Browse Source

Version 2.0.0-alpha1 (really)

Sam 4 years ago
parent
commit
abed5bc066
  1. 797
      v2/chat.go
  2. 2
      v2/docs.go
  3. 29
      v2/docs_test.go
  4. 36
      v2/keybase.go
  5. 218
      v2/kvstore.go
  6. 378
      v2/types.go

797
v2/chat.go

@ -2,47 +2,19 @@ package keybase @@ -2,47 +2,19 @@ package keybase
import (
"bufio"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"os/exec"
"strings"
"time"
)
// Returns a string representation of a message id suitable for use in a
// pagination struct
func getID(id uint) 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)
}
"samhofi.us/x/keybase/types/chat1"
"samhofi.us/x/keybase/types/keybase1"
"samhofi.us/x/keybase/types/stellar1"
)
// Creates a string of a json-encoded channel to pass to keybase chat api-listen --filter-channel
func createFilterString(channel Channel) string {
func createFilterString(channel chat1.ChatChannel) string {
if channel.Name == "" {
return ""
}
@ -51,7 +23,7 @@ func createFilterString(channel Channel) string { @@ -51,7 +23,7 @@ func createFilterString(channel Channel) string {
}
// Creates a string of json-encoded channels to pass to keybase chat api-listen --filter-channels
func createFiltersString(channels []Channel) string {
func createFiltersString(channels []chat1.ChatChannel) string {
if len(channels) == 0 {
return ""
}
@ -60,7 +32,7 @@ func createFiltersString(channels []Channel) string { @@ -60,7 +32,7 @@ func createFiltersString(channels []Channel) string {
}
// Run `keybase chat api-listen` to get new messages coming into keybase and send them into the channel
func getNewMessages(k *Keybase, c chan<- ChatAPI, execOptions []string) {
func getNewMessages(k *Keybase, subs *subscriptionChannels, execOptions []string) {
execString := []string{"chat", "api-listen"}
if len(execOptions) > 0 {
execString = append(execString, execOptions...)
@ -70,75 +42,128 @@ func getNewMessages(k *Keybase, c chan<- ChatAPI, execOptions []string) { @@ -70,75 +42,128 @@ func getNewMessages(k *Keybase, c chan<- ChatAPI, execOptions []string) {
stdOut, _ := execCmd.StdoutPipe()
execCmd.Start()
scanner := bufio.NewScanner(stdOut)
go func(scanner *bufio.Scanner, c chan<- ChatAPI) {
for scanner.Scan() {
var jsonData ChatAPI
json.Unmarshal([]byte(scanner.Text()), &jsonData)
if jsonData.ErrorRaw != nil {
var errorListen = string(*jsonData.ErrorRaw)
jsonData.ErrorListen = &errorListen
go func(scanner *bufio.Scanner, subs *subscriptionChannels) {
for {
scanner.Scan()
var subType subscriptionType
t := scanner.Text()
json.Unmarshal([]byte(t), &subType)
switch subType.Type {
case "chat":
var notification chat1.MsgNotification
if err := json.Unmarshal([]byte(t), &notification); err != nil {
subs.error <- err
break
}
if notification.Msg != nil {
subs.chat <- *notification.Msg
}
case "chat_conv":
var notification chat1.ConvNotification
if err := json.Unmarshal([]byte(t), &notification); err != nil {
subs.error <- err
break
}
if notification.Conv != nil {
subs.conversation <- *notification.Conv
}
case "wallet":
var holder paymentHolder
if err := json.Unmarshal([]byte(t), &holder); err != nil {
subs.error <- err
break
}
subs.wallet <- holder.Payment
default:
continue
}
c <- jsonData
}
}(scanner, c)
}(scanner, subs)
execCmd.Wait()
}
}
// Run runs `keybase chat api-listen`, and passes incoming messages to the message handler func
func (k *Keybase) Run(handler func(ChatAPI), options ...RunOptions) {
var heartbeatFreq int64
func (k *Keybase) Run(handlers Handlers, options *RunOptions) {
var channelCapacity = 100
runOptions := make([]string, 0)
if len(options) > 0 {
if options[0].Capacity > 0 {
channelCapacity = options[0].Capacity
}
if options[0].Heartbeat > 0 {
heartbeatFreq = options[0].Heartbeat
if handlers.WalletHandler != nil {
runOptions = append(runOptions, "--wallet")
}
if handlers.ConversationHandler != nil {
runOptions = append(runOptions, "--convs")
}
if options != nil {
if options.Capacity > 0 {
channelCapacity = options.Capacity
}
if options[0].Local {
if options.Local {
runOptions = append(runOptions, "--local")
}
if options[0].HideExploding {
if options.HideExploding {
runOptions = append(runOptions, "--hide-exploding")
}
if options[0].Dev {
if options.Dev {
runOptions = append(runOptions, "--dev")
}
if len(options[0].FilterChannels) > 0 {
if len(options.FilterChannels) > 0 {
runOptions = append(runOptions, "--filter-channels")
runOptions = append(runOptions, createFiltersString(options[0].FilterChannels))
runOptions = append(runOptions, createFiltersString(options.FilterChannels))
}
if options[0].FilterChannel.Name != "" {
if options.FilterChannel.Name != "" {
runOptions = append(runOptions, "--filter-channel")
runOptions = append(runOptions, createFilterString(options[0].FilterChannel))
runOptions = append(runOptions, createFilterString(options.FilterChannel))
}
}
c := make(chan ChatAPI, channelCapacity)
defer close(c)
if heartbeatFreq > 0 {
go heartbeat(c, time.Duration(heartbeatFreq)*time.Minute)
}
go getNewMessages(k, c, runOptions)
for {
go handler(<-c)
}
}
// heartbeat sends a message through the channel with a message type of `heartbeat`
func heartbeat(c chan<- ChatAPI, freq time.Duration) {
m := ChatAPI{
Type: "heartbeat",
chatCh := make(chan chat1.MsgSummary, channelCapacity)
convCh := make(chan chat1.ConvSummary, channelCapacity)
walletCh := make(chan stellar1.PaymentDetailsLocal, channelCapacity)
errorCh := make(chan error, channelCapacity)
subs := &subscriptionChannels{
chat: chatCh,
conversation: convCh,
wallet: walletCh,
error: errorCh,
}
count := 0
defer close(subs.chat)
defer close(subs.conversation)
defer close(subs.wallet)
defer close(subs.error)
go getNewMessages(k, subs, runOptions)
for {
time.Sleep(freq)
m.Msg.ID = count
c <- m
count++
select {
case chatMsg := <-subs.chat:
if handlers.ChatHandler == nil {
continue
}
chatHandler := *handlers.ChatHandler
go chatHandler(chatMsg)
case walletMsg := <-subs.wallet:
if handlers.WalletHandler == nil {
continue
}
walletHandler := *handlers.WalletHandler
go walletHandler(walletMsg)
case newConv := <-subs.conversation:
if handlers.ConversationHandler == nil {
continue
}
convHandler := *handlers.ConversationHandler
go convHandler(newConv)
case errMsg := <-subs.error:
if handlers.ErrorHandler == nil {
continue
}
errHandler := *handlers.ErrorHandler
go errHandler(errMsg)
}
}
}
@ -165,289 +190,391 @@ func chatAPIOut(k *Keybase, c ChatAPI) (ChatAPI, error) { @@ -165,289 +190,391 @@ func chatAPIOut(k *Keybase, c ChatAPI) (ChatAPI, error) {
return r, nil
}
// Send sends a chat message
func (c Chat) Send(message ...string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
}
m.Params.Options = options{
Message: &mesg{},
// SendMessage sends a chat message
func (k *Keybase) SendMessage(method string, options SendMessageOptions) (chat1.SendRes, error) {
type res struct {
Result chat1.SendRes `json:"result"`
Error *Error `json:"error,omitempty"`
}
m.Method = "send"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Message.Body = strings.Join(message, " ")
var r res
r, err := chatAPIOut(c.keybase, m)
arg := newSendMessageArg(options)
arg.Method = method
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return r, err
return r.Result, err
}
return r, nil
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
if r.Error != nil {
return r.Result, fmt.Errorf("%v", r.Error.Message)
}
return r.Result, nil
}
// SendEphemeral sends an exploding chat message, with specified duration
func (c Chat) SendEphemeral(duration time.Duration, message ...string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// SendMessageByChannel sends a chat message to a channel
func (k *Keybase) SendMessageByChannel(channel chat1.ChatChannel, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
}
m.Params.Options = options{
Message: &mesg{},
return k.SendMessage("send", opts)
}
// SendMessageByConvID sends a chat message to a conversation id
func (k *Keybase) SendMessageByConvID(convID chat1.ConvIDStr, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
}
m.Params.Options.ExplodingLifetime.Duration = duration
m.Method = "send"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Message.Body = strings.Join(message, " ")
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return r, err
return k.SendMessage("send", opts)
}
// SendEphemeralByChannel sends an exploding chat message to a channel
func (k *Keybase) SendEphemeralByChannel(channel chat1.ChatChannel, duration time.Duration, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
ExplodingLifetime: &ExplodingLifetime{duration},
}
return r, nil
return k.SendMessage("send", opts)
}
// Reply sends a reply to a chat message
func (c Chat) Reply(replyTo int, message ...string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// SendEphemeralByConvID sends an exploding chat message to a conversation id
func (k *Keybase) SendEphemeralByConvID(convID chat1.ConvIDStr, duration time.Duration, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
ExplodingLifetime: &ExplodingLifetime{duration},
}
m.Params.Options = options{
Message: &mesg{},
return k.SendMessage("send", opts)
}
// ReplyByChannel sends a reply message to a channel
func (k *Keybase) ReplyByChannel(channel chat1.ChatChannel, replyTo chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
ReplyTo: &replyTo,
}
m.Method = "send"
m.Params.Options.Channel = &c.Channel
m.Params.Options.ReplyTo = replyTo
m.Params.Options.Message.Body = strings.Join(message, " ")
return k.SendMessage("send", opts)
}
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return r, err
// ReplyByConvID sends a reply message to a conversation id
func (k *Keybase) ReplyByConvID(convID chat1.ConvIDStr, replyTo chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
ReplyTo: &replyTo,
}
return r, nil
return k.SendMessage("send", opts)
}
// Edit edits a previously sent chat message
func (c Chat) Edit(messageID int, message ...string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// EditByChannel sends an edit message to a channel
func (k *Keybase) EditByChannel(channel chat1.ChatChannel, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
MessageID: msgID,
}
m.Params.Options = options{
Message: &mesg{},
return k.SendMessage("edit", opts)
}
// EditByConvID sends an edit message to a conversation id
func (k *Keybase) EditByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
MessageID: msgID,
}
m.Method = "edit"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Message.Body = strings.Join(message, " ")
m.Params.Options.MessageID = messageID
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return r, err
return k.SendMessage("edit", opts)
}
// ReactByChannel reacts to a message in a channel
func (k *Keybase) ReactByChannel(channel chat1.ChatChannel, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
MessageID: msgID,
}
return r, nil
return k.SendMessage("reaction", opts)
}
// React sends a reaction to a message.
func (c Chat) React(messageID int, reaction string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// ReactByConvID reacts to a message in a conversation id
func (k *Keybase) ReactByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
Message: SendMessageBody{
Body: fmt.Sprintf(message, a...),
},
MessageID: msgID,
}
m.Params.Options = options{
Message: &mesg{},
return k.SendMessage("reaction", opts)
}
// DeleteByChannel reacts to a message in a channel
func (k *Keybase) DeleteByChannel(channel chat1.ChatChannel, msgID chat1.MessageID) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
MessageID: msgID,
}
m.Method = "reaction"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Message.Body = reaction
m.Params.Options.MessageID = messageID
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return r, err
return k.SendMessage("delete", opts)
}
// DeleteByConvID reacts to a message in a conversation id
func (k *Keybase) DeleteByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: convID,
MessageID: msgID,
}
return r, nil
return k.SendMessage("delete", opts)
}
// Delete deletes a chat message
func (c Chat) Delete(messageID int) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// GetConversations returns a list of all conversations.
func (k *Keybase) GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error) {
type res struct {
Result []chat1.ConvSummary `json:"result"`
Error *Error `json:"error,omitempty"`
}
m.Method = "delete"
m.Params.Options.Channel = &c.Channel
m.Params.Options.MessageID = messageID
r, err := chatAPIOut(c.keybase, m)
var r res
opts := SendMessageOptions{
UnreadOnly: unreadOnly,
}
arg := newSendMessageArg(opts)
arg.Method = "list"
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return r, err
return r.Result, err
}
return r, nil
}
// ChatList returns a list of all conversations.
// You can pass a Channel to use as a filter here, but you'll probably want to
// leave the TopicName empty.
func (k *Keybase) ChatList(opts ...Channel) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
if len(opts) > 0 {
m.Params.Options.Name = opts[0].Name
m.Params.Options.Public = opts[0].Public
m.Params.Options.MembersType = opts[0].MembersType
m.Params.Options.TopicType = opts[0].TopicType
m.Params.Options.TopicName = opts[0].TopicName
if r.Error != nil {
return r.Result, fmt.Errorf("%v", r.Error.Message)
}
m.Method = "list"
r, err := chatAPIOut(k, m)
return r, err
return r.Result, nil
}
// ReadMessage fetches the chat message with the specified message id from a conversation.
func (c Chat) ReadMessage(messageID int) (*ChatAPI, error) {
m := ChatAPI{
Params: &params{},
}
m.Params.Options = options{
Pagination: &pagination{},
// Read fetches chat messages
func (k *Keybase) Read(options ReadMessageOptions) (chat1.Thread, error) {
type res struct {
Result chat1.Thread `json:"result"`
Error *Error `json:"error"`
}
var r res
m.Method = "read"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Pagination.Num = 1
arg := newReadMessageArg(options)
m.Params.Options.Pagination.Previous = getID(uint(messageID - 1))
jsonBytes, _ := json.Marshal(arg)
r, err := chatAPIOut(c.keybase, m)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return &r, err
return r.Result, 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.
func (c Chat) Read(count ...int) (*ChatAPI, error) {
m := ChatAPI{
Params: &params{},
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
m.Params.Options = options{
Pagination: &pagination{},
if r.Error != nil {
return r.Result, fmt.Errorf("%v", r.Error.Message)
}
m.Method = "read"
m.Params.Options.Channel = &c.Channel
if len(count) == 0 {
m.Params.Options.Pagination.Num = 10
} else {
m.Params.Options.Pagination.Num = count[0]
return r.Result, nil
}
// ReadChannel fetches chat messages for a channel
func (k *Keybase) ReadChannel(channel chat1.ChatChannel) (chat1.Thread, error) {
opts := ReadMessageOptions{
Channel: channel,
}
return k.Read(opts)
}
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return &r, err
// ReadChannelNext fetches the next page of messages for a chat channel.
func (k *Keybase) ReadChannelNext(channel chat1.ChatChannel, next []byte, num int) (chat1.Thread, error) {
page := chat1.Pagination{
Next: next,
Num: num,
}
r.keybase = *c.keybase
return &r, nil
opts := ReadMessageOptions{
Channel: channel,
Pagination: &page,
}
return k.Read(opts)
}
// Next fetches the next page of chat messages that were fetched with Read. By
// default, Next will fetch the same amount of messages that were originally
// fetched with Read. However, if count is passed, then that is the number of
// messages that will be fetched.
func (c *ChatAPI) Next(count ...int) (*ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// ReadChannelPrevious fetches the previous page of messages for a chat channel
func (k *Keybase) ReadChannelPrevious(channel chat1.ChatChannel, previous []byte, num int) (chat1.Thread, error) {
page := chat1.Pagination{
Previous: previous,
Num: num,
}
m.Params.Options = options{
Pagination: &pagination{},
opts := ReadMessageOptions{
Channel: channel,
Pagination: &page,
}
return k.Read(opts)
}
m.Method = "read"
m.Params.Options.Channel = &c.Result.Messages[0].Msg.Channel
if len(count) == 0 {
m.Params.Options.Pagination.Num = c.Result.Pagination.Num
} else {
m.Params.Options.Pagination.Num = count[0]
// ReadConversation fetches chat messages for a conversation
func (k *Keybase) ReadConversation(conv chat1.ConvIDStr) (chat1.Thread, error) {
opts := ReadMessageOptions{
ConversationID: conv,
}
m.Params.Options.Pagination.Next = c.Result.Pagination.Next
return k.Read(opts)
}
result, err := chatAPIOut(&c.keybase, m)
if err != nil {
return &result, err
// ReadConversationNext fetches the next page of messages for a conversation.
func (k *Keybase) ReadConversationNext(conv chat1.ConvIDStr, next []byte, num int) (chat1.Thread, error) {
page := chat1.Pagination{
Next: next,
Num: num,
}
opts := ReadMessageOptions{
ConversationID: conv,
Pagination: &page,
}
k := c.keybase
*c = result
c.keybase = k
return c, nil
return k.Read(opts)
}
// Previous fetches the previous page of chat messages that were fetched with Read.
// By default, Previous will fetch the same amount of messages that were
// originally fetched with Read. However, if count is passed, then that is the
// number of messages that will be fetched.
func (c *ChatAPI) Previous(count ...int) (*ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// ReadConversationPrevious fetches the previous page of messages for a chat channel
func (k *Keybase) ReadConversationPrevious(conv chat1.ConvIDStr, previous []byte, num int) (chat1.Thread, error) {
page := chat1.Pagination{
Previous: previous,
Num: num,
}
m.Params.Options = options{
Pagination: &pagination{},
opts := ReadMessageOptions{
ConversationID: conv,
Pagination: &page,
}
return k.Read(opts)
}
m.Method = "read"
m.Params.Options.Channel = &c.Result.Messages[0].Msg.Channel
if len(count) == 0 {
m.Params.Options.Pagination.Num = c.Result.Pagination.Num
} else {
m.Params.Options.Pagination.Num = count[0]
// UploadToChannel attaches a file to a channel
// The filename must be an absolute path
func (k *Keybase) UploadToChannel(channel chat1.ChatChannel, title string, filename string) (chat1.SendRes, error) {
opts := SendMessageOptions{
Channel: channel,
Title: title,
Filename: filename,
}
m.Params.Options.Pagination.Previous = c.Result.Pagination.Previous
result, err := chatAPIOut(&c.keybase, m)
if err != nil {
return &result, err
return k.SendMessage("attach", opts)
}
// UploadToConversation attaches a file to a conversation
// The filename must be an absolute path
func (k *Keybase) UploadToConversation(conv chat1.ConvIDStr, title string, filename string) (chat1.SendRes, error) {
opts := SendMessageOptions{
ConversationID: conv,
Title: title,
Filename: filename,
}
k := c.keybase
*c = result
c.keybase = k
return c, nil
return k.SendMessage("attach", opts)
}
// Upload attaches a file to a conversation
// The filepath must be an absolute path
func (c Chat) Upload(title string, filepath string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// Download downloads a file
func (k *Keybase) Download(options DownloadOptions) error {
type res struct {
Error *Error `json:"error"`
}
m.Method = "attach"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Filename = filepath
m.Params.Options.Title = title
var r res
r, err := chatAPIOut(c.keybase, m)
arg := newDownloadArg(options)
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return r, err
return err
}
return r, nil
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return err
}
if r.Error != nil {
return fmt.Errorf("%v", r.Error.Message)
}
return nil
}
// Download downloads a file from a conversation
func (c Chat) Download(messageID int, filepath string) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// DownloadFromChannel downloads a file from a channel
func (k *Keybase) DownloadFromChannel(channel chat1.ChatChannel, msgID chat1.MessageID, output string) error {
opts := DownloadOptions{
Channel: channel,
MessageID: msgID,
Output: output,
}
m.Method = "download"
m.Params.Options.Channel = &c.Channel
m.Params.Options.Output = filepath
m.Params.Options.MessageID = messageID
return k.Download(opts)
}
r, err := chatAPIOut(c.keybase, m)
if err != nil {
return r, err
// DownloadFromConversation downloads a file from a conversation
func (k *Keybase) DownloadFromConversation(conv chat1.ConvIDStr, msgID chat1.MessageID, output string) error {
opts := DownloadOptions{
ConversationID: conv,
MessageID: msgID,
Output: output,
}
return r, nil
return k.Download(opts)
}
// LoadFlip returns the results of a flip
@ -517,40 +644,104 @@ func (c Chat) Mark(messageID int) (ChatAPI, error) { @@ -517,40 +644,104 @@ func (c Chat) Mark(messageID int) (ChatAPI, error) {
return r, nil
}
// AdvertiseCommands sends bot command advertisements.
// Valid values for the `Typ` field in chat1.AdvertiseCommandAPIParam are
// "public", "teamconvs", and "teammembers"
func (k *Keybase) AdvertiseCommands(options AdvertiseCommandsOptions) error {
type res struct {
Error *Error `json:"error,omitempty"`
}
var r res
arg := newAdvertiseCommandsArg(options)
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return err
}
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return err
}
if r.Error != nil {
return fmt.Errorf("%v", r.Error.Message)
}
return nil
}
// ClearCommands clears bot advertisements
func (k *Keybase) ClearCommands() (ChatAPI, error) {
m := ChatAPI{}
m.Method = "clearcommands"
func (k *Keybase) ClearCommands() error {
type res struct {
Error *Error `json:"error,omitempty"`
}
var r res
r, err := chatAPIOut(k, m)
cmdOut, err := k.Exec("chat", "api", "-m", `{"method": "clearcommands"}`)
if err != nil {
return r, err
return err
}
return r, nil
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return err
}
if r.Error != nil {
return fmt.Errorf("%v", r.Error.Message)
}
return nil
}
// AdvertiseCommands sets up bot command advertisements
// This method allows you to set up multiple different types of advertisements at once.
// Use this method if you have commands whose visibility differs from each other.
func (k *Keybase) AdvertiseCommands(advertisements []BotAdvertisement) (ChatAPI, error) {
m := ChatAPI{
Params: &params{},
// ListMembers returns member information for a channel or conversation
func (k *Keybase) ListMembers(options ListMembersOptions) (keybase1.TeamDetails, error) {
type res struct {
Result keybase1.TeamDetails `json:"result"`
Error *Error `json:"error,omitempty"`
}
var r res
arg := newListMembersArg(options)
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
if err != nil {
return r.Result, err
}
m.Method = "advertisecommands"
m.Params.Options.BotAdvertisements = advertisements
r, err := chatAPIOut(k, m)
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r, err
return r.Result, err
}
return r, nil
if r.Error != nil {
return r.Result, fmt.Errorf("%v", r.Error.Message)
}
return r.Result, nil
}
// ListMembersOfChannel returns member information for a channel
func (k *Keybase) ListMembersOfChannel(channel chat1.ChatChannel) (keybase1.TeamDetails, error) {
opts := ListMembersOptions{
Channel: channel,
}
return k.ListMembers(opts)
}
// AdvertiseCommand sets up bot command advertisements
// This method allows you to set up one type of advertisement.
// Use this method if you have commands whose visibility should all be the same.
func (k *Keybase) AdvertiseCommand(advertisement BotAdvertisement) (ChatAPI, error) {
return k.AdvertiseCommands([]BotAdvertisement{
advertisement,
})
// ListMembersOfConversation returns member information for a conversation
func (k *Keybase) ListMembersOfConversation(convID chat1.ConvIDStr) (keybase1.TeamDetails, error) {
opts := ListMembersOptions{
ConversationID: convID,
}
return k.ListMembers(opts)
}

2
v2/docs.go

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
The keybase package implements an interface for interacting with the Keybase Chat, Team, and Wallet APIs
Package keybase implements an interface for interacting with the Keybase Chat, Team, and Wallet APIs
I've tried to follow Keybase's JSON API as closely as possible, so if you're stuck on anything, or wondering
why things are organized in a certain way, it's most likely due to that. It may be helpful to look at the

29
v2/docs_test.go

@ -1,20 +1,35 @@ @@ -1,20 +1,35 @@
package keybase
func ExampleKeybase_AdvertiseCommand() {
import "samhofi.us/x/keybase/types/chat1"
func ExampleKeybase_AdvertiseCommands() {
var k = NewKeybase()
// Clear out any previously advertised commands
k.ClearCommands()
// Create BotAdvertisement
c := BotAdvertisement{
Type: "public",
BotCommands: []BotCommand{
NewBotCommand("help", "Get help using this bot", "!help <command>"),
NewBotCommand("hello", "Say hello", "!hello"),
ads := AdvertiseCommandsOptions{
Alias: "RSS Bot",
Advertisements: []chat1.AdvertiseCommandAPIParam{
{
Typ: "public",
Commands: []chat1.UserBotCommandInput{
{
Name: "rss addfeed",
Description: "Add RSS feed",
Usage: "<url>",
},
{
Name: "rss delfeed",
Description: "Remove RSS feed",
Usage: "<url>",
},
},
},
},
}
// Send advertisement
k.AdvertiseCommand(c)
k.AdvertiseCommands(ads)
}

36
v2/keybase.go

@ -5,6 +5,8 @@ import ( @@ -5,6 +5,8 @@ import (
"fmt"
"os/exec"
"strings"
"samhofi.us/x/keybase/types/chat1"
)
// Possible MemberTypes
@ -38,30 +40,6 @@ func NewKeybase(path ...string) *Keybase { @@ -38,30 +40,6 @@ func NewKeybase(path ...string) *Keybase {
return k
}
// NewBotCommand returns a new BotCommand instance
func NewBotCommand(name, description, usage string, extendedDescription ...BotCommandExtendedDescription) BotCommand {
result := BotCommand{
Name: name,
Description: description,
Usage: usage,
}
if len(extendedDescription) > 0 {
result.ExtendedDescription = &extendedDescription[0]
}
return result
}
// NewBotCommandExtendedDescription
func NewBotCommandExtendedDescription(title, desktopBody, mobileBody string) BotCommandExtendedDescription {
return BotCommandExtendedDescription{
Title: title,
DesktopBody: desktopBody,
MobileBody: mobileBody,
}
}
// Exec executes the given Keybase command
func (k *Keybase) Exec(command ...string) ([]byte, error) {
out, err := exec.Command(k.Path, command...).Output()
@ -72,7 +50,7 @@ func (k *Keybase) Exec(command ...string) ([]byte, error) { @@ -72,7 +50,7 @@ func (k *Keybase) Exec(command ...string) ([]byte, error) {
}
// NewChat returns a new Chat instance
func (k *Keybase) NewChat(channel Channel) Chat {
func (k *Keybase) NewChat(channel chat1.ChatChannel) Chat {
return Chat{
keybase: k,
Channel: channel,
@ -87,14 +65,6 @@ func (k *Keybase) NewTeam(name string) Team { @@ -87,14 +65,6 @@ func (k *Keybase) NewTeam(name string) Team {
}
}
// NewKV returns a new KV instance
func (k *Keybase) NewKV(team string) KV {
return KV{
keybase: k,
Team: team,
}
}
// NewWallet returns a new Wallet instance
func (k *Keybase) NewWallet() Wallet {
return Wallet{

218
v2/kvstore.go

@ -2,136 +2,190 @@ package keybase @@ -2,136 +2,190 @@ package keybase
import (
"encoding/json"
"errors"
"fmt"
"samhofi.us/x/keybase/types/keybase1"
)
// kvAPIOut sends a JSON request to the kvstore API and returns its response.
func kvAPIOut(k *Keybase, kv KVAPI) (KVAPI, error) {
jsonBytes, _ := json.Marshal(kv)
// KVListNamespaces returns all namespaces for a team
func (k *Keybase) KVListNamespaces(team *string) (keybase1.KVListNamespaceResult, error) {
type res struct {
Result keybase1.KVListNamespaceResult `json:"result"`
Error *Error `json:"error"`
}
var r res
arg := newKVArg("list", KVOptions{
Team: team,
})
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes))
if err != nil {
return KVAPI{}, err
return r.Result, err
}
var r KVAPI
if err := json.Unmarshal(cmdOut, &r); err != nil {
return KVAPI{}, err
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
if r.Error != nil {
return KVAPI{}, errors.New(r.Error.Message)
return r.Result, fmt.Errorf("%s", r.Error.Message)
}
return r, nil
return r.Result, nil
}
// Namespaces returns all namespaces for a team
func (kv KV) Namespaces() (KVAPI, error) {
m := KVAPI{
Params: &kvParams{},
}
m.Params.Options = kvOptions{
Team: kv.Team,
// KVListKeys returns all non-deleted keys for a namespace
func (k *Keybase) KVListKeys(team *string, namespace string) (keybase1.KVListEntryResult, error) {
type res struct {
Result keybase1.KVListEntryResult `json:"result"`
Error *Error `json:"error"`
}
var r res
arg := newKVArg("list", KVOptions{
Team: team,
Namespace: &namespace,
})
m.Method = "list"
jsonBytes, _ := json.Marshal(arg)
r, err := kvAPIOut(kv.keybase, m)
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes))
if err != nil {
return r, err
return r.Result, err
}
return r, nil
}
// Keys returns all non-deleted keys for a namespace
func (kv KV) Keys(namespace string) (KVAPI, error) {
m := KVAPI{
Params: &kvParams{},
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
m.Params.Options = kvOptions{
Team: kv.Team,
Namespace: namespace,
if r.Error != nil {
return r.Result, fmt.Errorf("%s", r.Error.Message)
}
m.Method = "list"
return r.Result, nil
}
r, err := kvAPIOut(kv.keybase, m)
if err != nil {
return r, err
// KVGet returns an entry
func (k *Keybase) KVGet(team *string, namespace string, key string) (keybase1.KVGetResult, error) {
type res struct {
Result keybase1.KVGetResult `json:"result"`
Error *Error `json:"error"`
}
return r, nil
}
var r res
arg := newKVArg("get", KVOptions{
Team: team,
Namespace: &namespace,
EntryKey: &key,
})
// Get returns an entry
func (kv KV) Get(namespace string, key string, revision ...uint) (KVAPI, error) {
m := KVAPI{
Params: &kvParams{},
jsonBytes, _ := json.Marshal(arg)
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes))
if err != nil {
return r.Result, err
}
m.Params.Options = kvOptions{
Team: kv.Team,
Namespace: namespace,
EntryKey: key,
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
if len(revision) > 0 {
m.Params.Options.Revision = revision[0]
if r.Error != nil {
return r.Result, fmt.Errorf("%s", r.Error.Message)
}
m.Method = "get"
return r.Result, nil
}
r, err := kvAPIOut(kv.keybase, m)
if err != nil {
return r, err
// KVPutWithRevision puts an entry, specifying the revision number
func (k *Keybase) KVPutWithRevision(team *string, namespace string, key string, value string, revision int) (keybase1.KVPutResult, error) {
type res struct {
Result keybase1.KVPutResult `json:"result"`
Error *Error `json:"error"`
}
return r, nil
}
var r res
// Put adds an entry
func (kv KV) Put(namespace string, key string, value string, revision ...uint) (KVAPI, error) {
m := KVAPI{
Params: &kvParams{},
opts := KVOptions{
Team: team,
Namespace: &namespace,
EntryKey: &key,
EntryValue: &value,
}
m.Params.Options = kvOptions{
Team: kv.Team,
Namespace: namespace,
EntryKey: key,
EntryValue: value,
if revision != 0 {
opts.Revision = &revision
}
if len(revision) > 0 {
m.Params.Options.Revision = revision[0]
}
arg := newKVArg("put", opts)
m.Method = "put"
jsonBytes, _ := json.Marshal(arg)
r, err := kvAPIOut(kv.keybase, m)
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes))
if err != nil {
return r, err
return r.Result, err
}
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r.Result, err
}
return r, nil
}
// Delete removes an entry
func (kv KV) Delete(namespace string, key string, revision ...uint) (KVAPI, error) {
m := KVAPI{
Params: &kvParams{},
if r.Error != nil {
return r.Result, fmt.Errorf("%s", r.Error.Message)
}
m.Params.Options = kvOptions{
Team: kv.Team,
Namespace: namespace,
EntryKey: key,
return r.Result, nil
}
// KVPut puts an entry
func (k *Keybase) KVPut(team *string, namespace string, key string, value string) (keybase1.KVPutResult, error) {
return k.KVPutWithRevision(team, namespace, key, value, 0)
}
// KVDeleteWithRevision deletes an entry, specifying the revision number
func (k *Keybase) KVDeleteWithRevision(team *string, namespace string, key string, revision int) (keybase1.KVDeleteEntryResult, error) {
type res struct {
Result keybase1.KVDeleteEntryResult `json:"result"`
Error *Error `json:"error"`
}
var r res
if len(revision) > 0 {
m.Params.Options.Revision = revision[0]
opts := KVOptions{
Team: team,
Namespace: &namespace,
EntryKey: &key,
}
if revision != 0 {
opts.Revision = &revision
}
arg := newKVArg("del", opts)
jsonBytes, _ := json.Marshal(arg)
m.Method = "del"
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes))
if err != nil {
return r.Result, err
}
r, err := kvAPIOut(kv.keybase, m)
err = json.Unmarshal(cmdOut, &r)
if err != nil {
return r, err
return r.Result, err
}
return r, nil
if r.Error != nil {
return r.Result, fmt.Errorf("%s", r.Error.Message)
}
return r.Result, nil
}
// KVDelete deletes an entry
func (k *Keybase) KVDelete(team *string, namespace string, key string) (keybase1.KVDeleteEntryResult, error) {
return k.KVDeleteWithRevision(team, namespace, key, 0)
}

378
v2/types.go

@ -5,18 +5,233 @@ import ( @@ -5,18 +5,233 @@ import (
"fmt"
"strings"
"time"
"samhofi.us/x/keybase/types/chat1"
"samhofi.us/x/keybase/types/stellar1"
)
// RunOptions holds a set of options to be passed to Run
type RunOptions struct {
Capacity int // Channel capacity for the buffered channel that holds messages. Defaults to 100 if not set
Heartbeat int64 // Send a heartbeat through the channel every X minutes (0 = off)
Local bool // Subscribe to local messages
HideExploding bool // Ignore exploding messages
Dev bool // Subscribe to dev channel messages
Wallet bool // Subscribe to wallet events
FilterChannel Channel // Only subscribe to messages from specified channel
FilterChannels []Channel // Only subscribe to messages from specified channels
Capacity int // Channel capacity for the buffered channel that holds messages. Defaults to 100 if not set
Local bool // Subscribe to local messages
HideExploding bool // Ignore exploding messages
Dev bool // Subscribe to dev channel messages
FilterChannel chat1.ChatChannel // Only subscribe to messages from specified channel
FilterChannels []chat1.ChatChannel // Only subscribe to messages from specified channels
}
type subscriptionType struct {
Type string `json:"type"`
}
type paymentHolder struct {
Payment stellar1.PaymentDetailsLocal `json:"notification"`
}
// Handlers holds pointers to handlers that you want to implement inside the bot type
type Handlers struct {
ChatHandler *func(chat1.MsgSummary)
ConversationHandler *func(chat1.ConvSummary)
WalletHandler *func(stellar1.PaymentDetailsLocal)
ErrorHandler *func(error)
}
// subscriptionChannels are passed to getNewMessages to return data through channels
type subscriptionChannels struct {
chat chan chat1.MsgSummary
conversation chan chat1.ConvSummary
wallet chan stellar1.PaymentDetailsLocal
error chan error
}
// Error holds an error message returned by the API
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
// ExplodingLifetime holds a time duration for ephemeral messages
type ExplodingLifetime struct {
time.Duration
}
// UnmarshalJSON unpacks exploding lifetimes from JSON
func (d *ExplodingLifetime) UnmarshalJSON(b []byte) (err error) {
d.Duration, err = time.ParseDuration(strings.Trim(string(b), `"`))
return
}
// MarshalJSON packs exploding lifetimes to JSON
func (d *ExplodingLifetime) MarshalJSON() (b []byte, err error) {
return []byte(fmt.Sprintf(`"%s"`, d.String())), nil
}
// SendMessageBody holds the body string for all send and reply methods
type SendMessageBody struct {
Body string
}
// SendMessageOptions holds a set of options to be passed to SendMessage
type SendMessageOptions struct {
Channel chat1.ChatChannel `json:"channel,omitempty"`
ConversationID chat1.ConvIDStr `json:"conversation_id,omitempty"`
Message SendMessageBody `json:",omitempty"`
Filename string `json:"filename,omitempty"`
Title string `json:"title,omitempty"`
MessageID chat1.MessageID `json:"message_id,omitempty"`
ConfirmLumenSend bool `json:"confirm_lumen_send"`
ReplyTo *chat1.MessageID `json:"reply_to,omitempty"`
ExplodingLifetime *ExplodingLifetime `json:"exploding_lifetime,omitempty"`
UnreadOnly bool `json:"unread_only,omitempty"`
}
type sendMessageParams struct {
Options SendMessageOptions
}
type sendMessageArg struct {
Method string
Params sendMessageParams
}
func newSendMessageArg(options SendMessageOptions) sendMessageArg {
return sendMessageArg{
Method: "send",
Params: sendMessageParams{
Options: options,
},
}
}
// ReadMessageOptions holds a set of options to be passed to Read
type ReadMessageOptions struct {
Channel chat1.ChatChannel `json:"channel,omitempty"`
ConversationID chat1.ConvIDStr `json:"conversation_id,omitempty"`
Pagination *chat1.Pagination `json:"pagination,omitempty"`
Peek bool `json:"peek"`
UnreadOnly bool `json:"unread_only"`
FailOffline bool `json:"fail_offline"`
}
type readMessageParams struct {
Options ReadMessageOptions
}
type readMessageArg struct {
Method string
Params readMessageParams
}
func newReadMessageArg(options ReadMessageOptions) readMessageArg {
return readMessageArg{
Method: "read",
Params: readMessageParams{
Options: options,
},
}
}
// AdvertiseCommandsOptions holds a set of options to be passed to AdvertiseCommands
type AdvertiseCommandsOptions struct {
Alias string
Advertisements []chat1.AdvertiseCommandAPIParam
}
type advertiseCommandsParams struct {
Options AdvertiseCommandsOptions
}
type advertiseCommandsArg struct {
Method string
Params advertiseCommandsParams
}
func newAdvertiseCommandsArg(options AdvertiseCommandsOptions) advertiseCommandsArg {
return advertiseCommandsArg{
Method: "advertisecommands",
Params: advertiseCommandsParams{
Options: options,
},
}
}
// DownloadOptions holds a set of options to be passed to Download
type DownloadOptions struct {
Channel chat1.ChatChannel
ConversationID chat1.ConvIDStr `json:"conversation_id"`
MessageID chat1.MessageID `json:"message_id"`
Output string
Preview bool
NoStream bool
}
type downloadParams struct {
Options DownloadOptions
}
type downloadArg struct {
Method string
Params downloadParams
}
func newDownloadArg(options DownloadOptions) downloadArg {
return downloadArg{
Method: "download",
Params: downloadParams{
Options: options,
},
}
}
// ListMembersOptions holds a set of options to be passed to ListMembers
type ListMembersOptions struct {
Channel chat1.ChatChannel
ConversationID chat1.ConvIDStr `json:"conversation_id"`
}
type listMembersParams struct {
Options ListMembersOptions
}
type listMembersArg struct {
Method string
Params listMembersParams
}
func newListMembersArg(options ListMembersOptions) listMembersArg {
return listMembersArg{
Method: "listmembers",
Params: listMembersParams{
Options: options,
},
}
}
// KVOptions holds a set of options to be passed to the KV methods
type KVOptions struct {
Team *string `json:"team"`
Namespace *string `json:"namespace,omitempty"`
EntryKey *string `json:"entryKey,omitempty"`
EntryValue *string `json:"entryValue,omitempty"`
Revision *int `json:"revision,omitempty"`
}
type kvParams struct {
Options KVOptions `json:"options"`
}
type kvArg struct {
Method string `json:"method"`
Params kvParams `json:"params"`
}
func newKVArg(method string, options KVOptions) kvArg {
return kvArg{
Method: method,
Params: kvParams{
Options: options,
},
}
}
// ChatAPI holds information about a message received by the `keybase chat api-listen` command
@ -229,19 +444,19 @@ type content struct { @@ -229,19 +444,19 @@ type content struct {
}
type msg struct {
ID int `json:"id"`
ConversationID string `json:"conversation_id"`
Channel Channel `json:"channel"`
Sender sender `json:"sender"`
SentAt int `json:"sent_at"`
SentAtMs int64 `json:"sent_at_ms"`
Content content `json:"content"`
Unread bool `json:"unread"`
AtMentionUsernames []string `json:"at_mention_usernames"`
IsEphemeral bool `json:"is_ephemeral"`
Etime int64 `json:"etime"`
HasPairwiseMacs bool `json:"has_pairwise_macs"`
ChannelMention string `json:"channel_mention"`
ID int `json:"id"`
ConversationID string `json:"conversation_id"`
Channel chat1.ChatChannel `json:"channel"`
Sender sender `json:"sender"`
SentAt int `json:"sent_at"`
SentAtMs int64 `json:"sent_at_ms"`
Content content `json:"content"`
Unread bool `json:"unread"`
AtMentionUsernames []string `json:"at_mention_usernames"`
IsEphemeral bool `json:"is_ephemeral"`
Etime int64 `json:"etime"`
HasPairwiseMacs bool `json:"has_pairwise_macs"`
ChannelMention string `json:"channel_mention"`
}
type summary struct {
@ -296,53 +511,12 @@ type notification struct { @@ -296,53 +511,12 @@ type notification struct {
Details details `json:"details"`
}
// Channel holds information about a conversation
type Channel struct {
Name string `json:"name,omitempty"`
Public bool `json:"public,omitempty"`
MembersType string `json:"members_type,omitempty"`
TopicType string `json:"topic_type,omitempty"`
TopicName string `json:"topic_name,omitempty"`
}
type BotCommand struct {
Name string `json:"name"`
Description string `json:"description"`
Usage string `json:"usage"`
ExtendedDescription *BotCommandExtendedDescription `json:"extended_description,omitempty"`
}
type BotCommandExtendedDescription struct {
Title string `json:"title"`
DesktopBody string `json:"desktop_body"`
MobileBody string `json:"mobile_body"`
}
type BotAdvertisement struct {
Type string `json:"type"` // "public", "teamconvs", "teammembers"
TeamName string `json:"team_name,omitempty"` // required if Type is not "public"
BotCommands []BotCommand `json:"commands"`
}
type mesg struct {
Body string `json:"body"`
}
type duration struct {
time.Duration
}
func (d *duration) UnmarshalJSON(b []byte) (err error) {
d.Duration, err = time.ParseDuration(strings.Trim(string(b), `"`))
return
}
func (d *duration) MarshalJSON() (b []byte, err error) {
return []byte(fmt.Sprintf(`"%s"`, d.String())), nil
}
type options struct {
Channel *Channel `json:"channel,omitempty"`
Channel *chat1.ChatChannel `json:"channel,omitempty"`
MessageID int `json:"message_id,omitempty"`
Message *mesg `json:"message,omitempty"`
Pagination *pagination `json:"pagination,omitempty"`
@ -355,8 +529,7 @@ type options struct { @@ -355,8 +529,7 @@ type options struct {
ReplyTo int `json:"reply_to,omitempty"`
GameID string `json:"game_id,omitempty"`
Alias string `json:"alias,omitempty"`
BotAdvertisements []BotAdvertisement `json:"advertisements,omitempty"`
ExplodingLifetime duration `json:"exploding_lifetime,omitempty"`
//ExplodingLifetime duration `json:"exploding_lifetime,omitempty"`
Name string `json:"name,omitempty"`
Public bool `json:"public,omitempty"`
@ -437,12 +610,12 @@ type rateLimits struct { @@ -437,12 +610,12 @@ type rateLimits struct {
}
type conversation struct {
ID string `json:"id"`
Channel Channel `json:"channel"`
Unread bool `json:"unread"`
ActiveAt int `json:"active_at"`
ActiveAtMs int64 `json:"active_at_ms"`
MemberStatus string `json:"member_status"`
ID string `json:"id"`
Channel chat1.ChatChannel `json:"channel"`
Unread bool `json:"unread"`
ActiveAt int `json:"active_at"`
ActiveAtMs int64 `json:"active_at_ms"`
MemberStatus string `json:"member_status"`
}
type SendPayment struct {
@ -602,11 +775,6 @@ type tParams struct { @@ -602,11 +775,6 @@ type tParams struct {
Options tOptions `json:"options"`
}
type Error struct {
Code int `json:"code"`
Message string `json:"message"`
}
type tResult struct {
ChatSent bool `json:"chatSent"`
CreatorAdded bool `json:"creatorAdded"`
@ -646,41 +814,6 @@ type teamInfo struct { @@ -646,41 +814,6 @@ type teamInfo struct {
Implicit implicit `json:"implicit,omitempty"`
}
// KVAPI holds information sent and received to/from the kvstore api
type KVAPI struct {
Method string `json:"method,omitempty"`
Params *kvParams `json:"params,omitempty"`
Result *kvResult `json:"result,omitempty"`
Error *Error `json:"error"`
keybase Keybase
}
type kvOptions struct {
Team string `json:"team,omitempty"`
Namespace string `json:"namespace,omitempty"`
EntryKey string `json:"entryKey,omitempty"`
Revision uint `json:"revision,omitempty"`
EntryValue string `json:"entryValue,omitempty"`
}
type kvParams struct {
Options kvOptions `json:"options,omitempty"`
}
type entryKey struct {
EntryKey string `json:"entryKey"`
Revision uint `json:"revision"`
}
type kvResult struct {
TeamName string `json:"teamName"`
Namespaces []string `json:"namespaces"`
EntryKeys []entryKey `json:"entryKeys"`
EntryKey string `json:"entryKey"`
EntryValue string `json:"entryValue"`
Revision uint `json:"revision"`
}
// UserAPI holds information received from the user/lookup api
type UserAPI struct {
Status uStatus `json:"status"`
@ -831,7 +964,7 @@ type Keybase struct { @@ -831,7 +964,7 @@ type Keybase struct {
// Chat holds basic information about a specific conversation
type Chat struct {
keybase *Keybase
Channel Channel
Channel chat1.ChatChannel
}
type chat interface {
@ -883,29 +1016,12 @@ type wallet interface { @@ -883,29 +1016,12 @@ type wallet interface {
TxDetail(txid string) (WalletAPI, error)
}
// KV holds basic information about a KVStore
type KV struct {
keybase *Keybase
Team string
}
type kvInterface interface {
Namespaces() (KVAPI, error)
Keys(namespace string) (KVAPI, error)
Get(namespace string, key string) (KVAPI, error)
Put(namespace string, key string, value string) (KVAPI, error)
Delete(namespace string, key string) (KVAPI, error)
}
type keybase interface {
AdvertiseCommand(advertisement BotAdvertisement) (ChatAPI, error)
AdvertiseCommands(advertisements []BotAdvertisement) (ChatAPI, error)
ChatList(opts ...Channel) (ChatAPI, error)
ChatList(opts ...chat1.ChatChannel) (ChatAPI, error)
ClearCommands() (ChatAPI, error)
CreateTeam(name string) (TeamAPI, error)
NewChat(channel Channel) Chat
NewChat(channel chat1.ChatChannel) Chat
NewTeam(name string) Team
NewKV(team string) KV
NewWallet() Wallet
Run(handler func(ChatAPI), options ...RunOptions)
status() status

Loading…
Cancel
Save