Difference between revisions of "AddWatchersOnCorrespond"

From Request Tracker Wiki
Jump to navigation Jump to search
m (5 revisions imported)
(BUGFIX: add CreatorEmailAddr as lower case. Check for bounce addresses lower case.)
 
(9 intermediate revisions by 3 users not shown)
Line 5: Line 5:
In our RT setup, we have a group named '''general''' which contains all admins for our site. If the user this [[Scrip]] is going to add as a [[Watcher]] to the ticket is also a member of the '''general''' group, then the Scrip will add them as an [[AdminCC]] [[Watcher]] instead of a [[CC]] [[Watcher]]. It should be fairly easy for others who do not need this feature to remove or modify this [[Scrip]] accordingly.
In our RT setup, we have a group named '''general''' which contains all admins for our site. If the user this [[Scrip]] is going to add as a [[Watcher]] to the ticket is also a member of the '''general''' group, then the Scrip will add them as an [[AdminCC]] [[Watcher]] instead of a [[CC]] [[Watcher]]. It should be fairly easy for others who do not need this feature to remove or modify this [[Scrip]] accordingly.


I wrote this [[Scrip]] to replace the patch we used to make to the RT Email Interface code, called [[ParseFollowupMessageForTicketCcs]]. Accordingly, it carries the same security warning about allowing basically '''anyone''' to add themselves to '''any''' ticket, simply by sending an appropriately formatted email. Some RT sites might not want this behavior, but it is necessary for us.
I wrote this [[Scrip]] to replace the patch we used to make to the RT Email Interface code, called [[ParseFollowupMessageForTicketCcs]]. Accordingly, it carries the same security warning about allowing basically '''anyone''' to add themselves to '''any''' ticket, simply by sending an appropriately formatted email. Some RT sites might not want this behaviour, but it is necessary for us.
 
* [[AddWatchersOnCorrespondDomains]] is a modified version of this Scrip which is more restrictive by domain.


Changelog
Changelog


2012-01-01: Fixed a bug in which the owner of the ticket would have been added as a Cc: for every reply he made. (HaimDimer)
2012-01-01: Fixed a bug in which the owner of the ticket would have been added as a Cc: for every reply he made. (HaimDimer)
2022-12-06: Populate RealName from email header when creating new user. (RT inserts the user's email address in the "phrase" part of the From: header if RealName is empty. This fix prevents emails bouncing because a mail provider's anti-spoofing policy rejects mail with an email address in the "phrase" section of From:/Cc: headers. )
2023-05-03: (RobL) BUGFIX: add CreatorEmailAddr as lower case. Check for bounce addresses lower case.


----
----
Line 30: Line 36:


----
----
<pre>
# Get some info:
my $scrip = 'Scrip:AddWatchersOnCorrespond';
my $Transaction = $self->TransactionObj;
my $EmailAddr = $self->TransactionObj->CreatorObj->EmailAddress;
my $Queue = $self->TicketObj->QueueObj;
my $Ticket = $self->TicketObj;
my $Id = $self->TicketObj->id;
# Extract a list of people associated with this transaction:
#  - including the transaction creator, and if it is an email, the sender and recipients of that email
my %People;
my $CreatorEmailAddr = lc $self->TransactionObj->CreatorObj->EmailAddress;
my $CreatorRealName  = $self->TransactionObj->CreatorObj->RealName;
$People{$CreatorEmailAddr}{RealName} = GetFullName($CreatorEmailAddr,$CreatorRealName);


