Skip to content

Commit 177dc65

Browse files
committed
Register Commands and stuff process Updates accordingly
1 parent dae977b commit 177dc65

File tree

6 files changed

+275
-6
lines changed

6 files changed

+275
-6
lines changed

examples/logs/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!.gitignore

examples/run.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
use PhpTelegramBot\Core\Entities\Update;
4+
use PhpTelegramBot\Core\Events\IncomingUpdate;
5+
use PhpTelegramBot\Core\Telegram;
6+
7+
require __DIR__ . '/bootstrap.php';
8+
9+
$bot = new Telegram($_SERVER['BOT_TOKEN']);
10+
11+
function save_dump(Update $update, Telegram $bot)
12+
{
13+
$filename = __DIR__ . '/logs/' . uniqid(date('Y-m-d_H-i-s')) . '_update.json';
14+
file_put_contents($filename, json_encode($update, JSON_PRETTY_PRINT));
15+
}
16+
17+
$bot->registerEventListener(IncomingUpdate::class, function (IncomingUpdate $event, Telegram $bot) {
18+
// Gets called on every incoming `Update` no matter what type
19+
dump('IncomingUpdate');
20+
});
21+
22+
$bot->registerUpdateTypes([
23+
'edited_message' => function (Update $update, Telegram $bot) {
24+
// Gets called on every `Update` of type "edited_message"
25+
dump('edited_message');
26+
},
27+
]);
28+
29+
$bot->registerMessageTypes([
30+
'text' => [
31+
function (Update $update, Telegram $bot) {
32+
// Gets called on every `Update` with a `Message` of type "text"
33+
dump('message.text');
34+
dump($update->getMessage()->getText());
35+
},
36+
'save_dump',
37+
],
38+
39+
'voice' => function (Update $update, Telegram $bot) {
40+
// Gets called on every `Update` with a `Message` of type "voice".
41+
dump('message.voice');
42+
dump($update->getMessage()->getVoice());
43+
},
44+
]);
45+
46+
// Start 30s long polling in an infinite loop
47+
// See https://en.wikipedia.org/wiki/Push_technology#Long_polling
48+
$bot->handleGetUpdates();

