File: //proc/1122/cwd/common/components/BotPushRateLimiter.php
<?php
namespace common\components;
use Yii;
use yii\base\Component;
/**
* Fixed-window rate limits for Push API (cache-backed).
*/
class BotPushRateLimiter extends Component
{
/** @var int Max requests per IP per window (0 = disabled) */
public int $ipLimit = 60;
/** @var int Max requests per bot API key per window */
public int $botLimit = 60;
/** @var int Max requests to the same recipient (chat_id / phone) per bot per window */
public int $recipientLimit = 6;
/** @var int Window size in seconds */
public int $windowSec = 60;
public function init(): void
{
parent::init();
$this->ipLimit = (int) env('BOT_PUSH_RATE_IP', $this->ipLimit);
$this->botLimit = (int) env('BOT_PUSH_RATE_BOT', $this->botLimit);
$this->recipientLimit = (int) env('BOT_PUSH_RATE_RECIPIENT', $this->recipientLimit);
$this->windowSec = max(1, (int) env('BOT_PUSH_RATE_WINDOW', $this->windowSec));
}
public function isIpAllowed(string $ip): bool
{
return $this->isAllowed('ip', $ip, $this->ipLimit);
}
public function isBotAllowed(int $botId): bool
{
return $this->isAllowed('bot', (string) $botId, $this->botLimit);
}
public function isRecipientAllowed(int $botId, string $recipientKey): bool
{
$scope = 'rcpt:' . $botId . ':' . hash('sha256', $recipientKey);
return $this->isAllowed($scope, '1', $this->recipientLimit);
}
private function isAllowed(string $scope, string $id, int $max): bool
{
if ($max <= 0) {
return true;
}
$bucket = (int) floor(time() / $this->windowSec);
$cacheKey = ['bot_push_rate', $scope, $id, $bucket];
$cache = Yii::$app->cache;
$count = (int) $cache->get($cacheKey);
if ($count >= $max) {
Yii::warning("BotPush rate limit hit: {$scope}={$id}", __METHOD__);
return false;
}
$cache->set($cacheKey, $count + 1, $this->windowSec + 5);
return true;
}
}