// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package slackimport import ( "regexp" "strconv" "strings" "github.com/mattermost/mattermost/server/public/shared/mlog" ) func slackConvertTimeStamp(ts string) int64 { timeString := strings.SplitN(ts, ".", 2)[0] timeStamp, err := strconv.ParseInt(timeString, 10, 64) if err != nil { mlog.Warn("Slack Import: Bad timestamp detected.") return 1 } return timeStamp * 1000 // Convert to milliseconds } func slackConvertChannelName(channelName string, channelId string) string { newName := strings.Trim(channelName, "_-") if len(newName) == 1 { return "slack-channel-" + newName } if isValidChannelNameCharacters(newName) { return newName } return strings.ToLower(channelId) } func slackConvertUserMentions(users []slackUser, posts map[string][]slackPost) map[string][]slackPost { var regexes = make(map[string]*regexp.Regexp, len(users)) for _, user := range users { r, err := regexp.Compile("<@" + user.Id + `(\|` + user.Username + ")?>") if err != nil { mlog.Warn("Slack Import: Unable to compile the @mention, matching regular expression for the Slack user.", mlog.String("user_name", user.Username), mlog.String("user_id", user.Id)) continue } regexes["@"+user.Username] = r } // Special cases. regexes["@here"], _ = regexp.Compile(``) regexes["@channel"], _ = regexp.Compile("") regexes["@all"], _ = regexp.Compile("") for channelName, channelPosts := range posts { for postIdx, post := range channelPosts { for mention, r := range regexes { post.Text = r.ReplaceAllString(post.Text, mention) posts[channelName][postIdx] = post } } } return posts } func slackConvertChannelMentions(channels []slackChannel, posts map[string][]slackPost) map[string][]slackPost { var regexes = make(map[string]*regexp.Regexp, len(channels)) for _, channel := range channels { r, err := regexp.Compile("<#" + channel.Id + `(\|` + channel.Name + ")?>") if err != nil { mlog.Warn("Slack Import: Unable to compile the !channel, matching regular expression for the Slack channel.", mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name)) continue } regexes["~"+channel.Name] = r } for channelName, channelPosts := range posts { for postIdx, post := range channelPosts { for channelReplace, r := range regexes { post.Text = r.ReplaceAllString(post.Text, channelReplace) posts[channelName][postIdx] = post } } } return posts } func slackConvertPostsMarkup(posts map[string][]slackPost) map[string][]slackPost { regexReplaceAllString := []struct { regex *regexp.Regexp rpl string }{ // URL { regexp.MustCompile(`<([^|<>]+)\|([^|<>]+)>`), "[$2]($1)", }, // bold { regexp.MustCompile(`(^|[\s.;,])\*(\S[^*\n]+)\*`), "$1**$2**", }, // strikethrough { regexp.MustCompile(`(^|[\s.;,])\~(\S[^~\n]+)\~`), "$1~~$2~~", }, // single paragraph blockquote // Slack converts > character to > { regexp.MustCompile(`(?sm)^>`), ">", }, } regexReplaceAllStringFunc := []struct { regex *regexp.Regexp fn func(string) string }{ // multiple paragraphs blockquotes { regexp.MustCompile(`(?sm)^>>>(.+)$`), func(src string) string { // remove >>> prefix, might have leading \n prefixRegexp := regexp.MustCompile(`^([\n])?>>>(.*)`) src = prefixRegexp.ReplaceAllString(src, "$1$2") // append > to start of line appendRegexp := regexp.MustCompile(`(?m)^`) return appendRegexp.ReplaceAllString(src, ">$0") }, }, } for channelName, channelPosts := range posts { for postIdx, post := range channelPosts { result := post.Text for _, rule := range regexReplaceAllString { result = rule.regex.ReplaceAllString(result, rule.rpl) } for _, rule := range regexReplaceAllStringFunc { result = rule.regex.ReplaceAllStringFunc(result, rule.fn) } posts[channelName][postIdx].Text = result } } return posts }