src/Entities/Entity.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function __construct(
2424

2525
public function __get(string $name)
2626
{
27-
return $this->fields[$name];
27+
return $this->fields[$name] ?? null;
2828
}
2929

3030
public function __set(string $name, $value): void
@@ -64,10 +64,10 @@ public function __call(string $name, array $arguments)
6464

6565
private function getField(string $name): mixed
6666
{
67-
$data = $this->fields[$name];
67+
$data = $this->fields[$name] ?? null;
6868

6969
$subEntities = static::subEntities();
70-
if (! isset($subEntities[$name])) {
70+
if (is_null($data) || ! isset($subEntities[$name])) {
7171
return $data;
7272
}
7373

@@ -110,6 +110,6 @@ private function setField(string $name, mixed $value): void
110110

111111
public function jsonSerialize(): mixed
112112
{
113-
return array_filter($this->fields, fn ($value, $key) => ! is_null($value) && str_starts_with($key, '__'), ARRAY_FILTER_USE_BOTH);
113+
return array_filter($this->fields, fn ($value) => ! is_null($value));
114114
}
115115
}

src/Events/Event.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace PhpTelegramBot\Core\Events;
4+
5+
abstract class Event
6+
{
7+
}

src/Events/IncomingUpdate.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace PhpTelegramBot\Core\Events;
4+
5+
use PhpTelegramBot\Core\Entities\Update;
6+
7+
class IncomingUpdate extends Event
8+
{
9+
public function __construct(
10+
public Update $update,
11+
) {
12+
}
13+
}

src/Telegram.php

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
use PhpTelegramBot\Core\ApiMethods\UpdatesMessages;
1010
use PhpTelegramBot\Core\Contracts\Factory;
1111
use PhpTelegramBot\Core\Entities\Update;
12+
use PhpTelegramBot\Core\Events\Event;
13+
use PhpTelegramBot\Core\Events\IncomingUpdate;
14+
use PhpTelegramBot\Core\Exceptions\InvalidArgumentException;
1215
use PhpTelegramBot\Core\Exceptions\TelegramException;
1316
use Psr\Http\Message\StreamInterface;
1417

@@ -101,6 +104,7 @@ protected function extractFiles(array $fields, array &$data): array
101104
foreach ($data[$key] as &$item) {
102105
$streams += $this->extractFiles($value, $item);
103106
}
107+
104108
} else {
105109

106110
$streams += $this->extractFiles($value, $data[$key]);
@@ -114,7 +118,7 @@ protected function send(string $methodName, ?array $data = null, string|array|nu
114118
{
115119
$requestUri = $this->apiBaseUri . '/bot' . $this->botToken . '/' . $methodName;
116120

117-
$streams = $this->extractFiles(self::inputFileFields()[$methodName] ?? null, $data);
121+
$streams = $this->extractFiles(self::inputFileFields()[$methodName] ?? [], $data);
118122

119123
$response = match (true) {
120124
empty($data) => $this->client->get($requestUri),
@@ -177,15 +181,210 @@ public function handleGetUpdates(int $pollingInterval = 30, ?array $allowedUpdat
177181
public function handle()
178182
{
179183
$data = file_get_contents('php://input');
184+
185+
if (empty($data)) {
186+
return;
187+
}
188+
180189
$json = json_decode($data, true);
181190

182191
$update = new Update($json);
183192

184193
$this->processUpdate($update);
185194
}
186195

196+
protected function dispatch(Event $event)
197+
{
198+
$listeners = $this->eventListeners[$event::class] ?? [];
199+
200+
foreach ($listeners as $callback) {
201+
$callback($event, $this);
202+
}
203+
}
204+
187205
protected function processUpdate(Update $update)
188206
{
189-
//
207+
// IncomingUpdate Event
208+
$this->dispatch(new IncomingUpdate($update));
209+
210+
// Update Types
211+
foreach ($this->updateTypeCallbacks as $key => $callbacks) {
212+
if (! is_null($update->$key)) {
213+
foreach ($callbacks as $callback) {
214+
$callback($update, $this);
215+
}
216+
}
217+
}
218+
219+
// Message Types
220+
$message = $update->getMessage();
221+
if ($message !== null) {
222+
foreach ($this->messageTypeCallbacks as $key => $callbacks) {
223+
if (! is_null($message->$key)) {
224+
foreach ($callbacks as $callback) {
225+
$callback($update, $this);
226+
}
227+
}
228+
}
229+
}
230+
231+
// Commands
232+
$command = $update->getMessage()?->getText() ?? null;
233+
if ($command !== null && str_starts_with($command, '/')) {
234+
// Cut / from beginning
235+
$command = ltrim($command, '/');
236+
237+
// Cut username
238+
$command = explode(' ', $command, 2)[0];
239+
[$command, $username] = explode('@', $command, 2) + [null, null];
240+
241+
// TODO: Check if the username belongs to this bot...
242+
243+
// Get callbacks
244+
$callbacks = $this->commandCallbacks[$command] ?? [];
245+
246+
foreach ($callbacks as $callback) {
247+
$callback($update, $this);
248+
}
249+
}
250+
}
251+
252+
/**
253+
* @var array<class-string<Event>, array<callable>>
254+
*/
255+
protected array $eventListeners = [];
256+
257+
/**
258+
* @param class-string<Event> $event
259+
* @param callable|array<callable> $callback
260+
* @return $this
261+
*/
262+
public function registerEventListener(string $event, callable|array $callback): static
263+
{
264+
if (is_callable($callback)) {
265+
$callback = [$callback];
266+
}
267+
268+
if (! is_subclass_of($event, Event::class)) {
269+
throw new InvalidArgumentException("$event is not a PhpTelegramBot Event");
270+
}
271+
272+
$this->eventListeners[$event] = array_merge($this->eventListeners[$event] ?? [], $callback);
273+
274+
return $this;
275+
}
276+
277+
/**
278+
* @param array<class-string<Event>, callable|array<callable>> $callbacks
279+
* @return $this
280+
*/
281+
public function registerEventListeners(array $callbacks): static
282+
{
283+
foreach ($callbacks as $event => $callback) {
284+
$this->registerEventListener($event, $callback);
285+
}
286+
287+
return $this;
288+
}
289+
290+
/**
291+
* @var array<string, array<callable>>
292+
*/
293+
protected array $updateTypeCallbacks = [];
294+
295+
/**
296+
* @param callable|array<callable> $callback
297+
* @return $this
298+
*/
299+
public function registerUpdateType(string $updateType, callable|array $callback): static
300+
{
301+
if (is_callable($callback)) {
302+
$callback = [$callback];
303+
}
304+
305+
$this->updateTypeCallbacks[$updateType] = array_merge($this->updateTypeCallbacks[$updateType] ?? [], $callback);
306+
307+
return $this;
308+
}
309+
310+
/**
311+
* @param array<string, callable|array<callable>> $callbacks
312+
* @return $this
313+
*/
314+
public function registerUpdateTypes(array $callbacks): Telegram
315+
{
316+
foreach ($callbacks as $type => $callback) {
317+
$this->registerUpdateType($type, $callback);
318+
}
319+
320+
return $this;
321+
}
322+
323+
/**
324+
* @var array<string, array<callable>>
325+
*/
326+
protected array $messageTypeCallbacks = [];
327+
328+
/**
329+
* @param callable|array<callable> $callback
330+
* @return $this
331+
*/
332+
public function registerMessageType(string $messageType, array|callable $callback): static
333+
{
334+
if (is_callable($callback)) {
335+
$callback = [$callback];
336+
}
337+
338+
$this->messageTypeCallbacks[$messageType] = array_merge($this->messageTypeCallbacks[$messageType] ?? [], $callback);
339+
340+
return $this;
341+
}
342+
343+
/**
344+
* @param array<string, callable|array<callable>> $callbacks
345+
* @return $this
346+
*/
347+
public function registerMessageTypes(array $callbacks): static
348+
{
349+
foreach ($callbacks as $messageType => $callback) {
350+
$this->registerMessageType($messageType, $callback);
351+
}
352+
353+
return $this;
354+
}
355+
356+
/**
357+
* @var array<string, array<callable>>
358+
*/
359+
protected array $commandCallbacks = [];
360+
361+
/**
362+
* @param callable|array<callable> $callback
363+
* @return $this
364+
*/
365+
public function registerCommand(string $command, callable|array $callback): static
366+
{
367+
$command = ltrim($command, '/');
368+
369+
if (is_callable($callback)) {
370+
$callback = [$callback];
371+
}
372+
373+
$this->commandCallbacks[$command] = array_merge($this->commandCallbacks[$command] ?? [], $callback);
374+
375+
return $this;
376+
}
377+
378+
/**
379+
* @param array<string, callable|array<callable>> $callbacks
380+
* @return $this
381+
*/
382+
public function registerCommands(array $callbacks): static
383+
{
384+
foreach ($callbacks as $command => $callback) {
385+
$this->registerCommand($command, $callback);
386+
}
387+
388+
return $this;
190389
}
191390
}

0 commit comments

Comments
 (0)