Passed
Push — main ( b0527a...6fd7ec )
by Miaad
01:30
created

tools.php (1 issue)

Severity
1
<?php
2
3
namespace BPT;
4
5
use BPT\api\telegram;
6
use BPT\constants\chatMemberStatus;
7
use BPT\constants\parseMode;
8
use DateTime;
9
use Exception;
10
use RecursiveDirectoryIterator;
11
use RecursiveIteratorIterator;
12
13
class tools {
14
    /**
15
     * Check the given username format
16
     *
17
     * e.g. => tools::isUsername('BPT_CH');
18
     *
19
     * e.g. => tools::isUsername(username: 'BPT_CH');
20
     *
21
     * @param string $username Your text to be check is username or not e.g. : 'BPT_CH' | '@BPT_CH'
22
     * @return bool
23
     */
24
    public static function isUsername(string $username): bool {
25
        $length = strlen($username);
26
        return strpos($username, '__') === false && $length >= 5 && $length <= 33 && preg_match('/^@?([a-zA-Z])(\w{4,31})$/', $username);
27
    }
28
29
    /**
30
     * Check given IP is in the given IP range or not
31
     *
32
     * e.g. => tools::ipInRange('192.168.1.1','149.154.160.0/20');
33
     *
34
     * e.g. => tools::ipInRange(ip: '192.168.1.1',range: '149.154.160.0/20');
35
     *
36
     * @param string $ip Your ip
37
     * @param string $range Your range ip for check , if you didn't specify the block , it will be 32
38
     * @return bool
39
     */
40
    public static function ipInRange (string $ip, string $range): bool {
41
        if (!str_contains($range, '/')) {
42
            $range .= '/32';
43
        }
44
        $range_full = explode('/', $range, 2);
45
        $netmask_decimal = ~(pow(2, (32 - $range_full[1])) - 1);
46
        return (ip2long($ip) & $netmask_decimal) == (ip2long($range_full[0]) & $netmask_decimal);
47
    }
48
49
    /**
50
     * Check the given IP is from telegram or not
51
     *
52
     * e.g. => tools::isTelegram('192.168.1.1');
53
     *
54
     * e.g. => tools::isTelegram(ip: '192.168.1.1');
55
     *
56
     * @param string $ip     Your ip to be check is telegram or not e.g. '192.168.1.1'
57
     * @return bool
58
     */
59
    public static function isTelegram (string $ip): bool {
60
        return self::ipInRange($ip, '149.154.160.0/20') || self::ipInRange($ip, '91.108.4.0/22');
61
    }
62
63
    /**
64
     * Check the given IP is from CloudFlare or not
65
     *
66
     * e.g. => tools::isCloudFlare('192.168.1.1');
67
     *
68
     * e.g. =>tools::isCloudFlare(ip: '192.168.1.1');
69
     *
70
     * @param string $ip Your ip to be check is CloudFlare or not e.g. '192.168.1.1'
71
     * @return bool
72
     */
73
    public static function isCloudFlare (string $ip): bool {
74
        $cf_ips = ['173.245.48.0/20', '103.21.244.0/22', '103.22.200.0/22', '103.31.4.0/22', '141.101.64.0/18', '108.162.192.0/18', '190.93.240.0/20', '188.114.96.0/20', '197.234.240.0/22', '198.41.128.0/17', '162.158.0.0/15', '104.16.0.0/12', '172.64.0.0/13', '131.0.72.0/22'];
75
        foreach ($cf_ips as $cf_ip) {
76
            if (self::ipInRange($ip,$cf_ip)) {
77
                return true;
78
            }
79
        }
80
        return false;
81
    }
82
83
    /**
84
     * Check the given token format
85
     *
86
     * if you want to verify token with telegram , you should set verify parameter => true
87
     *
88
     * in that case , if token was right , you will receive getMe result , otherwise you will receive false
89
     *
90
     * verify parameter has default value => false
91
     *
92
     * e.g. => tools::isToken('123123123:abcabcabcabc');
93
     * @param string $token your token e.g. => '123123123:abcabcabcabc'
94
     * @param bool $verify check token with telegram or not
95
     * @return bool|array return array when verify is active and token is true array of telegram getMe result
96
     */
97
    public static function isToken (string $token, bool $verify = false): bool|array {
98
        if (preg_match('/^(\d{8,10}):[\w\-]{35}$/', $token)) {
99
            if ($verify){
100
                $res = telegram::me($token);
101
                if ($res['ok']) {
102
                    return $res['result'];
103
                }
104
                return false;
105
            }
106
            return true;
107
        }
108
        return false;
109
    }
110
111
    /**
112
     * check user joined in channels or not
113
     *
114
     * this method only return true or false, if user join in all channels true, and if user not joined in one channel false
115
     *
116
     * this method does not care about not founded channel and count them as joined channel
117
     *
118
     * ids parameter can be array for multi channels or can be string for one channel
119
     *
120
     * user_id parameter have default value => generated by catchFields method
121
     *
122
     * NOTE : each channel will decrease speed a little(because of request count)
123
     *
124
     * e.g. => tools::isJoined('BPT_CH','442109602');
125
     *
126
     * e.g. => tools::isJoined(['BPT_CH','-1005465465454']);
127
     *
128
     * @param array|string|int $ids e.g. => ['BPT_CH'] , 'BPT_CH' , -100131231313
129
     * @param int|null $user_id e.g. => '442109602'
130
     * @return bool
131
     */
132
    public static function isJoined (array|string|int $ids , int|null $user_id = null): bool {
133
        if (is_numeric($ids) || is_string($ids)) {
0 ignored issues
show
The condition is_string($ids) is always false.
Loading history...
134
            $ids = [$ids];
135
        }
136
        //$user_id = $user_id ?? $this->catchFields(['field' => 'user_id']);
137
138
        foreach ($ids as $id) {
139
            $check = telegram::getChatMember($id,$user_id);
140
            if (isset($check['result'])) {
141
                $check = $check['result']['status'];
142
                return !($check === chatMemberStatus::LEFT || $check === chatMemberStatus::KICKED);
143
            }
144
        }
145
        return true;
146
    }
147
148
    /**
149
     * Generate random string
150
     *
151
     * you can use this method without any input
152
     *
153
     * length parameter have default value => 16
154
     *
155
     * characters parameter have default value => aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
156
     *
157
     * e.g. => tools::randomString();
158
     *
159
     * e.g. => tools::randomString(16,'abcdefg');
160
     *
161
     * e.g. => tools::randomString(length: 16,characters: 'abcdefg');
162
     *
163
     * @param int $length e.g. => 16
164
     * @param string $characters e.g. => 'abcdefg'
165
     * @return string
166
     */
167
    public static function randomString (int $length = 16, string $characters = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ'): string {
168
        $rand_string = '';
169
        $char_len = strlen($characters) - 1;
170
        for ($i = 0; $i < $length; $i ++) {
171
            $rand_string .= $characters[rand(0, $char_len)];
172
        }
173
        return $rand_string;
174
    }
175
176
    /**
177
     * Escape text for different parse_modes
178
     *
179
     * type parameter can be : `MarkdownV2` , `Markdown` , `HTML` , default : `parseMode::HTML`(`HTML`)
180
     *
181
     * e.g. => tools::modeEscape('hello men! *I* Have nothing anymore');
182
     *
183
     * e.g. => tools::modeEscape(text: 'hello men! *I* Have nothing anymore');
184
     *
185
     * @param string $text Your text e.g. => 'hello men! *I* Have nothing anymore'
186
     * @param string $mode Your selected mode e.g. => `parseMode::HTML` | `HTML`
187
     * @return string|false return false when mode is incorrect
188
     */
189
    public static function modeEscape (string $text, string $mode = parseMode::HTML): string|false {
190
        return match ($mode) {
191
            parseMode::HTML => str_replace(['&', '<', '>',], ["&amp;", "&lt;", "&gt;",], $text),
192
            parseMode::MARKDOWN => str_replace(['\\', '_', '*', '`', '['], ['\\\\', '\_', '\*', '\`', '\[',], $text),
193
            parseMode::MARKDOWNV2 => str_replace(
194
                ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!', '\\'],
195
                ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!', '\\\\'],
196
                $text),
197
            default => false
198
        };
199
    }
200
201
    /**
202
     * Convert byte to symbolic size like 2.98 MB
203
     *
204
     * Supp
205
     *
206
     * You could set `precision` to configure decimals after number(2 for 2.98 and 3 for 2.987)
207
     *
208
     * `precision` parameter have default value => 2
209
     *
210
     * e.g. => tools::byteFormat(123456789);
211
     *
212
     * e.g. => tools::byteFormat(byte: 123456789);
213
     *
214
     * @param int $byte e.g. => 29123452912
215
     * @param int $precision e.g. => 2
216
     * @return string
217
     */
218
    public static function byteFormat (int $byte, int $precision = 2): string {
219
        $rate_counter = 0;
220
221
        while ($byte > 1024){
222
            $byte /= 1024;
223
            $rate_counter++;
224
        }
225
226
        if ($rate_counter !== 0) {
227
            $byte = round($byte, $precision);
228
        }
229
230
        return $byte . ' ' . ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'][$rate_counter];
231
    }
232
233
    /**
234
     * receive size from path(can be url or file path)
235
     *
236
     * if format parameter has true value , the returned size converted to symbolic format
237
     *
238
     * format parameter have default value => true
239
     *
240
     * NOTE : some url will not return real size!
241
     *
242
     * e.g. => tools::size('xFile.zip');
243
     *
244
     * e.g. => tools::size(path: 'xFile.zip');
245
     *
246
     * @param string $path e.g. => 'xFile.zip'
247
     * @param bool $format if you set this true , you will receive symbolic string like 2.76MB
248
     * @return string|int|false string for formatted data , int for normal data , false when size can not be found(file not found or ...)
249
     */
250
    public static function size (string $path, bool $format = true): string|int|false {
251
        if (filter_var($path, FILTER_VALIDATE_URL)) {
252
            $ch = curl_init($path);
253
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
254
            curl_setopt($ch, CURLOPT_HEADER, true);
255
            curl_setopt($ch, CURLOPT_NOBODY, true);
256
            curl_exec($ch);
257
            $size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
258
            curl_close($ch);
259
        }
260
        else {
261
            $size = file_exists($path) ? filesize($path) : false;
262
        }
263
264
        if (isset($size) && is_numeric($size)) {
265
            return $format ? tools::byteFormat($size) : $size;
266
        }
267
        else return false;
268
    }
269
270
    //fix methods after here comments
271
272
    /**
273
     * Delete a folder or file if exist
274
     *
275
     * if the folder have subFiles , need to set sub parameter to true. if you don't , you will receive error
276
     *
277
     * sub parameter have default value => true
278
     *
279
     * e.g. => $this->delete(['path'=>'xfolder/yfolder']);
280
     *
281
     * e.g. => $this->delete(['path'=>'xfolder/yfolder','sub'=>false]);
282
     * @param array $array e.g. => ['path'=>'xfolder/yfolder','sub'=>true]
283
     * @return bool
284
     */
285
    public static function delete (string $path, bool $sub = true): bool {
286
        if (is_dir($path)) {
287
            if (count(scandir($path)) > 2) {
288
                if ($sub) {
289
                    $it = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
290
                    $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
291
                    foreach ($files as $file) {
292
                        $file->isDir() ? rmdir($file->getRealPath()) : unlink($file->getRealPath());
293
                    }
294
                    rmdir($path);
295
                }
296
                else {
297
                    logger::write("BPT delete function used\ndelete function cannot delete folder because its have subFiles and sub parameter haven't true value",'error');
298
                    return false;
299
                }
300
            }
301
            else rmdir($path);
302
        }
303
        else unlink($path);
304
305
        return true;
306
    }
307
308
    /**
309
     * Convert datetime or timestamp to array
310
     *
311
     * Its calculated different between given time and now
312
     *
313
     * e.g. => $this->time2string(['datetime'=>1636913656]);
314
     *
315
     * @param array $array e.g. => ['datetime'=>1636913656]
316
     *
317
     * @return array
318
     * @throws Exception
319
     */
320
    public static function time2string (int $datetime): array {
321
        $now = new DateTime;
322
        $input = new DateTime('@' . $datetime);
323
        $status = $now < $input ? 'later' : 'ago';
324
        $diff = $now->diff($input);
325
        $diff->w = floor($diff->d / 7);
326
        $string = ['year' => 'y', 'month' => 'm', 'week' => 'w', 'day' => 'd', 'hour' => 'h', 'minute' => 'i', 'second' => 's'];
327
        foreach ($string as $k => &$v) {
328
            if ($diff->$v) {
329
                $v = $diff->$v;
330
            }
331
            else unset($string[$k]);
332
        }
333
        $string['status'] = $status;
334
        return count($string) > 1 ? $string : ['status' => 'now'];
335
    }
336
}