This is a refactor of samhofi.us/x/keybase/v2 that takes advantage of the libkeybase performance improvements.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
6.7 KiB

package keybase
import (
"bufio"
"encoding/json"
"os/exec"
"time"
)
type ChatIn struct {
Type string `json:"type"`
Source string `json:"source"`
Msg chatInMsg `json:"msg"`
}
type chatInSender struct {
UID string `json:"uid"`
Username string `json:"username"`
DeviceID string `json:"device_id"`
DeviceName string `json:"device_name"`
}
type chatInAddedtoteam 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 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"`
Sent string `json:"sent"`
}
type chatInPayments struct {
Username string `json:"username"`
PaymentText string `json:"paymentText"`
Result chatInResult `json:"result"`
}
type chatInUserMentions struct {
Text string `json:"text"`
UID string `json:"uid"`
}
type chatInTeamMentions struct {
Name string `json:"name"`
Channel string `json:"channel"`
}
type chatInReaction struct {
M int `json:"m"`
B string `json:"b"`
}
type chatInDelete struct {
MessageIDs []int `json:"messageIDs"`
}
type chatInEdit struct {
MessageID int `json:"messageID"`
Body string `json:"body"`
Payments []chatInPayments `json:"payments"`
UserMentions []chatInUserMentions `json:"userMentions"`
TeamMentions []chatInTeamMentions `json:"teamMentions"`
}
type chatInText struct {
Body string `json:"body"`
Payments []chatInPayments `json:"payments"`
UserMentions []chatInUserMentions `json:"userMentions"`
TeamMentions []chatInTeamMentions `json:"teamMentions"`
}
type chatInContent struct {
Type string `json:"type"`
Delete chatInDelete `json:"delete"`
Edit chatInEdit `json:"edit"`
Reaction chatInReaction `json:"reaction"`
System chatInSystem `json:"system"`
Text chatInText `json:"text"`
}
type chatInMsg struct {
ID int `json:"id"`
Channel Channel `json:"channel"`
Sender chatInSender `json:"sender"`
SentAt int `json:"sent_at"`
SentAtMs int64 `json:"sent_at_ms"`
Content chatInContent `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"`
}
// RunOptions holds a set of options to be passed to Run
type RunOptions struct {
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
}
// 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)
}
// Get new messages coming into keybase and send them into the channel
func getNewMessages(k Keybase, c chan<- ChatIn, execOptions []string) {
execCommand := []string{"chat", "api-listen"}
if len(execOptions) > 0 {
execCommand = append(execCommand, execOptions...)
}
keybaseListen := exec.Command(k.Path, execCommand...)
keybaseOutput, _ := keybaseListen.StdoutPipe()
keybaseListen.Start()
scanner := bufio.NewScanner(keybaseOutput)
var jsonData ChatIn
for scanner.Scan() {
json.Unmarshal([]byte(scanner.Text()), &jsonData)
c <- jsonData
}
}
// Run() runs keybase chat api-listen, and passes incoming messages to the message handler func
func (k Keybase) Run(handler func(ChatIn), options ...RunOptions) {
var heartbeatFreq int64
runOptions := make([]string, 0)
if len(options) > 0 {
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 ChatIn, 50)
defer close(c)
if heartbeatFreq > 0 {
go heartbeat(c, time.Duration(heartbeatFreq)*time.Minute)
}
go getNewMessages(k, c, runOptions)
for {
go handler(<-c)
}
}
func heartbeat(c chan<- ChatIn, freq time.Duration) {
m := ChatIn{
Type: "heartbeat",
}
count := 0
for {
time.Sleep(freq)
m.Msg.ID = count
c <- m
}
}