355 lines
21 KiB
PHP
355 lines
21 KiB
PHP
<?php
|
||
// Community-Bot: Discord- und Telegram-Anbindung. Alle Funktionen beginnen mit communitybot_.
|
||
|
||
const COMMUNITYBOT = 'community-bot';
|
||
|
||
function communitybot_config(): array
|
||
{
|
||
$secret = static function (string $key): string {
|
||
$v = (string) plugin_setting(COMMUNITYBOT, $key, '');
|
||
if ($v === '') return '';
|
||
try { return decrypt_secret($v); } catch (Throwable $e) { return ''; }
|
||
};
|
||
return [
|
||
'discord_key' => strtolower(trim((string) plugin_setting(COMMUNITYBOT, 'discord_key', ''))),
|
||
'discord_app' => trim((string) plugin_setting(COMMUNITYBOT, 'discord_app', '')),
|
||
'discord_token' => $secret('discord_token'),
|
||
'telegram_token' => $secret('telegram_token'),
|
||
'telegram_secret' => $secret('telegram_secret'),
|
||
'department_id' => (int) plugin_setting(COMMUNITYBOT, 'department_id', '0'),
|
||
'priority_id' => (int) plugin_setting(COMMUNITYBOT, 'priority_id', '0'),
|
||
'limit' => max(1, min(50, (int) plugin_setting(COMMUNITYBOT, 'limit', '3'))),
|
||
'notify' => plugin_setting(COMMUNITYBOT, 'notify', '1') === '1',
|
||
];
|
||
}
|
||
|
||
function communitybot_set_secret(string $key, string $value): void
|
||
{
|
||
plugin_set_setting(COMMUNITYBOT, $key, $value === '' ? '' : encrypt_secret($value));
|
||
}
|
||
|
||
/** HTTP-Aufruf (für Tests austauschbar über $GLOBALS['communitybot_http']). Liefert ['status' => int, 'body' => string]. */
|
||
function communitybot_http(string $method, string $url, array $headers = [], ?string $body = null): array
|
||
{
|
||
if (isset($GLOBALS['communitybot_http']) && is_callable($GLOBALS['communitybot_http'])) {
|
||
return ($GLOBALS['communitybot_http'])($method, $url, $headers, $body);
|
||
}
|
||
$ctx = stream_context_create(['http' => [
|
||
'method' => $method,
|
||
'header' => implode("\r\n", $headers),
|
||
'content' => $body ?? '',
|
||
'timeout' => 8,
|
||
'ignore_errors' => true,
|
||
]]);
|
||
$res = @file_get_contents($url, false, $ctx);
|
||
$status = 0;
|
||
foreach ($http_response_header ?? [] as $h) if (preg_match('#^HTTP/\S+\s+(\d+)#', $h, $m)) $status = (int) $m[1];
|
||
return ['status' => $status, 'body' => $res === false ? '' : (string) $res];
|
||
}
|
||
|
||
function communitybot_json(string $method, string $url, array $headers, ?array $data): array
|
||
{
|
||
$r = communitybot_http($method, $url, array_merge(['Content-Type: application/json', 'User-Agent: DeskHub-CommunityBot (https://deskhub.example, 1.0)'], $headers), $data === null ? null : json_encode($data, JSON_UNESCAPED_UNICODE));
|
||
return ['status' => $r['status'], 'json' => json_decode($r['body'], true) ?: [], 'body' => $r['body']];
|
||
}
|
||
|
||
function communitybot_clip(string $text, int $max): string
|
||
{
|
||
$text = trim(preg_replace('/\s+/u', ' ', strip_tags(html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'))));
|
||
return mb_strlen($text) > $max ? mb_substr($text, 0, $max - 1) . '…' : $text;
|
||
}
|
||
|
||
// ── Gemeinsame Befehle ─────────────────────────────────────────────────────
|
||
|
||
/** Platzhalter-Adresse; Mails an ".invalid" werden vom Kern nie verschickt. */
|
||
function communitybot_email(string $platform, string $user): string
|
||
{
|
||
return $platform . '-' . preg_replace('/[^a-z0-9]/i', '', $user) . '@' . $platform . '.invalid';
|
||
}
|
||
|
||
function communitybot_find_link(PDO $pdo, string $platform, string $user, string $number): ?array
|
||
{
|
||
$number = ltrim(trim($number), '#');
|
||
if ($number === '') return null;
|
||
$s = $pdo->prepare('SELECT t.*, l.ext_chat FROM plugin_communitybot_links l JOIN tickets t ON t.id = l.ticket_id WHERE l.platform = ? AND l.ext_user = ? AND t.ticket_number = ? ORDER BY l.id DESC LIMIT 1');
|
||
$s->execute([$platform, $user, $number]);
|
||
return $s->fetch() ?: null;
|
||
}
|
||
|
||
function communitybot_help(string $platform): string
|
||
{
|
||
$p = $platform === 'telegram' ? '/ticket Betreff | Nachricht' : '/ticket betreff:… nachricht:…';
|
||
return "So geht's:\n{$p} – neues Ticket anlegen\n/status Nummer – Stand eines Tickets\n/antwort Nummer Text – Ihrem Ticket etwas hinzufügen";
|
||
}
|
||
|
||
function communitybot_cmd_ticket(PDO $pdo, string $platform, string $user, string $chat, string $name, string $subject, string $message): string
|
||
{
|
||
$cfg = communitybot_config();
|
||
$subject = trim($subject);
|
||
$message = trim($message);
|
||
if ($message === '' && $subject !== '') { $message = $subject; $subject = ''; }
|
||
if ($message === '') return 'Bitte beschreiben Sie Ihr Anliegen: ' . ($platform === 'telegram' ? '/ticket Betreff | Nachricht' : '/ticket mit Betreff und Nachricht');
|
||
if ($subject === '') $subject = communitybot_clip($message, 60);
|
||
$subject = mb_substr($subject, 0, 200);
|
||
$message = mb_substr($message, 0, 4000);
|
||
|
||
$c = $pdo->prepare('SELECT COUNT(*) FROM plugin_communitybot_links WHERE platform = ? AND ext_user = ? AND created_at >= ?');
|
||
$c->execute([$platform, $user, date('Y-m-d H:i:s', time() - 3600)]);
|
||
if ((int) $c->fetchColumn() >= $cfg['limit']) return 'Sie haben in der letzten Stunde schon ' . $cfg['limit'] . ' Tickets angelegt. Bitte versuchen Sie es später noch einmal oder ergänzen Sie ein bestehendes Ticket mit /antwort.';
|
||
|
||
$label = $platform === 'discord' ? 'Discord' : 'Telegram';
|
||
$name = trim(preg_replace('/[\x00-\x1F]+/u', ' ', $name));
|
||
if ($name === '') $name = $label . '-Nutzer';
|
||
try {
|
||
$r = public_ticket_create($pdo, [
|
||
'name' => mb_substr($name, 0, 120),
|
||
'email' => communitybot_email($platform, $user),
|
||
'subject' => $subject,
|
||
'message' => $message,
|
||
'departmentId' => $cfg['department_id'] ?: null,
|
||
'priorityId' => $cfg['priority_id'] ?: null,
|
||
], '0.0.0.0', ['activity' => 'Ticket über ' . $label . ' erstellt', 'tag' => $label]);
|
||
} catch (AdminModelException $e) {
|
||
return 'Das Ticket konnte nicht angelegt werden: ' . $e->getMessage();
|
||
} catch (Throwable $e) {
|
||
error_log('communitybot ticket: ' . $e->getMessage());
|
||
return 'Das Ticket konnte gerade nicht angelegt werden. Bitte versuchen Sie es später noch einmal.';
|
||
}
|
||
$pdo->prepare('INSERT INTO plugin_communitybot_links (ticket_id, platform, ext_user, ext_chat, ext_name, created_at) VALUES (?,?,?,?,?,?)')
|
||
->execute([$r['id'], $platform, $user, $chat, mb_substr($name, 0, 120), db_now()]);
|
||
$hint = $cfg['notify'] ? ' Wir melden uns hier, sobald es eine Antwort gibt.' : '';
|
||
return 'Ihr Ticket #' . $r['number'] . ' wurde angelegt.' . $hint . ' Stand abfragen mit /status ' . $r['number'] . '.';
|
||
}
|
||
|
||
function communitybot_cmd_status(PDO $pdo, string $platform, string $user, string $number): string
|
||
{
|
||
if (trim($number) === '') {
|
||
$s = $pdo->prepare('SELECT t.ticket_number, t.subject, t.status FROM plugin_communitybot_links l JOIN tickets t ON t.id = l.ticket_id WHERE l.platform = ? AND l.ext_user = ? ORDER BY l.id DESC LIMIT 5');
|
||
$s->execute([$platform, $user]);
|
||
$rows = $s->fetchAll();
|
||
if (!$rows) return 'Sie haben über diesen Kanal noch kein Ticket angelegt.';
|
||
return "Ihre letzten Tickets:\n" . implode("\n", array_map(fn($t) => '#' . $t['ticket_number'] . ' – ' . status_label((string) $t['status'])[0] . ' – ' . communitybot_clip((string) $t['subject'], 50), $rows));
|
||
}
|
||
$t = communitybot_find_link($pdo, $platform, $user, $number);
|
||
if (!$t) return 'Dieses Ticket gibt es nicht oder es gehört nicht zu Ihnen.';
|
||
return 'Ticket #' . $t['ticket_number'] . ' „' . communitybot_clip((string) $t['subject'], 60) . '“: ' . status_label((string) $t['status'])[0] . ' (zuletzt geändert ' . format_dt((string) $t['updated_at']) . ').';
|
||
}
|
||
|
||
function communitybot_cmd_reply(PDO $pdo, string $platform, string $user, string $number, string $text): string
|
||
{
|
||
$text = trim($text);
|
||
if (trim($number) === '' || $text === '') return 'So geht es: /antwort Ticketnummer Ihre Nachricht';
|
||
$t = communitybot_find_link($pdo, $platform, $user, $number);
|
||
if (!$t) return 'Dieses Ticket gibt es nicht oder es gehört nicht zu Ihnen.';
|
||
if ($t['status'] === 'closed') return 'Das Ticket #' . $t['ticket_number'] . ' ist geschlossen. Bitte legen Sie mit /ticket ein neues an.';
|
||
$now = db_now();
|
||
$pdo->prepare('INSERT INTO ticket_threads (ticket_id, author_type, message, is_internal_note, created_at) VALUES (?,?,?,0,?)')->execute([$t['id'], 'customer', mb_substr($text, 0, 4000), $now]);
|
||
$reopen = in_array($t['status'], ['answered', 'resolved'], true);
|
||
$new = $reopen ? 'open' : $t['status'];
|
||
$pdo->prepare('UPDATE tickets SET status = ?, updated_at = ? WHERE id = ?')->execute([$new, $now, $t['id']]);
|
||
log_activity($pdo, (int) $t['id'], null, 'Kunde hat geantwortet (' . ($platform === 'discord' ? 'Discord' : 'Telegram') . ')');
|
||
if ($reopen) log_activity($pdo, (int) $t['id'], null, 'Ticket wieder geöffnet');
|
||
webhook_dispatch($pdo, 'ticket.replied', (int) $t['id'], ['author' => 'customer']);
|
||
if ($new !== $t['status']) webhook_dispatch($pdo, 'ticket.status_changed', (int) $t['id'], ['previousStatus' => $t['status']]);
|
||
return 'Ihre Nachricht wurde zu Ticket #' . $t['ticket_number'] . ' hinzugefügt.';
|
||
}
|
||
|
||
// ── Discord ────────────────────────────────────────────────────────────────
|
||
|
||
function communitybot_discord_verify(array $req, string $publicKeyHex): bool
|
||
{
|
||
$sig = (string) ($req['headers']['x-signature-ed25519'] ?? '');
|
||
$ts = (string) ($req['headers']['x-signature-timestamp'] ?? '');
|
||
if ($publicKeyHex === '' || !ctype_xdigit($publicKeyHex) || strlen($publicKeyHex) !== 64 || !ctype_xdigit($sig) || strlen($sig) !== 128 || !ctype_digit($ts)) return false;
|
||
if (abs(time() - (int) $ts) > 600) return false;
|
||
if (!function_exists('sodium_crypto_sign_verify_detached')) return false;
|
||
try {
|
||
return sodium_crypto_sign_verify_detached(hex2bin($sig), $ts . $req['body'], hex2bin($publicKeyHex));
|
||
} catch (Throwable $e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function communitybot_discord(array $req): array
|
||
{
|
||
$cfg = communitybot_config();
|
||
if (!communitybot_discord_verify($req, $cfg['discord_key'])) return ['status' => 401, 'json' => ['error' => 'Ungültige Signatur.']];
|
||
$in = json_decode($req['body'], true);
|
||
if (!is_array($in)) return ['status' => 400, 'json' => ['error' => 'Ungültige Anfrage.']];
|
||
if (($in['type'] ?? 0) === 1) return ['status' => 200, 'json' => ['type' => 1]];
|
||
if (($in['type'] ?? 0) !== 2) return ['status' => 200, 'json' => ['type' => 4, 'data' => ['content' => 'Das wird nicht unterstützt.', 'flags' => 64]]];
|
||
|
||
$user = $in['member']['user'] ?? $in['user'] ?? [];
|
||
$uid = (string) ($user['id'] ?? '');
|
||
if (!preg_match('/^\d{5,25}$/', $uid)) return ['status' => 400, 'json' => ['error' => 'Kein Nutzer.']];
|
||
$name = (string) ($user['global_name'] ?? $user['username'] ?? '');
|
||
$opt = [];
|
||
foreach ((array) ($in['data']['options'] ?? []) as $o) if (isset($o['name'])) $opt[(string) $o['name']] = (string) ($o['value'] ?? '');
|
||
$pdo = get_db();
|
||
$chat = (string) ($in['channel_id'] ?? '');
|
||
switch ((string) ($in['data']['name'] ?? '')) {
|
||
case 'ticket': $text = communitybot_cmd_ticket($pdo, 'discord', $uid, $chat, $name, $opt['betreff'] ?? '', $opt['nachricht'] ?? ''); break;
|
||
case 'status': $text = communitybot_cmd_status($pdo, 'discord', $uid, $opt['nummer'] ?? ''); break;
|
||
case 'antwort': $text = communitybot_cmd_reply($pdo, 'discord', $uid, $opt['nummer'] ?? '', $opt['text'] ?? ''); break;
|
||
default: $text = communitybot_help('discord');
|
||
}
|
||
// flags 64: nur der Absender sieht die Antwort; keine Erwähnungen aus Nutzertext
|
||
return ['status' => 200, 'json' => ['type' => 4, 'data' => ['content' => mb_substr($text, 0, 1900), 'flags' => 64, 'allowed_mentions' => ['parse' => []]]]];
|
||
}
|
||
|
||
function communitybot_discord_commands(): array
|
||
{
|
||
$str = fn(string $n, string $d, bool $req, int $max = 1000) => ['type' => 3, 'name' => $n, 'description' => $d, 'required' => $req, 'max_length' => $max];
|
||
return [
|
||
['name' => 'ticket', 'description' => 'Ein Support-Ticket anlegen', 'options' => [$str('betreff', 'Worum geht es?', true, 100), $str('nachricht', 'Bitte beschreiben Sie Ihr Anliegen', true, 1000)]],
|
||
['name' => 'status', 'description' => 'Stand eines Tickets abfragen', 'options' => [$str('nummer', 'Ticketnummer (leer lassen für Ihre letzten)', false, 40)]],
|
||
['name' => 'antwort', 'description' => 'Zu einem Ihrer Tickets etwas hinzufügen', 'options' => [$str('nummer', 'Ticketnummer', true, 40), $str('text', 'Ihre Nachricht', true, 1000)]],
|
||
];
|
||
}
|
||
|
||
function communitybot_discord_register(): array
|
||
{
|
||
$cfg = communitybot_config();
|
||
if ($cfg['discord_app'] === '' || $cfg['discord_token'] === '') return ['ok' => false, 'message' => 'Bitte Anwendungs-ID und Bot-Token eintragen.'];
|
||
$r = communitybot_json('PUT', 'https://discord.com/api/v10/applications/' . rawurlencode($cfg['discord_app']) . '/commands', ['Authorization: Bot ' . $cfg['discord_token']], communitybot_discord_commands());
|
||
if ($r['status'] >= 200 && $r['status'] < 300) return ['ok' => true, 'message' => 'Die Befehle /ticket, /status und /antwort sind bei Discord eingetragen (es kann bis zu einer Stunde dauern, bis sie überall erscheinen).'];
|
||
return ['ok' => false, 'message' => 'Discord hat abgelehnt (Status ' . $r['status'] . '): ' . communitybot_clip((string) ($r['json']['message'] ?? $r['body']), 200)];
|
||
}
|
||
|
||
function communitybot_discord_dm(string $userId, string $text): bool
|
||
{
|
||
$cfg = communitybot_config();
|
||
if ($cfg['discord_token'] === '') return false;
|
||
$h = ['Authorization: Bot ' . $cfg['discord_token']];
|
||
$ch = communitybot_json('POST', 'https://discord.com/api/v10/users/@me/channels', $h, ['recipient_id' => $userId]);
|
||
$id = (string) ($ch['json']['id'] ?? '');
|
||
if ($id === '') return false;
|
||
$m = communitybot_json('POST', 'https://discord.com/api/v10/channels/' . rawurlencode($id) . '/messages', $h, ['content' => mb_substr($text, 0, 1900), 'allowed_mentions' => ['parse' => []]]);
|
||
return $m['status'] >= 200 && $m['status'] < 300;
|
||
}
|
||
|
||
// ── Telegram ───────────────────────────────────────────────────────────────
|
||
|
||
function communitybot_telegram(array $req): array
|
||
{
|
||
$cfg = communitybot_config();
|
||
$given = (string) ($req['headers']['x-telegram-bot-api-secret-token'] ?? '');
|
||
if ($cfg['telegram_secret'] === '' || !hash_equals($cfg['telegram_secret'], $given)) return ['status' => 401, 'json' => ['error' => 'Ungültiges Token.']];
|
||
$in = json_decode($req['body'], true);
|
||
$msg = is_array($in) ? ($in['message'] ?? null) : null;
|
||
if (!is_array($msg) || !isset($msg['chat']['id'], $msg['from']['id']) || !is_string($msg['text'] ?? null)) return ['status' => 200, 'json' => new stdClass()];
|
||
if (!empty($msg['from']['is_bot'])) return ['status' => 200, 'json' => new stdClass()];
|
||
if (!preg_match('/^\/([a-z_]+)(?:@\w+)?(?:\s+(.*))?$/isu', trim($msg['text']), $m)) return ['status' => 200, 'json' => new stdClass()];
|
||
|
||
$uid = (string) $msg['from']['id'];
|
||
$chat = (string) $msg['chat']['id'];
|
||
$name = trim(((string) ($msg['from']['first_name'] ?? '')) . ' ' . ((string) ($msg['from']['last_name'] ?? '')));
|
||
if ($name === '') $name = (string) ($msg['from']['username'] ?? '');
|
||
$arg = trim((string) ($m[2] ?? ''));
|
||
$pdo = get_db();
|
||
switch (strtolower($m[1])) {
|
||
case 'ticket':
|
||
$parts = array_map('trim', explode('|', $arg, 2));
|
||
$text = communitybot_cmd_ticket($pdo, 'telegram', $uid, $chat, $name, count($parts) === 2 ? $parts[0] : '', count($parts) === 2 ? $parts[1] : $parts[0]);
|
||
break;
|
||
case 'status': $text = communitybot_cmd_status($pdo, 'telegram', $uid, $arg); break;
|
||
case 'antwort':
|
||
$p = preg_split('/\s+/u', $arg, 2) ?: [''];
|
||
$text = communitybot_cmd_reply($pdo, 'telegram', $uid, $p[0] ?? '', $p[1] ?? '');
|
||
break;
|
||
case 'start': case 'hilfe': case 'help': $text = "Hallo! Ich nehme Ihre Support-Anfragen entgegen.\n" . communitybot_help('telegram'); break;
|
||
default: return ['status' => 200, 'json' => new stdClass()];
|
||
}
|
||
// Antwort direkt im Webhook-Ergebnis: kein zusätzlicher Aufruf nötig
|
||
$out = ['method' => 'sendMessage', 'chat_id' => $msg['chat']['id'], 'text' => mb_substr($text, 0, 3900)];
|
||
if (isset($msg['message_id'])) $out['reply_to_message_id'] = $msg['message_id'];
|
||
return ['status' => 200, 'json' => $out];
|
||
}
|
||
|
||
function communitybot_telegram_send(string $chatId, string $text): bool
|
||
{
|
||
$cfg = communitybot_config();
|
||
if ($cfg['telegram_token'] === '') return false;
|
||
$r = communitybot_json('POST', 'https://api.telegram.org/bot' . $cfg['telegram_token'] . '/sendMessage', [], ['chat_id' => $chatId, 'text' => mb_substr($text, 0, 3900)]);
|
||
return $r['status'] >= 200 && $r['status'] < 300;
|
||
}
|
||
|
||
function communitybot_telegram_register(): array
|
||
{
|
||
$cfg = communitybot_config();
|
||
if ($cfg['telegram_token'] === '') return ['ok' => false, 'message' => 'Bitte das Bot-Token von @BotFather eintragen.'];
|
||
if ($cfg['telegram_secret'] === '') {
|
||
$cfg['telegram_secret'] = bin2hex(random_bytes(24));
|
||
communitybot_set_secret('telegram_secret', $cfg['telegram_secret']);
|
||
}
|
||
$r = communitybot_json('POST', 'https://api.telegram.org/bot' . $cfg['telegram_token'] . '/setWebhook', [], [
|
||
'url' => communitybot_url('telegram'), 'secret_token' => $cfg['telegram_secret'], 'allowed_updates' => ['message'], 'drop_pending_updates' => true,
|
||
]);
|
||
if (!empty($r['json']['ok'])) {
|
||
communitybot_json('POST', 'https://api.telegram.org/bot' . $cfg['telegram_token'] . '/setMyCommands', [], ['commands' => [
|
||
['command' => 'ticket', 'description' => 'Ticket anlegen: /ticket Betreff | Nachricht'],
|
||
['command' => 'status', 'description' => 'Stand eines Tickets'],
|
||
['command' => 'antwort', 'description' => 'Zu einem Ticket antworten'],
|
||
['command' => 'hilfe', 'description' => 'Hilfe'],
|
||
]]);
|
||
return ['ok' => true, 'message' => 'Der Telegram-Bot ist verbunden.'];
|
||
}
|
||
return ['ok' => false, 'message' => 'Telegram hat abgelehnt: ' . communitybot_clip((string) ($r['json']['description'] ?? $r['body']), 200)];
|
||
}
|
||
|
||
function communitybot_url(string $endpoint): string
|
||
{
|
||
return rtrim(base_url(), '/') . '/api/public/v1/plugin_webhook.php?p=' . COMMUNITYBOT . '&e=' . $endpoint;
|
||
}
|
||
|
||
// ── Benachrichtigungen ─────────────────────────────────────────────────────
|
||
|
||
function communitybot_send_to(array $link, string $text): bool
|
||
{
|
||
return $link['platform'] === 'discord'
|
||
? communitybot_discord_dm((string) $link['ext_user'], $text)
|
||
: communitybot_telegram_send((string) $link['ext_chat'], $text);
|
||
}
|
||
|
||
function communitybot_links(PDO $pdo, int $ticketId): array
|
||
{
|
||
// pro Nutzer nur einmal, auch wenn mehrere Verknüpfungen bestehen
|
||
$s = $pdo->prepare('SELECT platform, ext_user, MAX(ext_chat) AS ext_chat FROM plugin_communitybot_links WHERE ticket_id = ? GROUP BY platform, ext_user');
|
||
$s->execute([$ticketId]);
|
||
return $s->fetchAll();
|
||
}
|
||
|
||
function communitybot_notify_reply(PDO $pdo, int $ticketId): void
|
||
{
|
||
if (!communitybot_config()['notify']) return;
|
||
$links = communitybot_links($pdo, $ticketId);
|
||
if (!$links) return;
|
||
$t = $pdo->prepare('SELECT ticket_number, is_spam FROM tickets WHERE id = ?');
|
||
$t->execute([$ticketId]);
|
||
$ticket = $t->fetch();
|
||
if (!$ticket || !empty($ticket['is_spam'])) return;
|
||
$m = $pdo->prepare("SELECT message FROM ticket_threads WHERE ticket_id = ? AND author_type = 'staff' AND is_internal_note = 0 ORDER BY id DESC LIMIT 1");
|
||
$m->execute([$ticketId]);
|
||
$msg = (string) $m->fetchColumn();
|
||
if ($msg === '') return;
|
||
$text = 'Neue Antwort zu Ticket #' . $ticket['ticket_number'] . ":\n\n" . communitybot_clip($msg, 1200) . "\n\nAntworten: /antwort " . $ticket['ticket_number'] . ' Ihre Nachricht';
|
||
foreach ($links as $l) {
|
||
try { communitybot_send_to($l, $text); } catch (Throwable $e) { error_log('communitybot notify: ' . $e->getMessage()); }
|
||
}
|
||
}
|
||
|
||
function communitybot_notify_status(PDO $pdo, int $ticketId): void
|
||
{
|
||
if (!communitybot_config()['notify']) return;
|
||
$links = communitybot_links($pdo, $ticketId);
|
||
if (!$links) return;
|
||
$t = $pdo->prepare('SELECT ticket_number, status, is_spam FROM tickets WHERE id = ?');
|
||
$t->execute([$ticketId]);
|
||
$ticket = $t->fetch();
|
||
if (!$ticket || !empty($ticket['is_spam']) || !in_array($ticket['status'], ['resolved', 'closed'], true)) return;
|
||
$text = 'Ihr Ticket #' . $ticket['ticket_number'] . ' ist jetzt „' . status_label((string) $ticket['status'])[0] . '“. Wenn noch etwas offen ist, antworten Sie mit /antwort ' . $ticket['ticket_number'] . ' Ihre Nachricht.';
|
||
foreach ($links as $l) {
|
||
try { communitybot_send_to($l, $text); } catch (Throwable $e) { error_log('communitybot notify: ' . $e->getMessage()); }
|
||
}
|
||
}
|