Browse Source

First cvs checkin of pisg, this is 0.16pre5

Morten Brix Pedersen 25 năm trước cách đây
commit
9ecb73b686
10 tập tin đã thay đổi với 2787 bổ sung và 0 xóa
  1. 340 0
      COPYING
  2. 43 0
      CREDITS
  3. 172 0
      Changelog
  4. 39 0
      FORMATS
  5. 29 0
      Makefile
  6. 78 0
      README
  7. BIN
      gfx/pipe-blue.png
  8. BIN
      gfx/pipe-purple.png
  9. 2063 0
      pisg.pl
  10. 23 0
      users.cfg

+ 340 - 0
COPYING

@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

+ 43 - 0
CREDITS

@@ -0,0 +1,43 @@
+CREDITS goes to...
+--
+
+ * Stefan aka Flyghost <flyghost@ghosthouse.cx>
+   | Code contribution and fixes
+ * P. Gnodde aka Shogun <peter@pcswebdesign.nl>
+   | Lots of feature enhancements and code cleanups, thanks!
+ * Steve Tibold aka T0mcat <t0mcat@chat.lu>
+   | Tons of ideas
+ * Markus Geiger <mail@markus-geiger.net>
+   | Alias/nick replacement
+ * Joakim Sandström <joose@nalle.puh.org>
+   | Ideas and bugfixes
+ * Ryan Ryttie <kesu@kesuki.dyndns.org>
+   | xchat logfiles request/help
+ * Martin Poulsen <xact@mail.dk>
+   | Suggestions and moral support
+ * Vetinary <iranitev@gmx.net>
+   | Lots of feature additions and code cleanups, thanks!
+ * Laocius <irish@rook.lug.usf.edu>
+   | The features 'most used words' and 'most referenced nicks'
+ * Luca (eni) <adsl.help@dada.it>
+   | Reported a very small, but stupid bug with the xchat fileformat :)
+ * kiss dess <duvet@mail.com>
+   | Found a few minor bugs. And added replace_links()
+ * Sam Bingner <sam@dhs.org>
+   | Added mIRC color stripping, eggdrop log format and nick/alias tracking.
+ * S. William Schulz <sws@astrum.com>
+   | Better commandline arguments, --help and fixed indenting problems with
+     xchat logs, also fixed a critical thing with --format
+ * Tim Hunter <tim@quic.net>
+   | 'shortest lines', 'most fouls', 'pageheader' and more. 
+ * Winfried (Doomshammer) <doomy@doomshammer.yi.org>
+   | grufti log format help, German translation and a ton of other things.
+ * Kim Ingemann (Tuxi) <tuxi@tuxi.dk>
+   | Danish translation
+ * Molator <molator@ifrance.com>
+   | French translation
+ * Jamie Cheetham <jamie@softham.co.uk>
+   | Eggdrop 'last messaged repeated x times' fix
+
+I probably forgot a lot of people here, _PLEASE_ notify me if I left you
+out.. because I have a bad habit of forgetting to maintain this list :)

+ 172 - 0
Changelog

@@ -0,0 +1,172 @@
+pisg (0.16) - 
+
+   * Danish translation (thanks to Kim Ingemann aka Tuxi)
+   * Fixed some unitialized values which came up in some situations
+   * Eggdrop log had 'last message repeated x times' - pisg didn't understood
+     this before, not it does! :) (thanks to Jamie Cheetham).
+   * French translation (thanks Molator)
+   * Also use the timeoffset in 'most active times'
+   * Started to distribute various scripts with pisg:
+     - 'dropegg.pl' - takes a bunch of eggdrop logs and converts them into
+       mIRC format (Emil Mikulic <darkmoon7@optushome.com.au>)
+     - 'addalias.pl' - webinterface for allowing users to add their own stuff
+       to the users.cfg file. (Doomshammer <doomshammer@doomshammer.yi.org>)
+     - 'crontab' - small file explaining how to use pisg with crontab
+     
+
+pisg (0.15) - Fri, July 20th 2001
+
+   * Fixed bug where grufti-logfiles wouldn't ignore oppers.
+   * Fixed pisg dying when there is no action and normallines in logfile
+   * Also strip mIRC bold and underline codes
+   * Nick tracking code optimized
+   * Eggdrop logs were logging server mode changes.. fixed.
+   * The 'active times' bars should now be more logical and always have a max
+     height of 100px
+   * Added new users.cfg file which obsoletes aliasfile and ignorefile
+   * New feature: ability to add user pictures to stats
+   * New feature: ability to add user links to stats (homepages, e-mail)
+   * Made a big code change so it now supports somekind of template system so
+     pisg can support more than one language
+   * German language support added (thanks Winfried aka Doomshammer)
+   * Now shows the time when the topic was changed in 'Latest topics'.
+   * Thousands of small things which I forgot.
+   * Removed the documentation for unnamed arguments on the commandline..
+     people should start using the named arguments (--logfile, -f etc.)
+
+pisg (0.14) - Mon, July 16th 2001
+
+   * 'Most referenced nicks' and 'most used words' now ignores the nicks in
+     ignore.txt
+   * 'Most used words' now ignores nicks
+   * Fixed critical bug; --format wasn't working. (thanks S. William Schulz)
+
+pisg (0.13) - Sun, July 15th 2001 
+
+   * New feature: Now supports 'grufti' (bot) logfiles.
+   * New feature: Now supports 'bxlog' (logging script for bitchx) logfiles
+   * Improved some debugging
+   * Cleaned up a lot of code, so it's structured better - less global
+     variables and everything is now in subs, also found some variables which
+     weren't used.. so it should be more correct and more efficient now
+   * Fix up some speed in the kicking stats... it was doing the same grep twice
+   * Fixed a stupid bug which caused pisg to die if the alias.txt wasn't
+     there.
+   * Really fix typo, sherif -> sheriff
+   * Added a FORMATS file explaining the supported fileformats, and updated
+     the README accordingly
+   * Added a new feature where one can specify a log directory, and then pisg
+     will run through all the files in the directory and combine them into one
+     big html file.
+   * -d on commandline now specifies logdir instead of debugging (removed)
+   * Also added --aliasfile and --ignorefile as commandline options.
+
+pisg (0.12) - Thu, July 12th 2001 
+
+   * Strip mIRC color codes (Sam Bingner)
+   * New feature: Support eggdrop logformats (Sam Bingner)
+   * Track nicks/aliases dynamically [as an option] (Sam Bingner)
+   * Added better commandline arguments (like --channel=#blah) and pisg now
+     has a cool usage --help option. (thanks S. William Schulz)
+   * xchat logformats which are indented should now be supported
+   * New feature: 'most fouls' (bad language)
+   * New feature: 'shortest lines' (as opposite to longest lines)
+   * Added $timeoffset so people can get times in their own time zones. 
+   * It's now possible to define a pageheader file which can be included on
+     the HTML page, with $pageheader
+   * Changed 'no topics' text 
+
+pisg (0.11) - Fri, June 29th 2001
+   * Before http:// didn't get counted as a smiley, now "all" protocols
+     doesn't get counted as smilies (ftp://, gohper://).
+   * Fixed typo, sherif -> sheriff
+   * Changed the appearence of 'most ref nicks/words', email me if you don't
+     like it.. :)
+   * Also add some fade stuff to the 'most active nicks'
+   * Topic history variable wasn't used, fixed.
+   * Replace & with &amp;
+   * Replace URLs and e-mail addys in 'random quote' and 'last topic' to HTML
+     hrefs
+   * Some other small changes which I forgot
+   
+pisg (0.10) - Fri, June 22th 2001
+   * The 'Im going on vacation'-release. Fixes very small things.
+   * Small bug in counting kicker/kicked person when using xchat - fixed.
+   * Fixed a bug where some lines didn't catch up the alias for a nick
+   * Join counting was broken.. fixed.
+   * Use 'px' to define font sizes instead of 'pt' in CSS.
+   * Fix some colors in the page which couldn't get set by variables in the
+     top
+   * Other small changes
+
+pisg (0.09) - Tue, June 5th 2001
+   * Some small improvements in the count of sadfaces/smilies
+   * New features:
+     - 'most used words'
+     - 'most referenced nick'
+   * Rewrote a LOT of the format dependent code, now it's very easy adding
+     new IRC log formats
+   * Fixed random bugs here and there
+   * The usual small, nice code enhancements.
+
+pisg (0.08) - Sat, April 14th 2001
+
+   * Documentation updates (remember to up the version number :))
+   * A lot of nice changes from Vetinary <iranitev@gmx.net>:
+     - 'op/deop stats'
+     - 'join stats'
+     - 'action counting'
+     - 'counting of :(, :-('
+   * use strict;
+
+pisg (0.07) - Sun, March 11th 2001
+
+   * New feature: Now supports xchat logfiles <:)
+   * Should render pages 100% HTML 4.01/CSS2 compatible now
+   * Some minor code cleanups (use strict; soon)
+
+pisg (0.06) - Wed, February 21th 2001
+
+   * Even more workarounds for small logfiles
+   * More customization options
+   * Now shows processtime
+   * You can now specify nicks to ignore in the logfile (bots etc)
+   * Now supports commandline arguments (./pisg.pl chan logfile outputfile)
+   * Some speedup improvements
+   * A bit more HTML 4 compliant :)
+
+pisg (0.05) - Thu, February 15th 2001
+
+    * New features:
+      - 'Runners-up to Big numbers'
+      - 'Latest topics'
+    * More fixes for small logfiles
+    * Enhanced 'Most active times' 
+    * Speedup improvements
+    * Added CREDITS file to the nice people who have contributed :)
+
+pisg (0.04) - Fri, January 26th 2001
+
+    * New features:
+      - 'the person that smiled the most'
+      - 'can now connect nicks together through alias file'
+      - 'person that wrote the most monologues (5 or more lines in a row)'
+    * Fixes for small logfiles
+    * Restrutured code
+    * More customization through variables in the top of the script
+
+pisg (0.03) - Thu, January 11rd 2001
+    
+    * Code cleanups
+    * New features:
+      - 'the op who kicked most'
+      - 'the one who got kicked the most'
+      - 'longest lines'
+    * Highlight most active hour
+    * New images, 'pipe-blue.png' and 'pipe-purple.png', removed 'pipe3v.gif'
+    * Updated README
+    * Added Changes file
+
+pisg (0.02) - Wed, January 3rd 2001
+    
+    * Initial release.

+ 39 - 0
FORMATS

@@ -0,0 +1,39 @@
+FORMATS - supported IRC logfile formats by pisg
+------------------
+
+pisg supports a variety of logfile formats, these are:
+
+* mIRC:
+  - A client for Windows (www.mirc.com)
+  
+  - If you want to create mIRC logfiles with your eggdrop, you can get some TCL
+    which does it all for you (also logrotating and much more).
+
+    MEL (mIRCStats Eggdrop Logger) can be downloaded from the following URL:
+
+    http://mel.angelbears.org/
+    
+  - In order to use this, format must be set to 'mIRC'.
+
+* X-Chat:
+  -  A client for Linux (www.xchat.org)
+    
+  -  In order to use this, format must be set to 'xchat'.
+
+* Eggdrop:
+  - A cool and advanced IRC bot for Linux (www.eggheads.org)
+
+  - In order to use this, format must be set to 'eggdrop'.
+     
+* Grufti:
+  - A bot which I'm not really familiar with (www.grufti.com)
+  - The author has actually made something like pisg called 'gruftistats',
+    but someone mailed me and told me that it hadn't been maintained for
+    over a year, and it had some problems..
+
+  - In order to use this, format must be set to 'grufti'
+
+* bxlog:
+  - A logging script for BitchX (console client - see scripts.bitchx.com)
+   
+  - In order to use this, format must be set to 'bxlog'

+ 29 - 0
Makefile