# Get some info:
foreach my $h (qw(From To Cc)) {
my $scrip = 'Scrip:AddWatchersOnCorrespond';
    my $header = $Transaction->Attachments->First->GetHeader($h);
my $Transaction = $self->TransactionObj;
    my @addr = Mail::Address->parse($header);
my $EmailAddr = $self->TransactionObj->CreatorObj->EmailAddress;
    foreach my $addrobj (@addr) {
my $Queue = $self->TicketObj->QueueObj;
        my $addr = lc $RT::Nobody->UserObj->CanonicalizeEmailAddress($addrobj->address);
my $Ticket = $self->TicketObj;
        # Ignore the specific addresses for this queue:
my $Id = $self->TicketObj->id;
        next if lc $Queue->CorrespondAddress eq $addr;
        next if lc $Queue->CommentAddress eq $addr;
        # Ignore any email address that looks like one for ANY of our queues:
        next if RT::EmailParser->IsRTAddress($addr);
   
   
# Extract a list of people associated with this transaction:
        # Get phrase from address header for full name: "Fred Bloggs" <fred@example.com>
# - including the transaction creator, and if it is an email, the sender and recipients of that email message.
        # (Extract Fred Bloggs to fullname.) Clean up. If nothing suitable then make something from email.
my @People = ($EmailAddr);
        my $fullname = GetFullName($addr,$addrobj->phrase);
foreach my $h (qw(From To Cc)) {
        $People{$addr}{RealName} = $fullname;
    my $header = $Transaction->Attachments->First->GetHeader($h);
        $RT::Logger->debug("$scrip: Ticket #$Id correspondence contains header - $h: $addr $fullname");
    my @addr = Mail::Address->parse($header);
    }
    foreach my $addrobj (@addr) {
}
my $addr = lc $RT::Nobody->UserObj->CanonicalizeEmailAddress($addrobj->address);
 
# Ignore the specific addresses for this queue:
# Lookup the 'experts' (general) group to use below:
next if lc $Queue->CorrespondAddress eq $addr;
my $Experts = RT::Group->new($self->CurrentUser);
next if lc $Queue->CommentAddress eq $addr;
$Experts->LoadUserDefinedGroup('general');
# Ignore any email address that looks like one for ANY of our queues:
 
next if RT::EmailParser->IsRTAddress($addr);
# Now check if each user is already watching the ticket or queue:
$RT::Logger->debug("$scrip: Ticket #$Id correspondence contains header - $h: $addr");
foreach my $addr (sort keys %People) {
push @People, $addr;
 
    next if ($addr =~ /^(postmaster|root|mailer-daemon)\@/);
 
    my $User = RT::User->new( $RT::SystemUser );
    $User->LoadOrCreateByEmail(
        RealName    => $People{$addr}{RealName},
        EmailAddress => $addr,
        Comments    => "Autocreated by $scrip",
    );
 
    my $Name = $User->Name;
    my $Principal = $User->PrincipalId;
    if ( not ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
            $Queue->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'Requestor', PrincipalId => $Principal) or
            $Ticket->IsOwner($User) )) {
        # If the user is a member of the experts group, then add them as an AdminCc, otherwise as a Cc:
        my $type = 'Cc';
        $type = 'AdminCc' if $Experts->HasMember($User->PrincipalObj);
        # Add the new watcher now and check for errors:
        my ($ret, $msg) = $Ticket->AddWatcher(Type  => $type, PrincipalId => $Principal);
        if ($ret) {
            $RT::Logger->info("$scrip: New $type watcher added to ticket #$Id: $addr $fullname (#$Principal)");
        } else {
            $RT::Logger->error("$scrip: Failed to add new $type watcher to ticket #$Id: $addr (#$Principal) - $msg");
        }
    }
}
 
sub GetFullName {
 
    # Get a nicely formatted name for RT RealName Field:
 
    # If fullname is blank, make something up from the
    # local_part of the email address: "fred.bloggs@..." -> "Fred Bloggs".
    # Do not allow fullname to contain "@".
 
    my ($addr,$fullname) = @_;
    my ($local_part,$domain) = split('@', $addr);
 
    if (($fullname eq '') || ($fullname =~ /\@/)) {
      $fullname = $local_part;
      $fullname =~ s/[\._-]/ /g;
      $fullname =~ s/(\w+)/\u$1/g;
     }
     }
}
 
    $fullname =~ s/^[\"\']|[\"\']$//g; # strip leading/trailing " or '
# Lookup the 'experts' (general) group to use below:
     $fullname =~ s/^\s+|\s+$//g; # strip leading/trailing spaces
my $Experts = RT::Group->new($self->CurrentUser);
 
$Experts->LoadUserDefinedGroup('general');
    return $fullname;
}
# Now check if each user is already watching the ticket or queue:
 
