]> ruin.nu Git - NDIRC.git/blob - Commands/Usermgm.pm
Make it possible to remove members of specified groups from .flag listing
[NDIRC.git] / Commands / Usermgm.pm
1 #**************************************************************************
2 #   Copyright (C) 2009 by Michael Andreen <harvATruinDOTnu>               *
3 #                                                                         *
4 #   This program is free software; you can redistribute it and/or modify  *
5 #   it under the terms of the GNU General Public License as published by  *
6 #   the Free Software Foundation; either version 2 of the License, or     *
7 #   (at your option) any later version.                                   *
8 #                                                                         *
9 #   This program is distributed in the hope that it will be useful,       *
10 #   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12 #   GNU General Public License for more details.                          *
13 #                                                                         *
14 #   You should have received a copy of the GNU General Public License     *
15 #   along with this program; if not, write to the                         *
16 #   Free Software Foundation, Inc.,                                       *
17 #   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
18 #**************************************************************************/
19
20 package NDIRC::Commands::Usermgm;
21
22 use strict;
23 use warnings;
24 use feature ':5.10';
25
26 use Moose;
27 use MooseX::MethodAttributes;
28
29 sub adduser
30         : Help(syntax: .+user username [pnick] | username must be alphanum characters, if no pnick is given then it will be set to the same as username)
31         : Alias(+user)
32         : ACL(irc_adduser)
33 {
34         my ($self,$c,$msg) = @_;
35         my ($nick,$pnick) = $msg =~ /^(\w+)(?: ([^.\s]+))?$/ or die 'ARGS';
36         $pnick //= $nick;
37
38         my $dbh = $c->model;
39
40         my $host = "$pnick.users.netgamers.org";
41         my ($username,$hostname,$p_nick) = $dbh->selectrow_array(q{
42 SELECT username, hostmask,pnick
43 FROM users WHERE username = $1 OR hostmask = $2 OR pnick = $3
44                 },undef,$nick,$host,$pnick);
45
46         if (defined $username){
47                 $c->reply("<b>$username ($p_nick)</b> already exists with host: <b>$hostname</b>");
48         }else{
49                 $dbh->do(q{
50 INSERT INTO users (username,hostmask,pnick,password) VALUES(?,?,?,'')
51                 },undef,$nick,$host,$pnick);
52                 $c->reply("Added <b>$nick(/$pnick)</b> with host: <b>$host</b>");
53         }
54 }
55
56 sub deactivateuser
57         : Help(syntax: .-user nick | nick must be alphanum characters, if no pnick is given then it will be set to nick)
58         : Alias(-user)
59         : ACL(irc_deactivateuser)
60 {
61         my ($self,$c,$msg) = @_;
62
63         my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';
64
65         my $dbh = $c->model;
66         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
67         $f->execute($nick);
68         my ($uid,$username) = $f->fetchrow();
69
70         if ($f->rows == 1){
71                 my $updated = $dbh->do(q{
72 UPDATE users SET hostmask = ?, password = '' WHERE uid = ?
73                 },undef,$username,$uid);
74                 if ($updated > 0){
75                         my $groups = $dbh->do(q{DELETE FROM groupmembers WHERE uid = ?},undef,$uid);
76                         $groups += 0;
77                         $c->reply("<b>$username</b> has been deactivated. Removed from $groups groups.");
78                 }else{
79                         $c->reply("Something went wrong when trying to modify <b>$username</b>");
80                 }
81         }elsif ($f->rows == 0){
82                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
83         }else{
84                 $c->reply("More than 1 user matched, please refine the search");
85         }
86         $f->finish;
87 }
88
89 sub chattr
90         : Help(syntax: .chattr username [-]flags | % can be used for wildcards \%arro% will match barrow, if a - is given then flags will be removed, otherwise added)
91         : ACL(irc_chattr)
92 {
93         my ($self,$c,$msg) = @_;
94
95         my ($nick, $mode, $flags) = $msg =~ /^(\S+) (\+|-)?(\w+)$/ or die 'ARGS';
96         my $dbh = $c->model;
97
98         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
99         $f->execute($nick);
100         my $user = $f->fetchrow_hashref;
101         if ($f->rows == 1){
102                 my $update;
103                 if ($mode ~~ '-'){
104                         $update = $dbh->prepare(q{
105 DELETE FROM groupmembers WHERE uid = $1 AND gid = ANY($2)
106                         });
107                 }else{
108                         $update = $dbh->prepare(q{
109 INSERT INTO groupmembers (uid,gid)
110         (SELECT $1,gid FROM unnest($2::text[]) gid WHERE
111                 gid NOT IN (SELECT gid FROM groupmembers WHERE uid = $1)
112                 AND gid IN (SELECT gid FROM groups))
113                         });
114                 }
115                 my @flags = split /\W*/,$flags;
116                 $update->execute($user->{uid},\@flags);
117                 $update = $dbh->prepare(q{
118 SELECT array_to_string(array_agg(gid),'')
119 FROM (SELECT uid,gid FROM groupmembers ORDER BY uid,gid ) g
120 WHERE uid = ?
121                 });
122                 $flags = $dbh->selectrow_array($update,undef,$user->{uid});
123                 $c->reply("Flags for <b>$user->{username}</b> are now: $flags");
124         }elsif ($f->rows == 0){
125                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
126         }else{
127                 $c->reply("More than 1 user matched, please refine the search");
128         }
129         $f->finish;
130 }
131
132 sub whois
133         : Help(syntax: .whois [username] | % can be used for wildcards \%arro% will match barrow. If no username is given then all flags will be listed)
134         : Alias(flags)
135         : ACL(irc_whois)
136 {
137         my ($self,$c,$msg) = @_;
138         my ($nick) = $msg =~ /^(\S+)?$/ or die 'ARGS';
139         my $dbh = $c->model;
140
141         unless ($nick){
142                 my ($flags) = $dbh->selectrow_array(q{
143 SELECT array_to_string(array_agg(gid||':'||groupname),', ') FROM groups
144                 });
145                 $c->reply("Current flags: $flags");
146                 return;
147         }
148
149         my $f = $dbh->prepare(q{
150 SELECT username, pnick, hostmask, array_to_string(array_agg(gid),'') AS flags
151 FROM users u
152         LEFT OUTER JOIN (SELECT uid,gid FROM groupmembers ORDER BY uid,gid
153         ) g USING (uid)
154 WHERE username ILIKE ?
155 GROUP BY username,pnick,hostmask LIMIT 5
156                 });
157         $f->execute($nick);
158         while (my $user = $f->fetchrow_hashref){
159                 $c->reply("<b>$user->{username} (/$user->{pnick})</b> flags: ($user->{flags}) host: $user->{hostmask}");
160         }
161         if ($f->rows == 0){
162                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
163         }
164 }
165
166 sub flag
167         : Help(syntax: .flag flag [-noflags] | Lists all users with the given flag. Can specify a second argument to remove members that has any of those flags. .flag M -C lists all members that doesn't have community flag)
168         : ACL(irc_flag)
169 {
170         my ($self,$c,$msg) = @_;
171         my ($flag,$noflag) = $msg =~ /^(\w)(?: -(\w+))?$/ or die 'ARGS';
172         $noflag //= '';
173
174         my $f = $c->model->prepare(q{
175 SELECT array_to_string(array_agg(username),', '),count(username)
176 FROM (SELECT uid, username FROM users ORDER BY username) u
177         JOIN groupmembers gm USING (uid)
178 WHERE gid = $1 AND uid NOT IN (SELECT uid FROM groupmembers WHERE gid =  ANY($2))
179                 });
180         my @noflag = split //, $noflag;
181         my ($users,$count) = $c->model->selectrow_array($f,undef,$flag,\@noflag);
182         $c->reply("<b>$count</b> Users with flag <b>$flag</b>: $users");
183 }
184
185 sub laston
186         : Help(syntax: .laston flag [days] | lists users and the number of days since they were last seen (irc|forum|claim). If days is specified it will only list users with at least that amount of idle time. Days can also specify forum and claim with irc|forum|claim syntax)
187         : ACL(irc_laston)
188 {
189         my ($self,$c,$msg) = @_;
190         my ($flag,$min,$forum,$claim) = $msg =~ /^(\w)(?: (\d+)(?:\|(\d+)\|(\d+))?)?$/
191                 or die 'ARGS';
192         $min //= 0;
193
194         my $f = $c->model->prepare(q{
195 SELECT username, COALESCE(last::text,'?') AS last
196         ,COALESCE(lastforum::text,'?') AS lastforum
197         ,COALESCE(lastclaim::text,'?') AS lastclaim
198 FROM (SELECT username
199                 ,date_part('day',now() - laston)::int AS last
200                 ,date_part('day',now() -
201                         (SELECT max(time) FROM forum_thread_visits WHERE uid = u.uid)) AS lastforum
202                 ,date_part('day',now() -
203                         (SELECT max(timestamp) FROM raid_claims WHERE uid = u.uid)) AS lastclaim
204         FROM users u
205                 NATURAL JOIN groupmembers
206         WHERE gid = $1
207         ) a
208 WHERE COALESCE(last >= $2,TRUE) AND COALESCE(lastforum >= $3,TRUE)
209         AND COALESCE(lastclaim >= $4,TRUE)
210 ORDER BY a.last DESC, a.lastforum DESC, a.lastclaim DESC
211                 });
212         $f->execute($flag,$min,$forum,$claim);
213
214         my $text;
215         my $i = 0;
216         while (my $user = $f->fetchrow_hashref){
217                 $text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
218                 $i++;
219         }
220         $c->reply("<b>$i</b> Users(days) with flag <b>$flag</b>: $text");
221 }
222
223
224 sub lastseen
225         : Help(syntax: .lastseen username | Shows the number of days since the user(s) last visited irc, forum and raids)
226         : ACL(irc_lastseen)
227 {
228         my ($self,$c,$msg) = @_;
229         my ($username) = $msg =~ /^(\S+)$/ or die 'ARGS';
230
231         my $f = $c->model->prepare(q{
232 SELECT username, COALESCE(date_part('day',now() - laston)::text,'?') AS last
233         ,COALESCE(date_part('day',now() - (SELECT max(time)
234                 FROM forum_thread_visits WHERE uid = u.uid))::text,'?') AS lastforum
235         ,COALESCE(date_part('day',now() - (SELECT max(timestamp)
236                 FROM raid_claims WHERE uid = u.uid))::text,'?') AS lastclaim
237 FROM users u
238 WHERE username ILIKE $1 ORDER BY username
239                 });
240         $f->execute($username);
241
242         my $text;
243         my $i = 0;
244         while (my $user = $f->fetchrow_hashref){
245                 $text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
246                 $i++;
247         }
248         $c->reply("<b>$i</b> Users(days): $text");
249 }
250
251 sub getships
252         : Help(Usage: .getships ship | % can be used as wildcard, e.g. beet%, shipshome shows the number of ships currently home)
253         : Alias(shipshome)
254         : ACL(irc_getships)
255 {
256         my ($self,$c,$msg) = @_;
257         my ($ship) = $msg =~ /^(\S+)$/ or die 'ARGS';
258         my $dbh = $c->model;
259
260         my $f = $dbh->prepare(q{
261 SELECT username,SUM(fs.amount) AS amount
262 FROM users u
263         JOIN (SELECT DISTINCT ON (pid) pid,fid FROM fleets
264                 WHERE mission = 'Full fleet' AND name <> 'Unit'
265                 ORDER BY pid,tick DESC,fid DESC
266         ) f  USING (pid)
267         JOIN fleet_ships fs USING (fid)
268 WHERE ship ILIKE $1 AND uid IN (SELECT uid FROM groupmembers WHERE gid = 'M')
269 GROUP BY username ORDER BY amount DESC
270                 });
271         if ($self->name eq 'shipshome'){
272                 $f = $dbh->prepare(q{
273 SELECT username,SUM(amount) AS amount
274 FROM available_ships
275 WHERE ship ILIKE ? AND uid IN (SELECT uid FROM groupmembers WHERE gid = 'M')
276 GROUP BY username ORDER BY amount DESC
277                 });
278         }
279         $f->execute($ship);
280         my $text;
281         my $i = 0;
282         my $total = 0;
283         while (my $user = $f->fetchrow_hashref){
284                 $text .= "$user->{username}: $user->{amount} ";
285                 $i++;
286                 $total += $user->{amount};
287         }
288         if ($text){
289                 $c->reply("<b>$i</b> Users with <b>$total $ship</b>: $text");
290         }else{
291                 $c->reply("Couldn't find any user with <b>$ship</b>");
292         }
293 }
294
295 sub getfleet
296         : Help(Usage: .getfleet username | % can be used as wildcard, e.g. barr%)
297         : ACL(irc_getfleet)
298 {
299         my ($self,$c,$msg) = @_;
300         my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';
301         my $dbh = $c->model;
302
303         my $f = $dbh->prepare(q{
304 SELECT fs.ship, fs.amount, username
305 FROM fleet_ships fs
306         JOIN (SELECT fid,username
307                 FROM fleets f
308                         JOIN users u USING (pid)
309                 WHERE mission = 'Full fleet' AND name <> 'Unit'
310                         AND username ILIKE $1
311                 ORDER BY pid,tick DESC,fid DESC
312                 LIMIT 1
313         ) f  USING (fid)
314 ORDER BY num
315                 });
316         $f->execute($nick);
317         my $text;
318         my $username;
319         while (my $ship = $f->fetchrow_hashref){
320                 unless (defined $username) {
321                         $username = $ship->{username};
322                         $text = "<b>$username</b> has: "
323                 }
324                 $text .= "$ship->{ship} $ship->{amount} ";
325         }
326         if ($text){
327                 $c->reply($text);
328         }else{
329                 $c->reply("Couldn't find any fleet for $nick");
330         }
331 }
332
333 sub sethost
334         : Help(Usage: .sethost username [host] | if host isn't given then it resets to netgamers host)
335         : ACL(irc_sethost)
336 {
337         my ($self,$c,$msg) = @_;
338         my ($nick,$host) = $msg =~ /^(\S+)(?: (\S+))?$/ or die 'ARGS';
339         my $dbh = $c->model;
340
341         my $f = $dbh->prepare(q{
342 SELECT uid,username,pnick,hostmask FROM users WHERE username ILIKE ?
343                 });
344         $f->execute($nick);
345         my $user = $f->fetchrow_hashref;
346         if ($f->rows == 1){
347                 $host //= "$user->{pnick}.users.netgamers.org";
348                 eval{
349                         $dbh->do(q{UPDATE users SET hostmask = ? WHERE uid = ?}
350                                 ,undef,$host,$user->{uid});
351                         $c->reply("Updated <b>$user->{username}</b>'s host to: <b>$host</b>");
352                 };
353                 if($@){
354                         if ($@ =~ /duplicate key value violates unique constraint/){
355                                 my ($username, $hostname) = $dbh->selectrow_array(q{
356 SELECT username,hostmask FROM users WHERE hostmask = $1
357                                 },undef,$host);
358                                 $c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b>.");
359                         }else{
360                                 die $@;
361                         }
362                 }
363         }elsif ($f->rows == 0){
364                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
365         }else{
366                 $c->reply("More than 1 user matched, please refine the search");
367         }
368         $f->finish;
369 }
370
371 sub setpnick
372         : Help(Usage: .setpnick username pnick | Changes a user's pnick)
373         : ACL(irc_setpnick)
374 {
375         my ($self,$c,$msg) = @_;
376         my ($nick,$pnick) = $msg =~ /^(\S+) (\S+)$/ or die 'ARGS';
377         my $dbh = $c->model;
378
379         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
380         $f->execute($nick);
381         my $user = $f->fetchrow_hashref;
382         if ($f->rows == 1){
383                 my $hostname = "$pnick.users.netgamers.org";
384                 eval{
385                         $dbh->do(q{UPDATE users SET pnick = ?, hostmask = ? WHERE uid = ?}
386                                 ,undef,$pnick,$hostname,$user->{uid});
387                         $c->reply("Updated <b>$user->{username}</b>'s pnick to: <b>$pnick</b> and hostname to <b>$hostname</b>");
388                 };
389                 if($@){
390                         if ($@ =~ /duplicate key value violates unique constraint/){
391                                 my ($username, $hostname, $pnick) = $dbh->selectrow_array(q{
392 SELECT username,hostmask,pnick FROM users WHERE hostmask = $1 OR pnick = $2
393                                 },undef,$hostname, $pnick);
394                                 $c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b> and pnick <b>$pnick</b>.");
395                         }else{
396                                 die $@;
397                         }
398                 }
399         }elsif ($f->rows == 0){
400                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
401         }else{
402                 $c->reply("More than 1 user matched, please refine the search");
403         }
404         $f->finish;
405 }
406
407 sub a
408         : Help(Usage: .a username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.)
409         : ACL(irc_a)
410 {
411         my ($self,$c,$msg) = @_;
412         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
413         $points //= 1;
414
415         my ($fleets) = $c->model->selectrow_array(q{
416 SELECT count(*) FROM raids r
417         JOIN raid_targets rt ON r.id = rt.raid
418         JOIN raid_claims rc ON rt.id = rc.target
419 WHERE not launched  AND tick + 24 > tick()
420         AND uid = (SELECT uid FROM users WHERE username ILIKE $1);
421                 },undef,$nick);
422
423         if ($fleets > 0 && $points > 0){
424                 $c->reply("$nick has $fleets claimed waves the last 24 ticks that aren't marked as launched, so no points.");
425                 return;
426         }
427         addPoints($c, 'attack', $nick, $points);
428 }
429
430 sub d
431         : Help(Usage: .d username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.)
432         : ACL(irc_d)
433         : Type(def)
434 {
435         my ($self,$c,$msg) = @_;
436         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
437
438         addPoints($c, 'defense', $nick, $points);
439 }
440
441 sub s
442         : Help(Usage: .s username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.)
443         : ACL(irc_s)
444 {
445         my ($self,$c,$msg) = @_;
446         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
447
448         addPoints($c, 'scan', $nick, $points);
449 }
450
451 sub h
452         : Help(Usage: .h username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.)
453         : ACL(irc_h)
454 {
455         my ($self,$c,$msg) = @_;
456         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
457
458         addPoints($c, 'humor', $nick, $points);
459 }
460
461 sub addPoints {
462         my ($c,$type, $nick, $points) = @_;
463         $points //= 1;
464
465         my $dbh = $c->model;
466
467         if ($points*$points > 400){
468                 $c->reply("Values between -20 and 20 please");
469                 return;
470         }
471
472         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
473         $f->execute($nick);
474         my $user = $f->fetchrow_hashref;
475         if ($f->rows == 1){
476                 $type .= "_points";
477                 $dbh->do(qq{UPDATE users SET $type = $type + ? WHERE uid = ?}
478                         ,undef,$points,$user->{uid});
479                 $c->reply("$user->{username} has been given $points $type");
480         }elsif ($f->rows == 0){
481                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
482         }else{
483                 $c->reply("More than 1 user matched, please refine the search");
484         }
485         $f->finish;
486 }
487
488 1;