static List <Models.Combined.AttachmentsMapping> GetAndUploadMessages(Models.Combined.ChannelsMapping channelsMapping, string basePath, List <ViewModels.SimpleUser> slackUserList, String aadAccessToken, String selectedTeamId, bool copyFileAttachments) { var messageList = new List <ViewModels.SimpleMessage>(); messageList.Clear(); var messageListJsonSource = new JArray(); messageListJsonSource.Clear(); List <Models.Combined.AttachmentsMapping> attachmentsToUpload = new List <Models.Combined.AttachmentsMapping>(); attachmentsToUpload.Clear(); Console.WriteLine("Migrating messages in channel " + channelsMapping.slackChannelName); foreach (var file in Directory.GetFiles(Path.Combine(basePath, channelsMapping.slackChannelName))) { Console.WriteLine("File " + file); using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(fs)) using (JsonTextReader reader = new JsonTextReader(sr)) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { JObject obj = JObject.Load(reader); // SelectToken returns null not an empty string if nothing is found // I'm too lazy right now for strongly typed classes // deal with message basics: when, body, who var messageTs = (string)obj.SelectToken("ts"); //added 04/18/2018 to make timestamps friendlier // 04/25/2018 try to convert to local time from UTC if (messageTs != null) { System.DateTime dateTime = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); dateTime = dateTime.AddSeconds(Convert.ToDouble(messageTs)); dateTime = dateTime.ToLocalTime(); messageTs = Convert.ToString(dateTime); } var messageText = (string)obj.SelectToken("text"); var messageId = channelsMapping.slackChannelId + "." + messageTs; /// for regex matching of in-message usernames //Multiple users? Match match = Regex.Match(messageText, @"\<\@([A-Za-z0-9\-]+)\>", RegexOptions.IgnoreCase); while (match.Success) { string inMsgSlackUserId = match.Value.Replace("<", "").Replace("@", "").Replace(">", ""); messageText = Regex.Replace(messageText, @"\<\@([A-Za-z0-9\-]+)\>", findInMsgUser(inMsgSlackUserId, slackUserList)); match = Regex.Match(messageText, @"\<\@([A-Za-z0-9\-]+)\>", RegexOptions.IgnoreCase); } // A better, more object oriented implementaion would be better //messageText = RegexDetector.DetectSlackParens(messageText, slackUserList); var messageSender = Utils.Messages.FindMessageSender(obj, slackUserList); // create a list of attachments to upload // deal with "attachments" that are files // specifically, files hosted by Slack // SelectToken returns null not an empty string if nothing is found var fileUrl = (string)obj.SelectToken("file.url_private"); var fileId = (string)obj.SelectToken("file.id"); var fileMode = (string)obj.SelectToken("file.mode"); var fileName = (string)obj.SelectToken("file.name"); // sometimes, slack returns a filename without an extension, which makes it a pain to //open the attachment. what's a reliable way to check file extensions? what field will give a clean reference? if (fileName != null) { if (fileName.Contains("_")) { fileName = ReplaceLastOccurence(fileName, "_", "."); } } //^^I'm just replacing the last occurence of _ with . , because hosted files seem to be in that format //Let's see if it works - so far, so good ViewModels.SimpleMessage.FileAttachment fileAttachment = null; if (fileMode != "external" && fileId != null && fileUrl != null) { Console.WriteLine("Message attachment found with ID " + fileId); attachmentsToUpload.Add(new Models.Combined.AttachmentsMapping { attachmentId = fileId, attachmentUrl = fileUrl, attachmentChannelId = channelsMapping.slackChannelId, attachmentFileName = fileName, msChannelName = channelsMapping.displayName }); // map the attachment to fileAttachment which is used in the viewmodel fileAttachment = new ViewModels.SimpleMessage.FileAttachment { id = fileId, originalName = (string)obj.SelectToken("file.name"), originalTitle = (string)obj.SelectToken("file.title"), originalUrl = (string)obj.SelectToken("file.permalink") }; } // deal with "attachments" that aren't files List <ViewModels.SimpleMessage.Attachments> attachmentsList = new List <ViewModels.SimpleMessage.Attachments>(); List <ViewModels.SimpleMessage.Attachments.Fields> fieldsList = new List <ViewModels.SimpleMessage.Attachments.Fields>(); //added try catch to handle the case of "attachments"": null , which was causing this to fail try { var attachmentsObject = (JArray)obj.SelectToken("attachments"); if (attachmentsObject != null) { foreach (var attachmentItem in attachmentsObject) { var attachmentText = (string)attachmentItem.SelectToken("text"); var attachmentTextFallback = (string)attachmentItem.SelectToken("fallback"); var attachmentItemToAdd = new ViewModels.SimpleMessage.Attachments(); if (!String.IsNullOrEmpty(attachmentText)) { attachmentItemToAdd.text = attachmentText; } else if (!String.IsNullOrEmpty(attachmentTextFallback)) { attachmentItemToAdd.text = attachmentTextFallback; } var attachmentServiceName = (string)attachmentItem.SelectToken("service_name"); if (!String.IsNullOrEmpty(attachmentServiceName)) { attachmentItemToAdd.service_name = attachmentServiceName; } var attachmentFromUrl = (string)attachmentItem.SelectToken("from_url"); if (!String.IsNullOrEmpty(attachmentFromUrl)) { attachmentItemToAdd.url = attachmentFromUrl; } var attachmentColor = (string)attachmentItem.SelectToken("color"); if (!String.IsNullOrEmpty(attachmentColor)) { attachmentItemToAdd.color = attachmentColor; } var fieldsObject = (JArray)attachmentItem.SelectToken("fields"); if (fieldsObject != null) { fieldsList.Clear(); foreach (var fieldItem in fieldsObject) { fieldsList.Add(new ViewModels.SimpleMessage.Attachments.Fields() { title = (string)fieldItem.SelectToken("title"), value = (string)fieldItem.SelectToken("value"), shortWidth = (bool)fieldItem.SelectToken("short") }); } attachmentItemToAdd.fields = fieldsList; } else { attachmentItemToAdd.fields = null; } attachmentsList.Add(attachmentItemToAdd); } } else { attachmentsList = null; } } catch (Exception e) { Console.WriteLine(e.Message); attachmentsList = null; } // do some stuff with slack message threading at some point messageList.Add(new ViewModels.SimpleMessage { id = messageId, text = messageText, ts = messageTs, user = messageSender, fileAttachment = fileAttachment, attachments = attachmentsList, }); } } } } if (copyFileAttachments) { Utils.FileAttachments.ArchiveMessageFileAttachments(aadAccessToken, selectedTeamId, attachmentsToUpload, "fileattachments").Wait(); foreach (var messageItem in messageList) { if (messageItem.fileAttachment != null) { var messageItemWithFileAttachment = attachmentsToUpload.Find(w => String.Equals(messageItem.fileAttachment.id, w.attachmentId, StringComparison.CurrentCultureIgnoreCase)); if (messageItemWithFileAttachment != null) { messageItem.fileAttachment.spoId = messageItemWithFileAttachment.msSpoId; messageItem.fileAttachment.spoUrl = messageItemWithFileAttachment.msSpoUrl; } } } } //Utils.Messages.CreateSlackMessageJsonArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); Utils.Messages.CreateSlackMessageHtmlArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); return(attachmentsToUpload); }
static List <Models.Combined.AttachmentsMapping> GetAndUploadMessages(Models.Combined.ChannelsMapping channelsMapping, string basePath, List <ViewModels.SimpleUser> slackUserList, String aadAccessToken, String selectedTeamId, bool copyFileAttachments) { var messageList = new List <ViewModels.SimpleMessage>(); messageList.Clear(); var messageListJsonSource = new JArray(); messageListJsonSource.Clear(); List <Models.Combined.AttachmentsMapping> attachmentsToUpload = new List <Models.Combined.AttachmentsMapping>(); attachmentsToUpload.Clear(); Console.WriteLine("Migrating messages in channel " + channelsMapping.slackChannelName); foreach (var file in Directory.GetFiles(Path.Combine(basePath, channelsMapping.slackChannelName)).OrderBy(f => f)) { Console.WriteLine("File " + file); using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(fs)) using (JsonTextReader reader = new JsonTextReader(sr)) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { JObject obj = JObject.Load(reader); // SelectToken returns null not an empty string if nothing is found // I'm too lazy right now for strongly typed classes // deal with message basics: when, body, who var messageTs = (string)obj.SelectToken("ts"); var messageText = (string)obj.SelectToken("text"); var messageId = channelsMapping.slackChannelId + "." + messageTs; //messageText = RegexDetector.DetectSlackParens(messageText, slackUserList); var messageSender = Utils.Messages.FindMessageSender(obj, slackUserList); // create a list of attachments to upload // deal with "attachments" that are files // specifically, files hosted by Slack // SelectToken returns null not an empty string if nothing is found var fileUrl = (string)obj.SelectToken("file.url_private"); var fileId = (string)obj.SelectToken("file.id"); var fileMode = (string)obj.SelectToken("file.mode"); var fileName = (string)obj.SelectToken("file.name"); ViewModels.SimpleMessage.FileAttachment fileAttachment = null; if (fileMode != "external" && fileId != null && fileUrl != null) { Console.WriteLine("Message attachment found with ID " + fileId); attachmentsToUpload.Add(new Models.Combined.AttachmentsMapping { attachmentId = fileId, attachmentUrl = fileUrl, attachmentChannelId = channelsMapping.slackChannelId, attachmentFileName = fileName, msChannelName = channelsMapping.displayName }); // map the attachment to fileAttachment which is used in the viewmodel fileAttachment = new ViewModels.SimpleMessage.FileAttachment { id = fileId, originalName = (string)obj.SelectToken("file.name"), originalTitle = (string)obj.SelectToken("file.title"), originalUrl = (string)obj.SelectToken("file.permalink") }; } // deal with "attachments" that aren't files List <ViewModels.SimpleMessage.Attachments> attachmentsList = new List <ViewModels.SimpleMessage.Attachments>(); List <ViewModels.SimpleMessage.Attachments.Fields> fieldsList = new List <ViewModels.SimpleMessage.Attachments.Fields>(); var attachmentsObject = (JArray)obj.SelectToken("attachments"); if (attachmentsObject != null) { foreach (var attachmentItem in attachmentsObject) { var attachmentText = (string)attachmentItem.SelectToken("text"); var attachmentTextFallback = (string)attachmentItem.SelectToken("fallback"); var attachmentItemToAdd = new ViewModels.SimpleMessage.Attachments(); if (!String.IsNullOrEmpty(attachmentText)) { attachmentItemToAdd.text = attachmentText; } else if (!String.IsNullOrEmpty(attachmentTextFallback)) { attachmentItemToAdd.text = attachmentTextFallback; } var attachmentServiceName = (string)attachmentItem.SelectToken("service_name"); if (!String.IsNullOrEmpty(attachmentServiceName)) { attachmentItemToAdd.service_name = attachmentServiceName; } var attachmentFromUrl = (string)attachmentItem.SelectToken("from_url"); if (!String.IsNullOrEmpty(attachmentFromUrl)) { attachmentItemToAdd.url = attachmentFromUrl; } var attachmentColor = (string)attachmentItem.SelectToken("color"); if (!String.IsNullOrEmpty(attachmentColor)) { attachmentItemToAdd.color = attachmentColor; } var fieldsObject = (JArray)attachmentItem.SelectToken("fields"); if (fieldsObject != null) { fieldsList.Clear(); foreach (var fieldItem in fieldsObject) { fieldsList.Add(new ViewModels.SimpleMessage.Attachments.Fields() { title = (string)fieldItem.SelectToken("title"), value = (string)fieldItem.SelectToken("value"), shortWidth = (bool)fieldItem.SelectToken("short") }); } attachmentItemToAdd.fields = fieldsList; } else { attachmentItemToAdd.fields = null; } attachmentsList.Add(attachmentItemToAdd); } } else { attachmentsList = null; } // do some stuff with slack message threading at some point messageList.Add(new ViewModels.SimpleMessage { id = messageId, text = messageText, ts = messageTs, user = messageSender, fileAttachment = fileAttachment, attachments = attachmentsList, }); } } } } if (copyFileAttachments) { Utils.FileAttachments.ArchiveMessageFileAttachments(aadAccessToken, selectedTeamId, attachmentsToUpload, "fileattachments").Wait(); foreach (var messageItem in messageList) { if (messageItem.fileAttachment != null) { var messageItemWithFileAttachment = attachmentsToUpload.Find(w => String.Equals(messageItem.fileAttachment.id, w.attachmentId, StringComparison.CurrentCultureIgnoreCase)); if (messageItemWithFileAttachment != null) { messageItem.fileAttachment.spoId = messageItemWithFileAttachment.msSpoId; messageItem.fileAttachment.spoUrl = messageItemWithFileAttachment.msSpoUrl; } } } } Utils.Messages.CreateSlackMessageJsonArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); Utils.Messages.CreateSlackMessageHtmlArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); return(attachmentsToUpload); }
static void GetAndUploadMessages(Models.Combined.ChannelsMapping channelsMapping, string basePath, List <ViewModels.SimpleUser> slackUserList, String aadAccessToken, String selectedTeamId, bool copyFileAttachments, bool uploadHTMLAndJSONFiles) { var messageList = new List <ViewModels.SimpleMessage>(); messageList.Clear(); var messageListJsonSource = new JArray(); messageListJsonSource.Clear(); List <Models.Combined.AttachmentsMapping> attachmentsToUpload = new List <Models.Combined.AttachmentsMapping>(); attachmentsToUpload.Clear(); Console.WriteLine("Migrating messages in channel " + channelsMapping.slackChannelName); foreach (var file in Directory.GetFiles(Path.Combine(basePath, channelsMapping.slackChannelName))) { Console.WriteLine("File " + file); using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(fs)) using (JsonTextReader reader = new JsonTextReader(sr)) { while (reader.Read()) { if (reader.TokenType == JsonToken.StartObject) { JObject obj = JObject.Load(reader); // SelectToken returns null not an empty string if nothing is found // Hence all the null coalescing // deal with message basics: when, body, who var messageTs = new DateTime(1970, 4, 9, 1, 5, 0).AddSeconds(Double.Parse((string)(obj.SelectToken("ts") ?? ""))); var messageText = (string)(obj.SelectToken("text") ?? ""); var messageId = channelsMapping.slackChannelId + "." + messageTs; messageText = DetectSlackParens(messageText, slackUserList); var messageSender = Utils.Messages.FindMessageSender(obj, slackUserList); // create a list of attachments to upload // deal with "attachments" that are files // specifically, files hosted by Slack var fileUrl = (string)obj.SelectToken("file.url_private"); var fileId = (string)obj.SelectToken("file.id"); var fileMode = (string)obj.SelectToken("file.mode"); var fileName = (string)obj.SelectToken("file.name"); ViewModels.SimpleMessage.FileAttachment fileAttachment = null; if (fileMode != "external" && fileId != null && fileUrl != null) { Console.WriteLine("Message attachment found with ID " + fileId); attachmentsToUpload.Add(new Models.Combined.AttachmentsMapping { attachmentId = fileId, attachmentUrl = fileUrl, attachmentChannelId = channelsMapping.slackChannelId, attachmentFileName = fileName, msChannelName = channelsMapping.displayName }); // map the attachment to fileAttachment which is used in the viewmodel fileAttachment = new ViewModels.SimpleMessage.FileAttachment { id = fileId, originalName = (string)obj.SelectToken("file.name"), originalTitle = (string)obj.SelectToken("file.title"), originalUrl = (string)obj.SelectToken("file.permalink") }; } // deal with "attachments" that aren't files List <ViewModels.SimpleMessage.Attachments> attachmentsList = new List <ViewModels.SimpleMessage.Attachments>(); List <ViewModels.SimpleMessage.Attachments.Fields> fieldsList = new List <ViewModels.SimpleMessage.Attachments.Fields>(); var attachmentsObject = (JArray)obj.SelectToken("attachments"); if (attachmentsObject != null) { foreach (var attachmentItem in attachmentsObject) { var attachmentText = (string)attachmentItem.SelectToken("text"); var attachmentTextFallback = (string)attachmentItem.SelectToken("fallback"); var attachmentItemToAdd = new ViewModels.SimpleMessage.Attachments(); if (!String.IsNullOrEmpty(attachmentText)) { attachmentItemToAdd.text = attachmentText; } else if (!String.IsNullOrEmpty(attachmentTextFallback)) { attachmentItemToAdd.text = attachmentTextFallback; } var attachmentServiceName = (string)attachmentItem.SelectToken("service_name"); if (!String.IsNullOrEmpty(attachmentServiceName)) { attachmentItemToAdd.service_name = attachmentServiceName; } var attachmentFromUrl = (string)attachmentItem.SelectToken("from_url"); if (!String.IsNullOrEmpty(attachmentFromUrl)) { attachmentItemToAdd.url = attachmentFromUrl; } var attachmentColor = (string)attachmentItem.SelectToken("color"); if (!String.IsNullOrEmpty(attachmentColor)) { attachmentItemToAdd.color = attachmentColor; } var fieldsObject = (JArray)attachmentItem.SelectToken("fields"); if (fieldsObject != null) { fieldsList.Clear(); foreach (var fieldItem in fieldsObject) { fieldsList.Add(new ViewModels.SimpleMessage.Attachments.Fields() { title = (string)fieldItem.SelectToken("title"), value = (string)fieldItem.SelectToken("value"), shortWidth = (bool)fieldItem.SelectToken("short") }); } attachmentItemToAdd.fields = fieldsList; } else { attachmentItemToAdd.fields = null; } attachmentsList.Add(attachmentItemToAdd); } } else { attachmentsList = null; } ViewModels.SimpleMessage message = new ViewModels.SimpleMessage { id = messageId, text = messageText, ts = messageTs.ToLocalTime().ToString(), user = messageSender, fileAttachment = fileAttachment, attachments = attachmentsList, }; JObject messageJson = new JObject { { "rootMessage", new JObject { { "body", new JObject { { "contentType", 1 }, // 1 is for html content, 0 is for text { "content", MessageToHtml(message, channelsMapping) } } } } } // You need the extra brackets so you're not directly adding a JValue }; var response = Helpers.httpClient.PostAsync(O365.MsGraphBetaEndpoint + "teams/" + selectedTeamId + "/channels/" + channelsMapping.id + "/chatthreads", new StringContent(JsonConvert.SerializeObject(messageJson), Encoding.UTF8, "application/json")).Result; if (!response.IsSuccessStatusCode) { Console.WriteLine("Unexpected status code of " + response.StatusCode + " returned when importing messages."); } messageList.Add(message); } } } } if (copyFileAttachments) { Utils.FileAttachments.ArchiveMessageFileAttachments(aadAccessToken, selectedTeamId, attachmentsToUpload, "fileattachments").Wait(); foreach (var messageItem in messageList) { if (messageItem.fileAttachment != null) { var messageItemWithFileAttachment = attachmentsToUpload.Find(w => String.Equals(messageItem.fileAttachment.id, w.attachmentId, StringComparison.CurrentCultureIgnoreCase)); if (messageItemWithFileAttachment != null) { messageItem.fileAttachment.spoId = messageItemWithFileAttachment.msSpoId; messageItem.fileAttachment.spoUrl = messageItemWithFileAttachment.msSpoUrl; } } } } if (uploadHTMLAndJSONFiles) { Utils.Messages.CreateSlackMessageJsonArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); Utils.Messages.CreateSlackMessageHtmlArchiveFile(basePath, channelsMapping, messageList, aadAccessToken, selectedTeamId); } }