Completed
Push — master ( 921fb2...012713 )
by Sergey
03:03
created

TelegramTarget::export()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 16
cts 16
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 13
nc 4
nop 0
crap 4
1
<?php
2
/**
3
 * Telegram log target for Yii 2
4
 *
5
 * @see       https://github.com/sergeymakinen/yii2-telegram-log
6
 * @copyright Copyright (c) 2017 Sergey Makinen (https://makinen.ru)
7
 * @license   https://github.com/sergeymakinen/yii2-telegram-log/blob/master/LICENSE MIT License
8
 */
9
10
namespace sergeymakinen\log;
11
12
use yii\base\InvalidValueException;
13
use yii\di\Instance;
14
use yii\httpclient\Client;
15
use yii\log\Target;
16
17
class TelegramTarget extends Target
18
{
19
    /**
20
     * @var Client|array|string Yii HTTP client configuration.
21
     * This can be a component ID, a configuration array or a Client instance.
22
     */
23
    public $httpClient = [
24
        'class' => 'yii\httpclient\Client',
25
    ];
26
27
    /**
28
     * @var string bot token.
29
     * @see https://core.telegram.org/bots/api#authorizing-your-bot
30
     */
31
    public $token;
32
33
    /**
34
     * @var int|string unique identifier for the target chat or username of the target channel
35
     * (in the format `{@}channelusername`).
36
     */
37
    public $chatId;
38
39
    /**
40
     * @var string log message template.
41
     */
42
    public $template = '{request}
43
{level}
44
45
{text}
46
47
{category}
48
{prefix}
49
{userIp}
50
{userId}
51
{sessionId}
52
{stackTrace}';
53
54
    /**
55
     * @var array log message template substitutions.
56
     * [[defaultSubstitutions()]] will be used by default.
57
     */
58
    public $substitutions;
59
60
    /**
61
     * @var bool whether to enable link previews for links in the message.
62
     */
63
    public $enableWebPagePreview = false;
64
65
    /**
66
     * @var bool whether to send the message silently.
67
     * iOS users will not receive a notification, Android users will receive a notification with no sound.
68
     */
69
    public $enableNotification = true;
70
71
    /**
72
     * @inheritDoc
73
     */
74
    public $exportInterval = 1;
75
76
    /**
77
     * @inheritDoc
78
     */
79 1
    public function init()
80
    {
81 1
        parent::init();
82 1
        $this->httpClient = Instance::ensure($this->httpClient, Client::className());
83 1
        if ($this->substitutions === null) {
84 1
            $this->substitutions = $this->defaultSubstitutions();
85 1
        }
86 1
    }
87
88
    /**
89
     * @inheritDoc
90
     */
91 2
    public function export()
92
    {
93 2
        foreach (array_map([$this, 'formatMessageRequest'], $this->messages) as $request) {
94 2
            $response = $this->httpClient
95 2
                ->post('https://api.telegram.org/bot' . $this->token . '/sendMessage', $request)
96 2
                ->setFormat(Client::FORMAT_JSON)
97 2
                ->send();
98 2
            if (!$response->getIsOk()) {
99 2
                if (isset($response->getData()['description'])) {
100 2
                    $description = $response->getData()['description'];
101 2
                } else {
102 1
                    $description = $response->getContent();
103
                }
104 2
                throw new InvalidValueException(
105 2
                    'Unable to send logs to Telegram: ' . $description, (int) $response->getStatusCode()
106 2
                );
107
            }
108 1
        }
109 1
    }
110
111
    /**
112
     * Returns a `sendMessage` request for Telegram.
113
     * @param array $message
114
     * @return array
115
     */
116 1
    protected function formatMessageRequest($message)
117
    {
118 1
        $message = new Message($message, $this);
119
        $request = [
120 1
            'chat_id' => $this->chatId,
121 1
            'parse_mode' => 'Markdown',
122 1
            'disable_web_page_preview' => !$this->enableWebPagePreview,
123 1
            'disable_notification' => !$this->enableNotification,
124 1
        ];
125
        $request['text'] = preg_replace_callback('/{([^}]+)}([\n]*|$)/', function (array $matches) use ($message) {
126 1
            if (isset($this->substitutions[$matches[1]])) {
127 1
                $value = $this->substitute($matches[1], $message);
128 1
                if ($value !== '') {
129 1
                    return $value . $matches[2];
130
                }
131 1
            }
132 1
            return '';
133 1
        }, $this->template);
134 1
        return $request;
135
    }
136
137
    /**
138
     * Returns an array with the default substitutions.
139
     * @return array the default substitutions.
140
     */
141 1
    protected function defaultSubstitutions()
142
    {
143
        return [
144
            'request' => [
145 1
                'title' => null,
146 1
                'short' => false,
147 1
                'wrapAsCode' => false,
148
                'value' => function (Message $message) {
149 1
                    if ($message->getIsConsoleRequest()) {
150 1
                        return $message->getCommandLine();
151
                    } else {
152 1
                        return '[' . $message->getUrl() . '](' . $message->getUrl() . ')';
153
                    }
154 1
                },
155 1
            ],
156
            'level' => [
157 1
                'title' => 'Level',
158 1
                'short' => true,
159 1
                'wrapAsCode' => false,
160
                'value' => function (Message $message) {
161 1
                    return $message->getLevel();
162 1
                },
163 1
            ],
164
            'category' => [
165 1
                'title' => 'Category',
166 1
                'short' => true,
167 1
                'wrapAsCode' => true,
168
                'value' => function (Message $message) {
169 1
                    return $message->getCategory();
170 1
                },
171 1
            ],
172
            'prefix' => [
173 1
                'title' => 'Prefix',
174 1
                'short' => true,
175 1
                'wrapAsCode' => true,
176
                'value' => function (Message $message) {
177 1
                    return $message->getPrefix();
178 1
                },
179 1
            ],
180
            'userIp' => [
181 1
                'title' => 'User IP',
182 1
                'short' => true,
183 1
                'wrapAsCode' => false,
184
                'value' => function (Message $message) {
185 1
                    return $message->getUserIp();
186 1
                },
187 1
            ],
188
            'userId' => [
189 1
                'title' => 'User ID',
190 1
                'short' => true,
191 1
                'wrapAsCode' => true,
192
                'value' => function (Message $message) {
193 1
                    return $message->getUserId();
194 1
                },
195 1
            ],
196
            'sessionId' => [
197 1
                'title' => 'Session ID',
198 1
                'short' => true,
199 1
                'wrapAsCode' => true,
200
                'value' => function (Message $message) {
201 1
                    return $message->getSessionId();
202 1
                },
203 1
            ],
204
            'stackTrace' => [
205 1
                'title' => 'Stack Trace',
206 1
                'short' => false,
207 1
                'wrapAsCode' => true,
208
                'value' => function (Message $message) {
209 1
                    return $message->getStackTrace();
210 1
                },
211 1
            ],
212
            'text' => [
213 1
                'title' => null,
214 1
                'short' => false,
215 1
                'wrapAsCode' => true,
216 1
                'value' => function (Message $message) {
217 1
                    return $message->getText();
218 1
                },
219 1
            ],
220 1
        ];
221
    }
222
223
    /**
224
     * Returns a substituted value.
225
     * @param string $name
226
     * @param Message $message
227
     * @return string
228
     */
229 1
    private function substitute($name, Message $message)
230
    {
231 1
        $config = $this->substitutions[$name];
232 1
        $value = (string) call_user_func($config['value'], $message);
233 1
        if ($value === '') {
234 1
            return '';
235
        }
236
237 1
        if ($config['wrapAsCode']) {
238 1
            if ($config['short']) {
239 1
                $value = '`' . $value . '`';
240 1
            } else {
241 1
                $value = "```text\n" . $value . "\n```";
242
            }
243 1
        }
244 1
        if ($config['title'] !== null) {
245 1
            if ($config['short']) {
246 1
                $value = "*{$config['title']}*: {$value}";
247 1
            } else {
248 1
                $value = "*{$config['title']}*:\n{$value}";
249
            }
250 1
        }
251 1
        return $value;
252
    }
253
}
254