Skip to content

Instantly share code, notes, and snippets.

@A35G
Created September 30, 2026 21:57
Show Gist options
  • Select an option

  • Save A35G/af301c31014b1073c10714e664866d0f to your computer and use it in GitHub Desktop.

Select an option

Save A35G/af301c31014b1073c10714e664866d0f to your computer and use it in GitHub Desktop.
Contatore di numero di download di una Libreria, dal portale Packagist con invio di Notifiche ad un Bot Telegram
<?php
declare(strict_types=1);
/*
* Packagist 2 Telegram notifier
*
* Requisiti:
* - PHP 8+
* - cURL
* - un bot Telegram
*
* Esecuzione:
* php packagist-telegram.php
*/
$package = 'vendor/nome-pacchetto';
$telegramBotToken = 'BOT_TOKEN';
$telegramChatId = 'CHAT_ID';
$stateFile = __DIR__ . '/packagist-state.json';
function queryJson(string $url): array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'User-Agent: PackagistTelegramNotifier/1.0',
],
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new RuntimeException('cURL error: ' . $error);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode < 200 OR $statusCode >= 300) throw new RuntimeException("HTTP error {$statusCode} ricevuto da Packagist");
$data = json_decode($response, true);
if (!is_array($data)) throw new RuntimeException('Risposta JSON non valida.');
return $data;
}
function send2Telegram(string $botToken, string $chatId, string $message): void
{
$url = "https://api.telegram.org/bot{$botToken}/sendMessage";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_POSTFIELDS => [
'chat_id' => $chatId,
'text' => $message,
'parse_mode' => 'HTML',
]
]);
$response = curl_exec($ch);
if ($response === false) {
$error = curl_error($ch);
curl_close($ch);
throw new RuntimeException('Errore Telegram: ' . $error);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode < 200 OR $statusCode >= 300) throw new RuntimeException("Telegram ha restituito HTTP {$statusCode}: {$response}");
}
function loadState(string $file): ?int
{
if (!file_exists($file)) return;
$content = file_get_contents($file);
if ($content === false) return;
$data = json_decode($content, true);
if (!is_array($data) OR !isset($data['downloads'])) return;
return (int)$data['downloads'];
}
function saveState(string $file, int $downloads): void
{
$data = [
'downloads' => $downloads,
'updated_at' => date(DATE_ATOM),
];
file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX);
}
try {
$url = 'https://packagist.org/packages/' . rawurlencode($package) . '.json';
$data = queryJson($url);
if (!isset($data['package']['downloads']['total'])) throw new RuntimeException('Il campo downloads.total non è presente nella risposta di Packagist.');
$currentDownloads = (int)$data['package']['downloads']['total'];
$previousDownloads = loadState($stateFile);
if ($previousDownloads === null) {
saveState($stateFile, $currentDownloads);
echo "Prima esecuzione.\n";
echo "Download attuali: {$currentDownloads}\n";
exit(0);
}
if ($currentDownloads <= $previousDownloads) {
echo sprintf("[%s] Nessun nuovo download. Totale: %d\n", date('Y-m-d H:i:s'), $currentDownloads);
saveState($stateFile, $currentDownloads);
exit(0);
}
$newDownloads = $currentDownloads - $previousDownloads;
$message = sprintf("<b>%s</b>\n\n<b>+%d nuovi download</b>\nTotale: <b>%d</b>\n %s", htmlspecialchars($package, ENT_QUOTES, 'UTF-8'), $newDownloads, $currentDownloads, date('d/m/Y H:i:s'));
send2Telegram($telegramBotToken, $telegramChatId, $message);
saveState($stateFile, $currentDownloads);
echo sprintf("[%s] Notifica inviata: +%d download. Totale: %d\n", date('Y-m-d H:i:s'), $newDownloads, $currentDownloads);
} catch (Throwable $e) {
fwrite(STDERR, '[' . date('Y-m-d H:i:s') . '] ERRORE: ' . $e->getMessage() . PHP_EOL);
exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment