]>
jfr.im git - irc/SurrealServices/srsv.git/blob - branches/erry-devel/modules/serviceslibs/memoserv.pm
020621d7cd9345ade57c0710347b1becc5846afb
1 # This file is part of SurrealServices.
3 # SurrealServices is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # SurrealServices is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with SurrealServices; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 use DBI
qw(:sql_types) ;
27 use SrSv
:: Agent
qw(is_agent) ;
30 use SrSv
:: Text
:: Format
qw(columnar) ;
33 use SrSv
:: User
qw(get_user_nick get_user_id :flood) ;
34 use SrSv
:: User
:: Notice
;
35 use SrSv
:: Help
qw( sendhelp ) ;
37 use SrSv
:: NickReg
:: Flags
;
38 use SrSv
:: NickReg
:: User
qw(is_identified get_nick_user_nicks) ;
40 use SrSv
:: MySQL
' $dbh ' ;
42 use SrSv
:: Util
qw( makeSeqList ) ;
48 our $msnick_default = 'MemoServ' ;
49 our $msnick = $msnick_default ;
52 $send_memo , $send_chan_memo , $get_chan_recipients ,
56 $get_memo , $get_memo_full , $get_memo_count , $get_unread_memo_count ,
60 $delete_memo , $purge_memos , $delete_all_memos ,
63 $add_ignore , $get_ignore_num , $del_ignore_nick , $list_ignore , $chk_ignore ,
64 $wipe_ignore , $purge_ignore ,
68 $send_memo = $dbh -> prepare ( "INSERT INTO memo SELECT ?, id, NULL, UNIX_TIMESTAMP(), NULL, ? FROM nickreg WHERE nick=?" );
69 $send_chan_memo = $dbh -> prepare ( "INSERT INTO memo SELECT ?, nickreg.id, ?, ?, NULL, ? FROM chanacc, nickreg
70 WHERE chanacc.chan=? AND chanacc.level >= ? AND chanacc.nrid=nickreg.id
71 AND !(nickreg.flags & " . NRF_NOMEMO
() . ")" );
72 $get_chan_recipients = $dbh -> prepare ( "SELECT user.nick FROM user, nickid, nickreg, chanacc WHERE
73 user.id=nickid.id AND nickid.nrid=chanacc.nrid AND chanacc.nrid=nickreg.id AND chanacc.chan=?
75 !(nickreg.flags & " . NRF_NOMEMO
() . ")" );
77 $get_memo_list = $dbh -> prepare ( "SELECT memo.src, memo.chan, memo.time, memo.flag, memo.msg FROM memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id ORDER BY memo.time ASC" );
79 $get_memo = $dbh -> prepare ( "SELECT memo.src, memo.chan, memo.time
80 FROM memo JOIN nickreg ON (memo.dstid=nickreg.id) WHERE nickreg.nick=? ORDER BY memo.time ASC LIMIT 1 OFFSET ?" );
81 $get_memo -> bind_param ( 2 , 0 , SQL_INTEGER
);
82 $get_memo_full = $dbh -> prepare ( "SELECT memo.src, memo.chan, memo.time, memo.flag, memo.msg FROM memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id ORDER BY memo.time ASC LIMIT 1 OFFSET ?" );
83 $get_memo_full -> bind_param ( 2 , 0 , SQL_INTEGER
);
84 $get_memo_count = $dbh -> prepare ( "SELECT COUNT(*) FROM memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id" );
85 $get_unread_memo_count = $dbh -> prepare ( "SELECT COUNT(*) FROM memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id AND memo.flag=0" );
87 $set_flag = $dbh -> prepare ( "UPDATE memo, nickreg SET memo.flag=? WHERE memo.src=? AND nickreg.nick=? AND memo.dstid=nickreg.id AND memo.chan=? AND memo.time=?" );
89 $delete_memo = $dbh -> prepare ( "DELETE FROM memo USING memo, nickreg WHERE memo.src=? AND nickreg.nick=? AND memo.dstid=nickreg.id AND memo.chan=? AND memo.time=?" );
90 $purge_memos = $dbh -> prepare ( "DELETE FROM memo USING memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id AND memo.flag=1" );
91 $delete_all_memos = $dbh -> prepare ( "DELETE FROM memo USING memo, nickreg WHERE nickreg.nick=? AND memo.dstid=nickreg.id" );
93 $add_ignore = $dbh -> prepare ( "INSERT INTO ms_ignore (ms_ignore.nrid, ms_ignore.ignoreid, time)
94 SELECT nickreg.id, ignorenick.id, UNIX_TIMESTAMP() FROM nickreg, nickreg AS ignorenick
95 WHERE nickreg.nick=? AND ignorenick.nick=?" );
96 $del_ignore_nick = $dbh -> prepare ( "DELETE FROM ms_ignore USING ms_ignore
97 JOIN nickreg ON (ms_ignore.nrid=nickreg.id)
98 JOIN nickreg AS ignorenick ON(ms_ignore.ignoreid=ignorenick.id)
99 WHERE nickreg.nick=? AND ignorenick.nick=?" );
100 $get_ignore_num = $dbh -> prepare ( "SELECT ignorenick.nick FROM ms_ignore
101 JOIN nickreg ON (ms_ignore.nrid=nickreg.id)
102 JOIN nickreg AS ignorenick ON(ms_ignore.ignoreid=ignorenick.id)
104 ORDER BY ms_ignore.time LIMIT 1 OFFSET ?" );
105 $get_ignore_num -> bind_param ( 2 , 0 , SQL_INTEGER
);
107 $list_ignore = $dbh -> prepare ( "SELECT ignorenick.nick, ms_ignore.time
108 FROM ms_ignore, nickreg, nickreg AS ignorenick
109 WHERE nickreg.nick=? AND ms_ignore.nrid=nickreg.id AND ms_ignore.ignoreid=ignorenick.id
110 ORDER BY ms_ignore.time" );
111 $chk_ignore = $dbh -> prepare ( "SELECT 1
112 FROM ms_ignore, nickreg, nickreg AS ignorenick
113 WHERE nickreg.nick=? AND ms_ignore.nrid=nickreg.id AND ignorenick.nick=? AND ms_ignore.ignoreid=ignorenick.id" );
115 $wipe_ignore = $dbh -> prepare ( "DELETE FROM ms_ignore USING ms_ignore JOIN nickreg ON(ms_ignore.nrid=nickreg.id) WHERE nickreg.nick=?" );
116 $purge_ignore = $dbh -> prepare ( "DELETE FROM ms_ignore USING ms_ignore JOIN nickreg ON(ms_ignore.ignoreid=nickreg.id) WHERE nickreg.nick=?" );
119 ### MEMOSERV COMMANDS ###
122 my ( $src , $dst , $msg ) = @_ ;
124 my @args = split ( /\s+/ , $msg );
125 my $cmd = shift @args ;
127 my $user = { NICK
=> $src , AGENT
=> $dst };
129 return if flood_check
( $user );
131 if ( $cmd =~ /^send$/i ) {
133 my @args = split ( /\s+/ , $msg , 3 );
134 ms_send
( $user , $args [ 1 ], $args [ 2 ], 0 );
136 notice
( $user , 'Syntax: SEND <recipient> <message>' );
139 elsif ( $cmd =~ /^csend$/i ) {
140 if ( @args >= 3 and $args [ 1 ] =~ /^(?:[uvhas]op|co?f(ounder)?|founder)$/i ) {
141 my @args = split ( /\s+/ , $msg , 4 );
142 my $level = chanserv
:: xop_byname
( $args [ 2 ]);
143 ms_send
( $user , $args [ 1 ], $args [ 3 ], $level );
145 notice
( $user , 'Syntax: CSEND <recipient> <uop|vop|hop|aop|sop|cf|founder> <message>' );
148 elsif ( $cmd =~ /^read$/i ) {
149 if ( @args == 1 and ( lc ( $args [ 0 ]) eq 'last' or $args [ 0 ] > 0 )) {
150 ms_read
( $user , $args [ 0 ]);
152 notice
( $user , 'Syntax: READ <num|LAST>' );
155 elsif ( $cmd =~ /^list$/i ) {
158 elsif ( $cmd =~ /^del(ete)?$/i ) {
159 if ( @args >= 1 and ( lc ( $args [ 0 ]) eq 'all' or $args [ 0 ] > 0 )) {
160 ms_delete
( $user , $args [ 0 ]);
162 notice
( $user , 'Syntax: DELETE <num|num1-num2|ALL>' );
165 elsif ( $cmd =~ /^ign(ore)?$/i ) {
166 my $cmd2 = shift @args ;
167 if ( $cmd2 =~ /^a(dd)?$/i ) {
169 ms_ignore_add
( $user , $args [ 0 ]);
172 notice
( $user , 'Syntax: IGNORE ADD <nick>' );
175 elsif ( $cmd2 =~ /^d(el)?$/i ) {
177 ms_ignore_del
( $user , $args [ 0 ]);
180 notice
( $user , 'Syntax: IGNORE DEL [nick|num]' );
183 elsif ( $cmd2 =~ /^l(ist)?$/i ) {
184 ms_ignore_list
( $user );
187 notice
( $user , 'Syntax: IGNORE <ADD|DEL|LIST> [nick|num]' );
190 elsif ( $cmd =~ /^help$/i ) {
191 sendhelp
( $user , 'memoserv' , @args );
194 notice
( $user , "Unrecognized command. For help, type: \002 /ms help \002 " );
199 my ( $user , $dst , $msg , $level ) = @_ ;
200 my $src = get_user_nick
( $user );
202 my $root = auth
( $user ) or return ;
204 if ( length ( $msg ) > MAX_MEMO_LEN
()) {
205 notice
( $user , 'Memo too long. Maximum memo length is ' . MAX_MEMO_LEN
(). ' characters.' );
210 my $chan = { CHAN
=> $dst };
211 unless ( chanserv
:: is_registered
( $chan )) {
212 notice
( $user , " $dst is not registered" );
216 my $srcnick = chanserv
:: can_do
( $chan , 'MEMO' , $user ) or return ;
218 send_chan_memo
( $srcnick , $chan , $msg , $level );
220 nickserv
:: chk_registered
( $user , $dst ) or return ;
222 if ( nr_chk_flag
( $dst , NRF_NOMEMO
(), + 1 )) {
223 notice
( $user , " \002 $dst \002 is not accepting memos." );
226 $chk_ignore -> execute ( nickserv
:: get_root_nick
( $dst ), $root );
227 if ( $chk_ignore -> fetchrow_array ) {
228 notice
( $user , " \002 $dst \002 is not accepting memos." );
232 send_memo
( $src , $dst , $msg );
235 notice
( $user , "Your memo has been sent." );
239 my ( $user , $num ) = @_ ;
240 my ( $from , $chan , $time , $flag , $msg );
241 my $src = get_user_nick
( $user );
243 my $root = auth
( $user ) or return ;
246 if ( lc ( $num ) eq 'last' ) {
247 $get_memo_count -> execute ( $root );
248 ( $num ) = $get_memo_count -> fetchrow_array ;
250 notice
( $user , "Memo \002 $num \002 not found." );
255 @nums = makeSeqList
( $num );
260 while ( my $num = shift @nums ) {
262 push @reply , "You can only read 5 memos at a time." ;
265 $get_memo_full -> execute ( $root , $num - 1 );
266 unless (( $from , $chan , $time , $flag , $msg ) = $get_memo_full -> fetchrow_array ) {
267 push @reply , "Memo \002 $num \002 not found." ;
270 $set_flag -> execute ( 1 , $from , $root , $chan , $time );
271 push @reply , "Memo \002 $num \002 from \002 $from \002 " .
272 ( $chan ? "to \002 $chan \002 " : "to \002 $root \002 " ).
273 "at " . gmtime2
( $time ), ' ' , ' ' . $msg , ' --' ;
275 notice
( $user , @reply );
280 my ( $i , @data , $mnlen , $mclen );
281 my $src = get_user_nick
( $user );
283 my $root = auth
( $user ) or return ;
285 $get_memo_list -> execute ( $root );
286 while ( my ( $from , $chan , $time , $flag , $msg ) = $get_memo_list -> fetchrow_array ) {
290 ( $flag ? '' : " \002 " ) . $i ,
291 $from , $chan , gmtime2
( $time ),
292 ( length ( $msg ) > 20 ? substr ( $msg , 0 , 17 ) . '...' : $msg )
297 notice
( $user , "You have no memos." );
301 notice
( $user , columnar
( { TITLE
=> "Memo list for \002 $root \002 . To read, type \002 /ms read <num> \002 " ,
302 NOHIGHLIGHT
=> nr_chk_flag_user
( $user , NRF_NOHIGHLIGHT
) }, @data ));
306 my ( $user , @args ) = @_ ;
307 my $src = get_user_nick
( $user );
309 my $root = auth
( $user ) or return ;
311 if ( scalar ( @args ) == 1 and lc ( $args [ 0 ]) eq 'all' ) {
312 $delete_all_memos -> execute ( $root );
313 notice
( $user , 'All of your memos have been deleted.' );
316 my ( @deleted , @notDeleted );
317 foreach my $num ( reverse makeSeqList
( @args )) {
318 if ( int ( $num ) ne $num ) { # can this happen, given makeSeqList?
319 notice
( $user , " \002 $num \002 is not an integer number" );
322 my ( $from , $chan , $time );
323 $get_memo -> execute ( $root , $num - 1 );
324 if ( my ( $from , $chan , $time ) = $get_memo -> fetchrow_array ) {
325 $delete_memo -> execute ( $from , $root , $chan , $time );
328 push @notDeleted , $num ;
331 if ( scalar ( @deleted )) {
332 my $plural = ( scalar ( @deleted ) == 1 );
333 my $msg = sprintf ( "Memo %s deleted: " . join ( ', ' , @deleted ), ( $plural ? '' : 's' ));
336 if ( scalar ( @notDeleted )) {
337 my $msg = sprintf ( "Memos not found: " . join ( ', ' , @notDeleted ));
342 sub ms_ignore_add
( $$ ) {
343 my ( $user , $nick ) = @_ ;
344 my $src = get_user_nick
( $user );
346 unless ( is_identified
( $user , $src ) or adminserv
:: can_do
( $user , 'SERVOP' )) {
347 notice
( $user , $err_deny );
351 my $nickroot = nickserv
:: get_root_nick
( $nick );
353 notice
( $user , " $nick is not registered" );
357 my $srcroot = nickserv
:: get_root_nick
( $src );
359 $add_ignore -> execute ( $srcroot , $nickroot );
361 notice
( $user , " \002 $nick \002 ( \002 $nickroot \002 ) added to \002 $src \002 ( \002 $srcroot \002 ) memo ignore list." );
364 sub ms_ignore_del
( $$ ) {
365 my ( $user , $entry ) = @_ ;
366 my $src = get_user_nick
( $user );
368 unless ( is_identified
( $user , $src ) or adminserv
:: can_do
( $user , 'SERVOP' )) {
369 notice
( $user , $err_deny );
372 my $srcroot = nickserv
:: get_root_nick
( $src );
375 if ( misc
:: isint
( $entry )) {
376 $get_ignore_num -> execute ( $srcroot , $entry - 1 );
377 ( $ignorenick ) = $get_ignore_num -> fetchrow_array ();
378 $get_ignore_num -> finish ();
380 my $ret = $del_ignore_nick -> execute ( $srcroot , ( $ignorenick ? $ignorenick : $entry ));
382 notice
( $user , "Delete succeeded for ( $srcroot ): $entry " );
385 notice
( $user , "Delete failed for ( $srcroot ): $entry . entry does not exist?" );
389 sub ms_ignore_list
($) {
391 my $src = get_user_nick
( $user );
393 unless ( is_identified
( $user , $src ) or adminserv
:: can_do
( $user , 'SERVOP' )) {
394 notice
( $user , $err_deny );
397 my $srcroot = nickserv
:: get_root_nick
( $src );
400 $list_ignore -> execute ( $srcroot );
401 while ( my ( $nick , $time ) = $list_ignore -> fetchrow_array ) {
402 push @data , [ $nick , '(' . gmtime2
( $time ). ')' ];
405 notice
( $user , columnar
({ TITLE
=> "Memo ignore list for \002 $src \002 :" ,
406 NOHIGHLIGHT
=> nr_chk_flag_user
( $user , NRF_NOHIGHLIGHT
)}, @data ));
410 my ( $user , $root ) = @_ ;
414 $user = { NICK
=> $user };
417 if ( $root ) { @nicks = ( $root ) }
418 else { @nicks = nickserv
:: get_id_nicks
( $user ) }
421 foreach my $n ( @nicks ) {
422 $get_unread_memo_count -> execute ( $n );
423 my ( $c ) = $get_unread_memo_count -> fetchrow_array ;
425 notice
( $user , "You have \002 $c \002 unread memo(s). " . ( @nicks > 1 ? "( \002 $n \002 ) " : '' ));
429 notice
( $user , "To view them, type: \002 /ms list \002 " ) if $hasmemos ;
432 ### DATABASE UTILITY FUNCTIONS ###
435 my ( $src , $dst , $msg ) = @_ ;
437 # This construct is intended to allow agents to send memos.
438 # Unfortunately this is raceable against %nickserv::enforcers.
439 # I don't want to change the %nickserv::enforcers decl tho, s/my/our/
440 $src = ( is_agent
( $src ) ? $src : nickserv
:: get_root_nick
( $src ));
441 $dst = nickserv
:: get_root_nick
( $dst );
443 $send_memo -> execute ( $src , $msg , $dst );
444 notice_all_nicks
( $dst , "You have a new memo from \002 $src \002 . To read it, type: \002 /ms read last \002 " );
447 sub send_chan_memo
( $$$$ ) {
448 my ( $src , $chan , $msg , $level ) = @_ ;
449 my $cn = $chan ->{ CHAN
};
450 $src = ( is_agent
( $src ) ? $src : nickserv
:: get_root_nick
( $src ));
452 $send_chan_memo -> execute ( $src , $cn , time (), $msg , $cn , $level );
453 # "INSERT INTO memo SELECT ?, nick, ?, ?, 0, ? FROM chanacc WHERE chan=? AND level >= ?"
455 $get_chan_recipients -> execute ( $cn , $level );
456 while ( my ( $u ) = $get_chan_recipients -> fetchrow_array ) {
457 notice
({ NICK
=> $u , AGENT
=> $msnick },
458 "You have a new memo from \002 $src \002 to \002 $cn \002 . To read it, type: \002 /ms read last \002 " );
462 sub notice_all_nicks
( $$ ) {
463 my ( $nick , $msg ) = @_ ;
465 foreach my $u ( get_nick_user_nicks
$nick ) {
466 notice
({ NICK
=> $u , AGENT
=> $msnick }, $msg );
472 my $src = get_user_nick
( $user );
474 my $root = nickserv
:: get_root_nick
( $src );
476 notice
( $user , "Your nick is not registered." );
480 unless ( is_identified
( $user , $root )) {
481 notice
( $user , $err_deny );