A postmaster filter for followups without a ticket number

Dont create your support topics here! No new topics with questions allowed!

Moderator: crythias

User avatar
Posts: 9920
Joined: 04 May 2010, 18:38
OTRS Version?: 4.0.x
Location: SouthWest Florida, USA

A postmaster filter for followups without a ticket number

Postby crythias » 02 Nov 2013, 02:06

What is this supposed to do?
Look for a TicketFreeText or TicketDynamicField that was stored with a (hopefully unique) identifier from another system and update a ticket if it exists.

Use case:
Prevent infinite loops from a third party ticketing system.
Assume that the "other" ticketing system creates a ticket and sends to your ticketing system.
First, Create a "normal" PostMasterFilter to *set* a TicketFreeText/DynamicField value (sorry, this is built on 3.0/TicketFreeText, but it can be adapted to 3.1+ )

Filter name: set the ticket number
Stop after match: No
Filter Condition:
Header 1: Subject Value 1: TICKETNO:(\d+)
Set Email Headers:
Header 1: X-OTRS-TicketKey2 Value 1: THIRDPARTY
Header 2: X-OTRS-TicketValue2 Value2: [***]

Note that it's important to make sure the other ticket number generator's ticket number string (TICKETNO:?) is searchably different from your OTRS implementation (Ticket#?) or else there's going to be problems.
also note that \d+ is indicative of any quantity of digits after TICKETNO ... you will need to make sure your filter matches what you expect from the other ticket system
(Of course, setting DynamicField should be similar.)

What this does:
It makes sure that the lookup field is set when the ticket is created.

Next, we'll need a filter.

Code: Select all

# PostMaster Filter to find something in a TicketFreeText (hopefully unique or most recent?) and look it up
package Kernel::System::PostMaster::Filter::TFTFind;

use strict;
use warnings;

use vars qw($VERSION);
$VERSION = qw($Revision:0.1 $) [1];

sub new {
    my ( $Type, %Param ) = @_;

    # allocate new hash for object
    my $Self = {};
    bless( $Self, $Type );

    $Self->{Debug} = $Param{Debug} || 0;

    # get needed objects
    for (qw(ConfigObject LogObject DBObject MainObject TicketObject ParserObject)) {
        $Self->{$_} = $Param{$_} || die "Got no $_!";

    return $Self;

sub Run {
    my ( $Self, %Param ) = @_;

    # check needed stuff
    for (qw(GetParam)) {
        if ( !$Param{$_} ) {
            $Self->{LogObject}->Log( Priority => 'error', Message => "Need $_!" );

    my %GetParam = %{ $Param{GetParam} };

        my $Subject = $GetParam{'Subject'};
        $Subject =~ /TICKETNO:(\d+)/;

        my $TNO = $1;

        if (!$TNO) { return 1; }

        # find and return the first TicketID that TNO matches TICKETNO:xxxx
        # TNO information should be passed
        if ( $Self->{Debug} > 0) {
                print "THIRDPARTY TNO: $TNO\n";

        my %TicketIDs = $Self->{TicketObject}->TicketSearch(
                Result => 'HASH',
                Limit => 1,
                TicketFreeKey2 => 'THIRDPARTY',
                TicketFreeText2 => $TNO,
#        or
#        DynamicField_THIRDPARTY => {
#            Equals => $TNO
#        },
# optional if only from a certain thirdparty sender
#               From => '%example@domain.com%',
                UserID => 1,

        #if it can't find anything, just return
        if (!%TicketIDs) {
                $Self->{LogObject}->Log( 'Priority' => 'info', Message => "THIRDPARTY Ticket $TNO not found." );
                return 1;

        my @tickets = values %TicketIDs;

                Priority => 'info',
                Message => "THIRDPARTY $TNO was found as @{[%TicketIDs]}"
        #otherwise, build a subject with the existing TicketNumber (as if it had it already).
        $Param{GetParam}->{Subject} = $Self->{TicketObject}->TicketSubjectBuild(
                TicketNumber => $tickets[0],
                Subject => $Subject,
                Action => 'Reply'

                Priority => 'info',
                Message => "Updating THIRDPARTY ticket"
        return 1;


And then we'll need to include it as a filter (create an xml file. Forinstance: Kernel/Config/Files/TFTFind.xml)

Code: Select all

<?xml version="1.0" encoding="utf-8"?>
<otrs_config version="1.0" init="Application">
    <ConfigItem Name="PostMaster::PreFilterModule###7-TFTFind" Required="0" Valid="1">
        <Description Translatable="1">Checks all incoming email for a THIRDPARTY number and if exists uses first result to append to ticket.</Description>
                <Item Key="Module">Kernel::System::PostMaster::Filter::TFTFind</Item>

Standard disclaimer applies: This has been slightly tested to work with 3.0 and probably can work with 3.1+/Dynamic Fields with just a small tweak (commented).
Test it at your testing box (save as ticket0007.eml and then cat ticket0007.eml | /opt/otrs/bin/otrs.PostMaster.pl). The second time you do this, it should show up as a followup.

Code: Select all

From skywalker@otrs.org  Fri Dec 21 23:59:24 2001
Return-Path: <skywalker@otrs.org>
Received: (from skywalker@localhost)
    by avro.de (8.11.3/8.11.3/SuSE Linux 8.11.1-0.5) id f3MMSE303694
    for martin@localhost; Fri, 21 Dec 2001 23:59:24 +0200
Date: Fri, 21 Dec 2001 23:59:24 +0200
From: System Tester I <skywalker@otrs.org>
To: darthvader@otrs.org
Subject: TICKETNO:0007
Message-ID: <20011221002814.A3599@avro>
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
X-Operating-System: Linux 2.4.10-4GB i686
X-Uptime: 12:23am  up  5:19,  6 users,  load average: 0.11, 0.13, 0.18
Content-Length: 139
Lines: 11

This is the first test of checking a DIFFERENT ticket number generator.

Adios ...

  Little "Skywalker"

System Tester I - <skywalker@otrs.org>
Old programmers never die. They just branch to a new address.

What could go wrong? You could possibly lose tickets (they'll end up unparsed within otrs/var/spool) and there might be some errors (hopefully helpful?) in the OTRS System Log. If you have an emergency on this, just clear the checkbox in
Edit Config Settings in Ticket -> Core::PostMaster

or delete the .xml file.

It's possible that it just won't work, and the third party system creates new tickets, in spite of the "matching." But you'll see that THIRDPARTY TNO xxx not found if that's the case.
Also, you may need to make sure the Subject filter regular expression in the TFTFind filter is the same as PostmasterFilter.
The example given assumes *only* numerical digits following the "TICKETNO" keyword. If you are expecting an alphanumeric pattern or something with additional characters, be sure to find a terminating character in case the subject changes. Note that ONLY the information in parentheses () will be recorded at [***] in the PostMasterFilter.

Bugfixes? Please reply here. Customization? Please link to this in the other forums.

This code is available for your use without restriction. Don't blame me if it breaks.
OTRS 4.0.x (private/testing/public) on Linux with MySQL database. Also on github.
Please edit your signature to include your OTRS version, Operating System, and database type.
Click Subscribe Topic below to get notifications. Consider amending your topic title to include [SOLVED] if it is so.
Need help? Before you ask

OTRS newbie
Posts: 3
Joined: 25 Sep 2014, 00:48
OTRS Version?: 3.1.7
Real Name: Scott Morgan

Re: A postmaster filter for followups without a ticket number

Postby skydiver » 08 Jan 2015, 00:49

Sorry, moving this to general

Return to “Howtos”

Who is online

Users browsing this forum: No registered users and 1 guest