Skip to content

Commit

Permalink
Import profile pictures from Slack.
Browse files Browse the repository at this point in the history
Closes mattermost#53.

This expects a Slack zip archive produced by slack-advanced-exporter
with the additional PR:
grundleborg/slack-advanced-exporter#37
  • Loading branch information
wetneb committed Sep 20, 2024
1 parent f1af9b2 commit dbfae1d
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 18 deletions.
13 changes: 13 additions & 0 deletions commands/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,19 @@ func transformSlackCmdF(cmd *cobra.Command, args []string) error {
}
}

profilePicturesDir := path.Join(attachmentsDir, "profile_pictures")
if !skipAttachments {
if fileInfo, err := os.Stat(profilePicturesDir); os.IsNotExist(err) {
if createErr := os.MkdirAll(profilePicturesDir, 0755); createErr != nil {
return createErr
}
} else if err != nil {
return err
} else if !fileInfo.IsDir() {
return fmt.Errorf("File \"%s\" is not a directory", attachmentsDir)
}
}

// input file
fileReader, err := os.Open(inputFilePath)
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion services/slack/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func GetImportLineFromUser(user *IntermediateUser, team string) *imports.LineImp
})
}

return &imports.LineImportData{
result := imports.LineImportData{
Type: "user",
User: &imports.UserImportData{
Username: model.NewString(user.Username),
Expand All @@ -126,6 +126,10 @@ func GetImportLineFromUser(user *IntermediateUser, team string) *imports.LineImp
},
},
}
if len(user.ProfilePicture) > 0 {
result.User.ProfileImage = model.NewString(user.ProfilePicture)
}
return &result
}

