1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Livia |
4
|
|
|
* Copyright 2017-2019 Charlotte Dunois, All Rights Reserved |
5
|
|
|
* |
6
|
|
|
* Website: https://charuru.moe |
7
|
|
|
* License: https://github.com/CharlotteDunois/Livia/blob/master/LICENSE |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace CharlotteDunois\Livia\Commands; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* A command that can be run in a client. |
14
|
|
|
* |
15
|
|
|
* @property \CharlotteDunois\Livia\Client $client The client which initiated the instance. |
16
|
|
|
* @property string $name The name of the command. |
17
|
|
|
* @property string[] $aliases Aliases of the command. |
18
|
|
|
* @property \CharlotteDunois\Livia\Commands\CommandGroup|null $group The group the command belongs to, assigned upon registration. |
19
|
|
|
* @property string $groupID ID of the command group the command is part of. |
20
|
|
|
* @property string $description A short description of the command. |
21
|
|
|
* @property string|null $details A longer description of the command. |
22
|
|
|
* @property string $format Usage format string of the command. |
23
|
|
|
* @property string[] $examples Examples of and for the command. |
24
|
|
|
* @property bool $guildOnly Whether the command can only be triggered in a guild channel. |
25
|
|
|
* @property bool $ownerOnly Whether the command can only be triggered by the bot owner (requires default hasPermission method). |
26
|
|
|
* @property string[]|null $clientPermissions The required permissions for the client user to make the command work. |
27
|
|
|
* @property string[]|null $userPermissions The required permissions for the user to use the command. |
28
|
|
|
* @property bool $nsfw Whether the command can only be run in NSFW channels. |
29
|
|
|
* @property array $throttling Options for throttling command usages. |
30
|
|
|
* @property bool $defaultHandling Whether the command gets handled normally. |
31
|
|
|
* @property array $args An array containing the command arguments. |
32
|
|
|
* @property int|double $argsPromptLimit How many times the user gets prompted for an argument. |
33
|
|
|
* @property bool $argsSingleQuotes Whether single quotes are allowed to encapsulate an argument. |
34
|
|
|
* @property string $argsType How the arguments are split when passed to the command's run method. |
35
|
|
|
* @property int $argsCount Maximum number of arguments that will be split. |
36
|
|
|
* @property string[] $patterns Regular expression triggers. |
37
|
|
|
* @property bool $guarded Whether the command is protected from being disabled. |
38
|
|
|
* @property bool $hidden Whether the command is hidden in the `help` command overview. |
39
|
|
|
*/ |
40
|
|
|
abstract class Command { |
41
|
|
|
/** |
42
|
|
|
* The client which initiated the instance. |
43
|
|
|
* @var \CharlotteDunois\Livia\Client |
44
|
|
|
*/ |
45
|
|
|
protected $client; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* The name of the command. |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
protected $name; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Aliases of the command. |
55
|
|
|
* @var string[] |
56
|
|
|
*/ |
57
|
|
|
protected $aliases = array(); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* ID of the command group the command is part of. |
61
|
|
|
* @var string |
62
|
|
|
*/ |
63
|
|
|
protected $groupID; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* A short description of the command. |
67
|
|
|
* @var string |
68
|
|
|
*/ |
69
|
|
|
protected $description; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* A longer description of the command. |
73
|
|
|
* @var string|null |
74
|
|
|
*/ |
75
|
|
|
protected $details; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Usage format string of the command. |
79
|
|
|
* @var string |
80
|
|
|
*/ |
81
|
|
|
protected $format = ''; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Examples of and for the command. |
85
|
|
|
* @var string[] |
86
|
|
|
*/ |
87
|
|
|
protected $examples = array(); |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Whether the command can only be triggered in a guild channel. |
91
|
|
|
* @var bool |
92
|
|
|
*/ |
93
|
|
|
protected $guildOnly = false; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Whether the command can only be triggered by the bot owner (requires default hasPermission method). |
97
|
|
|
* @var bool |
98
|
|
|
*/ |
99
|
|
|
protected $ownerOnly = false; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* The required permissions for the client user to make the command work. |
103
|
|
|
* @var string[]|null |
104
|
|
|
*/ |
105
|
|
|
protected $clientPermissions; |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* The required permissions for the user to use the command. |
109
|
|
|
* @var string[]|null |
110
|
|
|
*/ |
111
|
|
|
protected $userPermissions; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Whether the command can only be run in NSFW channels. |
115
|
|
|
* @var bool |
116
|
|
|
*/ |
117
|
|
|
protected $nsfw = false; |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Options for throttling command usages. |
121
|
|
|
* @var array |
122
|
|
|
*/ |
123
|
|
|
protected $throttling = array(); |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Whether the command gets handled normally. |
127
|
|
|
* @var bool |
128
|
|
|
*/ |
129
|
|
|
protected $defaultHandling = true; |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* An array containing the command arguments. |
133
|
|
|
* @var array |
134
|
|
|
*/ |
135
|
|
|
protected $args = array(); |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* How many times the user gets prompted for an argument. |
139
|
|
|
* @var int|float |
140
|
|
|
*/ |
141
|
|
|
protected $argsPromptLimit = \INF; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Whether single quotes are allowed to encapsulate an argument. |
145
|
|
|
* @var bool |
146
|
|
|
*/ |
147
|
|
|
protected $argsSingleQuotes = true; |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* How the arguments are split when passed to the command's run method. |
151
|
|
|
* @var string |
152
|
|
|
*/ |
153
|
|
|
protected $argsType = 'single'; |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Maximum number of arguments that will be split. |
157
|
|
|
* @var int |
158
|
|
|
*/ |
159
|
|
|
protected $argsCount = 0; |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Command patterns for pattern matches. |
163
|
|
|
* @var string[] |
164
|
|
|
*/ |
165
|
|
|
protected $patterns = array(); |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Whether the command is guarded (can not be disabled). |
169
|
|
|
* @var bool |
170
|
|
|
*/ |
171
|
|
|
protected $guarded = false; |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Whether the command is hidden in the `help` command overview. |
175
|
|
|
* @var bool |
176
|
|
|
*/ |
177
|
|
|
protected $hidden = false; |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Whether the command is globally enabled. |
181
|
|
|
* @var bool |
182
|
|
|
*/ |
183
|
|
|
protected $globalEnabled = true; |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Array of guild ID to bool, which indicates whether the command is enabled in that guild. |
187
|
|
|
* @var bool[] |
188
|
|
|
*/ |
189
|
|
|
protected $guildEnabled = array(); |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* The argument collector for the command. |
193
|
|
|
* @var \CharlotteDunois\Livia\Arguments\ArgumentCollector|null |
194
|
|
|
*/ |
195
|
|
|
protected $argsCollector; |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* A collection of throttle arrays. |
199
|
|
|
* @var \CharlotteDunois\Collect\Collection |
200
|
|
|
*/ |
201
|
|
|
protected $throttles; |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Constructs a new Command. Info is an array as following: |
205
|
|
|
* |
206
|
|
|
* ``` |
207
|
|
|
* array( |
208
|
|
|
* 'name' => string, |
209
|
|
|
* 'aliases' => string[], (optional) |
210
|
|
|
* 'group' => string, (the ID of the command group) |
211
|
|
|
* 'description => string, |
212
|
|
|
* 'details' => string, (optional) |
213
|
|
|
* 'format' => string, (optional) |
214
|
|
|
* 'examples' => string[], (optional) |
215
|
|
|
* 'guildOnly' => bool, (defaults to false) |
216
|
|
|
* 'ownerOnly' => bool, (defaults to false) |
217
|
|
|
* 'clientPermissions' => string[], (optional) |
218
|
|
|
* 'userPermissions' => string[], (optional) |
219
|
|
|
* 'nsfw' => bool, (defaults to false) |
220
|
|
|
* 'throttling' => array, (associative array of array('usages' => int, 'duration' => int) - duration in seconds, optional) |
221
|
|
|
* 'defaultHandling' => bool, (defaults to true) |
222
|
|
|
* 'args' => array, ({@see \CharlotteDunois\Livia\Arguments\Argument} - key can be the index instead, optional) |
223
|
|
|
* 'argsPromptLimit' => int|\INF, (optional) |
224
|
|
|
* 'argsType' => string, (one of 'single' or 'multiple', defaults to 'single') |
225
|
|
|
* 'argsCount' => int, (optional) |
226
|
|
|
* 'argsSingleQuotes' => bool, (optional) |
227
|
|
|
* 'patterns' => string[], (Regular Expression strings, pattern matches don't get parsed for arguments, optional) |
228
|
|
|
* 'guarded' => bool, (defaults to false) |
229
|
|
|
* 'hidden' => bool, (defaults to false) |
230
|
|
|
* ) |
231
|
|
|
* ``` |
232
|
|
|
* |
233
|
|
|
* @param \CharlotteDunois\Livia\Client $client |
234
|
|
|
* @param array $info |
235
|
|
|
* @throws \InvalidArgumentException |
236
|
|
|
*/ |
237
|
|
|
function __construct(\CharlotteDunois\Livia\Client $client, array $info) { |
238
|
|
|
$this->client = $client; |
239
|
|
|
|
240
|
|
|
\CharlotteDunois\Validation\Validator::make($info, array( |
241
|
|
|
'name' => 'required|string|lowercase|nowhitespace', |
242
|
|
|
'group' => 'required|string', |
243
|
|
|
'description' => 'required|string', |
244
|
|
|
'aliases' => 'array:string', |
245
|
|
|
'autoAliases' => 'boolean', |
246
|
|
|
'details' => 'string', |
247
|
|
|
'format' => 'string', |
248
|
|
|
'examples' => 'array:string', |
249
|
|
|
'guildOnly' => 'boolean', |
250
|
|
|
'ownerOnly' => 'boolean', |
251
|
|
|
'clientPermissions' => 'array', |
252
|
|
|
'userPermissions' => 'array', |
253
|
|
|
'nsfw' => 'boolean', |
254
|
|
|
'throttling' => 'array', |
255
|
|
|
'defaultHandling' => 'boolean', |
256
|
|
|
'args' => 'array', |
257
|
|
|
'argsType' => 'string|in:single,multiple', |
258
|
|
|
'argsPromptLimit' => 'integer|float', |
259
|
|
|
'argsCount' => 'integer|min:2', |
260
|
|
|
'patterns' => 'array:string', |
261
|
|
|
'guarded' => 'boolean', |
262
|
|
|
'hidden' => 'boolean' |
263
|
|
|
))->throw(\InvalidArgumentException::class); |
264
|
|
|
|
265
|
|
|
$this->name = $info['name']; |
266
|
|
|
$this->groupID = $info['group']; |
267
|
|
|
$this->description = $info['description']; |
268
|
|
|
|
269
|
|
|
if(!empty($info['aliases'])) { |
270
|
|
|
$this->aliases = $info['aliases']; |
271
|
|
|
|
272
|
|
|
foreach($this->aliases as $alias) { |
273
|
|
|
if(\mb_strtolower($alias) !== $alias) { |
274
|
|
|
throw new \InvalidArgumentException('Command aliases must be lowercase'); |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
if(!empty($info['autoAliases'])) { |
280
|
|
|
if(\mb_strpos($this->name, '-') !== false) { |
281
|
|
|
$this->aliases[] = \str_replace('-', '', $this->name); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
foreach($this->aliases as $alias) { |
285
|
|
|
if(\mb_strpos($alias, '-') !== false) { |
286
|
|
|
$this->aliases[] = \str_replace('-', '', $alias); |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
$this->details = $info['details'] ?? $this->details; |
292
|
|
|
$this->format = $info['format'] ?? $this->format; |
293
|
|
|
$this->examples = $info['examples'] ?? $this->examples; |
294
|
|
|
|
295
|
|
|
$this->guildOnly = $info['guildOnly'] ?? $this->guildOnly; |
296
|
|
|
$this->ownerOnly = $info['ownerOnly'] ?? $this->ownerOnly; |
297
|
|
|
|
298
|
|
|
$this->clientPermissions = $info['clientPermissions'] ?? $this->clientPermissions; |
299
|
|
|
$this->userPermissions = $info['userPermissions'] ?? $this->userPermissions; |
300
|
|
|
|
301
|
|
|
$this->nsfw = $info['nsfw'] ?? $this->nsfw; |
302
|
|
|
|
303
|
|
|
if(isset($info['throttling'])) { |
304
|
|
|
if(empty($info['throttling']['usages']) || empty($info['throttling']['duration'])) { |
305
|
|
|
throw new \InvalidArgumentException('Throttling array is missing elements or its elements are empty'); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
if(!\is_int($info['throttling']['usages'])) { |
309
|
|
|
throw new \InvalidArgumentException('Throttling usages must be an integer'); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
if(!\is_int($info['throttling']['duration'])) { |
313
|
|
|
throw new \InvalidArgumentException('Throttling duration must be an integer'); |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
$this->throttling = $info['throttling']; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
$this->defaultHandling = $info['defaultHandling'] ?? $this->defaultHandling; |
320
|
|
|
|
321
|
|
|
$this->args = $info['args'] ?? array(); |
322
|
|
|
if(!empty($this->args)) { |
323
|
|
|
$this->argsCollector = new \CharlotteDunois\Livia\Arguments\ArgumentCollector($this->client, $this->args, $this->argsPromptLimit); |
324
|
|
|
|
325
|
|
|
if(empty($this->format)) { |
326
|
|
|
$this->format = \array_reduce($this->argsCollector->args, function ($prev, $arg) { |
327
|
|
|
$wrapL = ($arg->default !== null ? '[' : '<'); |
328
|
|
|
$wrapR = ($arg->default !== null ? ']' : '>'); |
329
|
|
|
|
330
|
|
|
return $prev.($prev ? ' ' : '').$wrapL.$arg->label.(!empty($arg->infinite) ? '...' : '').$wrapR; |
331
|
|
|
}, null); |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
$this->argsSingleQuotes = (bool) ($info['argsSingleQuotes'] ?? $this->argsSingleQuotes); |
336
|
|
|
$this->argsType = $info['argsType'] ?? $this->argsType; |
337
|
|
|
$this->argsPromptLimit = $info['argsPromptLimit'] ?? $this->argsPromptLimit; |
338
|
|
|
$this->argsCount = $info['argsCount'] ?? $this->argsCount; |
339
|
|
|
|
340
|
|
|
$this->patterns = $info['patterns'] ?? $this->patterns; |
341
|
|
|
$this->guarded = $info['guarded'] ?? $this->guarded; |
342
|
|
|
$this->hidden = $info['hidden'] ?? $this->hidden; |
343
|
|
|
|
344
|
|
|
$this->throttles = new \CharlotteDunois\Collect\Collection(); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @param string $name |
349
|
|
|
* @return bool |
350
|
|
|
* @throws \Exception |
351
|
|
|
* @internal |
352
|
|
|
*/ |
353
|
|
|
function __isset($name) { |
354
|
|
|
try { |
355
|
|
|
return $this->$name !== null; |
356
|
|
|
} catch (\RuntimeException $e) { |
357
|
|
|
if($e->getTrace()[0]['function'] === '__get') { |
358
|
|
|
return false; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
throw $e; |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
/** |
366
|
|
|
* @param string $name |
367
|
|
|
* @return mixed |
368
|
|
|
* @throws \RuntimeException |
369
|
|
|
* @internal |
370
|
|
|
*/ |
371
|
|
|
function __get($name) { |
372
|
|
|
if(\property_exists($this, $name)) { |
373
|
|
|
return $this->$name; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
switch($name) { |
377
|
|
|
case 'group': |
378
|
|
|
return $this->client->registry->resolveGroup($this->groupID); |
379
|
|
|
break; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
throw new \RuntimeException('Unknown property '.\get_class($this).'::$'.$name); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
/** |
386
|
|
|
* Checks if the user has permission to use the command. |
387
|
|
|
* @param \CharlotteDunois\Livia\Commands\Context $context |
388
|
|
|
* @param bool $ownerOverride Whether the bot owner(s) will always have permission. |
389
|
|
|
* @return bool|string Whether the user has permission, or an error message to respond with if they don't. |
390
|
|
|
*/ |
391
|
|
|
function hasPermission(\CharlotteDunois\Livia\Commands\Context $context, bool $ownerOverride = true) { |
392
|
|
|
if($this->ownerOnly === false && empty($this->userPermissions)) { |
393
|
|
|
return true; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
if($ownerOverride && $this->client->isOwner($context->message->author)) { |
397
|
|
|
return true; |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
if($this->ownerOnly && ($ownerOverride || !$this->client->isOwner($context->message->author))) { |
401
|
|
|
return 'The command `'.$this->name.'` can only be used by the bot owner.'; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
// Ensure the user has the proper permissions |
405
|
|
|
if( |
406
|
|
|
$context->message->channel instanceof \CharlotteDunois\Yasmin\Interfaces\GuildChannelInterface && |
407
|
|
|
$context->message->guild !== null && |
408
|
|
|
!empty($this->userPermissions) |
409
|
|
|
) { |
410
|
|
|
$perms = $context->message->channel->permissionsFor($context->message->member); |
411
|
|
|
|
412
|
|
|
$missing = array(); |
413
|
|
|
foreach($this->userPermissions as $perm) { |
414
|
|
|
if($perms->missing($perm)) { |
415
|
|
|
$missing[] = $perm; |
416
|
|
|
} |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
if(\count($missing) > 0) { |
420
|
|
|
$this->client->emit('commandBlocked', $context, 'userPermissions'); |
421
|
|
|
|
422
|
|
|
if(\count($missing) === 1) { |
423
|
|
|
$msg = 'The command `'.$this->name.'` requires you to have the `'.$missing[0].'` permission.'; |
424
|
|
|
} else { |
425
|
|
|
$missing = \implode(', ', \array_map(function ($perm) { |
426
|
|
|
return '`'.\CharlotteDunois\Yasmin\Models\Permissions::resolveToName($perm).'`'; |
427
|
|
|
}, $missing)); |
428
|
|
|
$msg = 'The `'.$this->name.'` command requires you to have the following permissions:'.\PHP_EOL.$missing; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
return $msg; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
return true; |
436
|
|
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Runs the command. The method must return null, an array of Message instances or an instance of Message, a Promise that resolves to an instance of Message, or an array of Message instances. The array can contain Promises which each resolves to an instance of Message. |
440
|
|
|
* @param \CharlotteDunois\Livia\Commands\Context $context The context the command is being run for. |
441
|
|
|
* @param \ArrayObject $args The arguments for the command, or the matches from a pattern. If args is specified on the command, this will be the argument values object. If argsType is single, then only one string will be passed. If multiple, an array of strings will be passed. When fromPattern is true, this is the matches array from the pattern match. |
442
|
|
|
* @param bool $fromPattern Whether or not the command is being run from a pattern match. |
443
|
|
|
* @return \React\Promise\ExtendedPromiseInterface|\React\Promise\ExtendedPromiseInterface[]|\CharlotteDunois\Yasmin\Models\Message|\CharlotteDunois\Yasmin\Models\Message[]|null|void |
444
|
|
|
*/ |
445
|
|
|
abstract function run(\CharlotteDunois\Livia\Commands\Context $context, \ArrayObject $args, bool $fromPattern); |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Reloads the command. |
449
|
|
|
* @return void |
450
|
|
|
* @throws \RuntimeException |
451
|
|
|
*/ |
452
|
|
|
function reload() { |
453
|
|
|
$this->client->registry->reregisterCommand($this->groupID.':'.$this->name, $this); |
454
|
|
|
} |
455
|
|
|
|
456
|
|
|
/** |
457
|
|
|
* Unloads the command. |
458
|
|
|
* @return void |
459
|
|
|
* @throws \RuntimeException |
460
|
|
|
*/ |
461
|
|
|
function unload() { |
462
|
|
|
$this->client->registry->unregisterCommand($this); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Creates/obtains the throttle object for a user, if necessary (owners are excluded). |
467
|
|
|
* @param string $userID |
468
|
|
|
* @return array|null |
469
|
|
|
* @internal |
470
|
|
|
*/ |
471
|
|
|
final function throttle(string $userID) { |
472
|
|
|
if(empty($this->throttling) || $this->client->isOwner($userID)) { |
473
|
|
|
return null; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
if(!$this->throttles->has($userID)) { |
477
|
|
|
$this->throttles->set($userID, array( |
478
|
|
|
'start' => \time(), |
479
|
|
|
'usages' => 0, |
480
|
|
|
'timeout' => $this->client->addTimer($this->throttling['duration'], function () use ($userID) { |
481
|
|
|
$this->throttles->delete($userID); |
482
|
|
|
}) |
483
|
|
|
)); |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
return $this->throttles->get($userID); |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* Increments the usage of the throttle object for a user, if necessary (owners are excluded). |
491
|
|
|
* @param string $userID |
492
|
|
|
* @return void |
493
|
|
|
* @internal |
494
|
|
|
*/ |
495
|
|
|
final function updateThrottle(string $userID) { |
496
|
|
|
if(empty($this->throttling) || $this->client->isOwner($userID)) { |
497
|
|
|
return; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
if(!$this->throttles->has($userID)) { |
501
|
|
|
$this->throttles->set($userID, array( |
502
|
|
|
'start' => \time(), |
503
|
|
|
'usages' => 1, |
504
|
|
|
'timeout' => $this->client->addTimer($this->throttling['duration'], function () use ($userID) { |
505
|
|
|
$this->throttles->delete($userID); |
506
|
|
|
}) |
507
|
|
|
)); |
508
|
|
|
|
509
|
|
|
return; |
510
|
|
|
} |
511
|
|
|
|
512
|
|
|
$throttle = $this->throttles->get($userID); |
513
|
|
|
$throttle['usages']++; |
514
|
|
|
|
515
|
|
|
$this->throttles->set($userID, $throttle); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Enables or disables the command in a guild (or globally). |
520
|
|
|
* @param string|int|\CharlotteDunois\Yasmin\Models\Guild|null $guild The guild instance or the guild ID. |
521
|
|
|
* @param bool $enabled |
522
|
|
|
* @return bool |
523
|
|
|
* @throws \BadMethodCallException |
524
|
|
|
* @throws \InvalidArgumentException |
525
|
|
|
*/ |
526
|
|
|
function setEnabledIn($guild, bool $enabled) { |
527
|
|
|
if($guild !== null) { |
528
|
|
|
$guild = $this->client->guilds->resolve($guild); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
if($this->guarded) { |
532
|
|
|
throw new \BadMethodCallException('The group is guarded'); |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
if($guild !== null) { |
536
|
|
|
$this->guildEnabled[$guild->id] = $enabled; |
537
|
|
|
} else { |
538
|
|
|
$this->globalEnabled = $enabled; |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
$this->client->emit('commandStatusChange', $guild, $this, $enabled); |
542
|
|
|
return ($guild !== null ? $this->guildEnabled[$guild->id] : $this->globalEnabled); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
/** |
546
|
|
|
* Checks if the command is enabled in a guild or globally. |
547
|
|
|
* @param \CharlotteDunois\Yasmin\Models\Guild|string|int|null $guild The guild instance or the guild ID, null for global. |
548
|
|
|
* @return bool |
549
|
|
|
* @throws \InvalidArgumentException |
550
|
|
|
*/ |
551
|
|
|
function isEnabledIn($guild) { |
552
|
|
|
if($guild !== null) { |
553
|
|
|
$guild = $this->client->guilds->resolve($guild); |
554
|
|
|
return ($this->globalEnabled && (!\array_key_exists($guild->id, $this->guildEnabled) || $this->guildEnabled[$guild->id])); |
555
|
|
|
} |
556
|
|
|
|
557
|
|
|
return $this->globalEnabled; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
/** |
561
|
|
|
* Checks if the command is usable for a message. |
562
|
|
|
* @param \CharlotteDunois\Livia\Commands\Context|null $context |
563
|
|
|
* @return bool |
564
|
|
|
*/ |
565
|
|
|
function isUsable(?\CharlotteDunois\Livia\Commands\Context $context = null) { |
566
|
|
|
if($context === null) { |
567
|
|
|
return $this->globalEnabled; |
568
|
|
|
} |
569
|
|
|
|
570
|
|
|
if($this->guildOnly && $context->message->guild === null) { |
571
|
|
|
return false; |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
return ($this->isEnabledIn($context->message->guild) && $this->hasPermission($context) === true); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* Creates a usage string for the command. |
579
|
|
|
* @param string $argString A string of arguments for the command. |
580
|
|
|
* @param string|null $prefix Prefix to use for the prefixed command format. |
581
|
|
|
* @param \CharlotteDunois\Yasmin\Models\User $user User to use for the mention command format. Defaults to client user. |
582
|
|
|
* @return string |
583
|
|
|
*/ |
584
|
|
|
function usage(string $argString, string $prefix = null, \CharlotteDunois\Yasmin\Models\User $user = null) { |
585
|
|
|
if($prefix === null) { |
586
|
|
|
$prefix = $this->client->commandPrefix; |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
if($user === null) { |
590
|
|
|
$user = $this->client->user; |
591
|
|
|
} |
592
|
|
|
|
593
|
|
|
return self::anyUsage($this->name.' '.$argString, $prefix, $user); |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
/** |
597
|
|
|
* Creates a usage string for any command. |
598
|
|
|
* @param string $command A command + arguments string. |
599
|
|
|
* @param string|null $prefix Prefix to use for the prefixed command format. |
600
|
|
|
* @param \CharlotteDunois\Yasmin\Models\User|null $user User to use for the mention command format. |
601
|
|
|
* @return string |
602
|
|
|
*/ |
603
|
|
|
static function anyUsage(string $command, string $prefix = null, \CharlotteDunois\Yasmin\Models\User $user = null) { |
604
|
|
|
$command = \str_replace(' ', "\u{00A0}", $command); |
605
|
|
|
|
606
|
|
|
if(empty($prefix) && $user === null) { |
607
|
|
|
return '`'.$command.'`'; |
608
|
|
|
} |
609
|
|
|
|
610
|
|
|
$prStr = null; |
611
|
|
|
if(!empty($prefix)) { |
612
|
|
|
$prefix = \str_replace(' ', "\u{00A0}", $prefix); |
613
|
|
|
$prStr = '`'.\CharlotteDunois\Yasmin\Utils\MessageHelpers::escapeMarkdown($prefix.$command).'`'; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
$meStr = null; |
617
|
|
|
if($user !== null) { |
618
|
|
|
$meStr = '`@'.\CharlotteDunois\Yasmin\Utils\MessageHelpers::escapeMarkdown(\str_replace(' ', "\u{00A0}", $user->tag)."\u{00A0}".$command).'`'; |
619
|
|
|
} |
620
|
|
|
|
621
|
|
|
return ($prStr ?? '').(!empty($prefix) && $user !== null ? ' or ' : '').($meStr ?? ''); |
622
|
|
|
} |
623
|
|
|
} |
624
|
|
|
|