Skip to content

Commit

Permalink
Merge branch 'slack-ai-assistant' of github.com:MattDavisRV/slack int…
Browse files Browse the repository at this point in the history
…o slack-ai-assistant
  • Loading branch information
MattDavisRV committed Oct 23, 2024
2 parents 61280ce + 56dbc20 commit c106865
Show file tree
Hide file tree
Showing 108 changed files with 8,265 additions and 692 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/stale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '0 0 * * 1' # every Monday 0:00 UTC

workflow_dispatch:

permissions:
issues: write
pull-requests: write

jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
any-of-labels: 'feedback given'
days-before-stale: 45
days-before-pr-close: 10
stale-issue-message: 'This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
close-issue-message: 'This issue was closed because it has been stalled for 10 days with no activity.'
close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.'
operations-per-run: 120
27 changes: 18 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,38 @@ on:

jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
strategy:
matrix:
go:
- '1.16'
- '1.17'
- '1.18'
- '1.19'
- '1.20'
- '1.21'
- '1.22'
- '1.23'
name: test go-${{ matrix.go }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}
- name: run test
run: go test -v -race ./...
env:
GO111MODULE: on
lint:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
name: lint
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v5
with:
go-version: '1.20'
cache: false
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
with:
version: latest
version: v1.52.2
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.test
*~
.idea/
/vendor/
10 changes: 9 additions & 1 deletion apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@ type EventAuthorization struct {
IsEnterpriseInstall bool `json:"is_enterprise_install"`
}

// ListEventAuthorizations lists authed users and teams for the given event_context.
// You must provide an app-level token to the client using OptionAppLevelToken.
// For more details, see ListEventAuthorizationsContext documentation.
func (api *Client) ListEventAuthorizations(eventContext string) ([]EventAuthorization, error) {
return api.ListEventAuthorizationsContext(context.Background(), eventContext)
}

