Completed
Pull Request — develop (#365)
by
unknown
02:44
created

Botan   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 0%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 31
c 2
b 0
f 0
lcom 1
cbo 8
dl 0
loc 238
ccs 0
cts 140
cp 0
rs 9.8

4 Methods

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