December 2009
November 2009
October 2009
September 2009
June 2009
April 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
July 2008
June 2008
October 2007
September 2007
The management of my current place of business wants to have high priority tickets updated on a regular basis. RT doesn't have anything built in to accomplish this, which is where the RT API comes in handy. Below is my script, which is based on Tim Bishop's rt-escalate. It will send email to a set of addresses whenever a ticket of a given priority has not been updated within a specific threshold.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | #!/usr/bin/env perl my $criticalPriority = 3; # Minimum priority to check my $criticalOffset = 2 * 60 * 60; # Time since update my $fromAddr = 'root@example.com'; # Who mail should appear to come from my $trackpath = "/tmp"; # Where to place tracking files my @toAddrs = (); # You shouldn't change this. Use command-line arguments. my @queues; # You shouldn't change this. Use command-line arguments. my $is_test = 0; my $show_help = 0; # Location of RT3's libs use lib ("/opt/rt3/lib", "/opt/rt3/local/lib"); use strict; use warnings; use Getopt::Long; GetOptions( 'queue=s' => \@queues, 'to=s' => \@toAddrs, 'threshold=i' => \$criticalOffset, 'priority=i' => \$criticalPriority, 'test' => \$is_test, 'help' => \$show_help, ); if ($show_help){ print "\n$0 --threshold seconds --priority number [options]... --threshold # Number of seconds before next update --priority # Priority to check --to # Who should get mailed, you can specify multiple --queue queuename # Searches all by default, you can specify multiple --test # Don't send mail, just print message --help # Show this screen \n"; exit 1; } # Pull in the RT stuff package RT; use RT::Interface::CLI qw(CleanEnv); use Mail::Mailer; use POSIX qw(floor); use Time::Piece; # Clean our the environment CleanEnv(); # Load the RT configuration RT::LoadConfig(); # Initialise RT RT::Init(); # If no queues given, get all enabled queues if(!@queues) { my $queues = new RT::Queues($RT::SystemUser); $queues->LimitToEnabled(); foreach my $queue (@{$queues->ItemsArrayRef()}) { push @queues, $queue->Name; } } #Load tracking hash for repeat notification avoidance my %th; # tracking hash my @idlist; # used to strip tickets that are closed my $trackfile = "$trackpath/NotifyTimed.$criticalPriority"; open(FH, "<$trackfile"); while(<FH>){ my @parts = split; $th{$parts[0]} = $parts[1]; } close(FH); foreach my $queuename (@queues) { my $queue = new RT::Queue($RT::SystemUser); $queue->Load($queuename); # Get hold of new, open, and stalled tickets only my $tickets = new RT::Tickets($RT::SystemUser); $tickets->LimitStatus(VALUE => 'open'); $tickets->LimitStatus(VALUE => 'new'); $tickets->LimitStatus(VALUE => 'stalled'); $tickets->LimitPriority(OPERATOR => '=', VALUE => $criticalPriority); $tickets->LimitQueue(VALUE => $queue->Id); while (my $ticket = $tickets->Next) { my $ticketdt = Time::Piece->strptime($ticket->LastUpdated, "%Y-%m-%d %H:%M:%S"); my $now = localtime; my $dt = $now - $criticalOffset; my $will_notify = 0; if ($dt->epoch > $ticketdt->epoch){ my $minutes = floor(($now->epoch - $ticketdt->epoch) / 60); my $owner = $ticket->OwnerObj; # Keep track of ids to strip old ones push(@idlist, $ticket->Id); # Only notify once if (exists $th{$ticket->Id}){ if ($th{$ticket->Id} < $ticketdt->epoch){ $th{$ticket->Id} = $ticketdt->epoch; $will_notify = 1; } } else { $th{$ticket->Id} = $ticketdt->epoch; $will_notify = 1; } # Prepare report my $mailmsg = '<html> <body> <p>Ticket <a href="https://rt.wbsconnect.com/Ticket/Display.html?id=' .$ticket->Id.'">'.$ticket->Id.'</a> needs to be updated.</p> <p>Subject: '.$ticket->Subject.'</p> <p>Owner: '.$owner->RealName.'</p> <p>Status: '.$ticket->Status.'</p> <p>Priority: '.$ticket->Priority.'</p> <p>Minutes since last update: '.$minutes.'</p> </body> </html>'; # Send email, if this isn't a repeat or test next if not $will_notify; if ($is_test){ print $mailmsg; } else { foreach (@toAddrs){ my $mailer = Mail::Mailer->new; $mailer->open({ 'From' => $fromAddr, 'To' => $_, 'Subject' => "Update Required: Priority " .$ticket->Priority." ticket " .$ticket->Id." needs to be updated", 'MIME-Version' => "1.0", 'Content-Type' => "text/html", }); print $mailer $mailmsg; $mailer->close(); } } } } } # Strip old ids from tracking hash my %nth; # New tracking hash foreach(@idlist){ $nth{$_} = $th{$_}; } # Write tracking hash to file for future use open(FH, ">$trackfile"); while((my $key, my $value) = each(%nth)){ print FH $key, " ", $value, "\n"; } close(FH); # Disconnect before we finish off $RT::Handle->Disconnect(); exit 0; |
Usage is as such, assuming the script is named NotifyTimed.pl.
NotifyTimed.pl --threshold seconds --priority number [options]...
--threshold # Number of seconds before next update
--priority # Priority to check
--to # Who should get mailed, you can specify multiple
--queue queuename # Searches all by default, you can specify multiple
--test # Don't send mail, just print message
--help # Show this screen
To have this be useful, you'll want to set up a cron job. This was developed and tested on RT 3.8.1. It should work fine on many older and newer RT versions.
In Request Tracker, a.k.a. RT, tickets that have a lot of correspondence tend to get long and hard to view with redundant email quotes. It's not hard to modify RT to make the quoted text collapsed. Here's how I did it with RT 3.8.3.
The first file to change is /opt/rt3/share/html/Ticket/Elements/ShowTransactionAttachments. Find these two lines and comment them out. RT handles quotes with arrow brackets and more with Text::Quoted by default. The handling is not appropriate as it will make too many levels in the quotes to handle easily. Commenting this code turns that off.
eval { require Text::Quoted; $content = Text::Quoted::extract($content); }; if ($@) { $RT::Logger->warning( "Text::Quoted failed: $@" ) }
The next step is extending MakeClicky. Place the following code in /opt/rt3/local/html/Callbacks/MyCallbacks/Elements/MakeClicky/Default:
<%ARGS> $types => [] $actions => {} </%ARGS> <%INIT> my $web_path = RT->Config->Get('WebPath'); $actions->{'truncate_quotes'} = sub { my %args = @_; my $rid = int(rand(100000)); my $color = 'A22'; return ' <div> <a href="" id="show'.$rid. '" style="color: #'.$color. '; font-weight: bold;" onclick="Effect.toggle(\'quote'.$rid. '\', \'slide\'); return false;">Toggle quoted message</a> <div id="quote'.$rid.'" style="display: none;">'.$args{value}.'</div> </div> '; }; my @customtypes = ( { name => "httpurl", regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}/, action => "url", }, { name => "httpurl_overwrite", regex => qr/$RE{URI}{HTTP}{-keep}{-scheme => 'https?'}/, action => "url_overwrite", }, { name => 'truncate_arrowq', regex => qr/(^|\s)[>].*$/s, action => 'truncate_quotes', }, { name => 'truncate_outlookq', regex => qr/\s-----Original Message-----.*$/s, action => 'truncate_quotes', }, { name => 'truncate_underscoreq', regex => qr/\s_{5,}.*$/s, action => 'truncate_quotes', }, ); push(@$types, @customtypes); </%INIT>
This is the bit that will wrap the quoted text in a hidden div that can be toggled to be shown by the user. This uses RT's own install of scriptaculous to add toggle effects.
Almost done. The last part is telling RT we what to use the clickable link extensions. You'll do this in the /opt/rt3/etc/RT_SiteConfig.pm. Just add this line to turn on the changes we made.
Set(@Active_MakeClicky, qw(truncate_arrowq truncate_outlookq truncate_underscoreq));
Restart the web server and you'll now have more manageable quotes.
UPDATE: No longer as much of a hack. I found notes on extending MakeClicky. RT has yet to update its documentation to say that the custom actions go in /opt/rt3/local/html/Callbacks/MyCallbacks/Elements/MakeClicky/Default.
