Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use generated types from AVDL #21

Merged
merged 28 commits into from Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5cb3d8b
Add a makefile for the bot
Aug 16, 2019
25b1f5c
Generate the bot types
Aug 16, 2019
84c759f
Replace Channel with chat1.ChatChannel
Aug 16, 2019
327af40
Replace Send with chat1.MsgSender
Aug 16, 2019
a926547
Replace Conversation with ConvSummary
Aug 16, 2019
4445b19
Replace Message with MsgSummary, and update message ids where needed
Aug 16, 2019
1ac83e9
Replace Leave and JoinChannelResult with EmptyRes
Aug 16, 2019
a1dd656
Replace ThreadResult with chat1.Thread
Aug 16, 2019
77a270e
Remove some unused structs
Aug 16, 2019
c71d4c0
Replace CommandsAdvertisement with chat1.AdvertiseCommandsParam
Aug 16, 2019
8fc7e32
Remove ThreadResult and MessageHolder
Aug 16, 2019
3cbed36
Replace wallet types
Aug 16, 2019
5917161
Fix an example
Aug 19, 2019
f88a386
Use stellar1.LocalPayment for listen
Aug 19, 2019
2894e83
Use keybase1.TeamMembersDetails instead of ListTeamMembersResultMembers
Aug 19, 2019
25d9997
Replace ListUserMembershipsResultTeam with keybase1.AnnotatedMemberInfo
Aug 19, 2019
046425a
Update readme
Aug 19, 2019
34e8dae
Add .gitattributes file
Aug 19, 2019
a272024
Add clean
Aug 19, 2019
d6e656e
Update some types in the readme
Aug 22, 2019
f32441b
Add go fmt after generated types
Aug 22, 2019
da26530
Output of 1.4.1
Aug 23, 2019
ed95be7
Explicit default goal
Aug 23, 2019
53bae36
Use goimports instead of go fmt
Aug 28, 2019
7b9f668
Skip types/
Aug 28, 2019
4ecf8f2
Have golangci-lint run vet and lint
Aug 29, 2019
bb4865d
Add dependencies to makefile
Aug 29, 2019
d5beee6
Add a note about goimports
Aug 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
@@ -0,0 +1 @@
kbchat/types/ linguist-generated=true
5 changes: 5 additions & 0 deletions .golangci.yml
Expand Up @@ -9,3 +9,8 @@ linters:
- gofmt
- gocritic
- unconvert
- govet
- golint
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved govet and golint to be run by golanglint-ci instead of separately. Main reason for this is that it allows us to specify the same ignore config for all linters, but I believe it'd would also be faster?

run:
skip-dirs:
- kbchat/types/
3 changes: 1 addition & 2 deletions .travis.yml
Expand Up @@ -2,12 +2,11 @@ language: go

before_install:
- go get golang.org/x/lint/golint
- make dependencies
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a makefile in your go project, travis won't auto detect and install dependencies; it expects you to have a task for that.


script:
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.16.0
- golangci-lint run
- go vet ./...
- golint ./...

go:
- 1.10.x
Expand Down
19 changes: 19 additions & 0 deletions Makefile
@@ -0,0 +1,19 @@
PROTOCOL_PATH=../client/protocol
AVDLC=$(PROTOCOL_PATH)/node_modules/.bin/avdlc

.DEFAULT_GOAL := types

