Proof of concept: Creating reoccuring tickets for task

Jens Bothe11. Nov 2010 | Best PracticesModifications & Packages

Disclaimer:

The practical examples presented in our technical blog (blog.otrs.com) and now in the expert category in our FAQ blog section serve as a source of ideas and documentation to show what is theoretically possible with OTRS in concrete scenarios or sometimes even for more exotic configurations. All configurations presented here were developed under laboratory conditions as a proof of concept. 

We can only guarantee testing and implementation of these concepts to be error-free and productive if implemented in a workshop with one of our OTRS consultants. Without this, the responsibility lies with the customer himself. Please note that configurations from older OTRS versions may not work in the newer ones.

A question asked by my customers quite often is:

“How can we create a reoccuring ticket for tasks like checking the backup?”

As there was no solution inside of the OTRS Framework yet my answer was:

“Sorry, you have to create an e-mail via cron for this.”

An article in the german Linux Magazin put this question back to my mind. (Sorry it is in german..)

So I changed the script ‘ical-daemon’ to my needs. Now the following parameters of the ical file are used:
The ticket itself is created by the script ‘create-ticket.pl’ which will be triggered by ‘ical-daemon’ with the needed parameters.

  • DESCRIPTION: Article Body
  • SUMMARY:        Article and Ticket Subject
  • LOCATION:        Queue Name
  • Ticket created by the daemon:


    Example ics file:

    BEGIN:VCALENDAR
    VERSION:2.0
    PRODID:-//Apple Inc.//iCal 4.0.3//EN
    CALSCALE:GREGORIAN
    BEGIN:VTIMEZONE
    TZID:Europe/Berlin
    BEGIN:DAYLIGHT
    TZOFFSETFROM:+0100
    RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
    DTSTART:19810329T020000
    TZNAME:GMT+02:00
    TZOFFSETTO:+0200
    END:DAYLIGHT
    BEGIN:STANDARD
    TZOFFSETFROM:+0200
    RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
    DTSTART:19961027T030000
    TZNAME:GMT+01:00
    TZOFFSETTO:+0100
    END:STANDARD
    END:VTIMEZONE
    BEGIN:VEVENT
    CREATED:20101111T190648Z
    UID:6B58179D-E1C4-466B-A881-F15A663EE4A7
    DTEND;TZID=Europe/Berlin:20101111T225000
    TRANSP:OPAQUE
    SUMMARY:Please check Backup
    DTSTART;TZID=Europe/Berlin:20101111T230000
    DTSTAMP:20101111T202135Z
    LOCATION:Postmaster
    SEQUENCE:7
    DESCRIPTION:Please check Backup of the following servers:
    - server 1
    - server 2
    - server 3
    END:VEVENT
    END:VCALENDAR

    The modified ical-daemon script:


    #!/usr/bin/perl -w
    ###########################################
    # ical-daemon - Parse .ics files and send
    #               alerts on upcoming events.
    # Mike Schilli, 2010 (m@perlmeister.com)
    # modified by Jens Bothe (jb@otrs.org)
    ###########################################
    use strict;
    #use local::lib;
    use iCal::Parser;
    use Log::Log4perl qw(:easy);
    use App::Daemon qw(daemonize);
    use Sysadm::Install qw(mkd slurp tap);
    use FindBin qw($Bin);

    our $UPDATE_REQUESTED = 0;
    our $ALERT_BEFORE =
    DateTime::Duration->new( minutes => 15 );
    our $CURRENT_DAY      = DateTime->today();
    our @TODAYS_EVENTS    = ();

    my($home)  = glob "~";
    my $admdir = "$home/.ical-daemon";
    my $icsdir = "$admdir/ics";

    mkd $admdir unless -d $admdir;
    mkd $icsdir unless -d $icsdir;

    $App::Daemon::logfile = "$admdir/log";
    $App::Daemon::pidfile = "$admdir/pid";

    if( exists $ARGV[0] and
    $ARGV[0] eq '-q' ) {
    my $pid = App::Daemon::pid_file_read();
    kill 10, $pid; # Send USR1
    exit 0;
    }

    Log::Log4perl->easy_init({
    level => $DEBUG,
    file  => $App::Daemon::logfile
    });

    $SIG{ USR1 } = sub {
    DEBUG "Received USR1";
    $UPDATE_REQUESTED = 1;
    };

    $UPDATE_REQUESTED = 1; # bootstrap

    daemonize();

    while(1) {
    my $now = DateTime->now(
    time_zone => 'local' );

    my $today =
    $now->clone->truncate( to => 'day' );

    if( $UPDATE_REQUESTED or
    $CURRENT_DAY ne $today ) {

    $UPDATE_REQUESTED = 0;
    $CURRENT_DAY      = $today;

    DEBUG "Updating ...";
    @TODAYS_EVENTS    = update( $now );
    DEBUG "Update done.";
    }

    if( scalar @TODAYS_EVENTS ) {
    my $entry         = $TODAYS_EVENTS[0];

    DEBUG "Next event at: $entry->[0]";

    if( $now >
    $entry->[0] - $ALERT_BEFORE ) {
    INFO "Notification: ",
    "$entry->[1] $entry->[0]";
    #      tap "$Bin/ical-notify", $entry->[2], $entry->[1],
    tap "$Bin/create-ticket.pl", $entry->[2], $entry->[1], $entry->[3],
    $entry->[0];
    shift @TODAYS_EVENTS;
    next;
    }
    }

    DEBUG "Sleeping";
    sleep 60;
    }

    ###########################################
    sub update {
    ###########################################
    my($now) = @_;

    my $start = $now->clone->truncate(
    to => 'day' );
    my $tomorrow = $now->clone->add(
    days => 1 );

    my $parser=iCal::Parser->new(
    start => $start,
    end   => $tomorrow );

    my $hash;

    for my $file (<$icsdir/*.ics>) {
    DEBUG "Parsing $file";
    $hash = $parser->parse( $file );
    }

    my $year  = $now->year;
    my $month = $now->month;
    my $day   = $now->day;

    if(! exists $hash->{ events }->{
    $year }->{ $month }->{ $day } ) {
    return ();
    }

    my $events = $hash->{ events }->{
    $year }->{ $month }->{ $day };

    for my $key ( keys %$events ) {
    if( event_is_holiday(
    $events->{ $key } ) ) {
    WARN "No alerts today (holiday)";
    return ();
    }
    }

    my @events = ();

    for my $key ( keys %$events ) {
    next if $now >
    $events->{ $key }->{ DTSTART };
    # already over?

    push @events, [
    $events->{ $key }->{ DTSTART },
    $events->{ $key }->{ DESCRIPTION },
    $events->{ $key }->{ SUMMARY },
    $events->{ $key }->{ LOCATION },
    ];
    }

    @events = sort { $a->[0] <=> $b->[0] }
    @events;

    return @events;
    }

    ###########################################
    sub event_is_holiday {
    ###########################################
    my($event) = @_;

    return undef unless
    exists $event->{ ATTENDEE };

    if( $event->{ ATTENDEE }->[ 0 ]->{ CN }
    eq "US Holidays" ) {
    return 1;
    }
    return 0;
    }

    Script: create-ticket.pl

    #!/usr/bin/perl -w

    use SOAP::Lite( 'autodispatch', proxy => 'http://otrs-server/otrs/rpc.pl' );
    my($subject, $agenda, $queue, $time) = @ARGV;
    my $SOAP_User = 'otrs_test';
    my $SOAP_Pass = 'test_otrs!';

    my $OTRS_Subject = $subject;
    my $OTRS_Body = $agenda;

    my $OTRS_CustomerFrom = 'feedback@otrs.org';
    my $OTRS_Queue = $queue;

    # script
    my $RPC = Core->new();

    # create a new ticket number
    my $TicketNumber = $RPC->Dispatch( $SOAP_User, $SOAP_Pass, 'TicketObject', 'TicketCreateNumber' );
    #print "NOTICE: New Ticket Number is: $TicketNumber\n";
    # create a new ticket
    my %TicketData = (
    TN           => $TicketNumber,
    Title        => $OTRS_Subject,
    Queue        => $OTRS_Queue,
    Lock         => 'unlock',
    Priority     => '3 normal',
    State        => 'new',
    CustomerID   => $OTRS_CustomerFrom,
    CustomerUser => $OTRS_CustomerFrom,
    OwnerID      => 3,
    UserID       => 3,
    );
    my $TicketID = $RPC->Dispatch( $SOAP_User, $SOAP_Pass, 'TicketObject', 'TicketCreate', %TicketData => 1 );
    #print "NOTICE: TicketID is $TicketID\n";

    # create new article
    my $ArticleID = $RPC->Dispatch($SOAP_User, $SOAP_Pass, 'TicketObject', 'ArticleCreate',
    TicketID         => $TicketID,
    ArticleType      => 'phone',                # email-external|email-internal|phone|fax|...
    SenderType       => 'customer',             # agent|system|customer
    From             => $OTRS_CustomerFrom,     # not required but useful
    To               => $OTRS_Queue,            # not required but useful
    ReplyTo          => $OTRS_CustomerFrom,     # not required
    Subject          => $OTRS_Subject,          # required
    Body             => $OTRS_Body,             # required
    Charset          => 'utf-8',
    HistoryType      => 'PhoneCallCustomer',    # EmailCustomer|Move|AddNote|PriorityUpdate|WebRequestCustomer|...
    HistoryComment   => 'Customer called us.',
    UserID           => 3,
    NoAgentNotify    => 0,                      # if you don't want to send agent notifications
    MimeType         => 'text/plain',
    Loop             => 0,                      # auto reject|auto follow up|auto follow up|auto remove
    AutoResponseType => 'auto reply',
    ForceNotificationToUserID => '',
    OrigHeader       => {
    'From' => $OTRS_CustomerFrom,
    'To'   => $OTRS_Queue,
    'Subject' => $OTRS_Subject,
    'Body' => $OTRS_Body,
    },
    );
    #print "NOTICE: ArticleID is $ArticleID\n";

    exit 0;

    You can download the files here

    #5
    Murat Yaman at 04.01.2017, 12:24

    Hello can someone describe where we must place this folders and how we can schedule this via cron?

    #4
    Dani at 17.06.2013, 12:33

    Where do I place the files to work? I have tried many locations, but have not worked Please help me :D

    #3
    Patrick at 13.06.2013, 17:09

    How can i use this Script in OTRS... ? I think over Webservices but i really have no idea :-/ Could you help me ?

    #2
    Easy ticket creation via Generic Interface | OTRS Community Blog at 03.10.2012, 20:25

    [...] This script can also be used with the wrapper of reoccuring task described in the post “Proof of concept: Creating reoccuring tickets for task” [...]

    #1
    Tweets that mention Proof of concept: Creating reoccuring tickets for task - OTRS Community Blog -- Topsy.com at 12.11.2010, 08:02

    [...] This post was mentioned on Twitter by Christopher T. Kuhn, Jens Bothe. Jens Bothe said: Wrote a new blog entry on scheduling task with OTRS http://bit.ly/agP8Yd #otrs Thank you Linux Magazin [...]

    Your email address will not be published. Required fields are marked *