PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/release/infobot-1.5.2/src/Misc.pl

#
Perl | 714 lines | 551 code | 108 blank | 55 comment | 102 complexity | 4954c383090d9c3d2ee6187cc51111e6 MD5 | raw file
Possible License(s): LGPL-2.0
  1. #
  2. # Misc.pl: Miscellaneous stuff.
  3. # Author: dms
  4. # Version: 20000124
  5. # NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997
  6. #
  7. use strict;
  8. use vars qw(%file %mask %param %cmdstats %myModules);
  9. use vars qw($msgType $who $bot_pid $nuh $shm $force_public_reply
  10. $no_timehires $bot_data_dir $addrchar);
  11. sub help {
  12. my $topic = shift;
  13. my $file = $bot_data_dir . '/infobot.help';
  14. my %help = ();
  15. # crude hack for performStrictReply() to work as expected.
  16. $msgType = 'private' if ( $msgType eq 'public' );
  17. if ( !open( FILE, $file ) ) {
  18. &ERROR("Failed reading help file ($file): $!");
  19. return;
  20. }
  21. while ( defined( my $help = <FILE> ) ) {
  22. $help =~ s/^[\# ].*//;
  23. chomp $help;
  24. next unless $help;
  25. my ( $key, $val ) = split( /:/, $help, 2 );
  26. $val =~ s/^\s+//;
  27. $val =~ s/^D:/\002 Desc\002:/;
  28. $val =~ s/^E:/\002Example\002:/;
  29. $val =~ s/^N:/\002 NOTE\002:/;
  30. $val =~ s/^U:/\002 Usage\002:/;
  31. $val =~ s/##/$key/;
  32. $val =~ s/__/\037/g;
  33. $val =~ s/==/ /;
  34. $help{$key} = '' if ( !exists $help{$key} );
  35. $help{$key} .= $val . "\n";
  36. }
  37. close FILE;
  38. if ( !defined $topic or $topic eq '' ) {
  39. &msg( $who, $help{'main'} );
  40. my $i = 0;
  41. my @array;
  42. my $count = scalar( keys %help );
  43. my $reply;
  44. foreach ( sort keys %help ) {
  45. push( @array, $_ );
  46. $reply =
  47. scalar(@array) . ' topics: ' . join( "\002,\002 ", @array );
  48. $i++;
  49. if ( length $reply > 400 or $count == $i ) {
  50. &msg( $who, $reply );
  51. undef @array;
  52. }
  53. }
  54. return '';
  55. }
  56. $topic = &fixString( lc $topic );
  57. if ( exists $help{$topic} ) {
  58. foreach ( split /\n/, $help{$topic} ) {
  59. &performStrictReply($_);
  60. }
  61. }
  62. else {
  63. &performStrictReply(
  64. "no help on $topic. Use 'help' without arguments.");
  65. }
  66. return '';
  67. }
  68. sub getPath {
  69. my ($pathnfile) = @_;
  70. ### TODO: gotta hate an if statement.
  71. if ( $pathnfile =~ /(.*)\/(.*?)$/ ) {
  72. return $1;
  73. }
  74. else {
  75. return '.';
  76. }
  77. }
  78. sub timeget {
  79. if ($no_timehires) { # fallback.
  80. return time();
  81. }
  82. else { # the real thing.
  83. return [ gettimeofday() ];
  84. }
  85. }
  86. sub timedelta {
  87. my ($start_time) = shift;
  88. if ($no_timehires) { # fallback.
  89. return time() - $start_time;
  90. }
  91. else { # the real thing.
  92. return tv_interval($start_time);
  93. }
  94. }
  95. ###
  96. ### FORM Functions.
  97. ###
  98. ###
  99. # Usage; &formListReply($rand, $prefix, @list);
  100. sub formListReply {
  101. my ( $rand, $prefix, @list ) = @_;
  102. my $total = scalar @list;
  103. my $maxshow = &getChanConfDefault( 'maxListReplyCount', 15, $chan );
  104. my $maxlen = &getChanConfDefault( 'maxListReplyLength', 400, $chan );
  105. my $reply;
  106. # remove irc overhead
  107. $maxlen -= 30;
  108. # no results.
  109. return $prefix . 'returned no results.' unless ($total);
  110. # random.
  111. if ($rand) {
  112. my @rand;
  113. foreach ( &makeRandom($total) ) {
  114. push( @rand, $list[$_] );
  115. last if ( scalar @rand == $maxshow );
  116. }
  117. if ( $total > $maxshow ) {
  118. @list = sort @rand;
  119. }
  120. else {
  121. @list = @rand;
  122. }
  123. }
  124. elsif ( $total > $maxshow ) {
  125. &status('formListReply: truncating list.');
  126. @list = @list[ 0 .. $maxshow - 1 ];
  127. }
  128. # form the reply.
  129. # FIXME: should grow and exit when full, not discard any that are oversize
  130. while () {
  131. $reply = $prefix . "(\002" . scalar(@list) . "\002";
  132. $reply .= " of \002$total\002" if ( $total != scalar @list );
  133. $reply .= '): ' . join( " \002;;\002 ", @list ) . '.';
  134. last if ( length($reply) < $maxlen and scalar(@list) <= $maxshow );
  135. last if ( scalar(@list) == 1 );
  136. pop @list;
  137. }
  138. return $reply;
  139. }
  140. ### Intelligence joining of arrays.
  141. # Usage: &IJoin(@array);
  142. sub IJoin {
  143. if ( !scalar @_ ) {
  144. return 'NULL';
  145. }
  146. elsif ( scalar @_ == 1 ) {
  147. return $_[0];
  148. }
  149. else {
  150. return join( ', ', @{_}[ 0 .. $#_ - 1 ] ) . " and $_[$#_]";
  151. }
  152. }
  153. #####
  154. # Usage: &Time2String(seconds);
  155. sub Time2String {
  156. my ($time) = @_;
  157. my $prefix = '';
  158. my ( @s, @t );
  159. return 'NULL' if ( !defined $time );
  160. return $time if ( $time !~ /\d+/ );
  161. if ( $time < 0 ) {
  162. $time = -$time;
  163. $prefix = '- ';
  164. }
  165. $t[0] = int($time) % 60;
  166. $t[1] = int( $time / 60 ) % 60;
  167. $t[2] = int( $time / 3600 ) % 24;
  168. $t[3] = int( $time / 86400 );
  169. push( @s, "$t[3]d" ) if ( $t[3] != 0 );
  170. push( @s, "$t[2]h" ) if ( $t[2] != 0 );
  171. push( @s, "$t[1]m" ) if ( $t[1] != 0 );
  172. push( @s, "$t[0]s" ) if ( $t[0] != 0 or !@s );
  173. my $retval = $prefix . join( ' ', @s );
  174. $retval =~ s/(\d+)/\002$1\002/g;
  175. return $retval;
  176. }
  177. ###
  178. ### FIX Functions.
  179. ###
  180. # Usage: &fixFileList(@files);
  181. sub fixFileList {
  182. my @files = @_;
  183. my %files;
  184. # generate a hash list.
  185. foreach (@files) {
  186. next unless /^(.*\/)(.*?)$/;
  187. $files{$1}{$2} = 1;
  188. }
  189. @files = (); # reuse the array.
  190. # sort the hash list appropriately.
  191. foreach ( sort keys %files ) {
  192. my $file = $_;
  193. my @keys = sort keys %{ $files{$file} };
  194. my $i = scalar(@keys);
  195. if ( scalar @keys > 3 ) {
  196. pop @keys while ( scalar @keys > 3 );
  197. push( @keys, '...' );
  198. }
  199. if ( $i > 1 ) {
  200. $file .= "\002{\002" . join( "\002|\002", @keys ) . "\002}\002";
  201. }
  202. else {
  203. $file .= $keys[0];
  204. }
  205. push( @files, $file );
  206. }
  207. return @files;
  208. }
  209. # Usage: &fixString($str);
  210. sub fixString {
  211. my ( $str, $level ) = @_;
  212. if ( !defined $str ) {
  213. &WARN('fixString: str == NULL.');
  214. return '';
  215. }
  216. for ($str) {
  217. s/^\s+//; # remove start whitespaces.
  218. s/\s+$//; # remove end whitespaces.
  219. s/\s+/ /g; # remove excessive whitespaces.
  220. next unless ( defined $level );
  221. if (s/[\cA-\c_]//ig) { # remove control characters.
  222. &DEBUG('stripped control chars');
  223. }
  224. }
  225. return $str;
  226. }
  227. # Usage: &fixPlural($str,$int);
  228. sub fixPlural {
  229. my ( $str, $int ) = @_;
  230. if ( !defined $str ) {
  231. &WARN('fixPlural: str == NULL.');
  232. return;
  233. }
  234. if ( !defined $int or $int =~ /^\D+$/ ) {
  235. &WARN('fixPlural: int != defined or int');
  236. return $str;
  237. }
  238. if ( $str eq 'has' ) {
  239. $str = 'have' if ( $int > 1 );
  240. }
  241. elsif ( $str eq 'is' ) {
  242. $str = 'are' if ( $int > 1 );
  243. }
  244. elsif ( $str eq 'was' ) {
  245. $str = 'were' if ( $int > 1 );
  246. }
  247. elsif ( $str eq 'this' ) {
  248. $str = 'these' if ( $int > 1 );
  249. }
  250. elsif ( $str =~ /y$/ ) {
  251. if ( $int > 1 ) {
  252. if ( $str =~ /ey$/ ) {
  253. $str .= 's'; # eg: 'money' => 'moneys'.
  254. }
  255. else {
  256. $str =~ s/y$/ies/;
  257. }
  258. }
  259. }
  260. else {
  261. $str .= 's' if ( $int != 1 );
  262. }
  263. return $str;
  264. }
  265. ##########
  266. ### get commands.
  267. ###
  268. sub getRandomLineFromFile {
  269. my ($file) = @_;
  270. if ( !open( IN, $file ) ) {
  271. &WARN("gRLfF: could not open ($file): $!");
  272. return;
  273. }
  274. my @lines = <IN>;
  275. close IN;
  276. if ( !scalar @lines ) {
  277. &ERROR('GRLF: nothing loaded?');
  278. return;
  279. }
  280. # could we use the filehandler instead and put it through getRandom?
  281. while ( my $line = &getRandom(@lines) ) {
  282. chop $line;
  283. next if ( $line =~ /^\#/ );
  284. next if ( $line =~ /^\s*$/ );
  285. return $line;
  286. }
  287. }
  288. sub getLineFromFile {
  289. my ( $file, $lineno ) = @_;
  290. if ( !-f $file ) {
  291. &ERROR("getLineFromFile: file '$file' does not exist.");
  292. return 0;
  293. }
  294. if ( open( IN, $file ) ) {
  295. my @lines = <IN>;
  296. close IN;
  297. if ( $lineno > scalar @lines ) {
  298. &ERROR('getLineFromFile: lineno exceeds line count from file.');
  299. return 0;
  300. }
  301. my $line = $lines[ $lineno - 1 ];
  302. chop $line;
  303. return $line;
  304. }
  305. else {
  306. &ERROR("gLFF: Could not open file ($file): $!");
  307. return 0;
  308. }
  309. }
  310. # Usage: &getRandom(@array);
  311. sub getRandom {
  312. my @array = @_;
  313. srand();
  314. return $array[ int( rand( scalar @array ) ) ];
  315. }
  316. # Usage: &getRandomInt('30-60'); &getRandomInt(5);
  317. # Desc : Returns a randomn integer between 'X-Y' or 1 and the value passed
  318. sub getRandomInt {
  319. my $str = shift;
  320. if ( !defined $str ) {
  321. &WARN('getRandomInt: str == NULL.');
  322. return undef;
  323. }
  324. if ( $str =~ /^(\d+(\.\d+)?)$/ ) {
  325. return int( rand $str ) + 1;
  326. }
  327. elsif ( $str =~ /^(\d+)-(\d+)$/ ) {
  328. return $1 if $1 == $2;
  329. my $min = $1 < $2 ? $1 : $2; # Swap is backwords
  330. my $max = $2 > $1 ? $2 : $1;
  331. return int( rand( $max - $min + 1 ) ) + $min;
  332. }
  333. else {
  334. # &ERROR("getRandomInt: invalid arg '$str'.");
  335. return undef;
  336. }
  337. }
  338. ##########
  339. ### Is commands.
  340. ###
  341. sub iseq {
  342. my ( $left, $right ) = @_;
  343. return 0 unless defined $right;
  344. return 0 unless defined $left;
  345. return 1 if ( $left =~ /^\Q$right$/i );
  346. }
  347. sub isne {
  348. my $retval = &iseq(@_);
  349. return 1 unless ($retval);
  350. return 0;
  351. }
  352. # Usage: &IsHostMatch($nuh);
  353. sub IsHostMatch {
  354. my ($thisnuh) = @_;
  355. my ( %this, %local );
  356. if ( $nuh =~ /^(\S+)!(\S+)@(\S+)/ ) {
  357. $local{'nick'} = lc $1;
  358. $local{'user'} = lc $2;
  359. $local{'host'} = &makeHostMask( lc $3 );
  360. }
  361. if ( !defined $thisnuh ) {
  362. &WARN('IHM: thisnuh == NULL.');
  363. return 0;
  364. }
  365. elsif ( $thisnuh =~ /^(\S+)!(\S+)@(\S+)/ ) {
  366. $this{'nick'} = lc $1;
  367. $this{'user'} = lc $2;
  368. $this{'host'} = &makeHostMask( lc $3 );
  369. }
  370. else {
  371. &WARN("IHM: thisnuh is invalid '$thisnuh'.");
  372. return 1 if ( $thisnuh eq '' );
  373. return 0;
  374. }
  375. # auth if 1) user and host match 2) user and nick match.
  376. # this may change in the future.
  377. if ( $this{'user'} =~ /^\Q$local{'user'}\E$/i ) {
  378. return 2 if ( $this{'host'} eq $local{'host'} );
  379. return 1 if ( $this{'nick'} eq $local{'nick'} );
  380. }
  381. return 0;
  382. }
  383. ####
  384. # Usage: &isStale($file, $age);
  385. sub isStale {
  386. my ( $file, $age ) = @_;
  387. if ( !defined $age ) {
  388. &WARN('isStale: age == NULL.');
  389. return 1;
  390. }
  391. if ( !defined $file ) {
  392. &WARN('isStale: file == NULL.');
  393. return 1;
  394. }
  395. &DEBUG("!exist $file") if ( !-f $file );
  396. return 1 unless ( -f $file );
  397. if ( $file =~ /idx/ ) {
  398. my $age2 = time() - ( stat($file) )[9];
  399. &VERB( "stale: $age2. (" . &Time2String($age2) . ')', 2 );
  400. }
  401. $age *= 60 * 60 * 24 if ( $age >= 0 and $age < 30 );
  402. return 1 if ( time() - ( stat($file) )[9] > $age );
  403. return 0;
  404. }
  405. sub isFileUpdated {
  406. my ( $file, $time ) = @_;
  407. if ( !-f $file ) {
  408. return 1;
  409. }
  410. my $time_file = ( stat $file )[9];
  411. if ( $time <= $time_file ) {
  412. return 0;
  413. }
  414. else {
  415. return 1;
  416. }
  417. }
  418. ##########
  419. ### make commands.
  420. ###
  421. # Usage: &makeHostMask($host);
  422. sub makeHostMask {
  423. my ($host) = @_;
  424. my $nu = '';
  425. if ( $host =~ s/^(\S+!\S+\@)// ) {
  426. &DEBUG("mHM: detected nick!user\@ for host arg; fixing");
  427. &DEBUG("nu => $nu");
  428. $nu = $1;
  429. }
  430. if ( $host =~ /^$mask{ip}$/ ) {
  431. return $nu . "$1.$2.$3.*";
  432. }
  433. my @array = split( /\./, $host );
  434. return $nu . $host if ( scalar @array <= 3 );
  435. return $nu . '*.' . join( '.', @{array}[ 1 .. $#array ] );
  436. }
  437. # Usage: &makeRandom(int);
  438. sub makeRandom {
  439. my ($max) = @_;
  440. my @retval;
  441. my %done;
  442. if ( $max =~ /^\D+$/ ) {
  443. &ERROR("makeRandom: arg ($max) is not integer.");
  444. return 0;
  445. }
  446. if ( $max < 1 ) {
  447. &ERROR("makeRandom: arg ($max) is not positive.");
  448. return 0;
  449. }
  450. srand();
  451. while ( scalar keys %done < $max ) {
  452. my $rand = int( rand $max );
  453. next if ( exists $done{$rand} );
  454. push( @retval, $rand );
  455. $done{$rand} = 1;
  456. }
  457. return @retval;
  458. }
  459. sub checkMsgType {
  460. my ($reply) = @_;
  461. return unless ( &IsParam('minLengthBeforePrivate') );
  462. return if ($force_public_reply);
  463. if ( length $reply > $param{'minLengthBeforePrivate'} ) {
  464. &status(
  465. "Reply: len reply > minLBP ($param{'minLengthBeforePrivate'}); msgType now private."
  466. );
  467. $msgType = 'private';
  468. }
  469. }
  470. ###
  471. ### Valid.
  472. ###
  473. # Usage: &validExec($string);
  474. sub validExec {
  475. my ($str) = @_;
  476. if ( $str =~ /[\`\'\"\|]/ ) { # invalid.
  477. return 0;
  478. }
  479. else { # valid.
  480. return 1;
  481. }
  482. }
  483. # Usage: &hasProfanity($string);
  484. sub hasProfanity {
  485. my ($string) = @_;
  486. my $profanity = 1;
  487. for ( lc $string ) {
  488. /fuck/ and last;
  489. /dick|dildo/ and last;
  490. /shit/ and last;
  491. /pussy|[ck]unt/ and last;
  492. /wh[0o]re|bitch|slut/ and last;
  493. $profanity = 0;
  494. }
  495. return $profanity;
  496. }
  497. sub IsChanConfOrWarn {
  498. my ($param) = @_;
  499. if ( &IsChanConf($param) > 0 ) {
  500. return 1;
  501. }
  502. else {
  503. ### TODO: specific reason why it failed.
  504. &msg( $who,
  505. "unfortunately, \002$param\002 is disabled in my configuration" )
  506. unless ($addrchar);
  507. return 0;
  508. }
  509. }
  510. sub Forker {
  511. my ( $label, $code ) = @_;
  512. my $pid;
  513. &shmFlush();
  514. &VERB( 'double fork detected; not forking.', 2 ) if ( $$ != $bot_pid );
  515. if ( &IsParam('forking') and $$ == $bot_pid ) {
  516. return unless &addForked($label);
  517. $SIG{CHLD} = 'IGNORE';
  518. $pid = eval { fork() };
  519. return if $pid; # parent does nothing
  520. select( undef, undef, undef, 0.2 );
  521. # &status("fork starting for '$label', PID == $$.");
  522. &status(
  523. "--- fork starting for '$label', PID == $$, bot_pid == $bot_pid ---"
  524. );
  525. &shmWrite( $shm, "SET FORKPID $label $$" );
  526. sleep 1;
  527. }
  528. ### TODO: use AUTOLOAD
  529. ### very lame hack.
  530. if ( $label !~ /-/ and !&loadMyModule($label) ) {
  531. &DEBUG('Forker: failed?');
  532. &delForked($label);
  533. }
  534. if ( defined $code ) {
  535. $code->(); # weird, hey?
  536. }
  537. else {
  538. &WARN('Forker: code not defined!');
  539. }
  540. &delForked($label);
  541. }
  542. sub closePID {
  543. return 1 unless ( exists $file{PID} );
  544. return 1 unless ( -f $file{PID} );
  545. return 1 if ( unlink $file{PID} );
  546. return 0 if ( -f $file{PID} );
  547. }
  548. sub mkcrypt {
  549. my ($str) = @_;
  550. my $salt = join '',
  551. ( '.', '/', 0 .. 9, 'A' .. 'Z', 'a' .. 'z' )[ rand 64, rand 64 ];
  552. return crypt( $str, $salt );
  553. }
  554. sub closeStats {
  555. return unless ( &getChanConfList('ircTextCounters') );
  556. foreach ( keys %cmdstats ) {
  557. my $type = $_;
  558. my $i = &sqlSelect(
  559. 'stats',
  560. 'counter',
  561. {
  562. nick => $type,
  563. type => 'cmdstats',
  564. }
  565. );
  566. my $z = 0;
  567. $z++ unless ($i);
  568. $i += $cmdstats{$type};
  569. &sqlSet(
  570. 'stats',
  571. { 'nick' => $type },
  572. {
  573. type => 'cmdstats',
  574. 'time' => time(),
  575. counter => $i,
  576. }
  577. );
  578. }
  579. }
  580. 1;
  581. # vim:ts=4:sw=4:expandtab:tw=80