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

Socket Mode support #885

Merged
merged 57 commits into from
Jan 19, 2021
Merged

Socket Mode support #885

merged 57 commits into from
Jan 19, 2021

Conversation

mumoshu
Copy link
Contributor

@mumoshu mumoshu commented Jan 14, 2021

This is a WIP implementation of Socket Mode support for this library based on the official documentation at https://api.slack.com/apis/connections/socket and some reverse engineering on https://github.com/slackapi/node-slack-sdk/blob/main/packages/socket-mode.

Please read the official annoucement for more information about Socket Mode.

Resolves #883

Notes for reviewers:

  • This PR introduces NO API changes to existing types
  • socketmode.Client is added, which is a bit similar to the existing slack.RTM
  • Unit tests for the new feature
  • Documentation update (where should I add this to?)
Pull Request Guidelines

These are recommendations for pull requests.
They are strictly guidelines to help manage expectations.

PR preparation

Run make pr-prep from the root of the repository to run formatting, linting and tests.

Should this be an issue instead
  • is it a convenience method? (no new functionality, streamlines some use case)
  • exposes a previously private type, const, method, etc.
  • is it application specific (caching, retry logic, rate limiting, etc)
  • is it performance related.
API changes

Since API changes have to be maintained they undergo a more detailed review and are more likely to require changes.

  • no tests, if you're adding to the API include at least a single test of the happy case.
  • If you can accomplish your goal without changing the API, then do so.
  • dependency changes. updates are okay. adding/removing need justification.
Examples of API changes that do not meet guidelines:
  • in library cache for users. caches are use case specific.
  • Convenience methods for Sending Messages, update, post, ephemeral, etc. consider opening an issue instead.

This is a WIP implementation of Socket Mode support for this library based on the official documentation at https://api.slack.com/apis/connections/socket and some reverse engineering on https://github.com/slackapi/node-slack-sdk/blob/main/packages/socket-mode.

