I've made this kind of stuff in an outlook add-in
There is no 100% way to make it ...
The problem is that the conversation ID is not always kept by other software. So you need to use a set of data to link email to their answers:
- Message-ID: this is available in the email headers. Sent items doesn't have this :/
- In-Reply-To: this is also in the email headers
- Topic ID
For the topic ID, I retrieve values in this order (I take the first available):
- MailItem.ConversationIndex: Each reply add bytes to the conversation index
- Reference header
Then, I link email to their reply using topicID, email have the same first X characters than reply. Example original email topic id = abc, reply = abcdef For all mails than cannot be linked using conversation id, I try to link using Message-ID & In-Reply-To ID
Problem will particularly comes from email sent by outlook (no Message-ID) then user reply without Reference/ConversationIndex header... you'll not have any way to link both mail together.
Hope it helps
EDIT: Here's some code. I've copy/pasted code from different class/method to create a single method, so it may not compile. Take it more as a pseudo-code.
public SimpleTree<MailData> CreateTree(List<MailData> mails)
{
mails.Sort((m1, m2) => m1.TopicId == m2.TopicId ? m2.CreationDate.CompareTo(m1.CreationDate) : m1.TopicId.CompareTo(m2.TopicId));
var tree = new SimpleTree<MailData>();
var i = 0;
while (i < mails.Count)
{
var node = tree.Children.Add(mails[i]);
var topicId = mails[i].TopicId;
var start = i + 1;
while (start < mails.Count
&& !string.IsNullOrEmpty(topicId)
&& !string.IsNullOrEmpty(mails[start].TopicId)
&& mails[start].TopicId.StartsWith(topicId))
{
node.Children.Add(mails[start]);
start++;
}
i = start;
}
// Handle email where TopicId are different, but ParentId is filled with correct value
for (int j = tree.Children.Count - 1; j >= 0; j--)
{
var child = tree.Children[j];
if (child.Children.Count == 0 && !string.IsNullOrEmpty(child.Value.ParentId))
{
var parentNode = tree.FindNode(s => s != null && s.MessageId == child.Value.ParentId);
if (parentNode != null && parentNode != child)
parentNode.Children.Add(child);
}
}
return tree;
}
MailData is a class with the 3 fields needed as explained before:
- MessageID (from Message-ID header)
- ParentId (from In-Reply-To header)
- TopicId (from ConversationIndex or Reference header)
SimpleTree<> is a class to create tree, it's in fact a node with children. Nothing special or related to email here. The .Value property refer to the data associated to the node (MailData here)
The goal is to sort on the TopicId so that we can construct the tree in 1 loop Then I check all the mail in the tree root to check if we can move them under another mail usine MessageId/ParentId
Just remember that it create a one-level tree, something like:
- Mail A
- Reply AA
- Reply AAA
- Mail B
- Reply BB
- Reply BBB
But you would need something like this:
- Mail A
- Reply AA
- Reply AAA
- Reply AA
- Mail B
- Reply BB
- Reply BBB
- Reply BB