Passed
Push — appeals ( bdf92f )
by Simon
05:06
created

IrcNotificationHelper   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 497
Duplicated Lines 0 %

Importance

Changes 10
Bugs 0 Features 2
Metric Value
wmc 40
eloc 127
dl 0
loc 497
rs 9.2
c 10
b 0
f 2

31 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A userApproved() 0 3 1
A userNew() 0 3 1
B send() 0 56 6
A requestDeferred() 0 9 1
A userPrefChange() 0 3 1
A welcomeTemplateCreated() 0 3 1
A requestUnreserved() 0 3 1
A requestReservationSent() 0 7 1
A requestDeferredWithMail() 0 11 1
A welcomeTemplateEdited() 0 3 1
A requestCloseQueued() 0 5 1
A unbanned() 0 4 1
A requestReserveBroken() 0 5 1
A requestClosed() 0 5 1
A userRolesEdited() 0 4 1
A userRenamed() 0 3 1
A emailEdited() 0 4 1
A requestReceived() 0 13 1
A userDeactivated() 0 3 1
A welcomeTemplateDeleted() 0 3 1
A emailCreated() 0 4 1
A siteNoticeEdited() 0 3 1
A commentEdited() 0 6 1
A alertFlaggedComments() 0 3 1
A requestCreationFailed() 0 3 1
A commentCreated() 0 16 3
A requestWelcomeFailed() 0 3 1
A banned() 0 16 3
A requestReserved() 0 5 1
A sentMail() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like IrcNotificationHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use IrcNotificationHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca\Helpers;
11
12
use Exception;
13
use PhpAmqpLib\Connection\AMQPConnectionConfig;
14
use PhpAmqpLib\Connection\AMQPConnectionFactory;
15
use PhpAmqpLib\Message\AMQPMessage;
16
use Waca\DataObjects\Ban;
17
use Waca\DataObjects\Comment;
18
use Waca\DataObjects\Domain;
19
use Waca\DataObjects\EmailTemplate;
20
use Waca\DataObjects\Request;
21
use Waca\DataObjects\RequestQueue;
22
use Waca\DataObjects\User;
23
use Waca\DataObjects\WelcomeTemplate;
24
use Waca\ExceptionHandler;
25
use Waca\IrcColourCode;
26
use Waca\PdoDatabase;
27
use Waca\SiteConfiguration;
28
29
/**
30
 * Class IrcNotificationHelper
31
 * @package Waca\Helpers
32
 */
