github.com/mattermost/mattermost-server/server/v8@v8.0.0-20230610055354-a6d1d38b273d/platform/services/slackimport/converters.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package slackimport 5 6 import ( 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/mattermost/mattermost-server/server/public/shared/mlog" 12 ) 13 14 func slackConvertTimeStamp(ts string) int64 { 15 timeString := strings.SplitN(ts, ".", 2)[0] 16 17 timeStamp, err := strconv.ParseInt(timeString, 10, 64) 18 if err != nil { 19 mlog.Warn("Slack Import: Bad timestamp detected.") 20 return 1 21 } 22 return timeStamp * 1000 // Convert to milliseconds 23 } 24 25 func slackConvertChannelName(channelName string, channelId string) string { 26 newName := strings.Trim(channelName, "_-") 27 if len(newName) == 1 { 28 return "slack-channel-" + newName 29 } 30 31 if isValidChannelNameCharacters(newName) { 32 return newName 33 } 34 return strings.ToLower(channelId) 35 } 36 37 func slackConvertUserMentions(users []slackUser, posts map[string][]slackPost) map[string][]slackPost { 38 var regexes = make(map[string]*regexp.Regexp, len(users)) 39 for _, user := range users { 40 r, err := regexp.Compile("<@" + user.Id + `(\|` + user.Username + ")?>") 41 if err != nil { 42 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)) 43 continue 44 } 45 regexes["@"+user.Username] = r 46 } 47 48 // Special cases. 49 regexes["@here"], _ = regexp.Compile(`<!here\|@here>`) 50 regexes["@channel"], _ = regexp.Compile("<!channel>") 51 regexes["@all"], _ = regexp.Compile("<!everyone>") 52 53 for channelName, channelPosts := range posts { 54 for postIdx, post := range channelPosts { 55 for mention, r := range regexes { 56 post.Text = r.ReplaceAllString(post.Text, mention) 57 posts[channelName][postIdx] = post 58 } 59 } 60 } 61 62 return posts 63 } 64 65 func slackConvertChannelMentions(channels []slackChannel, posts map[string][]slackPost) map[string][]slackPost { 66 var regexes = make(map[string]*regexp.Regexp, len(channels)) 67 for _, channel := range channels { 68 r, err := regexp.Compile("<#" + channel.Id + `(\|` + channel.Name + ")?>") 69 if err != nil { 70 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)) 71 continue 72 } 73 regexes["~"+channel.Name] = r 74 } 75 76 for channelName, channelPosts := range posts { 77 for postIdx, post := range channelPosts { 78 for channelReplace, r := range regexes { 79 post.Text = r.ReplaceAllString(post.Text, channelReplace) 80 posts[channelName][postIdx] = post 81 } 82 } 83 } 84 85 return posts 86 } 87 88 func slackConvertPostsMarkup(posts map[string][]slackPost) map[string][]slackPost { 89 regexReplaceAllString := []struct { 90 regex *regexp.Regexp 91 rpl string 92 }{ 93 // URL 94 { 95 regexp.MustCompile(`<([^|<>]+)\|([^|<>]+)>`), 96 "[$2]($1)", 97 }, 98 // bold 99 { 100 regexp.MustCompile(`(^|[\s.;,])\*(\S[^*\n]+)\*`), 101 "$1**$2**", 102 }, 103 // strikethrough 104 { 105 regexp.MustCompile(`(^|[\s.;,])\~(\S[^~\n]+)\~`), 106 "$1~~$2~~", 107 }, 108 // single paragraph blockquote 109 // Slack converts > character to > 110 { 111 regexp.MustCompile(`(?sm)^>`), 112 ">", 113 }, 114 } 115 116 regexReplaceAllStringFunc := []struct { 117 regex *regexp.Regexp 118 fn func(string) string 119 }{ 120 // multiple paragraphs blockquotes 121 { 122 regexp.MustCompile(`(?sm)^>>>(.+)$`), 123 func(src string) string { 124 // remove >>> prefix, might have leading \n 125 prefixRegexp := regexp.MustCompile(`^([\n])?>>>(.*)`) 126 src = prefixRegexp.ReplaceAllString(src, "$1$2") 127 // append > to start of line 128 appendRegexp := regexp.MustCompile(`(?m)^`) 129 return appendRegexp.ReplaceAllString(src, ">$0") 130 }, 131 }, 132 } 133 134 for channelName, channelPosts := range posts { 135 for postIdx, post := range channelPosts { 136 result := post.Text 137 138 for _, rule := range regexReplaceAllString { 139 result = rule.regex.ReplaceAllString(result, rule.rpl) 140 } 141 142 for _, rule := range regexReplaceAllStringFunc { 143 result = rule.regex.ReplaceAllStringFunc(result, rule.fn) 144 } 145 posts[channelName][postIdx].Text = result 146 } 147 } 148 149 return posts 150 }