From 969168e53a389548af56a2be80b4e4434b41caf4 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:18:49 +0300 Subject: [PATCH 1/9] new table openvk-eventdb.user-events --- VKAPI/Handlers/Wall.php | 5 +- Web/Util/EventRateLimiter.php | 59 +++++++++++++++++++++++ install/sqls/eventdb/00001-events-log.sql | 8 +++ openvk-example.yml | 9 ++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Web/Util/EventRateLimiter.php create mode 100644 install/sqls/eventdb/00001-events-log.sql diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index 9113eb6e..9e4d9180 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -21,6 +21,7 @@ use openvk\Web\Models\Entities\Note; use openvk\Web\Models\Repositories\Notes as NotesRepo; use openvk\Web\Models\Repositories\Polls as PollsRepo; use openvk\Web\Models\Repositories\Audios as AudiosRepo; +use openvk\Web\Util\EventRateLimiter; final class Wall extends VKAPIRequestHandler { @@ -723,9 +724,11 @@ final class Wall extends VKAPIRequestHandler } if ($owner_id > 0 && $owner_id !== $this->getUser()->getId()) { - (new WallPostNotification($wallOwner, $post, $this->user->identity))->emit(); + (new WallPostNotification($wallOwner, $post, $this->getUser()->getId()))->emit(); } + EventRateLimiter::i()->writeEvent("wall.post", $this->getUser(), $wallOwner); + return (object) ["post_id" => $post->getVirtualId()]; } diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php new file mode 100644 index 00000000..80fea28a --- /dev/null +++ b/Web/Util/EventRateLimiter.php @@ -0,0 +1,59 @@ +data = $data; + } + + public function write($edb): bool + { + $edb->getConnection()->query("INSERT INTO `user-events` VALUES (?, ?, ?, ?, ?)", ...array_values($this->data)); + + return true; + } +} + +class EventRateLimiter +{ + use TSimpleSingleton; + + public function writeEvent(string $event_name, User $initiator, ?User $reciever = null): bool + { + $eventsConfig = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; + if (!$eventsConfig['enable']) { + return false; + } + + if (!($e = eventdb())) { + return false; + } + + $data = [ + 'initiatorId' => $initiator->getId(), + 'initiatorIp' => null, + 'receiverId' => null, + 'eventType' => $event_name, + 'eventTime' => time() + ]; + + if ($reciever) { + $data['receiverId'] = $reciever->getId(); + } + + $newEvent = new UserEvent($data); + $newEvent->write($e); + + return true; + } +} diff --git a/install/sqls/eventdb/00001-events-log.sql b/install/sqls/eventdb/00001-events-log.sql new file mode 100644 index 00000000..dc972bec --- /dev/null +++ b/install/sqls/eventdb/00001-events-log.sql @@ -0,0 +1,8 @@ +CREATE TABLE `user-events` +( + `initiatorId` BIGINT(20) NOT NULL, + `initiatorIp` VARBINARY(16) NULL DEFAULT NULL, + `receiverId` BIGINT(20) NOT NULL, + `eventType` CHAR(25) NOT NULL, + `eventTime` BIGINT(20) NOT NULL +) ENGINE = InnoDB; diff --git a/openvk-example.yml b/openvk-example.yml index 6f26803b..784711ba 100644 --- a/openvk-example.yml +++ b/openvk-example.yml @@ -41,6 +41,15 @@ openvk: maxViolations: 50 maxViolationsAge: 120 autoban: true + eventsLimit: + enable: true + restrictionTime: 86400 + list: + groups.create: 5 + groups.sub: 50 + friends.outgoing_sub: 25 + wall.post: 500 + gifts.send: 20 blacklists: limit: 100 applyToAdmins: true From b180a28b468dc139d22276c0002a6714e031478e Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Mon, 9 Jun 2025 16:15:21 +0300 Subject: [PATCH 2/9] set up common events --- VKAPI/Handlers/Wall.php | 3 +- Web/Models/Entities/Traits/TSubscribable.php | 9 ++++++ Web/Models/Entities/User.php | 3 ++ Web/Presenters/GroupPresenter.php | 3 ++ Web/Presenters/WallPresenter.php | 2 ++ Web/Util/EventRateLimiter.php | 30 +++++++++++++++++--- install/sqls/eventdb/00001-events-log.sql | 6 ++-- 7 files changed, 47 insertions(+), 9 deletions(-) diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index 9e4d9180..a29fc754 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -21,7 +21,6 @@ use openvk\Web\Models\Entities\Note; use openvk\Web\Models\Repositories\Notes as NotesRepo; use openvk\Web\Models\Repositories\Polls as PollsRepo; use openvk\Web\Models\Repositories\Audios as AudiosRepo; -use openvk\Web\Util\EventRateLimiter; final class Wall extends VKAPIRequestHandler { @@ -727,7 +726,7 @@ final class Wall extends VKAPIRequestHandler (new WallPostNotification($wallOwner, $post, $this->getUser()->getId()))->emit(); } - EventRateLimiter::i()->writeEvent("wall.post", $this->getUser(), $wallOwner); + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->getUser(), $wallOwner); return (object) ["post_id" => $post->getVirtualId()]; } diff --git a/Web/Models/Entities/Traits/TSubscribable.php b/Web/Models/Entities/Traits/TSubscribable.php index 898f33a8..654ae4ac 100644 --- a/Web/Models/Entities/Traits/TSubscribable.php +++ b/Web/Models/Entities/Traits/TSubscribable.php @@ -37,6 +37,15 @@ trait TSubscribable if (!($sub->fetch())) { $ctx->table("subscriptions")->insert($data); + + # todo change + + if (str_contains(static::class, "Club")) { + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.sub", $user, $this); + } else { + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("friends.outgoing_sub", $user, $this); + } + return true; } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index df4da4d0..71946b34 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -114,6 +114,7 @@ class User extends RowModel public function getChandlerUser(): ChandlerUser { + # TODO cache this function return new ChandlerUser($this->getRecord()->ref("ChandlerUsers", "user")); } @@ -1043,6 +1044,8 @@ class User extends RowModel "anonymous" => $anonymous, "sent" => time(), ]); + + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("gifts.send", $sender, $this); } public function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = null, ?int $initiator = null): void diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index a38dacaa..088dfc9d 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -79,6 +79,9 @@ final class GroupPresenter extends OpenVKPresenter } $club->toggleSubscription($this->user->identity); + + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.create", $this->user->identity, $club); + $this->redirect("/club" . $club->getId()); } else { $this->flashFail("err", tr("error"), tr("error_no_group_name")); diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index dbdfdde0..3311c777 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -432,6 +432,8 @@ final class WallPresenter extends OpenVKPresenter } } + \openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->user->identity, $wallOwner); + if ($should_be_suggested) { $this->redirect("/club" . $wallOwner->getId() . "/suggested"); } else { diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index 80fea28a..e39e3c61 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace openvk\Web\Util; use openvk\Web\Models\Entities\User; +use openvk\Web\Models\RowModel; use Chandler\Patterns\TSimpleSingleton; class UserEvent @@ -28,10 +29,31 @@ class EventRateLimiter { use TSimpleSingleton; - public function writeEvent(string $event_name, User $initiator, ?User $reciever = null): bool + private $config; + private $availableFields; + + public function __construct() { - $eventsConfig = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; - if (!$eventsConfig['enable']) { + $this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; + $this->availableFields = array_keys($this->config['list']); + } + + public function tryToLimit(?User $user): bool + { + if (!$this->config['enable']) { + return false; + } + + if ($user->isAdmin()) { + return false; + } + + return true; + } + + public function writeEvent(string $event_name, User $initiator, ?RowModel $reciever = null): bool + { + if (!$this->config['enable']) { return false; } @@ -48,7 +70,7 @@ class EventRateLimiter ]; if ($reciever) { - $data['receiverId'] = $reciever->getId(); + $data['receiverId'] = $reciever->getRealId(); } $newEvent = new UserEvent($data); diff --git a/install/sqls/eventdb/00001-events-log.sql b/install/sqls/eventdb/00001-events-log.sql index dc972bec..3b7c4b4c 100644 --- a/install/sqls/eventdb/00001-events-log.sql +++ b/install/sqls/eventdb/00001-events-log.sql @@ -1,8 +1,8 @@ CREATE TABLE `user-events` ( - `initiatorId` BIGINT(20) NOT NULL, + `initiatorId` BIGINT(20) UNSIGNED NOT NULL, `initiatorIp` VARBINARY(16) NULL DEFAULT NULL, - `receiverId` BIGINT(20) NOT NULL, + `receiverId` BIGINT(20) NULL DEFAULT NULL, `eventType` CHAR(25) NOT NULL, - `eventTime` BIGINT(20) NOT NULL + `eventTime` BIGINT(20) UNSIGNED NOT NULL ) ENGINE = InnoDB; From 2333924089906dfc71dd1907658e3a291eca5b82 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Mon, 9 Jun 2025 17:29:18 +0300 Subject: [PATCH 3/9] add limitation errors to api --- VKAPI/Handlers/Friends.php | 4 ++++ VKAPI/Handlers/Gifts.php | 4 ++++ VKAPI/Handlers/Groups.php | 4 ++++ VKAPI/Handlers/VKAPIRequestHandler.php | 5 +++++ VKAPI/Handlers/Wall.php | 6 ++++- Web/Presenters/GiftsPresenter.php | 4 ++++ Web/Presenters/GroupPresenter.php | 4 ++++ Web/Util/EventRateLimiter.php | 31 +++++++++++++++++++++----- locales/en.strings | 2 ++ locales/ru.strings | 2 ++ openvk-example.yml | 3 ++- 11 files changed, 62 insertions(+), 7 deletions(-) diff --git a/VKAPI/Handlers/Friends.php b/VKAPI/Handlers/Friends.php index 77de7d9d..4c109e13 100644 --- a/VKAPI/Handlers/Friends.php +++ b/VKAPI/Handlers/Friends.php @@ -98,6 +98,10 @@ final class Friends extends VKAPIRequestHandler switch ($user->getSubscriptionStatus($this->getUser())) { case 0: + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "friends.outgoing_sub")) { + $this->failTooOften(); + } + $user->toggleSubscription($this->getUser()); return 1; diff --git a/VKAPI/Handlers/Gifts.php b/VKAPI/Handlers/Gifts.php index 9ee5d222..460f11df 100644 --- a/VKAPI/Handlers/Gifts.php +++ b/VKAPI/Handlers/Gifts.php @@ -61,6 +61,10 @@ final class Gifts extends VKAPIRequestHandler $this->fail(-105, "Commerce is disabled on this instance"); } + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "gifts.send", false)) { + $this->failTooOften(); + } + $user = (new UsersRepo())->get((int) $user_ids); # FAKE прогноз погоды (в данном случае user_ids) if (!$user || $user->isDeleted()) { diff --git a/VKAPI/Handlers/Groups.php b/VKAPI/Handlers/Groups.php index 8933ca5a..707dc0f4 100644 --- a/VKAPI/Handlers/Groups.php +++ b/VKAPI/Handlers/Groups.php @@ -312,6 +312,10 @@ final class Groups extends VKAPIRequestHandler $isMember = !is_null($this->getUser()) ? (int) $club->getSubscriptionStatus($this->getUser()) : 0; if ($isMember == 0) { + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "groups.sub")) { + $this->failTooOften(); + } + $club->toggleSubscription($this->getUser()); } diff --git a/VKAPI/Handlers/VKAPIRequestHandler.php b/VKAPI/Handlers/VKAPIRequestHandler.php index 4804a8dd..1f40fceb 100644 --- a/VKAPI/Handlers/VKAPIRequestHandler.php +++ b/VKAPI/Handlers/VKAPIRequestHandler.php @@ -25,6 +25,11 @@ abstract class VKAPIRequestHandler throw new APIErrorException($message, $code); } + protected function failTooOften(): never + { + $this->fail(9, "Rate limited"); + } + protected function getUser(): ?User { return $this->user; diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index a29fc754..5309f025 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -620,6 +620,10 @@ final class Wall extends VKAPIRequestHandler return (object) ["post_id" => $post->getVirtualId()]; } + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "wall.post", false)) { + $this->failTooOften(); + } + $anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]; if ($wallOwner instanceof Club && $from_group == 1 && $signed != 1 && $anon) { $manager = $wallOwner->getManager($this->getUser()); @@ -723,7 +727,7 @@ final class Wall extends VKAPIRequestHandler } if ($owner_id > 0 && $owner_id !== $this->getUser()->getId()) { - (new WallPostNotification($wallOwner, $post, $this->getUser()->getId()))->emit(); + (new WallPostNotification($wallOwner, $post, $this->getUser()))->emit(); } \openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->getUser(), $wallOwner); diff --git a/Web/Presenters/GiftsPresenter.php b/Web/Presenters/GiftsPresenter.php index 007be976..f945c342 100644 --- a/Web/Presenters/GiftsPresenter.php +++ b/Web/Presenters/GiftsPresenter.php @@ -106,6 +106,10 @@ final class GiftsPresenter extends OpenVKPresenter return; } + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "gifts.send", false)) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + $comment = empty($c = $this->postParam("comment")) ? null : $c; $notification = new GiftNotification($user, $this->user->identity, $gift, $comment); $notification->emit(); diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 088dfc9d..57bdd8fd 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -63,6 +63,10 @@ final class GroupPresenter extends OpenVKPresenter if ($_SERVER["REQUEST_METHOD"] === "POST") { if (!empty($this->postParam("name")) && mb_strlen(trim($this->postParam("name"))) > 0) { + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.create")) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + $club = new Club(); $club->setName($this->postParam("name")); $club->setAbout(empty($this->postParam("about")) ? null : $this->postParam("about")); diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index e39e3c61..4cce791d 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -38,20 +38,41 @@ class EventRateLimiter $this->availableFields = array_keys($this->config['list']); } - public function tryToLimit(?User $user): bool + /* + Checks count of actions for last x seconds, returns true if limit has exceed + + x is OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]["restrictionTime"] + */ + public function tryToLimit(?User $user, string $event_type, bool $distinct = true): bool { if (!$this->config['enable']) { return false; } - if ($user->isAdmin()) { + if (!($e = eventdb())) { return false; } - return true; + if ($this->config['ignoreForAdmins'] && $user->isAdmin()) { + return false; + } + + $limitForThisEvent = $this->config['list'][$event_type]; + $compareTime = time() - $this->config['restrictionTime']; + + $query = "SELECT COUNT(".($distinct ? "DISTINCT(`receiverId`)" : "*").") as `cnt` FROM `user-events` WHERE `initiatorId` = ? AND `eventType` = ? AND `eventTime` > ?"; + + $result = $e->getConnection()->query($query, ...[$user->getId(), $event_type, $compareTime]); + $count = $result->fetch()->cnt; + #bdump($count); exit(); + + return $count > $limitForThisEvent; } - public function writeEvent(string $event_name, User $initiator, ?RowModel $reciever = null): bool + /* + Writes new event to `openvk-eventdb`.`user-events` + */ + public function writeEvent(string $event_type, User $initiator, ?RowModel $reciever = null): bool { if (!$this->config['enable']) { return false; @@ -65,7 +86,7 @@ class EventRateLimiter 'initiatorId' => $initiator->getId(), 'initiatorIp' => null, 'receiverId' => null, - 'eventType' => $event_name, + 'eventType' => $event_type, 'eventTime' => time() ]; diff --git a/locales/en.strings b/locales/en.strings index 1632b165..48576550 100644 --- a/locales/en.strings +++ b/locales/en.strings @@ -1657,6 +1657,8 @@ "error_geolocation" = "Error while trying to pin geolocation"; "error_no_geotag" = "There is no geo-tag pinned in this post"; +"limit_exceed_exception" = "You're doing this action too often. Try again later."; + /* Admin actions */ "login_as" = "Login as $1"; diff --git a/locales/ru.strings b/locales/ru.strings index 6366b747..9d116554 100644 --- a/locales/ru.strings +++ b/locales/ru.strings @@ -1561,6 +1561,8 @@ "error_geolocation" = "Ошибка при прикреплении геометки"; "error_no_geotag" = "У поста не указана гео-метка"; +"limit_exceed_exception" = "Вы совершаете это действие слишком часто. Повторите позже."; + /* Admin actions */ "login_as" = "Войти как $1"; diff --git a/openvk-example.yml b/openvk-example.yml index 784711ba..c92d0e2f 100644 --- a/openvk-example.yml +++ b/openvk-example.yml @@ -43,12 +43,13 @@ openvk: autoban: true eventsLimit: enable: true + ignoreForAdmins: true restrictionTime: 86400 list: groups.create: 5 groups.sub: 50 friends.outgoing_sub: 25 - wall.post: 500 + wall.post: 5000 gifts.send: 20 blacklists: limit: 100 From bdac64a4ae56f0cc044b337b1e78538cc268f2a1 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Wed, 25 Jun 2025 15:11:14 +0300 Subject: [PATCH 4/9] replace old migration --- Web/Util/EventRateLimiter.php | 11 +---------- install/sqls/00057-event-limiting.sql | 3 +++ install/sqls/eventdb/00001-events-log.sql | 8 -------- 3 files changed, 4 insertions(+), 18 deletions(-) create mode 100644 install/sqls/00057-event-limiting.sql delete mode 100644 install/sqls/eventdb/00001-events-log.sql diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index 4cce791d..fb5b84a7 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -49,10 +49,6 @@ class EventRateLimiter return false; } - if (!($e = eventdb())) { - return false; - } - if ($this->config['ignoreForAdmins'] && $user->isAdmin()) { return false; } @@ -61,8 +57,7 @@ class EventRateLimiter $compareTime = time() - $this->config['restrictionTime']; $query = "SELECT COUNT(".($distinct ? "DISTINCT(`receiverId`)" : "*").") as `cnt` FROM `user-events` WHERE `initiatorId` = ? AND `eventType` = ? AND `eventTime` > ?"; - - $result = $e->getConnection()->query($query, ...[$user->getId(), $event_type, $compareTime]); + $count = $result->fetch()->cnt; #bdump($count); exit(); @@ -78,10 +73,6 @@ class EventRateLimiter return false; } - if (!($e = eventdb())) { - return false; - } - $data = [ 'initiatorId' => $initiator->getId(), 'initiatorIp' => null, diff --git a/install/sqls/00057-event-limiting.sql b/install/sqls/00057-event-limiting.sql new file mode 100644 index 00000000..bf34f5be --- /dev/null +++ b/install/sqls/00057-event-limiting.sql @@ -0,0 +1,3 @@ +ALTER TABLE `profiles` +ADD `events_counters` VARCHAR(299) NULL DEFAULT NULL AFTER `audio_broadcast_enabled`, +ADD `events_refresh_time` BIGINT(20) UNSIGNED NULL DEFAULT NULL AFTER `events_counters`; diff --git a/install/sqls/eventdb/00001-events-log.sql b/install/sqls/eventdb/00001-events-log.sql deleted file mode 100644 index 3b7c4b4c..00000000 --- a/install/sqls/eventdb/00001-events-log.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE `user-events` -( - `initiatorId` BIGINT(20) UNSIGNED NOT NULL, - `initiatorIp` VARBINARY(16) NULL DEFAULT NULL, - `receiverId` BIGINT(20) NULL DEFAULT NULL, - `eventType` CHAR(25) NOT NULL, - `eventTime` BIGINT(20) UNSIGNED NOT NULL -) ENGINE = InnoDB; From 5898b9a45549e5a1e80f11f295b225ed88c5ddc6 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:02:27 +0300 Subject: [PATCH 5/9] draft --- Web/Models/Entities/Traits/TSubscribable.php | 8 -- Web/Models/Entities/User.php | 40 ++++++- Web/Presenters/GroupPresenter.php | 2 - Web/Presenters/WallPresenter.php | 2 - Web/Util/EventRateLimiter.php | 114 ++++++++++++------- 5 files changed, 110 insertions(+), 56 deletions(-) diff --git a/Web/Models/Entities/Traits/TSubscribable.php b/Web/Models/Entities/Traits/TSubscribable.php index 654ae4ac..712b2366 100644 --- a/Web/Models/Entities/Traits/TSubscribable.php +++ b/Web/Models/Entities/Traits/TSubscribable.php @@ -37,14 +37,6 @@ trait TSubscribable if (!($sub->fetch())) { $ctx->table("subscriptions")->insert($data); - - # todo change - - if (str_contains(static::class, "Club")) { - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.sub", $user, $this); - } else { - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("friends.outgoing_sub", $user, $this); - } return true; } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 71946b34..80a787fa 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1044,8 +1044,6 @@ class User extends RowModel "anonymous" => $anonymous, "sent" => time(), ]); - - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("gifts.send", $sender, $this); } public function ban(string $reason, bool $deleteSubscriptions = true, $unban_time = null, ?int $initiator = null): void @@ -1741,4 +1739,42 @@ class User extends RowModel { return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count(); } + + public function recieveEventsData(array $list): array + { + $ev_str = $this->getRecord()->events_counters; + $values = []; + + if (!$ev_str) { + for ($i = 0; $i < sizeof(array_keys($list)); $i++) { + $values[] = 0; + } + } else { + $keys = array_keys($list); + $values = unpack("S*", base64_decode($ev_str)); + } + + return [ + 'counters' => $values, + 'refresh_time' => $this->getRecord()->events_refresh_time, + ]; + } + + public function stateEvents(array $list): void + { + $this->stateChanges("events_counters", base64_encode(pack("S*", array_values($list)))); + } + + public function resetEvents(array $list, int $restriction_length) + { + $values = []; + + for ($i = 0; $i < sizeof(array_keys($list)); $i++) { + $values[] = 0; + } + + $this->stateEvents($values); + $this->stateChanges("events_refresh_time", $restriction_length + time()); + $this->save(); + } } diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 57bdd8fd..96f69083 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -83,8 +83,6 @@ final class GroupPresenter extends OpenVKPresenter } $club->toggleSubscription($this->user->identity); - - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("groups.create", $this->user->identity, $club); $this->redirect("/club" . $club->getId()); } else { diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index 3311c777..dbdfdde0 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -432,8 +432,6 @@ final class WallPresenter extends OpenVKPresenter } } - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->user->identity, $wallOwner); - if ($should_be_suggested) { $this->redirect("/club" . $wallOwner->getId() . "/suggested"); } else { diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index fb5b84a7..7d3b0862 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -8,23 +8,6 @@ use openvk\Web\Models\Entities\User; use openvk\Web\Models\RowModel; use Chandler\Patterns\TSimpleSingleton; -class UserEvent -{ - private $data; - - public function __construct($data) - { - $this->data = $data; - } - - public function write($edb): bool - { - $edb->getConnection()->query("INSERT INTO `user-events` VALUES (?, ?, ?, ?, ?)", ...array_values($this->data)); - - return true; - } -} - class EventRateLimiter { use TSimpleSingleton; @@ -35,58 +18,105 @@ class EventRateLimiter public function __construct() { $this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; + $this->availableFields = array_keys($this->config['list']); } /* - Checks count of actions for last x seconds, returns true if limit has exceed + Checks count of actions for last hours - x is OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]["restrictionTime"] + Uses config path OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"] + + Returns: + + true — limit has exceed and the action must be restricted + + false — the action can be performed */ public function tryToLimit(?User $user, string $event_type, bool $distinct = true): bool { - if (!$this->config['enable']) { + $isEnabled = $this->config['enable']; + $isIgnoreForAdmins = $this->config['ignoreForAdmins']; + $restrictionTime = $this->config['restrictionTime']; + $eventsList = $this->config['list']; + + if (!$isEnabled) { return false; } - if ($this->config['ignoreForAdmins'] && $user->isAdmin()) { + if ($isIgnoreForAdmins && $user->isAdmin()) { return false; } - $limitForThisEvent = $this->config['list'][$event_type]; - $compareTime = time() - $this->config['restrictionTime']; + $limitForThatEvent = $eventsList[$event_type]; + $stat = $this->getEvent($event_type, $user); + bdump($stat); - $query = "SELECT COUNT(".($distinct ? "DISTINCT(`receiverId`)" : "*").") as `cnt` FROM `user-events` WHERE `initiatorId` = ? AND `eventType` = ? AND `eventTime` > ?"; - - $count = $result->fetch()->cnt; - #bdump($count); exit(); + $is_restrict_over = $stat["refresh_time"] < time() - $restrictionTime; - return $count > $limitForThisEvent; + if ($is_restrict_over) { + $user->resetEvents($eventsList, $restrictionTime); + + return false; + } + + $is = $stat["compared"] > $limitForThatEvent; + + if ($is === false) { + $this->incrementEvent($event_type, $user); + } + + return $is; + } + + public function getEvent(string $event_type, User $by_user): array + { + $ev_data = $by_user->recieveEventsData($this->config['list']); + $values = $ev_data['counters']; + $i = 0; + + $compared = []; + bdump($values); + + foreach ($this->config['list'] as $name => $value) { + bdump($value); + $compared[$name] = $values[$i]; + $i += 1; + } + + return [ + "compared" => $compared, + "refresh_time" => $ev_data["refresh_time"] + ]; } /* - Writes new event to `openvk-eventdb`.`user-events` + Updates counter for user */ - public function writeEvent(string $event_type, User $initiator, ?RowModel $reciever = null): bool + public function incrementEvent(string $event_type, User $initiator): bool { - if (!$this->config['enable']) { + $isEnabled = $this->config['enable']; + $eventsList = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; + + if (!$isEnabled) { return false; } - $data = [ - 'initiatorId' => $initiator->getId(), - 'initiatorIp' => null, - 'receiverId' => null, - 'eventType' => $event_type, - 'eventTime' => time() - ]; + $ev_data = $initiator->recieveEventsData($eventsList); + $values = $ev_data['counters']; + $i = 0; - if ($reciever) { - $data['receiverId'] = $reciever->getRealId(); + $compared = []; + + foreach ($eventsList as $name => $value) { + $compared[$name] = $values[$i]; + $i += 1; } - $newEvent = new UserEvent($data); - $newEvent->write($e); + $compared[$event_type] += 1; + + bdump($compared); + $initiator->stateEvents($compared); return true; } From c3b1e46909fc07c8c0152072a14b9a070c5bc8d9 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:14:52 +0300 Subject: [PATCH 6/9] draft no.0,33 --- VKAPI/Handlers/Wall.php | 10 ++-- Web/Models/Entities/User.php | 39 ++++++++++----- Web/Presenters/GiftsPresenter.php | 2 +- Web/Presenters/GroupPresenter.php | 8 +-- Web/Presenters/WallPresenter.php | 4 ++ Web/Util/EventRateLimiter.php | 81 +++++++++++-------------------- 6 files changed, 69 insertions(+), 75 deletions(-) diff --git a/VKAPI/Handlers/Wall.php b/VKAPI/Handlers/Wall.php index 5309f025..f1fdf978 100644 --- a/VKAPI/Handlers/Wall.php +++ b/VKAPI/Handlers/Wall.php @@ -620,10 +620,6 @@ final class Wall extends VKAPIRequestHandler return (object) ["post_id" => $post->getVirtualId()]; } - if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "wall.post", false)) { - $this->failTooOften(); - } - $anon = OPENVK_ROOT_CONF["openvk"]["preferences"]["wall"]["anonymousPosting"]["enable"]; if ($wallOwner instanceof Club && $from_group == 1 && $signed != 1 && $anon) { $manager = $wallOwner->getManager($this->getUser()); @@ -717,6 +713,10 @@ final class Wall extends VKAPIRequestHandler $post->setSuggested(1); } + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->getUser(), "wall.post")) { + $this->failTooOften(); + } + $post->save(); } catch (\LogicException $ex) { $this->fail(100, "One of the parameters specified was missing or invalid"); @@ -730,8 +730,6 @@ final class Wall extends VKAPIRequestHandler (new WallPostNotification($wallOwner, $post, $this->getUser()))->emit(); } - \openvk\Web\Util\EventRateLimiter::i()->writeEvent("wall.post", $this->getUser(), $wallOwner); - return (object) ["post_id" => $post->getVirtualId()]; } diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 80a787fa..3fb28776 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1740,41 +1740,56 @@ class User extends RowModel return DatabaseConnection::i()->getContext()->table("blacklist_relations")->where("author", $this->getId())->count(); } - public function recieveEventsData(array $list): array + public function getEventCounters(array $list): array { $ev_str = $this->getRecord()->events_counters; - $values = []; + $counters = []; + $compared_counters = []; if (!$ev_str) { + bdump(sizeof(array_keys($list))); for ($i = 0; $i < sizeof(array_keys($list)); $i++) { - $values[] = 0; + $counters[] = 0; } } else { - $keys = array_keys($list); - $values = unpack("S*", base64_decode($ev_str)); + $counters = unpack("S*", base64_decode($ev_str)); } + $i = 1; + + foreach ($list as $name => $value) { + $compared_counters[$name] = $counters[$i] ?? 0; + $i += 1; + } + + bdump($counters); + bdump($compared_counters); return [ - 'counters' => $values, + 'counters' => $compared_counters, 'refresh_time' => $this->getRecord()->events_refresh_time, ]; } - public function stateEvents(array $list): void + public function stateEvents(array $state_list): void { - $this->stateChanges("events_counters", base64_encode(pack("S*", array_values($list)))); + bdump($state_list); + $this->stateChanges("events_counters", base64_encode(pack("S*", ...array_values($state_list)))); + + if (!$this->getRecord()->events_refresh_time) { + $this->stateChanges("events_refresh_time", time()); + } } - public function resetEvents(array $list, int $restriction_length) + public function resetEvents(array $list) { $values = []; - for ($i = 0; $i < sizeof(array_keys($list)); $i++) { - $values[] = 0; + foreach ($list as $key => $val) { + $values[$key] = 0; } $this->stateEvents($values); - $this->stateChanges("events_refresh_time", $restriction_length + time()); + $this->stateChanges("events_refresh_time", time()); $this->save(); } } diff --git a/Web/Presenters/GiftsPresenter.php b/Web/Presenters/GiftsPresenter.php index f945c342..ec2993a4 100644 --- a/Web/Presenters/GiftsPresenter.php +++ b/Web/Presenters/GiftsPresenter.php @@ -106,7 +106,7 @@ final class GiftsPresenter extends OpenVKPresenter return; } - if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "gifts.send", false)) { + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "gifts.send")) { $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); } diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 96f69083..2151b503 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -63,15 +63,15 @@ final class GroupPresenter extends OpenVKPresenter if ($_SERVER["REQUEST_METHOD"] === "POST") { if (!empty($this->postParam("name")) && mb_strlen(trim($this->postParam("name"))) > 0) { - if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.create")) { - $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); - } - $club = new Club(); $club->setName($this->postParam("name")); $club->setAbout(empty($this->postParam("about")) ? null : $this->postParam("about")); $club->setOwner($this->user->id); + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.create")) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + try { $club->save(); } catch (\PDOException $ex) { diff --git a/Web/Presenters/WallPresenter.php b/Web/Presenters/WallPresenter.php index dbdfdde0..53e662f6 100644 --- a/Web/Presenters/WallPresenter.php +++ b/Web/Presenters/WallPresenter.php @@ -356,6 +356,10 @@ final class WallPresenter extends OpenVKPresenter $this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big")); } + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "wall.post")) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + $should_be_suggested = $wall < 0 && !$wallOwner->canBeModifiedBy($this->user->identity) && $wallOwner->getWallType() == 2; try { $post = new Post(); diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index 7d3b0862..b381134b 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -13,19 +13,18 @@ class EventRateLimiter use TSimpleSingleton; private $config; - private $availableFields; public function __construct() { $this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; - - $this->availableFields = array_keys($this->config['list']); } /* - Checks count of actions for last hours + Checks count of actions for last x seconds. - Uses config path OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"] + Uses OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"] + + This check should be peformed only after checking other conditions cuz by default it increments counter Returns: @@ -33,8 +32,9 @@ class EventRateLimiter false — the action can be performed */ - public function tryToLimit(?User $user, string $event_type, bool $distinct = true): bool + public function tryToLimit(?User $user, string $event_type, bool $is_update = true): bool { + bdump("TRY TO LIMIT IS CALLLLED"); $isEnabled = $this->config['enable']; $isIgnoreForAdmins = $this->config['ignoreForAdmins']; $restrictionTime = $this->config['restrictionTime']; @@ -49,74 +49,51 @@ class EventRateLimiter } $limitForThatEvent = $eventsList[$event_type]; - $stat = $this->getEvent($event_type, $user); - bdump($stat); + $eventsStats = $user->getEventCounters($eventsList); - $is_restrict_over = $stat["refresh_time"] < time() - $restrictionTime; + $counters = $eventsStats["counters"]; + $refresh_time = $eventsStats["refresh_time"]; + $is_restrict_over = $refresh_time < (time() - $restrictionTime); + bdump($refresh_time); + bdump("time: " . time()); + $event_counter = $counters[$event_type]; - if ($is_restrict_over) { - $user->resetEvents($eventsList, $restrictionTime); + if ($refresh_time && $is_restrict_over) { + bdump("RESETTING EVENT COUTNERS"); + $user->resetEvents($eventsList); return false; } - $is = $stat["compared"] > $limitForThatEvent; + $is_limit_exceed = $event_counter > $limitForThatEvent; - if ($is === false) { - $this->incrementEvent($event_type, $user); + bdump($is_limit_exceed); + if (!$is_limit_exceed && $is_update) { + $this->incrementEvent($counters, $event_type, $user); } - return $is; - } - - public function getEvent(string $event_type, User $by_user): array - { - $ev_data = $by_user->recieveEventsData($this->config['list']); - $values = $ev_data['counters']; - $i = 0; - - $compared = []; - bdump($values); - - foreach ($this->config['list'] as $name => $value) { - bdump($value); - $compared[$name] = $values[$i]; - $i += 1; - } - - return [ - "compared" => $compared, - "refresh_time" => $ev_data["refresh_time"] - ]; + return $is_limit_exceed; } /* Updates counter for user */ - public function incrementEvent(string $event_type, User $initiator): bool + public function incrementEvent(array $old_values, string $event_type, User $initiator): bool { + bdump("INCREMENT IS CALLED"); $isEnabled = $this->config['enable']; - $eventsList = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; + $eventsList = $this->config['list']; if (!$isEnabled) { return false; } + bdump($old_values); - $ev_data = $initiator->recieveEventsData($eventsList); - $values = $ev_data['counters']; - $i = 0; + $old_values[$event_type] += 1; - $compared = []; - - foreach ($eventsList as $name => $value) { - $compared[$name] = $values[$i]; - $i += 1; - } - - $compared[$event_type] += 1; - - bdump($compared); - $initiator->stateEvents($compared); + bdump($old_values); + $initiator->stateEvents($old_values); + $initiator->save(); return true; } From 636a9e5b67d6480f7916a71991f0ae80151ee4d2 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Thu, 26 Jun 2025 13:49:23 +0300 Subject: [PATCH 7/9] draft v.0.99 --- Web/Models/Entities/User.php | 8 ++++---- Web/Presenters/GroupPresenter.php | 6 ++++++ Web/Presenters/UserPresenter.php | 6 ++++++ Web/Util/EventRateLimiter.php | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 3fb28776..78c56191 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1755,11 +1755,11 @@ class User extends RowModel $counters = unpack("S*", base64_decode($ev_str)); } - $i = 1; + $_i = 0; foreach ($list as $name => $value) { - $compared_counters[$name] = $counters[$i] ?? 0; - $i += 1; + $compared_counters[$name] = $counters[$_i] ?? 0; + $_i += 1; } bdump($counters); @@ -1773,7 +1773,7 @@ class User extends RowModel public function stateEvents(array $state_list): void { bdump($state_list); - $this->stateChanges("events_counters", base64_encode(pack("S*", ...array_values($state_list)))); + $this->stateChanges("events_counters", base64_encode(pack("S*", array_values($state_list)))); if (!$this->getRecord()->events_refresh_time) { $this->stateChanges("events_refresh_time", time()); diff --git a/Web/Presenters/GroupPresenter.php b/Web/Presenters/GroupPresenter.php index 2151b503..99d5fa33 100644 --- a/Web/Presenters/GroupPresenter.php +++ b/Web/Presenters/GroupPresenter.php @@ -108,6 +108,12 @@ final class GroupPresenter extends OpenVKPresenter $this->flashFail("err", tr("error"), tr("forbidden")); } + if (!$club->getSubscriptionStatus($this->user->identity)) { + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "groups.sub")) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + } + $club->toggleSubscription($this->user->identity); $this->redirect($club->getURL()); diff --git a/Web/Presenters/UserPresenter.php b/Web/Presenters/UserPresenter.php index 7d152df4..869d601e 100644 --- a/Web/Presenters/UserPresenter.php +++ b/Web/Presenters/UserPresenter.php @@ -418,6 +418,12 @@ final class UserPresenter extends OpenVKPresenter if ($this->postParam("act") == "rej") { $user->changeFlags($this->user->identity, 0b10000000, true); } else { + if ($user->getSubscriptionStatus($this->user->identity) == \openvk\Web\Models\Entities\User::SUBSCRIPTION_ABSENT) { + if (\openvk\Web\Util\EventRateLimiter::i()->tryToLimit($this->user->identity, "friends.outgoing_sub")) { + $this->flashFail("err", tr("error"), tr("limit_exceed_exception")); + } + } + $user->toggleSubscription($this->user->identity); } diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index b381134b..072b11c7 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -48,8 +48,8 @@ class EventRateLimiter return false; } - $limitForThatEvent = $eventsList[$event_type]; $eventsStats = $user->getEventCounters($eventsList); + $limitForThatEvent = $eventsList[$event_type]; $counters = $eventsStats["counters"]; $refresh_time = $eventsStats["refresh_time"]; From 9079df96466ca1291770b44aa817cac582e72790 Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Thu, 26 Jun 2025 15:48:53 +0300 Subject: [PATCH 8/9] change pack() --- Web/Models/Entities/User.php | 31 ++++++++++++++++++------------- Web/Util/EventRateLimiter.php | 2 +- openvk-example.yml | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index 78c56191..c31fe048 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -1742,9 +1742,9 @@ class User extends RowModel public function getEventCounters(array $list): array { + $count_of_keys = sizeof(array_keys($list)); $ev_str = $this->getRecord()->events_counters; $counters = []; - $compared_counters = []; if (!$ev_str) { bdump(sizeof(array_keys($list))); @@ -1752,28 +1752,33 @@ class User extends RowModel $counters[] = 0; } } else { - $counters = unpack("S*", base64_decode($ev_str)); - } - - $_i = 0; - - foreach ($list as $name => $value) { - $compared_counters[$name] = $counters[$_i] ?? 0; - $_i += 1; + $counters = unpack("S".$count_of_keys, base64_decode($ev_str, true)); + } + bdump(array_keys($list)); bdump($counters); - bdump($compared_counters); return [ - 'counters' => $compared_counters, + 'counters' => array_combine(array_keys($list), $counters), 'refresh_time' => $this->getRecord()->events_refresh_time, ]; } public function stateEvents(array $state_list): void { - bdump($state_list); - $this->stateChanges("events_counters", base64_encode(pack("S*", array_values($state_list)))); + $_ = ""; + $i = 0; + + foreach ($state_list as $item => $id) { + bdump($i); + $_ .= "S"; + $i += 1; + } + + bdump($_); + bdump(array_values($state_list)); + + $this->stateChanges("events_counters", base64_encode(pack($_, ...array_values($state_list)))); if (!$this->getRecord()->events_refresh_time) { $this->stateChanges("events_refresh_time", time()); diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index 072b11c7..3257bc4e 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -65,7 +65,7 @@ class EventRateLimiter return false; } - $is_limit_exceed = $event_counter > $limitForThatEvent; + $is_limit_exceed = $event_counter >= $limitForThatEvent; bdump($is_limit_exceed); if (!$is_limit_exceed && $is_update) { diff --git a/openvk-example.yml b/openvk-example.yml index c92d0e2f..548ec9b7 100644 --- a/openvk-example.yml +++ b/openvk-example.yml @@ -50,7 +50,7 @@ openvk: groups.sub: 50 friends.outgoing_sub: 25 wall.post: 5000 - gifts.send: 20 + gifts.send: 30 blacklists: limit: 100 applyToAdmins: true From 3e44516095fc3f2b2bba7996edb85841028ac09a Mon Sep 17 00:00:00 2001 From: mrilyew <99399973+mrilyew@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:17:45 +0300 Subject: [PATCH 9/9] remove dumps --- Web/Models/Entities/Traits/TSubscribable.php | 1 - Web/Models/Entities/User.php | 21 +++------- Web/Util/EventRateLimiter.php | 42 +++++++++----------- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/Web/Models/Entities/Traits/TSubscribable.php b/Web/Models/Entities/Traits/TSubscribable.php index 712b2366..2e3f3bcd 100644 --- a/Web/Models/Entities/Traits/TSubscribable.php +++ b/Web/Models/Entities/Traits/TSubscribable.php @@ -34,7 +34,6 @@ trait TSubscribable "target" => $this->getId(), ]; $sub = $ctx->table("subscriptions")->where($data); - if (!($sub->fetch())) { $ctx->table("subscriptions")->insert($data); diff --git a/Web/Models/Entities/User.php b/Web/Models/Entities/User.php index c31fe048..2f336f4a 100644 --- a/Web/Models/Entities/User.php +++ b/Web/Models/Entities/User.php @@ -114,7 +114,6 @@ class User extends RowModel public function getChandlerUser(): ChandlerUser { - # TODO cache this function return new ChandlerUser($this->getRecord()->ref("ChandlerUsers", "user")); } @@ -1747,17 +1746,13 @@ class User extends RowModel $counters = []; if (!$ev_str) { - bdump(sizeof(array_keys($list))); for ($i = 0; $i < sizeof(array_keys($list)); $i++) { $counters[] = 0; } } else { - $counters = unpack("S".$count_of_keys, base64_decode($ev_str, true)); - + $counters = unpack("S" . $count_of_keys, base64_decode($ev_str, true)); } - bdump(array_keys($list)); - bdump($counters); return [ 'counters' => array_combine(array_keys($list), $counters), 'refresh_time' => $this->getRecord()->events_refresh_time, @@ -1766,26 +1761,20 @@ class User extends RowModel public function stateEvents(array $state_list): void { - $_ = ""; - $i = 0; + $pack_str = ""; foreach ($state_list as $item => $id) { - bdump($i); - $_ .= "S"; - $i += 1; + $pack_str .= "S"; } - bdump($_); - bdump(array_values($state_list)); - - $this->stateChanges("events_counters", base64_encode(pack($_, ...array_values($state_list)))); + $this->stateChanges("events_counters", base64_encode(pack($pack_str, ...array_values($state_list)))); if (!$this->getRecord()->events_refresh_time) { $this->stateChanges("events_refresh_time", time()); } } - public function resetEvents(array $list) + public function resetEvents(array $list): void { $values = []; diff --git a/Web/Util/EventRateLimiter.php b/Web/Util/EventRateLimiter.php index 3257bc4e..cc71309f 100644 --- a/Web/Util/EventRateLimiter.php +++ b/Web/Util/EventRateLimiter.php @@ -19,22 +19,23 @@ class EventRateLimiter $this->config = OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"]; } - /* - Checks count of actions for last x seconds. - - Uses OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"] - - This check should be peformed only after checking other conditions cuz by default it increments counter - - Returns: - - true — limit has exceed and the action must be restricted - - false — the action can be performed - */ public function tryToLimit(?User $user, string $event_type, bool $is_update = true): bool { - bdump("TRY TO LIMIT IS CALLLLED"); + + /* + Checks count of actions for last x seconds. + + Uses OPENVK_ROOT_CONF["openvk"]["preferences"]["security"]["rateLimits"]["eventsLimit"] + + This check should be peformed only after checking other conditions cuz by default it increments counter + + Returns: + + true — limit has exceed and the action must be restricted + + false — the action can be performed + */ + $isEnabled = $this->config['enable']; $isIgnoreForAdmins = $this->config['ignoreForAdmins']; $restrictionTime = $this->config['restrictionTime']; @@ -54,12 +55,9 @@ class EventRateLimiter $counters = $eventsStats["counters"]; $refresh_time = $eventsStats["refresh_time"]; $is_restrict_over = $refresh_time < (time() - $restrictionTime); - bdump($refresh_time); - bdump("time: " . time()); $event_counter = $counters[$event_type]; if ($refresh_time && $is_restrict_over) { - bdump("RESETTING EVENT COUTNERS"); $user->resetEvents($eventsList); return false; @@ -67,7 +65,6 @@ class EventRateLimiter $is_limit_exceed = $event_counter >= $limitForThatEvent; - bdump($is_limit_exceed); if (!$is_limit_exceed && $is_update) { $this->incrementEvent($counters, $event_type, $user); } @@ -75,23 +72,20 @@ class EventRateLimiter return $is_limit_exceed; } - /* - Updates counter for user - */ public function incrementEvent(array $old_values, string $event_type, User $initiator): bool { - bdump("INCREMENT IS CALLED"); + /* + Updates counter for user + */ $isEnabled = $this->config['enable']; $eventsList = $this->config['list']; if (!$isEnabled) { return false; } - bdump($old_values); $old_values[$event_type] += 1; - bdump($old_values); $initiator->stateEvents($old_values); $initiator->save();