10 Commits
v0.01 ... v0.02

4 changed files with 276 additions and 113 deletions

View File

@ -1,19 +1,17 @@
package keybase
import ()
import (
"bufio"
"encoding/json"
"fmt"
"os/exec"
)
type chatIn struct {
type ChatIn struct {
Type string `json:"type"`
Source string `json:"source"`
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 {
UID string `json:"uid"`
Username string `json:"username"`
@ -32,10 +30,33 @@ type chatInAddedtoteam struct {
type chatInBulkaddtoconv struct {
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 {
SystemType int `json:"systemType"`
Addedtoteam chatInAddedtoteam `json:"addedtoteam"`
Bulkaddtoconv chatInBulkaddtoconv `json:"bulkaddtoconv"`
Gitpush chatInGitpush `json:"gitpush"`
}
type chatInResult struct {
ResultTyp int `json:"resultTyp"`
@ -84,7 +105,7 @@ type chatInContent struct {
}
type chatInMsg struct {
ID int `json:"id"`
Channel chatInChannel `json:"channel"`
Channel Channel `json:"channel"`
Sender chatInSender `json:"sender"`
SentAt int `json:"sent_at"`
SentAtMs int64 `json:"sent_at_ms"`
@ -96,3 +117,37 @@ type chatInMsg struct {
HasPairwiseMacs bool `json:"has_pairwise_macs"`
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
type chatOut struct { // not exported
type chatOut struct {
Method string `json:"method"`
Params chatOutParams `json:"params"`
}
type chatOutChannel struct {
type Channel struct {
Name string `json:"name"`
MembersType string `json:"members_type"`
TopicName string `json:"topic_name"`
Public bool `json:"public,omitempty"`
MembersType string `json:"members_type,omitempty"`
TopicType string `json:"topic_type,omitempty"`
TopicName string `json:"topic_name,omitempty"`
}
type chatOutMessage struct {
Body string `json:"body"`
}
type chatOutOptions struct {
Channel chatOutChannel `json:"channel"`
Channel Channel `json:"channel"`
MessageID int `json:"message_id"`
Message chatOutMessage `json:"message"`
}
@ -40,27 +42,20 @@ type chatOutResultRatelimits struct {
Reset int `json:"reset,omitempty"`
Gas int `json:"gas,omitempty"`
}
type chatOutResultChannel 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 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 chatOutResultConversations struct {
ID string `json:"id"`
Channel chatOutResultChannel `json:"channel"`
Unread bool `json:"unread"`
ActiveAt int `json:"active_at"`
ActiveAtMs int64 `json:"active_at_ms"`
MemberStatus string `json:"member_status"`
}
type ChatOut struct { // exported
Message string `json:"message,omitempty"`
ID int `json:"id,omitempty"`
Ratelimits []chatOutResultRatelimits `json:"ratelimits,omitempty"`
Conversations []chatOutResultConversations `json:"conversations,omitempty"`
Offline bool `json:"offline,omitempty"`
type ChatOut struct {
Message string `json:"message,omitempty"`
ID int `json:"id,omitempty"`
Ratelimits []chatOutResultRatelimits `json:"ratelimits,omitempty"`
Conversations []conversation `json:"conversations,omitempty"`
Offline bool `json:"offline,omitempty"`
}
// ----
@ -81,92 +76,58 @@ func chatAPIOut(keybasePath string, c chatOut) (chatOutResult, error) {
return r, nil
}
// ChatSendText() sends a chat message to a user.
func (k Keybase) ChatSendText(user string, message ...string) (ChatOut, error) {
// Send() sends a chat message
func (c Chat) Send(message ...string) (ChatOut, error) {
m := chatOut{}
m.Method = "send"
m.Params.Options.Channel.Name = user
m.Params.Options.Channel = c.Channel
m.Params.Options.Message.Body = strings.Join(message, " ")
r, err := chatAPIOut(k.path, m)
r, err := chatAPIOut(c.keybase.Path, m)
if err != nil {
return ChatOut{}, err
}
return r.Result, nil
}
// ChatSendTextTeam() sends a chat message to a team.
func (k Keybase) ChatSendTextTeam(team, channel string, message ...string) (ChatOut, error) {
// Edit() edits a previously sent chat message
func (c Chat) Edit(messageId int, message ...string) (ChatOut, error) {
m := chatOut{}
m.Method = "send"
m.Params.Options.Channel.Name = team
m.Params.Options.Channel.MembersType = "team"
m.Params.Options.Channel.TopicName = channel
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(k.path, m)
r, err := chatAPIOut(c.keybase.Path, m)
if err != nil {
return ChatOut{}, err
}
return r.Result, nil
}
// ChatReact() sends a reaction to a user's message.
func (k Keybase) ChatReact(user, reaction string, messageId int) (ChatOut, error) {
// React() sends a reaction to a message.
func (c Chat) React(messageId int, reaction string) (ChatOut, error) {
m := chatOut{}
m.Method = "reaction"
m.Params.Options.Channel.Name = user
m.Params.Options.MessageID = messageId
m.Params.Options.Channel = c.Channel
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 {
return ChatOut{}, err
}
return r.Result, nil
}
// ChatReactTeam() sends a reaction to a message on a team.
func (k Keybase) ChatReactTeam(team, channel, reaction string, 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) {
// Delete() deletes a chat message
func (c Chat) Delete(messageId int) (ChatOut, error) {
m := chatOut{}
m.Method = "delete"
m.Params.Options.Channel.Name = user
m.Params.Options.Channel = c.Channel
m.Params.Options.MessageID = messageId
r, err := chatAPIOut(k.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)
r, err := chatAPIOut(c.keybase.Path, m)
if err != nil {
return ChatOut{}, err
}
@ -174,10 +135,10 @@ func (k Keybase) ChatDeleteMessageTeam(team, channel string, messageId int) (Cha
}
// ChatList() returns a list of all conversations.
func (k Keybase) ChatList() ([]chatOutResultConversations, error) {
func (k Keybase) ChatList() ([]conversation, error) {
m := chatOut{}
m.Method = "list"
r, err := chatAPIOut(k.path, m)
r, err := chatAPIOut(k.Path, m)
return r.Result.Conversations, err
}

View File

@ -5,21 +5,46 @@ import (
"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 {
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 {
ChatSendText(user string, message ...string) (ChatOut, error)
ChatSendTextTeam(team, channel, message string) (ChatOut, error)
ChatReact(user, reaction string, messageId int) (ChatOut, error)
ChatReactTeam(team, channel, reaction string, messageId int) (ChatOut, error)
ChatDeleteMessage(user string, messageId int) (ChatOut, error)
ChatDeleteMessageTeam(team, channel string, messageId int) (ChatOut, error)
ChatList() ([]chatOutResultConversations, error)
LoggedIn() bool
Username() string
Version() string
NewChat(channel Channel) Chat
Runner(handler func(ChatIn), channelFilters ...Channel)
ChatList() ([]conversation, error)
loggedIn() bool
username() string
version() string
}
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.
func New(path ...string) Keybase {
func NewKeybase(path ...string) Keybase {
k := Keybase{}
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.
func (k Keybase) Username() string {
cmd := exec.Command(k.path, "status", "-j")
// Return a new Chat instance
func (k Keybase) NewChat(channel Channel) Chat {
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()
if err != nil {
return ""
@ -49,9 +90,9 @@ func (k Keybase) Username() string {
return s.Username
}
// LoggedIn() returns true if Keybase is currently logged in, otherwise returns false.
func (k Keybase) LoggedIn() bool {
cmd := exec.Command(k.path, "status", "-j")
// loggedIn() returns true if Keybase is currently logged in, otherwise returns false.
func (k Keybase) loggedIn() bool {
cmd := exec.Command(k.Path, "status", "-j")
cmdOut, err := cmd.Output()
if err != nil {
return false
@ -63,9 +104,9 @@ func (k Keybase) LoggedIn() bool {
return s.LoggedIn
}
// Version() returns the version string of the client.
func (k Keybase) Version() string {
cmd := exec.Command(k.path, "version", "-S", "-f", "s")
// version() returns the version string of the client.
func (k Keybase) version() string {
cmd := exec.Command(k.Path, "version", "-S", "-f", "s")
cmdOut, err := cmd.Output()
if err != nil {
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
}