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.
127 lines
2.7 KiB
127 lines
2.7 KiB
package keybase |
|
|
|
import ( |
|
"bufio" |
|
"context" |
|
"fmt" |
|
"sync" |
|
|
|
"git.hugfreevikings.wtf/keybase/keybase/pkg/ctxreader" |
|
) |
|
|
|
type ChatAPI struct { |
|
sync.Mutex |
|
toStdin chan []byte |
|
fromStdout chan []byte |
|
fromListen chan []byte |
|
errors chan error |
|
kbLoc string |
|
} |
|
|
|
func NewChatAPI(ctx context.Context, opts Options) (api *ChatAPI, err error) { |
|
if opts.ChannelBufferSize <= 0 { |
|
opts.ChannelBufferSize = 10 |
|
} |
|
|
|
// set up the channels |
|
api = &ChatAPI{ |
|
toStdin: make(chan []byte, opts.ChannelBufferSize), |
|
fromStdout: make(chan []byte, opts.ChannelBufferSize), |
|
fromListen: make(chan []byte, opts.ChannelBufferSize), |
|
errors: make(chan error, opts.ChannelBufferSize), |
|
} |
|
|
|
// get the basics |
|
if opts.KeybaseLocation == "" { |
|
opts.KeybaseLocation, err = locateKeybase() |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to locate keybase: %v", err) |
|
} |
|
} |
|
api.kbLoc = opts.KeybaseLocation |
|
chatApiArgs, err := buildArgs(opts, "chat", "api") |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to build api args: %v", err) |
|
} |
|
chatApiListenArgs, err := buildArgs(opts, "chat", "api-listen") |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to build api-listen args: %v", err) |
|
} |
|
|
|
// build the commands |
|
chatApi, err := newApiCmd(ctx, api.kbLoc, chatApiArgs...) |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to build api command") |
|
} |
|
chatListen, err := newApiCmd(ctx, api.kbLoc, chatApiListenArgs...) |
|
if err != nil { |
|
return nil, fmt.Errorf("failed to build api-listen command") |
|
} |
|
|
|
// create the goroutines |
|
// listen reader |
|
go func() { |
|
cr := ctxreader.NewContextReader(ctx, chatListen.Stdout()) |
|
scanner := bufio.NewScanner(cr) |
|
for scanner.Scan() { |
|
api.fromListen <- scanner.Bytes() |
|
} |
|
}() |
|
|
|
// writing to stdin |
|
go func() { |
|
for { |
|
select { |
|
case msg := <-api.toStdin: |
|
_, err := chatApi.Stdin().Write(msg) |
|
if err != nil { |
|
api.errors <- err |
|
} |
|
case <-ctx.Done(): |
|
return |
|
} |
|
} |
|
}() |
|
|
|
// reading from stdout |
|
go func() { |
|
cr := ctxreader.NewContextReader(ctx, chatApi.Stdout()) |
|
scanner := bufio.NewScanner(cr) |
|
for scanner.Scan() { |
|
api.fromStdout <- scanner.Bytes() |
|
} |
|
}() |
|
|
|
// reading from stderr |
|
go func() { |
|
cr := ctxreader.NewContextReader(ctx, chatApi.Stderr()) |
|
scanner := bufio.NewScanner(cr) |
|
for scanner.Scan() { |
|
api.errors <- fmt.Errorf("%v", scanner.Text()) |
|
} |
|
}() |
|
|
|
// then start the cmds |
|
err = chatApi.Start() |
|
if err != nil { |
|
return nil, err |
|
} |
|
err = chatListen.Start() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return |
|
} |
|
|
|
func (c *ChatAPI) SendRaw(msg []byte) ([]byte, error) { |
|
c.Lock() |
|
defer c.Unlock() |
|
c.toStdin <- msg |
|
select { |
|
case resp := <-c.fromStdout: |
|
return resp, nil |
|
case err := <-c.errors: |
|
return nil, err |
|
} |
|
}
|
|
|