func GetAttachmentImportDataFromPaths(paths []string) []imports.AttachmentImportData {
Expand Down
59 changes: 48 additions & 11 deletions services/slack/intermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,16 @@ func (c *IntermediateChannel) Sanitise(logger log.FieldLogger) {
}

type IntermediateUser struct {
Id string `json:"id"`
Username string `json:"username"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Position string `json:"position"`
Email string `json:"email"`
Password string `json:"password"`
Memberships []string `json:"memberships"`
DeleteAt int64 `json:"delete_at"`
Id string `json:"id"`
Username string `json:"username"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Position string `json:"position"`
Email string `json:"email"`
Password string `json:"password"`
Memberships []string `json:"memberships"`
DeleteAt int64 `json:"delete_at"`
ProfilePicture string `json:"profile_picture"`
}

func (u *IntermediateUser) Sanitise(logger log.FieldLogger, defaultEmailDomain string, skipEmptyEmails bool) {
Expand Down Expand Up @@ -129,7 +130,7 @@ type Intermediate struct {
Posts []*IntermediatePost `json:"posts"`
}

func (t *Transformer) TransformUsers(users []SlackUser, skipEmptyEmails bool, defaultEmailDomain string) {
func (t *Transformer) TransformUsers(users []SlackUser, skipEmptyEmails bool, defaultEmailDomain string, attachmentsDir string, pictures map[string]*zip.File) {
t.Logger.Info("Transforming users")

t.Logger.Debugf("TransformUsers: Input SlackUser structs: %+v", users)
Expand Down Expand Up @@ -165,6 +166,15 @@ func (t *Transformer) TransformUsers(users []SlackUser, skipEmptyEmails bool, de

t.Logger.Debugf("TransformUsers: newUser IntermediateUser struct: %+v", newUser)

if len(user.Profile.ImagePath) > 0 {
err := addProfilePicture(user.Profile.ImagePath, pictures, attachmentsDir)
if err == nil {
newUser.ProfilePicture = user.Profile.ImagePath
} else {
t.Logger.Warnf("Failed to import profile picture: %s", err.Error())
}
}

if user.IsBot {
newUser.Id = user.Profile.BotID
}
Expand All @@ -177,6 +187,33 @@ func (t *Transformer) TransformUsers(users []SlackUser, skipEmptyEmails bool, de
t.Intermediate.UsersById = resultUsers
}

func addProfilePicture(zipPath string, pictures map[string]*zip.File, attachmentsDir string) error {
zipFile, ok := pictures[zipPath]
if !ok {
return errors.Errorf("failed to retrieve profile picture with id %s", zipPath)
}

zipFileReader, err := zipFile.Open()
if err != nil {
return errors.Wrapf(err, "failed to open profile picture from zipfile for id %s", zipPath)
}
defer zipFileReader.Close()

destFilePath := path.Join(attachmentsDir, zipPath)
destFile, err := os.Create(destFilePath)
if err != nil {
return errors.Wrapf(err, "failed to create file %s in the profile pictures directory", zipPath)
}
defer destFile.Close()

_, err = io.Copy(destFile, zipFileReader)
if err != nil {
return errors.Wrapf(err, "failed to write file %s in the profile pictures directory", zipPath)
}

return nil
}

func filterValidMembers(members []string, users map[string]*IntermediateUser) []string {
validMembers := []string{}
for _, member := range members {
Expand Down Expand Up @@ -760,7 +797,7 @@ func (t *Transformer) TransformPosts(slackExport *SlackExport, attachmentsDir st
}

func (t *Transformer) Transform(slackExport *SlackExport, attachmentsDir string, skipAttachments, discardInvalidProps, allowDownload, skipEmptyEmails bool, defaultEmailDomain string) error {
t.TransformUsers(slackExport.Users, skipEmptyEmails, defaultEmailDomain)
t.TransformUsers(slackExport.Users, skipEmptyEmails, defaultEmailDomain, attachmentsDir, slackExport.ProfilePictures)

if err := t.TransformAllChannels(slackExport); err != nil {
return err
Expand Down
7 changes: 5 additions & 2 deletions services/slack/intermediate_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package slack

import (
"archive/zip"
"bytes"
"fmt"
"os"
Expand Down Expand Up @@ -571,7 +572,8 @@ func TestTransformUsers(t *testing.T) {

defaultEmailDomain := ""
skipEmptyEmails := false
slackTransformer.TransformUsers(users, skipEmptyEmails, defaultEmailDomain)
pictures := make(map[string]*zip.File)
slackTransformer.TransformUsers(users, skipEmptyEmails, defaultEmailDomain, "data/", pictures)
require.Len(t, slackTransformer.Intermediate.UsersById, len(users))

for i, id := range []string{id1, id2, id3} {
Expand Down Expand Up @@ -641,7 +643,8 @@ func TestDeleteAt(t *testing.T) {

defaultEmailDomain := ""
skipEmptyEmails := false
slackTransformer.TransformUsers(users, skipEmptyEmails, defaultEmailDomain)
pictures := make(map[string]*zip.File)
slackTransformer.TransformUsers(users, skipEmptyEmails, defaultEmailDomain, "data/", pictures)
require.Zero(t, slackTransformer.Intermediate.UsersById[activeUsers[0].Id].DeleteAt)
require.Zero(t, slackTransformer.Intermediate.UsersById[activeUsers[1].Id].DeleteAt)
require.NotZero(t, slackTransformer.Intermediate.UsersById[inactiveUsers[0].Id].DeleteAt)
Expand Down
13 changes: 9 additions & 4 deletions services/slack/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ type SlackChannelSub struct {
}

type SlackProfile struct {
BotID string `json:"bot_id"`
RealName string `json:"real_name"`
Email string `json:"email"`
Title string `json:"title"`
BotID string `json:"bot_id"`
RealName string `json:"real_name"`
Email string `json:"email"`
Title string `json:"title"`
ImagePath string `json:"image_path"`
}

type SlackUser struct {
Expand Down Expand Up @@ -134,6 +135,7 @@ type SlackExport struct {
Users []SlackUser
Posts map[string][]SlackPost
Uploads map[string]*zip.File
ProfilePictures map[string]*zip.File
}

func (t *Transformer) SlackParseUsers(data io.Reader) ([]SlackUser, error) {
Expand Down Expand Up @@ -345,6 +347,7 @@ func (t *Transformer) ParseSlackExportFile(zipReader *zip.Reader, skipConvertPos
slackExport := SlackExport{TeamName: t.TeamName}
slackExport.Posts = make(map[string][]SlackPost)
slackExport.Uploads = make(map[string]*zip.File)
slackExport.ProfilePictures = make(map[string]*zip.File)
numFiles := len(zipReader.File)

for i, file := range zipReader.File {
Expand Down Expand Up @@ -393,6 +396,8 @@ func (t *Transformer) ParseSlackExportFile(zipReader *zip.Reader, skipConvertPos
}
} else if len(spl) == 3 && spl[0] == "__uploads" {
slackExport.Uploads[spl[1]] = file
} else if len(spl) == 2 && spl[0] == "profile_pictures" {
slackExport.ProfilePictures[file.Name] = file
}
}

Expand Down

0 comments on commit dbfae1d

Please sign in to comment.