Code: Alles auswählen
Getopt::Long
XML::LibXML
HTML::Entities
Code: Alles auswählen
perl pm_cruncher.pl ARGUMENTE
Code: Alles auswählen
perl pm_cruncher.pl --input-file export.xml --mode all
Code: Alles auswählen
#! /usr/bin/env perl
use strict;
use warnings 'FATAL'=>'all';
use autodie;
use Getopt::Long;
use XML::LibXML;
use HTML::Entities 'encode_entities';
use constant SCRIPT_VERSION=>'0.3';
=pod
PM-Cruncher exklusiv für das ABF - (c) 2025 mmA - written from scratch
Dieser Code wandelt die aus dem ABF exportierte XML-Datei mit euren PN in wahlweise TXT oder HTML um.
BENUTZUNG: "perl pm_cruncher.pl --help" für Hilfe (auf Englisch)
LIZENZ:
-Exklusiv für das ABF / die von dort exportierten Daten.
-Verwendung und unveränderte Weitergabe innerhalb des ABF frei. Externe Weitergabe verboten.
-Keinerlei Garantie usw.
-Modifizierung für private Zwecke erlaubt, aber keine Weitergabe/Veröffentlichung der modifizierten Version.
Bugreports, Support, ... im Rahmen meiner Möglichkeiten im entsprechenden Thread im ABF: https://www.ab-forum.de/viewtopic.php?p=1382495
BEKANNTE FEHLER/LIMITIERUNGEN:
-Spoiler sind immer ausgeklappt/sichtbar, bräuchte Javascript (wontfix)
-Keine Anzeige der internen PN-ID
-Keine Anzeige in welchem (eventuell benutzerdefiniertem) Ordner die PN liegt
-Keine Möglichkeit nur PN aus einem Ordner zu exportieren
-Nicht per BBCode gekennzeichnete Links werden nicht automatisch umgewandelt
-...
STAND: 10.03.25 14:44
=cut
print 'PM-Cruncher for ABF version ',SCRIPT_VERSION,"\n";
print "provided without any warranty\n";
print "(c) 2025 mmA - for licence see top of file\n\n";
my $show_help=0;
my $show_version_only=0;
my $input_file;
my $mode;
my $sort='desc';
my $output_format='html';
my $output_file='my_pm.html';
GetOptions(
'help|usage' => \$show_help,
'version' => \$show_version_only,
'input-file=s' => \$input_file,
'mode=s' => \$mode,
'sort=s' => \$sort,
'output=s' => \$output_format,
'output-file=s' => \$output_file
) or print_help_and_exit();
exit(0) if($show_version_only);
print_help_and_exit() if($show_help);
die "error: no input file specified, try --help\n" if(!defined($input_file) || !length($input_file));
die "error: no mode specified, try --help\n" if(!defined($mode) || !length($mode));
die "error: invalid sorting specified, try --help\n" if(!defined($sort) || ($sort ne 'asc' && $sort ne 'desc'));
die "error: no or invalid output format specified, try --help\n" if(!defined($output_format) || ($output_format ne 'txt' && $output_format ne 'html'));
die "error: no output file specified, try --help\n" if(!defined($output_file) || !length($output_file));
my $start_time=time();
print "input file: $input_file\n";
print "output file: $output_file\n";
print "output format: $output_format\n";
print "parser mode: $mode\n";
print "sorting mode: $sort\n";
print 'started at: ',get_time_str_de($start_time),"\n\n";
die "error: output file \"$output_file\" already existing (and not empty), not overwriting\n" if(-e $output_file && -s $output_file);
die "error: non-existing or empty input file \"$input_file\"\n" if(!-e $input_file || !-s $input_file);
my $parser=XML::LibXML->new(no_network=>1);
my $dom=$parser->load_xml(location=>$input_file);
my @xml_header=$dom->getElementsByTagName('header');
die "incompatible XML file\n", if($xml_header[0]->getChildrenByTagName('exporter_name') ne 'mma/pnexporter');
my $input_time=$xml_header[0]->getChildrenByTagName('time');
print 'Input file exported from ABF on ',get_time_str_de($input_time),"\n";
print 'using mma/pnexporter version '.$xml_header[0]->getChildrenByTagName('exporter_version')."\n\n";
#parse all PM
my @xml_pm=$dom->getElementsByTagName('privmsg');
my @all_pm;
foreach(@xml_pm)
{
my ($id, $root_id, $time, $icon, $sender, @recipients, $subject, $message);
$id=($_->getChildrenByTagName('id'))[0]->textContent;
$root_id=($_->getChildrenByTagName('root_id'))[0]->textContent;
$time=($_->getChildrenByTagName('time'))[0]->textContent;
$icon=($_->getChildrenByTagName('icon'))[0]->textContent;
$sender=($_->getChildrenByTagName('sender'))[0]->textContent;
$subject=($_->getChildrenByTagName('subject'))[0]->textContent;
$message=($_->getChildrenByTagName('message'))[0]->textContent;
foreach my $r ($_->getChildrenByTagName('recipient'))
{
my ($type, $status, $name);
$type=$r->getAttribute('type');
$status=$r->getAttribute('status');
$name=$r->textContent;
push @recipients, {'type'=>$type, 'status'=>$status, 'name'=>$name};
}
push @all_pm, {'id'=>$id, 'root_id'=>$root_id, 'time'=>$time, 'icon'=>$icon, 'sender'=>$sender, 'recipients'=>[@recipients], 'subject'=>$subject, 'message'=>$message};
}
#filter depending on $mode
my @pm;
if($mode eq 'all')
{
print "no filtering applied\n\n";
@pm=@all_pm;
}
elsif($mode=~/^\d+$/)
{
my $id=$mode;
print "extracting conversation started with PM-ID $id ...\n\n";
die "error: invalid ID 0\n" if($id==0);
foreach my $p (@all_pm)
{
push @pm, $p if($p->{'id'}==$id || $p->{'root_id'}==$id);
}
die "error: no PM found\n" if(!scalar(@pm));
}
else
{
my $user=$mode;
print "extracting all PM from/to user $user ...\n\n";
foreach my $p (@all_pm)
{
push @pm, $p if($p->{'sender'} eq $user || grep ({$_->{'name'} eq $user} @{$p->{'recipients'}}));
}
die "error: no PM found from/to user $user\n" if(!scalar(@pm));
}
#sort PM
if($sort eq 'desc')
{
@pm=sort sortfunc_time_desc @pm;
}
elsif($sort eq 'asc')
{
@pm=sort sortfunc_time_asc @pm;
}
else
{
die "unknown sort mode $sort\n";
}
#write output
open my $out, '>', $output_file;
binmode $out, ':utf8';
if($output_format eq 'txt')
{
print "writing TXT file...\n\n";
do_write_txt(\@pm, $mode, $sort, $start_time, $input_file, $input_time, $output_file);
print "TXT file written\n";
}
elsif($output_format eq 'html')
{
print "writing HTML file...\n\n";
do_write_html(\@pm, $mode, $sort, $start_time, $input_file, $input_time, $output_file);
print "HTML file written\n";
}
close $out;
print "\n";
print "all done, thank you\n";
############# SUB VARIOUS #############
sub print_help_and_exit
{
print <<'USAGE';
USAGE: perl pm_cruncher.pl [arguments]
DEPENDENCIES:
XML::LibXML, on Debian and derived try "sudo apt install libxml-libxml-perl".
ARGUMENTS:
GENERAL:
--version
--help | --usage
INPUT:
--input-file $file
PARSER MODE:
--mode $mode
string 'all' -> extract all PM
numeric $id_start -> conversation starting with PM $id_start (!=0)
string $name (!='all') -> all PM from/to user $name
SORTING MODE:
--sort $mode
string 'asc' -> sort by time, newest message first
string 'desc' -> sort by time, oldest message first (default)
OUTPUT:
--output-file $file (default: my_pm.html)
--output txt|html -> output TXT or HTML (default)
OUTPUT:
TXT is mostly for debugging, no fancy formatting.
HTML converts BBCode and smilies are supported. You probably want this mode.
USAGE
exit(0);
}
sub sortfunc_time_desc
{
return $a->{'time'}<=>$b->{'time'};
}
sub sortfunc_time_asc
{
return $b->{'time'}<=>$a->{'time'};
}
sub get_time_str_de
{
my $t=shift;
return '' if(length($t)==0);
my @raw=localtime($t);
my @day=qw/Mo Di Mi Do Fr Sa So/;
return sprintf("%s %02d.%02d.%02d %02d:%02d:%02d",$day[$raw[6]], $raw[3], $raw[4]+1, $raw[5]%100, $raw[2], $raw[1], $raw[0]);
}
############# SUB TXT #############
sub do_write_txt
{
my ($pm_ref, $parser_mode, $sort_mode, $start_time, $inp_file, $inp_time, $outp_file)=@_;
print $out 'PM-Cruncher for ABF version ',SCRIPT_VERSION," (c) 2025 mmA\n\n";
print $out "input file: $inp_file\n";
print $out "output file: $outp_file\n";
print $out "output format: TXT\n";
print $out "parser mode: $parser_mode\n";
print $out "sort mode: $sort_mode\n";
print $out 'started at: ',get_time_str_de($start_time),"\n";
print $out "\n";
foreach my $pm (@{$pm_ref})
{
print $out 'SUBJECT: ',$pm->{'subject'},"\n";
print $out 'SENT: ',scalar(get_time_str_de($pm->{'time'})),"\n";
print $out 'FROM: ',$pm->{'sender'},"\n";
my @to=grep {$_->{'type'} eq 'to';} @{$pm->{'recipients'}};
print $out 'TO: ',join(', ', map {$_->{'name'};} @to),"\n" if(scalar(@to));
my @bcc=grep {$_->{'type'} eq 'bcc';} @{$pm->{'recipients'}};
print $out 'BCC: ',join(', ', map {$_->{'name'};} @bcc),"\n" if(scalar(@bcc));
print $out "\n";
print $out $pm->{'message'},"\n\n\n";
}
}
############# SUB HTML #############
sub do_write_html
{
my ($pm_ref, $parser_mode, $sort_mode, $start_time, $inp_file, $inp_time, $outp_file)=@_;
print $out <<'HTML_HEADER';
<!-- AUTOGENERATED FILE - DO NOT EDIT -->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title>PN-Export ABF</title>
<style>
body {
margin-left: auto;
margin-right: auto;
width: 80%;
height: 100%;
overflow-wrap: anywhere;
}
.header {
display: block;
min-height: 10%;
top: 0px;
position: sticky;
background-color: white;
padding-bottom: 3px;
}
.header_title {
text-align: center;
font-size: 1.5rem;
}
.header_details {
margin-bottom: 10px;
}
.messages {
overflow: auto;
}
p.msg {
width: 100%;
}
.msg_subject {
font-size: 1.2rem;
margin-bottom: 5px;
}
.msg_time, .msg_from, .msg_to, .msg_bcc {
font-size: 0.8rem;
margin-top: 0px;
margin-bottom: 0px;
}
div.msg_text {
margin-top: 5px;
width: 95%;
margin-left: auto;
margin-right: auto;
}
blockquote {
border: 1px solid grey;
padding: 5px;
}
.quote {
font-size: 0.8rem;
margin-bottom: 2px;
}
.quote_time {
float: right;
}
.code {
font-family: monospace;
font-size: 0.8rem;
border: 1px solid grey;
margin-left: auto;
margin-right: auto;
margin-top: 10px;
margin-bottom: 10px;
width: 95%;
white-space: pre-wrap;
padding: 5px;
}
.code_title {
font-weight: bold;
}
.spoiler {
border: 1px solid grey;
margin-left: auto;
margin-right: auto;
margin-top: 10px;
margin-bottom: 10px;
width: 95%;
}
.spoiler_title {
font-weight: bold;
}
</style>
</head>
<body>
<div class="header">
<p class="header_title">Exportierte PN aus dem ABF</h1>
HTML_HEADER
print $out "\t\t",'<p class="header_details">erzeugt von PM-Cruncher version ',SCRIPT_VERSION,' von mmA<br>Datei ',$inp_file,' vom ',get_time_str_de($inp_time),'<br>Parser-Modus: ',$parser_mode,'<br>Sortierung: ',$sort_mode,'</p>',"\n";
print $out <<'HTML2';
</div>
<div class="messages">
HTML2
foreach my $pm (@{$pm_ref})
{
my $icon=$pm->{'icon'};
$icon='' if($icon eq '[none]');
my $subject=$pm->{'subject'};
my $time_str=get_time_str_de($pm->{'time'});
my $from=$pm->{'sender'};
my @to=grep {$_->{'type'} eq 'to';} @{$pm->{'recipients'}};
my $to_str=join(', ', map {$_->{'name'};} @to);
my @bcc=grep {$_->{'type'} eq 'bcc';} @{$pm->{'recipients'}};
my $bcc_str=join(', ', map {$_->{'name'};} @bcc);
my $msg=$pm->{'message'};
print $out "\t\t<p class=\"msg\">\n";
print $out "\t\t\t<p class=\"msg_subject\">";
print $out '<img src="./icons/',$icon,'"></img>' if(length($icon));
print $out $subject,"</p>\n";
print $out "\t\t\t<p class=\"msg_time\">Datum: ",encode_entities($time_str),"</p>\n";
print $out "\t\t\t<p class=\"msg_from\">Von: ",encode_entities($from),"</p>\n";
print $out "\t\t\t<p class=\"msg_to\">An: ",encode_entities($to_str),"</p>\n";
print $out "\t\t\t<p class=\"msg_bcc\">BCC: ",encode_entities($bcc_str),"</p>\n" if(length($bcc_str));
print $out "\t\t\t<div class=\"msg_text\">",convert_to_html($msg),"</div>\n";
print $out "\t\t</p>\n";
}
print $out <<'HTML3';
</div>
</body>
</html>
HTML3
}
sub convert_to_html
{
my $msg=shift;
#convert newlines to HTML
$msg=~s/\n/<br>/gm;
#convert BBCode to HTML
1 while($msg=~s!\[(\w+)(?:\*|([^\]]+?))?\](.+?)\[/(\1)\]!conv_bb($1, $2, $3)!e);
#fix superfluous <br> before or after <blockquote> or <div>
$msg=~s/(?<=<\/(?:blockquote|div)>)<br>//gm;
$msg=~s/<br>(?=<(?:blockquote|div)>)//gm;
#convert smilies
$msg=~s!(:.+?)(?=[^\w\-\?:\|])!check_conv_smilie($1)!egi; #This might need tweaking
#some smilies do not start with ':' ...
$msg=~s!8-\)!check_conv_smilie('8-)')!eg;
$msg=~s!;\)!check_conv_smilie(';)')!eg;
return $msg;
}
sub conv_bb
{
my %bb_data=(
'b'=>{'simple'=>1, 'html_start'=>'<b>', 'html_end'=>'</b>'},
'i'=>{'simple'=>1, 'html_start'=>'<i>', 'html_end'=>'</i>'},
'u'=>{'simple'=>1, 'html_start'=>'<u>', 'html_end'=>'</u>'},
's'=>{'simple'=>1, 'html_start'=>'<s>', 'html_end'=>'</s>'},
'code'=>{'simple'=>1, 'html_start'=>'<div class="code"><div class="code_title">Code:</div>', 'html_end'=>'</div>'},
'list'=>{'simple'=>0, 'callback'=>\&handle_list},
'spoiler'=>{'simple'=>0, 'callback'=>\&handle_spoiler},
'quote'=>{'simple'=>0, 'callback'=>\&handle_quote},
'url'=>{'simple'=>0, 'callback'=>\&handle_url},
'color'=>{'simple'=>0, 'callback'=>\&handle_color},
'size'=>{'simple'=>0, 'callback'=>\&handle_size},
'img'=>{'simple'=>0, 'callback'=>\&handle_img}
);
my ($bb, $bb_param, $content)=(shift, shift, shift);
die "error: unknown BBCode $bb found\n" if(!defined($bb_data{$bb}));
if($bb_data{$bb}->{'simple'})
{
return $bb_data{$bb}->{'html_start'}.$content.$bb_data{$bb}->{'html_end'};
}
else
{
return &{$bb_data{$bb}->{'callback'}}($bb, $bb_param, $content);
}
}
sub handle_list
{
my (undef, $bb_param, $content)=(shift, shift, shift);
return '<ul>'.join('', map {$_=($_=~s/^\[\*\]//)?'<li>'.encode_entities($_).'</li>':$_; } split(/<br>/, $content)).'</ul>';
}
sub handle_spoiler
{
my (undef, $bb_param, $content)=(shift, shift, shift);
my $title='spoiler';
$title=$1 if($bb_param=~/^=\"(.+?)\"/);
return '<div class="spoiler"><div class="spoiler_title">'.$title.':</div>'.$content.'</div>';
}
sub handle_quote
{
my (undef, $bb_param, $content)=(shift, shift, shift);
my($user, $time)=('?', '');
if(length($bb_param))
{
$user=$1 if($bb_param=~/^=\"(.+?)\"/ || $bb_param=~/^=([^\s]+)/);
$time=$1 if($bb_param=~/time=(\d+)/);
}
return '<blockquote><div class="quote">'.$user.' schrieb:<span class="quote_time">'.get_time_str_de($time).'</span></div>'.$content.'</blockquote>';
}
sub handle_url
{
my (undef, $bb_param, $content)=(shift, shift, shift);
return '<a href="'.substr($bb_param, 1).'">'.$content.'</a>';
}
sub handle_color
{
my (undef, $bb_param, $content)=(shift, shift, shift);
return '<span style="color: '.substr($bb_param, 1).';">'.$content.'</span>';
}
sub handle_size
{
my (undef, $bb_param, $content)=(shift, shift, shift);
return '<span style="font-size: '.substr($bb_param, 1).'%;">'.$content.'</span>';
}
sub handle_img
{
my (undef, $bb_param, $content)=(shift, shift, shift);
return '<img src="https://'.$content.'"></img>';
}
sub check_conv_smilie
{
my %smilie_data=(
'8-)'=>{'url'=>'./smilies/icon_cool.gif', 'title'=>'Cool'},
':amor:'=>{'url'=>'./smilies/amor.gif', 'title'=>'Amor'},
':arrow:'=>{'url'=>'./smilies/icon_arrow.gif', 'title'=>'Pfeil'},
':boxing:'=>{'url'=>'./smilies/boxing.gif', 'title'=>'Boxen'},
':brille1:'=>{'url'=>'./smilies/brille3.gif', 'title'=>'Brille 1'},
':brille3:'=>{'url'=>'./smilies/brille7.gif', 'title'=>'Brille 3'},
':buch:'=>{'url'=>'./smilies/buchlesen.gif', 'title'=>'Buch'},
':buegeln:'=>{'url'=>'./smilies/buegeln.gif', 'title'=>'Bügeln'},
':cheerleader:'=>{'url'=>'./smilies/cheerleader.gif', 'title'=>'Cheerleader'},
':cooler:'=>{'url'=>'./smilies/cooler.gif', 'title'=>'Cool Man'},
':coolgun:'=>{'url'=>'./smilies/cool3.gif', 'title'=>'Cool Gun'},
':coolsmoke:'=>{'url'=>'./smilies/cool.gif', 'title'=>'Cool Smoke'},
':crybaby:'=>{'url'=>'./smilies/crybaby.gif', 'title'=>'Cry Baby'},
':cry:'=>{'url'=>'./smilies/icon_cry.gif', 'title'=>'Weinen'},
':daumen:'=>{'url'=>'./smilies/daumen.gif', 'title'=>'Daumen'},
':dont:'=>{'url'=>'./smilies/dont.gif', 'title'=>'Don\'t'},
':-D'=>{'url'=>'./smilies/icon_biggrin.gif', 'title'=>'Sehr glücklich'},
':D'=>{'url'=>'./smilies/icon_biggrin.gif', 'title'=>'Sehr glücklich'},
':duschen:'=>{'url'=>'./smilies/dusche.gif', 'title'=>'Duschen'},
':evil:'=>{'url'=>'./smilies/icon_evil.gif', 'title'=>'Böse'},
':fessel:'=>{'url'=>'./smilies/fessel.gif', 'title'=>'Fessel'},
':flirten:'=>{'url'=>'./smilies/flirten1.gif', 'title'=>'Flirten'},
':fluchen2:'=>{'url'=>'./smilies/fluchen1.gif', 'title'=>'Fluchen 2'},
':fluchen:'=>{'url'=>'./smilies/fluchen.gif', 'title'=>'Fluchen'},
':foto:'=>{'url'=>'./smilies/foto.gif', 'title'=>'Foto'},
':frech2:'=>{'url'=>'./smilies/frech3.gif', 'title'=>'Frech 2'},
':frech:'=>{'url'=>'./smilies/frech.gif', 'title'=>'Frech'},
':geschirr:'=>{'url'=>'./smilies/geschirr.gif', 'title'=>'Geschirr'},
':gewicht:'=>{'url'=>'./smilies/gewicht.gif', 'title'=>'Gewicht'},
':gewinner:'=>{'url'=>'./smilies/winner.gif', 'title'=>'Gewinner'},
':good:'=>{'url'=>'./smilies/good.gif', 'title'=>'Good'},
':gruebel:'=>{'url'=>'./smilies/gruebel.gif', 'title'=>'Grübel'},
':gruppe:'=>{'url'=>'./smilies/gathering.gif', 'title'=>'Gruppe'},
':hallo:'=>{'url'=>'./smilies/verstecken2.gif', 'title'=>'Hallo'},
':hammer:'=>{'url'=>'./smilies/hammer.gif', 'title'=>'Hammer'},
':handkuss:'=>{'url'=>'./smilies/liebe93.gif', 'title'=>'Handkuss'},
':headbang:'=>{'url'=>'./smilies/headbang.gif', 'title'=>'Headbang'},
':hierlang:'=>{'url'=>'./smilies/hierlang.gif', 'title'=>'Hier lang'},
':hochzeit:'=>{'url'=>'./smilies/hochzeit.gif', 'title'=>'Hochzeit'},
':holy:'=>{'url'=>'./smilies/holy.gif', 'title'=>'Heiliger'},
':hut:'=>{'url'=>'./smilies/hut.gif', 'title'=>'Hut'},
':idea:'=>{'url'=>'./smilies/icon_idea.gif', 'title'=>'Idee'},
':idee:'=>{'url'=>'./smilies/lighten.gif', 'title'=>'Idee'},
':kehren:'=>{'url'=>'./smilies/putzen.gif', 'title'=>'Kehren'},
':klassiker2:'=>{'url'=>'./smilies/klassiker1.gif', 'title'=>'Klassiker 2'},
':klassiker:'=>{'url'=>'./smilies/klassiker4.gif', 'title'=>'Klassiker'},
':klopfen:'=>{'url'=>'./smilies/klopfen.gif', 'title'=>'Teppich klopfen'},
':koch:'=>{'url'=>'./smilies/koch.gif', 'title'=>'Koch'},
':kopfhoerer:'=>{'url'=>'./smilies/kopfhoerer.gif', 'title'=>'Kopfhörer'},
':kopfstand:'=>{'url'=>'./smilies/kopfstand1.gif', 'title'=>'Kopfstand'},
':krank2:'=>{'url'=>'./smilies/krank1.gif', 'title'=>'Krank 2'},
':krank:'=>{'url'=>'./smilies/ill.gif', 'title'=>'Krank'},
':kuss:'=>{'url'=>'./smilies/kuss2.gif', 'title'=>'Kuss'},
':lach:'=>{'url'=>'./smilies/lach.gif', 'title'=>'Lachen'},
':lol:'=>{'url'=>'./smilies/icon_lol.gif', 'title'=>'Lachen'},
':mail:'=>{'url'=>'./smilies/mail.gif', 'title'=>'Mail'},
':mosh:'=>{'url'=>'./smilies/mosher.gif', 'title'=>'Mosh'},
':mrgreen:'=>{'url'=>'./smilies/icon_mrgreen.gif', 'title'=>'Mr. Green'},
':no:'=>{'url'=>'./smilies/no.gif', 'title'=>'Nein'},
':nudelholz:'=>{'url'=>'./smilies/wife.gif', 'title'=>'Nudelholz'},
':ohnmacht:'=>{'url'=>'./smilies/ohnmacht.gif', 'title'=>'Ohnmacht'},
':oho:'=>{'url'=>'./smilies/oho.gif', 'title'=>'Oho'},
':oma:'=>{'url'=>'./smilies/oma.gif', 'title'=>'Oma'},
':omg:'=>{'url'=>'./smilies/omg1.gif', 'title'=>'OMG'},
':oops:'=>{'url'=>'./smilies/icon_redface.gif', 'title'=>'Verlegen'},
':opa:'=>{'url'=>'./smilies/wiseopa.gif', 'title'=>'Kluger Opa'},
':-o'=>{'url'=>'./smilies/icon_surprised.gif', 'title'=>'Überrascht'},
':o'=>{'url'=>'./smilies/icon_surprised.gif', 'title'=>'Überrascht'},
':peitsche:'=>{'url'=>'./smilies/peitsche.gif', 'title'=>'Peitsche'},
':pfarrer:'=>{'url'=>'./smilies/pfarrer.gif', 'title'=>'Pfarrer'},
':pfeif:'=>{'url'=>'./smilies/pfeif.gif', 'title'=>'Pfeif'},
':poempel:'=>{'url'=>'./smilies/poempel.gif', 'title'=>'Pömpel'},
':prost:'=>{'url'=>'./smilies/prost.gif', 'title'=>'Prost'},
':putzen2:'=>{'url'=>'./smilies/putzenm.gif', 'title'=>'Putzen 2'},
':regen:'=>{'url'=>'./smilies/regen.gif', 'title'=>'Regen'},
':reporter:'=>{'url'=>'./smilies/reporter.gif', 'title'=>'Reporter'},
':roll:'=>{'url'=>'./smilies/icon_rolleyes.gif', 'title'=>'Mit den Augen rollen'},
':sadman:'=>{'url'=>'./smilies/traurigmann.gif', 'title'=>'Traurig Mann'},
':sadwoman:'=>{'url'=>'./smilies/traurig.gif', 'title'=>'Traurig Frau'},
':schlafen:'=>{'url'=>'./smilies/sleeping.gif', 'title'=>'Schlafen'},
':schreck:'=>{'url'=>'./smilies/schreck.gif', 'title'=>'Schreck'},
':schrei:'=>{'url'=>'./smilies/schrei1.gif', 'title'=>'Schrei'},
':schuechtern:'=>{'url'=>'./smilies/schuechtern2.gif', 'title'=>'Schüchtern'},
':schwarzekatze:'=>{'url'=>'./smilies/schwarzekatze.gif', 'title'=>'Schwarze Katze'},
':schwitzen:'=>{'url'=>'./smilies/schwitzen.gif', 'title'=>'Schwitzen'},
':shock:'=>{'url'=>'./smilies/icon_eek.gif', 'title'=>'Geschockt'},
':shylove:'=>{'url'=>'./smilies/shylove.gif', 'title'=>'Shy Love'},
':shy:'=>{'url'=>'./smilies/schuechtern3.gif', 'title'=>'Shy'},
':singen:'=>{'url'=>'./smilies/singen.gif', 'title'=>'Singen'},
':specht:'=>{'url'=>'./smilies/specht1.gif', 'title'=>'Specht'},
':spiegel:'=>{'url'=>'./smilies/spiegel.gif', 'title'=>'Spiegel'},
':stricken:'=>{'url'=>'./smilies/stricken.gif', 'title'=>'Stricken'},
':surprise:'=>{'url'=>'./smilies/surprise.gif', 'title'=>'Surprise'},
':tanzen2:'=>{'url'=>'./smilies/tanz2.gif', 'title'=>'Tanzen 2'},
':tanzen:'=>{'url'=>'./smilies/tanzen.gif', 'title'=>'Tanzen'},
':tea:'=>{'url'=>'./smilies/tea.gif', 'title'=>'Tee'},
':telefon:'=>{'url'=>'./smilies/telefon.gif', 'title'=>'Telefon'},
':tippen:'=>{'url'=>'./smilies/tippen.gif', 'title'=>'Tippen'},
':traurig2:'=>{'url'=>'./smilies/crying_3.gif', 'title'=>'Traurig Schneuz'},
':traurig:'=>{'url'=>'./smilies/sad.gif', 'title'=>'Traurig'},
':twisted:'=>{'url'=>'./smilies/icon_twisted.gif', 'title'=>'Sehr böse'},
':umarmung2:'=>{'url'=>'./smilies/umarmung4.gif', 'title'=>'Umarmung 2'},
':umarmungfrech:'=>{'url'=>'./smilies/umarmung3.gif', 'title'=>'Umarmung frech'},
':umarmung:'=>{'url'=>'./smilies/umarmung1.gif', 'title'=>'Umarmung'},
':upps:'=>{'url'=>'./smilies/zensur.gif', 'title'=>'Upps'},
':-?'=>{'url'=>'./smilies/icon_confused.gif', 'title'=>'Geschockt'},
':?'=>{'url'=>'./smilies/icon_confused.gif', 'title'=>'Geschockt'},
':!:'=>{'url'=>'./smilies/icon_exclaim.gif', 'title'=>'Ausrufezeichen'},
':-|'=>{'url'=>'./smilies/icon_neutral.gif', 'title'=>'Neutral'},
':?:'=>{'url'=>'./smilies/icon_question.gif', 'title'=>'Frage'},
':('=>{'url'=>'./smilies/icon_sad.gif', 'title'=>'Traurig'},
':)'=>{'url'=>'./smilies/icon_smile.gif', 'title'=>'Smilie'},
';)'=>{'url'=>'./smilies/icon_wink.gif', 'title'=>'Winken'},
':verwirrt:'=>{'url'=>'./smilies/verwirrt.gif', 'title'=>'Verwirrt'},
':vielglueck:'=>{'url'=>'./smilies/vielglueck.gif', 'title'=>'Viel Glück'},
':wahrsagerin:'=>{'url'=>'./smilies/wahrsager.gif', 'title'=>'Wahrsagerin'},
':whiteflag:'=>{'url'=>'./smilies/giveup.gif', 'title'=>'White flag'},
':winken:'=>{'url'=>'./smilies/hand.gif', 'title'=>'Winken'},
':wink:'=>{'url'=>'./smilies/icon_wink.gif', 'title'=>'Winken'},
':wuetend:'=>{'url'=>'./smilies/wuetend2.gif', 'title'=>'Wütend'},
':yes:'=>{'url'=>'./smilies/yes.gif', 'title'=>'Ja'},
':zaehneputzen:'=>{'url'=>'./smilies/zaehneputzen.gif', 'title'=>'Zähneputzen'},
':zugabe:'=>{'url'=>'./smilies/zugabe.gif', 'title'=>'Zugabe'}
);
my $candidate=shift;
if(defined($smilie_data{$candidate}))
{
return '<img src="'.$smilie_data{$candidate}->{'url'}.'" title="'.$smilie_data{$candidate}->{'title'}.'">';
}
else
{
return $candidate;
}
}