|
|
|
package keybase
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"samhofi.us/x/keybase/v2/types/chat1"
|
|
|
|
"samhofi.us/x/keybase/v2/types/stellar1"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Creates a string of a json-encoded channel to pass to keybase chat api-listen --filter-channel
|
|
|
|
func createFilterString(channel chat1.ChatChannel) string {
|
|
|
|
if channel.Name == "" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
jsonBytes, _ := json.Marshal(channel)
|
|
|
|
return string(jsonBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a string of json-encoded channels to pass to keybase chat api-listen --filter-channels
|
|
|
|
func createFiltersString(channels []chat1.ChatChannel) string {
|
|
|
|
if len(channels) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
jsonBytes, _ := json.Marshal(channels)
|
|
|
|
return string(jsonBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run `keybase chat api-listen` to get new messages coming into keybase and send them into the channel
|
|
|
|
func getNewMessages(k *Keybase, subs *subscriptionChannels, execOptions []string) {
|
|
|
|
execString := []string{"chat", "api-listen"}
|
|
|
|
if len(execOptions) > 0 {
|
|
|
|
execString = append(execString, execOptions...)
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
cmd := make([]string, 0)
|
|
|
|
|
|
|
|
if k.HomePath != "" {
|
|
|
|
cmd = append(cmd, "--home", k.HomePath)
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = append(cmd, execString...)
|
|
|
|
|
|
|
|
execCmd := execCommand(k.ExePath, cmd...)
|
|
|
|
stdOut, _ := execCmd.StdoutPipe()
|
|
|
|
execCmd.Start()
|
|
|
|
scanner := bufio.NewScanner(stdOut)
|
|
|
|
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), ¬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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}(scanner, subs)
|
|
|
|
execCmd.Wait()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run runs `keybase chat api-listen`, and passes incoming messages to the message handler func
|
|
|
|
func (k *Keybase) Run(handlers Handlers, options *RunOptions) {
|
|
|
|
var channelCapacity = 100
|
|
|
|
|
|
|
|
runOptions := make([]string, 0)
|
|
|
|
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.Local {
|
|
|
|
runOptions = append(runOptions, "--local")
|
|
|
|
}
|
|
|
|
if options.HideExploding {
|
|
|
|
runOptions = append(runOptions, "--hide-exploding")
|
|
|
|
}
|
|
|
|
if options.Dev {
|
|
|
|
runOptions = append(runOptions, "--dev")
|
|
|
|
}
|
|
|
|
if len(options.FilterChannels) > 0 {
|
|
|
|
runOptions = append(runOptions, "--filter-channels")
|
|
|
|
runOptions = append(runOptions, createFiltersString(options.FilterChannels))
|
|
|
|
|
|
|
|
}
|
|
|
|
if options.FilterChannel.Name != "" {
|
|
|
|
runOptions = append(runOptions, "--filter-channel")
|
|
|
|
runOptions = append(runOptions, createFilterString(options.FilterChannel))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
defer close(subs.chat)
|
|
|
|
defer close(subs.conversation)
|
|
|
|
defer close(subs.wallet)
|
|
|
|
defer close(subs.error)
|
|
|
|
|
|
|
|
go getNewMessages(k, subs, runOptions)
|
|
|
|
for {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// chatAPIOut sends JSON requests to the chat API and returns its response.
|
|
|
|
func chatAPIOut(k *Keybase, c ChatAPI) (ChatAPI, error) {
|
|
|
|
jsonBytes, _ := json.Marshal(c)
|
|
|
|
|
|
|
|
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
|
|
|
|
if err != nil {
|
|
|
|
return ChatAPI{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var r ChatAPI
|
|
|
|
if err := json.Unmarshal(cmdOut, &r); err != nil {
|
|
|
|
return ChatAPI{}, err
|
|
|
|
}
|
|
|
|
if r.ErrorRaw != nil {
|
|
|
|
var errorRead Error
|
|
|
|
json.Unmarshal([]byte(*r.ErrorRaw), &errorRead)
|
|
|
|
r.ErrorRead = &errorRead
|
|
|
|
return r, errors.New(r.ErrorRead.Message)
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var r res
|
|
|
|
|
|
|
|
arg := newSendMessageArg(options)
|
|
|
|
arg.Method = method
|
|
|
|
|
|
|
|
jsonBytes, _ := json.Marshal(arg)
|
|
|
|
|
|
|
|
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
|
|
|
|
if err != nil {
|
|
|
|
return r.Result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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...),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
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...),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
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 k.SendMessage("send", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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},
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
return k.SendMessage("send", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 k.SendMessage("send", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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 k.SendMessage("reaction", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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 k.SendMessage("delete", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
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.Result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
arg := newReadMessageArg(options)
|
|
|
|
|
|
|
|
jsonBytes, _ := json.Marshal(arg)
|
|
|
|
|
|
|
|
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
|
|
|
|
if err != nil {
|
|
|
|
return r.Result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := ReadMessageOptions{
|
|
|
|
Channel: channel,
|
|
|
|
Pagination: &page,
|
|
|
|
}
|
|
|
|
return k.Read(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := ReadMessageOptions{
|
|
|
|
Channel: channel,
|
|
|
|
Pagination: &page,
|
|
|
|
}
|
|
|
|
return k.Read(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadConversation fetches chat messages for a conversation
|
|
|
|
func (k *Keybase) ReadConversation(conv chat1.ConvIDStr) (chat1.Thread, error) {
|
|
|
|
opts := ReadMessageOptions{
|
|
|
|
ConversationID: conv,
|
|
|
|
}
|
|
|
|
return k.Read(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
return k.Read(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
opts := ReadMessageOptions{
|
|
|
|
ConversationID: conv,
|
|
|
|
Pagination: &page,
|
|
|
|
}
|
|
|
|
return k.Read(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
|
|
|
return k.SendMessage("attach", opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Download downloads a file
|
|
|
|
func (k *Keybase) Download(options DownloadOptions) error {
|
|
|
|
type res struct {
|
|
|
|
Error *Error `json:"error"`
|
|
|
|
}
|
|
|
|
var r res
|
|
|
|
|
|
|
|
arg := newDownloadArg(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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
return k.Download(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 k.Download(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadFlip returns the results of a flip
|
|
|
|
// If the flip is still in progress, this can be expected to change if called again
|
|
|
|
func (c Chat) LoadFlip(messageID int, conversationID string, flipConversationID string, gameID string) (ChatAPI, error) {
|
|
|
|
m := ChatAPI{
|
|
|
|
Params: ¶ms{},
|
|
|
|
}
|
|
|
|
m.Method = "loadflip"
|
|
|
|
m.Params.Options.Channel = &c.Channel
|
|
|
|
m.Params.Options.MsgID = messageID
|
|
|
|
m.Params.Options.ConversationID = conversationID
|
|
|
|
m.Params.Options.FlipConversationID = flipConversationID
|
|
|
|
m.Params.Options.GameID = gameID
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m)
|
|
|
|
if err != nil {
|
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pin pins a message to a channel
|
|
|
|
func (c Chat) Pin(messageID int) (ChatAPI, error) {
|
|
|
|
m := ChatAPI{
|
|
|
|
Params: ¶ms{},
|
|
|
|
}
|
|
|
|
m.Method = "pin"
|
|
|
|
m.Params.Options.Channel = &c.Channel
|
|
|
|
m.Params.Options.MessageID = messageID
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m)
|
|
|
|
if err != nil {
|
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unpin clears any pinned messages from a channel
|
|
|
|
func (c Chat) Unpin() (ChatAPI, error) {
|
|
|
|
m := ChatAPI{
|
|
|
|
Params: ¶ms{},
|
|
|
|
}
|
|
|
|
m.Method = "unpin"
|
|
|
|
m.Params.Options.Channel = &c.Channel
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m)
|
|
|
|
if err != nil {
|
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark marks a conversation as read up to a specified message
|
|
|
|
func (c Chat) Mark(messageID int) (ChatAPI, error) {
|
|
|
|
m := ChatAPI{
|
|
|
|
Params: ¶ms{},
|
|
|
|
}
|
|
|
|
m.Method = "mark"
|
|
|
|
m.Params.Options.Channel = &c.Channel
|
|
|
|
m.Params.Options.MessageID = messageID
|
|
|
|
|
|
|
|
r, err := chatAPIOut(c.keybase, m)
|
|
|
|
if err != nil {
|
|
|
|
return r, err
|
|
|
|
}
|
|
|
|
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() error {
|
|
|
|
type res struct {
|
|
|
|
Error *Error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var r res
|
|
|
|
|
|
|
|
cmdOut, err := k.Exec("chat", "api", "-m", `{"method": "clearcommands"}`)
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListMembers returns member information for a channel or conversation
|
|
|
|
func (k *Keybase) ListMembers(options ListMembersOptions) (chat1.ChatMembersDetails, error) {
|
|
|
|
type res struct {
|
|
|
|
Result chat1.ChatMembersDetails `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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListMembersOfChannel returns member information for a channel
|
|
|
|
func (k *Keybase) ListMembersOfChannel(channel chat1.ChatChannel) (chat1.ChatMembersDetails, error) {
|
|
|
|
opts := ListMembersOptions{
|
|
|
|
Channel: channel,
|
|
|
|
}
|
|
|
|
return k.ListMembers(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListMembersOfConversation returns member information for a conversation
|
|
|
|
func (k *Keybase) ListMembersOfConversation(convID chat1.ConvIDStr) (chat1.ChatMembersDetails, error) {
|
|
|
|
opts := ListMembersOptions{
|
|
|
|
ConversationID: convID,
|
|
|
|
}
|
|
|
|
return k.ListMembers(opts)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListConvsOnName returns a list of all conversations for a chat1.ChatChannel
|
|
|
|
func (k *Keybase) ListConvsOnName(channel chat1.ChatChannel) (*[]chat1.ConvSummary, error) {
|
|
|
|
type result struct {
|
|
|
|
Conversations []chat1.ConvSummary `json:"conversations"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type res struct {
|
|
|
|
Result result `json:"result"`
|
|
|
|
Error *Error `json:"error,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var r res
|
|
|
|
|
|
|
|
arg := newListConvsOnNameArg(channel)
|
|
|
|
|
|
|
|
jsonBytes, _ := json.Marshal(arg)
|
|
|
|
|
|
|
|
cmdOut, err := k.Exec("chat", "api", "-m", string(jsonBytes))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(cmdOut, &r)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Error != nil {
|
|
|
|
return nil, fmt.Errorf("%v", r.Error.Message)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &r.Result.Conversations, nil
|
|
|
|
}
|