#!/usr/bin/perl
################################################################################
# #
# ############################################################################ #
# # Title : xtp.pl # #
# # Description : Randomly picks and sets a topic for a channel from a file. # #
# # Coded by : Justin "HEYO--!" Lee # #
# # Version : 0.4.2-2 # #
# # Date : 2008-12-13 # #
# # License : GNU General Public License v.2 # #
# # Copyright (C) 2008 Justin Lee <kool.name at gmail.com> # #
# ############################################################################ #
# #
# ############################################################################ #
# # This program is free software; you can redistribute it and/or # #
# # modify it under the terms of the GNU General Public License version 2 # #
# # as published by the Free Software Foundation. # #
# # # #
# # 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., 51 Franklin Street, Fifth Floor, Boston, MA # #
# # 02110-1301, USA. Or, see: <http://www.gnu.org/licenses/> # #
# ############################################################################ #
# #
################################################################################
use strict;
Xchat::register('XTP', '0.4.2-2', 'Randomly picks and sets a topic for a channel from a file.');
Xchat::hook_print('Channel Message', 'channel_watch');
Xchat::hook_command('tcmd','command_wrapper');
## preferences ##
my $topic_dir = Xchat::get_info('xchatdir');
my $topic_file = 'rtopics.txt';
my $topic_set_time = '3600000'; # 60 minutes
my $topic_prefix = '#51: ';
my $channel = '#51';
my $topic_trigger = '!topic';
my $topic_autoset = 1;
## stop editing ##
# I hate global vars
my @topics;
my %prefix = (
'~' => 5,
'&' => 4,
'@' => 3,
'%' => 2,
'+' => 1,
'' => 0
);
chdir($topic_dir) or die;
if ($topic_autoset) {
# autoset in one minute
# allows for time joining
Xchat::hook_timer(360000,"topic_set");
} else {
# don't autoset, set in $time ms
timer_set();
}
Xchat::print('XTP: Loaded.');
sub topic_set {
# reset the timer as long as
# topic_set(1) is not called
timer_set() unless shift;
# build the queue if needed
build_stack() unless @topics;
Xchat::command("topic $channel $topic_prefix @{[ pop(@topics) ]}",$channel) or warn;
return Xchat::EAT_NONE;
}
# watch for !topic <command> in $channel
sub channel_watch {
# channel check
return unless uc(Xchat::get_info('channel')) eq uc($channel);
Xchat::set_context(Xchat::find_context($channel));
# user check # this section could be something like:
my $user = Xchat::user_info($_[0][0]); # return unless $prefix{$_[0][2]} > $val;
# must have a voice or higher (>=1) # but I don't know how reliable $_[0][2] is
return unless $prefix{$user->{'prefix'}} > 0; # when compared with Xchat::user_info();
#line check
return unless $_[0][1] =~ /^$topic_trigger(?:\b.*)?$/oi;
# pass the user's line ($_[0][1]) on as an array (split)
unless (topic_command(split(/\s+/,$_[0][1]))) {
# if t_command returned 0, print help
Xchat::command("msg $channel XTP Basics: !topic ADD This is a new topic. && !topic",$channel) or warn;
Xchat::command("msg $channel XTP: For command help, ask someone who knows.",$channel) or warn;
}
return Xchat::EAT_NONE;
}
sub command_wrapper {
unless (topic_command(@{$_[0]})) {
Xchat::print('XTP: Non-fatal error.');
}
return Xchat::EAT_XCHAT;
}
sub topic_command {
# !topic
if ($#_ == 0) {
topic_set(1);
}
# !topic delete (not implemented)
elsif ($_[1] =~ /^(?:(?:de|ba)l(?:eted?)?|rem(?:ove)?)$/oi) { # don't ask :)
return 0;
}
# !topic update
elsif ($_[1] =~ /^(?:update|refresh)$/oi) {
build_stack();
}
# !topic add
elsif ($_[1] =~ /^add$/oi) {
add_topic(join(' ',@_[2..$#_]));
}
# !topic shuffle
elsif ($_[1] =~ /^shuffle$/oi) {
if (@topics > 1) {
# needs @topics to contain 2+ entries
fisher_yates_shuffle(\@topics);
} else {
# else it rebuilds the stack
build_stack();
}
}
# !topic clean
elsif ($_[1] =~ /^clean(?:se)?$/oi) {
topic_clean();
}
# !topic imabigdouche
else {
return 0;
}
return 1;
}
sub build_stack {
# empty the topic stack
@topics = ();
open(TFILE,"<$topic_file") or die;
while(<TFILE>) {
chomp;
# skip blank lines
next if /^(\s*)$/o;
# clean up the rest
s/^\s+//o;
s/\s+$//o;
# add it to the stack
push(@topics,$_);
}
close(TFILE) or die;
# shuffle the stack
fisher_yates_shuffle(\@topics);
}
sub add_topic {
open(TFILE,">>$topic_file") or die;
print TFILE "\n", shift;
close(TFILE) or die;
}
# reset the timer if set time != and > 0
sub timer_set {
if ( $topic_set_time and $topic_set_time > 0 ) {
Xchat::hook_timer($topic_set_time,"topic_set");
}
}
# cookbook
sub fisher_yates_shuffle {
my $array = shift;
my $i;
for ($i = @$array; --$i; ) {
my $j = int rand ($i+1);
next if $i == $j;
@$array[$i,$j] = @$array[$j,$i];
}
}
# clean up the topic file
sub topic_clean {
if (-f "$topic_file.bak") {
# remove the old backup, if it exists, or abort (non-fatal)
unless (unlink("$topic_file.bak")) {
Xchat::print("XTP: Error: Unable to remove `$topic_file.bak', aborting re-write.");
return 0;
}
}
# backup the old file by renaming it to *.bak, or abort (non-fatal)
unless (rename($topic_file,"$topic_file.bak")) {
Xchat::print("XTP: Error: Unable to backup $topic_file by renaming, aborting re-write.");
return 0;
}
# if the topic file can't be opened for re-writing, attempt recovery of old one
unless (open(TFILE,">$topic_file")) {
Xchat::print("XTP: Error: Wasn't able to open $topic_file for writing, attempting to recover $topic_file.bak...");
# if it can't be recovered, we're screwed. drop a hint and die
unless (rename("$topic_file.bak",$topic_file)) {
Xchat::print('XTP: Fatal Error: Recovery failed, shit. You better fix it.');
Xchat::print("XTP: Hint: Poke around `$topic_dir'.");
die;
}
}
# if it all works out, we're ready to write!
print TFILE "$_\n" foreach @topics;
close(TFILE) or die;
}