From 6c33b3d5f1137ceee78f6bccdcfbec70dec9fe9e Mon Sep 17 00:00:00 2001 From: CrazyBoy49z Date: Tue, 23 Apr 2024 17:26:50 +0300 Subject: [PATCH 1/3] Refactor Dialog and DialogManager for improved key generation The previous implementation of Dialog and DialogManager was refactored to improve key generation. Instead of using just chatId, it is now combined with userId to generate a unique key for a dialog. This solution further enhance balance between performance and security for reading and storing dialog states. Some unnecessary methods and properties were also cleaned up in the process. --- .gitignore | 1 + src/Dialog.php | 70 +++++++++++++++++++------------------------ src/DialogManager.php | 27 +++++++++-------- 3 files changed, 47 insertions(+), 51 deletions(-) diff --git a/.gitignore b/.gitignore index 5ff2aa0..5ef1fa4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ composer.lock # phpunit .phpunit.cache .phpunit.result.cache +.idea diff --git a/src/Dialog.php b/src/Dialog.php index e2d1196..a4715c2 100644 --- a/src/Dialog.php +++ b/src/Dialog.php @@ -1,4 +1,5 @@ - Key-value storage to store data between steps. */ protected array $memory = []; @@ -29,9 +32,13 @@ abstract class Dialog /** @var int|null Index of the next step that set manually using jump() method. */ private ?int $afterProceedJumpToIndex = null; - public function __construct(int $chatId, Api $bot = null) + public function __construct(Update $update, Api $bot = null) { - $this->chat_id = $chatId; + $message = $update->getMessage(); + + $this->chatId = $message->chat->id; + $this->userId = $message->from->id; + if ($bot) { $this->bot = $bot; } @@ -59,14 +66,9 @@ final public function start(Update $update): void */ final public function proceed(Update $update): void { - $currentStepIndex = $this->next; - - if ($this->isStart()) { - $this->beforeAllStep($update); - } + $currentStepIndex = $this->next; if ($this->isEnd()) { - $this->afterAllStep($update); return; } @@ -105,22 +107,9 @@ final public function proceed(Update $update): void } } - /** @experimental Run code before all step. */ - protected function beforeAllStep(Update $update): void - { - // override the method to add your logic here - } - - /** @experimental Run code after all step. */ - protected function afterAllStep(Update $update): void - { - // override the method to add your logic here - } - /** @experimental Run code before every step. */ protected function beforeEveryStep(Update $update, int $step): void { - // add experimental Dialog::beforeAllStep // override the method to add your logic here } @@ -159,14 +148,6 @@ final protected function forget(string $key): void unset($this->memory[$key]); } - - /** Check if Dialog started */ - final public function isStart(): bool - { - return $this->next === 0; - } - - /** Check if Dialog ended */ final public function isEnd(): bool { @@ -174,9 +155,14 @@ final public function isEnd(): bool } /** Returns Telegram Chat ID */ - final public function getChatId(): int + final public function getChatId(): ?int { - return $this->chat_id; + return $this->chatId; + } + + final public function getUserId(): ?int + { + return $this->userId; } /** Get a number of seconds to store state of the Dialog after latest activity on it. */ @@ -191,7 +177,7 @@ final public function ttl(): int */ private function proceedConfiguredStep(array $stepConfig, Update $update, int $currentStepIndex): void { - if (!isset($stepConfig['name'])) { + if (! isset($stepConfig['name'])) { throw new InvalidDialogStep('Configurable Dialog step does not contain required “name” value.'); } @@ -200,17 +186,17 @@ private function proceedConfiguredStep(array $stepConfig, Update $update, int $c if (isset($stepConfig['response'])) { $params = [ 'chat_id' => $this->getChatId(), - 'text' => $stepConfig['response'], + 'text' => $stepConfig['response'], ]; - if (!empty($stepConfig['options'])) { + if (! empty($stepConfig['options'])) { $params = array_merge($params, $stepConfig['options']); } $this->bot->sendMessage($params); } - if (!empty($stepConfig['jump'])) { + if (! empty($stepConfig['jump'])) { $this->jump($stepConfig['jump']); } @@ -225,9 +211,15 @@ private function proceedConfiguredStep(array $stepConfig, Update $update, int $c public function __serialize(): array { return [ - 'chat_id' => $this->getChatId(), - 'next' => $this->next, + 'chatId' => $this->getChatId(), + 'userId' => $this->getUserId(), + 'next' => $this->next, 'memory' => $this->memory, ]; } + + public function getDialogKey(): string + { + return implode('-', [$this->getUserId(), $this->getChatId()]); + } } diff --git a/src/DialogManager.php b/src/DialogManager.php index 77901d0..c45b94f 100644 --- a/src/DialogManager.php +++ b/src/DialogManager.php @@ -6,6 +6,7 @@ use Telegram\Bot\Api; use Telegram\Bot\Objects\Message; use Telegram\Bot\Objects\Update; +use Illuminate\Support\Collection; final class DialogManager { @@ -42,11 +43,9 @@ private function getDialogInstance(Update $update): ?Dialog return null; } - $message = $update->getMessage(); - assert($message instanceof \Telegram\Bot\Objects\Message); - $chatId = $message->chat->id; + $key = $this->generateDialogKey($update); - $dialog = $this->readDialogState($chatId); + $dialog = $this->readDialogState($key); $dialog->setBot($this->bot); return $dialog; @@ -67,8 +66,7 @@ public function proceed(Update $update): void $dialog->proceed($update); if ($dialog->isEnd()) { - $this->store->delete($dialog->getChatId()); - $dialog->proceed($update); + $this->store->delete($dialog->getDialogKey()); } else { $this->storeDialogState($dialog); } @@ -77,20 +75,25 @@ public function proceed(Update $update): void /** Whether Dialog exist for a given Update. */ public function exists(Update $update): bool { - $message = $update->getMessage(); - $chatId = $message instanceof Message ? $message->chat->id : null; - return $chatId && $this->store->has($chatId); + $key = $this->generateDialogKey($update); + + return $key && $this->store->has($key); } /** Store all Dialog. */ private function storeDialogState(Dialog $dialog): void { - $this->store->set($dialog->getChatId(), $dialog, $dialog->ttl()); + $this->store->set($dialog->getDialogKey(), $dialog, $dialog->ttl()); } /** Restore Dialog. */ - private function readDialogState(int $chatId): Dialog + private function readDialogState($key): Dialog + { + return $this->store->get($key); + } + + private function generateDialogKey(Update $update): string { - return $this->store->get($chatId); + return implode('-', [$update->getMessage()->from->id, $update->getChat()->id]); } } From acc26f477341afa043f4718c862ccc8b84292b38 Mon Sep 17 00:00:00 2001 From: CrazyBoy49z Date: Tue, 23 Apr 2024 17:32:10 +0300 Subject: [PATCH 2/3] Add beforeAllStep and afterAllStep methods to Dialog Two new methods have been added to the Dialog class: "beforeAllStep" and "afterAllStep". These methods, which can be overridden for custom functionality, will run at the start and end of a Dialog sequence respectively. Additionally, a check has been implemented to determine when the Dialog has started. --- src/Dialog.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Dialog.php b/src/Dialog.php index a4715c2..066eead 100644 --- a/src/Dialog.php +++ b/src/Dialog.php @@ -68,7 +68,14 @@ final public function proceed(Update $update): void { $currentStepIndex = $this->next; + $currentStepIndex = $this->next; + + if ($this->isStart()) { + $this->beforeAllStep($update); + } + if ($this->isEnd()) { + $this->afterAllStep($update); return; } @@ -107,9 +114,22 @@ final public function proceed(Update $update): void } } + /** @experimental Run code before all step. */ + protected function beforeAllStep(Update $update): void + { + // override the method to add your logic here + } + + /** @experimental Run code after all step. */ + protected function afterAllStep(Update $update): void + { + // override the method to add your logic here + } + /** @experimental Run code before every step. */ protected function beforeEveryStep(Update $update, int $step): void { + // add experimental Dialog::beforeAllStep // override the method to add your logic here } @@ -148,6 +168,12 @@ final protected function forget(string $key): void unset($this->memory[$key]); } + /** Check if Dialog started */ + final public function isStart(): bool + { + return $this->next === 0; + } + /** Check if Dialog ended */ final public function isEnd(): bool { From 130b8eb4f0161c17f6e810c640adc5e0bc0685d8 Mon Sep 17 00:00:00 2001 From: CrazyBoy49z Date: Tue, 23 Apr 2024 17:33:48 +0300 Subject: [PATCH 3/3] Remove duplicate line in Dialog.php The same line setting variable $currentStepIndex to $this->next was unnecessarily repeated in the script. This redundancy was removed to enhance code efficiency and improve readability. --- src/Dialog.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Dialog.php b/src/Dialog.php index 066eead..ad176d7 100644 --- a/src/Dialog.php +++ b/src/Dialog.php @@ -68,8 +68,6 @@ final public function proceed(Update $update): void { $currentStepIndex = $this->next; - $currentStepIndex = $this->next; - if ($this->isStart()) { $this->beforeAllStep($update); }