Completed
Push — 1.0 ( 794bad...d76faf )
by Vladimir
08:37
created

ConversationManager::handle()   B

Complexity

Conditions 6
Paths 22

Size

Total Lines 49
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6.0033

Importance

Changes 0
Metric Value
cc 6
eloc 25
nc 22
nop 2
dl 0
loc 49
ccs 21
cts 22
cp 0.9545
crap 6.0033
rs 8.5906
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FondBot\Conversation;
6
7
use FondBot\Http\Request;
8
use FondBot\Drivers\DriverManager;
9
use FondBot\Channels\ChannelManager;
10
use FondBot\Drivers\Exceptions\InvalidRequest;
11
use FondBot\Drivers\Extensions\WebhookVerification;
12
13
class ConversationManager
14
{
15
    /**
16
     * Determine if conversation transitioned.
17
     *
18
     * @var bool
19
     */
20
    private $transitioned = false;
21
22
    /**
23
     * Handle incoming request (webhook).
24
     *
25
     * @param string  $channelName
26
     * @param Request $request
27
     *
28
     * @return mixed
29
     */
30 4
    public function handle(string $channelName, Request $request)
31
    {
32
        try {
33
            /** @var ChannelManager $channelManager */
34 4
            $channelManager = resolve(ChannelManager::class);
35
36
            /** @var DriverManager $driverManager */
37 4
            $driverManager = resolve(DriverManager::class);
38
39 4
            $channel = $channelManager->create($channelName);
40 4
            $driver = $driverManager->get($channel, $request);
41
42 4
            kernel()->setChannel($channel);
43 4
            kernel()->setDriver($driver);
44
45
            // Driver has webhook verification
46 4
            if ($driver instanceof WebhookVerification && $driver->isVerificationRequest()) {
47 1
                return $driver->verifyWebhook();
48
            }
49
50
            // Verify request
51 3
            $driver->verifyRequest();
52
53
            // Load session
54 2
            kernel()->loadSession($channel, $driver);
55
56 2
            if (!$this->isInConversation()) {
57 1
                $this->converse(
58 1
                    $this->findIntent()
0 ignored issues
show
Bug introduced by
It seems like $this->findIntent() can be null; however, converse() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
59
                );
60
            } else {
61 1
                $this->converse(
62 1
                    session()->getInteraction()
0 ignored issues
show
Bug introduced by
It seems like session()->getInteraction() can be null; however, converse() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
63
                );
64
            }
65
66
            // Close session if conversation has not been transitioned
67
            // Otherwise, save session state
68 2
            if (!$this->transitioned) {
69 2
                kernel()->closeSession();
70
            } else {
71
                kernel()->saveSession();
72
            }
73 1
        } catch (InvalidRequest $exception) {
74 1
            logger()->warning('ConversationManager[handle] - Invalid Request', ['message' => $exception->getMessage()]);
75
        }
76
77 3
        return 'OK';
78
    }
79
80
    /**
81
     * Start conversation.
82
     *
83
     * @param Conversable|mixed $conversable
84
     */
85 5
    public function converse(Conversable $conversable): void
86
    {
87 5
        if ($conversable instanceof Intent) {
88 2
            $session = session();
89 2
            $session->setIntent($conversable);
90 2
            $session->setInteraction(null);
91 2
            $session->setContext([]);
92
93 2
            kernel()->setSession($session);
94
95 2
            $conversable->handle(kernel());
96 3
        } elseif ($conversable instanceof Interaction) {
97 2
            $conversable->handle(kernel());
98
        } else {
99 1
            $conversable->handle(kernel());
100
        }
101 5
    }
102
103
    /**
104
     * Transition to intent or interaction.
105
     *
106
     * @param Conversable $conversable
107
     */
108 1
    public function transition(Conversable $conversable): void
109
    {
110 1
        $this->converse($conversable);
111 1
        $this->transitioned = true;
112 1
    }
113
114
    /**
115
     * Restart intent or interaction.
116
     *
117
     * @param Conversable|mixed $conversable
118
     */
119 2
    public function restart(Conversable $conversable): void
120
    {
121
        switch (true) {
122 2
            case $conversable instanceof Intent:
123 1
                $this->converse($conversable);
124
125 1
                $this->transitioned = true;
126 1
                break;
127 1
            case $conversable instanceof Interaction:
128 1
                $session = session();
129 1
                $session->setInteraction(null);
130 1
                $session->setContext([]);
131
132 1
                kernel()->setSession($session);
133
134 1
                $this->transitioned = true;
135
136 1
                $this->converse($conversable);
137 1
                break;
138
        }
139 2
    }
140
141
    /**
142
     * Find matching intent.
143
     *
144
     * @return Intent|null
145
     */
146 1
    private function findIntent(): Intent
147
    {
148
        /** @var IntentManager $intentManager */
149 1
        $intentManager = resolve(IntentManager::class);
150
151 1
        return $intentManager->find(kernel()->getDriver()->getMessage());
152
    }
153
154
    /**
155
     * Determine if conversation started.
156
     *
157
     * @return bool
158
     */
159 2
    private function isInConversation(): bool
160
    {
161 2
        return session()->getInteraction() !== null;
162
    }
163
}
164