Sam
5 years ago
10 changed files with 2168 additions and 0 deletions
@ -0,0 +1,556 @@
@@ -0,0 +1,556 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"bufio" |
||||
"encoding/base64" |
||||
"encoding/binary" |
||||
"encoding/json" |
||||
"errors" |
||||
"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) |
||||
} |
||||
|
||||
// Creates a string of a json-encoded channel to pass to keybase chat api-listen --filter-channel
|
||||
func createFilterString(channel Channel) string { |
||||
if channel.Name == "" { |
||||
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 []Channel) 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, c chan<- ChatAPI, execOptions []string) { |
||||
execString := []string{"chat", "api-listen"} |
||||
if len(execOptions) > 0 { |
||||
execString = append(execString, execOptions...) |
||||
} |
||||
for { |
||||
execCmd := exec.Command(k.Path, execString...) |
||||
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 |
||||
} |
||||
c <- jsonData |
||||
} |
||||
}(scanner, c) |
||||
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 |
||||
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 options[0].Local { |
||||
runOptions = append(runOptions, "--local") |
||||
} |
||||
if options[0].HideExploding { |
||||
runOptions = append(runOptions, "--hide-exploding") |
||||
} |
||||
if options[0].Dev { |
||||
runOptions = append(runOptions, "--dev") |
||||
} |
||||
if len(options[0].FilterChannels) > 0 { |
||||
runOptions = append(runOptions, "--filter-channels") |
||||
runOptions = append(runOptions, createFiltersString(options[0].FilterChannels)) |
||||
|
||||
} |
||||
if options[0].FilterChannel.Name != "" { |
||||
runOptions = append(runOptions, "--filter-channel") |
||||
runOptions = append(runOptions, createFilterString(options[0].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", |
||||
} |
||||
count := 0 |
||||
for { |
||||
time.Sleep(freq) |
||||
m.Msg.ID = count |
||||
c <- m |
||||
count++ |
||||
} |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// Send sends a chat message
|
||||
func (c Chat) Send(message ...string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Message: &mesg{}, |
||||
} |
||||
|
||||
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 r, nil |
||||
} |
||||
|
||||
// SendEphemeral sends an exploding chat message, with specified duration
|
||||
func (c Chat) SendEphemeral(duration time.Duration, message ...string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Message: &mesg{}, |
||||
} |
||||
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 r, nil |
||||
} |
||||
|
||||
// Reply sends a reply to a chat message
|
||||
func (c Chat) Reply(replyTo int, message ...string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Message: &mesg{}, |
||||
} |
||||
|
||||
m.Method = "send" |
||||
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) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// Edit edits a previously sent chat message
|
||||
func (c Chat) Edit(messageID int, message ...string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Message: &mesg{}, |
||||
} |
||||
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 r, nil |
||||
} |
||||
|
||||
// React sends a reaction to a message.
|
||||
func (c Chat) React(messageID int, reaction string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Message: &mesg{}, |
||||
} |
||||
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 r, nil |
||||
} |
||||
|
||||
// Delete deletes a chat message
|
||||
func (c Chat) Delete(messageID int) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Method = "delete" |
||||
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 |
||||
} |
||||
|
||||
// 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: ¶ms{}, |
||||
} |
||||
|
||||
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 |
||||
} |
||||
m.Method = "list" |
||||
|
||||
r, err := chatAPIOut(k, m) |
||||
return r, err |
||||
} |
||||
|
||||
// ReadMessage fetches the chat message with the specified message id from a conversation.
|
||||
func (c Chat) ReadMessage(messageID int) (*ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Pagination: &pagination{}, |
||||
} |
||||
|
||||
m.Method = "read" |
||||
m.Params.Options.Channel = &c.Channel |
||||
m.Params.Options.Pagination.Num = 1 |
||||
|
||||
m.Params.Options.Pagination.Previous = getID(uint(messageID - 1)) |
||||
|
||||
r, err := chatAPIOut(c.keybase, m) |
||||
if err != nil { |
||||
return &r, 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: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Pagination: &pagination{}, |
||||
} |
||||
|
||||
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] |
||||
} |
||||
|
||||
r, err := chatAPIOut(c.keybase, m) |
||||
if err != nil { |
||||
return &r, err |
||||
} |
||||
r.keybase = *c.keybase |
||||
return &r, nil |
||||
} |
||||
|
||||
// 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: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Pagination: &pagination{}, |
||||
} |
||||
|
||||
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] |
||||
} |
||||
m.Params.Options.Pagination.Next = c.Result.Pagination.Next |
||||
|
||||
result, err := chatAPIOut(&c.keybase, m) |
||||
if err != nil { |
||||
return &result, err |
||||
} |
||||
k := c.keybase |
||||
*c = result |
||||
c.keybase = k |
||||
return c, nil |
||||
} |
||||
|
||||
// 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: ¶ms{}, |
||||
} |
||||
m.Params.Options = options{ |
||||
Pagination: &pagination{}, |
||||
} |
||||
|
||||
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] |
||||
} |
||||
m.Params.Options.Pagination.Previous = c.Result.Pagination.Previous |
||||
|
||||
result, err := chatAPIOut(&c.keybase, m) |
||||
if err != nil { |
||||
return &result, err |
||||
} |
||||
k := c.keybase |
||||
*c = result |
||||
c.keybase = k |
||||
return c, nil |
||||
} |
||||
|
||||
// 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: ¶ms{}, |
||||
} |
||||
m.Method = "attach" |
||||
m.Params.Options.Channel = &c.Channel |
||||
m.Params.Options.Filename = filepath |
||||
m.Params.Options.Title = title |
||||
|
||||
r, err := chatAPIOut(c.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// Download downloads a file from a conversation
|
||||
func (c Chat) Download(messageID int, filepath string) (ChatAPI, error) { |
||||
m := ChatAPI{ |
||||
Params: ¶ms{}, |
||||
} |
||||
m.Method = "download" |
||||
m.Params.Options.Channel = &c.Channel |
||||
m.Params.Options.Output = filepath |
||||
m.Params.Options.MessageID = messageID |
||||
|
||||
r, err := chatAPIOut(c.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// ClearCommands clears bot advertisements
|
||||
func (k *Keybase) ClearCommands() (ChatAPI, error) { |
||||
m := ChatAPI{} |
||||
m.Method = "clearcommands" |
||||
|
||||
r, err := chatAPIOut(k, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, 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: ¶ms{}, |
||||
} |
||||
m.Method = "advertisecommands" |
||||
m.Params.Options.BotAdvertisements = advertisements |
||||
|
||||
r, err := chatAPIOut(k, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// 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, |
||||
}) |
||||
} |
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
The keybase package 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 |
||||
Keybase JSON API docs by running some of the following commands in your terminal: |
||||
// Chat API
|
||||
keybase chat api -h |
||||
|
||||
// Chat Message Stream
|
||||
keybase chat api-listen -h |
||||
|
||||
// Team API
|
||||
keybase team api -h |
||||
|
||||
// Wallet API
|
||||
keybase wallet api -h |
||||
|
||||
The git repo for this code is hosted on Keybase. You can contact me directly (https://keybase.io/dxb),
|
||||
or join the mkbot team (https://keybase.io/team/mkbot) if you need assistance, or if you'd like to contribute.
|
||||
|
||||
Basic Example |
||||
|
||||
Here's a quick example of a bot that will attach a reaction with the sender's device name to every message sent |
||||
in @mkbot#test1: |
||||
|
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
|
||||
"samhofi.us/x/keybase" |
||||
) |
||||
|
||||
var k = keybase.NewKeybase() |
||||
|
||||
func main() { |
||||
channel := keybase.Channel{ |
||||
Name: "mkbot", |
||||
TopicName: "test1", |
||||
MembersType: keybase.TEAM, |
||||
} |
||||
opts := keybase.RunOptions{ |
||||
FilterChannel: channel, |
||||
} |
||||
fmt.Println("Running...") |
||||
k.Run(handler, opts) |
||||
} |
||||
|
||||
func handler(m keybase.ChatAPI) { |
||||
if m.ErrorListen != nil { |
||||
fmt.Printf("Error: %s\n", *m.ErrorListen) |
||||
return |
||||
} |
||||
|
||||
msgType := m.Msg.Content.Type |
||||
msgID := m.Msg.ID |
||||
deviceName := m.Msg.Sender.DeviceName |
||||
|
||||
if msgType == "text" { |
||||
chat := k.NewChat(m.Msg.Channel) |
||||
chat.React(msgID, deviceName) |
||||
} |
||||
} |
||||
*/ |
||||
package keybase |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
package keybase |
||||
|
||||
func ExampleKeybase_AdvertiseCommand() { |
||||
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"), |
||||
}, |
||||
} |
||||
|
||||
// Send advertisement
|
||||
k.AdvertiseCommand(c) |
||||
} |
@ -0,0 +1,160 @@
@@ -0,0 +1,160 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"os/exec" |
||||
"strings" |
||||
) |
||||
|
||||
// Possible MemberTypes
|
||||
const ( |
||||
TEAM string = "team" |
||||
USER string = "impteamnative" |
||||
) |
||||
|
||||
// Possible TopicTypes
|
||||
const ( |
||||
DEV string = "dev" |
||||
CHAT string = "chat" |
||||
) |
||||
|
||||
// NewKeybase returns a new Keybase. Optionally, you can pass a string containing the path to the Keybase executable as the first argument.
|
||||
func NewKeybase(path ...string) *Keybase { |
||||
k := &Keybase{} |
||||
if len(path) < 1 { |
||||
k.Path = "keybase" |
||||
} else { |
||||
k.Path = path[0] |
||||
} |
||||
|
||||
s := k.status() |
||||
k.Version = k.version() |
||||
k.LoggedIn = s.LoggedIn |
||||
if k.LoggedIn { |
||||
k.Username = s.Username |
||||
k.Device = s.Device.Name |
||||
} |
||||
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() |
||||
if err != nil { |
||||
return []byte{}, err |
||||
} |
||||
return out, nil |
||||
} |
||||
|
||||
// NewChat returns a new Chat instance
|
||||
func (k *Keybase) NewChat(channel Channel) Chat { |
||||
return Chat{ |
||||
keybase: k, |
||||
Channel: channel, |
||||
} |
||||
} |
||||
|
||||
// NewTeam returns a new Team instance
|
||||
func (k *Keybase) NewTeam(name string) Team { |
||||
return Team{ |
||||
keybase: k, |
||||
Name: name, |
||||
} |
||||
} |
||||
|
||||
// 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{ |
||||
keybase: k, |
||||
} |
||||
} |
||||
|
||||
// status returns the results of the `keybase status` command, which includes
|
||||
// information about the client, and the currently logged-in Keybase user.
|
||||
func (k *Keybase) status() status { |
||||
cmdOut, err := k.Exec("status", "-j") |
||||
if err != nil { |
||||
return status{} |
||||
} |
||||
|
||||
var s status |
||||
json.Unmarshal(cmdOut, &s) |
||||
|
||||
return s |
||||
} |
||||
|
||||
// version returns the version string of the client.
|
||||
func (k *Keybase) version() string { |
||||
cmdOut, err := k.Exec("version", "-S", "-f", "s") |
||||
if err != nil { |
||||
return "" |
||||
} |
||||
|
||||
return string(cmdOut) |
||||
} |
||||
|
||||
// UserLookup pulls information about users.
|
||||
// The following fields are currently returned: basics, profile, proofs_summary, devices -- See https://keybase.io/docs/api/1.0/call/user/lookup for more info.
|
||||
func (k *Keybase) UserLookup(users ...string) (UserAPI, error) { |
||||
var fields = []string{"basics", "profile", "proofs_summary", "devices"} |
||||
|
||||
cmdOut, err := k.Exec("apicall", "--arg", fmt.Sprintf("usernames=%s", strings.Join(users, ",")), "--arg", fmt.Sprintf("fields=%s", strings.Join(fields, ",")), "user/lookup") |
||||
if err != nil { |
||||
return UserAPI{}, err |
||||
} |
||||
|
||||
var r UserAPI |
||||
if err := json.Unmarshal(cmdOut, &r); err != nil { |
||||
return UserAPI{}, err |
||||
} |
||||
|
||||
return r, nil |
||||
} |
||||
|
||||
// UserCard pulls the information that is typically displayed when you open a user's profile.
|
||||
func (k *Keybase) UserCard(user string) (UserCardAPI, error) { |
||||
cmdOut, err := k.Exec("apicall", "--arg", "username="+user, "user/card") |
||||
if err != nil { |
||||
return UserCardAPI{}, err |
||||
} |
||||
|
||||
var r UserCardAPI |
||||
if err := json.Unmarshal(cmdOut, &r); err != nil { |
||||
return UserCardAPI{}, err |
||||
} |
||||
|
||||
return r, nil |
||||
} |
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
) |
||||
|
||||
// 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) |
||||
|
||||
cmdOut, err := k.Exec("kvstore", "api", "-m", string(jsonBytes)) |
||||
if err != nil { |
||||
return KVAPI{}, err |
||||
} |
||||
|
||||
var r KVAPI |
||||
if err := json.Unmarshal(cmdOut, &r); err != nil { |
||||
return KVAPI{}, err |
||||
} |
||||
|
||||
if r.Error != nil { |
||||
return KVAPI{}, errors.New(r.Error.Message) |
||||
} |
||||
|
||||
return r, 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, |
||||
} |
||||
|
||||
m.Method = "list" |
||||
|
||||
r, err := kvAPIOut(kv.keybase, m) |
||||
if err != nil { |
||||
return r, 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{}, |
||||
} |
||||
m.Params.Options = kvOptions{ |
||||
Team: kv.Team, |
||||
Namespace: namespace, |
||||
} |
||||
|
||||
m.Method = "list" |
||||
|
||||
r, err := kvAPIOut(kv.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// Get returns an entry
|
||||
func (kv KV) Get(namespace string, key string, revision ...uint) (KVAPI, error) { |
||||
m := KVAPI{ |
||||
Params: &kvParams{}, |
||||
} |
||||
m.Params.Options = kvOptions{ |
||||
Team: kv.Team, |
||||
Namespace: namespace, |
||||
EntryKey: key, |
||||
} |
||||
|
||||
if len(revision) > 0 { |
||||
m.Params.Options.Revision = revision[0] |
||||
} |
||||
|
||||
m.Method = "get" |
||||
|
||||
r, err := kvAPIOut(kv.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// Put adds an entry
|
||||
func (kv KV) Put(namespace string, key string, value string, revision ...uint) (KVAPI, error) { |
||||
m := KVAPI{ |
||||
Params: &kvParams{}, |
||||
} |
||||
m.Params.Options = kvOptions{ |
||||
Team: kv.Team, |
||||
Namespace: namespace, |
||||
EntryKey: key, |
||||
EntryValue: value, |
||||
} |
||||
|
||||
if len(revision) > 0 { |
||||
m.Params.Options.Revision = revision[0] |
||||
} |
||||
|
||||
m.Method = "put" |
||||
|
||||
r, err := kvAPIOut(kv.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// Delete removes an entry
|
||||
func (kv KV) Delete(namespace string, key string, revision ...uint) (KVAPI, error) { |
||||
m := KVAPI{ |
||||
Params: &kvParams{}, |
||||
} |
||||
m.Params.Options = kvOptions{ |
||||
Team: kv.Team, |
||||
Namespace: namespace, |
||||
EntryKey: key, |
||||
} |
||||
|
||||
if len(revision) > 0 { |
||||
m.Params.Options.Revision = revision[0] |
||||
} |
||||
|
||||
m.Method = "del" |
||||
|
||||
r, err := kvAPIOut(kv.keybase, m) |
||||
if err != nil { |
||||
return r, err |
||||
} |
||||
return r, nil |
||||
} |
@ -0,0 +1,189 @@
@@ -0,0 +1,189 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
) |
||||
|
||||
// teamAPIOut sends JSON requests to the team API and returns its response.
|
||||
func teamAPIOut(k *Keybase, t TeamAPI) (TeamAPI, error) { |
||||
jsonBytes, _ := json.Marshal(t) |
||||
|
||||
cmdOut, err := k.Exec("team", "api", "-m", string(jsonBytes)) |
||||
if err != nil { |
||||
return TeamAPI{}, err |
||||
} |
||||
|
||||
var r TeamAPI |
||||
if err := json.Unmarshal(cmdOut, &r); err != nil { |
||||
return TeamAPI{}, err |
||||
} |
||||
if r.Error != nil { |
||||
return TeamAPI{}, errors.New(r.Error.Message) |
||||
} |
||||
|
||||
return r, nil |
||||
} |
||||
|
||||
// AddUser adds a member to a team by username
|
||||
func (t Team) AddUser(user, role string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "add-members" |
||||
m.Params.Options.Team = t.Name |
||||
m.Params.Options.Usernames = []usernames{ |
||||
{ |
||||
Username: user, |
||||
Role: role, |
||||
}, |
||||
} |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
if err == nil && r.Error == nil { |
||||
r, err = t.MemberList() |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// RemoveUser removes a member from a team
|
||||
func (t Team) RemoveUser(user string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "remove-member" |
||||
m.Params.Options.Team = t.Name |
||||
m.Params.Options.Username = user |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
return r, err |
||||
} |
||||
|
||||
// AddReaders adds members to a team by username, and sets their roles to Reader
|
||||
func (t Team) AddReaders(users ...string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "add-members" |
||||
m.Params.Options.Team = t.Name |
||||
addUsers := []usernames{} |
||||
for _, u := range users { |
||||
addUsers = append(addUsers, usernames{Username: u, Role: "reader"}) |
||||
} |
||||
m.Params.Options.Usernames = addUsers |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
if err == nil && r.Error == nil { |
||||
r, err = t.MemberList() |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// AddWriters adds members to a team by username, and sets their roles to Writer
|
||||
func (t Team) AddWriters(users ...string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "add-members" |
||||
m.Params.Options.Team = t.Name |
||||
addUsers := []usernames{} |
||||
for _, u := range users { |
||||
addUsers = append(addUsers, usernames{Username: u, Role: "writer"}) |
||||
} |
||||
m.Params.Options.Usernames = addUsers |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
if err == nil && r.Error == nil { |
||||
r, err = t.MemberList() |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// AddAdmins adds members to a team by username, and sets their roles to Writer
|
||||
func (t Team) AddAdmins(users ...string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "add-members" |
||||
m.Params.Options.Team = t.Name |
||||
addUsers := []usernames{} |
||||
for _, u := range users { |
||||
addUsers = append(addUsers, usernames{Username: u, Role: "admin"}) |
||||
} |
||||
m.Params.Options.Usernames = addUsers |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
if err == nil && r.Error == nil { |
||||
r, err = t.MemberList() |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// AddOwners adds members to a team by username, and sets their roles to Writer
|
||||
func (t Team) AddOwners(users ...string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "add-members" |
||||
m.Params.Options.Team = t.Name |
||||
addUsers := []usernames{} |
||||
for _, u := range users { |
||||
addUsers = append(addUsers, usernames{Username: u, Role: "owner"}) |
||||
} |
||||
m.Params.Options.Usernames = addUsers |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
if err == nil && r.Error == nil { |
||||
r, err = t.MemberList() |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// MemberList returns a list of a team's members
|
||||
func (t Team) MemberList() (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "list-team-memberships" |
||||
m.Params.Options.Team = t.Name |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
return r, err |
||||
} |
||||
|
||||
// CreateSubteam creates a subteam
|
||||
func (t Team) CreateSubteam(name string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "create-team" |
||||
m.Params.Options.Team = fmt.Sprintf("%s.%s", t.Name, name) |
||||
|
||||
r, err := teamAPIOut(t.keybase, m) |
||||
return r, err |
||||
} |
||||
|
||||
// CreateTeam creates a new team
|
||||
func (k *Keybase) CreateTeam(name string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "create-team" |
||||
m.Params.Options.Team = name |
||||
|
||||
r, err := teamAPIOut(k, m) |
||||
return r, err |
||||
} |
||||
|
||||
// ListUserMemberships returns information about a given user's team memberships
|
||||
func (k *Keybase) ListUserMemberships(user string) (TeamAPI, error) { |
||||
m := TeamAPI{ |
||||
Params: &tParams{}, |
||||
} |
||||
m.Method = "list-user-memberships" |
||||
m.Params.Options.Username = user |
||||
|
||||
r, err := teamAPIOut(k, m) |
||||
return r, err |
||||
} |
@ -0,0 +1,926 @@
@@ -0,0 +1,926 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"fmt" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
// 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
|
||||
} |
||||
|
||||
// ChatAPI holds information about a message received by the `keybase chat api-listen` command
|
||||
type ChatAPI struct { |
||||
Type string `json:"type,omitempty"` |
||||
Source string `json:"source,omitempty"` |
||||
Msg *msg `json:"msg,omitempty"` |
||||
Method string `json:"method,omitempty"` |
||||
Params *params `json:"params,omitempty"` |
||||
Message string `json:"message,omitempty"` |
||||
ID int `json:"id,omitempty"` |
||||
Ratelimits []rateLimits `json:"ratelimits,omitempty"` |
||||
Notification *notification `json:"notification,omitempty"` |
||||
Result *result `json:"result,omitempty"` |
||||
Pagination *pagination `json:"pagination,omitempty"` |
||||
ErrorRaw *json.RawMessage `json:"error,omitempty"` // Raw JSON string containing any errors returned
|
||||
ErrorRead *Error `json:"-"` // Errors returned by any outgoing chat functions such as Read(), Edit(), etc
|
||||
ErrorListen *string `json:"-"` // Errors returned by the api-listen command (used in the Run() function)
|
||||
keybase Keybase // Some methods will need this, so I'm passing it but keeping it unexported
|
||||
} |
||||
|
||||
type sender struct { |
||||
UID string `json:"uid"` |
||||
Username string `json:"username"` |
||||
DeviceID string `json:"device_id"` |
||||
DeviceName string `json:"device_name"` |
||||
} |
||||
|
||||
type addedtoteam struct { |
||||
Team string `json:"team"` |
||||
Adder string `json:"adder"` |
||||
Addee string `json:"addee"` |
||||
Owners []string `json:"owners"` |
||||
Admins []string `json:"admins"` |
||||
Writers []string `json:"writers"` |
||||
Readers []string `json:"readers"` |
||||
} |
||||
|
||||
type bulkaddtoconv struct { |
||||
Usernames []string `json:"usernames"` |
||||
} |
||||
|
||||
type commits struct { |
||||
CommitHash string `json:"commitHash"` |
||||
Message string `json:"message"` |
||||
AuthorName string `json:"authorName"` |
||||
AuthorEmail string `json:"authorEmail"` |
||||
Ctime int `json:"ctime"` |
||||
} |
||||
|
||||
type refs struct { |
||||
RefName string `json:"refName"` |
||||
Commits []commits `json:"commits"` |
||||
MoreCommitsAvailable bool `json:"moreCommitsAvailable"` |
||||
IsDelete bool `json:"isDelete"` |
||||
} |
||||
|
||||
type gitpush struct { |
||||
Team string `json:"team"` |
||||
Pusher string `json:"pusher"` |
||||
RepoName string `json:"repoName"` |
||||
RepoID string `json:"repoID"` |
||||
Refs []refs `json:"refs"` |
||||
PushType int `json:"pushType"` |
||||
PreviousRepoName string `json:"previousRepoName"` |
||||
} |
||||
|
||||
type system struct { |
||||
SystemType int `json:"systemType"` |
||||
Addedtoteam addedtoteam `json:"addedtoteam"` |
||||
Bulkaddtoconv bulkaddtoconv `json:"bulkaddtoconv"` |
||||
Gitpush gitpush `json:"gitpush"` |
||||
} |
||||
|
||||
type paymentsResult struct { |
||||
ResultTyp int `json:"resultTyp"` |
||||
Sent string `json:"sent"` |
||||
} |
||||
|
||||
type payments struct { |
||||
Username string `json:"username"` |
||||
PaymentText string `json:"paymentText"` |
||||
Result paymentsResult `json:"result"` |
||||
} |
||||
|
||||
type userMentions struct { |
||||
Text string `json:"text"` |
||||
UID string `json:"uid"` |
||||
} |
||||
|
||||
type teamMentions struct { |
||||
Name string `json:"name"` |
||||
Channel string `json:"channel"` |
||||
} |
||||
|
||||
type reaction struct { |
||||
M int `json:"m"` |
||||
B string `json:"b"` |
||||
} |
||||
|
||||
type delete struct { |
||||
MessageIDs []int `json:"messageIDs"` |
||||
} |
||||
|
||||
type edit struct { |
||||
MessageID int `json:"messageID"` |
||||
Body string `json:"body"` |
||||
Payments []payments `json:"payments"` |
||||
UserMentions []userMentions `json:"userMentions"` |
||||
TeamMentions []teamMentions `json:"teamMentions"` |
||||
} |
||||
|
||||
type text struct { |
||||
Body string `json:"body"` |
||||
Payments []payments `json:"payments"` |
||||
ReplyTo int `json:"replyTo"` |
||||
ReplyToUID string `json:"replyToUID"` |
||||
UserMentions []userMentions `json:"userMentions"` |
||||
TeamMentions []teamMentions `json:"teamMentions"` |
||||
} |
||||
|
||||
type flip struct { |
||||
Text string `json:"text"` |
||||
GameID string `json:"game_id"` |
||||
FlipConvID string `json:"flip_conv_id"` |
||||
UserMentions interface{} `json:"user_mentions"` |
||||
TeamMentions interface{} `json:"team_mentions"` |
||||
} |
||||
|
||||
type image struct { |
||||
Width int `json:"width"` |
||||
Height int `json:"height"` |
||||
} |
||||
|
||||
type metadata struct { |
||||
AssetType int `json:"assetType"` |
||||
Image image `json:"image"` |
||||
} |
||||
|
||||
type preview struct { |
||||
Filename string `json:"filename"` |
||||
Region string `json:"region"` |
||||
Endpoint string `json:"endpoint"` |
||||
Bucket string `json:"bucket"` |
||||
Path string `json:"path"` |
||||
Size int `json:"size"` |
||||
MimeType string `json:"mimeType"` |
||||
EncHash string `json:"encHash"` |
||||
Key string `json:"key"` |
||||
VerifyKey string `json:"verifyKey"` |
||||
Title string `json:"title"` |
||||
Nonce string `json:"nonce"` |
||||
Metadata metadata `json:"metadata"` |
||||
Tag int `json:"tag"` |
||||
} |
||||
|
||||
type previews struct { |
||||
Filename string `json:"filename"` |
||||
Region string `json:"region"` |
||||
Endpoint string `json:"endpoint"` |
||||
Bucket string `json:"bucket"` |
||||
Path string `json:"path"` |
||||
Size int `json:"size"` |
||||
MimeType string `json:"mimeType"` |
||||
EncHash string `json:"encHash"` |
||||
Key string `json:"key"` |
||||
VerifyKey string `json:"verifyKey"` |
||||
Title string `json:"title"` |
||||
Nonce string `json:"nonce"` |
||||
Metadata metadata `json:"metadata"` |
||||
Tag int `json:"tag"` |
||||
} |
||||
|
||||
type object struct { |
||||
Filename string `json:"filename"` |
||||
Region string `json:"region"` |
||||
Endpoint string `json:"endpoint"` |
||||
Bucket string `json:"bucket"` |
||||
Path string `json:"path"` |
||||
Size int `json:"size"` |
||||
MimeType string `json:"mimeType"` |
||||
EncHash string `json:"encHash"` |
||||
Key string `json:"key"` |
||||
VerifyKey string `json:"verifyKey"` |
||||
Title string `json:"title"` |
||||
Nonce string `json:"nonce"` |
||||
Metadata metadata `json:"metadata"` |
||||
Tag int `json:"tag"` |
||||
} |
||||
|
||||
type attachment struct { |
||||
Object object `json:"object"` |
||||
Preview preview `json:"preview"` |
||||
Previews []previews `json:"previews"` |
||||
Metadata metadata `json:"metadata"` |
||||
Uploaded bool `json:"uploaded"` |
||||
} |
||||
|
||||
type content struct { |
||||
Type string `json:"type"` |
||||
Attachment attachment `json:"attachment"` |
||||
Delete delete `json:"delete"` |
||||
Edit edit `json:"edit"` |
||||
Reaction reaction `json:"reaction"` |
||||
System system `json:"system"` |
||||
Text text `json:"text"` |
||||
SendPayment SendPayment `json:"send_payment"` |
||||
RequestPayment RequestPayment `json:"request_payment"` |
||||
Flip flip `json:"flip"` |
||||
} |
||||
|
||||
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"` |
||||
} |
||||
|
||||
type summary struct { |
||||
ID string `json:"id"` |
||||
TxID string `json:"txID"` |
||||
Time int64 `json:"time"` |
||||
StatusSimplified int `json:"statusSimplified"` |
||||
StatusDescription string `json:"statusDescription"` |
||||
StatusDetail string `json:"statusDetail"` |
||||
ShowCancel bool `json:"showCancel"` |
||||
AmountDescription string `json:"amountDescription"` |
||||
Delta int `json:"delta"` |
||||
Worth string `json:"worth"` |
||||
WorthAtSendTime string `json:"worthAtSendTime"` |
||||
IssuerDescription string `json:"issuerDescription"` |
||||
FromType int `json:"fromType"` |
||||
ToType int `json:"toType"` |
||||
AssetCode string `json:"assetCode"` |
||||
FromAccountID string `json:"fromAccountID"` |
||||
FromAccountName string `json:"fromAccountName"` |
||||
FromUsername string `json:"fromUsername"` |
||||
ToAccountID string `json:"toAccountID"` |
||||
ToAccountName string `json:"toAccountName"` |
||||
ToUsername string `json:"toUsername"` |
||||
ToAssertion string `json:"toAssertion"` |
||||
OriginalToAssertion string `json:"originalToAssertion"` |
||||
Note string `json:"note"` |
||||
NoteErr string `json:"noteErr"` |
||||
SourceAmountMax string `json:"sourceAmountMax"` |
||||
SourceAmountActual string `json:"sourceAmountActual"` |
||||
SourceAsset sourceAsset `json:"sourceAsset"` |
||||
SourceConvRate string `json:"sourceConvRate"` |
||||
IsAdvanced bool `json:"isAdvanced"` |
||||
SummaryAdvanced string `json:"summaryAdvanced"` |
||||
Operations interface{} `json:"operations"` |
||||
Unread bool `json:"unread"` |
||||
BatchID string `json:"batchID"` |
||||
FromAirdrop bool `json:"fromAirdrop"` |
||||
IsInflation bool `json:"isInflation"` |
||||
} |
||||
|
||||
type details struct { |
||||
PublicNote string `json:"publicNote"` |
||||
PublicNoteType string `json:"publicNoteType"` |
||||
ExternalTxURL string `json:"externalTxURL"` |
||||
FeeChargedDescription string `json:"feeChargedDescription"` |
||||
PathIntermediate interface{} `json:"pathIntermediate"` |
||||
} |
||||
|
||||
type notification struct { |
||||
Summary summary `json:"summary"` |
||||
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"` |
||||
MessageID int `json:"message_id,omitempty"` |
||||
Message *mesg `json:"message,omitempty"` |
||||
Pagination *pagination `json:"pagination,omitempty"` |
||||
Filename string `json:"filename,omitempty,omitempty"` |
||||
Title string `json:"title,omitempty,omitempty"` |
||||
Output string `json:"output,omitempty,omitempty"` |
||||
ConversationID string `json:"conversation_id,omitempty"` |
||||
FlipConversationID string `json:"flip_conversation_id,omitempty"` |
||||
MsgID int `json:"msg_id,omitempty"` |
||||
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"` |
||||
|
||||
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 params struct { |
||||
Options options `json:"options"` |
||||
} |
||||
|
||||
type pagination struct { |
||||
Next string `json:"next"` |
||||
Previous string `json:"previous"` |
||||
Num int `json:"num"` |
||||
Last bool `json:"last,omitempty"` |
||||
ForceFirstPage bool `json:"forceFirstPage,omitempty"` |
||||
} |
||||
|
||||
type participants struct { |
||||
UID string `json:"uid"` |
||||
DeviceID string `json:"deviceID"` |
||||
Username string `json:"username"` |
||||
DeviceName string `json:"deviceName"` |
||||
Commitment string `json:"commitment"` |
||||
Reveal string `json:"reveal"` |
||||
} |
||||
|
||||
type dupreg struct { |
||||
User string `json:"user"` |
||||
Device string `json:"device"` |
||||
} |
||||
|
||||
type errorInfo struct { |
||||
Typ int `json:"typ"` |
||||
Dupreg dupreg `json:"dupreg"` |
||||
} |
||||
|
||||
type resultInfo struct { |
||||
Typ int `json:"typ"` |
||||
Coin bool `json:"coin"` |
||||
} |
||||
|
||||
type flipStatus struct { |
||||
GameID string `json:"gameID"` |
||||
Phase int `json:"phase"` |
||||
ProgressText string `json:"progressText"` |
||||
ResultText string `json:"resultText"` |
||||
CommitmentVisualization string `json:"commitmentVisualization"` |
||||
RevealVisualization string `json:"revealVisualization"` |
||||
Participants []participants `json:"participants"` |
||||
ResultInfo *resultInfo `json:"resultInfo"` |
||||
ErrorInfo *errorInfo `json:"errorInfo"` |
||||
} |
||||
|
||||
type result struct { |
||||
Messages []messages `json:"messages,omitempty"` |
||||
Pagination pagination `json:"pagination"` |
||||
Message string `json:"message"` |
||||
ID int `json:"id"` |
||||
Ratelimits []rateLimits `json:"ratelimits"` |
||||
Conversations []conversation `json:"conversations,omitempty"` |
||||
Offline bool `json:"offline,omitempty"` |
||||
Status flipStatus `json:"status,omitempty"` |
||||
IdentifyFailures interface{} `json:"identifyFailures,omitempty"` |
||||
} |
||||
|
||||
type messages struct { |
||||
Msg msg `json:"msg,omitempty"` |
||||
} |
||||
|
||||
type rateLimits struct { |
||||
Tank string `json:"tank,omitempty"` |
||||
Capacity int `json:"capacity,omitempty"` |
||||
Reset int `json:"reset,omitempty"` |
||||
Gas int `json:"gas,omitempty"` |
||||
} |
||||
|
||||
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"` |
||||
} |
||||
|
||||
type SendPayment struct { |
||||
PaymentID string `json:"paymentID"` |
||||
} |
||||
|
||||
type RequestPayment struct { |
||||
RequestID string `json:"requestID"` |
||||
Note string `json:"note"` |
||||
} |
||||
|
||||
// WalletAPI holds data for sending to API
|
||||
type WalletAPI struct { |
||||
Method string `json:"method,omitempty"` |
||||
Params *wParams `json:"params,omitempty"` |
||||
Result *wResult `json:"result,omitempty"` |
||||
Error *Error `json:"error"` |
||||
} |
||||
|
||||
type wOptions struct { |
||||
Name string `json:"name"` |
||||
Txid string `json:"txid"` |
||||
Recipient string `json:"recipient"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
Message string `json:"message"` |
||||
} |
||||
|
||||
type wParams struct { |
||||
Options wOptions `json:"options"` |
||||
} |
||||
|
||||
type asset struct { |
||||
Type string `json:"type"` |
||||
Code string `json:"code"` |
||||
Issuer string `json:"issuer"` |
||||
VerifiedDomain string `json:"verifiedDomain"` |
||||
IssuerName string `json:"issuerName"` |
||||
Desc string `json:"desc"` |
||||
InfoURL string `json:"infoUrl"` |
||||
} |
||||
|
||||
type sourceAsset struct { |
||||
Type string `json:"type"` |
||||
Code string `json:"code"` |
||||
Issuer string `json:"issuer"` |
||||
VerifiedDomain string `json:"verifiedDomain"` |
||||
IssuerName string `json:"issuerName"` |
||||
Desc string `json:"desc"` |
||||
InfoURL string `json:"infoUrl"` |
||||
InfoURLText string `json:"infoUrlText"` |
||||
} |
||||
|
||||
type balance struct { |
||||
Asset asset `json:"asset"` |
||||
Amount string `json:"amount"` |
||||
Limit string `json:"limit"` |
||||
} |
||||
|
||||
type exchangeRate struct { |
||||
Currency string `json:"currency"` |
||||
Rate string `json:"rate"` |
||||
} |
||||
|
||||
type wResult struct { |
||||
AccountID string `json:"accountID"` |
||||
IsPrimary bool `json:"isPrimary"` |
||||
Name string `json:"name"` |
||||
Balance []balance `json:"balance"` |
||||
ExchangeRate exchangeRate `json:"exchangeRate"` |
||||
AccountMode int `json:"accountMode"` |
||||
TxID string `json:"txID"` |
||||
Time int64 `json:"time"` |
||||
Status string `json:"status"` |
||||
StatusDetail string `json:"statusDetail"` |
||||
Amount string `json:"amount"` |
||||
Asset asset `json:"asset"` |
||||
DisplayAmount string `json:"displayAmount"` |
||||
DisplayCurrency string `json:"displayCurrency"` |
||||
SourceAmountMax string `json:"sourceAmountMax"` |
||||
SourceAmountActual string `json:"sourceAmountActual"` |
||||
SourceAsset sourceAsset `json:"sourceAsset"` |
||||
FromStellar string `json:"fromStellar"` |
||||
ToStellar string `json:"toStellar"` |
||||
FromUsername string `json:"fromUsername"` |
||||
ToUsername string `json:"toUsername"` |
||||
Note string `json:"note"` |
||||
NoteErr string `json:"noteErr"` |
||||
Unread bool `json:"unread"` |
||||
Username string `json:"username"` |
||||
} |
||||
|
||||
// TeamAPI holds information sent and received to/from the team api
|
||||
type TeamAPI struct { |
||||
Method string `json:"method,omitempty"` |
||||
Params *tParams `json:"params,omitempty"` |
||||
Result *tResult `json:"result,omitempty"` |
||||
Error *Error `json:"error"` |
||||
} |
||||
|
||||
type emails struct { |
||||
Email string `json:"email"` |
||||
Role string `json:"role"` |
||||
} |
||||
|
||||
type usernames struct { |
||||
Username string `json:"username"` |
||||
Role string `json:"role"` |
||||
} |
||||
|
||||
type user struct { |
||||
UID string `json:"uid"` |
||||
Username string `json:"username"` |
||||
} |
||||
|
||||
type uv struct { |
||||
UID string `json:"uid"` |
||||
EldestSeqno int `json:"eldestSeqno"` |
||||
} |
||||
|
||||
type member struct { |
||||
Uv uv `json:"uv"` |
||||
Username string `json:"username"` |
||||
FullName string `json:"fullName"` |
||||
NeedsPUK bool `json:"needsPUK"` |
||||
Status int `json:"status"` |
||||
} |
||||
|
||||
type members struct { |
||||
Owners []member `json:"owners"` |
||||
Admins []member `json:"admins"` |
||||
Writers []member `json:"writers"` |
||||
Readers []member `json:"readers"` |
||||
} |
||||
|
||||
type annotatedActiveInvites struct { |
||||
} |
||||
|
||||
type settings struct { |
||||
Open bool `json:"open"` |
||||
JoinAs int `json:"joinAs"` |
||||
} |
||||
|
||||
type showcase struct { |
||||
IsShowcased bool `json:"is_showcased"` |
||||
AnyMemberShowcase bool `json:"any_member_showcase"` |
||||
} |
||||
|
||||
type tOptions struct { |
||||
Team string `json:"team"` |
||||
Emails []emails `json:"emails"` |
||||
Usernames []usernames `json:"usernames"` |
||||
Username string `json:"username"` |
||||
} |
||||
|
||||
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"` |
||||
Invited bool `json:"invited"` |
||||
User user `json:"user"` |
||||
EmailSent bool `json:"emailSent"` |
||||
ChatSending bool `json:"chatSending"` |
||||
Members members `json:"members"` |
||||
KeyGeneration int `json:"keyGeneration"` |
||||
AnnotatedActiveInvites annotatedActiveInvites `json:"annotatedActiveInvites"` |
||||
Settings settings `json:"settings"` |
||||
Showcase showcase `json:"showcase"` |
||||
Teams []teamInfo `json:"teams"` |
||||
} |
||||
|
||||
type implicit struct { |
||||
Role int `json:"role"` |
||||
Ancestor string `json:"ancestor"` |
||||
} |
||||
|
||||
type teamInfo struct { |
||||
UID string `json:"uid"` |
||||
TeamID string `json:"team_id"` |
||||
Username string `json:"username"` |
||||
FullName string `json:"full_name"` |
||||
FqName string `json:"fq_name"` |
||||
IsImplicitTeam bool `json:"is_implicit_team"` |
||||
ImplicitTeamDisplayName string `json:"implicit_team_display_name"` |
||||
IsOpenTeam bool `json:"is_open_team"` |
||||
Role int `json:"role"` |
||||
NeedsPUK bool `json:"needsPUK"` |
||||
MemberCount int `json:"member_count"` |
||||
MemberEldestSeqno int `json:"member_eldest_seqno"` |
||||
AllowProfilePromote bool `json:"allow_profile_promote"` |
||||
IsMemberShowcased bool `json:"is_member_showcased"` |
||||
Status int `json:"status"` |
||||
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"` |
||||
Them []them `json:"them"` |
||||
} |
||||
|
||||
type uStatus struct { |
||||
Code int `json:"code"` |
||||
Name string `json:"name"` |
||||
} |
||||
|
||||
type basics struct { |
||||
Ctime int `json:"ctime"` |
||||
EldestSeqno int `json:"eldest_seqno"` |
||||
IDVersion int `json:"id_version"` |
||||
LastIDChange int `json:"last_id_change"` |
||||
Mtime int `json:"mtime"` |
||||
PassphraseGeneration int `json:"passphrase_generation"` |
||||
RandomPw bool `json:"random_pw"` |
||||
Salt string `json:"salt"` |
||||
Status int `json:"status"` |
||||
TrackVersion int `json:"track_version"` |
||||
Username string `json:"username"` |
||||
UsernameCased string `json:"username_cased"` |
||||
} |
||||
|
||||
type profile struct { |
||||
Bio string `json:"bio"` |
||||
FullName string `json:"full_name"` |
||||
Location string `json:"location"` |
||||
Mtime int `json:"mtime"` |
||||
} |
||||
|
||||
type proof struct { |
||||
HumanURL string `json:"human_url"` |
||||
Nametag string `json:"nametag"` |
||||
PresentationGroup string `json:"presentation_group"` |
||||
PresentationTag string `json:"presentation_tag"` |
||||
ProofID string `json:"proof_id"` |
||||
ProofType string `json:"proof_type"` |
||||
ProofURL string `json:"proof_url"` |
||||
ServiceURL string `json:"service_url"` |
||||
SigID string `json:"sig_id"` |
||||
State int `json:"state"` |
||||
} |
||||
|
||||
type proofsSummary struct { |
||||
All []proof `json:"all"` |
||||
HasWeb bool `json:"has_web"` |
||||
} |
||||
|
||||
type key struct { |
||||
KeyRole int `json:"key_role"` |
||||
Kid string `json:"kid"` |
||||
SigID string `json:"sig_id"` |
||||
} |
||||
|
||||
type uDevice struct { |
||||
Ctime int `json:"ctime"` |
||||
Keys []key `json:"keys"` |
||||
Mtime int `json:"mtime"` |
||||
Name string `json:"name"` |
||||
Status int `json:"status"` |
||||
Type string `json:"type"` |
||||
} |
||||
|
||||
type them struct { |
||||
Basics basics `json:"basics,omitempty"` |
||||
ID string `json:"id"` |
||||
Profile profile `json:"profile,omitempty"` |
||||
ProofsSummary proofsSummary `json:"proofs_summary"` |
||||
Devices map[string]uDevice `json:"devices,omitempty"` |
||||
} |
||||
|
||||
// UserCardAPI holds information received from the user/card api
|
||||
type UserCardAPI struct { |
||||
AirdropRegistered bool `json:"airdrop_registered"` |
||||
Blocked bool `json:"blocked"` |
||||
FollowSummary followSummary `json:"follow_summary"` |
||||
Profile cardProfile `json:"profile"` |
||||
Status uStatus `json:"status"` |
||||
TeamShowcase []teamShowcase `json:"team_showcase"` |
||||
TheyFollowYou bool `json:"they_follow_you"` |
||||
UserBlocks userBlocks `json:"user_blocks"` |
||||
YouFollowThem bool `json:"you_follow_them"` |
||||
} |
||||
|
||||
type followSummary struct { |
||||
Followers int `json:"followers"` |
||||
Following int `json:"following"` |
||||
} |
||||
|
||||
type cardProfile struct { |
||||
Bio string `json:"bio"` |
||||
Comment string `json:"comment"` |
||||
CrimeAll int `json:"crime_all"` |
||||
CrimeChat int `json:"crime_chat"` |
||||
CrimeFollow int `json:"crime_follow"` |
||||
CrimeIllegal int `json:"crime_illegal"` |
||||
CrimeLegacyAll int `json:"crime_legacy_all"` |
||||
CrimeLegacyPorn int `json:"crime_legacy_porn"` |
||||
CrimeLegacyStellar int `json:"crime_legacy_stellar"` |
||||
CrimePorn int `json:"crime_porn"` |
||||
CrimeSmurfing int `json:"crime_smurfing"` |
||||
CrimeSpacedrop int `json:"crime_spacedrop"` |
||||
CrimeStellarDust int `json:"crime_stellar_dust"` |
||||
CrimeStellarPaymentReq int `json:"crime_stellar_payment_req"` |
||||
CrimeTeam int `json:"crime_team"` |
||||
Ctime time.Time `json:"ctime"` |
||||
FullName string `json:"full_name"` |
||||
IsAdmin int `json:"is_admin"` |
||||
Location string `json:"location"` |
||||
Mtime time.Time `json:"mtime"` |
||||
Reporter string `json:"reporter"` |
||||
Status int `json:"status"` |
||||
Twitter string `json:"twitter"` |
||||
UID string `json:"uid"` |
||||
Website string `json:"website"` |
||||
} |
||||
|
||||
type teamShowcase struct { |
||||
Description string `json:"description"` |
||||
FqName string `json:"fq_name"` |
||||
NumMembers int `json:"num_members"` |
||||
Open bool `json:"open"` |
||||
PublicAdmins []string `json:"public_admins"` |
||||
Role int `json:"role"` |
||||
TeamID string `json:"team_id"` |
||||
TeamIsShowcased bool `json:"team_is_showcased"` |
||||
} |
||||
|
||||
type userBlocks struct { |
||||
Chat bool `json:"chat"` |
||||
Ctime time.Time `json:"ctime"` |
||||
Follow bool `json:"follow"` |
||||
Mtime time.Time `json:"mtime"` |
||||
} |
||||
|
||||
// Keybase holds basic information about the local Keybase executable
|
||||
type Keybase struct { |
||||
Path string |
||||
Username string |
||||
LoggedIn bool |
||||
Version string |
||||
Device string |
||||
} |
||||
|
||||
// Chat holds basic information about a specific conversation
|
||||
type Chat struct { |
||||
keybase *Keybase |
||||
Channel Channel |
||||
} |
||||
|
||||
type chat interface { |
||||
Delete(messageID int) (ChatAPI, error) |
||||
Edit(messageID int, message ...string) (ChatAPI, error) |
||||
React(messageID int, reaction string) (ChatAPI, error) |
||||
Send(message ...string) (ChatAPI, error) |
||||
Reply(replyTo int, message ...string) (ChatAPI, error) |
||||
Upload(title string, filepath string) (ChatAPI, error) |
||||
Download(messageID int, filepath string) (ChatAPI, error) |
||||
LoadFlip(messageID int, conversationID string, flipConversationID string, gameID string) (ChatAPI, error) |
||||
Pin(messageID int) (ChatAPI, error) |
||||
Unpin() (ChatAPI, error) |
||||
Mark(messageID int) (ChatAPI, error) |
||||
} |
||||
|
||||
type chatAPI interface { |
||||
Next(count ...int) (*ChatAPI, error) |
||||
Previous(count ...int) (*ChatAPI, error) |
||||
} |
||||
|
||||
// Team holds basic information about a team
|
||||
type Team struct { |
||||
keybase *Keybase |
||||
Name string |
||||
} |
||||
|
||||
type team interface { |
||||
AddAdmins(users ...string) (TeamAPI, error) |
||||
AddOwners(users ...string) (TeamAPI, error) |
||||
AddReaders(users ...string) (TeamAPI, error) |
||||
AddUser(user, role string) (TeamAPI, error) |
||||
AddWriters(users ...string) (TeamAPI, error) |
||||
CreateSubteam(name string) (TeamAPI, error) |
||||
MemberList() (TeamAPI, error) |
||||
} |
||||
|
||||
// Wallet holds basic information about a wallet
|
||||
type Wallet struct { |
||||
keybase *Keybase |
||||
} |
||||
|
||||
type wallet interface { |
||||
CancelRequest(requestID string) error |
||||
RequestPayment(user string, amount float64, memo ...string) |
||||
Send(recipient string, amount string, currency string, message ...string) (WalletAPI, error) |
||||
SendXLM(recipient string, amount string, message ...string) (WalletAPI, error) |
||||
StellarAddress(user string) (string, error) |
||||
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) |
||||
ClearCommands() (ChatAPI, error) |
||||
CreateTeam(name string) (TeamAPI, error) |
||||
NewChat(channel Channel) Chat |
||||
NewTeam(name string) Team |
||||
NewKV(team string) KV |
||||
NewWallet() Wallet |
||||
Run(handler func(ChatAPI), options ...RunOptions) |
||||
status() status |
||||
version() string |
||||
UserLookup(users ...string) (UserAPI, error) |
||||
ListUserMemberships(user string) (TeamAPI, error) |
||||
UserCard(user string) (UserCardAPI, error) |
||||
} |
||||
|
||||
type status struct { |
||||
Username string `json:"Username"` |
||||
LoggedIn bool `json:"LoggedIn"` |
||||
Device device `json:"Device"` |
||||
} |
||||
|
||||
type device struct { |
||||
Name string `json:"name"` |
||||
} |
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
package keybase |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"strings" |
||||
) |
||||
|
||||
// walletAPIOut sends JSON requests to the wallet API and returns its response.
|
||||
func walletAPIOut(k *Keybase, w WalletAPI) (WalletAPI, error) { |
||||
jsonBytes, _ := json.Marshal(w) |
||||
|
||||
cmdOut, err := k.Exec("wallet", "api", "-m", string(jsonBytes)) |
||||
if err != nil { |
||||
return WalletAPI{}, err |
||||
} |
||||
|
||||
var r WalletAPI |
||||
json.Unmarshal(cmdOut, &r) |
||||
if r.Error != nil { |
||||
return WalletAPI{}, errors.New(r.Error.Message) |
||||
} |
||||
return r, nil |
||||
} |
||||
|
||||
// TxDetail returns details of a stellar transaction
|
||||
func (w Wallet) TxDetail(txid string) (WalletAPI, error) { |
||||
m := WalletAPI{ |
||||
Params: &wParams{}, |
||||
} |
||||
m.Method = "details" |
||||
m.Params.Options.Txid = txid |
||||
|
||||
r, err := walletAPIOut(w.keybase, m) |
||||
return r, err |
||||
} |
||||
|
||||
// StellarAddress returns the primary stellar address of a given user
|
||||
func (w Wallet) StellarAddress(user string) (string, error) { |
||||
m := WalletAPI{ |
||||
Params: &wParams{}, |
||||
} |
||||
m.Method = "lookup" |
||||
m.Params.Options.Name = user |
||||
|
||||
r, err := walletAPIOut(w.keybase, m) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
return r.Result.AccountID, err |
||||
} |
||||
|
||||
// StellarUser returns the keybase username of a given wallet address
|
||||
func (w Wallet) StellarUser(wallet string) (string, error) { |
||||
m := WalletAPI{ |
||||
Params: &wParams{}, |
||||
} |
||||
m.Method = "lookup" |
||||
m.Params.Options.Name = wallet |
||||
|
||||
r, err := walletAPIOut(w.keybase, m) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
return r.Result.Username, err |
||||
} |
||||
|
||||
// RequestPayment sends a request for payment to a user
|
||||
func (w Wallet) RequestPayment(user string, amount float64, memo ...string) error { |
||||
k := w.keybase |
||||
if len(memo) > 0 { |
||||
_, err := k.Exec("wallet", "request", user, fmt.Sprintf("%f", amount), "-m", memo[0]) |
||||
return err |
||||
} |
||||
_, err := k.Exec("wallet", "request", user, fmt.Sprintf("%f", amount)) |
||||
return err |
||||
} |
||||
|
||||
// CancelRequest cancels a request for payment previously sent to a user
|
||||
func (w Wallet) CancelRequest(requestID string) error { |
||||
k := w.keybase |
||||
_, err := k.Exec("wallet", "cancel-request", requestID) |
||||
return err |
||||
} |
||||
|
||||
// Send sends the specified amount of the specified currency to a user
|
||||
func (w Wallet) Send(recipient string, amount string, currency string, message ...string) (WalletAPI, error) { |
||||
m := WalletAPI{ |
||||
Params: &wParams{}, |
||||
} |
||||
m.Method = "send" |
||||
m.Params.Options.Recipient = recipient |
||||
m.Params.Options.Amount = amount |
||||
m.Params.Options.Currency = currency |
||||
if len(message) > 0 { |
||||
m.Params.Options.Message = strings.Join(message, " ") |
||||
} |
||||
|
||||
r, err := walletAPIOut(w.keybase, m) |
||||
if err != nil { |
||||
return WalletAPI{}, err |
||||
} |
||||
return r, err |
||||
} |
||||
|
||||
// SendXLM sends the specified amount of XLM to a user
|
||||
func (w Wallet) SendXLM(recipient string, amount string, message ...string) (WalletAPI, error) { |
||||
result, err := w.Send(recipient, amount, "XLM", message...) |
||||
return result, err |
||||
} |
Loading…
Reference in new issue