// ListEventAuthorizationsContext lists authed users and teams for the given event_context. You must provide an app-level token to the client using OptionAppLevelToken. More info: https://api.slack.com/methods/apps.event.authorizations.list
// ListEventAuthorizationsContext lists authed users and teams for the given event_context with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.event.authorizations.list
func (api *Client) ListEventAuthorizationsContext(ctx context.Context, eventContext string) ([]EventAuthorization, error) {
resp := &listEventAuthorizationsResponse{}

Expand All @@ -43,10 +47,14 @@ func (api *Client) ListEventAuthorizationsContext(ctx context.Context, eventCont
return resp.Authorizations, nil
}

// UninstallApp uninstalls your app from a workspace.
// For more details, see UninstallAppContext documentation.
func (api *Client) UninstallApp(clientID, clientSecret string) error {
return api.UninstallAppContext(context.Background(), clientID, clientSecret)
}

// UninstallAppContext uninstalls your app from a workspace with a custom context.
// Slack API docs: https://api.slack.com/methods/apps.uninstall
func (api *Client) UninstallAppContext(ctx context.Context, clientID, clientSecret string) error {
values := url.Values{
"client_id": {clientID},
Expand Down
41 changes: 39 additions & 2 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ func (api *Client) authRequest(ctx context.Context, path string, values url.Valu
return response, response.Err()
}

// SendAuthRevoke will send a revocation for our token
// SendAuthRevoke will send a revocation for our token.
// For more details, see SendAuthRevokeContext documentation.
func (api *Client) SendAuthRevoke(token string) (*AuthRevokeResponse, error) {
return api.SendAuthRevokeContext(context.Background(), token)
}

// SendAuthRevokeContext will send a revocation request for our token to api.revoke with context
// SendAuthRevokeContext will send a revocation request for our token to api.revoke with a custom context.
// Slack API docs: https://api.slack.com/methods/auth.revoke
func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*AuthRevokeResponse, error) {
if token == "" {
token = api.token
Expand All @@ -38,3 +40,38 @@ func (api *Client) SendAuthRevokeContext(ctx context.Context, token string) (*Au

return api.authRequest(ctx, "auth.revoke", values)
}

type listTeamsResponse struct {
Teams []Team `json:"teams"`
SlackResponse
}

type ListTeamsParameters struct {
Limit int
Cursor string
}

// ListTeams returns all workspaces a token can access.
// For more details, see ListTeamsContext documentation.
func (api *Client) ListTeams(params ListTeamsParameters) ([]Team, string, error) {
return api.ListTeamsContext(context.Background(), params)
}

// ListTeamsContext returns all workspaces a token can access with a custom context.
// Slack API docs: https://api.slack.com/methods/auth.teams.list
func (api *Client) ListTeamsContext(ctx context.Context, params ListTeamsParameters) ([]Team, string, error) {
values := url.Values{
"token": {api.token},
}
if params.Cursor != "" {
values.Add("cursor", params.Cursor)
}

response := &listTeamsResponse{}
err := api.postMethod(ctx, "auth.teams.list", values, response)
if err != nil {
return nil, "", err
}

return response.Teams, response.ResponseMetadata.Cursor, response.Err()
}
51 changes: 51 additions & 0 deletions auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package slack

import (
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

func getTeamList(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
response := []byte(`{
"ok": true,
"teams": [
{
"name": "Shinichi's workspace",
"id": "T12345678"
},
{
"name": "Migi's workspace",
"id": "T12345679"
}
],
"response_metadata": {
"next_cursor": "dXNlcl9pZDo5MTQyOTI5Mzkz"
}
}`)
rw.Write(response)
}

func TestListTeams(t *testing.T) {
http.HandleFunc("/auth.teams.list", getTeamList)

once.Do(startServer)
api := New("testing-token", OptionAPIURL("http://"+serverAddr+"/"))

teams, cursor, err := api.ListTeams(ListTeamsParameters{})
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}

assert.Len(t, teams, 2)
assert.Equal(t, "T12345678", teams[0].ID)
assert.Equal(t, "Shinichi's workspace", teams[0].Name)

assert.Equal(t, "T12345679", teams[1].ID)
assert.Equal(t, "Migi's workspace", teams[1].Name)

assert.Equal(t, "dXNlcl9pZDo5MTQyOTI5Mzkz", cursor)
}
4 changes: 4 additions & 0 deletions block.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const (
MBTInput MessageBlockType = "input"
MBTHeader MessageBlockType = "header"
MBTRichText MessageBlockType = "rich_text"
MBTCall MessageBlockType = "call"
MBTVideo MessageBlockType = "video"
)

// Block defines an interface all block types should implement
Expand All @@ -39,6 +41,7 @@ type BlockAction struct {
Type ActionType `json:"type"`
Text TextBlockObject `json:"text"`
Value string `json:"value"`
Files []File `json:"files"`
ActionTs string `json:"action_ts"`
SelectedOption OptionBlockObject `json:"selected_option"`
SelectedOptions []OptionBlockObject `json:"selected_options"`
Expand All @@ -50,6 +53,7 @@ type BlockAction struct {
SelectedConversations []string `json:"selected_conversations"`
SelectedDate string `json:"selected_date"`
SelectedTime string `json:"selected_time"`
SelectedDateTime int64 `json:"selected_date_time"`
InitialOption OptionBlockObject `json:"initial_option"`
InitialUser string `json:"initial_user"`
InitialChannel string `json:"initial_channel"`
Expand Down
23 changes: 23 additions & 0 deletions block_call.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package slack

// CallBlock defines data that is used to display a call in slack.
//
// More Information: https://api.slack.com/apis/calls#post_to_channel
type CallBlock struct {
Type MessageBlockType `json:"type"`
BlockID string `json:"block_id,omitempty"`
CallID string `json:"call_id"`
}

// BlockType returns the type of the block
func (s CallBlock) BlockType() MessageBlockType {
return s.Type
}

// NewFileBlock returns a new instance of a file block
func NewCallBlock(callID string) *CallBlock {
return &CallBlock{
Type: MBTCall,
CallID: callID,
}
}
13 changes: 13 additions & 0 deletions block_call_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package slack

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewCallBlock(t *testing.T) {
callBlock := NewCallBlock("ACallID")
assert.Equal(t, string(callBlock.Type), "call")
assert.Equal(t, callBlock.CallID, "ACallID")
}
40 changes: 37 additions & 3 deletions block_conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package slack

import (
"encoding/json"
"errors"
"fmt"
)

Expand Down Expand Up @@ -66,8 +65,14 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
block = &InputBlock{}
case "rich_text":
block = &RichTextBlock{}
case "rich_text_input":
block = &RichTextBlock{}
case "section":
block = &SectionBlock{}
case "call":
block = &CallBlock{}
case "video":
block = &VideoBlock{}
default:
block = &UnknownBlock{}
}
Expand Down Expand Up @@ -110,8 +115,16 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &DatePickerBlockElement{}
case "timepicker":
e = &TimePickerBlockElement{}
case "datetimepicker":
e = &DateTimePickerBlockElement{}
case "plain_text_input":
e = &PlainTextInputBlockElement{}
case "rich_text_input":
e = &RichTextInputBlockElement{}
case "email_text_input":
e = &EmailTextInputBlockElement{}
case "url_text_input":
e = &URLTextInputBlockElement{}
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
e = &SelectBlockElement{}
case "multi_static_select", "multi_external_select", "multi_users_select", "multi_conversations_select", "multi_channels_select":
Expand All @@ -122,8 +135,12 @@ func (b *InputBlock) UnmarshalJSON(data []byte) error {
e = &OverflowBlockElement{}
case "radio_buttons":
e = &RadioButtonsBlockElement{}
case "number_input":
e = &NumberInputBlockElement{}
case "file_input":
e = &FileInputBlockElement{}
default:
return errors.New("unsupported block element type")
return fmt.Errorf("unsupported block element type %v", s.TypeVal)
}

if err := json.Unmarshal(a.Element, e); err != nil {
Expand Down Expand Up @@ -184,14 +201,24 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
blockElement = &DatePickerBlockElement{}
case "timepicker":
blockElement = &TimePickerBlockElement{}
case "datetimepicker":
blockElement = &DateTimePickerBlockElement{}
case "plain_text_input":
blockElement = &PlainTextInputBlockElement{}
case "rich_text_input":
blockElement = &RichTextInputBlockElement{}
case "email_text_input":
blockElement = &EmailTextInputBlockElement{}
case "url_text_input":
blockElement = &URLTextInputBlockElement{}
case "checkboxes":
blockElement = &CheckboxGroupsBlockElement{}
case "radio_buttons":
blockElement = &RadioButtonsBlockElement{}
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
blockElement = &SelectBlockElement{}
case "number_input":
blockElement = &NumberInputBlockElement{}
default:
return fmt.Errorf("unsupported block element type %v", blockElementType)
}
Expand Down Expand Up @@ -221,6 +248,7 @@ func (a *Accessory) MarshalJSON() ([]byte, error) {

// UnmarshalJSON implements the Unmarshaller interface for Accessory, so that any JSON
// unmarshalling is delegated and proper type determination can be made before unmarshal
// Note: datetimepicker is not supported in Accessory
func (a *Accessory) UnmarshalJSON(data []byte) error {
var r json.RawMessage

Expand Down Expand Up @@ -281,6 +309,12 @@ func (a *Accessory) UnmarshalJSON(data []byte) error {
return err
}
a.PlainTextInputElement = element.(*PlainTextInputBlockElement)
case "rich_text_input":
element, err := unmarshalBlockElement(r, &RichTextInputBlockElement{})
if err != nil {
return err
}
a.RichTextInputElement = element.(*RichTextInputBlockElement)
case "radio_buttons":
element, err := unmarshalBlockElement(r, &RadioButtonsBlockElement{})
if err != nil {
Expand Down Expand Up @@ -412,7 +446,7 @@ func (e *ContextElements) UnmarshalJSON(data []byte) error {

e.Elements = append(e.Elements, elem.(*ImageBlockElement))
default:
return errors.New("unsupported context element type")
return fmt.Errorf("unsupported context element type %v", contextElementType)
}
}

Expand Down
Loading

0 comments on commit c106865

Please sign in to comment.