33
class IrcNotificationHelper
34
{
35
    /** @var bool $notificationsEnabled */
36
    private $notificationsEnabled;
37
    /** @var User $currentUser */
38
    private $currentUser;
39
    /** @var string $instanceName */
40
    private $instanceName;
41
    /** @var string */
42
    private $baseUrl;
43
    /** @var SiteConfiguration */
44
    private $siteConfiguration;
45
    /** @var PdoDatabase */
46
    private $primaryDatabase;
47
48
    /**
49
     * IrcNotificationHelper constructor.
50
     *
51
     * @param SiteConfiguration $siteConfiguration
52
     * @param PdoDatabase       $primaryDatabase
53
     */
54
    public function __construct(
55
        SiteConfiguration $siteConfiguration,
56
        PdoDatabase $primaryDatabase
57
    ) {
58
        $this->siteConfiguration = $siteConfiguration;
59
        $this->primaryDatabase = $primaryDatabase;
60
61
        $this->notificationsEnabled = $siteConfiguration->getIrcNotificationsEnabled();
62
        $this->instanceName = $siteConfiguration->getIrcNotificationsInstance();
63
        $this->baseUrl = $siteConfiguration->getBaseUrl();
64
65
        $this->currentUser = User::getCurrent($primaryDatabase);
66
    }
67
68
    /**
69
     * Send a notification
70
     *
71
     * This method does some *basic* filtering for IRC safety, and then delivers the message to an AMQP queue manager.
72
     * It is the responsibility of the queue manager and consumer to handle message delivery to IRC, message routing to
73
     * the correct channel, and message expiry.
74
     *
75
     * It's also arguably the responsibility of the consumer to ensure the message is *safe* for delivery to IRC.
76
     *
77
     * As of Feb 2022, the message consumer is stwalkerster's IRC bot Helpmebot.
78
     *
79
     * @param string $message The text to send
80
     */
81
    protected function send($message)
82
    {
83
        $instanceName = $this->instanceName;
84
85
        if (!$this->notificationsEnabled) {
86
            return;
87
        }
88
89
        $blacklist = array("DCC", "CCTP", "PRIVMSG");
90
        $message = str_replace($blacklist, "(IRC Blacklist)", $message); // Lets stop DCC etc
91
92
        $msg = $message;
93
        if ($instanceName !== null && mb_strlen($instanceName) > 0) {
94
            $msg = IrcColourCode::RESET . IrcColourCode::BOLD . "[$instanceName]" . IrcColourCode::RESET . ": $message";
95
        }
96
97
        // FIXME: domains!
98
        /** @var Domain $domain */
99
        $domain = Domain::getById(1, $this->primaryDatabase);
100
101
        try {
102
            $amqpSiteConfig = $this->siteConfiguration->getAmqpConfiguration();
103
104
            $amqpConnectionConfig = new AMQPConnectionConfig();
105
            $amqpConnectionConfig->setHost($amqpSiteConfig['host']);
106
            $amqpConnectionConfig->setPort($amqpSiteConfig['port']);
107
            $amqpConnectionConfig->setUser($amqpSiteConfig['user']);
108
            $amqpConnectionConfig->setPassword($amqpSiteConfig['password']);
109
            $amqpConnectionConfig->setVhost($amqpSiteConfig['vhost']);
110
111
            if ($amqpSiteConfig['tls']) {
112
                $amqpConnectionConfig->setIsSecure(true);
113
                $amqpConnectionConfig->setSslVerify(true);
114
            }
115
            else {
116
                $amqpConnectionConfig->setIsSecure(false);
117
            }
118
119
            $connection = AMQPConnectionFactory::create($amqpConnectionConfig);
120
            $channel = $connection->channel();
121
122
            $msg = new AMQPMessage(substr($msg, 0, 512));
123
            $msg->set('user_id', $amqpSiteConfig['user']);
124
            $msg->set('app_id', $this->siteConfiguration->getUserAgent());
125
            $msg->set('content_type', 'text/plain');
126
            
127
            $channel->basic_publish($msg, $amqpSiteConfig['exchange'], $domain->getNotificationTarget());
128
            $channel->close();
129
        }
130
        catch (Exception $ex) {
131
            // OK, so we failed to send the notification - that db might be down?
132
            // This is non-critical, so silently fail.
133
            ExceptionHandler::logExceptionToDisk($ex, $this->siteConfiguration);
134
135
            // Disable notifications for remainder of request.
136
            $this->notificationsEnabled = false;
137
        }
138
    }
139
140
    #region user management
141
142
    /**
143
     * send a new user notification
144
     *
145
     * @param User $user
146
     */
147
    public function userNew(User $user)
148
    {
149
        $this->send("New user: {$user->getUsername()}");
150
    }
151
152
    /**
153
     * send an approved notification
154
     *
155
     * @param User $user
156
     */
157
    public function userApproved(User $user)
158
    {
159
        $this->send("{$user->getUsername()} approved by " . $this->currentUser->getUsername());
160
    }
161
162
    /**
163
     * send a deactivated notification
164
     *
165
     * @param User   $user
166
     * @param string $reason The reason the user has been deactivated
167
     */
168
    public function userDeactivated(User $user, $reason)
169
    {
170
        $this->send("{$user->getUsername()} deactivated by " . $this->currentUser->getUsername() . " ($reason)");
171
    }
172
173
    /**
174
     * Send a preference change notification
175
     *
176
     * @param User $user
177
     */
178
    public function userPrefChange(User $user)
179
    {
180
        $this->send("{$user->getUsername()}'s preferences were changed by " . $this->currentUser->getUsername());
181
    }
182
183
    /**
184
     * Send a user renamed notification
185
     *
186
     * @param User   $user
187
     * @param string $old
188
     */
189
    public function userRenamed(User $user, $old)
190
    {
191
        $this->send($this->currentUser->getUsername() . " renamed $old to {$user->getUsername()}");
192
    }
193
194
    /**
195
     * @param User   $user
196
     * @param string $reason
197
     */
198
    public function userRolesEdited(User $user, $reason)
199
    {
200
        $currentUser = $this->currentUser->getUsername();
201
        $this->send("Active roles for {$user->getUsername()} changed by " . $currentUser . " ($reason)");
202
    }
203
204
    #endregion
205
206
    #region Site Notice
207
208
    /**
209
     * Summary of siteNoticeEdited
210
     */
211
    public function siteNoticeEdited()
212
    {
213
        $this->send("Site notice edited by " . $this->currentUser->getUsername());
214
    }
215
    #endregion
216
217
    #region Welcome Templates
218
    /**
219
     * Summary of welcomeTemplateCreated
220
     *
221
     * @param WelcomeTemplate $template
222
     */
223
    public function welcomeTemplateCreated(WelcomeTemplate $template)
224
    {
225
        $this->send("Welcome template {$template->getId()} created by " . $this->currentUser->getUsername());
226
    }
227
228
    /**
229
     * Summary of welcomeTemplateDeleted
230
     *
231
     * @param int $templateid
232
     */
233
    public function welcomeTemplateDeleted($templateid)
234
    {
235
        $this->send("Welcome template {$templateid} deleted by " . $this->currentUser->getUsername());
236
    }
237
238
    /**
239
     * Summary of welcomeTemplateEdited
240
     *
241
     * @param WelcomeTemplate $template
242
     */
243
    public function welcomeTemplateEdited(WelcomeTemplate $template)
244
    {
245
        $this->send("Welcome template {$template->getId()} edited by " . $this->currentUser->getUsername());
246
    }
247
248
    #endregion
249
250
    #region bans
251
    /**
252
     * Summary of banned
253
     *
254
     * @param Ban $ban
255
     */
256
    public function banned(Ban $ban)
257
    {
258
        if ($ban->getDuration() === null) {
259
            $duration = "indefinitely";
260
        }
261
        else {
262
            $duration = "until " . date("F j, Y, g:i a", $ban->getDuration());
263
        }
264
265
        $username = $this->currentUser->getUsername();
266
267
        if ($ban->getVisibility() == 'user') {
268
            $this->send("Ban {$ban->getId()} set by {$username} for '{$ban->getReason()}' {$duration}");
269
        }
270
        else {
271
            $this->send("Ban {$ban->getId()} set by {$username} {$duration}");
272
        }
273
    }
274
275
    /**
276
     * Summary of unbanned
277
     *
278
     * @param Ban    $ban
279
     * @param string $unbanReason
280
     */
281
    public function unbanned(Ban $ban, $unbanReason)
282
    {
283
        $this->send("Ban {$ban->getId()} unbanned by " . $this->currentUser
284
                ->getUsername() . " (" . $unbanReason . ")");
285
    }
286
287
    #endregion
288
289
    #region request management
290
291
    /**
292
     * Summary of requestReceived
293
     *
294
     * @param Request $request
295
     */
296
    public function requestReceived(Request $request)
297
    {
298
        $this->send(
299
            IrcColourCode::DARK_GREY . "[["
300
            . IrcColourCode::DARK_GREEN . "acc:"
301
            . IrcColourCode::ORANGE . $request->getId()
302
            . IrcColourCode::DARK_GREY . "]]"
303
            . IrcColourCode::RED . " N "
304
            . IrcColourCode::DARK_BLUE . $this->baseUrl . "/internal.php/viewRequest?id={$request->getId()} "
305
            . IrcColourCode::DARK_RED . "* "
306
            . IrcColourCode::DARK_GREEN . $request->getName()
307
            . IrcColourCode::DARK_RED . " * "
308
            . IrcColourCode::RESET
309
        );
310
    }
311
312
    /**
313
     * Summary of requestDeferred
314
     *
315
     * @param Request $request
316
     */
317
    public function requestDeferred(Request $request)
318
    {
319
        /** @var RequestQueue $queue */
320
        $queue = RequestQueue::getById($request->getQueue(), $request->getDatabase());
321
322
        $deferTo = $queue->getDisplayName();
323
        $username = $this->currentUser->getUsername();
324
325
        $this->send("Request {$request->getId()} ({$request->getName()}) deferred to {$deferTo} by {$username}");
326
    }
327
328
    /**
329
     *
330
     * Summary of requestDeferredWithMail
331
     *
332
     * @param Request $request
333
     */
334
    public function requestDeferredWithMail(Request $request)
335
    {
336
        /** @var RequestQueue $queue */
337
        $queue = RequestQueue::getById($request->getQueue(), $request->getDatabase());
338
339
        $deferTo = $queue->getDisplayName();
340
        $username = $this->currentUser->getUsername();
341
        $id = $request->getId();
342
        $name = $request->getName();
343
344
        $this->send("Request {$id} ({$name}) deferred to {$deferTo} with an email by {$username}");
345
    }
346
347
    /**
348
     * Summary of requestClosed
349
     *
350
     * @param Request $request
351
     * @param string  $closetype
352
     */
353
    public function requestClosed(Request $request, $closetype)
354
    {
355
        $username = $this->currentUser->getUsername();
356
357
        $this->send("Request {$request->getId()} ({$request->getName()}) closed ($closetype) by {$username}");
358
    }
359
360
    /**
361
     * Summary of requestClosed
362
     *
363
     * @param Request $request
364
     * @param string  $closetype
365
     */
366
    public function requestCloseQueued(Request $request, $closetype)
367
    {
368
        $username = $this->currentUser->getUsername();
369
370
        $this->send("Request {$request->getId()} ({$request->getName()}) queued for creation ($closetype) by {$username}");
371
    }
372
373
    /**
374
     * Summary of requestClosed
375
     *
376
     * @param Request $request
377
     * @param User    $triggerUser
378
     */
379
    public function requestCreationFailed(Request $request, User $triggerUser)
380
    {
381
        $this->send("Request {$request->getId()} ({$request->getName()}) failed auto-creation for {$triggerUser->getUsername()}, and was sent to the hospital queue.");
382
    }
383
384
    /**
385
     * @param Request $request
386
     * @param User    $triggerUser
387
     */
388
    public function requestWelcomeFailed(Request $request, User $triggerUser)
389
    {
390
        $this->send("Request {$request->getId()} ({$request->getName()}) failed welcome for {$triggerUser->getUsername()}.");
391
    }
392
393
    /**
394
     * Summary of sentMail
395
     *
396
     * @param Request $request
397
     */
398
    public function sentMail(Request $request)
399
    {
400
        $this->send($this->currentUser->getUsername()
401
            . " sent an email related to Request {$request->getId()} ({$request->getName()})");
402
    }
403
404
    #endregion
405
406
    #region reservations
407
408
    /**
409
     * Summary of requestReserved
410
     *
411
     * @param Request $request
412
     */
413
    public function requestReserved(Request $request)
414
    {
415
        $username = $this->currentUser->getUsername();
416
417
        $this->send("Request {$request->getId()} ({$request->getName()}) reserved by {$username}");
418
    }
419
420
    /**
421
     * Summary of requestReserveBroken
422
     *
423
     * @param Request $request
424
     */
425
    public function requestReserveBroken(Request $request)
426
    {
427
        $username = $this->currentUser->getUsername();
428
429
        $this->send("Reservation on request {$request->getId()} ({$request->getName()}) broken by {$username}");
430
    }
431
432
    /**
433
     * Summary of requestUnreserved
434
     *
435
     * @param Request $request
436
     */
437
    public function requestUnreserved(Request $request)
438
    {
439
        $this->send("Request {$request->getId()} ({$request->getName()}) is no longer being handled.");
440
    }
441
442
    /**
443
     * Summary of requestReservationSent
444
     *
445
     * @param Request $request
446
     * @param User    $target
447
     */
448
    public function requestReservationSent(Request $request, User $target)
449
    {
450
        $username = $this->currentUser->getUsername();
451
452
        $this->send(
453
            "Reservation of request {$request->getId()} ({$request->getName()}) sent to {$target->getUsername()} by "
454
            . $username);
455
    }
456
457
    #endregion
458
459
    #region comments
460
461
    /**
462
     * Summary of commentCreated
463
     *
464
     * @param Comment $comment
465
     * @param Request $request
466
     */
467
    public function commentCreated(Comment $comment, Request $request)
468
    {
469
        $username = $this->currentUser->getUsername();
470
        switch ($comment->getVisibility()) {
471
            case 'admin':
472
                $visibility = " with visibility 'admin'";
473
                break;
474
            case 'checkuser':
475
                $visibility = " with visibility 'checkuser'";
476
                break;
477
            default:
478
                $visibility = "";
479
                break;
480
        }
481
482
        $this->send("{$username} posted a comment on request {$request->getId()} ({$request->getName()}){$visibility}");
483
    }
484
485
    /**
486
     * Summary of commentEdited
487
     *
488
     * @param Comment $comment
489
     * @param Request $request
490
     */
491
    public function commentEdited(Comment $comment, Request $request)
492
    {
493
        $username = $this->currentUser->getUsername();
494
495
        $this->send(<<<TAG
496
Comment {$comment->getId()} on request {$request->getId()} ({$request->getName()}) edited by {$username}
497
TAG
498
        );
499
    }
500
501
    public function alertFlaggedComments(int $count)
502
    {
503
        $this->send("There are ${count} flagged comments on closed requests currently awaiting redaction.");
504
    }
505
506
    #endregion
507
508
    #region email management (close reasons)
509
510
    /**
511
     * Summary of emailCreated
512
     *
513
     * @param EmailTemplate $template
514
     */
515
    public function emailCreated(EmailTemplate $template)
516
    {
517
        $username = $this->currentUser->getUsername();
518
        $this->send("Email {$template->getId()} ({$template->getName()}) created by " . $username);
519
    }
520
521
    /**
522
     * Summary of emailEdited
523
     *
524
     * @param EmailTemplate $template
525
     */
526
    public function emailEdited(EmailTemplate $template)
527
    {
528
        $username = $this->currentUser->getUsername();
529
        $this->send("Email {$template->getId()} ({$template->getName()}) edited by " . $username);
530
    }
531
    #endregion
532
}
533