#!/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.1 # # # # Date : 2008-12-13 # # # # License : GNU General Public License v.2 # # # # Copyright (C) 2008 Justin Lee # # # ############################################################################ # # # # ############################################################################ # # # 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: # # # ############################################################################ # # # ################################################################################ use strict; Xchat::register('XTP', '0.4.1', 'Randomly picks and sets a topic for a channel from a file.'); Xchat::hook_print('Channel Message', 'channel_watch'); Xchat::hook_command('tcom','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(60000,"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; } # watch for !topic 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: 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) { build_stack(1); } # !topic imabigdouche else { return 0; } return 1; } sub build_stack { # empty the topic stack @topics = (); open(TFILE,"<$topic_file") or die; while() { 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); # build_stack(1), write out the shuffled stack if (shift) { 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; } return 1; } 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]; } }