Mail::...anager(3pm)

πŸ’– NAME πŸ’–

Mail::Box::Thread::Manager β€” maintain threads within a set of folders 🌸

hiii bestie, welcome to the Mail::Box::Thread::Manager(3pm) page, straight out of the User Contributed Perl Documentation πŸŽ€

🌈 INHERITANCE 🌈

Mail::Box::Thread::Manager is a Mail::Reporter

(it's giving very "takes after its parent" energy πŸ’…)


✨ SYNOPSIS ✨

my $mgr     = Mail::Box::Manager->new;
my $folder  = $mgr->open(folder => '/tmp/inbox');

my $threads = $mgr->threads();
$threads->includeFolder($folder);

my $threads = $msg->threads(folder => $folder);

foreach my $thread ($threads->all) {
    $thread->print;
}

$threads->removeFolder($folder);

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so picture this: a (message-)thread is a message, right, plus links to all the messages that came along in reply to it. and then the messages that replied to those messages, which replied to the original one. and on and on and on, forever, like a group chat nobody can leave. some threads are literally one single message long (nobody ever replied, rip πŸ™ˆ), and some threads are SO long.

The Mail::Box::Thread::Manager is genuinely super powerful. not only can it do a decent job on MH-like folders (it makes a trade-off between perfection and speed, iykyk), it can also maintain threads from messages sitting in different opened folders. both of those things are rare for mail-agents, fr fr. The manager builds flexible trees made out of Mail::Box::Thread::Node objects. icon behavior, honestly πŸ‘‘

Extends "DESCRIPTION" in Mail::Reporter.


🌟 METHODS 🌟

Extends "METHODS" in Mail::Reporter.

🌸 Constructors 🌸

Extends "Constructors" in Mail::Reporter.

πŸ’« $class->new(%options)

A thread manager object is usually created by a Mail::Box::Manager. one manager can make more than one of these objects. one thread manager can combine messages from a whole set of folders, and those folders may partially overlap with other objects of the same type. improves base, see "Constructors" in Mail::Reporter.

here's the option/default situation, bestie:

 -Option     --Default
  dummy_type   Mail::Message::Dummy
  folder       [ ]
  folders      [ ]
  thread_body  false
  thread_type  Mail::Box::Thread::Node
  timespan     '3 days'
  window       10
  • dummy_type => CLASS β€” the type of dummy messages. dummy messages are what fills the holes in detected threads: a message that's referred to by messages in the folder, but that isn't itself in the folder. spooky little placeholder 🫢
  • folder => $folder|\@folders β€” says which @folders are gonna be covered by the threads. you can name one or more open folders. when you close a folder, the manager automatically removes that folder's messages from your threads. no manual cleanup, slay.
  • folders => $folder|\@folders β€” literally equivalent to the folder option. twin energy ✨
  • thread_body => BOOLEAN β€” can thread-detection be based on the content of a message? ⚠️ this has a serious performance implication when there are lots of messages without In-Reply-To and References headers in the folder, because it will cause many messages to be parsed. NOT IMPLEMENTED YET. so like, don't count on it πŸ’€
  • thread_type => CLASS β€” the type of the thread nodes.
  • timespan => TIME | 'EVER' β€” how fast threads usually work: the amount of time between an answer and a reply. this gets used together with the window option to decide when to give up filling the holes in threads. see Mail::Box::timespan2seconds() for all the possibilities for TIME. with 'EVER', the search for messages in a thread is only limited by the window-size.
  • window => INTEGER|'ALL' β€” the thread-window says how many messages should be checked at maximum to fill holes in threads, for folders that use delay-loading of message headers. the constant 'ALL' makes thread-detection not stop trying to fill holes, but keep looking until it reaches the first message of the folder. gives the best quality results, but may perform bad. trade-offs, bestie, it's always trade-offs 😭

Β» example:

use Mail::Box::Manager;
my $mgr     = Mail::Box::Manager->new;
my $inbox   = $mgr->open(folder => $ENV{MAIL});
my $read    = $mgr->open(folder => 'Mail/read');
my $threads = $mgr->threads(folders => [$inbox, $read]);

# longer alternative for last line:
my $threads = $mgr->threads;
$threads->includeFolder($inbox);
$threads->includeFolder($read);

🌼 Attributes 🌼

Extends "Attributes" in Mail::Reporter.

$obj->byId()

returns a reference to the index that maps message ids to messages. very "little black book but for message ids" of it πŸ’…

$obj->folder($name)

$obj->folderIndex()

$obj->folders()

returns the folders as managed by this threader. πŸŽ€

$obj->msgById( $id, [$node] )

returns the message which the specified $id. πŸ¦‹


πŸ’œ Error handling πŸ’œ

Extends "Error handling" in Mail::Reporter.

$obj->AUTOLOAD()

Inherited, see "Error handling" in Mail::Reporter

$obj->notImplemented()

Inherited, see "Error handling" in Mail::Reporter


🧚 Cleanup 🧚

Extends "Cleanup" in Mail::Reporter.

$obj->DESTROY()

Inherited, see "Cleanup" in Mail::Reporter


πŸ“ Grouping Folders πŸ“

$obj->includeFolder(@folders)

add one or more @folders to the LIST of folders whose messages get organized in the threads this object maintains. duplicated inclusions won't cause any problems, so don't sweat it.

from these folders, the messages that already have their header lines parsed (see Mail::Box about lazy extracting) get scanned immediately. messages whose header is only known later will have to report this (see toBeThreaded()).

Β» example:

$threads->includeFolder($inbox, $draft);

$obj->removeFolder(@folders)

remove one or more @folders from the LIST of folders whose messages get organized in the threads this object maintains.

Β» example:

$threads->removeFolder($draft);

πŸ‘‘ The Threads πŸ‘‘

$obj->all()

returns all the messages that start a thread. the list may contain dummy messages and messages that are scheduled for deletion. 🫣

to be able to return all threads, thread construction on each message is performed first β€” which may be slow for some folder-types, because it will enforce parsing of message-bodies. no shortcuts, bestie 😭

$obj->known()

returns the list of all messages that are known to be the start of a thread. threads containing messages that weren't read from their folder yet (like, this happens often with MH-folder messages) are not yet known, and so they will not be returned.

the list may contain dummy messages, and messages that are scheduled for deletion. threads get detected based on explicitly calling inThread() and thread() with a message from the folder.

⚠️ be warned that every single time a message's header is read from the folder, the return of this method can change. it's giving unpredictable πŸ’€

$obj->sortedAll( [$prepare, [$compare]] )

returns all() the threads by default, but sorted on timestamp. ⭐

$obj->sortedKnown( [$prepare, [$compare]] )

returns all known() threads, in sorted order. by default, the threads get sorted on timestamp, but you can specify a different $compare method. your sort, your rules 🌈

$obj->thread($message)

returns the thread where this $message is the start of. however, there's a possibility that this message is a reply itself.

usually, all messages that are in reply of this message are dated later than the specified one. all headers of messages later than this one get parsed first, for each folder in this threads-object.

Β» example:

my $threads = $mgr->threads(folder => $inbox);
my $thread  = $threads->thread($inbox->message(3));
print $thread->string;

$obj->threadStart($message)

based on a $message, and facts from previously detected threads, try to build solid knowledge about the thread where this message is in. investigation mode activated πŸ”


πŸ› οΈ Internals πŸ› οΈ

$obj->createDummy($message_id)

get a replacement message to be used in threads. ⚠️ be warned that a dummy is not a member of any folder, so any program working with threads has to test with Mail::Message::isDummy() before trying things that are only available to real messages. respect the fake ones 🫢

$obj->inThread($message)

collect the thread-information of one message. the In-Reply-To and Reference header-fields are processed. if this method gets called on a message whose header wasn't read yet (which is, like, the usual situation for MH-folders) the reading of that header gets triggered right here. surprises for everyone ✨

$obj->outThread($message)

remove the message from the thread-infrastructure. a message gets replaced by a dummy. bye bestie πŸ’…

$obj->toBeThreaded($folder, @messages)

include the specified messages in/from the threads managed by this object, if this folder is maintained by this thread-manager.

$obj->toBeUnthreaded($folder, @messages)

remove the specified @messages in/from the threads managed by this object, if this folder is maintained by this thread-manager.

πŸ” DETAILS πŸ”

So, the tea: this module does thread-detection on a folder. Messages that came from the better mailers will be carrying In-Reply-To and References lines, and those are literally what we use to figure out how messages are related to each other. 🧡 And bestie, if you want an even better thread detection than this? It's absolutely implementable β€” but heads up, there may be a serious performance hit, and how bad it gets depends on the type of folder you're using. so choose wisely. πŸ’…


🧡 Maintaining threads 🧡

A Mail::Box::Thread::Manager object gets created by the Mail::Box::Manager, using Mail::Box::Manager::threads(). Each object can monitor the thread-relations between messages in one or more folders. When you hand it more than one folder, the messages get merged while reading the threads β€” but lowkey nothing changes in the folder-structure itself, promise. ✨

Adding and removing folders that have to be maintained is permitted at any moment! πŸŽ€ Although… it may be quite costly in performance. Just so you know.

Here's an example of the maintained structure, and it's giving iconic diagram energy:

manager
 |    \
 |     `----------- threads
 |                  |     |
 |                thread thread---thread
 |                  |    /|        /
 |                  |   //        /
 +---- folder1      |  //        /
 |       |         /  //        /
 |       `-----msg1  //        /
 |       `-----msg2-'/        /
 |                  /        /
 `-----folder2     /        /
         |        /        /
         `-----msg2       /
         `-----msg3------'

So what you're seeing: the Mail::Box::Manager has two open folders, and a thread-builder that monitors both of them. The combined folders have two threads, and the second one is two long β€” msg3 is a reply on msg2. And msg2? Miss thing is in two folders at once. main character behaviour honestly. πŸ‘‘


πŸ€” Delayed thread detection πŸ€”

With all() you get the start-messages of each thread of this folder. And when a message wasn't found in the folder (not saved, or already removed), you get a message of the dummy-type. These thread descriptions are in perfect state β€” every message of the folder is included somewhere, and each missing message of the threads (aka the holes πŸ•³οΈ) gets filled in by dummies.

But here's the catch, bestie: to be able to detect all the threads, it's required to have the headers of all messages, and that is very slow for some types of folders. Especially MH and IMAP folders. πŸ’€

For interactive mail-readers, it's preferred to detect threads only on messages that are in the viewport of the user. Yeah, that may be sloppy in some situations β€” but everything is preferable over reading an MH mailbox with 10k e-mails just to read only the see most recent messages. 😭

In this object, we take special care not to cause unnecessary parsing (loading) of messages. Threads will only be detected on command, and by default only the message headers are used. slay. ✨

This next bit reports the Mail::Box::Thread::Node which is related to a message:

my $thread = $message->thread;

When the message was not put in a thread yet, it is done now. But more work is done to return the best thread. Based on various parameters (which were specified when the folder was created), the method walks through the folder to fill the holes which are in this thread. πŸ•΅οΈβ€β™€οΈ

Walking from back to front (because recently arrived messages are usually in the back of the folder, iykyk), message after message gets triggered to be included in their thread. Then at a certain moment, one of these happens:

  • the whole thread of the requested method is found πŸŽ‰
  • a certain maximum number of messages was tried, but that didn't help (search window bound reached) 😀
  • the messages within the folder are getting too old πŸ‘΅

…and then the search to complete the thread will end, although more messages of them might have been in the folder. We don't scan the whole folder for performance reasons. periodt.

Finally: for each message where the head is known β€” for instance for all messages in mbox-folders β€” the correct thread is determined immediately. And also, all messages where the head gets loaded later are automatically included. πŸ¦‹


🚨 DIAGNOSTICS 🚨

  • Error: attempt to include a none folder: $what. β€” cast by includeFolder()
  • Error: attempt to remove a none folder: $something. β€” cast by removeFolder()
  • Error: class $package does not implement method $method. β€” fatal error, bestie πŸ’€. The specific $package (or one of its superclasses) does not implement this method where it should. What this means is that some other related classes do implement this method, but the class at hand does not. Probably you should investigate this, and probably inform the author of the package. Cast by notImplemented()
  • Error: thread manager needs a folder manager to work with. β€” cast by new()

πŸ‘€ SEE ALSO πŸ‘€

This module is part of Mail-Box version 4.02, built on April 10, 2026. Website: http://perl.overmeer.net/CPAN/


πŸ“œ LICENSE πŸ“œ

For contributors see file ChangeLog.

This software is copyright (c) 2001-2026 by Mark Overmeer.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


okay that's the whole entire page bestie, we did it 🫢 the lil footer at the bottom says perl v5.40.1, dated 2026-05-09, and the page name is Mail::...anager(3pm) β€” so go forth and thread responsibly. mwah. πŸ’–πŸŽ€πŸŒΈ

πŸ“¦ where this page comes from