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] 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; }