Telegram::getInfo()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
dl 0
loc 9
c 1
b 0
f 0
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace TelegramBot;
4
5
use TelegramBot\Entities\Response;
6
use TelegramBot\Entities\Update;
7
use TelegramBot\Exception\TelegramException;
8
use TelegramBot\Traits\EnvironmentsTrait;
9
use TelegramBot\Traits\WebhookTrait;
10
use TelegramBot\Util\Toolkit;
11
12
/**
13
 * Telegram class
14
 *
15
 * @link    https://github.com/telegram-bot-php/core
16
 * @author  Shahrad Elahi (https://github.com/shahradelahi)
17
 * @license https://github.com/telegram-bot-php/core/blob/master/LICENSE (MIT License)
18
 */
19
class Telegram
20
{
21
22
    use EnvironmentsTrait;
23
    use WebhookTrait;
24
25
    /**
26
     * @var string
27
     */
28
    public static string $VERSION = 'v1.0.0';
29
30
    /**
31
     * @var string|null
32
     */
33
    private static string|null $api_key = null;
34
35
    /**
36
     * Telegram constructor.
37
     *
38
     * @param string $api_token
39
     */
40
    public function __construct(string $api_token = '')
41
    {
42
        if ($api_token === '') {
43
            self::tryAutoloadEnv();
44
            $api_token = $_ENV['TELEGRAM_BOT_TOKEN'];
45
        }
46
47
        if (empty($api_token) || self::validateToken($api_token) === false) {
48
            throw new TelegramException('Invalid Telegram API token');
49
        }
50
51
        self::setToken($api_token);
52
    }
53
54
    /**
55
     * Validate the token
56
     *
57
     * @param string $token (e.g. 123456789:ABC-DEF1234ghIkl-zyx57W2v1u123ew11) {digit}:{alphanumeric[34]}
58
     * @return bool
59
     */
60
    public static function validateToken(string $token): bool
61
    {
62
        preg_match_all('/([0-9]+:[a-zA-Z0-9-_]+)/', $token, $matches);
63
        return count($matches[0]) == 1;
64
    }
65
66
    /**
67
     * Set the current bot token.
68
     *
69
     * @param string $api_key
70
     * @return void
71
     */
72
    public static function setToken(string $api_key): void
73
    {
74
        static::$api_key = $api_key;
0 ignored issues
show
Bug introduced by
Since $api_key is declared private, accessing it with static will lead to errors in possible sub-classes; you can either use self, or increase the visibility of $api_key to at least protected.
Loading history...
75
        $_ENV['TELEGRAM_BOT_TOKEN'] = $api_key;
76
    }
77
78
    /**
79
     * Get bot info from given API key
80
     *
81
     * @return Response
82
     * @throws TelegramException
83
     */
84
    public function getInfo(): Response
85
    {
86
        $result = Request::getMe();
87
88
        if (!$result->isOk()) {
89
            throw new TelegramException($result->getErrorCode() . ': ' . $result->getDescription());
90
        }
91
92
        return $result;
93
    }
94
95
    /**
96
     * Pass the update to the given update handler
97
     *
98
     * @param UpdateHandler $update_handler The update handler
99
     * @param Update|null $update By default, it will get the update from input
100
     * @return void
101
     */
102
    public function fetchWith(UpdateHandler $update_handler, Update|null $update = null): void
103
    {
104
        if (is_subclass_of($update_handler, UpdateHandler::class)) {
105
            if ($update === null) $update = self::getUpdate();
106
            $update_handler->resolve($update);
107
        }
108
    }
109
110
    /**
111
     * Get the update from input
112
     *
113
     * @return Update|null
114
     */
115
    public static function getUpdate(): Update|null
116
    {
117
        $input = self::getInput();
118
        if (empty($input)) return null;
119
        return Telegram::processUpdate($input, self::getApiToken());
0 ignored issues
show
Bug introduced by
It seems like self::getApiToken() can also be of type false; however, parameter $apiKey of TelegramBot\Telegram::processUpdate() does only seem to accept null|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        return Telegram::processUpdate($input, /** @scrutinizer ignore-type */ self::getApiToken());
Loading history...
120
    }
121
122
    /**
123
     * Get input from stdin and return it
124
     *
125
     * @return string|null
126
     */
127
    public static function getInput(): string|null
128
    {
129
        $input = file_get_contents('php://input');
130
        if (!empty($input)) {
131
            return $input;
132
        }
133
134
        $env = getenv('TG_CURRENT_UPDATE');
135
        if (!empty($env) && is_string($env)) {
136
            return $env;
137
        }
138
139
        return null;
140
    }
141
142
    /**
143
     * This method will convert a string to an update object
144
     *
145
     * @param string $input The input string
146
     * @param string|null $apiKey The API key
147
     * @return Update|false
148
     */
149
    public static function processUpdate(string $input, string|null $apiKey = null): Update|false
150
    {
151
        if (empty($input)) {
152
            throw new TelegramException(
153
                'Input is empty! Please check your code and try again.'
154
            );
155
        }
156
157
        if ($apiKey !== null && !self::validateToken($apiKey)) {
158
            throw new TelegramException(
159
                'Invalid token! Please check your code and try again.'
160
            );
161
        }
162
163
        if ($apiKey !== null && self::validateWebData($apiKey, $input)) {
164
            if (Toolkit::isUrlEncode($input)) {
165
                $web_data = Toolkit::urlDecode($input);
166
            }
167
168
            if (Toolkit::isJson($input)) {
169
                $web_data = json_decode($input, true);
170
            }
171
172
            if (!empty($web_data) && is_array($web_data)) {
173
                $input = json_encode([
174
                    'web_app_data' => $web_data,
175
                ]);
176
            }
177
        }
178
179
        if (!Toolkit::isJson($input)) {
180
            throw new TelegramException(sprintf(
181
                "Input is not a valid JSON string! Please check your code and try again.\nInput: %s",
182
                $input
183
            ));
184
        }
185
186
        $input = json_decode($input, true);
187
188
        return new Update($input);
189
    }
190
191
    /**
192
     * Validate webapp data from is from Telegram
193
     *
194
     * @link https://core.telegram.org/bots/webapps#validating-data-received-via-the-web-app
195
     *
196
     * @param string $token The bot token
197
     * @param string $body The message body from getInput()
198
     * @return bool
199
     */
200
    public static function validateWebData(string $token, string $body): bool
201
    {
202
        if (!Toolkit::isJson($body)) {
203
            $raw_data = rawurldecode(str_replace('_auth=', '', $body));
204
            $data = Toolkit::urlDecode($raw_data);
205
206
            if (empty($data['user'])) {
207
                return false;
208
            }
209
210
            $data['user'] = urldecode($data['user']);
211
212
        } else {
213
            $data = json_decode($body, true);
214
215
            if (empty($data['user'])) {
216
                return false;
217
            }
218
219
            $data['user'] = json_encode($data['user']);
220
        }
221
222
        $data_check_string = "auth_date={$data['auth_date']}\nquery_id={$data['query_id']}\nuser={$data['user']}";
223
        $secret_key = hash_hmac('sha256', $token, "WebAppData", true);
224
225
        return hash_hmac('sha256', $data_check_string, $secret_key) == $data['hash'];
226
    }
227
228
    /**
229
     * Get the current bot token.
230
     *
231
     * @return string|false
232
     */
233
    public static function getApiToken(): string|false
234
    {
235
        return self::$api_key !== null ? (self::validateToken(self::$api_key) ? self::$api_key : false) : false;
236
    }
237
238
}
239