10 Commits
v0.01 ... v0.02

4 changed files with 276 additions and 113 deletions

View File

@ -1,19 +1,17 @@
package keybase package keybase
import () import (
"bufio"
"encoding/json"
"fmt"
"os/exec"
)
type chatIn struct { type ChatIn struct {
Type string `json:"type"` Type string `json:"type"`
Source string `json:"source"` Source string `json:"source"`
Msg chatInMsg `json:"msg"` Msg chatInMsg `json:"msg"`
} }
type chatInChannel struct {
Name string `json:"name"`
Public bool `json:"public"`
MembersType string `json:"members_type"`
TopicType string `json:"topic_type"`
TopicName string `json:"topic_name"`
}
type chatInSender struct { type chatInSender struct {
UID string `json:"uid"` UID string `json:"uid"`
Username string `json:"username"` Username string `json:"username"`
@ -32,10 +30,33 @@ type chatInAddedtoteam struct {
type chatInBulkaddtoconv struct { type chatInBulkaddtoconv struct {
Usernames []string `json:"usernames"` Usernames []string `json:"usernames"`
} }
type chatInCommits struct {
CommitHash string `json:"commitHash"`
Message string `json:"message"`
AuthorName string `json:"authorName"`
AuthorEmail string `json:"authorEmail"`
Ctime int `json:"ctime"`
}
type chatInRefs struct {
RefName string `json:"refName"`
Commits []chatInCommits `json:"commits"`
MoreCommitsAvailable bool `json:"moreCommitsAvailable"`
IsDelete bool `json:"isDelete"`
}
type chatInGitpush struct {
Team string `json:"team"`
Pusher string `json:"pusher"`
RepoName string `json:"repoName"`
RepoID string `json:"repoID"`
Refs []chatInRefs `json:"refs"`
PushType int `json:"pushType"`
PreviousRepoName string `json:"previousRepoName"`
}
type chatInSystem struct { type chatInSystem struct {
SystemType int `json:"systemType"` SystemType int `json:"systemType"`
Addedtoteam chatInAddedtoteam `json:"addedtoteam"` Addedtoteam chatInAddedtoteam `json:"addedtoteam"`
Bulkaddtoconv chatInBulkaddtoconv `json:"bulkaddtoconv"` Bulkaddtoconv chatInBulkaddtoconv `json:"bulkaddtoconv"`
Gitpush chatInGitpush `json:"gitpush"`
} }
type chatInResult struct { type chatInResult struct {
ResultTyp int `json:"resultTyp"` ResultTyp int `json:"resultTyp"`
@ -84,7 +105,7 @@ type chatInContent struct {
} }
type chatInMsg struct { type chatInMsg struct {
ID int `json:"id"` ID int `json:"id"`
Channel chatInChannel `json:"channel"` Channel Channel `json:"channel"`
Sender chatInSender `json:"sender"` Sender chatInSender `json:"sender"`
SentAt int `json:"sent_at"` SentAt int `json:"sent_at"`
SentAtMs int64 `json:"sent_at_ms"` SentAtMs int64 `json:"sent_at_ms"`
@ -96,3 +117,37 @@ type chatInMsg struct {
HasPairwiseMacs bool `json:"has_pairwise_macs"` HasPairwiseMacs bool `json:"has_pairwise_macs"`
ChannelMention string `json:"channel_mention"` ChannelMention string `json:"channel_mention"`
} }
// Creates a string of json-encoded channels to pass to keybase chat api-listen --filter-channels
func createFilterString(channelFilters ...Channel) string {
if len(channelFilters) == 0 {
return "[]"
}
jsonBytes, _ := json.Marshal(channelFilters)
return fmt.Sprintf("%s", string(jsonBytes))
}
// Get new messages coming into keybase and send them into the channel
func getNewMessages(k Keybase, c chan<- ChatIn, filterString string) {
keybaseListen := exec.Command(k.Path, "chat", "api-listen", "--filter-channels", filterString)
keybaseOutput, _ := keybaseListen.StdoutPipe()
keybaseListen.Start()
scanner := bufio.NewScanner(keybaseOutput)
var jsonData ChatIn
for scanner.Scan() {
json.Unmarshal([]byte(scanner.Text()), &jsonData)
c <- jsonData
}
}
// Runner() runs keybase chat api-listen, and passes incoming messages to the message handler func
func (k Keybase) Runner(handler func(ChatIn), channelFilters ...Channel) {
c := make(chan ChatIn, 50)
defer close(c)
go getNewMessages(k, c, createFilterString(channelFilters...))
for {
go handler(<-c)
}
}

View File

@ -7,20 +7,22 @@ import (
) )
// ---- Struct for sending to API // ---- Struct for sending to API
type chatOut struct { // not exported type chatOut struct {
Method string `json:"method"` Method string `json:"method"`
Params chatOutParams `json:"params"` Params chatOutParams `json:"params"`
} }
type chatOutChannel struct { type Channel struct {
Name string `json:"name"` Name string `json:"name"`
MembersType string `json:"members_type"` Public bool `json:"public,omitempty"`
TopicName string `json:"topic_name"` MembersType string `json:"members_type,omitempty"`
TopicType string `json:"topic_type,omitempty"`
TopicName string `json:"topic_name,omitempty"`
} }
type chatOutMessage struct { type chatOutMessage struct {
Body string `json:"body"` Body string `json:"body"`
} }
type chatOutOptions struct { type chatOutOptions struct {
Channel chatOutChannel `json:"channel"` Channel Channel `json:"channel"`
MessageID int `json:"message_id"` MessageID int `json:"message_id"`
Message chatOutMessage `json:"message"` Message chatOutMessage `json:"message"`
} }
@ -40,26 +42,19 @@ type chatOutResultRatelimits struct {
Reset int `json:"reset,omitempty"` Reset int `json:"reset,omitempty"`
Gas int `json:"gas,omitempty"` Gas int `json:"gas,omitempty"`
} }
type chatOutResultChannel struct { type conversation struct {
Name string `json:"name"`
Public bool `json:"public"`
MembersType string `json:"members_type"`
TopicType string `json:"topic_type,omitempty"`
TopicName string `json:"topic_name,omitempty"`
}
type chatOutResultConversations struct {
ID string `json:"id"` ID string `json:"id"`
Channel chatOutResultChannel `json:"channel"` Channel Channel `json:"channel"`
Unread bool `json:"unread"` Unread bool `json:"unread"`
ActiveAt int `json:"active_at"` ActiveAt int `json:"active_at"`
ActiveAtMs int64 `json:"active_at_ms"` ActiveAtMs int64 `json:"active_at_ms"`
MemberStatus string `json:"member_status"` MemberStatus string `json:"member_status"`
} }
type ChatOut struct { // exported type ChatOut struct {
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
ID int `json:"id,omitempty"` ID int `json:"id,omitempty"`
Ratelimits []chatOutResultRatelimits `json:"ratelimits,omitempty"` Ratelimits []chatOutResultRatelimits `json:"ratelimits,omitempty"`
Conversations []chatOutResultConversations `json:"conversations,omitempty"` Conversations []conversation `json:"conversations,omitempty"`
Offline bool `json:"offline,omitempty"` Offline bool `json:"offline,omitempty"`
} }
@ -81,92 +76,58 @@ func chatAPIOut(keybasePath string, c chatOut) (chatOutResult, error) {
return r, nil return r, nil
} }
// ChatSendText() sends a chat message to a user. // Send() sends a chat message
func (k Keybase) ChatSendText(user string, message ...string) (ChatOut, error) { func (c Chat) Send(message ...string) (ChatOut, error) {
m := chatOut{} m := chatOut{}
m.Method = "send" m.Method = "send"
m.Params.Options.Channel.Name = user m.Params.Options.Channel = c.Channel
m.Params.Options.Message.Body = strings.Join(message, " ") m.Params.Options.Message.Body = strings.Join(message, " ")
r, err := chatAPIOut(k.path, m) r, err := chatAPIOut(c.keybase.Path, m)
if err != nil { if err != nil {
return ChatOut{}, err return ChatOut{}, err
} }
return r.Result, nil return r.Result, nil
} }
// ChatSendTextTeam() sends a chat message to a team. // Edit() edits a previously sent chat message
func (k Keybase) ChatSendTextTeam(team, channel string, message ...string) (ChatOut, error) { func (c Chat) Edit(messageId int, message ...string) (ChatOut, error) {
m := chatOut{} m := chatOut{}
m.Method = "send" m.Method = "edit"
m.Params.Options.Channel.Name = team m.Params.Options.Channel = c.Channel
m.Params.Options.Channel.MembersType = "team"
m.Params.Options.Channel.TopicName = channel
m.Params.Options.Message.Body = strings.Join(message, " ") m.Params.Options.Message.Body = strings.Join(message, " ")
m.Params.Options.MessageID = messageId
r, err := chatAPIOut(k.path, m) r, err := chatAPIOut(c.keybase.Path, m)
if err != nil { if err != nil {
return ChatOut{}, err return ChatOut{}, err
} }
return r.Result, nil return r.Result, nil
} }
// ChatReact() sends a reaction to a user's message. // React() sends a reaction to a message.
func (k Keybase) ChatReact(user, reaction string, messageId int) (ChatOut, error) { func (c Chat) React(messageId int, reaction string) (ChatOut, error) {
m := chatOut{} m := chatOut{}
m.Method = "reaction" m.Method = "reaction"
m.Params.Options.Channel.Name = user m.Params.Options.Channel = c.Channel
m.Params.Options.MessageID = messageId
m.Params.Options.Message.Body = reaction m.Params.Options.Message.Body = reaction
m.Params.Options.MessageID = messageId
r, err := chatAPIOut(k.path, m) r, err := chatAPIOut(c.keybase.Path, m)
if err != nil { if err != nil {
return ChatOut{}, err return ChatOut{}, err
} }
return r.Result, nil return r.Result, nil
} }
// ChatReactTeam() sends a reaction to a message on a team. // Delete() deletes a chat message
func (k Keybase) ChatReactTeam(team, channel, reaction string, messageId int) (ChatOut, error) { func (c Chat) Delete(messageId int) (ChatOut, error) {
m := chatOut{}
m.Method = "reaction"
m.Params.Options.Channel.Name = team
m.Params.Options.Channel.MembersType = "team"
m.Params.Options.Channel.TopicName = channel
m.Params.Options.MessageID = messageId
m.Params.Options.Message.Body = reaction
r, err := chatAPIOut(k.path, m)
if err != nil {
return ChatOut{}, err
}
return r.Result, nil
}
// ChatDeleteMessage() deletes a message from a one-on-one conversation.
func (k Keybase) ChatDeleteMessage(user string, messageId int) (ChatOut, error) {
m := chatOut{} m := chatOut{}
m.Method = "delete" m.Method = "delete"
m.Params.Options.Channel.Name = user m.Params.Options.Channel = c.Channel
m.Params.Options.MessageID = messageId m.Params.Options.MessageID = messageId
r, err := chatAPIOut(k.path, m) r, err := chatAPIOut(c.keybase.Path, m)
if err != nil {
return ChatOut{}, err
}
return r.Result, nil
}
// ChatDeleteMessageTeam() deletes a message from a team conversation.
func (k Keybase) ChatDeleteMessageTeam(team, channel string, messageId int) (ChatOut, error) {
m := chatOut{}
m.Method = "delete"
m.Params.Options.Channel.Name = team
m.Params.Options.Channel.MembersType = "team"
m.Params.Options.Channel.TopicName = channel
m.Params.Options.MessageID = messageId
r, err := chatAPIOut(k.path, m)
if err != nil { if err != nil {
return ChatOut{}, err return ChatOut{}, err
} }
@ -174,10 +135,10 @@ func (k Keybase) ChatDeleteMessageTeam(team, channel string, messageId int) (Cha
} }
// ChatList() returns a list of all conversations. // ChatList() returns a list of all conversations.
func (k Keybase) ChatList() ([]chatOutResultConversations, error) { func (k Keybase) ChatList() ([]conversation, error) {
m := chatOut{} m := chatOut{}
m.Method = "list" m.Method = "list"
r, err := chatAPIOut(k.path, m) r, err := chatAPIOut(k.Path, m)
return r.Result.Conversations, err return r.Result.Conversations, err
} }

View File

@ -5,21 +5,46 @@ import (
"os/exec" "os/exec"
) )
// Possible MemberTypes
const (
TEAM string = "team"
USER string = "impteamnative"
)
// Possible TopicTypes
const (
DEV string = "dev"
CHAT string = "chat"
)
// Keybase holds basic information about the local Keybase executable
type Keybase struct { type Keybase struct {
path string Path string
Username string
LoggedIn bool
Version string
}
// Chat holds basic information about a specific conversation
type Chat struct {
keybase Keybase
Channel Channel
}
type chat interface {
Send(message ...string) (ChatOut, error)
Edit(messageId int, message ...string) (ChatOut, error)
React(messageId int, reaction string) (ChatOut, error)
Delete(messageId int) (ChatOut, error)
} }
type keybase interface { type keybase interface {
ChatSendText(user string, message ...string) (ChatOut, error) NewChat(channel Channel) Chat
ChatSendTextTeam(team, channel, message string) (ChatOut, error) Runner(handler func(ChatIn), channelFilters ...Channel)
ChatReact(user, reaction string, messageId int) (ChatOut, error) ChatList() ([]conversation, error)
ChatReactTeam(team, channel, reaction string, messageId int) (ChatOut, error) loggedIn() bool
ChatDeleteMessage(user string, messageId int) (ChatOut, error) username() string
ChatDeleteMessageTeam(team, channel string, messageId int) (ChatOut, error) version() string
ChatList() ([]chatOutResultConversations, error)
LoggedIn() bool
Username() string
Version() string
} }
type status struct { type status struct {
@ -28,16 +53,32 @@ type status struct {
} }
// New() returns a new instance of Keybase object. Optionally, you can pass a string containing the path to the Keybase executable as the first argument. // New() returns a new instance of Keybase object. Optionally, you can pass a string containing the path to the Keybase executable as the first argument.
func New(path ...string) Keybase { func NewKeybase(path ...string) Keybase {
k := Keybase{}
if len(path) < 1 { if len(path) < 1 {
return Keybase{path: "/usr/bin/keybase"} k.Path = "keybase"
} else {
k.Path = path[0]
} }
return Keybase{path: path[0]} k.Version = k.version()
k.LoggedIn = k.loggedIn()
if k.LoggedIn == true {
k.Username = k.username()
}
return k
} }
// Username() returns the username of the currently logged-in Keybase user. // Return a new Chat instance
func (k Keybase) Username() string { func (k Keybase) NewChat(channel Channel) Chat {
cmd := exec.Command(k.path, "status", "-j") return Chat{
keybase: k,
Channel: channel,
}
}
// username() returns the username of the currently logged-in Keybase user.
func (k Keybase) username() string {
cmd := exec.Command(k.Path, "status", "-j")
cmdOut, err := cmd.Output() cmdOut, err := cmd.Output()
if err != nil { if err != nil {
return "" return ""
@ -49,9 +90,9 @@ func (k Keybase) Username() string {
return s.Username return s.Username
} }
// LoggedIn() returns true if Keybase is currently logged in, otherwise returns false. // loggedIn() returns true if Keybase is currently logged in, otherwise returns false.
func (k Keybase) LoggedIn() bool { func (k Keybase) loggedIn() bool {
cmd := exec.Command(k.path, "status", "-j") cmd := exec.Command(k.Path, "status", "-j")
cmdOut, err := cmd.Output() cmdOut, err := cmd.Output()
if err != nil { if err != nil {
return false return false
@ -63,9 +104,9 @@ func (k Keybase) LoggedIn() bool {
return s.LoggedIn return s.LoggedIn
} }
// Version() returns the version string of the client. // version() returns the version string of the client.
func (k Keybase) Version() string { func (k Keybase) version() string {
cmd := exec.Command(k.path, "version", "-S", "-f", "s") cmd := exec.Command(k.Path, "version", "-S", "-f", "s")
cmdOut, err := cmd.Output() cmdOut, err := cmd.Output()
if err != nil { if err != nil {
return "" return ""

106
wallet.go Normal file
View File

@ -0,0 +1,106 @@
package keybase
import (
"encoding/json"
"os/exec"
)
// ---- Struct for sending to API
type walletOut struct {
Method string `json:"method"`
Params walletOutParams `json:"params"`
}
type walletOutOptions struct {
Txid string `json:"txid"`
}
type walletOutParams struct {
Options walletOutOptions `json:"options"`
}
// ----
// ---- Struct for data received after sending to API
type walletOutResult struct {
Result WalletResult `json:"result"`
}
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"`
}
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 WalletResult 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"`
}
// ----
// walletAPIOut() sends JSON requests to the wallet API and returns its response.
func walletAPIOut(keybasePath string, w walletOut) (walletOutResult, error) {
jsonBytes, _ := json.Marshal(w)
cmd := exec.Command(keybasePath, "wallet", "api", "-m", string(jsonBytes))
cmdOut, err := cmd.Output()
if err != nil {
return walletOutResult{}, err
}
var r walletOutResult
json.Unmarshal(cmdOut, &r)
return r, nil
}
// TxDetail() returns details of a stellar transaction
func (k Keybase) TxDetail(txid string) (WalletResult, error) {
m := walletOut{}
m.Method = "details"
m.Params.Options.Txid = txid
r, err := walletAPIOut(k.Path, m)
return r.Result, err
}