2 # Topic.pl: Advanced topic management (maxtopiclen>=512)
4 # Version: v0.8 (19990919).
9 use vars qw(%topiccmp);
10 no strict "refs"; ### FIXME!!!
12 ###############################
13 ##### INTERNAL FUNCTIONS
14 ###############################
17 # Usage: &topicDecipher(chan);
22 if (!exists $topic{$chan}{'Current'}) {
23 &DEBUG("Topic: does not exist for $chan.");
27 foreach (split /\|\|/, $topic{$chan}{'Current'}) {
31 # very nice fix to solve the null subtopic problem.
32 ### if nick contains a space, treat topic as ownerless.
34 next unless ($1 =~ /\s/);
38 my $owner = "Unknown";
39 if (/(.*)\s+\((.*?)\)$/) {
44 if (grep /^\Q$subtopic\E\|\|\Q$owner\E$/, @results) {
45 &DEBUG("topic: we have found a dupe in the topic, not adding.");
49 push(@results, "$subtopic||$owner");
56 # Usage: &topicCipher(@topics);
59 &DEBUG("topicCipher: topic is NULL.");
65 my ($subtopic, $setby) = split /\|\|/;
67 $result .= " || $subtopic";
68 next if ($setby eq "" or $setby =~ /unknown/i);
70 $result .= " (" . $setby . ")";
73 return substr($result, 4);
77 # Usage: &topicNew($chan, $topic, $updateMsg, $topicUpdate);
79 my ($chan, $topic, $updateMsg, $topicUpdate) = @_;
82 &DEBUG("topic: chan{$chan} is +t.") if ($channels{$chan}{t});
83 &DEBUG("topic: chan{$chan} is -t.") unless ($channels{$chan}{t});
84 &DEBUG("topic: I have +o.") if ($channels{$chan}{o}{$ident});
85 &DEBUG("topic: I don't have +o.") unless ($channels{$chan}{o}{$ident});
87 if ($channels{$chan}{t} and !$channels{$chan}{o}{$ident}) {
88 &msg($who, "error: cannot change topic without ops. (channel is +t) :(");
92 if (defined $topiccmp{$chan} and $topiccmp{$chan} eq $topic) {
93 &msg($who, "warning: action had no effect on topic; no change required.");
97 # bail out if the new topic is too long.
98 my $newlen = length($chan.$topic);
99 if ($newlen > $maxlen) {
100 &msg($who, "new topic will be too long. ($newlen > $maxlen)");
104 $topic{$chan}{'Current'} = $topic;
106 # notification that the topic was altered.
107 if (!$topicUpdate) { # for cached changes with '-'.
108 &performReply("okay");
112 if ($updateMsg ne "") {
113 &msg($who, $updateMsg);
116 $topic{$chan}{'Last'} = $topic;
117 $topic{$chan}{'Who'} = $orig{who}."!".$uh;
118 $topic{$chan}{'Time'} = time();
119 rawout("TOPIC $chan :$topic");
120 &topicAddHistory($chan,$topic);
125 # Usage: &topicAddHistory($chan,$topic);
126 sub topicAddHistory {
127 my ($chan, $topic) = @_;
130 return 1 if ($topic eq ""); # required fix.
132 foreach (@{ $topic{$chan}{'History'} }) {
133 next if ($_ ne "" and $_ ne $topic);
134 # checking length is required.
142 my @topics = @{ $topic{$chan}{'History'} };
143 unshift(@topics, $topic);
144 pop(@topics) while (scalar @topics > 6);
145 $topic{$chan}{'History'} = \@topics;
150 ###############################
151 ##### HELPER FUNCTIONS
152 ###############################
166 ###############################
168 ###############################
171 # Usage: &Topic($cmd, $args);
173 my ($chan, $cmd, $args) = @_;
176 if ($cmd =~ /^-(\S+)/) {
181 if ($cmd =~ /^(add)$/i) {
188 # heh, joeyh. 19990819. -xk
189 if ($who =~ /\|\|/) {
190 &msg($who, "error: you have an invalid nick, loser!");
194 my @prev = &topicDecipher($chan);
195 my $new = "$args ($orig{who})";
196 $topic{$chan}{'What'} = "Added '$args'.";
198 $new = &topicCipher(@prev, sprintf("%s||%s", $args, $who));
200 &topicNew($chan, $new, "", $topicUpdate);
202 } elsif ($cmd =~ /^(del|delete|rm|remove|kill|purge)$/i) {
204 my @subtopics = &topicDecipher($chan);
205 my $topiccount = scalar @subtopics;
207 if ($topiccount == 0) {
208 &msg($who, "No topic set.");
217 $args = ",".$args.",";
219 $args =~ s/(first|1st)/1/i;
220 $args =~ s/last/$topiccount/i;
221 $args =~ s/,-(\d+)/,1-$1/;
222 $args =~ s/(\d+)-,/,$1-$topiccount/;
224 if ($args !~ /[\,\-\d]/) {
225 &msg($who, "error: Invalid argument ($args).");
229 foreach (split ",", $args) {
233 # change to hash list instead of array?
234 if (/^(\d+)-(\d+)$/) {
235 my ($from,$to) = ($1,$2);
236 ($from,$to) = ($2,$1) if ($from > $to);
238 push(@delete, $1..$2);
239 } elsif (/^(\d+)$/) {
242 &msg($who, "error: Invalid sub-argument ($_).");
246 $topic{$chan}{'What'} = "Deleted ".join("/",@delete);
249 if ($_ > $topiccount || $_ < 1) {
250 &msg($who, "error: argument out of range. (max: $topiccount)");
253 # skip if already deleted.
254 # only checked if x-y range is given.
255 next unless (defined($subtopics[$_-1]));
257 my ($subtopic,$whoby) = split('\|\|', $subtopics[$_-1]);
258 $whoby = "unknown" if ($whoby eq "");
259 &msg($who, "Deleting topic: $subtopic ($whoby)");
260 undef $subtopics[$_-1];
265 foreach (@subtopics) {
266 next unless (defined $_);
267 push(@newtopics, $_);
270 &topicNew($chan, &topicCipher(@newtopics), "", $topicUpdate);
272 } elsif ($cmd =~ /^list$/i) {
274 my @topics = &topicDecipher($chan);
275 if (!scalar @topics) {
276 &msg($who, "No topics for \002$chan\002.");
280 &msg($who, "Topics for \002$chan\002:");
281 &msg($who, "No \002[\002 Set by \002]\002 Topic");
285 my ($subtopic, $setby) = split /\|\|/;
287 &msg($who, sprintf(" %d. \002[\002%-10s\002]\002 %s",
288 $i, $setby, $subtopic));
291 &msg($who, "End of Topics.");
293 } elsif ($cmd =~ /^(mod|modify|change|alter)$/i) {
301 # a warning message instead of halting. we kind of trust the user now.
302 if ($args =~ /\|\|/) {
303 &msg($who, "warning: adding double pipes manually == evil. be warned.");
306 $topic{$chan}{'What'} = "SAR $args";
309 if ($args =~ m|^\s*s([/,#])(.+?)\1(.*?)\1([a-z]*);?\s*$|) {
310 my ($delim, $op, $np, $flags) = ($1,quotemeta $2,$3,$4);
312 if ($flags !~ /^(g)?$/) {
313 &msg($who, "error: Invalid flags to regex.");
317 my $topic = $topic{$chan}{'Current'};
319 if (($flags eq "g" and $topic =~ s/$op/$np/g) ||
320 ($flags eq "" and $topic =~ s/$op/$np/)) {
322 $_ = "Modifying topic with sar s/$op/$np/.";
323 &topicNew($chan, $topic, $_, $topicUpdate);
325 &msg($who, "warning: regex not found in topic.");
330 &msg($who, "error: Invalid regex. Try s/1/2/, s#3#4#...");
332 } elsif ($cmd =~ /^(mv|move)$/i) {
340 if ($args =~ /^(first|last|\d+)\s+(before|after|swap)\s+(first|last|\d+)$/i) {
341 my ($from, $action, $to) = ($1,$2,$3);
342 my @subtopics = &topicDecipher($chan);
344 my $topiccount = scalar @subtopics;
346 if ($topiccount == 1) {
347 &msg($who, "error: impossible to move the only subtopic, dumbass.");
351 # Is there an easier way to do this?
352 $from =~ s/first/1/i;
354 $from =~ s/last/$topiccount/i;
355 $to =~ s/last/$topiccount/i;
357 if ($from > $topiccount || $to > $topiccount || $from < 1 || $to < 1) {
358 &msg($who, "error: <from> or <to> is out of range.");
363 &msg($who, "error: <from> and <to> are the same.");
367 $topic{$chan}{'What'} = "Move $from to $to";
369 if ($action =~ /^(swap)$/i) {
370 my $tmp = $subtopics[$to - 1];
371 $subtopics[$to - 1] = $subtopics[$from - 1];
372 $subtopics[$from - 1] = $tmp;
374 $_ = "Swapped #\002$from\002 with #\002$to\002.";
375 &topicNew($chan, &topicCipher(@subtopics), $_, $topicUpdate);
380 # Is there a better way to do this? guess not.
382 my $subtopic = $subtopics[$from - 1];
383 foreach (@subtopics) {
385 $newtopics[$j] = $_ if ($i != $from);
389 if ($action =~ /^(before|b4)$/i) {
390 $newtopics[$to*2-2] = $subtopic;
393 $newtopics[$to*2] = $subtopic;
396 undef @subtopics; # lets reuse this array.
397 foreach (@newtopics) {
399 push(@subtopics, $_);
402 $_ = "Moved #\002$from\002 $action #\002$to\002.";
403 &topicNew($chan, &topicCipher(@subtopics), $_, $topicUpdate);
408 &msg($who, "Invalid arguments.");
410 } elsif ($cmd =~ /^shuffle$/i) {
412 my @subtopics = &topicDecipher($chan);
415 $topic{$chan}{'What'} = "shuffled";
417 foreach (&makeRandom(scalar @subtopics)) {
418 push(@newtopics, $subtopics[$_]);
421 $_ = "Shuffling the bag of lollies.";
422 &topicNew($chan, &topicCipher(@newtopics), $_, $topicUpdate);
424 } elsif ($cmd =~ /^(history)$/i) {
426 if (!scalar @{$topic{$chan}{'History'}}) {
427 &msg($who, "Sorry, no topics in history list.");
431 &msg($who, "History of topics on \002$chan\002:");
432 for (1 .. scalar @{$topic{$chan}{'History'}}) {
433 my $topic = ${$topic{$chan}{'History'}}[$_-1];
434 &msg($who, " #\002$_\002: $topic");
436 # To prevent excess floods.
437 sleep 1 if (length($topic) > 160);
439 &msg($who, "End of list.");
441 } elsif ($cmd =~ /^restore$/i) {
444 &help("topic restore");
448 $topic{$chan}{'What'} = "Restore topic $args";
450 # following needs to be verified.
451 if ($args =~ /^last$/i) {
452 if (${$topic{$chan}{'History'}}[0] eq $topic{$chan}{'Current'}) {
453 &msg($who,"error: cannot restore last topic because it's mine.");
459 if ($args =~ /\d+/) {
460 if ($args > $#{$topic{$chan}{'History'}} || $args < 1) {
461 &msg($who, "error: argument is out of range.");
465 $_ = "Changing topic according to request.";
466 &topicNew($chan, ${$topic{$chan}{'History'}}[$args-1], $_, $topicUpdate);
471 &msg($who, "error: argument is not positive integer.");
473 } elsif ($cmd =~ /^rehash$/i) {
475 $_ = "Rehashing topic...";
476 $topic{$chan}{'What'} = "Rehash";
477 &topicNew($chan, $topic{$chan}{'Current'}, $_, 1);
479 } elsif ($cmd =~ /^info$/i) {
481 my $reply = "no topic info.";
482 if (exists $topic{$chan}{'Who'} and exists $topic{$chan}{'Time'}) {
483 $reply = "topic on \002$chan\002 was last set by ".
484 $topic{$chan}{'Who'}. ". This was done ".
485 &Time2String(time() - $topic{$chan}{'Time'}) ." ago.";
486 my $change = $topic{$chan}{'What'};
487 $reply .= "Change => $change" if (defined $change);
490 &performStrictReply($reply);
493 if ($cmd ne "" and $cmd !~ /^help/i) {
494 &msg($who, "Invalid command [$cmd].");
495 &msg($who, "Try 'help topic'.");