Passed
Push — master ( 350d73...c9415f )
by Vladimir
02:02
created

ConversationManager::restartInteraction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
            $intent = resolve(/** @scrutinizer ignore-type */ $intent);
Loading history...
60 1
61 1
            foreach ($intent->activators() as $activator) {
62
                if (!$intent->passesAuthorization($messageReceived)) {
63
                    continue;
64
                }
65
66
                if ($activator instanceof Closure && value($activator($messageReceived)) === true) {
67 1
                    return $intent;
68
                }
69
70
                if ($activator instanceof Activator && $activator->matches($messageReceived)) {
71 1
                    return $intent;
72
                }
73 1
            }
74 1
        }
75 1
76
        // Otherwise, return fallback intent
77
        return resolve($this->fallbackIntent);
0 ignored issues
show
Bug Best Practice introduced by
The expression return resolve($this->fallbackIntent) could return the type Illuminate\Foundation\Application which is incompatible with the type-hinted return FondBot\Conversation\Intent|null. Consider adding an additional type-check to rule them out.
Loading history...
78
    }
79
80
    /** {@inheritdoc} */
81 1
    public function resolveContext(Channel $channel, Chat $chat, User $user): Context
82
    {
83 1
        $value = $this->cache->get($this->getCacheKeyForContext($channel, $chat, $user), [
84 1
            'chat' => $chat,
85
            'user' => $user,
86
            'intent' => null,
87 1
            'interaction' => null,
88 1
            'items' => [],
89
        ]);
90
91
        $context = new Context($channel, $chat, $user, $value['items'] ?? []);
92 1
93
        if (isset($value['intent'])) {
94 1
            $context->setIntent(resolve($value['intent']));
0 ignored issues
show
Bug introduced by
It seems like resolve($value['intent']) can also be of type Illuminate\Foundation\Application; however, parameter $intent of FondBot\Conversation\Context::setIntent() does only seem to accept FondBot\Conversation\Intent, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
            $context->setIntent(/** @scrutinizer ignore-type */ resolve($value['intent']));
Loading history...
95
        }
96
97
        if (isset($value['interaction'])) {
98 1
            $context->setInteraction(resolve($value['interaction']));
0 ignored issues
show
Bug introduced by
It seems like resolve($value['interaction']) can also be of type Illuminate\Foundation\Application; however, parameter $interaction of FondBot\Conversation\Context::setInteraction() does only seem to accept null|FondBot\Conversation\Interaction, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

98
            $context->setInteraction(/** @scrutinizer ignore-type */ resolve($value['interaction']));
Loading history...
99
        }
100 1
101 1
        // Bind resolved instance to the container
102 1
        $this->application->instance('fondbot.conversation.context', $context);
103
104 1
        return $context;
105
    }
106
107
    /** {@inheritdoc} */
108
    public function saveContext(Context $context): void
109
    {
110
        $this->cache->forever(
111
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser()),
112
            $context->toArray()
113
        );
114
    }
115 82
116
    /** {@inheritdoc} */
117 82
    public function flushContext(Context $context): void
118 82
    {
119
        $this->cache->forget(
120
            $this->getCacheKeyForContext($context->getChannel(), $context->getChat(), $context->getUser())
121 15
        );
122
    }
123
124
    /** {@inheritdoc} */
125
    public function getContext(): ?Context
126
    {
127
        if (!$this->application->has('fondbot.conversation.context')) {
128
            return null;
129
        }
130
131
        return $this->application->get('fondbot.conversation.context');
132
    }
133
134
    /** {@inheritdoc} */
135
    public function setReceivedMessage(MessageReceived $messageReceived): void
136
    {
137
        $this->messageReceived = $messageReceived;
138
    }
139
140
    /** {@inheritdoc} */
141
    public function markAsTransitioned(): void
142
    {
143
        $this->transitioned = true;
144
    }
145
146
    /** {@inheritdoc} */
147
    public function transitioned(): bool
148
    {
149
        return $this->transitioned;
150
    }
151
152
    /** {@inheritdoc} */
153
    public function converse(Conversable $conversable): void
154 82
    {
155
        context()->incrementAttempts();
156 82
157
        if ($conversable instanceof Intent) {
158 82
            context()->setIntent($conversable)->setInteraction(null);
159 82
        }
160
161
        $conversable->handle($this->messageReceived);
162
    }
163
164
    /** {@inheritdoc} */
165
    public function restartInteraction(Interaction $interaction): void
166
    {
167
        context()->setInteraction(null);
168
169
        $this->converse($interaction);
170
171
        $this->markAsTransitioned();
172
    }
173 2
174
    public function __destruct()
175 2
    {
176
        $context = $this->getContext();
177
178
        if ($context === null) {
179
            return;
180
        }
181
182
        // Close session if conversation has not been transitioned
183
        if (!$this->transitioned()) {
184
            $this->flushContext($context);
185
        }
186
187
        // Save context if exists
188
        if ($this->transitioned() && $context = context()) {
189
            $this->saveContext($context);
190
        }
191
    }
192
193
    private function getCacheKeyForContext(Channel $channel, Chat $chat, User $user): string
194
    {
195
        return implode('.', ['context', $channel->getName(), $chat->getId(), $user->getId()]);
196
    }
197
}
198