|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
declare(strict_types = 1); |
|
4
|
|
|
|
|
5
|
|
|
namespace unreal4u; |
|
6
|
|
|
|
|
7
|
|
|
use \GuzzleHttp\Client; |
|
8
|
|
|
use unreal4u\InternalFunctionality\TelegramDocument; |
|
9
|
|
|
use unreal4u\Telegram\Types\File; |
|
10
|
|
|
|
|
11
|
|
|
/** |
|
12
|
|
|
* The main API which does it all |
|
13
|
|
|
*/ |
|
14
|
|
|
class TgLog |
|
15
|
|
|
{ |
|
16
|
|
|
/** |
|
17
|
|
|
* Stores the token |
|
18
|
|
|
* @var string |
|
19
|
|
|
*/ |
|
20
|
|
|
private $botToken = ''; |
|
21
|
|
|
|
|
22
|
|
|
/** |
|
23
|
|
|
* Stores the API URL from Telegram |
|
24
|
|
|
* @var string |
|
25
|
|
|
*/ |
|
26
|
|
|
private $apiUrl = ''; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* With this flag we'll know what type of request to send to Telegram |
|
30
|
|
|
* |
|
31
|
|
|
* 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker. |
|
32
|
|
|
* 'multipart/form-data' should be used only to upload documents, photos, etc. |
|
33
|
|
|
* |
|
34
|
|
|
* @var string |
|
35
|
|
|
*/ |
|
36
|
|
|
private $formType = 'application/x-www-form-urlencoded'; |
|
37
|
|
|
|
|
38
|
|
|
/** |
|
39
|
|
|
* Stores the last method name used |
|
40
|
|
|
* @var string |
|
41
|
|
|
*/ |
|
42
|
|
|
protected $methodName = ''; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* TelegramLog constructor. |
|
46
|
|
|
* @param string $botToken |
|
47
|
|
|
*/ |
|
48
|
13 |
|
public function __construct(string $botToken) |
|
49
|
|
|
{ |
|
50
|
13 |
|
$this->botToken = $botToken; |
|
51
|
13 |
|
$this->constructApiUrl(); |
|
52
|
13 |
|
} |
|
53
|
|
|
|
|
54
|
|
|
/** |
|
55
|
|
|
* Performs the actual telegram request to telegram's servers |
|
56
|
|
|
* |
|
57
|
|
|
* @param $method |
|
58
|
|
|
* @return mixed |
|
59
|
|
|
*/ |
|
60
|
8 |
|
public function performApiRequest($method) |
|
61
|
|
|
{ |
|
62
|
8 |
|
$this->resetObjectValues(); |
|
63
|
8 |
|
$jsonDecoded = $this->sendRequestToTelegram($method, $this->constructFormData($method)); |
|
64
|
|
|
|
|
65
|
6 |
|
$returnObject = 'unreal4u\\Telegram\\Types\\' . $method::bindToObjectType(); |
|
66
|
6 |
|
return new $returnObject($jsonDecoded->result); |
|
67
|
|
|
} |
|
68
|
|
|
|
|
69
|
|
|
/** |
|
70
|
|
|
* Will download a file from the Telegram server. Before calling this function, you have to call the getFile method! |
|
71
|
|
|
* |
|
72
|
|
|
* @see unreal4u\Telegram\Types\File |
|
73
|
|
|
* @see unreal4u\Telegram\Methods\GetFile |
|
74
|
|
|
* |
|
75
|
|
|
* @param File $file |
|
76
|
|
|
* @return string |
|
77
|
|
|
*/ |
|
78
|
|
|
public function downloadFile(File $file): TelegramDocument |
|
79
|
|
|
{ |
|
80
|
|
|
$url = 'https://api.telegram.org/file/bot' . $this->botToken . '/' . $file->file_path; |
|
81
|
|
|
$client = new Client(); |
|
82
|
|
|
return new TelegramDocument($client->get($url)); |
|
|
|
|
|
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Builds up the Telegram API url |
|
87
|
|
|
* @return TgLog |
|
88
|
|
|
*/ |
|
89
|
13 |
|
final private function constructApiUrl(): TgLog |
|
90
|
|
|
{ |
|
91
|
13 |
|
$this->apiUrl = 'https://api.telegram.org/bot' . $this->botToken; |
|
92
|
13 |
|
return $this; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
/** |
|
96
|
|
|
* This is the method that actually makes the call, which can be easily overwritten so that our unit tests can work |
|
97
|
|
|
* |
|
98
|
|
|
* @param $method |
|
99
|
|
|
* @param array $formData |
|
100
|
|
|
* @return \stdClass |
|
101
|
|
|
*/ |
|
102
|
|
|
protected function sendRequestToTelegram($method, array $formData): \stdClass |
|
103
|
|
|
{ |
|
104
|
|
|
$client = new Client(); |
|
105
|
|
|
$response = $client->post($this->composeApiMethodUrl($method), $formData); |
|
106
|
|
|
return json_decode((string)$response->getBody()); |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
8 |
|
private function resetObjectValues(): TgLog |
|
110
|
|
|
{ |
|
111
|
8 |
|
$this->formType = 'application/x-www-form-urlencoded'; |
|
112
|
8 |
|
$this->methodName = ''; |
|
113
|
|
|
|
|
114
|
8 |
|
return $this; |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
8 |
|
private function constructFormData($method): array |
|
118
|
|
|
{ |
|
119
|
8 |
|
$result = $this->checkSpecialConditions($method); |
|
120
|
|
|
|
|
121
|
8 |
|
switch ($this->formType) { |
|
122
|
|
|
case 'application/x-www-form-urlencoded': |
|
123
|
|
|
$formData = [ |
|
124
|
8 |
|
'form_params' => get_object_vars($method), |
|
125
|
|
|
]; |
|
126
|
8 |
|
break; |
|
127
|
|
|
case 'multipart/form-data': |
|
128
|
|
|
$formData = $this->buildMultipartFormData(get_object_vars($method), $result['id'], $result['stream']); |
|
129
|
|
|
break; |
|
130
|
|
|
default: |
|
131
|
|
|
$formData = []; |
|
132
|
|
|
break; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
8 |
|
return $formData; |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* Can perform any special checks needed to be performed before sending the actual request to Telegram |
|
140
|
|
|
* |
|
141
|
|
|
* This will return an array with data that will be different in each case (for now). This can be changed in the |
|
142
|
|
|
* future. |
|
143
|
|
|
* |
|
144
|
|
|
* @param $method |
|
145
|
|
|
* @return array |
|
146
|
|
|
*/ |
|
147
|
8 |
|
private function checkSpecialConditions($method): array |
|
148
|
|
|
{ |
|
149
|
8 |
|
$return = [false]; |
|
150
|
|
|
|
|
151
|
8 |
|
foreach ($method as $key => $value) { |
|
152
|
6 |
|
if (is_object($value)) { |
|
153
|
1 |
|
if (get_class($value) == 'unreal4u\\Telegram\\Types\\Custom\\InputFile') { |
|
154
|
|
|
// If we are about to send a file, we must use the multipart/form-data way |
|
155
|
|
|
$this->formType = 'multipart/form-data'; |
|
156
|
|
|
$return = [ |
|
157
|
|
|
'id' => $key, |
|
158
|
|
|
'stream' => $value->getStream(), |
|
159
|
|
|
]; |
|
160
|
1 |
|
} elseif (in_array('unreal4u\\InternalFunctionality\\AbstractKeyboardMethods', class_parents($value))) { |
|
161
|
|
|
// If we are about to send a KeyboardMethod, we must send a serialized object |
|
162
|
1 |
|
$method->$key = json_encode($value); |
|
163
|
6 |
|
$return = [true]; |
|
164
|
|
|
} |
|
165
|
|
|
} |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
8 |
|
return $return; |
|
169
|
|
|
} |
|
170
|
|
|
|
|
171
|
|
|
/** |
|
172
|
|
|
* Builds up the URL with which we can work with |
|
173
|
|
|
* |
|
174
|
|
|
* @param $call |
|
175
|
|
|
* @return string |
|
176
|
|
|
*/ |
|
177
|
8 |
|
protected function composeApiMethodUrl($call): string |
|
178
|
|
|
{ |
|
179
|
8 |
|
$completeClassName = get_class($call); |
|
180
|
8 |
|
$this->methodName = lcfirst(substr($completeClassName, strrpos($completeClassName, '\\') + 1)); |
|
181
|
|
|
|
|
182
|
8 |
|
return $this->apiUrl . '/' . $this->methodName; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
/** |
|
186
|
|
|
* Builds up a multipart form-like array for Guzzle |
|
187
|
|
|
* |
|
188
|
|
|
* @param array $data The original object in array form |
|
189
|
|
|
* @param string $fileKeyName A file handler will be sent instead of a string, state here which field it is |
|
190
|
|
|
* @param mixed $stream The actual file handler |
|
191
|
|
|
* @return array Returns the actual formdata to be sent |
|
192
|
|
|
*/ |
|
193
|
|
|
private function buildMultipartFormData(array $data, string $fileKeyName, $stream): array |
|
194
|
|
|
{ |
|
195
|
|
|
$formData = [ |
|
196
|
|
|
'multipart' => [], |
|
197
|
|
|
]; |
|
198
|
|
|
|
|
199
|
|
|
foreach ($data as $id => $value) { |
|
200
|
|
|
// Always send as a string unless it's a file |
|
201
|
|
|
$multiPart = [ |
|
202
|
|
|
'name' => $id, |
|
203
|
|
|
'contents' => null, |
|
204
|
|
|
]; |
|
205
|
|
|
|
|
206
|
|
|
if ($id === $fileKeyName) { |
|
207
|
|
|
$multiPart['contents'] = $stream; |
|
208
|
|
|
} else { |
|
209
|
|
|
$multiPart['contents'] = (string)$value; |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
$formData['multipart'][] = $multiPart; |
|
213
|
|
|
} |
|
214
|
|
|
|
|
215
|
|
|
return $formData; |
|
216
|
|
|
} |
|
217
|
|
|
} |
|
218
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.