This commit is contained in:
mrilyew 2025-06-25 23:02:27 +03:00
parent bdac64a4ae
commit 5898b9a455
5 changed files with 110 additions and 56 deletions

View file

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

View file

@ -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();
}
}

View file

@ -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 {

View file

@ -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 {

View file

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