Completed
Push — master ( a22976...bdfc9e )
by Vladimir
03:53
created

ConversationManager::matchIntent()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 1
dl 0
loc 16
ccs 7
cts 7
cp 1
crap 5
rs 9.4222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FondBot\Conversation;
6
7
use FondBot\Channels\Chat;
8
use FondBot\Channels\User;
9
use FondBot\Channels\Channel;
10
use InvalidArgumentException;
11
use Illuminate\Cache\Repository;
12
use FondBot\Events\MessageReceived;
13
use FondBot\Contracts\Conversation\Manager;
14
use FondBot\Contracts\Conversation\Conversable;
15
use Illuminate\Contracts\Foundation\Application;
16
17
class ConversationManager implements Manager
18
{
19
    private $intents = [];
20
    private $fallbackIntent;
21
22
    private $application;
23
    private $cache;
24
25
    private $transitioned = false;
26
27
    private $messageReceived;
28
29 80
    public function __construct(Application $application, Repository $cache)
30
    {
31 80
        $this->application = $application;
32 80
        $this->cache = $cache;
33 80
    }
34
35
    /** {@inheritdoc} */
36 2
    public function registerIntent(string $class): void
37
    {
38 2
        $this->intents[] = $class;
39 2
    }
40
41
    /** {@inheritdoc} */
42 80
    public function registerFallbackIntent(string $class): void
43
    {
44 80
        $this->fallbackIntent = $class;
45 80
    }
46
47
    /** {@inheritdoc} */
48 1
    public function getIntents(): array
49
    {
50 1
        return $this->intents;
51
    }
52
53
    /** {@inheritdoc} */
54 1
    public function matchIntent(MessageReceived $messageReceived): ?Intent
55
    {
56 1
        foreach ($this->intents as $intent) {
57
            /** @var Intent $intent */
58 1
            $intent = resolve($intent);
0 ignored issues
show
Documentation introduced by
$intent is of type object<FondBot\Conversation\Intent>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
59
60 1
            foreach ($intent->activators() as $activator) {
61 1
                if ($activator->matches($messageReceived) && $intent->passesAuthorization($messageReceived)) {
62 1
                    return $intent;
63
                }
64
            }
65
        }
66
67
        // Otherwise, return fallback intent
68 1
        return resolve($this->fallbackIntent);
69
    }
70
71
    /** {@inheritdoc} */
72 1
    public function resolveContext(Channel $channel, Chat $chat, User $user): Context
73
    {
74 1
        $value = $this->cache->get($this->getCacheKeyForContext($channel, $chat, $user), [
75 1
            'chat' => $chat,
76 1
            'user' => $user,
77
            'intent' => null,
78
            'interaction' => null,
79
            'items' => [],
80
        ]);
81
82 1
        $context = new Context($channel, $chat, $user, $value['items'] ?? []);
83
84 1
        if (isset($value['intent'])) {
85 1
            $context->setIntent(resolve($value['intent']));
86
        }
87
88 1
        if (isset($value['interaction'])) {
89 1
            $context->setInteraction(resolve($value['interaction']));
90
        }
91
92
        // Bind resolved instance to the container
93 1
        $this->application->instance('fondbot.conversation.context', $context);
94
95 1
        return $context;
96
    }
97
98
    /** {@inheritdoc} */
99 1
    public function saveContext(Context $context): void
100
    {
101 1
        $this->cache->forever(
102 1
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser()),
103 1
            $context->toArray()
104
        );
105 1
    }
106
107
    /** {@inheritdoc} */
108
    public function flushContext(Context $context): void
109
    {
110
        $this->cache->forget(
111
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser())
112
        );
113
    }
114
115
    /** {@inheritdoc} */
116 80
    public function getContext(): ?Context
117
    {
118 80
        if (!$this->application->has('fondbot.conversation.context')) {
119 80
            return null;
120
        }
121
122 13
        return $this->application->get('fondbot.conversation.context');
123
    }
124
125
    /** {@inheritdoc} */
126
    public function setReceivedMessage(MessageReceived $messageReceived): void
127
    {
128
        $this->messageReceived = $messageReceived;
129
    }
130
131
    /** {@inheritdoc} */
132
    public function markAsTransitioned(): void
133
    {
134
        $this->transitioned = true;
135
    }
136
137
    /** {@inheritdoc} */
138
    public function transitioned(): bool
139
    {
140
        return $this->transitioned;
141
    }
142
143
    /** {@inheritdoc} */
144
    public function converse(Conversable $conversable): void
145
    {
146
        if ($conversable instanceof Intent) {
147
            context()->setIntent($conversable)->setInteraction(null);
148
        }
149
150
        $conversable->handle($this->messageReceived);
151
    }
152
153
    /** {@inheritdoc} */
154
    public function transition(string $conversable): void
155
    {
156
        /** @var Interaction $instance */
157
        $instance = resolve($conversable);
158
159
        if (!$instance instanceof Conversable) {
160
            throw new InvalidArgumentException('Invalid conversable `'.$conversable.'`');
161
        }
162
163
        $this->converse($instance);
164
        $this->markAsTransitioned();
165
    }
166
167
    /** {@inheritdoc} */
168
    public function restart(Conversable $conversable): void
169
    {
170
        if ($conversable instanceof Intent) {
171
            $this->markAsTransitioned();
172
        }
173
174
        $this->converse($conversable);
175
176
        if ($conversable instanceof Interaction) {
177
            $this->markAsTransitioned();
178
        }
179
    }
180
181 80
    public function __destruct()
182
    {
183 80
        $context = $this->getContext();
184
185 80
        if ($context === null) {
186 80
            return;
187
        }
188
189
        // Close session if conversation has not been transitioned
190
        if (!$this->transitioned()) {
191
            $this->flushContext($context);
192
        }
193
194
        // Save context if exists
195
        if ($this->transitioned() && $context = context()) {
196
            $this->saveContext($context);
197
        }
198
    }
199
200 2
    private function getCacheKeyForContext(Channel $channel, Chat $chat, User $user): string
201
    {
202 2
        return implode('.', ['context', $channel->getName(), $chat->getId(), $user->getId()]);
203
    }
204
}
205