foreach my $addr (@People) {
return 1;
    my $User = RT::User->new($RT::SystemUser);
</pre>
    $User->LoadOrCreateByEmail($addr);
 
    my $Name = $User->Name;
     my $Principal = $User->PrincipalId;
    if ( not ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
    $Queue->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
    $Ticket->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
    $Ticket->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
    $Ticket->IsWatcher(Type => 'Requestor', PrincipalId => $Principal) or
    $Ticket->IsOwner($User) )) {
# If the user is a member of the experts group, then add them as an AdminCc, otherwise as a normal Cc:
my $type = 'Cc';
$type = 'AdminCc' if $Experts->HasMember($User->PrincipalObj);
# Add the new watcher now and check for errors:
my ($ret, $msg) = $Ticket->AddWatcher(Type  => $type, PrincipalId => $Principal);
if ($ret) {
    $RT::Logger->info("$scrip: New $type watcher added to ticket #$Id: $addr (#$Principal)");
} else {
    $RT::Logger->error("$scrip: Failed to add new $type watcher to ticket #$Id: $addr (#$Principal) - $msg");
}
    }
}
return 1;
  # vim:ft=perl:
  # vim:ft=perl:


----
----
[[Category:RT Action]]

Latest revision as of 07:47, 3 May 2023

AddWatchersOnCorrespond

This RT Scrip will add the person making the correspondence as a Watcher to the ticket if they are not already a Watcher, also, if the transaction originated from an email message, the script will scan the email headers and add all other recipients to the ticket as Watchers (if they are not yet Watchers). This can be used to complement the ParseNewMessageForTicketCcs SiteConfig option, which is part of the EmailInterface and does the same thing when tickets are created.

In our RT setup, we have a group named general which contains all admins for our site. If the user this Scrip is going to add as a Watcher to the ticket is also a member of the general group, then the Scrip will add them as an AdminCC Watcher instead of a CC Watcher. It should be fairly easy for others who do not need this feature to remove or modify this Scrip accordingly.

I wrote this Scrip to replace the patch we used to make to the RT Email Interface code, called ParseFollowupMessageForTicketCcs. Accordingly, it carries the same security warning about allowing basically anyone to add themselves to any ticket, simply by sending an appropriately formatted email. Some RT sites might not want this behaviour, but it is necessary for us.

Changelog

2012-01-01: Fixed a bug in which the owner of the ticket would have been added as a Cc: for every reply he made. (HaimDimer)

2022-12-06: Populate RealName from email header when creating new user. (RT inserts the user's email address in the "phrase" part of the From: header if RealName is empty. This fix prevents emails bouncing because a mail provider's anti-spoofing policy rejects mail with an email address in the "phrase" section of From:/Cc: headers. )

2023-05-03: (RobL) BUGFIX: add CreatorEmailAddr as lower case. Check for bounce addresses lower case.


Description: AddWatchersOnCorrespond

Condition: On Correspond

Action: User Defined

Template: Global template: Blank

Stage: TransactionBatch

Custom condition:

Custom action preparation code: return 1;

Custom action cleanup code:


# Get some info:
my $scrip = 'Scrip:AddWatchersOnCorrespond';
my $Transaction = $self->TransactionObj;
my $EmailAddr = $self->TransactionObj->CreatorObj->EmailAddress;
my $Queue = $self->TicketObj->QueueObj;
my $Ticket = $self->TicketObj;
my $Id = $self->TicketObj->id;

