Skip to content

Commit

Permalink
Import profile pictures from Slack. Closes mattermost#53.
Browse files Browse the repository at this point in the history
  • Loading branch information
wetneb committed Sep 12, 2024
1 parent 6f44c6c commit f6f6bb6
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 5 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
45 changes: 43 additions & 2 deletions services/slack/intermediate.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type IntermediateUser struct {
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 @@ -146,7 +147,7 @@ type Intermediate struct {
SeenUsers map[string]bool `json:"seenUsers"`
}

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 @@ -185,6 +186,16 @@ func (t *Transformer) TransformUsers(users []SlackUser, skipEmptyEmails bool, de

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

fmt.Printf("user.Profile.ImagePath %s\n", user.Profile.ImagePath)
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 @@ -197,6 +208,36 @@ 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)
fmt.Printf("Copying profile pic to %s\n", destFilePath)
destFile, err := os.Create(destFilePath)
if err != nil {
return errors.Wrapf(err, "failed to create file %s in the attachments directory", zipPath)
}
defer destFile.Close()

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

log.Printf("SUCCESS COPYING FILE %s TO DEST %s", zipPath, destFilePath)

return nil
}

func filterValidMembers(members []string, users map[string]*IntermediateUser) []string {
validMembers := []string{}
for _, member := range members {
Expand Down Expand Up @@ -807,7 +848,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)

t.Intermediate.SeenUsers = map[string]bool{}
if err := t.TransformAllChannels(slackExport); err != nil {
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 @@ -600,7 +601,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 @@ -670,7 +672,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
5 changes: 5 additions & 0 deletions services/slack/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type SlackProfile struct {
DisplayNameNormalized string `json:"display_name_normalized"`
Email string `json:"email"`
Title string `json:"title"`
ImagePath string `json:"image_path"`
}

type SlackUser struct {
Expand Down Expand Up @@ -147,6 +148,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 @@ -359,6 +361,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 @@ -407,6 +410,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 f6f6bb6

Please sign in to comment.