Completed
Push — feature/refactor-app-design ( 84f9ff...b18d21 )
by Avtandil
04:09
created

Botan   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 256
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 2.35%

Importance

Changes 0
Metric Value
wmc 32
c 0
b 0
f 0
lcom 2
cbo 8
dl 0
loc 256
ccs 2
cts 85
cp 0.0235
rs 9.6

6 Methods

Rating   Name   Duplication   Size   Complexity  
A initializeBotan() 0 21 3
A lock() 0 6 2
D track() 0 91 18
C shortenUrl() 0 39 7
A isEnabled() 0 4 1
A setEnabled() 0 4 1
1
<?php
2
/**
3
 * This file is part of the TelegramBot package.
4
 *
5
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Longman\TelegramBot\Extensions\Botan;
12
13
use GuzzleHttp\Client;
14
use GuzzleHttp\Exception\RequestException;
15
use Longman\TelegramBot\Entities\Update;
16
use Longman\TelegramBot\Exception\TelegramException;
17
use Longman\TelegramBot\TelegramLog;
18
19
/**
20
 * Class Botan
21
 *
22
 * Integration with http://botan.io statistics service for Telegram bots
23
 */
24
class Botan
25
{
26
    /**
27
     * Botan.io API URL
28
     *
29
     * @var string
30
     */
31
    protected static $api_base_uri = 'https://api.botan.io';
32
33
    /**
34
     * Yandex AppMetrica application key
35
     *
36
     * @var string
37
     */
38
    protected static $token = '';
39
40
    /**
41
     * Guzzle Client object
42
     *
43
     * @var \GuzzleHttp\Client
44
     */
45
    private static $client;
46
47
    /**
48
     * The actual command that is going to be reported
49
     *
50
     *  Set as public to let the developers either:
51
     *  - block tracking from inside commands by setting the value to non-existent command
52
     *  - override which command is tracked when commands call other commands with executeCommand()
53
     *
54
     * @var string
55
     */
56
    public static $command = '';
57
58
    /**
59
     * Enabled
60
     *
61
     * @var bool
62
     */
63
    protected static $enabled = false;
64
65
    /**
66
     * Initialize Botan
67
     *
68
     * @param  string $token
69
     * @param  array $options
70
     *
71
     * @throws \Longman\TelegramBot\Exception\TelegramException
72
     */
73
    public static function initializeBotan($token, array $options = [])
74
    {
75
        if (empty($token)) {
76
            throw new TelegramException('Botan token is empty!');
77
        }
78
79
        $options_default = [
80
            'timeout' => 3,
81
        ];
82
83
        $options = array_merge($options_default, $options);
84
85
        if (! is_numeric($options['timeout'])) {
86
            throw new TelegramException('Timeout must be a number!');
87
        }
88
89
        self::$token = $token;
90
        self::$client = new Client(['base_uri' => self::$api_base_uri, 'timeout' => $options['timeout']]);
91
92
        BotanDB::initializeBotanDb();
93
    }
94
95
    /**
96
     * Lock function to make sure only the first command is reported (the one user requested)
97
     *
98
     *  This is in case commands are calling other commands with executeCommand()
99
     *
100
     * @param string $command
101
     */
102
    public static function lock($command = '')
103
    {
104
        if (empty(self::$command)) {
105
            self::$command = strtolower($command);
106
        }
107
    }
108
109
    /**
110
     * Track function
111
     *
112
     * @param \Longman\TelegramBot\Entities\Update $update
113
     * @param string $command
114
     *
115
     * @return bool|string
116
     * @throws \Longman\TelegramBot\Exception\TelegramException
117
     */
118
    public static function track(Update $update, $command = '')
119
    {
120
        $command = strtolower($command);
121
122
        if (empty(self::$token) || $command !== self::$command) {
123
            return false;
124
        }
125
126
        if ($update === null) {
127
            throw new TelegramException('Update object is empty!');
128
        }
129
130
        // Release the lock in case this is getUpdates instance in foreach loop
131
        self::$command = '';
132
133
        $data = [];
134
        $update_data = (array) $update; // For now, this is the only way
135
        $update_type = $update->getUpdateType();
136
137
        $update_object_names = [
138
            'message'              => 'Message',
139
            'edited_message'       => 'Edited Message',
140
            'channel_post'         => 'Channel Post',
141
            'edited_channel_post'  => 'Edited Channel Post',
142
            'inline_query'         => 'Inline Query',
143
            'chosen_inline_result' => 'Chosen Inline Result',
144
            'callback_query'       => 'Callback Query',
145
        ];
146
147
        if (array_key_exists($update_type, $update_object_names)) {
148
            $data = $update_data[$update_type];
149
            $event_name = $update_object_names[$update_type];
150
151
            if ($update_type === 'message' && $entities = $update->getMessage()->getEntities()) {
152
                foreach ($entities as $entity) {
153
                    if ($entity->getType() === 'bot_command' && $entity->getOffset() === 0) {
154
                        if ($command === 'generic') {
155
                            $command = 'Generic';
156
                        } else if ($command === 'genericmessage') {  // This should not happen as it equals normal message but leaving it as a fail-safe
157
                            $command = 'Generic Message';
158
                        } else {
159
                            $command = '/' . $command;
160
                        }
161
162
                        $event_name = 'Command (' . $command . ')';
163
                        break;
164
                    }
165
                }
166
            }
167
        }
168
169
        if (empty($event_name)) {
170
            TelegramLog::error('Botan.io stats report failed, no suitable update object found!');
171
172
            return false;
173
        }
174
175
        // In case there is no from field assign id = 0
176
        $uid = isset($data['from']['id']) ? $data['from']['id'] : 0;
177
178
        try {
179
            $response = self::$client->post(
180
                sprintf(
181
                    '/track?token=%1$s&uid=%2$s&name=%3$s',
182
                    self::$token,
183
                    $uid,
184
                    urlencode($event_name)
185
                ),
186
                [
187
                    'headers' => [
188
                        'Content-Type' => 'application/json',
189
                    ],
190
                    'json'    => $data,
191
                ]
192
            );
193
194
            $result = (string) $response->getBody();
195
        } catch (RequestException $e) {
196
            $result = $e->getMessage();
197
        }
198
199
        $responseData = json_decode($result, true);
200
201
        if (! $responseData || $responseData['status'] !== 'accepted') {
202
            TelegramLog::debug('Botan.io stats report failed: %s', $result ?: 'empty response');
203
204
            return false;
205
        }
206
207
        return $responseData;
208
    }
209
210
    /**
211
     * Url Shortener function
212
     *
213
     * @param string $url
214
     * @param integer $user_id
215
     *
216
     * @return string
217
     * @throws \Longman\TelegramBot\Exception\TelegramException
218
     */
219
    public static function shortenUrl($url, $user_id)
220
    {
221
        if (empty(self::$token)) {
222
            return $url;
223
        }
224
225
        if (empty($user_id)) {
226
            throw new TelegramException('User id is empty!');
227
        }
228
229
        if ($cached = BotanDB::selectShortUrl($url, $user_id)) {
230
            return $cached;
231
        }
232
233
        try {
234
            $response = self::$client->post(
235
                sprintf(
236
                    '/s?token=%1$s&user_ids=%2$s&url=%3$s',
237
                    self::$token,
238
                    $user_id,
239
                    urlencode($url)
240
                )
241
            );
242
243
            $result = (string) $response->getBody();
244
        } catch (RequestException $e) {
245
            $result = $e->getMessage();
246
        }
247
248
        if (filter_var($result, FILTER_VALIDATE_URL) === false) {
249
            TelegramLog::debug('Botan.io URL shortening failed for "%s": %s', $url, $result ?: 'empty response');
250
251
            return $url;
252
        }
253
254
        BotanDB::insertShortUrl($url, $user_id, $result);
255
256
        return $result;
257
    }
258
259
    /**
260
     * Return enabled status
261
     *
262
     * @return bool
263
     */
264 2
    public static function isEnabled()
265
    {
266 2
        return self::$enabled;
267
    }
268
269
    /**
270
     * Set enabled status
271
     *
272
     * @param $enabled bool
273
     * @return void
274
     */
275
    public static function setEnabled($enabled)
276
    {
277
        self::$enabled = $enabled;
278
    }
279
}
280