Files
skyview.astronomiemuseum.de/scripts/telegram/lib/telegram_api.php
T
2026-04-08 12:34:02 +02:00

300 lines
10 KiB
PHP

<?php
declare(strict_types=1);
/**
* Telegram-Transportfunktionen fuer Bot-Nachrichten und Dateiversand.
*
* Diese Datei kapselt die HTTP-Kommunikation mit der Telegram Bot API,
* sowohl fuer JSON-Requests als auch fuer Multipart-Uploads von Bildern
* und Dokumenten. Die Cron-Tasks muessen dadurch keine Request-Details kennen.
*/
function telegramHttpPostJson(string $url, array $payload): array
{
$jsonPayload = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($jsonPayload === false) {
return [
'ok' => false,
'error' => 'Die Telegram-Anfrage konnte nicht als JSON kodiert werden.',
'body' => null,
];
}
if (DIRECTORY_SEPARATOR === '\\' && function_exists('shell_exec')) {
$urlBase64 = base64_encode($url);
$payloadBase64 = base64_encode($jsonPayload);
$powerShellScript = str_replace(
['__URL_BASE64__', '__PAYLOAD_BASE64__'],
[$urlBase64, $payloadBase64],
<<<'PS'
$ErrorActionPreference = 'Stop'
$url = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__URL_BASE64__'))
$body = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__PAYLOAD_BASE64__'))
try {
$response = Invoke-RestMethod -Method Post -Uri $url -ContentType 'application/json' -Body $body
[PSCustomObject]@{
ok = $true
body = ($response | ConvertTo-Json -Depth 20 -Compress)
error = ''
} | ConvertTo-Json -Compress
} catch {
$stream = $null
$responseBody = ''
if ($_.Exception.Response) {
try {
$stream = $_.Exception.Response.GetResponseStream()
if ($stream) {
$reader = New-Object System.IO.StreamReader($stream)
$responseBody = $reader.ReadToEnd()
}
} catch {
} finally {
if ($stream) {
$stream.Dispose()
}
}
}
[PSCustomObject]@{
ok = $false
body = $responseBody
error = $_.Exception.Message
} | ConvertTo-Json -Compress
}
PS
);
$encodedCommandBinary = function_exists('iconv')
? iconv('UTF-8', 'UTF-16LE', $powerShellScript)
: $powerShellScript;
$encodedCommand = base64_encode($encodedCommandBinary === false ? $powerShellScript : $encodedCommandBinary);
$command = 'powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ' . $encodedCommand;
$rawOutput = shell_exec($command);
if ($rawOutput !== null && trim($rawOutput) !== '') {
$decodedShell = json_decode(trim($rawOutput), true);
if (is_array($decodedShell)) {
return [
'ok' => (bool) ($decodedShell['ok'] ?? false),
'error' => $decodedShell['error'] ?? null,
'body' => $decodedShell['body'] ?? null,
];
}
}
}
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Content-Length: ' . strlen($jsonPayload),
],
CURLOPT_POSTFIELDS => $jsonPayload,
CURLOPT_TIMEOUT => 20,
]);
$body = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'ok' => $body !== false,
'error' => $body === false ? $error : null,
'body' => $body !== false ? $body : null,
];
}
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => implode("\r\n", [
'Content-Type: application/json',
'Content-Length: ' . strlen($jsonPayload),
]),
'content' => $jsonPayload,
'timeout' => 20,
'ignore_errors' => true,
],
]);
$body = @file_get_contents($url, false, $context);
return [
'ok' => $body !== false,
'error' => $body === false ? 'HTTP-Request fehlgeschlagen.' : null,
'body' => $body !== false ? $body : null,
];
}
function telegramHttpPostMultipart(string $url, array $fields, string $fileField, string $filePath, ?string $fileName = null): array
{
if (!is_file($filePath)) {
return [
'ok' => false,
'error' => 'Datei fuer Telegram-Versand nicht gefunden: ' . $filePath,
'body' => null,
];
}
if (DIRECTORY_SEPARATOR === '\\' && function_exists('shell_exec')) {
$urlBase64 = base64_encode($url);
$filePathBase64 = base64_encode($filePath);
$fileNameBase64 = base64_encode((string) ($fileName ?? basename($filePath)));
$fieldsBase64 = base64_encode(json_encode($fields, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '{}');
$fileFieldBase64 = base64_encode($fileField);
$powerShellScript = str_replace(
['__URL_BASE64__', '__FILE_PATH_BASE64__', '__FILE_NAME_BASE64__', '__FIELDS_BASE64__', '__FILE_FIELD_BASE64__'],
[$urlBase64, $filePathBase64, $fileNameBase64, $fieldsBase64, $fileFieldBase64],
<<<'PS'
$ErrorActionPreference = 'Stop'
$url = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__URL_BASE64__'))
$filePath = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__FILE_PATH_BASE64__'))
$fileName = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__FILE_NAME_BASE64__'))
$fileField = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__FILE_FIELD_BASE64__'))
$fieldsJson = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('__FIELDS_BASE64__'))
$fieldsObject = $fieldsJson | ConvertFrom-Json
$form = @{}
foreach ($property in $fieldsObject.PSObject.Properties) {
$form[$property.Name] = [string] $property.Value
}
$form[$fileField] = Get-Item -LiteralPath $filePath
try {
$response = Invoke-RestMethod -Method Post -Uri $url -Form $form
[PSCustomObject]@{
ok = $true
body = ($response | ConvertTo-Json -Depth 20 -Compress)
error = ''
} | ConvertTo-Json -Compress
} catch {
[PSCustomObject]@{
ok = $false
body = ''
error = $_.Exception.Message
} | ConvertTo-Json -Compress
}
PS
);
$encodedCommandBinary = function_exists('iconv')
? iconv('UTF-8', 'UTF-16LE', $powerShellScript)
: $powerShellScript;
$encodedCommand = base64_encode($encodedCommandBinary === false ? $powerShellScript : $encodedCommandBinary);
$command = 'powershell -NoProfile -ExecutionPolicy Bypass -EncodedCommand ' . $encodedCommand;
$rawOutput = shell_exec($command);
if ($rawOutput !== null && trim($rawOutput) !== '') {
$decodedShell = json_decode(trim($rawOutput), true);
if (is_array($decodedShell)) {
return [
'ok' => (bool) ($decodedShell['ok'] ?? false),
'error' => $decodedShell['error'] ?? null,
'body' => $decodedShell['body'] ?? null,
];
}
}
}
if (function_exists('curl_init')) {
$payload = $fields;
$extension = strtolower((string) pathinfo($filePath, PATHINFO_EXTENSION));
$mimeType = match ($extension) {
'png' => 'image/png',
'jpg', 'jpeg' => 'image/jpeg',
'svg' => 'image/svg+xml',
default => 'application/octet-stream',
};
$payload[$fileField] = new CURLFile($filePath, $mimeType, $fileName ?? basename($filePath));
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_TIMEOUT => 30,
]);
$body = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
return [
'ok' => $body !== false,
'error' => $body === false ? $error : null,
'body' => $body !== false ? $body : null,
];
}
return [
'ok' => false,
'error' => 'Multipart-Versand ist ohne curl nicht verfuegbar.',
'body' => null,
];
}
function telegramSendBotMessage(array $telegramConfig, string $chatId, string $messageText): bool
{
$botToken = trim((string) ($telegramConfig['bot_token'] ?? ''));
if ($botToken === '' || $chatId === '' || $messageText === '') {
return false;
}
$response = telegramHttpPostJson(
'https://api.telegram.org/bot' . $botToken . '/sendMessage',
[
'chat_id' => $chatId,
'text' => appendTelegramServiceFooter($messageText),
]
);
return (bool) ($response['ok'] ?? false);
}
function telegramSendBotDocument(array $telegramConfig, string $chatId, string $filePath, string $caption = ''): bool
{
$botToken = trim((string) ($telegramConfig['bot_token'] ?? ''));
if ($botToken === '' || $chatId === '' || !is_file($filePath)) {
return false;
}
$response = telegramHttpPostMultipart(
'https://api.telegram.org/bot' . $botToken . '/sendDocument',
[
'chat_id' => $chatId,
'caption' => appendTelegramServiceFooter($caption),
],
'document',
$filePath,
basename($filePath)
);
return (bool) ($response['ok'] ?? false);
}
function telegramSendBotPhoto(array $telegramConfig, string $chatId, string $filePath, string $caption = ''): bool
{
$botToken = trim((string) ($telegramConfig['bot_token'] ?? ''));
if ($botToken === '' || $chatId === '' || !is_file($filePath)) {
return false;
}
$response = telegramHttpPostMultipart(
'https://api.telegram.org/bot' . $botToken . '/sendPhoto',
[
'chat_id' => $chatId,
'caption' => appendTelegramServiceFooter($caption),
],
'photo',
$filePath,
basename($filePath)
);
return (bool) ($response['ok'] ?? false);
}