Please read [the official annoucement](https://medium.com/slack-developer-blog/socket-to-me-3d122f96d955) for more information about Socket Mode.

Resolves slack-go#883
Copy link

@seratch seratch left a comment

Choose a reason for hiding this comment

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

@mumoshu You are so quick! I am happy to be of help. Please feel free to ask me or other folks in the github.com/SlackAPI team any questions 👋

Type string `json:"type"`
Reason string `json:"reason"`
Payload SocketModeMessagePayload `json:"payload"`
EnvelopeID string `json:"envelope_id"`
Copy link

Choose a reason for hiding this comment

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

FYI: https://github.com/slackapi/python-slack-sdk/blob/v3.2.0/slack_sdk/socket_mode/request.py#L7-L12

A Socket Mode envelope can have the following attributes.

  • AcceptsResponsePayload bool
  • RetryAttempt *int // only for Events API
  • RetryReason string // only for Events API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! I've added a bunch of fields according to your info and my observation

https://github.com/mumoshu/slack/blob/852061eaff58f9acb2a18f70ac871341d0f57982/socketmode/request.go#L15-L38

examples/socketmode/events.go Outdated Show resolved Hide resolved
socket_mode_managed_conn.go Outdated Show resolved Hide resolved
examples/socketmode/events.go Outdated Show resolved Hide resolved
socket_mode.go Outdated Show resolved Hide resolved
examples/socketmode/events.go Outdated Show resolved Hide resolved
slacksocketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
smc.Debugln("Sending ACK ", envelopeID)

// See https://github.com/slackapi/node-slack-sdk/blob/c3f4d7109062a0356fb765d53794b7b5f6b3b5ae/packages/socket-mode/src/SocketModeClient.ts#L417
msg := map[string]interface{}{"envelope_id": envelopeID}
Copy link

Choose a reason for hiding this comment

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

The response message can have "payload" in addition to "envelop_id" for interactive / slash_commands types, while events_api requires only "envelope_id".

slack.OutgoingMessage is not compatible with Socket Mode.

Copy link
Contributor Author

@mumoshu mumoshu Jan 16, 2021

Choose a reason for hiding this comment

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

Ah thanks! First of all, I didn't realize till now that slash_commands and interactive are top-level types that are comparable to events_api and hello and so on.

And it's really good to know that the way we ack the socket mode "request" (or "event", which do you prefer btw? I thought you call it SocketModeRequest in python-sdk and event in node-sdk)

I will soon try modifying examples/socketmode and the library and test it accordingly and circle back to you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

slacksocketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
slacksocketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
Copy link
Member

@kanata2 kanata2 left a comment

Choose a reason for hiding this comment

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

Great work! Thank you.
I've add a few comments, but I will do another detailed review when you finish implementing.

internal/misc/misc.go Show resolved Hide resolved
examples/socketmode/socketmode.go Outdated Show resolved Hide resolved
slacksocketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
slacksocketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
@mumoshu
Copy link
Contributor Author

mumoshu commented Jan 17, 2021

Added proper support (as I think) for disconnect handling - Client.Run() has been redesigned to handle reconnection when Slack sends disconnect-type request over the Socket Mode connection.

Also note that the Client struct has much less fields = states now, which I believe to help maintainability.

Copy link

@seratch seratch left a comment

Choose a reason for hiding this comment

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

I just left a few more comments. This pull request looks almost done 👍

(Please feel free to mention me whenever you and the maintainers of this project need my help.)

socketmode/client.go Outdated Show resolved Hide resolved
// `events_api` type only
EnvelopeID string `json:"envelope_id"`
// TODO Can it really be a non-object type?
// See https://github.com/slackapi/python-slack-sdk/blob/3f1c4c6e27bf7ee8af57699b2543e6eb7848bcf9/slack_sdk/socket_mode/request.py#L26-L31
Copy link

Choose a reason for hiding this comment

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

Ah, never mind about L30-31 in the Python SDK (the code should be improved). You can safely assume the "payload" is an object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it. Thanks for clarification 😃

socketmode/socketmode.go Outdated Show resolved Hide resolved
socketmode/socketmode.go Outdated Show resolved Hide resolved
Comment on lines 330 to 332
// We treat the `disconnect` request from Slack as an error internally,
// so that we can tell the consumer of this function to reopen the connection on it.
return ErrorRequestedDisconnect{}
Copy link

@seratch seratch Jan 18, 2021

Choose a reason for hiding this comment

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

How does the handling code look like? Do developers need to write a handler on their own? If so, having the code in the example should be helpful. The disconnect requests can be regularly sent to all Socket Mode apps. This is not a rare case.

If this library can have the reconnection logic (= acquiring a new WSS URL and establishing a new connection to the endpoint under the hood), that's amazing!

Copy link
Contributor Author

@mumoshu mumoshu Jan 19, 2021

Choose a reason for hiding this comment

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

If this library can have the reconnection logic (= acquiring a new WSS URL and establishing a new connection to the endpoint under the hood), that's amazing!

Good question - Yes I've made the client to handle the reconnection on its own, following all the awesome Socket Mode clients you've written for other languages 😄

For your reference, the below is where we implement the infinite reconnection loop:

https://github.com/mumoshu/slack/blob/e9e7822646b5341295e0854167d774e4fb5cd33b/socketmode/socket_mode_managed_conn.go#L36-L42

Copy link
Contributor Author

@mumoshu mumoshu Jan 19, 2021

Choose a reason for hiding this comment

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

BTW In Go anything that is NOT under internal package whose name starts with an uppercase letter is considered public - so ErrorRequestedDisconnect is fairly confusing as it can give users misconception that one might want to use this outside of this package.

I'm going to make it private so that it is clear that the consumer of the client does not need to know about this internal error happens on disconnect request.

Copy link

Choose a reason for hiding this comment

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

so ErrorRequestedDisconnect is fairly confusing as it can give users misconception that one might want to use this outside of this package.

Yes, actually I was thinking so too. Making it private should be the right direction 👍

Also, I've verified the reconnecting logic works properly. Great work!

Copy link
Member

@kanata2 kanata2 left a comment

Choose a reason for hiding this comment

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

Added few comments, but looks good to me!

socketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
socketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
socketmode/socket_mode_managed_conn.go Outdated Show resolved Hide resolved
socket_mode.go Outdated Show resolved Hide resolved
socket_mode.go Outdated Show resolved Hide resolved
socketmode/client.go Outdated Show resolved Hide resolved
@mumoshu
Copy link
Contributor Author

mumoshu commented Jan 19, 2021

Thanks to everyone for reviewing! I believe I've addressed all the feedback we had so far.

Copy link
Member

@kanata2 kanata2 left a comment

Choose a reason for hiding this comment

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

LGTM!!!

@seratch How do you think?

@kanata2 kanata2 added this to the v0.8.0 milestone Jan 19, 2021
Copy link

@seratch seratch left a comment

Choose a reason for hiding this comment

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

Although I am not a maintainer of this project, please allow me to give an approval. I am pretty sure that this Socket Mode client implementation is production-ready 👍

@kanata2 kanata2 merged commit 483202e into slack-go:master Jan 19, 2021
@kanata2
Copy link
Member

kanata2 commented Jan 19, 2021

released! https://github.com/slack-go/slack/releases/tag/v0.8.0

@prologic
Copy link

Hey 👋 Did the slacktest package ever get updated at all? Doesn't look like it did by this PP, and that means using it to unit test your bot's business logic doesn't currently work :/ e.g:

time="2023-12-11T14:14:58+10:00" level=debug msg="Starting SocketMode" file=socketmode/socket_mode_managed_conn.go line=293 logger=log2Logrus
2023/12/11 14:14:58 (127.0.0.1:57589 -) "POST /apps.connections.open HTTP/1.1" 404 19 41µs
time="2023-12-11T14:14:58+10:00" level=debug msg="HTTP/1.1 404 Not Found\r\nContent-Length: 19\r\nContent-Type: text/plain; charset=utf-8\r\nDate: Mon, 11 Dec 2023 04:14:58 GMT\r\nX-Content-Type-Options: nosniff\r\n\r\n404 page not found\n\n" file=misc.go line=266 logger=log2Logrus

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: support new Socket Mode feature
5 participants