@@ -0,0 +1,29 @@
+# Simple Makefile to make new releases of pisg (newreleases dir must exist)
+
+VERSION = 0.16
+
+DIRNAME = pisg-$(VERSION)
+
+TARFILE = pisg-$(VERSION).tar.gz
+ZIPFILE = pisg-$(VERSION).zip
+
+pisg:
+	mkdir $(DIRNAME)
+	cp pisg.pl $(DIRNAME)
+	cp CREDITS $(DIRNAME)
+	cp gfx/pipe-purple.png $(DIRNAME)
+	cp gfx/pipe-blue.png $(DIRNAME)
+	cp COPYING $(DIRNAME)
+	cp users.cfg $(DIRNAME)
+	cp README $(DIRNAME)
+	cp FORMATS $(DIRNAME)
+	cp Changelog $(DIRNAME)
+	cp -r scripts $(DIRNAME)
+	tar zcfv newrelease/pisg-$(VERSION).tar.gz $(DIRNAME)
+	zip -r pisg $(DIRNAME)
+	mv pisg.zip newrelease/$(ZIPFILE)
+	mv $(DIRNAME) newrelease
+clean:
+	rm -r newrelease/$(DIRNAME)
+	rm newrelease/$(TARFILE)
+	rm newrelease/$(ZIPFILE)

+ 78 - 0
README

@@ -0,0 +1,78 @@
+README, pisg - by me and others (see CREDITS file)
+------------------
+pisg - Perl IRC Statistics Generator, is a Perl script which takes IRC
+logfiles and makes nice looking stats of them, which can be amusing to show
+for the users of your channel.
+
+The supported logfile formats is explained in the FORMATS file included with
+this distribution.
+
+If you don't have access to any of the clients/bots or you use some other
+IRC client/bot which isn't supported, then it's fairly easy to add support
+for a new IRC log format. Take a look at the source code or send me an
+e-mail with the logfile format.
+
+SETTING UP PISG
+---------------
+It's quite simple to set up pisg. You have 2 choices:
+
+ * Set settings from commandline (try pisg.pl --help)
+ * Set settings from inside the script
+
+The most important this is the name of the logfile, the maintainers name,
+and then the format of your logfile.
+
+You cannot set everything up on the commandline, only the important things.
+
+The 'users.cfg' file control a lot of things, like connecting nicks
+together, ignoring nicks in the stats, adding user pictures etc. There is
+more documentation in the users.cfg file.
+
+NOTE: The stats will look best with a logfile which is a least a day-long.
+Some stats (like smilies, exclamation marks etc) doesn't get counted before
+a special amount of time.
+
+Also, pisg supports multiple languages so you're text will be in your own
+language; English, German, Danish and French is supported at the moment and
+can be set with the $lang variable in the script.
+
+You're welcome to translate pisg to your own language, it's not very hard,
+look in the bottom of pisg.pl!
+
+RUNNING PISG
+------------
+If you have setup everything inside the script, then you just need to run
+it. If you're on a Linux/Unix system, these steps should do the work:
+
+    $ chmod +x pisg.pl
+    $ ./pisg.pl
+
+If you have more than one channel, then you can also tell it to pisg on the
+commandline, the syntax is explained when doing:
+
+    $ ./pisg.pl --help
+
+For example the following would work:
+
+    $ ./pisg.pl -c \#channel -l logfile.log -f mIRC -o index.html
+    
+Try it out.
+
+Setting settings on commandline, will override the relevant settings in the
+script.
+
+CONTACT INFORMATION
+-------------------
+If you have any problems with setting up or running this script, please
+don't hesitate to contact me. I can either be via e-mail (morten@wtf.dk) or
+via ICQ (6681780).
+
+The pisg homepage is located at http://www.wtf.dk/hp/index.php?page=pisg.
+
+If you use this script, and have a permanent URL for the location of your
+generated stats, I would be delighted if you would send the url to me.  Just
+to see how many people uses it, and so I can link to it from my pisg page.
+
+Have fun :)
+
+ - Morten 'LostStar' Brix Pedersen <morten@wtf.dk>

BIN
gfx/pipe-blue.png


BIN
gfx/pipe-purple.png


+ 2063 - 0
pisg.pl

