Skip to content

Commit

Permalink
add users channels and team.info chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Mar 8, 2023
1 parent 5613f40 commit c590000
Show file tree
Hide file tree
Showing 7 changed files with 432 additions and 63 deletions.
82 changes: 42 additions & 40 deletions internal/chunk/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package chunk

import (
"fmt"
"strings"

"github.com/slack-go/slack"
)
Expand All @@ -15,68 +16,69 @@ const (
CMessages ChunkType = iota
CThreadMessages
CFiles
CUsers
CChannels
CChannelInfo
CTeamInfo
)

// Chunk is a single chunk that was recorded. It contains the type of chunk,
// the timestamp of the chunk, the channel ID, and the number of messages or
// files that were recorded.
type Chunk struct {
Type ChunkType `json:"_t"`
Timestamp int64 `json:"_ts"`
Type ChunkType `json:"_t"`
Timestamp int64 `json:"_ts"`
IsThread bool `json:"_tm,omitempty"`
Count int `json:"_c"` // number of messages or files

Channel *slack.Channel `json:"_ci,omitempty"`

ChannelID string `json:"_id"`
IsThread bool `json:"_tm,omitempty"`
Count int `json:"_c"` // number of messages or files
Parent *slack.Message `json:"_p,omitempty"`
Messages []slack.Message `json:"_m,omitempty"`
Files []slack.File `json:"_f,omitempty"`
Channel *slack.Channel `json:"_ci,omitempty"`
}

func (c *Chunk) messageID() string {
return c.ChannelID
}

func (c *Chunk) threadID() string {
return threadID(c.ChannelID, c.Parent.ThreadTimestamp)
}

func threadID(channelID, threadTS string) string {
return "t" + channelID + ":" + threadTS
TeamID string `json:"_tid,omitempty"`
Team *slack.Team `json:"_ti,omitempty"`
Users []slack.User `json:"_u,omitempty"`
}

