Skip to content

Commit

Permalink
Support private channels
Browse files Browse the repository at this point in the history
This patch adds support to private channels. Previously, private
channels were read-only, while now it's bidirectional like public
channels and IMs.

This required a refactoring of the channels API, and a few bug fixes.
Now anything channel-related goes through the Channel and Channels
structures, and should not use the Slack conversations API directly
anymore.

Signed-off-by: Andrea Barberio <[email protected]>
  • Loading branch information
insomniacslk committed Jan 26, 2021
1 parent 3cfeb31 commit 6debed9
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 157 deletions.
47 changes: 30 additions & 17 deletions pkg/ircslack/channels.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package ircslack

import (
"context"
"strings"
"sync"
"time"

Expand All @@ -11,23 +10,36 @@ import (

// Channels wraps the channel list with convenient operations and cache.
type Channels struct {
channels map[string]slack.Channel
channels map[string]Channel
Pagination int
mu sync.Mutex
}

// NewChannels creates a new Channels object.
func NewChannels(pagination int) *Channels {
return &Channels{
channels: make(map[string]slack.Channel),
channels: make(map[string]Channel),
Pagination: pagination,
}
}

// SupportedChannelPrefixes returns a list of supported channel prefixes.
func SupportedChannelPrefixes() []string {
return []string{
ChannelPrefixPublicChannel,
ChannelPrefixPrivateChannel,
ChannelPrefixMpIM,
ChannelPrefixThread,
}

}

// AsMap returns the channels as a map of name -> channel. The map is copied to
// avoid data races
func (c *Channels) AsMap() map[string]slack.Channel {
var ret map[string]slack.Channel
func (c *Channels) AsMap() map[string]Channel {
c.mu.Lock()
defer c.mu.Unlock()
ret := make(map[string]Channel, len(c.channels))
for k, v := range c.channels {
ret[k] = v
}
Expand All @@ -43,20 +55,22 @@ func (c *Channels) Fetch(client *slack.Client) error {
var (
err error
ctx = context.Background()
channels = make(map[string]slack.Channel)
channels = make(map[string]Channel)
)
start := time.Now()
params := slack.GetConversationsParameters{
Types: []string{"public_channel", "private_channel"},
Limit: c.Pagination,
}
for err == nil {
chans, nextCursor, err := client.GetConversationsContext(ctx, &params)
if err == nil {
log.Debugf("Retrieved %d channels (current total is %d)", len(chans), len(channels))
for _, c := range chans {
for _, sch := range chans {
// WARNING WARNING WARNING: channels are internally mapped by
// name, while users are mapped by ID.
channels[c.Name] = c
// the Slack name, while users are mapped by Slack ID.
ch := Channel(sch)
channels[ch.SlackName()] = ch
}
} else if rateLimitedError, ok := err.(*slack.RateLimitedError); ok {
select {
Expand Down Expand Up @@ -88,7 +102,7 @@ func (c *Channels) Count() int {
}

// ByID retrieves a channel by its Slack ID.
func (c *Channels) ByID(id string) *slack.Channel {
func (c *Channels) ByID(id string) *Channel {
c.mu.Lock()
defer c.mu.Unlock()
for _, c := range c.channels {
Expand All @@ -99,17 +113,16 @@ func (c *Channels) ByID(id string) *slack.Channel {
return nil
}

// ByName retrieves a channel by its Slack name.
func (c *Channels) ByName(name string) *slack.Channel {
if strings.HasPrefix(name, "#") {
// ByName retrieves a channel by its Slack or IRC name.
func (c *Channels) ByName(name string) *Channel {
if HasChannelPrefix(name) {
// without prefix, the channel now has the form of a Slack name
name = name[1:]
}
c.mu.Lock()
defer c.mu.Unlock()
for _, c := range c.channels {
if c.Name == name {
return &c
}
if ch, ok := c.channels[name]; ok {
return &ch
}
return nil
}
37 changes: 13 additions & 24 deletions pkg/ircslack/event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,18 @@ func joinText(first string, second string, divider string) string {
return first + divider + second
}

func formatMultipartyChannelName(slackChannelID string, slackChannelName string) string {
name := "&" + slackChannelID + "|" + slackChannelName
name = strings.Replace(name, "mpdm-", "", -1)
name = strings.Replace(name, "--", "-", -1)
if len(name) >= 30 {
return name[:29] + "…"
}
return name
}

func formatThreadChannelName(threadTimestamp string, channel *slack.Channel) string {
func formatThreadChannelName(threadTimestamp string, channel *Channel) string {
return "+" + channel.Name + "-" + threadTimestamp
}

func resolveChannelName(ctx *IrcContext, msgChannel, threadTimestamp string) string {
// channame := ""
if strings.HasPrefix(msgChannel, "C") || strings.HasPrefix(msgChannel, "G") {
// Channel message
channel, err := ctx.GetConversationInfo(msgChannel)
channel := ctx.Channels.ByID(msgChannel)

if err != nil {
log.Warningf("Failed to get channel info for %v: %v", msgChannel, err)
if channel == nil {
log.Warningf("Unknown channel %s", msgChannel)
return ""
} else if threadTimestamp != "" {
channame := formatThreadChannelName(threadTimestamp, channel)
Expand Down Expand Up @@ -77,21 +67,20 @@ func resolveChannelName(ctx *IrcContext, msgChannel, threadTimestamp string) str
}
return channame
} else if channel.IsMpIM {
channame := formatMultipartyChannelName(msgChannel, channel.Name)
if ctx.Channels.ByName(channame) == nil {
if ctx.Channels.ByName(channel.IRCName()) == nil {
IrcSendChanInfoAfterJoin(
ctx,
channame,
channel.IRCName(),
msgChannel,
channel.Purpose.Value,
[]string{},
true,
)
}
return channame
return channel.IRCName()
}

return "#" + channel.Name
return channel.IRCName()
} else if strings.HasPrefix(msgChannel, "D") {
// Direct message to me
users, err := usersInConversation(ctx, msgChannel)
Expand Down Expand Up @@ -301,11 +290,11 @@ func eventHandler(ctx *IrcContext, rtm *slack.RTM) {
case "channel_topic":
// https://api.slack.com/events/message/channel_topic
// Send out new topic
channel, err := ctx.SlackClient.GetChannelInfo(message.Channel)
if err != nil {
channel := ctx.Channels.ByID(message.Channel)
if channel == nil {
log.Warningf("Cannot get channel name for %v", message.Channel)
} else {
newTopic := fmt.Sprintf(":%v TOPIC #%v :%v\r\n", ctx.Mask(), channel.Name, message.Topic)
newTopic := fmt.Sprintf(":%v TOPIC %s :%v\r\n", ctx.Mask(), channel.IRCName(), message.Topic)
log.Infof("Got new topic: %v", newTopic)
if _, err := ctx.Conn.Write([]byte(newTopic)); err != nil {
log.Warningf("Failed to send IRC message: %v", err)
Expand Down Expand Up @@ -339,7 +328,7 @@ func eventHandler(ctx *IrcContext, rtm *slack.RTM) {
log.Warningf("Unknown channel: %s", ev.Channel)
continue
}
if _, err := ctx.Conn.Write([]byte(fmt.Sprintf(":%v JOIN #%v\r\n", ctx.Mask(), ch.Name))); err != nil {
if _, err := ctx.Conn.Write([]byte(fmt.Sprintf(":%v JOIN %s\r\n", ctx.Mask(), ch.IRCName()))); err != nil {
log.Warningf("Failed to send IRC message: %v", err)
}
case *slack.MemberLeftChannelEvent:
Expand All @@ -352,7 +341,7 @@ func eventHandler(ctx *IrcContext, rtm *slack.RTM) {
log.Warningf("Unknown channel: %s", ev.Channel)
continue
}
if _, err := ctx.Conn.Write([]byte(fmt.Sprintf(":%v PART #%v\r\n", ctx.Mask(), ch.Name))); err != nil {
if _, err := ctx.Conn.Write([]byte(fmt.Sprintf(":%v PART %s\r\n", ctx.Mask(), ch.IRCName()))); err != nil {
log.Warningf("Failed to send IRC message: %v", err)
}
case *slack.TeamJoinEvent:
Expand Down
40 changes: 0 additions & 40 deletions pkg/ircslack/irc_channel.go

This file was deleted.

27 changes: 0 additions & 27 deletions pkg/ircslack/irc_channel_test.go

This file was deleted.

Loading

0 comments on commit 6debed9

Please sign in to comment.