types:
@mkdir -p kbchat/types/{keybase1,gregor1,chat1,stellar1}/
$(AVDLC) -b -l go -t -o kbchat/types/keybase1 $(PROTOCOL_PATH)/avdl/keybase1/*.avdl
$(AVDLC) -b -l go -t -o kbchat/types/gregor1 $(PROTOCOL_PATH)/avdl/gregor1/*.avdl
$(AVDLC) -b -l go -t -o kbchat/types/chat1 $(PROTOCOL_PATH)/avdl/chat1/*.avdl
$(AVDLC) -b -l go -t -o kbchat/types/stellar1 $(PROTOCOL_PATH)/avdl/stellar1/*.avdl
goimports -w ./kbchat/types/

dependencies:
go get github.com/stretchr/testify/require
go get gopkg.in/yaml.v2

clean:
rm -rf kbchat/types/
35 changes: 31 additions & 4 deletions README.md
Expand Up @@ -63,19 +63,19 @@ This must be run first in order to start the Keybase JSON API stdin/stdout inter

send a new message by specifying a TLF name

#### `API.SendMessage(channel Channel, body string) (SendResponse, error)`
#### `API.SendMessage(channel chat1.ChatChannel, body string) (SendResponse, error)`

send a new message by specifying a channel

#### `API.SendMessageByConvID(convID string, body string) (SendResponse, error)`

send a new message by specifying a conversation ID

#### `API.GetConversations(unreadOnly bool) ([]Conversation, error)`
#### `API.GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error)`

get all conversations, optionally filtering for unread status

#### `API.GetTextMessages(channel Channel, unreadOnly bool) ([]Message, error)`
#### `API.GetTextMessages(channel chat1.ChatChannel, unreadOnly bool) ([]chat1.MsgSummary, error)`

get all text messages, optionally filtering for unread status

Expand Down Expand Up @@ -142,7 +142,7 @@ Returns the same object as above, but this one will have another channel on it t
fail("failed to read message: %s", err.Error())
}

if msg.Message.Content.Type != "text" {
if msg.Message.Content.TypeName != "text" {
continue
}

Expand Down Expand Up @@ -178,6 +178,33 @@ To enable use of these pre-commit hooks:
- Remove any existing pre-commit hooks via `rm .git/hooks/pre-commit`
- Configure via `pre-commit install`

### Types

Most of the types the bot uses are generated from definitions defined in the [`protocol/`](https://github.com/keybase/client/tree/master/protocol) directory inside the Keybase client repo. This ensures that the types that the bot uses are consistent across bots and always up to date with the output of the API.

To build the types for the Go bot, you'll need to clone the `client` repo in the same parent directory that contains `go-keybase-chat-bot/`.

```shell
git clone https://github.com/keybase/client
```

and install the nessecary dependencies for compiling the protocol files. This requires [node.js](https://nodejs.org) and [Yarn](https://yarnpkg.com).

```shell
cd client/protocol
yarn install
```

Then you can generate the types by using the provided Makefile in this Go bot repo. Note that [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) is required to generate the types.

```shell
go get golang.org/x/tools/cmd/goimports # if you don't have goimports installed
cd ../../go-keybase-chat-bot
make
```

Should you need to remove all the types for some reason, you can run `make clean`.

### Testing

You'll need to have a few demo bot accounts and teams to run the suite of tests. Make a copy of `kbchat/test_config.example.yaml`, rename it to `kbchat/test_config.yaml`, and replace the example data with your own. Tests can then be run inside of `kbchat/` with `go test`.
2 changes: 1 addition & 1 deletion examples/echobot/echobot.go
Expand Up @@ -36,7 +36,7 @@ func main() {
fail("failed to read message: %s", err.Error())
}

if msg.Message.Content.Type != "text" {
if msg.Message.Content.TypeName != "text" {
continue
}

Expand Down
67 changes: 35 additions & 32 deletions kbchat/kbchat.go
Expand Up @@ -11,6 +11,9 @@ import (
"strings"
"sync"
"time"

"github.com/keybase/go-keybase-chat-bot/kbchat/types/chat1"
"github.com/keybase/go-keybase-chat-bot/kbchat/types/stellar1"
)

// API is the main object used for communicating with the Keybase JSON API
Expand Down Expand Up @@ -173,7 +176,7 @@ func (a *API) getAPIPipesLocked() (io.Writer, *bufio.Reader, error) {
}

// GetConversations reads all conversations from the current user's inbox.
func (a *API) GetConversations(unreadOnly bool) ([]Conversation, error) {
func (a *API) GetConversations(unreadOnly bool) ([]chat1.ConvSummary, error) {
apiInput := fmt.Sprintf(`{"method":"list", "params": { "options": { "unread_only": %v}}}`, unreadOnly)
output, err := a.doFetch(apiInput)
if err != nil {
Expand All @@ -189,7 +192,7 @@ func (a *API) GetConversations(unreadOnly bool) ([]Conversation, error) {

// GetTextMessages fetches all text messages from a given channel. Optionally can filter
// ont unread status.
func (a *API) GetTextMessages(channel Channel, unreadOnly bool) ([]Message, error) {
func (a *API) GetTextMessages(channel chat1.ChatChannel, unreadOnly bool) ([]chat1.MsgSummary, error) {
channelBytes, err := json.Marshal(channel)
if err != nil {
return nil, err
Expand All @@ -206,10 +209,10 @@ func (a *API) GetTextMessages(channel Channel, unreadOnly bool) ([]Message, erro
return nil, fmt.Errorf("unable to decode thread: %s", err.Error())
}

var res []Message
var res []chat1.MsgSummary
for _, msg := range thread.Result.Messages {
if msg.Msg.Content.Type == "text" {
res = append(res, msg.Msg)
if msg.Msg.Content.TypeName == "text" {
res = append(res, *msg.Msg)
}
}

Expand All @@ -221,12 +224,12 @@ type sendMessageBody struct {
}

type sendMessageOptions struct {
Channel Channel `json:"channel,omitempty"`
ConversationID string `json:"conversation_id,omitempty"`
Message sendMessageBody `json:",omitempty"`
Filename string `json:"filename,omitempty"`
Title string `json:"title,omitempty"`
MsgID int `json:"message_id,omitempty"`
Channel chat1.ChatChannel `json:"channel,omitempty"`
ConversationID string `json:"conversation_id,omitempty"`
Message sendMessageBody `json:",omitempty"`
Filename string `json:"filename,omitempty"`
Title string `json:"title,omitempty"`
MsgID chat1.MessageID `json:"message_id,omitempty"`
}

type sendMessageParams struct {
Expand Down Expand Up @@ -282,7 +285,7 @@ func (a *API) doFetch(apiInput string) ([]byte, error) {
return byteOutput, nil
}

func (a *API) SendMessage(channel Channel, body string) (SendResponse, error) {
func (a *API) SendMessage(channel chat1.ChatChannel, body string) (SendResponse, error) {
arg := sendMessageArg{
Method: "send",
Params: sendMessageParams{
Expand Down Expand Up @@ -318,7 +321,7 @@ func (a *API) SendMessageByTlfName(tlfName string, body string) (SendResponse, e
Method: "send",
Params: sendMessageParams{
Options: sendMessageOptions{
Channel: Channel{
Channel: chat1.ChatChannel{
Name: tlfName,
},
Message: sendMessageBody{
Expand All @@ -339,7 +342,7 @@ func (a *API) SendMessageByTeamName(teamName string, body string, inChannel *str
Method: "send",
Params: sendMessageParams{
Options: sendMessageOptions{
Channel: Channel{
Channel: chat1.ChatChannel{
MembersType: "team",
Name: teamName,
TopicName: channel,
Expand All @@ -362,7 +365,7 @@ func (a *API) SendAttachmentByTeam(teamName string, filename string, title strin
Method: "attach",
Params: sendMessageParams{
Options: sendMessageOptions{
Channel: Channel{
Channel: chat1.ChatChannel{
MembersType: "team",
Name: teamName,
TopicName: channel,
Expand All @@ -378,8 +381,8 @@ func (a *API) SendAttachmentByTeam(teamName string, filename string, title strin
type reactionOptions struct {
ConversationID string `json:"conversation_id"`
Message sendMessageBody
MsgID int `json:"message_id"`
Channel Channel `json:"channel"`
MsgID chat1.MessageID `json:"message_id"`
Channel chat1.ChatChannel `json:"channel"`
}

type reactionParams struct {
Expand All @@ -398,7 +401,7 @@ func newReactionArg(options reactionOptions) reactionArg {
}
}

func (a *API) ReactByChannel(channel Channel, msgID int, reaction string) (SendResponse, error) {
func (a *API) ReactByChannel(channel chat1.ChatChannel, msgID chat1.MessageID, reaction string) (SendResponse, error) {
arg := newReactionArg(reactionOptions{
Message: sendMessageBody{Body: reaction},
MsgID: msgID,
Expand All @@ -407,7 +410,7 @@ func (a *API) ReactByChannel(channel Channel, msgID int, reaction string) (SendR
return a.doSend(arg)
}

func (a *API) ReactByConvID(convID string, msgID int, reaction string) (SendResponse, error) {
func (a *API) ReactByConvID(convID string, msgID chat1.MessageID, reaction string) (SendResponse, error) {
arg := newReactionArg(reactionOptions{
Message: sendMessageBody{Body: reaction},
MsgID: msgID,
Expand Down Expand Up @@ -444,12 +447,12 @@ func (a *API) Username() string {

// SubscriptionMessage contains a message and conversation object
type SubscriptionMessage struct {
Message Message
Conversation Conversation
Message chat1.MsgSummary
Conversation chat1.ConvSummary
}

type SubscriptionWalletEvent struct {
Payment Payment
Payment stellar1.PaymentDetailsLocal
}

// NewSubscription has methods to control the background message fetcher loop
Expand Down Expand Up @@ -522,16 +525,16 @@ func (a *API) Listen(opts ListenOptions) (NewSubscription, error) {
}
switch typeHolder.Type {
case "chat":
var holder MessageHolder
if err := json.Unmarshal([]byte(t), &holder); err != nil {
var notification chat1.MsgNotification
if err := json.Unmarshal([]byte(t), &notification); err != nil {
errorCh <- err
break
}
subscriptionMessage := SubscriptionMessage{
Message: holder.Msg,
Conversation: Conversation{
ID: holder.Msg.ConversationID,
Channel: holder.Msg.Channel,
Message: *notification.Msg,
Conversation: chat1.ConvSummary{
Id: notification.Msg.ConvID,
Channel: notification.Msg.Channel,
},
}
newMsgCh <- subscriptionMessage
Expand Down Expand Up @@ -615,8 +618,8 @@ func (a *API) ListChannels(teamName string) ([]string, error) {
return channels, nil
}

func (a *API) JoinChannel(teamName string, channelName string) (JoinChannelResult, error) {
empty := JoinChannelResult{}
func (a *API) JoinChannel(teamName string, channelName string) (chat1.EmptyRes, error) {
empty := chat1.EmptyRes{}

apiInput := fmt.Sprintf(`{"method": "join", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName)
output, err := a.doFetch(apiInput)
Expand All @@ -636,8 +639,8 @@ func (a *API) JoinChannel(teamName string, channelName string) (JoinChannelResul
return joinChannel.Result, nil
}

func (a *API) LeaveChannel(teamName string, channelName string) (LeaveChannelResult, error) {
empty := LeaveChannelResult{}
func (a *API) LeaveChannel(teamName string, channelName string) (chat1.EmptyRes, error) {
empty := chat1.EmptyRes{}

apiInput := fmt.Sprintf(`{"method": "leave", "params": {"options": {"channel": {"name": "%s", "members_type": "team", "topic_name": "%s"}}}}`, teamName, channelName)
output, err := a.doFetch(apiInput)
Expand Down