// fileChunkID returns a unique ID for the file chunk.
func (c *Chunk) fileChunkID() string {
return fileID(c.ChannelID, c.Parent.Timestamp)
// ID returns a unique ID for the chunk.
func (c *Chunk) ID() string {
switch c.Type {
case CMessages:
return c.ChannelID
case CThreadMessages:
return threadID(c.ChannelID, c.Parent.ThreadTimestamp)
case CFiles:
return id("f", c.ChannelID, c.Parent.Timestamp)
case CChannelInfo:
return channelInfoID(c.ChannelID, c.IsThread)
case CTeamInfo:
return id("ti", c.TeamID)
case CUsers:
return usersID(c.TeamID)
case CChannels:
return id("ch", c.TeamID)
}
return fmt.Sprintf("<unknown:%s>", c.Type)
}

func (c *Chunk) channelID() string {
return channelID(c.ChannelID, c.IsThread)
func id(prefix string, ids ...string) string {
return prefix + strings.Join(ids, ":")
}

func fileID(channelID, parentTS string) string {
return "f" + channelID + ":" + parentTS
func threadID(channelID, threadTS string) string {
return id("t", channelID, threadTS)
}

func channelID(channelID string, isThread bool) string {
thread := ""
func channelInfoID(channelID string, isThread bool) string {
if isThread {
thread = "t"
return id("tci", channelID)
}
return thread + "ci" + channelID
return id("ci", channelID)
}

// ID returns a unique ID for the chunk.
func (c *Chunk) ID() string {
switch c.Type {
case CMessages:
return c.messageID()
case CThreadMessages:
return c.threadID()
case CFiles:
return c.fileChunkID()
case CChannelInfo:
return c.channelID()
}
return fmt.Sprintf("<unknown:%s>", c.Type)
func usersID(teamID string) string {
return id("u", teamID)
}
155 changes: 155 additions & 0 deletions internal/chunk/chunk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package chunk

import (
"testing"

"github.com/slack-go/slack"
)

func TestChunk_ID(t *testing.T) {
type fields struct {
Type ChunkType
Timestamp int64
IsThread bool
Count int
Channel *slack.Channel
ChannelID string
Parent *slack.Message
Messages []slack.Message
Files []slack.File
TeamID string
Team *slack.Team
Users []slack.User
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "messages",
fields: fields{
Type: CMessages,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "C123",
},
want: "C123",
},
{
name: "threads",
fields: fields{
Type: CThreadMessages,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "C123",
Parent: &slack.Message{
Msg: slack.Msg{ThreadTimestamp: "1234"},
},
},
want: "tC123:1234",
},
{
name: "files",
fields: fields{
Type: CFiles,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
Parent: &slack.Message{
Msg: slack.Msg{Timestamp: "1234"},
},
ChannelID: "C123",
},
want: "fC123:1234",
},
{
name: "channel info",
fields: fields{
Type: CChannelInfo,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "C123",
},
want: "ciC123",
},
{
name: "channel info (thread)",
fields: fields{
Type: CChannelInfo,
Timestamp: 0,
IsThread: true,
Count: 0,
Channel: nil,
ChannelID: "C123",
},
want: "tciC123",
},
{
name: "users",
fields: fields{
Type: CUsers,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "",
TeamID: "T123",
},
want: "uT123",
},
{
name: "team info",
fields: fields{
Type: CTeamInfo,
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "",
TeamID: "T123",
},
want: "tiT123",
},
{
name: "unknown",
fields: fields{
Type: ChunkType(999),
Timestamp: 0,
IsThread: false,
Count: 0,
Channel: nil,
ChannelID: "",
TeamID: "",
},
want: "<unknown:ChunkType(999)>",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &Chunk{
Type: tt.fields.Type,
Timestamp: tt.fields.Timestamp,
IsThread: tt.fields.IsThread,
Count: tt.fields.Count,
Channel: tt.fields.Channel,
ChannelID: tt.fields.ChannelID,
Parent: tt.fields.Parent,
Messages: tt.fields.Messages,
Files: tt.fields.Files,
TeamID: tt.fields.TeamID,
Users: tt.fields.Users,
}
if got := c.ID(); got != tt.want {
t.Errorf("Chunk.ID() = %v, want %v", got, tt.want)
}
})
}
}
9 changes: 6 additions & 3 deletions internal/chunk/chunktype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 42 additions & 13 deletions internal/chunk/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,10 @@ func (p *Player) ForEach(fn func(ev *Chunk) error) error {
return nil
}

// namer is an interface that allows us to get the name of the file.
type namer interface {
// Name should return the name of the file. *os.File implements this
// interface.
Name() string
}

Expand Down Expand Up @@ -232,13 +235,49 @@ func (p *Player) State() (*state.State, error) {
return s, nil
}

// AllMessages returns all the messages for the given channel.
func (p *Player) AllMessages(channelID string) ([]slack.Message, error) {
return p.allMessagesForID(channelID)
}

// AllThreadMessages returns all the messages for the given thread.
func (p *Player) AllThreadMessages(channelID, threadTS string) ([]slack.Message, error) {
return p.allMessagesForID(threadID(channelID, threadTS))
}

func (p *Player) AllTeamUsers(teamID string) ([]slack.User, error) {
return allForID(p, usersID(teamID), func(c *Chunk) []slack.User {
return c.Users
})
}

func (p *Player) AllUsers() (map[string][]slack.User, error) {
users := make(map[string][]slack.User)
if err := p.ForEach(func(c *Chunk) error {
if c.Type != CUsers {
return nil
}
users[c.TeamID] = append(users[c.TeamID], c.Users...)
return nil
}); err != nil {
return nil, err
}
return users, nil
}

// allMessagesForID returns all the messages for the given id. It will reset
// the Player prior to execution.
func (p *Player) allMessagesForID(id string) ([]slack.Message, error) {
return allForID(p, id, func(c *Chunk) []slack.Message {
return c.Messages
})
}

func allForID[T any](p *Player, id string, fn func(*Chunk) []T) ([]T, error) {
if err := p.Reset(); err != nil {
return nil, err
}
var m []slack.Message
var m []T
for {
chunk, err := p.tryGetChunk(id)
if err != nil {
Expand All @@ -247,21 +286,11 @@ func (p *Player) allMessagesForID(id string) ([]slack.Message, error) {
}
return nil, err
}
m = append(m, chunk.Messages...)
m = append(m, fn(chunk)...)
}
return m, nil
}

// AllMessages returns all the messages for the given channel.
func (p *Player) AllMessages(channelID string) ([]slack.Message, error) {
return p.allMessagesForID(channelID)
}

// AllThreadMessages returns all the messages for the given thread.
func (p *Player) AllThreadMessages(channelID, threadTS string) ([]slack.Message, error) {
return p.allMessagesForID(threadID(channelID, threadTS))
}

// AllChannels returns all the channels in the chunkfile.
func (p *Player) AllChannels() []string {
var ids = make([]string, 0, 1)
Expand All @@ -277,7 +306,7 @@ func (p *Player) AllChannels() []string {
// ChannelInfo returns the channel information for the given channel. It
// returns an error if the channel is not found within the chunkfile.
func (p *Player) ChannelInfo(id string) (*slack.Channel, error) {
chunk, err := p.tryGetChunk(channelID(id, false))
chunk, err := p.tryGetChunk(channelInfoID(id, false))
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit c590000

Please sign in to comment.