# Extract a list of people associated with this transaction:
#  - including the transaction creator, and if it is an email, the sender and recipients of that email
my %People;
my $CreatorEmailAddr = lc $self->TransactionObj->CreatorObj->EmailAddress;
my $CreatorRealName  = $self->TransactionObj->CreatorObj->RealName;
$People{$CreatorEmailAddr}{RealName} = GetFullName($CreatorEmailAddr,$CreatorRealName);

foreach my $h (qw(From To Cc)) {
    my $header = $Transaction->Attachments->First->GetHeader($h);
    my @addr = Mail::Address->parse($header);
    foreach my $addrobj (@addr) {
        my $addr = lc $RT::Nobody->UserObj->CanonicalizeEmailAddress($addrobj->address);
        # Ignore the specific addresses for this queue:
        next if lc $Queue->CorrespondAddress eq $addr;
        next if lc $Queue->CommentAddress eq $addr;
        # Ignore any email address that looks like one for ANY of our queues:
        next if RT::EmailParser->IsRTAddress($addr);
 
        # Get phrase from address header for full name: "Fred Bloggs" <fred@example.com> 
        # (Extract Fred Bloggs to fullname.) Clean up. If nothing suitable then make something from email.
        my $fullname = GetFullName($addr,$addrobj->phrase);
        $People{$addr}{RealName} = $fullname;
        $RT::Logger->debug("$scrip: Ticket #$Id correspondence contains header - $h: $addr $fullname");
    }
}

# Lookup the 'experts' (general) group to use below:
my $Experts = RT::Group->new($self->CurrentUser);
$Experts->LoadUserDefinedGroup('general');

# Now check if each user is already watching the ticket or queue:
foreach my $addr (sort keys %People) {

    next if ($addr =~ /^(postmaster|root|mailer-daemon)\@/);

    my $User = RT::User->new( $RT::SystemUser );
    $User->LoadOrCreateByEmail(
        RealName     => $People{$addr}{RealName},
        EmailAddress => $addr,
        Comments     => "Autocreated by $scrip",
    );

    my $Name = $User->Name;
    my $Principal = $User->PrincipalId;
    if ( not ($Queue->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
            $Queue->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'Cc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'AdminCc', PrincipalId => $Principal) or
            $Ticket->IsWatcher(Type => 'Requestor', PrincipalId => $Principal) or
            $Ticket->IsOwner($User) )) {
        # If the user is a member of the experts group, then add them as an AdminCc, otherwise as a Cc:
        my $type = 'Cc';
        $type = 'AdminCc' if $Experts->HasMember($User->PrincipalObj);
        # Add the new watcher now and check for errors:
        my ($ret, $msg) = $Ticket->AddWatcher(Type  => $type, PrincipalId => $Principal);
        if ($ret) {
            $RT::Logger->info("$scrip: New $type watcher added to ticket #$Id: $addr $fullname (#$Principal)");
        } else {
            $RT::Logger->error("$scrip: Failed to add new $type watcher to ticket #$Id: $addr (#$Principal) - $msg");
        }
    }
}

sub GetFullName {

    # Get a nicely formatted name for RT RealName Field:

    # If fullname is blank, make something up from the
    # local_part of the email address: "fred.bloggs@..." -> "Fred Bloggs".
    # Do not allow fullname to contain "@".

    my ($addr,$fullname) = @_;
    my ($local_part,$domain) = split('@', $addr);

     if (($fullname eq '') || ($fullname =~ /\@/)) {
      $fullname = $local_part;
      $fullname =~ s/[\._-]/ /g;
      $fullname =~ s/(\w+)/\u$1/g;
     }

     $fullname =~ s/^[\"\']|[\"\']$//g; # strip leading/trailing " or '
     $fullname =~ s/^\s+|\s+$//g; # strip leading/trailing spaces

    return $fullname;
}

return 1;
# vim:ft=perl: