From 662390b8bde1da9d7231645a905b71505571c57d Mon Sep 17 00:00:00 2001 From: Adrien Reynard Date: Fri, 9 Jan 2026 09:04:10 +0100 Subject: [PATCH 1/5] verify UUID prior to registration --- src/Commands/RegisterCommand.php | 8 ++++++++ src/bot.php | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/Commands/RegisterCommand.php b/src/Commands/RegisterCommand.php index 075451b..4352df3 100644 --- a/src/Commands/RegisterCommand.php +++ b/src/Commands/RegisterCommand.php @@ -47,6 +47,14 @@ public function execute() : ServerResponse 'text' => "*error:* server '$uid' not found", 'parse_mode' => 'Markdown' ]); + } + else if ($id_server == -1) + { + return Request::sendMessage([ + 'chat_id' => $chat_id, + 'text' => "*error:* '$uid' is not in a valid format.", + 'parse_mode' => 'Markdown' + ]); } echo "here now\n"; diff --git a/src/bot.php b/src/bot.php index ba02d04..cd39d5a 100644 --- a/src/bot.php +++ b/src/bot.php @@ -71,6 +71,12 @@ public function pdo() return $this->pdo; } + private function is_valid_uuid($uuid) + { + $pattern = '/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i'; + return preg_match($pattern, $uuid) === 1; + } + private function init_db($mysql_credentials) { $dsn = 'mysql:host=' . $mysql_credentials['host'] . ';dbname=' . $mysql_credentials['database']; @@ -194,6 +200,11 @@ public function udpate_db() public function id_server($uid) { + if (!$this->is_valid_uuid($uid)) + { + echo "Invalid UUID format rejected: " . $uid; + return -1; + } $pdo = $this->pdo(); $sql = "SELECT * FROM `ob_online` where uid=:uid order by id DESC limit 1"; $statement = $pdo->prepare($sql); From 5232dfed1dcb02b47076464e7d939014e884d680 Mon Sep 17 00:00:00 2001 From: Adrien Reynard Date: Fri, 9 Jan 2026 09:23:37 +0100 Subject: [PATCH 2/5] refactor: don't update time when period too short --- src/bot.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bot.php b/src/bot.php index cd39d5a..612e44b 100644 --- a/src/bot.php +++ b/src/bot.php @@ -38,6 +38,12 @@ class PimOnlineBot */ private $base_url; + /** + * Minimum interval in seconds before updating the database for an online host. + * @var int + */ + const MIN_UPDATE_INTERVAL = 30; + /** * Init a PimOnlineBot */ @@ -133,6 +139,11 @@ public function online($uid) $id = $row['id']; $past = $row['now']; $alarm = $row['alarm']; + $currentTime = time(); + + if (!$alarm && ($currentTime - $past) < self::MIN_UPDATE_INTERVAL) { + return; + } $sql = "UPDATE `ob_online` SET `now` = :now, `past` = :past, `alarm` = :alarm WHERE `id` = :id"; $statement = $pdo->prepare($sql); From dd7c7bf085e5d8bbc11d8fd6e872f2c0755e7518 Mon Sep 17 00:00:00 2001 From: Adrien Reynard Date: Wed, 14 Jan 2026 07:49:18 +0100 Subject: [PATCH 3/5] Verify UUID on the online function --- src/Commands/RegisterCommand.php | 8 -------- src/bot.php | 10 +++++----- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/Commands/RegisterCommand.php b/src/Commands/RegisterCommand.php index 4352df3..075451b 100644 --- a/src/Commands/RegisterCommand.php +++ b/src/Commands/RegisterCommand.php @@ -47,14 +47,6 @@ public function execute() : ServerResponse 'text' => "*error:* server '$uid' not found", 'parse_mode' => 'Markdown' ]); - } - else if ($id_server == -1) - { - return Request::sendMessage([ - 'chat_id' => $chat_id, - 'text' => "*error:* '$uid' is not in a valid format.", - 'parse_mode' => 'Markdown' - ]); } echo "here now\n"; diff --git a/src/bot.php b/src/bot.php index 612e44b..fe89995 100644 --- a/src/bot.php +++ b/src/bot.php @@ -111,6 +111,11 @@ private function init_telegram($pdo, $bot_api_key, $bot_username) public function online($uid) { + if (!$this->is_valid_uuid($uid)) + { + echo "Invalid UUID format rejected: " . $uid; + return; + } $pdo = $this->pdo(); $sql = "SELECT * FROM `ob_online` where uid=:uid order by id DESC limit 1"; $statement = $pdo->prepare($sql); @@ -211,11 +216,6 @@ public function udpate_db() public function id_server($uid) { - if (!$this->is_valid_uuid($uid)) - { - echo "Invalid UUID format rejected: " . $uid; - return -1; - } $pdo = $this->pdo(); $sql = "SELECT * FROM `ob_online` where uid=:uid order by id DESC limit 1"; $statement = $pdo->prepare($sql); From e719a1ded4555bc299c2c6b13a4193da8dc6a804 Mon Sep 17 00:00:00 2001 From: Adrien Reynard Date: Fri, 23 Jan 2026 07:38:54 +0100 Subject: [PATCH 4/5] Add SQL transactions --- src/Commands/UnregisterCommand.php | 2 +- src/bot.php | 268 ++++++++++++++++------------- 2 files changed, 154 insertions(+), 116 deletions(-) diff --git a/src/Commands/UnregisterCommand.php b/src/Commands/UnregisterCommand.php index 763b72e..4d43101 100644 --- a/src/Commands/UnregisterCommand.php +++ b/src/Commands/UnregisterCommand.php @@ -50,7 +50,7 @@ public function execute() : ServerResponse { return Request::sendMessage([ 'chat_id' => $chat_id, - 'text' => "*error:* register host '$uid' failed", + 'text' => "*error:* unregister host '$uid' failed", 'parse_mode' => 'Markdown' ]); } diff --git a/src/bot.php b/src/bot.php index fe89995..722b7fd 100644 --- a/src/bot.php +++ b/src/bot.php @@ -116,108 +116,126 @@ public function online($uid) echo "Invalid UUID format rejected: " . $uid; return; } - $pdo = $this->pdo(); - $sql = "SELECT * FROM `ob_online` where uid=:uid order by id DESC limit 1"; - $statement = $pdo->prepare($sql); - $statement->bindValue(':uid', $uid); - $statement->execute(); - $row = $statement->fetch(); - - $telegram = $this->telegram(); - $inserted = false; - - if ($statement->rowCount() == 0) - { - $past = 0; - $sql = "INSERT INTO `ob_online` (`uid`, `now`, `past`, `alarm`) VALUES (:uid, :now, :past, :alarm)"; + + try { + $this->pdo->beginTransaction(); + + $pdo = $this->pdo(); + $sql = "SELECT * FROM `ob_online` WHERE uid=:uid ORDER BY id DESC LIMIT 1 FOR UPDATE"; $statement = $pdo->prepare($sql); $statement->bindValue(':uid', $uid); - $statement->bindValue(':now', time()); - $statement->bindValue(':past', $past); - $statement->bindValue(':alarm', 0); + $statement->execute(); + $row = $statement->fetch(); - // Execute the statement and insert our values. - $inserted = $statement->execute(); - } - else - { - $id = $row['id']; - $past = $row['now']; - $alarm = $row['alarm']; - $currentTime = time(); + $telegram = $this->telegram(); + $inserted = false; + + if ($statement->rowCount() == 0) + { + $past = 0; + $sql = "INSERT INTO `ob_online` (`uid`, `now`, `past`, `alarm`) VALUES (:uid, :now, :past, :alarm)"; + $statement = $pdo->prepare($sql); + $statement->bindValue(':uid', $uid); + $statement->bindValue(':now', time()); + $statement->bindValue(':past', $past); + $statement->bindValue(':alarm', 0); + + // Execute the statement and insert our values. + $inserted = $statement->execute(); + } + else + { + $id = $row['id']; + $past = $row['now']; + $alarm = $row['alarm']; + $currentTime = time(); - if (!$alarm && ($currentTime - $past) < self::MIN_UPDATE_INTERVAL) { - return; + if (!$alarm && ($currentTime - $past) < self::MIN_UPDATE_INTERVAL) { + $this->pdo->rollBack(); + return; + } + + $sql = "UPDATE `ob_online` SET `now` = :now, `past` = :past, `alarm` = :alarm WHERE `id` = :id"; + $statement = $pdo->prepare($sql); + $statement->bindValue(':id', $id); + $statement->bindValue(':now', time()); + $statement->bindValue(':past', $past); + $statement->bindValue(':alarm', 0); + $inserted = $statement->execute(); + + if ($alarm) + { + $users = $this->users($id); + while ($r = $users->fetch()) + { + $chat_id = $r['id_user']; + $name = $r['name']; + + Longman\TelegramBot\Request::sendMessage([ + 'chat_id' => $chat_id, + 'text' => "*info:* Host _{$name}_ is *online*", + 'parse_mode' => 'Markdown' + ]); + } + } } - - $sql = "UPDATE `ob_online` SET `now` = :now, `past` = :past, `alarm` = :alarm WHERE `id` = :id"; + + $this->pdo->commit(); + + if ($this->doWithoutCron) + { + $this->udpate_db(); + } + } catch (Exception $e) { + $this->pdo->rollBack(); + } + } + + public function udpate_db() + { + try { + $this->pdo->beginTransaction(); + + $pdo = $this->pdo(); + $time = time(); + $sql = "SELECT * FROM `ob_online` WHERE (alarm = 0) AND ((now - past) > 120) AND (((now - past) * 2.4) < (:time - now)) FOR UPDATE"; $statement = $pdo->prepare($sql); - $statement->bindValue(':id', $id); - $statement->bindValue(':now', time()); - $statement->bindValue(':past', $past); - $statement->bindValue(':alarm', 0); - $inserted = $statement->execute(); - - if ($alarm) + $statement->bindValue(':time', time()); + $statement->execute(); + while ($row = $statement->fetch()) { + $id = $row['id']; + $uid = $row['uid']; + $sql = "UPDATE `ob_online` SET `alarm` = :alarm WHERE `id` = :id"; + $s = $pdo->prepare($sql); + $s->bindValue(':id', $id); + $s->bindValue(':alarm', time()); + $s->execute(); + $users = $this->users($id); while ($r = $users->fetch()) { $chat_id = $r['id_user']; $name = $r['name']; - + Longman\TelegramBot\Request::sendMessage([ 'chat_id' => $chat_id, - 'text' => "*info:* Host _{$name}_ is *online*", + 'text' => "*error:* Host _{$name}_ is *offline*", 'parse_mode' => 'Markdown' ]); } } - } - if ($this->doWithoutCron) - { - $this->udpate_db(); - } - } - - public function udpate_db() - { - $pdo = $this->pdo(); - $time = time(); - $sql = "SELECT * FROM `ob_online` WHERE (alarm = 0) AND ((now - past) > 120) AND (((now - past) * 2.4) < (:time - now))"; - $statement = $pdo->prepare($sql); - $statement->bindValue(':time', time()); - $statement->execute(); - while ($row = $statement->fetch()) - { - $id = $row['id']; - $uid = $row['uid']; - $sql = "UPDATE `ob_online` SET `alarm` = :alarm WHERE `id` = :id"; - $s = $pdo->prepare($sql); - $s->bindValue(':id', $id); - $s->bindValue(':alarm', time()); - $s->execute(); - - $users = $this->users($id); - while ($r = $users->fetch()) - { - $chat_id = $r['id_user']; - $name = $r['name']; - - Longman\TelegramBot\Request::sendMessage([ - 'chat_id' => $chat_id, - 'text' => "*error:* Host _{$name}_ is *offline*", - 'parse_mode' => 'Markdown' - ]); - } + $this->pdo->commit(); + } catch (Exception $e) { + $this->pdo->rollBack(); } } public function id_server($uid) { $pdo = $this->pdo(); - $sql = "SELECT * FROM `ob_online` where uid=:uid order by id DESC limit 1"; + $sql = "SELECT * FROM `ob_online` WHERE uid=:uid ORDER BY id DESC LIMIT 1"; $statement = $pdo->prepare($sql); $statement->bindValue(':uid', $uid); $statement->execute(); @@ -234,7 +252,7 @@ public function id_server($uid) public function server_count_for_user($id_user) { $pdo = $this->pdo(); - $sql = "SELECT count(*) as nr FROM `ob_servers_users` WHERE id_user=:id_user order by id"; + $sql = "SELECT count(*) as nr FROM `ob_servers_users` WHERE id_user=:id_user ORDER BY id"; $statement = $pdo->prepare($sql); $statement->bindValue(':id_user', $id_user); $statement->execute(); @@ -244,7 +262,7 @@ public function server_count_for_user($id_user) private function users($id_server) { $pdo = $this->pdo(); - $sql = "SELECT * FROM `ob_servers_users` WHERE id_server=:id_server order by id"; + $sql = "SELECT * FROM `ob_servers_users` WHERE id_server=:id_server ORDER BY id"; $statement = $pdo->prepare($sql); $statement->bindValue(':id_server', $id_server); $statement->execute(); @@ -253,53 +271,73 @@ private function users($id_server) public function register($id_user, $id_server, $name) { - $pdo = $this->pdo(); - $sql = "SELECT * FROM `ob_servers_users` WHERE id_user=:id_user AND id_server=:id_server order by id DESC limit 1"; - $statement = $pdo->prepare($sql); - $statement->bindValue(':id_user', $id_user); - $statement->bindValue(':id_server', $id_server); - $statement->execute(); - $row = $statement->fetch(); - - if ($statement->rowCount() != 0) - { - return true; - } - else - { - $sql = "INSERT INTO `ob_servers_users` (`id_user`, `id_server`, `name`) VALUES (:id_user, :id_server, :name)"; + try { + $this->pdo->beginTransaction(); + + $pdo = $this->pdo(); + $sql = "SELECT * FROM `ob_servers_users` WHERE id_user=:id_user AND id_server=:id_server ORDER BY id DESC LIMIT 1 FOR UPDATE"; $statement = $pdo->prepare($sql); $statement->bindValue(':id_user', $id_user); $statement->bindValue(':id_server', $id_server); - $statement->bindValue(':name', $name); + $statement->execute(); + $row = $statement->fetch(); - // Execute the statement and insert our values. - return $statement->execute(); + if ($statement->rowCount() != 0) + { + $this->pdo->rollBack(); + return true; + } + else + { + $sql = "INSERT INTO `ob_servers_users` (`id_user`, `id_server`, `name`) VALUES (:id_user, :id_server, :name)"; + $statement = $pdo->prepare($sql); + $statement->bindValue(':id_user', $id_user); + $statement->bindValue(':id_server', $id_server); + $statement->bindValue(':name', $name); + + // Execute the statement and insert our values. + $res = $statement->execute(); + $this->pdo->commit(); + return $res; + } + } catch (Exception $e) { + $this->pdo->rollBack(); + return false; } } public function unregister($id_user, $id_server) { - $pdo = $this->pdo(); - $sql = "SELECT * FROM `ob_servers_users` WHERE id_user=:id_user AND id_server=:id_server order by id DESC limit 1"; - $statement = $pdo->prepare($sql); - $statement->bindValue(':id_user', $id_user); - $statement->bindValue(':id_server', $id_server); - $statement->execute(); - $row = $statement->fetch(); - - if ($statement->rowCount() != 1) - { - return false; - } - else - { - $id = $row['id']; - $sql = "DELETE FROM `ob_servers_users` WHERE id=:id"; + try { + $this->pdo->beginTransaction(); + + $pdo = $this->pdo(); + $sql = "SELECT * FROM `ob_servers_users` WHERE id_user=:id_user AND id_server=:id_server ORDER BY id DESC LIMIT 1 FOR UPDATE"; $statement = $pdo->prepare($sql); - $statement->bindValue(':id', $id); + $statement->bindValue(':id_user', $id_user); + $statement->bindValue(':id_server', $id_server); + $statement->execute(); + $row = $statement->fetch(); + + if ($statement->rowCount() != 1) + { + $this->pdo->rollBack(); + return false; + } + else + { + $id = $row['id']; + $sql = "DELETE FROM `ob_servers_users` WHERE id=:id"; + $statement = $pdo->prepare($sql); + $statement->bindValue(':id', $id); - return $statement->execute(); + $res = $statement->execute(); + $this->pdo->commit(); + return $res; + } + } catch (Exception $e) { + $this->pdo->rollBack(); + return false; } } From 348a289d559fb7cb23319a75f88ea583c67f0c7c Mon Sep 17 00:00:00 2001 From: Adrien Reynard Date: Fri, 23 Jan 2026 08:45:39 +0100 Subject: [PATCH 5/5] Add broadcast message --- config/config.php.txt | 3 ++ src/Commands/BroadcastCommand.php | 57 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/Commands/BroadcastCommand.php diff --git a/config/config.php.txt b/config/config.php.txt index 1818c8c..5f4d744 100644 --- a/config/config.php.txt +++ b/config/config.php.txt @@ -4,6 +4,9 @@ $bot_api_key = 'your_key'; $bot_username = 'OnlineBot'; $doWithoutCron = true; $base_url = ''; +$admins_id = [ + 0123456789, +]; $mysql_credentials = [ 'host' => 'localhost', diff --git a/src/Commands/BroadcastCommand.php b/src/Commands/BroadcastCommand.php new file mode 100644 index 0000000..c454c70 --- /dev/null +++ b/src/Commands/BroadcastCommand.php @@ -0,0 +1,57 @@ +'; + protected $version = '1.0.0'; + + public function execute(): ServerResponse + { + require __DIR__ . '/../../config/config.php'; + + $message = $this->getMessage(); + $chat_id = $message->getChat()->getId(); + $user_id = $message->getFrom()->getId(); + $text = trim($message->getText(true)); + + if (!isset($admins_id) || !in_array($user_id, $admins_id)) { + return Request::sendMessage([ + 'chat_id' => $chat_id, + 'text' => '*Error:* You do not have permission to execute this command.', + 'parse_mode' => 'Markdown' + ]); + } + + if ($text === '') { + return Request::sendMessage([ + 'chat_id' => $chat_id, + 'text' => '*Error*: Message body is empty. Usage: `/broadcast `', + 'parse_mode' => 'Markdown' + ]); + } + + $results = DB::getPdo()->query('SELECT id FROM user')->fetchAll(\PDO::FETCH_COLUMN); + + foreach ($results as $target_id) { + Request::sendMessage([ + 'chat_id' => $target_id, + 'text' => "*Important message*\n\n" . $text, + 'parse_mode' => 'Markdown' + ]); + } + + return Request::sendMessage([ + 'chat_id' => $chat_id, + 'text' => 'Broadcast sent to ' . count($results) . ' users.' + ]); + } +} \ No newline at end of file