]> ruin.nu Git - NDIRC.git/blob - Commands/Usermgm.pm
247fb33a18f1ed865942b3eca44e4e49e4b81ff5
[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 ILIKE ? OR hostmask ILIKE ? OR pnick ILIKE ?
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, $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 ($flags =~ /^(-)/){
104                         $update = $dbh->prepare(q{
105 DELETE FROM groupmembers WHERE uid = $1 AND
106         gid IN (SELECT gid FROM groups WHERE flag = ANY($2))
107                         });
108                 }else{
109                         $update = $dbh->prepare(q{
110 INSERT INTO groupmembers (uid,gid) (SELECT $1,gid FROM groups
111         WHERE flag = ANY($2) AND gid NOT IN (SELECT gid FROM groupmembers WHERE uid = $1))
112                         });
113                 }
114                 my @flags = split /\W*/,$flags;
115                 $update->execute($user->{uid},\@flags);
116                 $update = $dbh->prepare(q{
117 SELECT concat(flag)
118 FROM (SELECT uid,flag FROM groupmembers NATURAL JOIN groups ORDER BY uid,flag ) g
119 WHERE uid = ?
120                 });
121                 $flags = $dbh->selectrow_array($update,undef,$user->{uid});
122                 $c->reply("Flags for <b>$user->{username}</b> are now: $flags");
123         }elsif ($f->rows == 0){
124                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
125         }else{
126                 $c->reply("More than 1 user matched, please refine the search");
127         }
128         $f->finish;
129 }
130
131 sub whois
132         : Help(syntax: .whois [username] | % can be used for wildcards \%arro% will match barrow. If no username is given then all flags will be listed)
133         : Alias(flags)
134         : ACL(irc_whois)
135 {
136         my ($self,$c,$msg) = @_;
137         my ($nick) = $msg =~ /^(\S+)?$/ or die 'ARGS';
138         my $dbh = $c->model;
139
140         unless ($nick){
141                 my ($flags) = $dbh->selectrow_array(q{
142 SELECT TRIM(', ' FROM concat(flag||':'||groupname||', ')) FROM groups
143                 });
144                 $c->reply("Current flags: $flags");
145                 return;
146         }
147
148         my $f = $dbh->prepare(q{
149 SELECT username, pnick, hostmask, concat(flag) AS flags
150 FROM users u
151         LEFT OUTER JOIN (SELECT uid,flag FROM groupmembers
152                 NATURAL JOIN groups ORDER BY uid,flag ) g USING (uid)
153 WHERE username ILIKE ?
154 GROUP BY username,pnick,hostmask LIMIT 5
155                 });
156         $f->execute($nick);
157         while (my $user = $f->fetchrow_hashref){
158                 $c->reply("<b>$user->{username} (/$user->{pnick})</b> flags: ($user->{flags}) host: $user->{hostmask}");
159         }
160         if ($f->rows == 0){
161                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
162         }
163 }
164
165 sub flag
166         : Help(syntax: .flag flag | Lists all users with the given flag.)
167         : ACL(irc_flag)
168 {
169         my ($self,$c,$msg) = @_;
170         my ($flag) = $msg =~ /^(\w)$/ or die 'ARGS';
171
172         my $f = $c->model->prepare(q{
173 SELECT TRIM(', ' FROM concat(username||', ')),count(username)
174 FROM (SELECT uid, username FROM users ORDER BY username) u
175         JOIN groupmembers gm USING (uid)
176         JOIN groups g USING (gid)
177 WHERE flag = $1
178                 });
179         my ($users,$count) = $c->model->selectrow_array($f,undef,$flag);
180         $c->reply("<b>$count</b> Users with flag <b>$flag</b>: $users");
181 }
182
183 sub laston
184         : 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)
185         : ACL(irc_laston)
186 {
187         my ($self,$c,$msg) = @_;
188         my ($flag,$min,$forum,$claim) = $msg =~ /^(\w)(?: (\d+)(?:\|(\d+)\|(\d+))?)?$/
189                 or die 'ARGS';
190         $min //= 0;
191
192         my $f = $c->model->prepare(q{
193 SELECT username, COALESCE(last::text,'?') AS last
194         ,COALESCE(lastforum::text,'?') AS lastforum
195         ,COALESCE(lastclaim::text,'?') AS lastclaim
196 FROM (SELECT username
197                 ,date_part('day',now() - laston)::int AS last
198                 ,date_part('day',now() -
199                         (SELECT max(time) FROM forum_thread_visits WHERE uid = u.uid)) AS lastforum
200                 ,date_part('day',now() -
201                         (SELECT max(timestamp) FROM raid_claims WHERE uid = u.uid)) AS lastclaim
202         FROM users u
203                 NATURAL JOIN groupmembers
204                 NATURAL JOIN groups
205         WHERE flag = $1
206         ) a
207 WHERE COALESCE(last >= $2,TRUE) AND COALESCE(lastforum >= $3,TRUE)
208         AND COALESCE(lastclaim >= $4,TRUE)
209 ORDER BY last DESC, lastforum DESC, lastclaim DESC
210                 });
211         $f->execute($flag,$min,$forum,$claim);
212
213         my $text;
214         my $i = 0;
215         while (my $user = $f->fetchrow_hashref){
216                 $text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
217                 $i++;
218         }
219         $c->reply("<b>$i</b> Users(days) with flag <b>$flag</b>: $text");
220 }
221
222
223 sub lastseen
224         : Help(syntax: .lastseen username | Shows the number of days since the user(s) last visited irc, forum and raids)
225         : ACL(irc_lastseen)
226 {
227         my ($self,$c,$msg) = @_;
228         my ($username) = $msg =~ /^(\S+)$/ or die 'ARGS';
229
230         my $f = $c->model->prepare(q{
231 SELECT username, COALESCE(date_part('day',now() - laston)::text,'?') AS last
232         ,COALESCE(date_part('day',now() - (SELECT max(time)
233                 FROM forum_thread_visits WHERE uid = u.uid))::text,'?') AS lastforum
234         ,COALESCE(date_part('day',now() - (SELECT max(timestamp)
235                 FROM raid_claims WHERE uid = u.uid))::text,'?') AS lastclaim
236 FROM users u
237 WHERE username ILIKE $1 ORDER BY lower(username)
238                 });
239         $f->execute($username);
240
241         my $text;
242         my $i = 0;
243         while (my $user = $f->fetchrow_hashref){
244                 $text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
245                 $i++;
246         }
247         $c->reply("<b>$i</b> Users(days): $text");
248 }
249
250 sub getships
251         : Help(Usage: .getships ship | % can be used as wildcard, e.g. beet%, shipshome shows the number of ships currently home)
252         : Alias(shipshome)
253         : ACL(irc_getships)
254 {
255         my ($self,$c,$msg) = @_;
256         my ($ship) = $msg =~ /^(\S+)$/ or die 'ARGS';
257         my $dbh = $c->model;
258
259         my $f = $dbh->prepare(q{
260 SELECT username,SUM(fs.amount) AS amount
261 FROM users u
262         JOIN (SELECT DISTINCT ON (planet) planet,fid FROM fleets
263                 WHERE mission = 'Full fleet' AND name <> 'Unit'
264                 ORDER BY planet,tick DESC,fid DESC
265         ) f  USING (planet)
266         JOIN fleet_ships fs USING (fid)
267 WHERE ship ILIKE $1 AND uid IN (SELECT uid FROM groupmembers WHERE gid = 2)
268 GROUP BY username ORDER BY amount DESC
269                 });
270         if ($self->name eq 'shipshome'){
271                 $f = $dbh->prepare(q{
272 SELECT username,SUM(amount) AS amount
273 FROM available_ships
274 WHERE ship ILIKE ? AND uid IN (SELECT uid FROM groupmembers WHERE gid = 2)
275 GROUP BY username ORDER BY amount DESC
276                 });
277         }
278         $f->execute($ship);
279         my $text;
280         my $i = 0;
281         my $total = 0;
282         while (my $user = $f->fetchrow_hashref){
283                 $text .= "$user->{username}: $user->{amount} ";
284                 $i++;
285                 $total += $user->{amount};
286         }
287         if ($text){
288                 $c->reply("<b>$i</b> Users with <b>$total $ship</b>: $text");
289         }else{
290                 $c->reply("Couldn't find any user with <b>$ship</b>");
291         }
292 }
293
294 sub getfleet
295         : Help(Usage: .getfleet username | % can be used as wildcard, e.g. barr%)
296         : ACL(irc_getfleet)
297 {
298         my ($self,$c,$msg) = @_;
299         my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';
300         my $dbh = $c->model;
301
302         my $f = $dbh->prepare(q{
303 SELECT fs.ship, fs.amount, username
304 FROM fleet_ships fs
305         JOIN (SELECT fid,username
306                 FROM fleets f
307                         JOIN users u USING (planet)
308                 WHERE mission = 'Full fleet' AND name <> 'Unit'
309                         AND username ILIKE $1
310                 ORDER BY planet,tick DESC,fid DESC
311                 LIMIT 1
312         ) f  USING (fid)
313 ORDER BY num
314                 });
315         $f->execute($nick);
316         my $text;
317         my $username;
318         while (my $ship = $f->fetchrow_hashref){
319                 unless (defined $username) {
320                         $username = $ship->{username};
321                         $text = "<b>$username</b> has: "
322                 }
323                 $text .= "$ship->{ship} $ship->{amount} ";
324         }
325         if ($text){
326                 $c->reply($text);
327         }else{
328                 $c->reply("Couldn't find any fleet for $nick");
329         }
330 }
331
332 sub sethost
333         : Help(Usage: .sethost username [host] | if host isn't given then it resets to netgamers host)
334         : ACL(irc_sethost)
335 {
336         my ($self,$c,$msg) = @_;
337         my ($nick,$host) = $msg =~ /^(\S+)(?: (\S+))?$/ or die 'ARGS';
338         my $dbh = $c->model;
339
340         my $f = $dbh->prepare(q{
341 SELECT uid,username,pnick,hostmask FROM users WHERE username ILIKE ?
342                 });
343         $f->execute($nick);
344         my $user = $f->fetchrow_hashref;
345         if ($f->rows == 1){
346                 $host //= "$user->{pnick}.users.netgamers.org";
347                 eval{
348                         $dbh->do(q{UPDATE users SET hostmask = ? WHERE uid = ?}
349                                 ,undef,$host,$user->{uid});
350                         $c->reply("Updated <b>$user->{username}</b>'s host to: <b>$host</b>");
351                 };
352                 if($@){
353                         if ($@ =~ /duplicate key value violates unique constraint/){
354                                 my ($username, $hostname) = $dbh->selectrow_array(q{
355 SELECT username,hostmask FROM users WHERE hostmask ILIKE $1
356                                 },undef,$host);
357                                 $c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b>.");
358                         }else{
359                                 die $@;
360                         }
361                 }
362         }elsif ($f->rows == 0){
363                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
364         }else{
365                 $c->reply("More than 1 user matched, please refine the search");
366         }
367         $f->finish;
368 }
369
370 sub setpnick
371         : Help(Usage: .setpnick username pnick | Changes a user's pnick)
372         : ACL(irc_setpnick)
373 {
374         my ($self,$c,$msg) = @_;
375         my ($nick,$pnick) = $msg =~ /^(\S+) (\S+)$/ or die 'ARGS';
376         my $dbh = $c->model;
377
378         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
379         $f->execute($nick);
380         my $user = $f->fetchrow_hashref;
381         if ($f->rows == 1){
382                 my $hostname = "$pnick.users.netgamers.org";
383                 eval{
384                         $dbh->do(q{UPDATE users SET pnick = ?, hostmask = ? WHERE uid = ?}
385                                 ,undef,$pnick,$hostname,$user->{uid});
386                         $c->reply("Updated <b>$user->{username}</b>'s pnick to: <b>$pnick</b> and hostname to <b>$hostname</b>");
387                 };
388                 if($@){
389                         if ($@ =~ /duplicate key value violates unique constraint/){
390                                 my ($username, $hostname, $pnick) = $dbh->selectrow_array(q{
391 SELECT username,hostmask,pnick FROM users WHERE hostmask ILIKE $1 OR pnick ILIKE $2
392                                 },undef,$hostname, $pnick);
393                                 $c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b> and pnick <b>$pnick</b>.");
394                         }else{
395                                 die $@;
396                         }
397                 }
398         }elsif ($f->rows == 0){
399                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
400         }else{
401                 $c->reply("More than 1 user matched, please refine the search");
402         }
403         $f->finish;
404 }
405
406 sub a
407         : 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.)
408         : ACL(irc_a)
409 {
410         my ($self,$c,$msg) = @_;
411         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
412         $points //= 1;
413
414         my ($fleets) = $c->model->selectrow_array(q{
415 SELECT count(*) FROM raids r
416         JOIN raid_targets rt ON r.id = rt.raid
417         JOIN raid_claims rc ON rt.id = rc.target
418 WHERE not launched  AND tick + 24 > tick()
419         AND uid = (SELECT uid FROM users WHERE username ILIKE $1);
420                 },undef,$nick);
421
422         if ($fleets > 0 && $points > 0){
423                 $c->reply("$nick has $fleets claimed waves the last 24 ticks that aren't marked as launched, so no points.");
424                 return;
425         }
426         addPoints($c, 'attack', $nick, $points);
427 }
428
429 sub d
430         : 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.)
431         : ACL(irc_d)
432         : Type(def)
433 {
434         my ($self,$c,$msg) = @_;
435         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
436
437         addPoints($c, 'defense', $nick, $points);
438 }
439
440 sub s
441         : 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.)
442         : ACL(irc_s)
443 {
444         my ($self,$c,$msg) = @_;
445         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
446
447         addPoints($c, 'scan', $nick, $points);
448 }
449
450 sub h
451         : 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.)
452         : ACL(irc_h)
453 {
454         my ($self,$c,$msg) = @_;
455         my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';
456
457         addPoints($c, 'humor', $nick, $points);
458 }
459
460 sub addPoints {
461         my ($c,$type, $nick, $points) = @_;
462         $points //= 1;
463
464         my $dbh = $c->model;
465
466         if ($points*$points > 400){
467                 $c->reply("Values between -20 and 20 please");
468                 return;
469         }
470
471         my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
472         $f->execute($nick);
473         my $user = $f->fetchrow_hashref;
474         if ($f->rows == 1){
475                 $type .= "_points";
476                 $dbh->do(qq{UPDATE users SET $type = $type + ? WHERE uid = ?}
477                         ,undef,$points,$user->{uid});
478                 $c->reply("$user->{username} has been given $points $type");
479         }elsif ($f->rows == 0){
480                 $c->reply("No hit, maybe spelling mistake, or add % as wildcard");
481         }else{
482                 $c->reply("More than 1 user matched, please refine the search");
483         }
484         $f->finish;
485 }
486
487 1;