1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Nopolabs\Yabot\Plugin; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Nopolabs\Yabot\Helpers\ConfigTrait; |
7
|
|
|
use Nopolabs\Yabot\Helpers\LogTrait; |
8
|
|
|
use Nopolabs\Yabot\Message\Message; |
9
|
|
|
use Throwable; |
10
|
|
|
|
11
|
|
|
trait PluginTrait |
12
|
|
|
{ |
13
|
|
|
use LogTrait; |
14
|
|
|
use ConfigTrait; |
15
|
|
|
|
16
|
|
|
private $pluginId; |
17
|
|
|
|
18
|
|
|
/** @var PluginMatcher */ |
19
|
|
|
private $pluginMatcher; |
20
|
|
|
|
21
|
|
|
/** @var MethodMatcher[] */ |
22
|
|
|
private $methodMatchers; |
23
|
|
|
|
24
|
|
|
public function help(): string |
25
|
|
|
{ |
26
|
|
|
return $this->get('help', 'no help available'); |
27
|
|
|
} |
28
|
|
|
|
29
|
|
|
public function status(): string |
30
|
|
|
{ |
31
|
|
|
return 'running'; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
public function init(string $pluginId, array $params) |
35
|
|
|
{ |
36
|
|
|
$this->pluginId = $pluginId; |
37
|
|
|
$this->overrideConfig($params); |
38
|
|
|
$this->methodMatchers = $this->buildMethodMatchers($this->get('matchers')); |
39
|
|
|
$this->pluginMatcher = new PluginMatcher($pluginId, $this->getIsBot(), $this->getChannels(), $this->getUsers(), $this->getLog()); |
40
|
|
|
|
41
|
|
|
$this->info("inited $pluginId config:", $this->getConfig()); |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
public function replaceInPatterns($search, $replace, array $matchers): array |
45
|
|
|
{ |
46
|
|
|
$replaced = []; |
47
|
|
|
foreach ($matchers as $name => $params) { |
48
|
|
|
$params['patterns'] = array_map(function($pattern) use ($search, $replace) { |
49
|
|
|
return str_replace($search, $replace, $pattern); |
50
|
|
|
}, $params['patterns']); |
51
|
|
|
$replaced[$name] = $params; |
52
|
|
|
} |
53
|
|
|
return $replaced; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
public function getPriority(): int |
57
|
|
|
{ |
58
|
|
|
return $this->get('priority'); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
public function getPrefix(): string |
62
|
|
|
{ |
63
|
|
|
return $this->get('prefix'); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
public function handle(Message $message) |
67
|
|
|
{ |
68
|
|
|
if (!$this->pluginMatch($message)) { |
69
|
|
|
return; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
if ($matched = $this->methodMatch($message)) { |
73
|
|
|
list($method, $matches) = $matched; |
74
|
|
|
|
75
|
|
|
$this->dispatch($method, $message, $matches); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
protected function pluginMatch(Message $message): bool |
80
|
|
|
{ |
81
|
|
|
return $this->pluginMatcher->matches($message); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
protected function methodMatch(Message $message): array |
85
|
|
|
{ |
86
|
|
|
foreach ($this->methodMatchers as $methodMatcher) { |
87
|
|
|
if (($matches = $methodMatcher->matches($message)) === false) { |
88
|
|
|
continue; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
$method = $methodMatcher->getMethod(); |
92
|
|
|
|
93
|
|
|
$this->info("{$this->pluginId}:{$methodMatcher->getName()}:$method matched"); |
94
|
|
|
|
95
|
|
|
return [$method, $matches]; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
return []; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
protected function dispatch(string $method, Message $message, array $matches) |
102
|
|
|
{ |
103
|
|
|
if (!method_exists($this, $method)) { |
104
|
|
|
$this->warning("{$this->pluginId} no method named: $method"); |
105
|
|
|
return; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
try { |
109
|
|
|
|
110
|
|
|
$this->$method($message, $matches); |
111
|
|
|
|
112
|
|
|
} catch (Throwable $throwable) { |
113
|
|
|
$errmsg = 'Exception in '.static::class.'::'.$method."\n" |
114
|
|
|
.$throwable->getMessage()."\n" |
115
|
|
|
.$throwable->getTraceAsString(); |
116
|
|
|
$this->warning($errmsg); |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
protected function overrideConfig(array $params) |
121
|
|
|
{ |
122
|
|
|
$config = $this->canonicalConfig(array_merge($this->getConfig(), $params)); |
123
|
|
|
|
124
|
|
|
$this->setConfig($config); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
protected function setPluginId($pluginId) |
128
|
|
|
{ |
129
|
|
|
$this->pluginId = $pluginId; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
protected function getUsers() |
133
|
|
|
{ |
134
|
|
|
return $this->get('users'); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
protected function getChannels() |
138
|
|
|
{ |
139
|
|
|
return $this->get('channels'); |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return bool|null |
144
|
|
|
*/ |
145
|
|
|
protected function getIsBot() |
146
|
|
|
{ |
147
|
|
|
return $this->get('isBot'); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
protected function getMatchers(): array |
151
|
|
|
{ |
152
|
|
|
return $this->get('matchers'); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
protected function canonicalConfig(array $config): array |
156
|
|
|
{ |
157
|
|
|
return [ |
158
|
|
|
'priority' => $config['priority'] ?? PluginManager::DEFAULT_PRIORITY, |
159
|
|
|
'prefix' => ($config['prefix'] ?? '') ? $config['prefix'] : PluginManager::NO_PREFIX, |
160
|
|
|
'isBot' => $config['isBot'] ?? null, |
161
|
|
|
'channels' => $config['channels'] ?? array_filter([$config['channel'] ?? null]), |
162
|
|
|
'users' => $config['users'] ?? array_filter([$config['user'] ?? null]), |
163
|
|
|
'matchers' => $this->canonicalMatchers($config['matchers'] ?? []), |
164
|
|
|
]; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
protected function canonicalMatchers(array $matchers): array |
168
|
|
|
{ |
169
|
|
|
$expanded = []; |
170
|
|
|
|
171
|
|
|
foreach ($matchers as $name => $params) { |
172
|
|
|
$expanded[$name] = $this->canonicalMatcher($name, $params); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
if (!$expanded) { |
|
|
|
|
176
|
|
|
$this->warning("{$this->pluginId} has no matchers"); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return $expanded; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
protected function canonicalMatcher(string $name, $params) : array |
183
|
|
|
{ |
184
|
|
|
$params = is_array($params) ? $params : ['patterns' => [$params]]; |
185
|
|
|
|
186
|
|
|
$method = $params['method'] ?? $name; |
187
|
|
|
|
188
|
|
|
if (!method_exists($this, $method)) { |
189
|
|
|
$this->warning("{$this->pluginId} no method named: $method"); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
return [ |
193
|
|
|
'isBot' => $params['isBot'] ?? null, |
194
|
|
|
'channels' => $params['channels'] ?? array_filter([$params['channel'] ?? null]), |
195
|
|
|
'users' => $params['users'] ?? array_filter([$params['user'] ?? null]), |
196
|
|
|
'patterns' => $params['patterns'] ?? array_filter([$params['pattern'] ?? null]), |
197
|
|
|
'method' => $params['method'] ?? $name, |
198
|
|
|
]; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
protected function buildMethodMatchers(array $matchers) : array |
202
|
|
|
{ |
203
|
|
|
$methodMatchers = []; |
204
|
|
|
|
205
|
|
|
foreach ($matchers as $name => $params) { |
206
|
|
|
$methodMatchers[] = new MethodMatcher( |
207
|
|
|
$name, |
208
|
|
|
$params['isBot'], |
209
|
|
|
$params['channels'], |
210
|
|
|
$params['users'], |
211
|
|
|
$this->validPatterns($params['patterns'], $name), |
212
|
|
|
$params['method'], |
213
|
|
|
$this->getLog() |
214
|
|
|
); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
return $methodMatchers; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
protected function validPatterns(array $patterns, $name) : array |
221
|
|
|
{ |
222
|
|
|
$valid = []; |
223
|
|
|
foreach ($patterns as $pattern) { |
224
|
|
|
if ($this->isValidRegExp($pattern, $name)) { |
225
|
|
|
$valid[] = $pattern; |
226
|
|
|
} |
227
|
|
|
} |
228
|
|
|
return $valid; |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
protected function isValidRegExp($pattern, $name) : bool |
232
|
|
|
{ |
233
|
|
|
try { |
234
|
|
|
preg_match($pattern, ''); |
235
|
|
|
return true; |
236
|
|
|
} catch (Throwable $e) { |
237
|
|
|
$this->warning("$name.pattern='$pattern' ".$e->getMessage()); |
238
|
|
|
return false; |
239
|
|
|
} |
240
|
|
|
} |
241
|
|
|
} |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.