2024-11-01 15:47:02 -04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace TgBotLib;
|
|
|
|
|
2024-11-04 00:56:21 -05:00
|
|
|
use RuntimeException;
|
2024-11-04 22:04:58 -05:00
|
|
|
use function RuntimeException;
|
2024-11-01 15:47:02 -04:00
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* PollingBot class that extends Bot for handling updates using polling.
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
class PollingBot extends Bot
|
|
|
|
{
|
|
|
|
private ?int $offset;
|
|
|
|
private int $limit;
|
|
|
|
private int $timeout;
|
|
|
|
private array $allowedUpdates;
|
2024-11-04 00:56:21 -05:00
|
|
|
private bool $fork;
|
2024-11-04 22:04:58 -05:00
|
|
|
private array $childPids;
|
2024-11-01 15:47:02 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor for the class, initializing with a Bot instance.
|
|
|
|
*
|
|
|
|
* @param string $token
|
|
|
|
* @param string $endpoint
|
|
|
|
*/
|
|
|
|
public function __construct(string $token, string $endpoint='https://api.telegram.org')
|
|
|
|
{
|
|
|
|
parent::__construct($token, $endpoint);
|
|
|
|
$this->offset = null;
|
|
|
|
$this->limit = 100;
|
|
|
|
$this->timeout = 0;
|
|
|
|
$this->allowedUpdates = [];
|
2024-11-04 00:56:21 -05:00
|
|
|
$this->fork = false;
|
2024-11-04 22:04:58 -05:00
|
|
|
$this->childPids = [];
|
|
|
|
|
|
|
|
// Register signal handler for child processes
|
|
|
|
if (function_exists('pcntl_signal'))
|
|
|
|
{
|
|
|
|
pcntl_signal(SIGCHLD, [$this, 'signalHandler']);
|
|
|
|
}
|
2024-11-01 15:47:02 -04:00
|
|
|
}
|
|
|
|
|
2024-11-04 00:56:21 -05:00
|
|
|
/**
|
|
|
|
* Sets the offset value.
|
|
|
|
*
|
|
|
|
* @param int $offset The offset value to be set.
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function setOffset(int $offset): void
|
|
|
|
{
|
|
|
|
$this->offset = $offset;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Retrieves the current offset value.
|
|
|
|
*
|
|
|
|
* @return int The current offset.
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function getOffset(): int
|
|
|
|
{
|
|
|
|
return $this->offset;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Sets the limit for the number of items to process.
|
|
|
|
*
|
|
|
|
* @param int $limit The maximum number of items to be processed.
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function setLimit(int $limit): void
|
|
|
|
{
|
|
|
|
$this->limit = $limit;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Retrieves the limit for the current operation.
|
|
|
|
*
|
|
|
|
* @return int The limit value.
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function getLimit(): int
|
|
|
|
{
|
|
|
|
return $this->limit;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Sets the timeout value.
|
|
|
|
*
|
|
|
|
* @param int $timeout The timeout value to set.
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function setTimeout(int $timeout): void
|
|
|
|
{
|
|
|
|
$this->timeout = $timeout;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Retrieves the current timeout setting.
|
|
|
|
*
|
|
|
|
* @return int The configured timeout value.
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function getTimeout(): int
|
|
|
|
{
|
|
|
|
return $this->timeout;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Sets the list of allowed updates.
|
|
|
|
*
|
|
|
|
* @param array $allowedUpdates The array of update types that should be allowed.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function setAllowedUpdates(array $allowedUpdates): void
|
|
|
|
{
|
|
|
|
$this->allowedUpdates = $allowedUpdates;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAllowedUpdates(): array
|
|
|
|
{
|
|
|
|
return $this->allowedUpdates;
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Adds a new allowed update type to the list if it does not already exist.
|
|
|
|
*
|
|
|
|
* @param string $allowedUpdate The type of update to allow.
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function addAllowedUpdate(string $allowedUpdate): void
|
|
|
|
{
|
|
|
|
if(in_array($allowedUpdate, $this->allowedUpdates))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->allowedUpdates[] = $allowedUpdate;
|
|
|
|
}
|
|
|
|
|
2024-11-04 00:56:21 -05:00
|
|
|
/**
|
2024-11-04 22:04:58 -05:00
|
|
|
* Sets the fork value.
|
2024-11-04 00:56:21 -05:00
|
|
|
*
|
2024-11-04 22:04:58 -05:00
|
|
|
* @param bool $fork The fork value to set.
|
2024-11-04 00:56:21 -05:00
|
|
|
* @return void
|
|
|
|
*/
|
|
|
|
public function setFork(bool $fork): void
|
|
|
|
{
|
|
|
|
$this->fork = $fork;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-11-04 22:04:58 -05:00
|
|
|
* Retrieves the current fork setting.
|
2024-11-04 00:56:21 -05:00
|
|
|
*
|
2024-11-04 22:04:58 -05:00
|
|
|
* @return bool The configured fork value.
|
2024-11-04 00:56:21 -05:00
|
|
|
*/
|
|
|
|
public function getFork(): bool
|
|
|
|
{
|
|
|
|
return $this->fork;
|
|
|
|
}
|
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
private function signalHandler(int $signal): void
|
|
|
|
{
|
|
|
|
if ($signal === SIGCHLD)
|
|
|
|
{
|
|
|
|
$i = -1;
|
|
|
|
while (($pid = pcntl_wait($i, WNOHANG)) > 0)
|
|
|
|
{
|
|
|
|
$key = array_search($pid, $this->childPids);
|
|
|
|
if ($key !== false)
|
|
|
|
{
|
|
|
|
unset($this->childPids[$key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Handles incoming updates by fetching and processing them with appropriate event handlers.
|
|
|
|
*
|
|
|
|
* @return void
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
public function handleUpdates(): void
|
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
// Install signal handler
|
|
|
|
if ($this->fork && function_exists('pcntl_signal_dispatch'))
|
|
|
|
{
|
|
|
|
pcntl_signal_dispatch();
|
|
|
|
}
|
|
|
|
|
2024-11-04 00:56:21 -05:00
|
|
|
$updates = $this->getUpdates(offset: ($this->offset ?: 0), limit: $this->limit, timeout: $this->timeout, allowed_updates: $this->retrieveAllowedUpdates());
|
|
|
|
|
|
|
|
if (empty($updates))
|
2024-11-01 15:47:02 -04:00
|
|
|
{
|
2024-11-04 00:56:21 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
// Track the highest update ID we've seen
|
|
|
|
$maxUpdateId = null;
|
2024-11-04 00:56:21 -05:00
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
foreach ($updates as $update)
|
2024-11-04 00:56:21 -05:00
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
// Update the maximum update ID as we go
|
|
|
|
if ($maxUpdateId === null || $update->getUpdateId() > $maxUpdateId)
|
2024-11-04 13:49:50 -05:00
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
$maxUpdateId = $update->getUpdateId();
|
2024-11-04 13:49:50 -05:00
|
|
|
}
|
2024-11-04 00:56:21 -05:00
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
if ($this->fork)
|
|
|
|
{
|
|
|
|
// Clean up any finished processes
|
|
|
|
if (function_exists('pcntl_signal_dispatch'))
|
|
|
|
{
|
|
|
|
pcntl_signal_dispatch();
|
|
|
|
}
|
2024-11-04 00:56:21 -05:00
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
$pid = pcntl_fork();
|
|
|
|
|
|
|
|
if ($pid == -1)
|
|
|
|
{
|
|
|
|
// Fork failed
|
|
|
|
throw new RuntimeException('Failed to fork process for update handling');
|
|
|
|
}
|
|
|
|
elseif ($pid)
|
|
|
|
{
|
|
|
|
// Parent process
|
|
|
|
$this->childPids[] = $pid;
|
|
|
|
|
|
|
|
// If we have too many child processes, wait for some to finish
|
|
|
|
$maxChildren = 32; // Adjust this value based on your system's capabilities
|
|
|
|
while (count($this->childPids) >= $maxChildren)
|
|
|
|
{
|
|
|
|
if (function_exists('pcntl_signal_dispatch'))
|
|
|
|
{
|
|
|
|
pcntl_signal_dispatch();
|
|
|
|
}
|
|
|
|
|
|
|
|
usleep(10000); // Sleep for 10ms to prevent CPU hogging
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Child process
|
|
|
|
try
|
|
|
|
{
|
|
|
|
$this->handleUpdate($update);
|
|
|
|
}
|
|
|
|
finally
|
|
|
|
{
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$this->handleUpdate($update);
|
|
|
|
}
|
2024-11-04 00:56:21 -05:00
|
|
|
}
|
2024-11-04 22:04:58 -05:00
|
|
|
|
|
|
|
// Update the offset based on the highest update ID we've seen
|
|
|
|
if ($maxUpdateId !== null)
|
2024-11-04 00:56:21 -05:00
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
$this->offset = $maxUpdateId + 1;
|
2024-11-04 00:56:21 -05:00
|
|
|
}
|
2024-11-04 22:04:58 -05:00
|
|
|
|
|
|
|
// If forking is enabled, ensure we clean up any remaining child processes
|
|
|
|
if ($this->fork)
|
2024-11-04 00:56:21 -05:00
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
// Wait for remaining child processes to finish
|
|
|
|
while (!empty($this->childPids))
|
2024-11-01 15:47:02 -04:00
|
|
|
{
|
2024-11-04 22:04:58 -05:00
|
|
|
if (function_exists('pcntl_signal_dispatch'))
|
|
|
|
{
|
|
|
|
pcntl_signal_dispatch();
|
|
|
|
}
|
2024-11-01 15:47:02 -04:00
|
|
|
|
2024-11-04 22:04:58 -05:00
|
|
|
usleep(10000); // Sleep for 10ms to prevent CPU hogging
|
|
|
|
}
|
2024-11-01 15:47:02 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-01 21:25:50 -04:00
|
|
|
/**
|
|
|
|
* Retrieves the allowed updates.
|
|
|
|
*
|
|
|
|
* @return array|null Returns an array of allowed updates if available, otherwise null.
|
|
|
|
*/
|
2024-11-01 15:47:02 -04:00
|
|
|
private function retrieveAllowedUpdates(): ?array
|
|
|
|
{
|
2024-11-04 00:56:21 -05:00
|
|
|
if (count($this->allowedUpdates) === 0)
|
2024-11-01 15:47:02 -04:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->allowedUpdates;
|
|
|
|
}
|
|
|
|
}
|