Skip to content

Commit

Permalink
Fetch channels once only
Browse files Browse the repository at this point in the history
Reduces number of times we run into Slack API limitations.
  • Loading branch information
timoreimann committed Jan 8, 2021
1 parent c4a9e7a commit 80226a5
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 45 deletions.
4 changes: 4 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ type ConfigChannel struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
}
func (cc ConfigChannel) String() string {
return fmt.Sprintf("{ID:%s Name:%q}", cc.ID, cc.Name)
}


// ConfigSlackSync represents a synchronization between a set of PagerDuty schedules and a Slack channel.
type ConfigSlackSync struct {
Expand Down
77 changes: 35 additions & 42 deletions slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ type slackUser struct {
email string
}

type channelList []slack.Channel

func (cl channelList) find(id, name string) *slack.Channel {
if id == "" && name == "" {
return nil
}

for _, ch := range cl {
if ch.ID == id || ch.Name == name {
return &ch
}
}

return nil
}

type slackClient struct {
*slack.Client
}
Expand All @@ -63,68 +79,46 @@ func (cl *slackClient) getSlackUsers(ctx context.Context) (slackUsers, error) {
return slUsers, nil
}

func (cl *slackClient) getChannel(ctx context.Context, name, id string) (*slack.Channel, error) {
var (
slChannel *slack.Channel
err error
)
if id != "" {
fmt.Printf("Looking up channel by ID %s\n", id)
slChannel, err = cl.getChannelByID(ctx, id)
} else {
fmt.Printf("Looking up channel by name %q\n", name)
slChannel, err = cl.getChannelByName(ctx, name)
}

return slChannel, err
}

func (cl *slackClient) getChannelByID(ctx context.Context, id string) (*slack.Channel, error) {
return cl.GetConversationInfoContext(ctx, id, false)
}

func (cl *slackClient) getChannelByName(ctx context.Context, name string) (*slack.Channel, error) {
func (cl *slackClient) getChannels(ctx context.Context) (channelList, error) {
var (
slChannel *slack.Channel
cursor string
list channelList
cursor string
)

Loop:
for {
var (
nextCursor string
channels []slack.Channel
nextCursor string
)
rErr := retryOnSlackRateLimit(ctx, func(ctx context.Context) error {
retErr := retryOnSlackRateLimit(ctx, func(ctx context.Context) error {
var err error
channels, nextCursor, err = cl.GetConversationsContext(ctx, &slack.GetConversationsParameters{
Cursor: cursor,
ExcludeArchived: "true",
Limit: 200,
Types: []string{"public_channel", "private_channel"},
})
return err
})
if rErr != nil {
return nil, rErr
if retErr != nil {
return nil, retErr
}

for _, channel := range channels {
if channel.Name == name {
slChannel = &channel
break Loop
}
list = append(list, channel)
}

if nextCursor == "" {
break
}
cursor = nextCursor
}
if slChannel == nil {
return nil, errors.New("failed to find channel")
}

return slChannel, nil
return list, nil
}

func (cl *slackClient) getChannelByID(ctx context.Context, id string) (*slack.Channel, error) {
return cl.GetConversationInfoContext(ctx, id, false)
}

func (cl *slackClient) getUserGroups(ctx context.Context) ([]UserGroup, error) {
Expand All @@ -141,8 +135,8 @@ func (cl *slackClient) getUserGroups(ctx context.Context) ([]UserGroup, error) {
userGroups := make([]UserGroup, 0, len(groups))
for _, group := range groups {
userGroups = append(userGroups, UserGroup{
ID: group.ID,
Name: group.Name,
ID: group.ID,
Name: group.Name,
Handle: group.Handle,
})
}
Expand All @@ -160,17 +154,17 @@ func (ocgs *oncallGroups) getOrCreate(ug UserGroup) *oncallGroup {
}

ocg := &oncallGroup{
userGroupID: ug.ID,
userGroupID: ug.ID,
userGroupName: ug.Name,
}
*ocgs = append(*ocgs, ocg)
return ocg
}

type oncallGroup struct {
userGroupID string
userGroupID string
userGroupName string
members []string
members []string
}

func (ocg *oncallGroup) ensureMember(m string) {
Expand Down Expand Up @@ -249,7 +243,6 @@ func retryOnSlackRateLimit(ctx context.Context, f func(ctx context.Context) erro
var rle *slack.RateLimitedError
if errors.As(err, &rle) {
sleep := rle.RetryAfter
//sleep := rle.RetryAfter * time.Nanosecond
fmt.Printf("Slack rate limit hit -- waiting %s\n", sleep)
time.Sleep(sleep)
return true, err
Expand Down
15 changes: 12 additions & 3 deletions syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ type syncerParams struct {

func (sp syncerParams) createSlackSyncs(ctx context.Context, cfg config) ([]runSlackSync, error) {
var slSyncs []runSlackSync

fmt.Println("Getting Slack channels")
slChannels, err := sp.slClient.getChannels(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get channels: %s", err)
}
fmt.Printf("Got %d Slack channel(s)\n", len(slChannels))

for _, cfgSlSync := range cfg.SlackSyncs {
slSync := runSlackSync{
name: cfgSlSync.Name,
Expand All @@ -40,9 +48,10 @@ func (sp syncerParams) createSlackSyncs(ctx context.Context, cfg config) ([]runS
return nil, fmt.Errorf("failed to create slack sync %q: failed to parse template %q: %s", slSync.name, cfgSlSync.Template, err)
}

slChannel, err := sp.slClient.getChannel(ctx, cfgSlSync.Channel.Name, cfgSlSync.Channel.ID)
if err != nil {
return nil, fmt.Errorf("failed to create slack sync %q: failed to get Slack channel: %s", slSync.name, err)
cfgChannel := cfgSlSync.Channel
slChannel := slChannels.find(cfgChannel.ID, cfgChannel.Name)
if slChannel == nil {
return nil, fmt.Errorf("failed to create slack sync %q: failed to find configured Slack channel %s", slSync.name, cfgChannel)
}
slSync.slackChannelID = slChannel.ID
fmt.Printf("Slack sync %s: found Slack channel %q (ID %s)\n", slSync.name, slChannel.Name, slChannel.ID)
Expand Down

0 comments on commit 80226a5

Please sign in to comment.