Completed
Push — master ( a3a7a3...e1b39c )
by Vladimir
02:13
created

ConversationManager::getIntents()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
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 84
    public function __construct(Application $application, Repository $cache)
30
    {
31 84
        $this->application = $application;
32 84
        $this->cache = $cache;
33 84
    }
34
35
    /** {@inheritdoc} */
36 1
    public function registerIntent(string $class): void
37
    {
38 1
        $this->intents[] = $class;
39 1
    }
40
41
    /** {@inheritdoc} */
42 84
    public function registerFallbackIntent(string $class): void
43
    {
44 84
        $this->fallbackIntent = $class;
45 84
    }
46
47
    /** {@inheritdoc} */
48 1
    public function getIntents(): array
49
    {
50 1
        return $this->intents;
51
    }
52
53
    /** {@inheritdoc} */
54
    public function matchIntent(MessageReceived $messageReceived): ?Intent
55
    {
56
        foreach ($this->intents as $intent) {
57
            /** @var Intent $intent */
58
            $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
            $activators = (new ActivatorParser($intent->activators()))->getResult();
61
62
            foreach ($activators as $activator) {
63
                if ($activator->matches($messageReceived) && $intent->passesAuthorization($messageReceived)) {
64
                    return $intent;
65
                }
66
            }
67
        }
68
69
        // Otherwise, return fallback intent
70
        return resolve($this->fallbackIntent);
71
    }
72
73
    /** {@inheritdoc} */
74
    public function resolveContext(Channel $channel, Chat $chat, User $user): Context
75
    {
76
        $value = $this->cache->get($this->getCacheKeyForContext($channel, $chat, $user), [
77
            'chat' => $chat,
78
            'user' => $user,
79
            'intent' => null,
80
            'interaction' => null,
81
        ]);
82
83
        $context = new Context($channel, $chat, $user);
84
85
        if ($value['intent'] !== null) {
86
            $context->setIntent(resolve($value['intent']));
87
        }
88
89
        if ($value['interaction'] !== null) {
90
            $context->setInteraction(resolve($value['interaction']));
91
        }
92
93
        // Bind resolved instance to the container
94
        $this->application->instance('fondbot.conversation.context', $context);
95
96
        return $context;
97
    }
98
99
    /** {@inheritdoc} */
100
    public function saveContext(Context $context): void
101
    {
102
        $this->cache->forever(
103
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser()),
104
            $context->toArray()
105
        );
106
    }
107
108
    /** {@inheritdoc} */
109
    public function flushContext(Context $context): void
110
    {
111
        $this->cache->forget(
112
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser())
113
        );
114
    }
115
116
    /** {@inheritdoc} */
117 84
    public function getContext(): ?Context
118
    {
119 84
        if (!$this->application->has('fondbot.conversation.context')) {
120 84
            return null;
121
        }
122
123 13
        return $this->application->get('fondbot.conversation.context');
124
    }
125
126
    /** {@inheritdoc} */
127
    public function setReceivedMessage(MessageReceived $messageReceived): void
128
    {
129
        $this->messageReceived = $messageReceived;
130
    }
131
132
    /** {@inheritdoc} */
133
    public function markAsTransitioned(): void
134
    {
135
        $this->transitioned = true;
136
    }
137
138
    /** {@inheritdoc} */
139
    public function transitioned(): bool
140
    {
141
        return $this->transitioned;
142
    }
143
144
    /** {@inheritdoc} */
145
    public function converse(Conversable $conversable): void
146
    {
147
        if ($conversable instanceof Intent) {
148
            context()->setIntent($conversable)->setInteraction(null);
149
        }
150
151
        $conversable->handle($this->messageReceived);
152
    }
153
154
    /** {@inheritdoc} */
155
    public function transition(string $conversable): void
156
    {
157
        /** @var Interaction $instance */
158
        $instance = resolve($conversable);
159
160
        if (!$instance instanceof Conversable) {
161
            throw new InvalidArgumentException('Invalid conversable `'.$conversable.'`');
162
        }
163
164
        $this->converse($instance, $this->messageReceived);
0 ignored issues
show
Unused Code introduced by
The call to ConversationManager::converse() has too many arguments starting with $this->messageReceived.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
165
        $this->markAsTransitioned();
166
    }
167
168
    /** {@inheritdoc} */
169
    public function restart(Conversable $conversable): void
170
    {
171
        if ($conversable instanceof Intent) {
172
            $this->markAsTransitioned();
173
        }
174
175
        $this->converse($conversable);
176
177
        if ($conversable instanceof Interaction) {
178
            $this->markAsTransitioned();
179
        }
180
    }
181
182 84
    public function __destruct()
183
    {
184 84
        $context = $this->getContext();
185
186 84
        if ($context === null) {
187 84
            return;
188
        }
189
190
        // Close session if conversation has not been transitioned
191
        if (!$this->transitioned()) {
192
            $this->flushContext($context);
193
        }
194
195
        // Save context if exists
196
        if ($this->transitioned() && $context = context()) {
197
            $this->saveContext($context);
198
        }
199
    }
200
201
    private function getCacheKeyForContext(Channel $channel, Chat $chat, User $user): string
202
    {
203
        return implode('.', ['context', $channel->getName(), $chat->getId(), $user->getId()]);
204
    }
205
}
206