ConnectionManager   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 8
eloc 31
c 1
b 0
f 0
dl 0
loc 102
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getApiClient() 0 3 1
A startPingLoop() 0 12 2
A __construct() 0 6 1
A handleInterrupt() 0 4 1
A getRtmClient() 0 3 1
A connect() 0 25 2
1
<?php
2
namespace PortlandLabs\Slackbot\Slack;
3
4
use PortlandLabs\Slackbot\Slack\Api\Payload\RtmConnectPayloadResponse;
5
use PortlandLabs\Slackbot\Slack\Rtm\WebsocketNegotiator;
6
use Psr\Log\LoggerInterface;
7
use React\EventLoop\LoopInterface;
8
9
class ConnectionManager
10
{
11
12
    /** @var Api\Client */
13
    protected $apiClient;
14
15
    /** @var Rtm\Client */
16
    protected $rtmClient;
17
18
    /** @var WebsocketNegotiator */
19
    protected $negotiator;
20
    /**
21
     * @var LoggerInterface
22
     */
23
    private $logger;
24
25
    public function __construct(Api\Client $apiClient, Rtm\Client $rtmClient, WebsocketNegotiator $negotiator, LoggerInterface $logger)
26
    {
27
        $this->apiClient = $apiClient;
28
        $this->rtmClient = $rtmClient;
29
        $this->negotiator = $negotiator;
30
        $this->logger = $logger;
31
    }
32
33
    /**
34
     * Connect to slack
35
     *
36
     * @return \React\Promise\Promise
37
     * @throws \GuzzleHttp\Exception\GuzzleException
38
     */
39
    public function connect(LoopInterface $loop, array $middleware)
40
    {
41
        $this->connecting = true;
0 ignored issues
show
Bug Best Practice introduced by
The property connecting does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
42
        $payload = $this->negotiator->resolveUrl($this->apiClient, 10);
43
44
        if (!$payload) {
45
            $this->logger->critical('[CON.ERR] Failed to negotiate websocket URL.');
46
            throw new \RuntimeException('Failed to negotiate websocket URL.');
47
        }
48
49
        // Start listening
50
        $promise = $this->rtmClient->listen($loop, $payload, $middleware, function(){});
51
52
        // Handle connecting
53
        $promise->done(function($result) use ($loop) {
54
            /** @var RtmConnectPayloadResponse $payload */
55
            [$connection, $payload] = $result;
56
57
            $this->apiClient->setUsername($payload->getUserName());
58
59
            // Start sending Pings
60
            $this->startPingLoop($loop);
61
        });
62
63
        return $promise;
64
    }
65
66
    /**
67
     * Start sending pings over RTM to keep us alive
68
     *
69
     * @param LoopInterface $loop
70
     */
71
    protected function startPingLoop(LoopInterface $loop)
72
    {
73
        // Track how many times pings fail
74
        $fails = 0;
75
76
        $loop->addPeriodicTimer(10, function () use (&$fails) {
77
            $this->rtmClient->sendPing()->otherwise(function () use (&$fails) {
78
                $fails++;
79
80
                if ($fails > 3) {
81
                    $this->handleInterrupt();
82
                    $fails = 0;
83
                }
84
            });
85
        });
86
    }
87
88
    /**
89
     * Handle the RTM session getting interrupted
90
     */
91
    protected function handleInterrupt()
92
    {
93
        $this->logger->critical('-- Disconnected from RTM, Ping timeout --');
94
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
95
    }
96
97
    /**
98
     * @return Api\Client
99
     */
100
    public function getApiClient(): Api\Client
101
    {
102
        return $this->apiClient;
103
    }
104
105
    /**
106
     * @return Rtm\Client
107
     */
108
    public function getRtmClient(): Rtm\Client
109
    {
110
        return $this->rtmClient;
111
    }
112
113
}