|
|
@ -2,47 +2,19 @@ package keybase |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"bufio" |
|
|
|
"bufio" |
|
|
|
"encoding/base64" |
|
|
|
|
|
|
|
"encoding/binary" |
|
|
|
|
|
|
|
"encoding/json" |
|
|
|
"encoding/json" |
|
|
|
"errors" |
|
|
|
"errors" |
|
|
|
|
|
|
|
"fmt" |
|
|
|
"os/exec" |
|
|
|
"os/exec" |
|
|
|
"strings" |
|
|
|
|
|
|
|
"time" |
|
|
|
"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: |
|
|
|
"samhofi.us/x/keybase/types/chat1" |
|
|
|
// uint32
|
|
|
|
"samhofi.us/x/keybase/types/keybase1" |
|
|
|
b = make([]byte, 4) |
|
|
|
"samhofi.us/x/keybase/types/stellar1" |
|
|
|
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
|
|
|
|
// 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 == "" { |
|
|
|
if channel.Name == "" { |
|
|
|
return "" |
|
|
|
return "" |
|
|
|
} |
|
|
|
} |
|
|
@ -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
|
|
|
|
// 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 { |
|
|
|
if len(channels) == 0 { |
|
|
|
return "" |
|
|
|
return "" |
|
|
|
} |
|
|
|
} |
|
|
@ -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
|
|
|
|
// 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"} |
|
|
|
execString := []string{"chat", "api-listen"} |
|
|
|
if len(execOptions) > 0 { |
|
|
|
if len(execOptions) > 0 { |
|
|
|
execString = append(execString, execOptions...) |
|
|
|
execString = append(execString, execOptions...) |
|
|
@ -70,75 +42,128 @@ func getNewMessages(k *Keybase, c chan<- ChatAPI, execOptions []string) { |
|
|
|
stdOut, _ := execCmd.StdoutPipe() |
|
|
|
stdOut, _ := execCmd.StdoutPipe() |
|
|
|
execCmd.Start() |
|
|
|
execCmd.Start() |
|
|
|
scanner := bufio.NewScanner(stdOut) |
|
|
|
scanner := bufio.NewScanner(stdOut) |
|
|
|
go func(scanner *bufio.Scanner, c chan<- ChatAPI) { |
|
|
|
go func(scanner *bufio.Scanner, subs *subscriptionChannels) { |
|
|
|
for scanner.Scan() { |
|
|
|
for { |
|
|
|
var jsonData ChatAPI |
|
|
|
scanner.Scan() |
|
|
|
json.Unmarshal([]byte(scanner.Text()), &jsonData) |
|
|
|
var subType subscriptionType |
|
|
|
if jsonData.ErrorRaw != nil { |
|
|
|
t := scanner.Text() |
|
|
|
var errorListen = string(*jsonData.ErrorRaw) |
|
|
|
json.Unmarshal([]byte(t), &subType) |
|
|
|
jsonData.ErrorListen = &errorListen |
|
|
|
switch subType.Type { |
|
|
|
|
|
|
|
case "chat": |
|
|
|
|
|
|
|
var notification chat1.MsgNotification |
|
|
|
|
|
|
|
if err := json.Unmarshal([]byte(t), ¬ification); 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), ¬ification); 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() |
|
|
|
execCmd.Wait() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Run runs `keybase chat api-listen`, and passes incoming messages to the message handler func
|
|
|
|
// Run runs `keybase chat api-listen`, and passes incoming messages to the message handler func
|
|
|
|
func (k *Keybase) Run(handler func(ChatAPI), options ...RunOptions) { |
|
|
|
func (k *Keybase) Run(handlers Handlers, options *RunOptions) { |
|
|
|
var heartbeatFreq int64 |
|
|
|
|
|
|
|
var channelCapacity = 100 |
|
|
|
var channelCapacity = 100 |
|
|
|
|
|
|
|
|
|
|
|
runOptions := make([]string, 0) |
|
|
|
runOptions := make([]string, 0) |
|
|
|
if len(options) > 0 { |
|
|
|
if handlers.WalletHandler != nil { |
|
|
|
if options[0].Capacity > 0 { |
|
|
|
runOptions = append(runOptions, "--wallet") |
|
|
|
channelCapacity = options[0].Capacity |
|
|
|
} |
|
|
|
} |
|
|
|
if handlers.ConversationHandler != nil { |
|
|
|
if options[0].Heartbeat > 0 { |
|
|
|
runOptions = append(runOptions, "--convs") |
|
|
|
heartbeatFreq = options[0].Heartbeat |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if options != nil { |
|
|
|
|
|
|
|
if options.Capacity > 0 { |
|
|
|
|
|
|
|
channelCapacity = options.Capacity |
|
|
|
} |
|
|
|
} |
|
|
|
if options[0].Local { |
|
|
|
if options.Local { |
|
|
|
runOptions = append(runOptions, "--local") |
|
|
|
runOptions = append(runOptions, "--local") |
|
|
|
} |
|
|
|
} |
|
|
|
if options[0].HideExploding { |
|
|
|
if options.HideExploding { |
|
|
|
runOptions = append(runOptions, "--hide-exploding") |
|
|
|
runOptions = append(runOptions, "--hide-exploding") |
|
|
|
} |
|
|
|
} |
|
|
|
if options[0].Dev { |
|
|
|
if options.Dev { |
|
|
|
runOptions = append(runOptions, "--dev") |
|
|
|
runOptions = append(runOptions, "--dev") |
|
|
|
} |
|
|
|
} |
|
|
|
if len(options[0].FilterChannels) > 0 { |
|
|
|
if len(options.FilterChannels) > 0 { |
|
|
|
runOptions = append(runOptions, "--filter-channels") |
|
|
|
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, "--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`
|
|
|
|
chatCh := make(chan chat1.MsgSummary, channelCapacity) |
|
|
|
func heartbeat(c chan<- ChatAPI, freq time.Duration) { |
|
|
|
convCh := make(chan chat1.ConvSummary, channelCapacity) |
|
|
|
m := ChatAPI{ |
|
|
|
walletCh := make(chan stellar1.PaymentDetailsLocal, channelCapacity) |
|
|
|
Type: "heartbeat", |
|
|
|
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 { |
|
|
|
for { |
|
|
|
time.Sleep(freq) |
|
|
|
select { |
|
|
|
m.Msg.ID = count |
|
|
|
case chatMsg := <-subs.chat: |
|
|
|
c <- m |
|
|
|
if handlers.ChatHandler == nil { |
|
|
|
count++ |
|
|
|
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) { |
|
|
|
return r, nil |
|
|
|
return r, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Send sends a chat message
|
|
|
|
// SendMessage sends a chat message
|
|
|
|
func (c Chat) Send(message ...string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) SendMessage(method string, options SendMessageOptions) (chat1.SendRes, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
type res struct { |
|
|
|
Params: ¶ms{}, |
|
|
|
Result chat1.SendRes `json:"result"` |
|
|
|
} |
|
|
|
Error *Error `json:"error,omitempty"` |
|
|
|
m.Params.Options = options{ |
|
|
|
|
|
|
|
Message: &mesg{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
m.Method = "send" |
|
|
|
var r res |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
|
|
|
|
m.Params.Options.Message.Body = strings.Join(message, " ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
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
|
|
|
|
// SendMessageByChannel sends a chat message to a channel
|
|
|
|
func (c Chat) SendEphemeral(duration time.Duration, message ...string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) SendMessageByChannel(channel chat1.ChatChannel, message string, a ...interface{}) (chat1.SendRes, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
opts := SendMessageOptions{ |
|
|
|
Params: ¶ms{}, |
|
|
|
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) |
|
|
|
return k.SendMessage("send", opts) |
|
|
|
if err != nil { |
|
|
|
} |
|
|
|
return r, err |
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// SendEphemeralByConvID sends an exploding chat message to a conversation id
|
|
|
|
func (c Chat) Reply(replyTo int, message ...string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) SendEphemeralByConvID(convID chat1.ConvIDStr, duration time.Duration, message string, a ...interface{}) (chat1.SendRes, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
opts := SendMessageOptions{ |
|
|
|
Params: ¶ms{}, |
|
|
|
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" |
|
|
|
return k.SendMessage("send", opts) |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
} |
|
|
|
m.Params.Options.ReplyTo = replyTo |
|
|
|
|
|
|
|
m.Params.Options.Message.Body = strings.Join(message, " ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m) |
|
|
|
// ReplyByConvID sends a reply message to a conversation id
|
|
|
|
if err != nil { |
|
|
|
func (k *Keybase) ReplyByConvID(convID chat1.ConvIDStr, replyTo chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) { |
|
|
|
return r, err |
|
|
|
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
|
|
|
|
// EditByChannel sends an edit message to a channel
|
|
|
|
func (c Chat) Edit(messageID int, message ...string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) EditByChannel(channel chat1.ChatChannel, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
opts := SendMessageOptions{ |
|
|
|
Params: ¶ms{}, |
|
|
|
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) |
|
|
|
return k.SendMessage("edit", opts) |
|
|
|
if err != nil { |
|
|
|
} |
|
|
|
return r, err |
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
// ReactByConvID reacts to a message in a conversation id
|
|
|
|
func (c Chat) React(messageID int, reaction string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) ReactByConvID(convID chat1.ConvIDStr, msgID chat1.MessageID, message string, a ...interface{}) (chat1.SendRes, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
opts := SendMessageOptions{ |
|
|
|
Params: ¶ms{}, |
|
|
|
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) |
|
|
|
return k.SendMessage("delete", opts) |
|
|
|
if err != nil { |
|
|
|
} |
|
|
|
return r, err |
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
// GetConversations returns a list of all conversations.
|
|
|
|
func (c Chat) Delete(messageID int) (ChatAPI, error) { |
|
|
|
func (k *Keybase) GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
type res struct { |
|
|
|
Params: ¶ms{}, |
|
|
|
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 { |
|
|
|
if err != nil { |
|
|
|
return r, err |
|
|
|
return r.Result, err |
|
|
|
} |
|
|
|
} |
|
|
|
return r, nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ChatList returns a list of all conversations.
|
|
|
|
err = json.Unmarshal(cmdOut, &r) |
|
|
|
// You can pass a Channel to use as a filter here, but you'll probably want to
|
|
|
|
if err != nil { |
|
|
|
// leave the TopicName empty.
|
|
|
|
return r.Result, err |
|
|
|
func (k *Keybase) ChatList(opts ...Channel) (ChatAPI, error) { |
|
|
|
|
|
|
|
m := ChatAPI{ |
|
|
|
|
|
|
|
Params: ¶ms{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if len(opts) > 0 { |
|
|
|
if r.Error != nil { |
|
|
|
m.Params.Options.Name = opts[0].Name |
|
|
|
return r.Result, fmt.Errorf("%v", r.Error.Message) |
|
|
|
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 |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Method = "list" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r, err := chatAPIOut(k, m) |
|
|
|
return r.Result, nil |
|
|
|
return r, err |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ReadMessage fetches the chat message with the specified message id from a conversation.
|
|
|
|
// Read fetches chat messages
|
|
|
|
func (c Chat) ReadMessage(messageID int) (*ChatAPI, error) { |
|
|
|
func (k *Keybase) Read(options ReadMessageOptions) (chat1.Thread, error) { |
|
|
|
m := ChatAPI{ |
|
|
|
type res struct { |
|
|
|
Params: ¶ms{}, |
|
|
|
Result chat1.Thread `json:"result"` |
|
|
|
} |
|
|
|
Error *Error `json:"error"` |
|
|
|
m.Params.Options = options{ |
|
|
|
|
|
|
|
Pagination: &pagination{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var r res |
|
|
|
|
|
|
|
|
|
|
|
m.Method = "read" |
|
|
|
arg := newReadMessageArg(options) |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
|
|
|
|
m.Params.Options.Pagination.Num = 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
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
|
|
|
|
err = json.Unmarshal(cmdOut, &r) |
|
|
|
// be fetched at a time. However, if count is passed, then that is the number of
|
|
|
|
if err != nil { |
|
|
|
// messages that will be fetched.
|
|
|
|
return r.Result, err |
|
|
|
func (c Chat) Read(count ...int) (*ChatAPI, error) { |
|
|
|
|
|
|
|
m := ChatAPI{ |
|
|
|
|
|
|
|
Params: ¶ms{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Params.Options = options{ |
|
|
|
|
|
|
|
Pagination: &pagination{}, |
|
|
|
if r.Error != nil { |
|
|
|
|
|
|
|
return r.Result, fmt.Errorf("%v", r.Error.Message) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
m.Method = "read" |
|
|
|
return r.Result, nil |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
} |
|
|
|
if len(count) == 0 { |
|
|
|
|
|
|
|
m.Params.Options.Pagination.Num = 10 |
|
|
|
// ReadChannel fetches chat messages for a channel
|
|
|
|
} else { |
|
|
|
func (k *Keybase) ReadChannel(channel chat1.ChatChannel) (chat1.Thread, error) { |
|
|
|
m.Params.Options.Pagination.Num = count[0] |
|
|
|
opts := ReadMessageOptions{ |
|
|
|
|
|
|
|
Channel: channel, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return k.Read(opts) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m) |
|
|
|
// ReadChannelNext fetches the next page of messages for a chat channel.
|
|
|
|
if err != nil { |
|
|
|
func (k *Keybase) ReadChannelNext(channel chat1.ChatChannel, next []byte, num int) (chat1.Thread, error) { |
|
|
|
return &r, err |
|
|
|
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
|
|
|
|
// ReadChannelPrevious fetches the previous page of messages for a chat channel
|
|
|
|
// default, Next will fetch the same amount of messages that were originally
|
|
|
|
func (k *Keybase) ReadChannelPrevious(channel chat1.ChatChannel, previous []byte, num int) (chat1.Thread, error) { |
|
|
|
// fetched with Read. However, if count is passed, then that is the number of
|
|
|
|
page := chat1.Pagination{ |
|
|
|
// messages that will be fetched.
|
|
|
|
Previous: previous, |
|
|
|
func (c *ChatAPI) Next(count ...int) (*ChatAPI, error) { |
|
|
|
Num: num, |
|
|
|
m := ChatAPI{ |
|
|
|
|
|
|
|
Params: ¶ms{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Params.Options = options{ |
|
|
|
|
|
|
|
Pagination: &pagination{}, |
|
|
|
opts := ReadMessageOptions{ |
|
|
|
|
|
|
|
Channel: channel, |
|
|
|
|
|
|
|
Pagination: &page, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return k.Read(opts) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
m.Method = "read" |
|
|
|
// ReadConversation fetches chat messages for a conversation
|
|
|
|
m.Params.Options.Channel = &c.Result.Messages[0].Msg.Channel |
|
|
|
func (k *Keybase) ReadConversation(conv chat1.ConvIDStr) (chat1.Thread, error) { |
|
|
|
if len(count) == 0 { |
|
|
|
opts := ReadMessageOptions{ |
|
|
|
m.Params.Options.Pagination.Num = c.Result.Pagination.Num |
|
|
|
ConversationID: conv, |
|
|
|
} else { |
|
|
|
|
|
|
|
m.Params.Options.Pagination.Num = count[0] |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Params.Options.Pagination.Next = c.Result.Pagination.Next |
|
|
|
return k.Read(opts) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
result, err := chatAPIOut(&c.keybase, m) |
|
|
|
// ReadConversationNext fetches the next page of messages for a conversation.
|
|
|
|
if err != nil { |
|
|
|
func (k *Keybase) ReadConversationNext(conv chat1.ConvIDStr, next []byte, num int) (chat1.Thread, error) { |
|
|
|
return &result, err |
|
|
|
page := chat1.Pagination{ |
|
|
|
|
|
|
|
Next: next, |
|
|
|
|
|
|
|
Num: num, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
opts := ReadMessageOptions{ |
|
|
|
|
|
|
|
ConversationID: conv, |
|
|
|
|
|
|
|
Pagination: &page, |
|
|
|
} |
|
|
|
} |
|
|
|
k := c.keybase |
|
|
|
return k.Read(opts) |
|
|
|
*c = result |
|
|
|
|
|
|
|
c.keybase = k |
|
|
|
|
|
|
|
return c, nil |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Previous fetches the previous page of chat messages that were fetched with Read.
|
|
|
|
// ReadConversationPrevious fetches the previous page of messages for a chat channel
|
|
|
|
// By default, Previous will fetch the same amount of messages that were
|
|
|
|
func (k *Keybase) ReadConversationPrevious(conv chat1.ConvIDStr, previous []byte, num int) (chat1.Thread, error) { |
|
|
|
// originally fetched with Read. However, if count is passed, then that is the
|
|
|
|
page := chat1.Pagination{ |
|
|
|
// number of messages that will be fetched.
|
|
|
|
Previous: previous, |
|
|
|
func (c *ChatAPI) Previous(count ...int) (*ChatAPI, error) { |
|
|
|
Num: num, |
|
|
|
m := ChatAPI{ |
|
|
|
|
|
|
|
Params: ¶ms{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Params.Options = options{ |
|
|
|
|
|
|
|
Pagination: &pagination{}, |
|
|
|
opts := ReadMessageOptions{ |
|
|
|
|
|
|
|
ConversationID: conv, |
|
|
|
|
|
|
|
Pagination: &page, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return k.Read(opts) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
m.Method = "read" |
|
|
|
// UploadToChannel attaches a file to a channel
|
|
|
|
m.Params.Options.Channel = &c.Result.Messages[0].Msg.Channel |
|
|
|
// The filename must be an absolute path
|
|
|
|
if len(count) == 0 { |
|
|
|
func (k *Keybase) UploadToChannel(channel chat1.ChatChannel, title string, filename string) (chat1.SendRes, error) { |
|
|
|
m.Params.Options.Pagination.Num = c.Result.Pagination.Num |
|
|
|
opts := SendMessageOptions{ |
|
|
|
} else { |
|
|
|
Channel: channel, |
|
|
|
m.Params.Options.Pagination.Num = count[0] |
|
|
|
Title: title, |
|
|
|
|
|
|
|
Filename: filename, |
|
|
|
} |
|
|
|
} |
|
|
|
m.Params.Options.Pagination.Previous = c.Result.Pagination.Previous |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result, err := chatAPIOut(&c.keybase, m) |
|
|
|
return k.SendMessage("attach", opts) |
|
|
|
if err != nil { |
|
|
|
} |
|
|
|
return &result, err |
|
|
|
|
|
|
|
|
|
|
|
// 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 |
|
|
|
return k.SendMessage("attach", opts) |
|
|
|
c.keybase = k |
|
|
|
|
|
|
|
return c, nil |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Upload attaches a file to a conversation
|
|
|
|
// Download downloads a file
|
|
|
|
// The filepath must be an absolute path
|
|
|
|
func (k *Keybase) Download(options DownloadOptions) error { |
|
|
|
func (c Chat) Upload(title string, filepath string) (ChatAPI, error) { |
|
|
|
type res struct { |
|
|
|
m := ChatAPI{ |
|
|
|
Error *Error `json:"error"` |
|
|
|
Params: ¶ms{}, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
m.Method = "attach" |
|
|
|
var r res |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
|
|
|
|
m.Params.Options.Filename = filepath |
|
|
|
|
|
|
|
m.Params.Options.Title = title |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
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
|
|
|
|
// DownloadFromChannel downloads a file from a channel
|
|
|
|
func (c Chat) Download(messageID int, filepath string) (ChatAPI, error) { |
|
|
|
func (k *Keybase) DownloadFromChannel(channel chat1.ChatChannel, msgID chat1.MessageID, output string) error { |
|
|
|
m := ChatAPI{ |
|
|
|
opts := DownloadOptions{ |
|
|
|
Params: ¶ms{}, |
|
|
|
Channel: channel, |
|
|
|
|
|
|
|
MessageID: msgID, |
|
|
|
|
|
|
|
Output: output, |
|
|
|
} |
|
|
|
} |
|
|
|
m.Method = "download" |
|
|
|
return k.Download(opts) |
|
|
|
m.Params.Options.Channel = &c.Channel |
|
|
|
} |
|
|
|
m.Params.Options.Output = filepath |
|
|
|
|
|
|
|
m.Params.Options.MessageID = messageID |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m) |
|
|
|
// DownloadFromConversation downloads a file from a conversation
|
|
|
|
if err != nil { |
|
|
|
func (k *Keybase) DownloadFromConversation(conv chat1.ConvIDStr, msgID chat1.MessageID, output string) error { |
|
|
|
return r, err |
|
|
|
opts := DownloadOptions{ |
|
|
|
|
|
|
|
ConversationID: conv, |
|
|
|
|
|
|
|
MessageID: msgID, |
|
|
|
|
|
|
|
Output: output, |
|
|
|
} |
|
|
|
} |
|
|
|
return r, nil |
|
|
|
return k.Download(opts) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// LoadFlip returns the results of a flip
|
|
|
|
// LoadFlip returns the results of a flip
|
|
|
@ -517,40 +644,104 @@ func (c Chat) Mark(messageID int) (ChatAPI, error) { |
|
|
|
return r, nil |
|
|
|
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
|
|
|
|
// ClearCommands clears bot advertisements
|
|
|
|
func (k *Keybase) ClearCommands() (ChatAPI, error) { |
|
|
|
func (k *Keybase) ClearCommands() error { |
|
|
|
m := ChatAPI{} |
|
|
|
type res struct { |
|
|
|
m.Method = "clearcommands" |
|
|
|
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 { |
|
|
|
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
|
|
|
|
// ListMembers returns member information for a channel or conversation
|
|
|
|
// This method allows you to set up multiple different types of advertisements at once.
|
|
|
|
func (k *Keybase) ListMembers(options ListMembersOptions) (keybase1.TeamDetails, error) { |
|
|
|
// Use this method if you have commands whose visibility differs from each other.
|
|
|
|
type res struct { |
|
|
|
func (k *Keybase) AdvertiseCommands(advertisements []BotAdvertisement) (ChatAPI, error) { |
|
|
|
Result keybase1.TeamDetails `json:"result"` |
|
|
|
m := ChatAPI{ |
|
|
|
Error *Error `json:"error,omitempty"` |
|
|
|
Params: ¶ms{}, |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 { |
|
|
|
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
|
|
|
|
// ListMembersOfConversation returns member information for a conversation
|
|
|
|
// This method allows you to set up one type of advertisement.
|
|
|
|
func (k *Keybase) ListMembersOfConversation(convID chat1.ConvIDStr) (keybase1.TeamDetails, error) { |
|
|
|
// Use this method if you have commands whose visibility should all be the same.
|
|
|
|
opts := ListMembersOptions{ |
|
|
|
func (k *Keybase) AdvertiseCommand(advertisement BotAdvertisement) (ChatAPI, error) { |
|
|
|
ConversationID: convID, |
|
|
|
return k.AdvertiseCommands([]BotAdvertisement{ |
|
|
|
} |
|
|
|
advertisement, |
|
|
|
return k.ListMembers(opts) |
|
|
|
}) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|