@@ -0,0 +1,2063 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Getopt::Long;
+
+# pisg - Perl IRC Statistics Generator
+#
+# Copyright (C) 2001  <Morten 'LostStar' Brix Pedersen> - morten@wtf.dk
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+my ($channel, $logfile, $format, $network, $outputfile, $maintainer,
+$pagehead, $usersfile, $imagepath, $logdir, $lang, $bgcolor, $text, $hbgcolor,
+$hcolor, $hicell, $hicell2, $tdcolor, $tdtop, $link, $vlink, $hlink, $headline,
+$rankc, $minquote, $maxquote, $activenicks, $activenicks2, $topichistory,
+$nicktracking, $timeoffset, $version, $debug, $debugfile);
+
+# Values that _MUST_ be set below (unless you pass them on commandline)
+$channel = "#channel";		# The name of your channel.
+$logfile = "channel.log";	# The exact filename of the logfile
+$format = "mIRC";		# logfile format. see FORMATS file
+$network = "SomeIRCNet";	# Network the channels is using.
+$outputfile = "index.html";	# The name of the html file to be generated
+$maintainer = "MAINTAINER";	# The maintainer or bot which makes the logfile
+$pagehead = "none";		# Some 'page header' file which you want to
+				# include in top of the stats
+
+$usersfile = "users.cfg";	# Path to users config file (aliases, ignores,
+				# pics and more, see users.cfg for examples)
+
+$imagepath = "";		# If your user pictures is located
+				# some special directory, set the path here.
+
+$logdir = "";			# If you specify a path to a dir here, then
+				# pisg will take that dir, and parse ALL
+				# logfiles in it, and create 1 HTML file
+				# from it
+
+$lang = 'EN';			# Language to use, EN | DE | DK | FR
+
+# Here you can set the colors for your stats page..
+$bgcolor = "#dedeee";		# Background color of the page
+$text = "black";		# Normal text color
+$hbgcolor = "#666699";		# Background color in headlines
+$hcolor = "white";		# Text color in headline
+$hicell = "#BABADD";		# Background color in highlighted cells
+$hicell2 = "#CCCCCC";		# Background color in highlighted cells
+$tdcolor = "black";		# Color of text in tables
+$tdtop = "#C8C8DD";		# Top color in some tables.
+$link = "#0b407a";		# Color of links
+$vlink = "#0b407a";		# Color of visited links
+$hlink = "#0b407a";		# Color of hovered links
+$headline = "#000000";		# Border color of headlines
+$rankc = "#CCCCCC";             # Colors of 'ranks' (1,2,3,4)
+
+# Other things that you might set, but not everyone cares about them
+$minquote = "25";		# Minimal value of letters for a random quote
+$maxquote = "65";		# Maximum value of letters for a random quote
+$activenicks = "25";		# Number of nicks to show in the 'top 25'
+$activenicks2 = "30";		# Nicks to show in 'these didnt make it...'
+$topichistory = "3";		# How many topics to show in 'latest topics'
+$nicktracking = 0;		# Track nickchanges and create aliases (can
+				# be slow, so it's disabled by default)
+
+$timeoffset = "+0";		# A time offset on the stats page - if your
+				# country has a different timezone than the
+				# machine where the stats are being
+				# generated, then for example do +1
+				# to add 1 hour to the time
+
+# You shouldn't care about anything below this point
+$debug = 0;			# 0 = Debugging off, 1 = Debugging on
+$debugfile = "debug.log";	# Path to debug file(must be set if $debug == 1)
+$version = "v0.16pre5";
+
+my ($lines, $kicked, $gotkicked, $smile, $longlines, $time, $timestamp, %alias,
+$normalline, $actionline, $thirdline, @ignore, $line, $processtime, @topics,
+%monologue, %kicked, %gotkick, %line, %length, %qpercent, %lpercent, %sadface,
+%smile, $nicks, %longlines, %mono, %times, %question, %loud, $totallength,
+%gaveop, %tookop, %joins, %actions, %sayings, %wordcount, %lastused,
+$colorcode, $boldcode, $underlinecode, %gotban, %setban, %foul, $days,
+$oldtime, $lastline, $actions, $normals, %userpics, %userlinks, %T,
+$repeated, $lastnormal);
+
+sub main
+{
+    init_pisg();        # Init commandline arguments and other things
+    init_lineformats(); # Attempt to set line formats in compliance with user specification (--format)
+
+    init_users_config();        # Init users config. (Aliases, ignores etc.)
+    init_debug();       # Init the debugging file
+
+    if ($logdir) {
+        parse_dir();            # Run through all logfiles in dir
+    } else {
+        parse_file($logfile);   # Run through the whole logfile
+    }
+
+    create_html();      # Create the HTML
+                        # (look here if you want to remove some of the
+                        # stats which you don't care about)
+
+    close_debug();      # Close the debugging file
+
+    print "\nFile was parsed succesfully in $processtime on $time.\n";
+}
+
+sub init_pisg
+{
+    print "pisg $version - Perl IRC Statistics Generator\n\n";
+
+    get_cmdlineoptions();
+
+    $timestamp = time;
+
+    if ($timeoffset =~ /\+(\d)/) {
+        # We must plus some hours to the time
+        $timestamp += 3600 * $1; # 3600 seconds per hour
+
+    } elsif ($timeoffset =~ /-(\d)/) {
+        # We must remove some hours from the time
+        $timestamp -= 3600 * $1; # 3600 seconds per hour
+    }
+
+    # Set useful values.
+    $days = 1;
+    $oldtime = "00";
+    $lastline = "";
+    $actions = "0";
+    $normals = "0";
+    $colorcode = chr(3);
+    $boldcode = chr(2);
+    $underlinecode = chr(31);
+    $time = localtime($timestamp);
+    $repeated = 0;
+    $lastnormal = "";
+
+    print "Statistics for channel $channel \@ $network by $maintainer\n\n";
+    print "Using language template: $lang\n\n" if ($lang ne 'EN');
+
+}
+
+sub init_lineformats {
+
+    # These are the regular expressions which matches the lines in the logfile,
+    # and looks different if it's xchat, mIRC or whatever.
+    # If you want to add support for a new format - you first have to add the
+    # regex here, and then you also have to modify the parse subroutines called
+    # 'parse_normalline()', 'parse_actionline()' and 'parse_thirdline()'
+
+    if ($format eq 'xchat') {
+        $normalline = '^(\d+):\d+:\d+ <([^>]+)>\s+(.*)';
+        $actionline = '^(\d+):\d+:\d+ \*\s+(\S+) (.*)';
+        $thirdline = '^(\d+):(\d+):\d+ .--\s+(\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (.*)';
+    } elsif ($format eq 'mIRC') {
+        $normalline = '^\[(\d+):\d+\] <([^>]+)> (.*)';
+        $actionline = '^\[(\d+):\d+\] \* (\S+) (.*)';
+        $thirdline = '^\[(\d+):(\d+)\] \*\*\* (\S+) (\S+) (\S+) (\S+) (\S+)(.*)';
+    } elsif ($format eq 'eggdrop') {
+        $normalline = '^\[(\d+):\d+\] <([^>]+)> (.*)';
+        $actionline = '^\[(\d+):\d+\] Action: (\S+) (.*)';
+        $thirdline = '^\[(\d+):(\d+)\] (\S+) (\S+) (\S+) (\S+)(.*)';
+    } elsif ($format eq 'bxlog') {
+        $normalline = '^\[\d+ \w+\/(\d+):\d+\] <([^>]+)> (.*)';
+        $actionline = '^\[\d+ \w+\/(\d+):\d+\] \* (\S+) (.*)';
+        $thirdline = '^\[\d+ \w+\/(\d+):(\d+)\] \S (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (\S+) (.*)';
+    } elsif ($format eq 'grufti') {
+        $normalline = '^\[(\d+):\d+\] <([^>]+)> (.*)';
+        $actionline = '^\[(\d+):\d+\] \* (\S+) (.*)';
+        $thirdline = '^\[(\d+):(\d+)\] (\S+) (\S+) (\S+) (\S+) (\S+) (\S+)(.*)';
+    } else {
+        die("Logfile format not supported, check \$format setting.\n");
+    }
+
+}
+
+sub init_users_config
+{
+
+    if (open(USERS, $usersfile)) {
+
+        my $lineno = 0;
+        while (<USERS>)
+        {
+            $lineno++;
+            my $line = $_;
+            next if /^#/;
+
+            if ($line =~ /<user.*>/) {
+                my $nick;
+
+                if ($line =~ /nick="([^"]+)"/) {
+                    $nick = $1;
+                } else {
+                    print STDERR "Warning: no nick specified in $usersfile on line $lineno\n";
+                    next;
+                }
+
+                if ($line =~ /alias="([^"]+)"/) {
+                    my @thisalias = split(/\s+/, $1);
+                    push(@{ $alias{$nick} }, @thisalias);
+                }
+
+                if ($line =~ /pic="([^"]+)"/) {
+                    $userpics{$nick} = $1;
+                }
+
+                if ($line =~ /link="([^"]+)"/) {
+                    $userlinks{$nick} = $1;
+                }
+
+                if ($line =~ /ignore="Y"/i) {
+                    push(@ignore, $nick);
+                }
+
+            }
+
+        }
+
+        close(USERS);
+    }
+
+}
+
+sub init_debug
+{
+    if ($debug) {
+        print "[ Debugging => $debugfile ]\n";
+        open(DEBUG,"> $debugfile") or print STDERR "$0: Unable to open debug
+        file($debugfile): $!\n";
+        debug("*** pisg debug file for $logfile\n");
+
+    }
+}
+
+sub parse_dir
+{
+    print "Going into $logdir and parsing all files there...\n\n";
+    my $files = `ls $logdir`;
+
+    my @filesarray = split(/\n/, $files);
+
+    # Add trailing slash when it's not there..
+    if (substr($logdir, -1) ne '/') {
+        $logdir =~ s/(.*)/$1\//;
+    }
+
+    foreach my $file (@filesarray) {
+        $file = $logdir . $file;
+        parse_file($file);
+    }
+
+}
+
+sub parse_file
+{
+    my $file = shift;
+
+    # This parses the file..
+    print "Analyzing log($file) in '$format' format...\n";
+
+    open (LOGFILE, $file) or die("$0: Unable to open logfile($file): $!\n");
+
+    while($line = <LOGFILE>) {
+        $lines++; # Increment number of lines.
+
+        # Strip mIRC color codes
+        $line =~ s/$colorcode\d{1,2},\d{1,2}//g;
+        $line =~ s/$colorcode\d{0,2}//g;
+        # Strip mIRC bold and underline codes
+        $line =~ s/[$boldcode$underlinecode]//g;
+
+        my $hashref;
+
+        # Match normal lines.
+        if ($hashref = parse_normalline($line)) {
+
+            my ($hour, $nick, $saying, $i);
+
+            for ($i = 0; $i <= $repeated; $i++) {
+
+                if ($i > 0) {
+                    $hashref = parse_normalline($lastnormal);
+                    $lines++; #Increment number of lines for repeated lines
+                }
+
+
+                $hour = $hashref->{hour};
+                $nick = find_alias($hashref->{nick});
+                $saying = $hashref->{saying};
+
+                # Timestamp collecting
+                $times{$hour}++;
+
+                unless (grep /^\Q$nick\E$/i, @ignore) {
+                    $normals++;
+                    $line{$nick}++;
+
+                    # Count up monologues
+
+                    if ($lastline eq $nick) {
+                        $mono{$nick}++;
+
+                        if ($mono{$nick} == "5") {
+                            $monologue{$nick}++;
+                            $mono{$nick} = 0;
+                        }
+                    } else {
+                        $mono{$nick} = 0;
+                    }
+
+                    $lastline = $nick;
+
+                    my $l = length($saying);
+
+                    if ($l > $minquote && $l < $maxquote) {
+                        $saying = htmlentities($saying);
+
+                        # Creates $hash{nick}[n] - a hash of an array.
+                        push (@{ $sayings{$nick} }, $saying);
+                        $longlines{$nick}++;
+                    }
+
+                    $question{$nick}++
+                        if ($saying =~ /\?/);
+
+                    $loud{$nick}++
+                        if ($saying =~ /!/);
+
+                    $foul{$nick}++
+                        if ($saying =~ /ass|fuck|bitch|shit|scheisse|scheiße|kacke|arsch|ficker|ficken|schlampe/);
+
+                    # Who smiles the most?
+                    # A regex matching al lot of smilies
+
+                    $smile{$nick}++
+                        if ($saying =~ /[8;:=][ ^-o]?[)pPD}\]>]/);
+                    $sadface{$nick}++
+                        if ($saying =~ /[8;:=][ ^-]?[\(\[\\\/{]/);
+
+                    # Don't count http:// as a :/ face
+                    $sadface{$nick}--
+                        if ($saying =~ /\w+:\/\//);
+
+                    foreach my $word (split(/[\s,!?.:;)(]+/, $saying)) {
+                        # remove uninteresting words
+                        next unless (length($word) > 5);
+                        # ignore contractions
+                        next if ($word =~ m/'..?$/);
+
+                        $wordcount{$word}++ unless (grep /^\Q$word\E$/i, @ignore);
+                        $lastused{$word} = $nick;
+                    }
+
+
+                    $length{$nick} += $l;
+                    $totallength += $l;
+                }
+            }
+            $lastnormal = $line;
+            $repeated = 0;
+        }
+
+        # Match action lines.
+        elsif ($hashref = parse_actionline($line)) {
+
+            my ($hour, $nick, $saying);
+
+            $hour = $hashref->{hour};
+            $nick = find_alias($hashref->{nick});
+            $saying = $hashref->{saying};
+
+            # Timestamp collecting
+            $times{$hour}++;
+
+            unless (grep /^\Q$nick\E$/i, @ignore) {
+                $actions++;
+                $line{$nick}++;
+
+                my $len = length($saying);
+                $length{$nick} += $len;
+                $totallength += $len;
+            }
+        }
+
+        # Match *** lines.
+        elsif ($hashref = parse_thirdline($line)) {
+
+            my ($hour, $min, $nick, $kicker, $newtopic, $newmode, $newjoin, $newnick);
+
+            $hour = $hashref->{hour};
+            $min = $hashref->{min};
+            $nick = find_alias($hashref->{nick});
+            $kicker = $hashref->{kicker};
+            $newtopic = $hashref->{newtopic};
+            $newmode = $hashref->{newmode};
+            $newjoin = $hashref->{newjoin};
+            $newnick = $hashref->{newnick};
+
+            # Timestamp collecting
+            $times{$hour}++;
+
+            unless (grep /^\Q$nick\E$/i, @ignore) {
+
+                if (defined($kicker)) {
+                    unless (grep /^\Q$kicker\E$/i, @ignore) {
+                        $gotkick{$nick}++;
+                        $kicked{$kicker}++;
+                    }
+                } elsif (defined($newtopic)) {
+                    my $tcount = @topics;
+
+                    $topics[$tcount]{topic} = htmlentities($newtopic);
+                    $topics[$tcount]{nick} = $nick;
+                    $topics[$tcount]{hour} = $hour;
+                    $topics[$tcount]{min} = $min;
+
+                    # Strip off the quotes (')
+                    $topics[$tcount]{topic} =~ s/^\'(.*)\'$/$1/;
+                } elsif (defined($newmode)) {
+                    my @opchange = opchanges($newmode);
+                    unless (exists $gaveop{$nick}) { $gaveop{$nick} = 0 }
+                    $gaveop{$nick} += $opchange[0] if $opchange[0];
+                    $tookop{$nick} += $opchange[1] if $opchange[1];
+                } elsif (defined($newjoin)) {
+                    $joins{$nick}++;
+                } elsif (defined($newnick) && ($nicktracking == 1)) {
+                    if (find_alias($newnick) eq $newnick) {
+                        if (defined($alias{$nick}) && !defined($alias{$newnick})) {
+                            push (@{$alias{$nick}}, $newnick);
+                        } elsif (defined($alias{$newnick}) && !defined($alias{$nick})) {
+                            push (@{$alias{$newnick}}, $nick);
+                        } elsif ($nick =~ /Guest/) {
+                            push (@{$alias{$newnick}}, ($newnick, $nick));
+                        } else {
+                            push (@{$alias{$nick}}, ($nick, $newnick));
+                        }
+                    }
+                }
+
+            }
+
+            if ($hour < $oldtime) { $days++ }
+            $oldtime = $hour;
+
+        }
+    }
+
+    close(LOGFILE);
+
+    my ($sec,$min,$hour) = gmtime(time - $^T);
+    $processtime = sprintf("%02d hours, %02d minutes and %02d seconds", $hour,  $min, $sec);
+
+    $nicks = scalar keys %line;
+
+    print "Finished analyzing log, $days days total.\n";
+}
+
+
+# Here is the 3 parse sub routines, their function is to return a hash with
+# the elements in the line. This is where we add the format dependent stuff.
+sub parse_normalline
+{
+    # Parse a normal line - returns a hash with 'hour', 'nick' and 'saying'
+    my $line = shift;
+    my %hash;
+
+    if ($line =~ /$normalline/) {
+        debug("[$lines] Normal: $1 $2 $3");
+
+        if (($format eq 'mIRC') || ($format eq 'xchat') || ($format eq
+        'eggdrop') || ($format eq 'bxlog') || ($format eq 'grufti')) {
+
+            $hash{hour} = $1;
+            $hash{nick} = $2;
+            $hash{saying} = $3;
+
+        } else {
+            die("Format not supported?!\n");
+        }
+
+        return \%hash;
+
+    } else {
+        return;
+    }
+}
+
+sub parse_actionline
+{
+    # Parse an action line - returns a hash with 'hour', 'nick' and 'saying'
+    my $line = shift;
+    my %hash;
+
+    if ($line =~ /$actionline/) {
+        debug("[$lines] Action: $1 $2 $3");
+
+        if (($format eq 'mIRC') || ($format eq 'xchat') || ($format eq
+        'eggdrop') || $format eq 'bxlog' || ($format eq 'grufti')) {
+
+            $hash{hour} = $1;
+            $hash{nick} = $2;
+            $hash{saying} = $3;
+
+        } else {
+            die("Format not supported?!\n");
+        }
+
+        return \%hash;
+
+    } else {
+        return;
+    }
+}
+
+sub parse_thirdline
+{
+    # Parses the 'third' line - (the third line is everything else, like
+    # topic changes, mode changes, kicks, etc.)
+    # parse_thirdline() have to return a hash with the following keys, for
+    # every format:
+    #   hour            - the hour we're in (for timestamp loggin)
+    #   min             - the minute we're in (for timestamp loggin)
+    #   nick            - the nick
+    #   kicker          - the nick which were kicked (if any)
+    #   newtopic        - the new topic (if any)
+    #   newmode         - a deop or an op, must be '+o' or '-o'
+    #   newjoin         - a new nick which has joined the channel
+    #   newnick         - a person has changed nick and this is the new nick
+    my $line = shift;
+    my %hash;
+
+    if ($line =~ /$thirdline/) {
+        if ($7) {
+          debug("[$lines] ***: $1 $2 $3 $4 $5 $6 $7");
+        } else {
+          debug("[$lines] ***: $1 $2 $3 $4 $5 $6");
+        }
+
+        if ($format eq 'mIRC') {
+            $hash{hour} = $1;
+            $hash{min} = $2;
+            $hash{nick} = $3;
+
+            if (($4.$5) eq 'waskicked') {
+                $hash{kicker} = $7;
+
+            } elsif (($4.$5) eq 'changestopic') {
+                $hash{newtopic} = "$7 $8";
+
+            } elsif (($4.$5) eq 'setsmode:') {
+                $hash{newmode} = $6;
+
+            } elsif (($4.$5) eq 'hasjoined') {
+                $hash{newjoin} = $3;
+
+            } elsif (($4.$5) eq 'nowknown') {
+                $hash{newnick} = $8;
+            }
+
+
+        } elsif ($format eq 'xchat') {
+            $hash{hour} = $1;
+            $hash{min} = $2;
+            $hash{nick} = $3;
+
+            if (($4.$5) eq 'haskicked') {
+                $hash{kicker} = $3;
+                $hash{nick} = $6;
+
+            } elsif (($4.$5) eq 'haschanged') {
+                $hash{newtopic} = $9;
+
+            } elsif (($4.$5) eq 'giveschannel') {
+                $hash{newmode} = '+o';
+
+            } elsif (($4.$5) eq 'removeschannel') {
+                $hash{newmode} = '-o';
+
+            } elsif (($5.$6) eq 'hasjoined') {
+                $hash{newjoin} = $1;
+
+            } elsif (($5.$6) eq 'nowknown') {
+                $hash{newnick} = $8;
+            }
+
+        } elsif ($format eq 'eggdrop') {
+            $hash{hour} = $1;
+            $hash{min} = $2;
+            $hash{nick} = $3;
+
+            if (($4.$5) eq 'kickedfrom') {
+                $7 =~ /^ by ([\S]+):.*/;
+                $hash{kicker} = $1;
+
+            } elsif ($3 eq 'Topic') {
+                $7 =~ /^ by ([\S]+)![\S]+: (.*)/;
+                $hash{nick} = $1;
+                $hash{newtopic} = $2;
+
+            } elsif (($4.$5) eq 'modechange') {
+                $hash{newmode} = $6;
+                $7 =~ /^ .+ by ([\S]+)!.*/;
+                $hash{nick} = $1;
+                $hash{newmode} =~ s/^\'//;
+
+            } elsif ($5 eq 'joined') {
+                $hash{newjoin} = $3;
+
+            } elsif (($3.$4) eq 'Nickchange:') {
+                $hash{nick} = $5;
+                $7 =~ /([\S]+)/;
+                $hash{newnick} = $1;
+
+            } elsif (($3.$4.$5) eq 'Lastmessagerepeated') {
+                $repeated = $6;
+            }
+
+        } elsif ($format eq 'bxlog') {
+            $hash{hour} = $1;
+            $hash{min} = $2;
+            $hash{nick} = $3;
+
+            if ($5 eq 'kicked') {
+                $hash{kicker} = $9;
+                $hash{kicker} =~ s/!.*$//;
+
+            } elsif ($3 eq 'Topic') {
+                $hash{nick} = substr($5, 0, -1);
+                $hash{newtopic} = "$6 $7 $8 $9 $10";
+
+            } elsif ($5 eq '[+o') {
+                $hash{newmode} = '+o';
+                $hash{newmode} = substr($6, 0, -1);
+
+            } elsif ($5 eq '[-o') {
+                $hash{newmode} = '-o';
+                $hash{newmode} = substr($6, 0, -1);
+
+            } elsif (($4.$5) eq 'hasjoined') {
+                $hash{newjoin} = $1;
+
+            } elsif (($4.$5) eq 'isknown') {
+                $hash{newnick} = $6;
+            }
+
+            $hash{nick} =~ s/!.*$//;
+
+        } elsif ($format eq 'grufti') {
+            $hash{hour} = $1;
+            $hash{min} = $2;
+            $hash{nick} = $3;
+
+            if ($5 eq 'kicked') {
+                $hash{kicker} = $3;
+                $hash{nick} = $6;
+
+            } elsif (($4.$5) eq 'haschanged') {
+                $hash{newtopic} = $9;
+
+            } elsif (($4.$5) eq 'modechange') {
+                $hash{newmode} = substr($6, 1);
+                $hash{nick} = substr($9, 1);
+
+            } elsif ($5 eq 'joined') {
+                $hash{newjoin} = $1;
+
+            } elsif (($3.$4) eq 'Nickchange') {
+                $hash{nick} = $7;
+                $hash{newnick} = $9;
+            }
+
+        } else {
+            die("Format not supported?!\n");
+        }
+
+        return \%hash;
+
+    } else {
+        return;
+    }
+}
+
+
+sub opchanges
+{
+    my @modes = split(//, $_[0]);
+    my ($mode,$plus)        = ("",0);
+    my ($gaveop,$tookop)    = (0,0);
+
+    foreach (@modes) {
+        if    (/^\+$/) { $plus  = 1; next; }
+        elsif (/^-$/)  { $plus  = 0; next; }
+        else  { next unless /^o$/ }
+
+        if ($plus) { $gaveop++ } else { $tookop++ }
+    }
+
+    my @out = ($gaveop,$tookop);
+    return @out;
+}
+
+
+sub htmlentities
+{
+    my $str = shift;
+
+    $str =~ s/\&/\&amp;/g;
+    $str =~ s/\</\&lt;/g;
+    $str =~ s/\>/\&gt;/g;
+
+    return $str;
+}
+
+sub html
+{
+    my $html = shift;
+    print OUTPUT $html . "\n";
+}
+
+sub template_text
+{
+    # This function is for the homemade template system. It receives a name
+    # of a template and a hash with the fields in the template to update to
+    # its corresponding value
+    my $template = shift;
+    my %hash = @_;
+
+    my $text;
+
+    if (!$T{$lang}{$template}) {
+        # Fall back to English if the language template doesn't exist
+        $text = $T{EN}{$template};
+    }
+
+    $text = $T{$lang}{$template};
+
+    if (!$text) {
+        die("No such template $template\n");
+    }
+
+    foreach my $key (sort keys %hash) {
+        $text =~ s/\[:$key\]/$hash{$key}/;
+        $text =~ s/ü/&uuml;/g;
+        $text =~ s/ö/&ouml;/g;
+        $text =~ s/ä/&auml;/g;
+        $text =~ s/ß/&szlig/g;
+    }
+
+    return $text;
+
+}
+
+
+sub replace_links
+{
+    # Sub to replace urls and e-mail addys to links
+    my $str = shift;
+    my $nick = shift;
+
+    if ($nick) {
+        $str =~ s/(http|https|ftp|telnet|news)(:\/\/[-a-zA-Z0-9_]+.[-a-zA-Z0-9.,_~=:;&@%?#\/+]+)/<a href="$1$2" target="_blank" title="Open in new window: $1$2">$nick<\/a>/g;
+        $str =~ s/([-a-zA-Z0-9._]+@[-a-zA-Z0-9_]+.[-a-zA-Z0-9._]+)/<a href="mailto:$1" title="Mail to $nick">$nick<\/a>/g;
+    } else {
+        $str =~ s/(http|https|ftp|telnet|news)(:\/\/[-a-zA-Z0-9_]+.[-a-zA-Z0-9.,_~=:;&@%?#\/+]+)/<a href="$1$2" target="_blank" title="Open in new window: $1$2">$1$2<\/a>/g;
+        $str =~ s/([-a-zA-Z0-9._]+@[-a-zA-Z0-9_]+.[-a-zA-Z0-9._]+)/<a href="mailto:$1" title="Mail to $1">$1<\/a>/g;
+    }
+
+
+    return $str;
+
+}
+
+sub debug
+{
+    if ($debug) {
+        my $debugline = $_[0] . "\n";
+        print DEBUG $debugline;
+    }
+}
+
+sub find_alias
+{
+    my $nick = shift;
+
+    foreach (keys %alias) {
+        return $_ if (grep /^\Q$nick\E$/i, @{$alias{$_}});
+    }
+
+    return $nick;
+}
+
+sub create_html
+{
+
+    # This is where all subroutines get executed, you can actually design
+    # your own layout here, the lines should be self-explainable
+
+    print "Now generating HTML($outputfile)...\n";
+
+    open (OUTPUT, "> $outputfile") or die("$0: Unable to open outputfile($outputfile): $!\n");
+
+    htmlheader();
+    pageheader();
+    activetimes();
+    activenicks();
+
+    headline(template_text('bignumtopic'));
+    html("<table width=\"614\">\n"); # Needed for sections
+    questions();
+    loudpeople();
+    mostsmiles();
+    mostsad();
+    longlines();
+    shortlines();
+    html("</table>"); # Needed for sections
+
+    mostusedword();
+
+    mostreferenced();
+
+    headline(template_text('othernumtopic'));
+    html("<table width=\"614\">\n"); # Needed for sections
+    gotkicks();
+    mostkicks();
+    mostop();
+    mostmonologues();
+    mostjoins();
+    mostfoul();
+    html("</table>"); # Needed for sections
+
+    headline(template_text('latesttopic'));
+    html("<table width=\"614\">\n"); # Needed for sections
+    lasttopics();
+    html("</table>"); # Needed for sections
+
+    my %hash = ( lines => $lines );
+    html(template_text('totallines', %hash) . "<br><br>");
+
+    htmlfooter();
+
+    close(OUTPUT);
+
+}
+
+
+sub activetimes
+{
+    # The most actives times on the channel
+
+    my (%output, $tbgcolor);
+
+    &headline(template_text('activetimestopic'));
+
+    my @toptime = sort { $times{$b} <=> $times{$a} } keys %times;
+
+    my $highest_value = $times{$toptime[0]};
+
+    my @now = localtime($timestamp);
+
+    my $image = "pipe-blue.png";
+
+    for my $hour (sort keys %times) {
+        debug("Time: $hour => ". $times{$hour});
+
+        if ($toptime[0] == $hour) {
+            $image = "pipe-purple.png";
+        } 
+
+        my $size = ($times{$hour} / $highest_value) * 100;
+        my $percent = ($times{$hour} / $lines) * 100;
+        $percent =~ s/(\.\d)\d+/$1/;
+
+        print STDERR "before: $hour\n";
+        if ($timeoffset =~ /\+(\d)/) {
+            # We must plus some hours to the time
+            $hour += $1;
+            $hour = $hour % 24;
+            print STDERR "hour: $hour\n";
+            if ($hour < 10) { $hour = "0" . $hour; }
+
+        } elsif ($timeoffset =~ /-(\d)/) {
+            # We must remove some hours from the time
+            $hour -= $1;
+            $hour = $hour % 24;
+            if ($hour < 10) { $hour = "0" . $hour; }
+        }
+
+        $output{$hour} = "<td align=\"center\" valign=\"bottom\" class=\"asmall\">$percent%<br><img src=\"$image\" width=\"15\" height=\"$size\" alt=\"$percent\"></td>\n";
+    }
+
+    html("<table border=\"0\" width=\"614\"><tr>\n");
+
+    for ($b = 0; $b < 24; $b++) {
+        if ($b < 10) { $a = "0" . $b; } else { $a = $b; }
+
+        if (!defined($output{$a}) || $output{$a} eq "") {
+            html("<td align=\"center\" valign=\"bottom\" class=\"asmall\">0%</td>");
+        } else {
+            html($output{$a});
+        }
+    }
+
+    html("</tr><tr>");
+
+    for ($b = 0; $b < 24; $b++) {
+        if ($now[2] == $b) { $tbgcolor = "\#AAAAAA"; } else { $tbgcolor = "\#CCCCCC"; }
+        html("<td bgcolor=\"$tbgcolor\" align=\"center\" class=\"small\">$b</td>");
+}
+
+    html("</tr></table>");
+}
+
+sub activenicks
+{
+    # The most active nicks (those who wrote most lines)
+
+    my $randomline;
+
+    &headline(template_text('activenickstopic'));
+
+    html("<table border=\"0\" width=\"614\"><tr>");
+    html("<td>&nbsp;</td><td bgcolor=\"$tdtop\"><b>" . template_text('nick') . "</b></td><td bgcolor=\"$tdtop\"><b>" . template_text('numberlines') ."</b></td><td bgcolor=\"$tdtop\"><b>". template_text('randquote') ."</b></td>");
+    if (%userpics) {
+        html("<td bgcolor=\"$tdtop\"><b>" . template_text('userpic') ."</b></td>");
+    }
+
+    html("</tr>");
+
+    my @active = sort { $line{$b} <=> $line{$a} } keys %line;
+
+    if ($activenicks > $nicks) {
+        $activenicks = $nicks;
+        print "Note: There was less nicks in the logfile than your specificied there to be in most active nicks...\n";
+    }
+
+    my ($nick, $visiblenick);
+    my $i = 1;
+    for (my $c = 0; $c < $activenicks; $c++) {
+        $nick = $active[$c];
+        $visiblenick = $active[$c];
+
+        if (!$longlines{$nick}) {
+            $randomline = "";
+        } else {
+            my $rand = rand($longlines{$nick});
+            $randomline = $sayings{$nick}[$rand];
+        }
+
+
+        # Convert URLs and e-mail addys to links
+        $randomline = replace_links($randomline);
+
+        # Add a link to the nick if there is any
+        if ($userlinks{$nick}) {
+            $visiblenick = replace_links($userlinks{$nick}, $nick);
+        }
+
+        my $h = $hicell;
+        $h =~ s/^#//;
+        $h = hex $h;
+        my $h2 = $hicell2;
+        $h2 =~ s/^#//;
+        $h2 = hex $h2;
+        my $f_b = $h & 0xff;
+        my $f_g = ($h & 0xff00) >> 8;
+        my $f_r = ($h & 0xff0000) >> 16;
+        my $t_b = $h2 & 0xff;
+        my $t_g = ($h2 & 0xff00) >> 8;
+        my $t_r = ($h2 & 0xff0000) >> 16;
+        my $col_b  = sprintf "%0.2x", abs int(((($t_b - $f_b) / $activenicks) * +$c) + $f_b);
+        my $col_g  = sprintf "%0.2x", abs int(((($t_g - $f_g) / $activenicks) * +$c) + $f_g);
+        my $col_r  = sprintf "%0.2x", abs int(((($t_r - $f_r) / $activenicks) * +$c) + $f_r);
+
+
+        html("<tr><td bgcolor=\"$rankc\" align=\"left\">");
+        my $line = $line{$nick};
+        html("$i</td><td bgcolor=\"#$col_r$col_g$col_b\">$visiblenick</td><td bgcolor=\"#$col_r$col_g$col_b\">$line</td><td bgcolor=\"#$col_r$col_g$col_b\">");
+        html("\"$randomline\"</td>");
+
+        if ($userpics{$nick}) {
+            html("<td bgcolor=\"#$col_r$col_g$col_b\" align=\"center\"><img valign=\"middle\" src=\"$imagepath$userpics{$nick}\"></td>");
+        }
+
+        html("</tr>");
+        $i++;
+    }
+
+    html("</table><br>");
+
+    # ALMOST AS ACTIVE NICKS
+
+    @active = sort { $line{$b} <=> $line{$a} } keys %line;
+
+    my $nickstoshow = $activenicks + $activenicks2;
+
+    unless ($nickstoshow > $nicks) {
+
+        html("<br><b><i>" . template_text('nottop') . "</i></b><table><tr>");
+        for (my $c = $activenicks; $c < $nickstoshow; $c++) {
+            unless ($c % 5) { unless ($c == $activenicks) { html("</tr><tr>"); } }
+            html("<td bgcolor=\"$rankc\" class=\"small\">");
+            my $nick = $active[$c];
+            my $lines = $line{$nick};
+            html("$nick ($lines)</td>");
+        }
+
+        html("</table>");
+    }
+
+
+}
+
+sub mostusedword
+{
+    # Lao the infamous word usage statistics
+    my %usages;
+
+    foreach my $word (sort keys %wordcount) {
+        next if exists $line{$word};
+        $usages{$word} = $wordcount{$word};
+    }
+
+
+    my @popular = sort { $usages{$b} <=> $usages{$a} } keys %usages;
+
+    if (@popular) {
+        &headline(template_text('mostwordstopic'));
+
+        html("<table border=\"0\" width=\"614\"><tr>");
+        html("<td>&nbsp;</td><td bgcolor=\"$tdtop\"><b>" . template_text('word') . "</b></td>");
+        html("<td bgcolor=\"$tdtop\"><b>" . template_text('numberuses') . "</b></td>");
+        html("<td bgcolor=\"$tdtop\"><b>" . template_text('lastused') . "</b></td>");
+
+
+        for(my $i = 0; $i < 10; $i++) {
+            last unless $i < $#popular;
+            my $a = $i + 1;
+            my $popular = $popular[$i];
+            my $wordcount = $wordcount{$popular[$i]};
+            my $lastused = $lastused{$popular[$i]};
+            html("<tr><td bgcolor=\"$rankc\"><b>$a</b>");
+            html("<td bgcolor=\"$hicell\">$popular</td>");
+            html("<td bgcolor=\"$hicell\">$wordcount</td>");
+            html("<td bgcolor=\"$hicell\">$lastused</td>");
+            html("</tr>");
+       }
+
+       html("</table>");
+   }
+
+}
+
+
+sub mostreferenced
+{
+    my %usages;
+
+    foreach my $word (sort keys %wordcount) {
+        next unless exists $line{$word};
+        $usages{$word} = $wordcount{$word};
+    }
+
+    my @popular = sort { $usages{$b} <=> $usages{$a} } keys %usages;
+
+    if (@popular) {
+
+        &headline(template_text('referencetopic'));
+
+        html("<table border=\"0\" width=\"614\"><tr>");
+        html("<td>&nbsp;</td><td bgcolor=\"$tdtop\"><b>" . template_text('nick') . "</b></td>");
+        html("<td bgcolor=\"$tdtop\"><b>" . template_text('numberuses') . "</b></td>");
+        html("<td bgcolor=\"$tdtop\"><b>" . template_text('lastused') . "</b></td>");
+
+       for(my $i = 0; $i < 5; $i++) {
+           last unless $i < $#popular;
+           my $a = $i + 1;
+           my $popular = $popular[$i];
+           my $wordcount = $wordcount{$popular[$i]};
+           my $lastused = $lastused{$popular[$i]};
+           html("<tr><td bgcolor=\"$rankc\"><b>$a</b>");
+           html("<td bgcolor=\"$hicell\">$popular</td>");
+           html("<td bgcolor=\"$hicell\">$wordcount</td>");
+           html("<td bgcolor=\"$hicell\">$lastused</td>");
+           html("</tr>");
+       }
+   html("</table>");
+   }
+
+}
+
+sub questions
+{
+    # Persons who asked the most questions
+
+    foreach my $nick (sort keys %question) {
+        if ($line{$nick} > 100) {
+            $qpercent{$nick} = ($question{$nick} / $line{$nick}) * 100;
+            $qpercent{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @question = sort { $qpercent{$b} <=> $qpercent{$a} } keys %qpercent;
+
+    if (@question) {
+        my %hash = (
+            nick => $question[0],
+            per => $qpercent{$question[0]}
+        );
+
+        my $text = template_text('question1', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text");
+        if (@question >= 2) {
+            my %hash = (
+                nick => $question[1],
+                per => $qpercent{$question[1]}
+            );
+
+            my $text = template_text('question2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+
+    } else {
+        html("<tr><td bgcolor=\"$hicell\">" . template_text('question3') . "</td></tr>");
+    }
+}
+
+sub loudpeople
+{
+    # The ones who speak LOUDLY!
+
+    foreach my $nick (sort keys %loud) {
+        if ($line{$nick} > 100) {
+            $lpercent{$nick} = ($loud{$nick} / $line{$nick}) * 100;
+            $lpercent{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @loud = sort { $lpercent{$b} <=> $lpercent{$a} } keys %lpercent;
+
+    if (@loud) {
+        my %hash = (
+            nick => $loud[0],
+            per => $lpercent{$loud[0]}
+        );
+
+        my $text = template_text('loud1', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text");
+        if (@loud >= 2) {
+            my %hash = (
+                nick => $loud[1],
+                per => $lpercent{$loud[1]}
+            );
+
+            my $text = template_text('loud2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+
+    } else {
+        my $text = template_text('loud3');
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+
+}
+
+sub gotkicks
+{
+    # The persons who got kicked the most
+
+    my @gotkick = sort { $gotkick{$b} <=> $gotkick{$a} } keys %gotkick;
+
+    if (@gotkick) {
+        my %hash = (
+            nick => $gotkick[0],
+            kicks => $gotkick{$gotkick[0]}
+        );
+
+        my $text = template_text('gotkick1', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text");
+        if (@gotkick >= 2) {
+            my %hash = (
+                nick => $gotkick[1],
+                kicks => $gotkick{$gotkick[1]}
+            );
+
+            my $text = template_text('gotkick2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+    }
+}
+
+sub mostjoins
+{
+
+    my @joins = sort { $joins{$b} <=> $joins{$a} } keys %joins;
+
+    if (@joins) {
+        my %hash = (
+            nick => $joins[0],
+            joins => $joins{$joins[0]}
+        );
+
+        my $text = template_text('joins', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+}
+
+
+sub mostkicks
+{
+     # The person who got kicked the most
+
+     my @kicked = sort { $kicked{$b} <=> $kicked{$a} } keys %kicked;
+
+     if (@kicked) {
+         my %hash = (
+             nick => $kicked[0],
+             kicked => $kicked{$kicked[0]}
+         );
+
+         my $text = template_text('kick1', %hash);
+         html("<tr><td bgcolor=\"$hicell\">$text");
+
+         if (@kicked >= 2) {
+         my %hash = (
+             oldnick => $kicked[0],
+             nick => $kicked[1],
+             kicked => $kicked{$kicked[1]}
+         );
+
+         my $text = template_text('kick2', %hash);
+             html("<br><span class=\"small\">$text</span>");
+         }
+         html("</td></tr>");
+     } else {
+         my $text = template_text('kick3');
+         html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+     }
+
+}
+
+sub mostmonologues
+{
+    # The person who had the most monologues (speaking to himself)
+
+     my @monologue = sort { $monologue{$b} <=> $monologue{$a} } keys %monologue;
+
+     if (@monologue) {
+         my %hash = (
+             nick => $monologue[0],
+             monos => $monologue{$monologue[0]}
+         );
+
+         my $text = template_text('mono1', %hash);
+
+         html("<tr><td bgcolor=\"$hicell\">$text");
+         if (@monologue >= 2) {
+             my %hash = (
+                 nick => $monologue[1],
+                 monos => $monologue{$monologue[1]}
+             );
+
+             my $text = template_text('mono2', %hash);
+             html("<br><span class=\"small\">$text</span>");
+         }
+         html("</td></tr>");
+     }
+}
+
+sub longlines
+{
+
+    my %len;
+
+    # The person(s) who wrote the longest lines
+
+    foreach my $nick (sort keys %length) {
+        if ($line{$nick} > 100) {
+            $len{$nick} = $length{$nick} / $line{$nick};
+            $len{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @len = sort { $len{$b} <=> $len{$a} } keys %len;
+
+    my $all_lines = $normals + $actions;
+
+    my $totalaverage;
+
+    if ($all_lines > 0) {
+        $totalaverage = $totallength / $all_lines;
+        $totalaverage =~ s/(\.\d)\d+/$1/;
+    }
+
+    if (@len) {
+        my %hash = (
+            nick => $len[0],
+            letters => $len{$len[0]}
+        );
+
+        my $text = template_text('long1', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text<br>");
+
+        if (@len >= 2) {
+            %hash = (
+                channel => $channel,
+                avg => $totalaverage
+            );
+
+            $text = template_text('long2', %hash);
+            html("<span class=\"small\">$text</span></td></tr>");
+        }
+    }
+}
+
+sub shortlines
+{
+    # This sub should be combined with the longlines sub at some point.. it
+    # does basically the same thing.
+
+    my %len;
+
+    # The person(s) who wrote the shortest lines
+
+    foreach my $nick (sort keys %length) {
+        if ($line{$nick} > 5) {
+            $len{$nick} = $length{$nick} / $line{$nick};
+            $len{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @len = sort { $len{$a} <=> $len{$b} } keys %len;
+
+    if (@len) {
+        my %hash = (
+            nick => $len[0],
+            letters => $len{$len[0]}
+        );
+
+        my $text = template_text('short1', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text<br>");
+
+        if (@len >= 2) {
+            %hash = (
+                nick => $len[1],
+                letters => $len{$len[1]}
+            );
+
+            $text = template_text('short2', %hash);
+            html("<span class=\"small\">$text</span></td></tr>");
+        }
+    }
+
+}
+
+sub mostfoul
+{
+    my %spercent;
+
+    foreach my $nick (sort keys %foul) {
+        if ($line{$nick} > 15) {
+            $spercent{$nick} = ($foul{$nick} / $line{$nick}) * 100;
+            $spercent{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @foul = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
+
+
+    if (@foul) {
+
+        my %hash = (
+            nick => $foul[0],
+            per => $spercent{$foul[0]}
+        );
+
+        my $text = template_text('foul1', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text");
+
+        if (@foul >= 2) {
+            my %hash = (
+                nick => $foul[1],
+                per => $spercent{$foul[1]}
+            );
+
+            my $text = template_text('foul2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+
+       html("</td></tr>");
+    } else {
+        my %hash = (
+            channel => $channel
+        );
+
+        my $text = template_text('foul3', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+}
+
+
+sub mostsad
+{
+    my %spercent;
+
+    foreach my $nick (sort keys %sadface) {
+        if ($line{$nick} > 100) {
+            $spercent{$nick} = ($sadface{$nick} / $line{$nick}) * 100;
+            $spercent{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @sadface = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
+
+
+    if (@sadface) {
+        my %hash = (
+            nick => $sadface[0],
+            per => $spercent{$sadface[0]}
+        );
+
+        my $text = template_text('sad1', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text");
+
+        if (@sadface >= 2) {
+            my %hash = (
+                nick => $sadface[1],
+                per => $spercent{$sadface[1]}
+            );
+
+            my $text = template_text('sad2', %hash);
+
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+    } else {
+        my %hash = (
+            channel => $channel
+        );
+        my $text = template_text('sad3', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+}
+
+
+sub mostop
+{
+    my @ops = sort { $gaveop{$b} <=> $gaveop{$a} } keys %gaveop;
+    my @deops = sort { $tookop{$b} <=> $tookop{$a} } keys %tookop;
+
+    if (@ops) {
+        my %hash = (
+            nick => $ops[0],
+            ops => $gaveop{$ops[0]}
+        );
+
+        my $text = template_text('mostop1', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text");
+
+        if (@ops >= 2) {
+            my %hash = (
+                nick => $ops[1],
+                ops => $gaveop{$ops[1]}
+            );
+
+            my $text = template_text('mostop2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+    } else {
+        my %hash = ( channel => $channel );
+        my $text = template_text('mostop3', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+    if (@deops) {
+        my %hash = (
+            channel => $channel,
+            nick => $deops[0],
+            deops => $tookop{$deops[0]}
+        );
+        my $text = template_text('mostdeop1', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text");
+
+        if (@deops >= 2) {
+            my %hash = (
+                nick => $deops[1],
+                deops => $tookop{$deops[1]}
+            );
+            my $text = template_text('mostdeop2', %hash);
+
+            html("<br><span class=\"small\">$text</span>");
+        }
+            html("</td></tr>");
+    } else {
+        my %hash = ( channel => $channel );
+        my $text = template_text('mostdeop3', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text");
+    }
+}
+
+sub mostsmiles
+{
+    # The person(s) who smiled the most :-)
+
+    my %spercent;
+
+    foreach my $nick (sort keys %smile) {
+        if ($line{$nick} > 100) {
+            $spercent{$nick} = ($smile{$nick} / $line{$nick}) * 100;
+            $spercent{$nick} =~ s/(\.\d)\d+/$1/;
+        }
+    }
+
+    my @smiles = sort { $spercent{$b} <=> $spercent{$a} } keys %spercent;
+
+
+    if (@smiles) {
+        my %hash = (
+            nick => $smiles[0],
+            per => $spercent{$smiles[0]}
+        );
+
+        my $text = template_text('smiles1', %hash);
+
+        html("<tr><td bgcolor=\"$hicell\">$text");
+        if (@smiles >= 2) {
+            my %hash = (
+                nick => $smiles[1],
+                per => $spercent{$smiles[1]}
+            );
+
+            my $text = template_text('smiles2', %hash);
+            html("<br><span class=\"small\">$text</span>");
+        }
+        html("</td></tr>");
+
+    } else {
+        my %hash = (
+            channel => $channel
+        );
+
+        my $text = template_text('smiles3', %hash);
+        html("<tr><td bgcolor=\"$hicell\">$text</td></tr>");
+    }
+}
+
+sub lasttopics
+{
+    debug("Total number of topics: ". scalar @topics);
+
+    if (@topics) {
+        my $ltopic = @topics - 1;
+        my $tlimit = 0;
+
+        $topichistory -= 1;
+
+
+        if ($ltopic > $topichistory) { $tlimit = $ltopic - $topichistory; }
+
+        for (my $i = $ltopic; $i >= $tlimit; $i--) {
+            $topics[$i]{"topic"} = replace_links($topics[$i]{"topic"});
+            my $topic = $topics[$i]{topic};
+            my $nick = $topics[$i]{nick};
+            my $hour = $topics[$i]{hour};
+            my $min = $topics[$i]{min};
+            html("<tr><td bgcolor=\"$hicell\"><i>$topic</i></td>");
+            html("<td bgcolor=\"$hicell\">By <b>$nick</b> at <b>$hour:$min</b></td></tr>");
+        }
+       } else {
+            html("<tr><td bgcolor=\"$hicell\">" . template_text('notopic') ."</td></tr>");
+       }
+}
+
+
+# Some HTML subs
+sub htmlheader
+{
+print OUTPUT <<HTML;
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>$channel @ $network channel statistics</title>
+<style type="text/css">
+a { text-decoration: none }
+a:link { color: $link; }
+a:visited { color: $vlink; }
+a:hover { text-decoration: underline; color: $hlink }
+
+body {
+    background-color: $bgcolor;
+    font-family: verdana, arial, sans-serif;
+    font-size: 13px;
+    color: $text;
+}
+
+td {
+    font-family: verdana, arial, sans-serif;
+    font-size: 13px;
+    color: $tdcolor;
+}
+
+.title {
+    font-family: tahoma, arial, sans-serif;
+    font-size: 16px;
+    font-weight: bold;
+}
+
+.headline { color: $hcolor; }
+.small { font-family: verdana, arial, sans-serif; font-size: 10px; }
+.asmall { font-family: arial narrow, sans-serif; font-size: 10px }
+</style></head>
+<body>
+<div align="center">
+HTML
+my %hash = (
+    channel => $channel,
+    network => $network,
+    maintainer => $maintainer,
+    time => $time,
+    days => $days,
+    nicks => $nicks,
+    channel => $channel
+);
+print OUTPUT "<span class=\"title\">" . template_text('pagetitle1', %hash) . "</span><br>";
+print OUTPUT "<br>" . template_text('pagetitle2', %hash) . "<br>";
+print OUTPUT template_text('pagetitle3', %hash) . "<br><br>";
+
+}
+
+sub htmlfooter
+{
+print OUTPUT <<HTML;
+<span class="small">
+Stats generated by <a href="http://www.wtf.dk/hp/index.php?page=pisg" title="Go to the pisg homepage">pisg</a> $version<br>
+pisg by <a href="http://www.wtf.dk/hp/" title="Go to the authors homepage">Morten "LostStar" Brix Pedersen</a> and others<br>
+Stats generated in $processtime
+</span>
+</div>
+</body>
+</html>
+HTML
+}
+
+sub headline
+{
+    my ($title) = @_;
+print OUTPUT <<HTML;
+   <br>
+   <table width="610" cellpadding="1" cellspacing="0" border="0">
+    <tr>
+     <td bgcolor="$headline">
+      <table width="100%" cellpadding="2" cellspacing="0" border="0" align="center">
+       <tr>
+        <td bgcolor="$hbgcolor" class="text10">
+         <div align="center" class="headline"><b>$title</b></div>
+        </td>
+       </tr>
+      </table>
+     </td>
+    </tr>
+   </table>
+HTML
+}
+
+sub pageheader
+{
+    if ($pagehead ne 'none') {
+        open(PAGEHEAD, $pagehead) or die("$0: Unable to open $pagehead for reading: $!\n");
+        while (<PAGEHEAD>) {
+            html($_);
+        }
+    }
+}
+
+sub close_debug
+{
+    if ($debug) {
+        close(DEBUG) or print STDERR "$0: Cannot close debugfile($debugfile): $!\n";
+    }
+}
+
+sub get_cmdlineoptions
+{
+    my $tmp;
+    # Commandline options
+    my $help;
+
+my $usage = <<END_USAGE;
+Usage: pisg.pl [-c channel] [-l logfile] [-o outputfile] [-m
+maintainer]  [-f format] [-n network] [-d logdir] [-a aliasfile]
+[-i ignorefile] [-h]
+
+-c --channel=xxx       : Set channel name
+-l --logfile=xxx       : Log file to parse
+-o --outfile=xxx       : Name of html file to create
+-m --maintainer=xxx    : Channel/statistics maintainer
+-f --format=xxx        : Logfile format [see FORMATS file]
+-n --network=xxx       : IRC Network this channel is on.
+-d --dir=xxx           : Analyze all files in this dir. Ignores logfile.
+-u --usersfile=xxx     : Users config file
+-h --help              : Output this message and exit (-? also works).
+
+Example:
+
+ \$ pisg.pl -n IRCnet -f xchat -o suid.html -c \\#channel -l logfile.log
+
+As always, all options may also be defined by editing the source and calling
+pisg without arguments.
+
+END_USAGE
+
+    if (GetOptions('channel=s'    => \$channel,
+                   'logfile=s'    => \$logfile,
+                   'format=s'     => \$format,
+                   'network=s'    => \$network,
+                   'maintainer=s' => \$maintainer,
+                   'outfile=s'    => \$outputfile,
+                   'dir=s'        => \$logdir,
+                   'ignorefile=s' => \$tmp,
+                   'aliasfile=s'  => \$tmp,
+                   'usersfile=s'  => \$usersfile,
+                   'help|?'       => \$help
+               ) == 0 or $help) {
+                   die($usage);
+               }
+
+    if (@ARGV) {
+        if ($ARGV[0]) { $channel = $ARGV[0]; }
+        if ($ARGV[1]) { $logfile = $ARGV[1]; }
+        if ($ARGV[2]) { $outputfile = $ARGV[2]; }
+        if ($ARGV[3]) { $maintainer = $ARGV[3]; }
+    }
+
+    if ($tmp) {
+        die("The aliasfile and ignorefile has been obsoleted by the new
+        userscfg, please use that instead [look in users.cfg]\n");
+    }
+
+}
+
+### English
+$T{EN}{mostop1} = "<b>[:nick]</b> donated [:ops] ops in the channel...";
+$T{EN}{mostop2} = "<b>[:nick]</b> was also very polite: [:ops] from her/him";
+$T{EN}{mostop3} = "Strange, no op was given on [:channel]!";
+
+$T{EN}{mostdeop1} = "<b>[:nick]</b> is the channel sheriff with [:deops] deops...";
+$T{EN}{mostdeop2} = "<b>[:nick]</b> deoped [:deops] users";
+$T{EN}{mostdeop3} = "Wow, no op was taken on [:channel]!";
+
+$T{EN}{question1} = "<b>[:nick]</b> is either stupid or just making many questions... [:per]% lines contained a question!";
+$T{EN}{question2} = "<b>[:nick]</b> didn't know that much either, [:per]% of his lines were questions";
+$T{EN}{question3} = "Nobody asked questions here, just geniuses at this channel?";
+
+$T{EN}{loud1} = "Loudest one was <b>[:nick]</b> who yelled [:per]% of the time!";
+$T{EN}{loud2} = "Another <i>old yeller</i> was <b>[:nick]</b> who shouted [:per]% of the time!";
+$T{EN}{loud3} = "Nobody raised an exclamation mark, wow.";
+
+$T{EN}{gotkick1} = "<b>[:nick]</b> wasn't very popular, got kicked [:kicks] times!";
+$T{EN}{gotkick2} = "<b>[:nick]</b> seemed to be hated too, [:kicks] kicks were received";
+
+$T{EN}{joins} = "<b>[:nick]</b> couldn't decide to stay or to go, [:joins] joins during this reporting period!";
+
+$T{EN}{kick1} = "<b>[:nick]</b> is insane or just a fair op, kicked a total of [:kicked] people!";
+$T{EN}{kick2} = "[:oldnick]'s faithfull follower, <b>[:nick]</b>, kicked about [:kicked] people";
+$T{EN}{kick3} = "Nice oppers here, no one got kicked!";
+
+$T{EN}{mono1} = "<b>[:nick]</b> is talking to himself a lot, wrote over 5 lines in a row [:monos] times!";
+$T{EN}{mono2} = "Another lonely one was <b>[:nick]</b>, who managed to hit [:monos] times";
+
+$T{EN}{long1} = "<b>[:nick]</b> wrote longest lines, average of [:letters] per line...";
+$T{EN}{long2} = "Channel average on [:channel] was [:avg] letters per line";
+
+$T{EN}{short1} = "<b>[:nick]</b> wrote shortest lines, average of [:letters] per line...";
+$T{EN}{short2} = "[:nick] was tight-lipped, too, averaging [:letters]";
+
+$T{EN}{foul1} = "<b>[:nick]</b> has quite a potty mouth, [:per]% lines contained foul language";
+$T{EN}{foul2} = "<b>[:nick]</b> also makes sailors blush, [:per]% of the time";
+$T{EN}{foul3} = "Nobody is foul-mouthed at [:channel]! Get out much?";
+
+$T{EN}{smiles1} = "<b>[:nick]</b> brings happiness to the world, [:per]% lines contained smiling faces :)";
+$T{EN}{smiles2} = "<b>[:nick]</b> isn't a sad person either, who smiled [:per]% of the time";
+$T{EN}{smiles3} = "Nobody smiles at [:channel]! Cheer up guys and girls.";
+
+$T{EN}{sad1} = "<b>[:nick]</b> seems to be sad at the moment, [:per]% lines contained sad faces :(";
+$T{EN}{sad2} = "<b>[:nick]</b> is also a sad person, who cried [:per]% of the time";
+$T{EN}{sad3} = "Nobody is sad at [:channel]! What a happy channel :-)";
+
+$T{EN}{notopic} = "A topic was never set on this channel";
+
+## Topics
+
+$T{EN}{bignumtopic} = "Big numbers";
+$T{EN}{othernumtopic} = "Other interesting numbers";
+$T{EN}{latesttopic} = "Latest Topics";
+$T{EN}{activetimestopic} = "Most active times";
+$T{EN}{activenickstopic} = "Most active nicks";
+$T{EN}{mostwordstopic} = "Most used words";
+$T{EN}{referencetopic} = "Most referenced nick";
+
+## Other text
+
+$T{EN}{totallines} = "Total number of lines: [:lines]";
+$T{EN}{nick} = "Nick";
+$T{EN}{numberlines} = "Number of lines";
+$T{EN}{randquote} = "Random quote";
+$T{EN}{userpic} = "Userpic";
+$T{EN}{nottop} = "These didn't make it to the top:";
+$T{EN}{word} = "Word";
+$T{EN}{numberuses} = "Number of Uses";
+$T{EN}{lastused} = "Last Used by";
+$T{EN}{pagetitle1} = "[:channel] @ [:network] stats by [:maintainer]";
+$T{EN}{pagetitle2} = "Statistics generated on [:time]";
+$T{EN}{pagetitle3} = "During this [:days]\-days reporting period, a total number of <b>[:nicks]</b> different nicks were represented on [:channel].";
+
+### German
+
+$T{DE}{mostop1} = "<b>[:nick]</b> vergab [:ops] ops im Channel...";
+$T{DE}{mostop2} = "<b>[:nick]</b> war auch sehr zuvorkommend: [:ops] von ihm/ihr";
+$T{DE}{mostop3} = "Komisch, kein op wurde in [:channel] vergeben!";
+
+$T{DE}{mostdeop1} = "<b>[:nick]</b> ist die Channel-Polizei. Er/Sie hat [:deops] Usern das @ wieder weggenommen...";
+$T{DE}{mostdeop2} = "<b>[:nick]</b> nahm [:deops] Usern das @ wieder weg.";
+$T{DE}{mostdeop3} = "Wow, niemand wurde deopped in [:channel]!";
+
+$T{DE}{question1} = "<b>[:nick]</b> hat wohl in der Schule nicht gut aufgepasst... [:per]% seiner Zeilen enthielten eine Frage!";
+$T{DE}{question2} = "<b>[:nick]</b> weiss wohl auch nicht viel, [:per]% seiner Zeilen waren Fragen";
+$T{DE}{question3} = "Niemand hat hier was gefragt, sollte das etwa ein Channel voller Genies sein? ;)";
+
+$T{DE}{loud1} = "Am lautesten war <b>[:nick]</b> der [:per]% der Zeit geschrieen hat!";
+$T{DE}{loud2} = "Ein anderer <i>Schreihals</i> war <b>[:nick]</b> der/die [:per]% der Zeit rumgeschrieen hat!";
+$T{DE}{loud3} = "Niemand hat ein Ausrufungszeichen benutzt, wow... Zurückhaltende User im Channel ;)";
+
+$T{DE}{gotkick1} = "<b>[:nick]</b> war wohl der Channelclown, er/sie wurde [:kicks] mal gekickt!";
+$T{DE}{gotkick2} = "<b>[:nick]</b> konnte sich scheinbar auch nicht benehmen, [:kicks] kicks für ihn/sie";
+
+$T{DE}{joins} = "<b>[:nick]</b> konnte sich nicht entscheiden im Channel zu bleiben oder zu gehen, [:joins] joins während des Statistik-Zeitraums!";
+
+$T{DE}{kick1} = "Ist <b>[:nick]</b> jetzt einfach nur ein fairer Op oder macht ihm/ihr das Spass? Er/Sie kickte [:kicked] User!";
+$T{DE}{kick2} = "[:oldnick]'s würdiger Nachfolger, <b>[:nick]</b>, er/sie kickte [:kicked] User";
+$T{DE}{kick3} = "Nette Opper hier, niemand wurde gekickt!";
+
+$T{DE}{mono1} = "<b>[:nick]</b> spricht viel mit sich selbst, er/sie schrieb über 5 Zeilen in einer Reihe und das [:monos] mal!";
+$T{DE}{mono2} = "Ein anderes einsames Herz ist <b>[:nick]</b>, der/die [:monos] mal mit sich selbst redete";
+
+$T{DE}{long1} = "<b>[:nick]</b> ist die Labertasche im Channel Er/Sie schrieb die längste Zeile mit durchschnittlich [:letters] Buchstaben pro Zeile...";
+$T{DE}{long2} = "Channel-Durchschnitt in [:channel] war [:avg] Buchstaben pro Zeile";
+
+$T{DE}{short1} = "<b>[:nick]</b> ist nicht sehr Mitteilungsbedürftig. Er/Sie schrieb die kürzeste Zeile mit durchschnittlich [:letters] Buchstaben pro Zeile...";
+$T{DE}{short2} = "[:nick] war auch sehr kurzfassend, durchschnittlich [:letters] Buchstaben pro Zeile";
+
+$T{DE}{foul1} = "<b>[:nick]</b> hat in seiner/ihrer Jugend keine Erziehung genossen, [:per]% seiner/ihrer S&auml;tze enthielten Schimpfworte!";
+$T{DE}{foul2} = "<b>[:nick]</b> war [:per]% der Zeit unartig (Ob da der Weihnachtsman noch kommt?)";
+$T{DE}{foul3} = "Alle gut erzogen in [:channel], niemand hat Schimpfworte benutzt!";
+
+$T{DE}{smiles1} = "<b>[:nick]</b> ist ein fröhlicher Mensch, [:per]% seiner/ihrer Zeilen enthielt ein fröhliches Smily :)";
+$T{DE}{smiles2} = "<b>[:nick]</b> scheint auch glücklich zu sein. Er/Sie \"smilte\" [:per]% der Zeit";
+$T{DE}{smiles3} = "Niemand \"smilte\" in [:channel]! Los Leute legt mal wieder ein Lächeln auf :)";
+
+$T{DE}{sad1} = "<b>[:nick]</b> scheint im Moment nicht gut drauf zu sein, [:per]% seiner/ihrer Zeilen enthielten ein trauriges Smily :(";
+$T{DE}{sad2} = "<b>[:nick]</b> ist auch eine traurige Person, die [:per]% der Zeit geweint hat";
+$T{DE}{sad3} = "Niemand ist traurig in [:channel]! Was für ein fröhlicher Channel :)";
+
+$T{DE}{notopic} = "In diesem Channel wurde kein Topic gesetzt";
+
+## Topics
+
+$T{DE}{bignumtopic} = "Die Zahlen sprechen für sich :)";
+$T{DE}{othernumtopic} = "Andere interessante Zahlen";
+$T{DE}{latesttopic} = "Letzte Topics";
+$T{DE}{activetimestopic} = "Wann war am meisten los?";
+$T{DE}{activenickstopic} = "Wer quatscht am meisten?";
+$T{DE}{mostwordstopic} = "Am meisten benutzte Wörter";
+$T{DE}{referencetopic} = "Begehrte Nicks :)";
+
+## Other text
+
+$T{DE}{totallines} = "Gesamtanzahl der Zeilen: [:lines]";
+$T{DE}{nick} = "Nick";
+$T{DE}{numberlines} = "Anzahl der Zeilen";
+$T{DE}{randquote} = "Zufalls quote";
+$T{DE}{userpic} = "Userbild";
+$T{DE}{nottop} = "Sie haben es nicht an die Spitze geschafft:";
+$T{DE}{word} = "Wort";
+$T{DE}{numberuses} = "Wie oft benutzt";
+$T{DE}{lastused} = "Zuletzt benutzt von";
+$T{DE}{pagetitle1} = "[:channel] @ [:network] stats erstellt von [:maintainer]";
+$T{DE}{pagetitle2} = "Statistik erstellt am [:time]";
+$T{DE}{pagetitle3} = "Während des Statistikzeitraums von [:days] Tage(n) wurden <b>[:nicks]</b> verschiedene Nicks in [:channel] gezählt.";
+
+### Danish
+$T{DK}{mostop1} = "<b>[:nick]</b> gav [:ops] op status i kanalen...";
+$T{DK}{mostop2} = "<b>[:nick]</b> var også venlig at gi': [:ops] fra sig";
+$T{DK}{mostop3} = "Underligt, ingen har fået op på [:channel] endnu!";
+
+$T{DK}{mostdeop1} = "<b>[:nick]</b> holder orden på [:channel] med [:deops] deops...";
+$T{DK}{mostdeop2} = "<b>[:nick]</b> deoppede [:deops] brugere";
+$T{DK}{mostdeop3} = "Hmm, der var ingen som fik op på [:channel]!";
+
+$T{DK}{question1} = "<b>[:nick]</b> må være rimelig dum eller efterligner bare Spørge-Jørgen... [:per]% af hans linjer var spørgsmål!";
+$T{DK}{question2} = "<b>[:nick]</b> var heller ingen Einstein, [:per]% af hans linjer var også spørgsmål";
+$T{DK}{question3} = "Ingen spørgsmål på denne kanal. De må sørme være kloge!";
+
+$T{DK}{loud1} = "Den mest larmende var <b>[:nick]</b>, som råbte [:per]% af tiden!";
+$T{DK}{loud2} = "En anden skrigehals var <b>[:nick]</b>, som råbte [:per]% af tiden!";
+$T{DK}{loud3} = "Der er ingen som råber. Leger de stilleleg?";
+
+$T{DK}{gotkick1} = "<b>[:nick]</b> er ret upopulær, blev sparket ud [:kicks] gange!";
+$T{DK}{gotkick2} = "<b>[:nick]</b> er heller ikke nogen Elvis, [:kicks] gange fik han sparket";
+
+$T{DK}{joins} = "<b>[:nick]</b> er lidt forvirret. Han har været inde og ude af kanalen [:joins] gange!";
+
+$T{DK}{kick1} = "<b>[:nick]</b> er magtsygt eller ved hvordan han/hun skal kontrollere sine fjender. Han har sparket [:kicked] folk ud af kanalen!";
+$T{DK}{kick2} = "[:oldnick] har en efterfølger. <b>[:nick]</b> sparkede [:kicked] folk ud af kanalen";
+$T{DK}{kick3} = "Venlige operatører må man sige. Ingen har fået sparket!";
+
+$T{DK}{mono1} = "<b>[:nick]</b> snakker meget med sig selv. Han har skrevet over 5 linjer på en gang, [:monos] gange!";
+$T{DK}{mono2} = "En anden ensom person var <b>[:nick]</b>, som snakkede til sig selv [:monos] gange";
+
+$T{DK}{long1} = "<b>[:nick]</b> skriver meget lange sætninger. Gennemsnittet er [:letters] bogstaver pr. linje...";
+$T{DK}{long2} = "Gennemsnittet på [:channel] er [:avg] bogstaver pr. linje";
+
+$T{DK}{short1} = "<b>[:nick]</b> skrev de korteste linjer. Gennemsnittet er [:letters] bogstaver pr. linje...";
+$T{DK}{short2} = "[:nick] skriver også korte linjer, med et gennesnit på [:letters]";
+
+$T{DK}{foul1} = "<b>[:nick]</b> er lidt stor i munden, [:per]% af linjerne indholte uhøfligt sprog";
+$T{DK}{foul2} = "<b>[:nick]</b> burde også vaske munden med sæbe med at det han fyrer af, [:per]% af tiden";
+$T{DK}{foul3} = "Ingen rapkæftede på [:channel]!";
+
+$T{DK}{smiles1} = "<b>[:nick]</b> er altid glad, [:per]% af linjerne havde glade ansigter :)";
+$T{DK}{smiles2} = "<b>[:nick]</b> er heller ikke en trist person, som smilede [:per]% af tiden";
+$T{DK}{smiles3} = "Ingen smiler på [:channel]! Op med humøret drenge og piger.";
+
+$T{DK}{sad1} = "<b>[:nick]</b> er meget ked af det for tiden, [:per]% af linjerne havde sørgelige ansigter :(";
+$T{DK}{sad2} = "<b>[:nick]</b> er også en trist person, som græd [:per]% af tiden";
+$T{DK}{sad3} = "Ingen er kede af det på [:channel]! Sikke en glad kanal :-)";
+
+$T{DK}{notopic} = "A topic was never set on this channel";
+
+## Topics
+
+$T{DK}{bignumtopic} = "Store numre";
+$T{DK}{othernumtopic} = "Andre interessante numre";
+$T{DK}{latesttopic} = "Sidst nyeste topics";
+$T{DK}{activetimestopic} = "Mest aktive tider";
+$T{DK}{activenickstopic} = "Mest aktive nicks";
+$T{DK}{mostwordstopic} = "Mest brugte ord";
+$T{DK}{referencetopic} = "Mest tiltalte nicks";
+
+## Other text
+
+$T{DK}{totallines} = "Hele antal linjer: [:lines]";
+$T{DK}{nick} = "Nick";
+$T{DK}{numberlines} = "Antal linjer";
+$T{DK}{randquote} = "Tilfældig sætning";
+$T{DK}{userpic} = "Brugerbillede";
+$T{DK}{nottop} = "Disse nåede ikke til tops:";
+$T{DK}{word} = "Ord";
+$T{DK}{numberuses} = "Antal forbrug";
+$T{DK}{lastused} = "Sidst brugt af";
+$T{DK}{pagetitle1} = "[:channel] @ [:network] statistikker af [:maintainer]";
+$T{DK}{pagetitle2} = "Statistikker lavet d. [:time]";
+$T{DK}{pagetitle3} = "Igennem denne [:days]\-dages periode, har der været <b>[:nicks]</b> forskellige nicks på [:channel].";
+
+### French
+
+# Not exactly the translation but something fun
+# Time translation needs more update
+
+$T{FR}{mostop1} = "<b>[:nick]</b> a donné [:ops] ops sur le channel...";
+$T{FR}{mostop2} = "<b>[:nick]</b> est aussi très poli : [:ops] ops de sa part";
+$T{FR}{mostop3} = "Etrange, aucun op n'a été donné sur [:channel]!";
+
+$T{FR}{mostdeop1} = "<b>[:nick]</b> est le shérif avec [:deops] deops...";
+$T{FR}{mostdeop2} = "<b>[:nick]</b> a deopé [:deops] utilisateurs";
+$T{FR}{mostdeop3} = "Wow, aucun op n'a été retiré sur [:channel]!";
+
+$T{FR}{question1} = "<b>[:nick]</b> est soit stupide soit trop curieux... [:per]% de ses lignes contiennent une question!";
+$T{FR}{question2} = "<b>[:nick]</b> n'en connait pas davantage, [:per]% de ses lignes étaient des questions";
+$T{FR}{question3} = "Personne ne pose de question ici, tous des génies sur ce channel?";
+
+$T{FR}{loud1} = "Le plus bruyant est <b>[:nick]</b> qui gueule [:per]% du temps!";
+$T{FR}{loud2} = "Un autre <i>vieux raleur</i> est <b>[:nick]</b> qui braille [:per]% du temps!";
+$T{FR}{loud3} = "Personne ne s'exclame ici, wow.";
+
+$T{FR}{gotkick1} = "<b>[:nick]</b> n'est pas trés populaire, kické [:kicks] fois!";
+$T{FR}{gotkick2} = "<b>[:nick]</b> n'a pas d'ami non plus, [:kicks] kicks reçus";
+
+$T{FR}{joins} = "<b>[:nick]</b> ne sait pas s'il doit rester ou partir, [:joins] visites durant cette période!";
+
+$T{FR}{kick1} = "<b>[:nick]</b> est malade ou alors aime bien jouer, son kick a sévi [:kicked] fois!";
+$T{FR}{kick2} = "Un disciple de [:oldnick], <b>[:nick]</b>, a kické [:kicked] personnes";
+$T{FR}{kick3} = "Les ops sont sympas ici, personne n'a été kické!";
+
+$T{FR}{mono1} = "<b>[:nick]</b> se parle à lui-meme trés souvent, il a écrit plus de 5 lignes d'un coup à [:monos] reprises!";
+$T{FR}{mono2} = "Un autre incompris est <b>[:nick]</b>, qui a tenté de le dépasser à [:monos] reprises";
+
+$T{FR}{long1} = "<b>[:nick]</b> a écrit les lignes les plus longues, avec en moyenne [:letters] lettres par ligne...";
+$T{FR}{long2} = "La moyenne sur [:channel] est de [:avg] lettres par ligne";
+
+$T{FR}{short1} = "<b>[:nick]</b> a écrit les lignes les plus courtes, avec en moyenne [:letters] lettres par ligne...";
+$T{FR}{short2} = "[:nick] n'a pas grand chose a dire non plus, en moyenne [:letters]";
+
+$T{FR}{foul1} = "<b>[:nick]</b> a du mal a s'exprimer, [:per]% de ses lignes contiennent des mots incompréhensibles";
+$T{FR}{foul2} = "<b>[:nick]</b> parle aussi comme un chartier, [:per]% du temps";
+$T{FR}{foul3} = "Personne n'a de problème pour parler sur [:channel]! Pendant combien de temps encore?";
+
+$T{FR}{smiles1} = "<b>[:nick]</b> apporte un peu de gaiété dans le monde, [:per]% de ses lignes contiennent un smiley :)";
+$T{FR}{smiles2} = "<b>[:nick]</b> n'est pas triste non plus, il sourit [:per]% du temps";
+$T{FR}{smiles3} = "Personne ne sourit sur [:channel]! Allez faites un effort quoi!";
+
+$T{FR}{sad1} = "<b>[:nick]</b> semble un peu triste en ce moment, [:per]% de ses lignes contiennent un smiley :(";
+$T{FR}{sad2} = "<b>[:nick]</b> est aussi bien triste, il pleure [:per]% du temps";
+$T{FR}{sad3} = "Personne n'est triste sur [:channel]! Quel channel merveilleux :-)";
+
+$T{FR}{notopic} = "Il n'y a jamais eu de topic sur ce channel";
+
+## Topics
+
+$T{FR}{bignumtopic} = "Les gros chiffres";
+$T{FR}{othernumtopic} = "D'autres chiffres intéressants";
+$T{FR}{latesttopic} = "Les derniers topics";
+$T{FR}{activetimestopic} = "Les périodes d'intense activité";
+$T{FR}{activenickstopic} = "Les nicks les plus actifs";
+$T{FR}{mostwordstopic} = "Les mots les plus utilisés";
+$T{FR}{referencetopic} = "Les mots les plus utilisés dans la conversation";
+
+## Other text
+
+$T{FR}{totallines} = "Nombre total de lignes: [:lines]";
+$T{FR}{nick} = "Nick";
+$T{FR}{numberlines} = "Nombre de lignes";
+$T{FR}{randquote} = "Citation typique";
+$T{FR}{userpic} = "Image";
+$T{FR}{nottop} = "Ne sont pas dans le classement";
+$T{FR}{word} = "Mot";
+$T{FR}{numberuses} = "Nombre d'occurences";
+$T{FR}{lastused} = "Dernière utilisation par";
+$T{FR}{pagetitle1} = "[:channel] @ [:network] stats par [:maintainer]";
+$T{FR}{pagetitle2} = "Statistiques générées le [:time]";
+$T{FR}{pagetitle3} = "Durant cette periode de [:days] jours, <b>[:nicks]</b> nicks différents sont apparus sur [:channel].";
+
+&main();        # Run the script

+ 23 - 0
users.cfg

@@ -0,0 +1,23 @@
+# Users config file for pisg
+# This file is used for a couple of things:
+# - Setting aliases for nicks
+# - Setting nicks to be ignored
+# - Setting user pictures
+# - Setting links to users homepages/e-mails
+#
+# The format is quite simple.
+#
+# To add aliases to a user:
+# <user nick="Joe" alias="Joe^away Joe^work">
+#
+# To add a picture to a user:
+# <user nick="Joe" pic="joe.jpg">
+#
+# To ignore a nick:
+# <user nick="nameofbot" ignore="y">
+#
+# You don't have to make it over many lines, you could just as easily do:
+# <user nick="Joe" alias="Joe^away Joe^work" pic="joe.jpg" link="http://www.joe.com">
+#
+# Have fun.. :)
+