Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
BaseSendNotificationsCommand
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 6
380
0.00% covered (danger)
0.00%
0 / 1
 getAllowedAppEnvs
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getNotificationSubscriptionType
n/a
0 / 0
n/a
0 / 0
0
 autogenerateSubscriptions
n/a
0 / 0
n/a
0 / 0
0
 getNotification
n/a
0 / 0
n/a
0 / 0
0
 getNonReminderNotificationTypes
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 handle
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getNotificationSubscriptions
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 sendNotifications
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 sendNotificationToSubscription
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2
3namespace Olz\Command\Notifications;
4
5use Olz\Command\Common\OlzCommand;
6use Olz\Entity\NotificationSubscription;
7use Olz\Entity\TelegramLink;
8use Symfony\Component\Console\Command\Command;
9use Symfony\Component\Console\Input\InputInterface;
10use Symfony\Component\Console\Output\OutputInterface;
11use Symfony\Component\Mime\Email;
12
13abstract class BaseSendNotificationsCommand extends OlzCommand {
14    /** @return array<string> */
15    protected function getAllowedAppEnvs(): array {
16        return ['dev', 'test', 'staging', 'prod'];
17    }
18
19    abstract public function getNotificationSubscriptionType(): string;
20
21    abstract public function autogenerateSubscriptions(): void;
22
23    /** @param array<string, mixed> $args */
24    abstract public function getNotification(array $args): ?Notification;
25
26    /** @return array<string> */
27    protected function getNonReminderNotificationTypes(): array {
28        return array_values(array_filter(
29            NotificationSubscription::ALL_NOTIFICATION_TYPES,
30            function ($notification_type): bool {
31                return
32                    $notification_type !== NotificationSubscription::TYPE_EMAIL_CONFIG_REMINDER
33                    && $notification_type !== NotificationSubscription::TYPE_TELEGRAM_CONFIG_REMINDER
34                    && $notification_type !== NotificationSubscription::TYPE_ROLE_REMINDER;
35            }
36        ));
37    }
38
39    protected function handle(InputInterface $input, OutputInterface $output): int {
40        $this->autogenerateSubscriptions();
41
42        $subscriptions_by_args = $this->getNotificationSubscriptions();
43        $this->sendNotifications($subscriptions_by_args);
44
45        return Command::SUCCESS;
46    }
47
48    /** @return array<string, array<NotificationSubscription>> */
49    private function getNotificationSubscriptions(): array {
50        $type = $this->getNotificationSubscriptionType();
51        $notification_subscription_repo = $this->entityManager()->getRepository(NotificationSubscription::class);
52        $subscriptions = $notification_subscription_repo->findBy(['notification_type' => $type]);
53
54        $subscriptions_by_args = [];
55        foreach ($subscriptions as $subscription) {
56            $notification_args = $subscription->getNotificationTypeArgs() ?? '';
57            $subscriptions_of_args = $subscriptions_by_args[$notification_args] ?? [];
58            $subscriptions_of_args[] = $subscription;
59            $subscriptions_by_args[$notification_args] = $subscriptions_of_args;
60        }
61        return $subscriptions_by_args;
62    }
63
64    /** @param array<string, array<NotificationSubscription>> $subscriptions_by_args */
65    private function sendNotifications(array $subscriptions_by_args): void {
66        $this->log()->info("Sending '{$this->getNotificationSubscriptionType()}' notifications...");
67        foreach ($subscriptions_by_args as $args_json => $subscriptions) {
68            $this->log()->info("Getting notification for '{$args_json}'...");
69            $args = json_decode($args_json, true);
70            $notification = $this->getNotification($args);
71            if ($notification) {
72                foreach ($subscriptions as $subscription) {
73                    $this->sendNotificationToSubscription($notification, $subscription);
74                }
75            } else {
76                $this->log()->info("Nothing to send.");
77            }
78        }
79    }
80
81    private function sendNotificationToSubscription(Notification $notification, NotificationSubscription $subscription): void {
82        $user = $subscription->getUser();
83        $title = $notification->title;
84        $text = $notification->getTextForUser($user);
85        $config = $notification->config;
86        $subscription_id = $subscription->getId();
87        $delivery_type = $subscription->getDeliveryType();
88        $user_id = $user->getId();
89        $this->log()->info("Sending notification {$title} over {$delivery_type} to user ({$user_id})...");
90        switch ($delivery_type) {
91            case NotificationSubscription::DELIVERY_EMAIL:
92                try {
93                    $email = (new Email())->subject("[OLZ] {$title}");
94                    $email = $this->emailUtils()->buildOlzEmail($email, $user, $text, $config);
95                    $this->emailUtils()->send($email);
96                    $this->log()->info("Email sent to user ({$user_id}): {$title}");
97                } catch (\Throwable $th) {
98                    $th_class = get_class($th);
99                    $message = $th->getMessage();
100                    $this->log()->critical("Error sending email to user ({$user_id}): [{$th_class}{$message}", []);
101                }
102                break;
103            case NotificationSubscription::DELIVERY_TELEGRAM:
104                $telegram_link_repo = $this->entityManager()->getRepository(TelegramLink::class);
105                $telegram_link = $telegram_link_repo->findOneBy(['user' => $user]);
106                if (!$telegram_link) {
107                    $this->log()->notice("User ({$user_id}) has no telegram link, but a subscription ({$subscription_id})");
108                    return;
109                }
110                $user_chat_id = $telegram_link->getTelegramChatId();
111                if (!$user_chat_id) {
112                    $this->log()->critical("User ({$user_id}) has a telegram link without chat ID, but a subscription ({$subscription_id})");
113                    return;
114                }
115                $html_title = $this->telegramUtils()->renderMarkdown($title);
116                $html_text = $this->telegramUtils()->renderMarkdown($text);
117                try {
118                    $this->telegramUtils()->callTelegramApi('sendMessage', [
119                        'chat_id' => $user_chat_id,
120                        'parse_mode' => 'HTML',
121                        'text' => "<b>{$html_title}</b>\n\n{$html_text}",
122                        'disable_web_page_preview' => true,
123                    ]);
124                    $this->log()->info("Telegram sent to user ({$user_id}): {$title}");
125                } catch (\Throwable $th) {
126                    $th_class = get_class($th);
127                    $message = $th->getMessage();
128                    $this->log()->notice("Error sending telegram to user ({$user_id}): [{$th_class}{$message}", []);
129                }
130                break;
131            default:
132                $this->log()->critical("Unknown delivery type '{$delivery_type}'");
133                break;
134        }
135    }
136}