140 lines
5.9 KiB
PHP
140 lines
5.9 KiB
PHP
<?php
|
|
/**
|
|
* Discord- und Telegram-Benachrichtigungen für Mitarbeiter.
|
|
*
|
|
* Konfiguration erfolgt unter "Einstellungen" im Mitarbeiterbereich (nicht in
|
|
* config/config.php), da es sich um Werte handelt, die Admins ohne Dateizugriff
|
|
* ändern können sollen. Gespeichert in der settings-Tabelle.
|
|
*
|
|
* Datenschutz-Hinweis: Bei aktivierten Benachrichtigungen werden Ticket-
|
|
* Ausschnitte (Betreff, Kundenname/-E-Mail, Nachrichtenanfang) an Discord
|
|
* bzw. Telegram als Drittanbieter (außerhalb der EU) übermittelt. Bitte vor
|
|
* der Aktivierung die eigenen Datenschutzanforderungen prüfen.
|
|
*
|
|
* Voraussetzung: PHP-Einstellung allow_url_fopen muss aktiviert sein
|
|
* (Standard auf den meisten Hosting-Paketen).
|
|
*/
|
|
|
|
function http_post_json(string $url, array $data, int $timeoutSeconds = 5): bool
|
|
{
|
|
$opts = ['http' => [
|
|
'method' => 'POST',
|
|
'header' => "Content-Type: application/json\r\n",
|
|
'content' => json_encode($data),
|
|
'timeout' => $timeoutSeconds,
|
|
'ignore_errors' => true,
|
|
]];
|
|
$result = @file_get_contents($url, false, stream_context_create($opts));
|
|
return $result !== false;
|
|
}
|
|
|
|
function http_post_form(string $url, array $data, int $timeoutSeconds = 5): bool
|
|
{
|
|
$opts = ['http' => [
|
|
'method' => 'POST',
|
|
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
|
|
'content' => http_build_query($data),
|
|
'timeout' => $timeoutSeconds,
|
|
'ignore_errors' => true,
|
|
]];
|
|
$result = @file_get_contents($url, false, stream_context_create($opts));
|
|
return $result !== false;
|
|
}
|
|
|
|
function discord_notify(string $message): bool
|
|
{
|
|
$url = decrypt_secret(get_setting('discord_webhook_url') ?? '');
|
|
if (!$url) {
|
|
return false;
|
|
}
|
|
return http_post_json($url, ['content' => $message]);
|
|
}
|
|
|
|
function telegram_notify(string $message): bool
|
|
{
|
|
$token = decrypt_secret(get_setting('telegram_bot_token') ?? '');
|
|
$chatId = get_setting('telegram_chat_id');
|
|
if (!$token || !$chatId) {
|
|
return false;
|
|
}
|
|
$url = "https://api.telegram.org/bot{$token}/sendMessage";
|
|
return http_post_form($url, ['chat_id' => $chatId, 'text' => $message]);
|
|
}
|
|
|
|
/** Benachrichtigt E-Mail (Abteilung) + Discord + Telegram über ein neues Ticket */
|
|
function notify_new_ticket_channels(array $ticket, ?string $departmentEmail): void
|
|
{
|
|
fcm_notify_new_ticket($ticket); // Push auf die Android-App (läuft nach dem Senden der Seite)
|
|
if ($departmentEmail) {
|
|
notify_department_new_ticket($ticket, $departmentEmail);
|
|
}
|
|
|
|
$text = "🆕 Neues Ticket #{$ticket['ticket_number']}\n"
|
|
. "Betreff: {$ticket['subject']}\n"
|
|
. "Von: {$ticket['requester_name']} ({$ticket['requester_email']})";
|
|
if (!empty($ticket['department_name'])) {
|
|
$text .= "\nAbteilung: {$ticket['department_name']}";
|
|
}
|
|
$text .= "\n" . base_url() . '/staff/ticket_view.php?n=' . urlencode($ticket['ticket_number']);
|
|
|
|
discord_notify($text);
|
|
telegram_notify($text);
|
|
}
|
|
|
|
/** Benachrichtigt Discord + Telegram über eine neue Kundenantwort (kein Mail-Duplikat nötig) */
|
|
function notify_new_reply_channels(array $ticket, string $excerpt): void
|
|
{
|
|
fcm_notify_new_reply($ticket); // Push auf die Android-App (läuft nach dem Senden der Seite)
|
|
try {
|
|
// Antwort des Kunden holt das Ticket aus der Wiedervorlage und setzt die Erinnerung zurück; Beobachter erfahren davon
|
|
$pdo = get_db();
|
|
ticket_snooze_clear($pdo, (int) $ticket['id'], null, 'Wiedervorlage beendet (Kunde hat geantwortet)');
|
|
$pdo->prepare('UPDATE tickets SET reminder_sent_at = NULL WHERE id = ?')->execute([$ticket['id']]);
|
|
ticket_followers_notify($ticket, 'Neue Kundenantwort');
|
|
} catch (Throwable $e) {
|
|
// Datenbank noch nicht aktualisiert: nicht schlimm
|
|
}
|
|
$short = mb_strlen($excerpt) > 200 ? mb_substr($excerpt, 0, 200) . '…' : $excerpt;
|
|
$text = "💬 Neue Kundenantwort zu Ticket #{$ticket['ticket_number']}\n"
|
|
. "Betreff: {$ticket['subject']}\n"
|
|
. "„{$short}“\n"
|
|
. base_url() . '/staff/ticket_view.php?n=' . urlencode($ticket['ticket_number']);
|
|
|
|
discord_notify($text);
|
|
telegram_notify($text);
|
|
}
|
|
|
|
|
|
/**
|
|
* Benachrichtigt die Mitglieder eines Teams per E-Mail, wenn ihm ein Ticket zugewiesen wurde.
|
|
* Entfällt bei deaktivierten Teams und wenn im Team "Benachrichtigung bei Ticketzuweisung
|
|
* deaktivieren" gesetzt ist. Der auslösende Mitarbeiter ($actorId) bekommt keine Mail.
|
|
*
|
|
* @return int Anzahl verschickter Benachrichtigungen
|
|
*/
|
|
function notify_team_assignment(PDO $pdo, array $ticket, int $teamId, ?int $actorId = null): int
|
|
{
|
|
$stmt = $pdo->prepare('SELECT name, is_active, no_alerts FROM teams WHERE id = ?');
|
|
$stmt->execute([$teamId]);
|
|
$team = $stmt->fetch();
|
|
if (!$team || !$team['is_active'] || $team['no_alerts']) return 0;
|
|
|
|
$members = $pdo->prepare('SELECT s.id FROM team_members tm JOIN staff s ON s.id = tm.staff_id WHERE tm.team_id = ? AND s.is_active = 1');
|
|
$members->execute([$teamId]);
|
|
// Mitglieder im Urlaub: stattdessen ihre Vertretung
|
|
$memberRows = [];
|
|
foreach (vacation_expand_ids($pdo, $members->fetchAll(PDO::FETCH_COLUMN)) as $mid) {
|
|
$em = staff_emails_by_ids($pdo, [$mid]);
|
|
if ($em) $memberRows[] = ['id' => $mid, 'email' => $em[0]];
|
|
}
|
|
$sent = 0;
|
|
$subject = 'Ticket #' . $ticket['ticket_number'] . ' wurde Team „' . $team['name'] . '“ zugewiesen';
|
|
$body = '<p>Das Ticket <strong>#' . e($ticket['ticket_number']) . '</strong> („' . e($ticket['subject']) . '“) wurde dem Team <strong>'
|
|
. e($team['name']) . '</strong> zugewiesen.</p>';
|
|
foreach ($memberRows as $m) {
|
|
if ($actorId !== null && (int) $m['id'] === $actorId) continue;
|
|
if (send_mail($m['email'], $subject, $body)) $sent++;
|
|
}
|
|
return $sent;
|
|
}
|