|
9 | 9 | use BushlanovDev\MaxMessengerBot\Enums\UploadType;
|
10 | 10 | use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException;
|
11 | 11 | use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException;
|
| 12 | +use BushlanovDev\MaxMessengerBot\Exceptions\SecurityException; |
12 | 13 | use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException;
|
13 | 14 | use BushlanovDev\MaxMessengerBot\Models\AbstractModel;
|
14 | 15 | use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\AbstractAttachmentRequest;
|
|
20 | 21 | use BushlanovDev\MaxMessengerBot\Models\Result;
|
21 | 22 | use BushlanovDev\MaxMessengerBot\Models\Subscription;
|
22 | 23 | use BushlanovDev\MaxMessengerBot\Models\UpdateList;
|
| 24 | +use BushlanovDev\MaxMessengerBot\Models\Updates\AbstractUpdate; |
23 | 25 | use BushlanovDev\MaxMessengerBot\Models\UploadEndpoint;
|
24 | 26 | use InvalidArgumentException;
|
25 | 27 | use LogicException;
|
| 28 | +use Psr\Http\Message\ServerRequestInterface; |
26 | 29 | use ReflectionException;
|
27 | 30 | use RuntimeException;
|
28 | 31 |
|
@@ -105,6 +108,146 @@ public function createWebhookHandler(?string $secret = null): WebhookHandler
|
105 | 108 | return new WebhookHandler($this, $this->modelFactory, $secret);
|
106 | 109 | }
|
107 | 110 |
|
| 111 | + /** |
| 112 | + * Parses an incoming webhook request and returns a single Update object. |
| 113 | + * This is an alternative to the event-driven WebhookHandler::handle() method, |
| 114 | + * allowing for manual processing of updates. |
| 115 | + * |
| 116 | + * @param string|null $secret The secret key to verify the request signature. |
| 117 | + * @param ServerRequestInterface|null $request The PSR-7 request object. If null, it's created from globals. |
| 118 | + * |
| 119 | + * @return AbstractUpdate The parsed update object (e.g., MessageCreatedUpdate). |
| 120 | + * @throws \ReflectionException |
| 121 | + * @throws SecurityException |
| 122 | + * @throws SerializationException |
| 123 | + * @throws \LogicException |
| 124 | + */ |
| 125 | + public function getWebhookUpdate(?string $secret = null, ?ServerRequestInterface $request = null): AbstractUpdate |
| 126 | + { |
| 127 | + return $this->createWebhookHandler($secret)->getUpdate($request); |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * A simple way to process a single incoming webhook request using callbacks. |
| 132 | + * This method creates a WebhookHandler, registers the provided callbacks, and processes the request. |
| 133 | + * |
| 134 | + * @param array<string, callable> $handlers An associative array where keys are UpdateType string values |
| 135 | + * (e.g., UpdateType::MessageCreated->value) and values are handlers. |
| 136 | + * @param string|null $secret The secret key for request verification. |
| 137 | + * @param ServerRequestInterface|null $request The PSR-7 request object. |
| 138 | + * |
| 139 | + * @throws SecurityException |
| 140 | + * @throws SerializationException |
| 141 | + * @throws ReflectionException |
| 142 | + * @throws LogicException |
| 143 | + */ |
| 144 | + public function handleWebhooks( |
| 145 | + array $handlers, |
| 146 | + ?string $secret = null, |
| 147 | + ?ServerRequestInterface $request = null, |
| 148 | + ): void { |
| 149 | + $webhookHandler = $this->createWebhookHandler($secret); |
| 150 | + |
| 151 | + foreach ($handlers as $updateType => $callback) { |
| 152 | + $updateType = UpdateType::tryFrom($updateType); |
| 153 | + // @phpstan-ignore-next-line |
| 154 | + if ($updateType && is_callable($callback)) { |
| 155 | + $webhookHandler->addHandler($updateType, $callback); |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + $webhookHandler->handle($request); |
| 160 | + } |
| 161 | + |
| 162 | + /** |
| 163 | + * You can use this method for getting updates in case your bot is not subscribed to WebHook. |
| 164 | + * The method is based on long polling. |
| 165 | + * |
| 166 | + * @param int|null $limit Maximum number of updates to be retrieved (1-1000). |
| 167 | + * @param int|null $timeout Timeout in seconds for long polling (0-90). |
| 168 | + * @param int|null $marker Pass `null` to get updates you didn't get yet. |
| 169 | + * @param UpdateType[]|null $types Comma separated list of update types your bot want to receive. |
| 170 | + * |
| 171 | + * @return UpdateList |
| 172 | + * @throws ClientApiException |
| 173 | + * @throws NetworkException |
| 174 | + * @throws ReflectionException |
| 175 | + * @throws SerializationException |
| 176 | + */ |
| 177 | + public function getUpdates( |
| 178 | + ?int $limit = null, |
| 179 | + ?int $timeout = null, |
| 180 | + ?int $marker = null, |
| 181 | + ?array $types = null, |
| 182 | + ): UpdateList { |
| 183 | + $query = [ |
| 184 | + 'limit' => $limit, |
| 185 | + 'timeout' => $timeout, |
| 186 | + 'marker' => $marker, |
| 187 | + 'types' => $types !== null ? implode(',', array_map(fn($type) => $type->value, $types)) : null, |
| 188 | + ]; |
| 189 | + |
| 190 | + return $this->modelFactory->createUpdateList( |
| 191 | + $this->client->request( |
| 192 | + self::METHOD_GET, |
| 193 | + self::ACTION_UPDATES, |
| 194 | + array_filter($query, fn($value) => $value !== null), |
| 195 | + ) |
| 196 | + ); |
| 197 | + } |
| 198 | + |
| 199 | + /** |
| 200 | + * Starts a long-polling loop to process updates using callbacks. |
| 201 | + * This method will run indefinitely until the script is terminated. |
| 202 | + * |
| 203 | + * @param array<string, callable> $handlers An associative array where keys are UpdateType enums |
| 204 | + * and values are the corresponding handler functions. |
| 205 | + * @param int|null $timeout Timeout in seconds for long polling (0-90). Defaults to 90. |
| 206 | + * @param int|null $marker Pass `null` to get updates you didn't get yet. |
| 207 | + */ |
| 208 | + public function handleUpdates(array $handlers, ?int $timeout = null, ?int $marker = null): void |
| 209 | + { |
| 210 | + // @phpstan-ignore-next-line |
| 211 | + while (true) { |
| 212 | + try { |
| 213 | + $this->processUpdatesBatch($handlers, $timeout, $marker); |
| 214 | + } catch (NetworkException $e) { |
| 215 | + error_log("Network error: " . $e->getMessage()); |
| 216 | + sleep(5); |
| 217 | + } catch (\Exception $e) { |
| 218 | + error_log("An error occurred: " . $e->getMessage()); |
| 219 | + sleep(1); |
| 220 | + } |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + /** |
| 225 | + * Processes a single batch of updates. This is the core logic used by handleUpdates(). |
| 226 | + * Useful for custom loop implementations or for testing. |
| 227 | + * |
| 228 | + * @param array<string, callable> $handlers An associative array of update handlers. |
| 229 | + * @param int|null $timeout Timeout for the getUpdates call. |
| 230 | + * @param int|null $marker The marker for which updates to fetch. |
| 231 | + * |
| 232 | + * @throws ClientApiException |
| 233 | + * @throws NetworkException |
| 234 | + * @throws ReflectionException |
| 235 | + * @throws SerializationException |
| 236 | + */ |
| 237 | + public function processUpdatesBatch(array $handlers, ?int $timeout, ?int &$marker = null): void |
| 238 | + { |
| 239 | + $updateList = $this->getUpdates(timeout: $timeout, marker: $marker); |
| 240 | + |
| 241 | + foreach ($updateList->updates as $update) { |
| 242 | + $handler = $handlers[$update->updateType->value] ?? null; |
| 243 | + if ($handler) { |
| 244 | + $handler($update, $this); |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + $marker = $updateList->marker; |
| 249 | + } |
| 250 | + |
108 | 251 | /**
|
109 | 252 | * Information about the current bot, identified by an access token.
|
110 | 253 | *
|
@@ -330,41 +473,4 @@ public function getChat(int $chatId): Chat
|
330 | 473 | $this->client->request(self::METHOD_GET, self::ACTION_CHATS . '/' . $chatId)
|
331 | 474 | );
|
332 | 475 | }
|
333 |
| - |
334 |
| - /** |
335 |
| - * You can use this method for getting updates in case your bot is not subscribed to WebHook. |
336 |
| - * The method is based on long polling. |
337 |
| - * |
338 |
| - * @param int|null $limit Maximum number of updates to be retrieved (1-1000). |
339 |
| - * @param int|null $timeout Timeout in seconds for long polling (0-90). |
340 |
| - * @param int|null $marker Pass `null` to get updates you didn't get yet. |
341 |
| - * @param UpdateType[]|null $types Comma separated list of update types your bot want to receive. |
342 |
| - * |
343 |
| - * @return UpdateList |
344 |
| - * @throws ClientApiException |
345 |
| - * @throws NetworkException |
346 |
| - * @throws ReflectionException |
347 |
| - * @throws SerializationException |
348 |
| - */ |
349 |
| - public function getUpdates( |
350 |
| - ?int $limit = null, |
351 |
| - ?int $timeout = null, |
352 |
| - ?int $marker = null, |
353 |
| - ?array $types = null, |
354 |
| - ): UpdateList { |
355 |
| - $query = [ |
356 |
| - 'limit' => $limit, |
357 |
| - 'timeout' => $timeout, |
358 |
| - 'marker' => $marker, |
359 |
| - 'types' => $types !== null ? implode(',', array_map(fn($type) => $type->value, $types)) : null, |
360 |
| - ]; |
361 |
| - |
362 |
| - return $this->modelFactory->createUpdateList( |
363 |
| - $this->client->request( |
364 |
| - self::METHOD_GET, |
365 |
| - self::ACTION_UPDATES, |
366 |
| - array_filter($query, fn($value) => $value !== null), |
367 |
| - ) |
368 |
| - ); |
369 |
| - } |
370 | 476 | }
|
0 commit comments