1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of GitterBot package. |
5
|
|
|
* |
6
|
|
|
* @author Serafim <[email protected]> |
7
|
|
|
* @author butschster <[email protected]> |
8
|
|
|
* @date 24.09.2015 00:00 |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please view the LICENSE |
11
|
|
|
* file that was distributed with this source code. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace Interfaces\Gitter; |
15
|
|
|
|
16
|
|
|
use Domains\Bot\ClientInterface; |
17
|
|
|
use Domains\Bot\TextParserInterface; |
18
|
|
|
use Domains\User; |
19
|
|
|
use Domains\Message; |
20
|
|
|
use Interfaces\Gitter\Http\Stream; |
21
|
|
|
use Interfaces\Gitter\Http\Request; |
22
|
|
|
use Domains\Middleware\Storage; |
23
|
|
|
use Domains\Room\RoomInterface; |
24
|
|
|
use InvalidArgumentException; |
25
|
|
|
use Interfaces\Gitter\Http\UrlStorage; |
26
|
|
|
use React\EventLoop\Factory as EventLoop; |
27
|
|
|
use React\HttpClient\Client as ReactClient; |
28
|
|
|
use React\HttpClient\Factory as HttpClient; |
29
|
|
|
use React\Dns\Resolver\Factory as DnsResolver; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Class Client |
33
|
|
|
*/ |
34
|
|
|
class Client implements ClientInterface |
35
|
|
|
{ |
36
|
|
|
const VERSION = 'KarmaBot for Gitter 0.1b'; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $token; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var \React\EventLoop\ExtEventLoop|\React\EventLoop\LibEventLoop|\React\EventLoop\LibEvLoop|\React\EventLoop\StreamSelectLoop |
45
|
|
|
*/ |
46
|
|
|
protected $loop; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var \React\Dns\Resolver\Resolver |
50
|
|
|
*/ |
51
|
|
|
protected $dnsResolver; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var UrlStorage |
55
|
|
|
*/ |
56
|
|
|
protected $urlStorage; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var string |
60
|
|
|
*/ |
61
|
|
|
protected $room; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var User |
65
|
|
|
*/ |
66
|
|
|
protected $user; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var TextParserInterface |
70
|
|
|
*/ |
71
|
|
|
protected $parser; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var \Gitter\Client |
75
|
|
|
*/ |
76
|
|
|
protected $gitterClient; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var ReactClient |
80
|
|
|
*/ |
81
|
|
|
protected $httpClient; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Client constructor. |
85
|
|
|
* |
86
|
|
|
* @param string $token |
87
|
|
|
*/ |
88
|
|
|
public function __construct($token) |
89
|
|
|
{ |
90
|
|
|
$this->token = $token; |
91
|
|
|
$this->loop = EventLoop::create(); |
92
|
|
|
$this->dnsResolver = (new DnsResolver())->createCached('8.8.8.8', $this->loop); |
93
|
|
|
$this->httpClient = (new HttpClient())->create($this->loop, $this->dnsResolver); |
94
|
|
|
$this->urlStorage = new UrlStorage($token); |
95
|
|
|
$this->gitterClient = new \Gitter\Client($token); |
96
|
|
|
|
97
|
|
|
$this->authAs(null); |
98
|
|
|
$this->parser = new TextParser(''); |
|
|
|
|
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @param null|string $gitterUserId |
103
|
|
|
* @return $this |
104
|
|
|
* @throws InvalidArgumentException |
105
|
|
|
*/ |
106
|
|
|
public function authAs($gitterUserId = null) |
107
|
|
|
{ |
108
|
|
|
$this->user = $this->getUserById($gitterUserId); |
109
|
|
|
|
110
|
|
|
\Auth::loginUsingId($this->user->id); |
111
|
|
|
|
112
|
|
|
return $this; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @return User |
117
|
|
|
*/ |
118
|
|
|
public function getAuthUser() |
119
|
|
|
{ |
120
|
|
|
return $this->user; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @return array |
125
|
|
|
*/ |
126
|
|
|
public function getHeaders(): array |
127
|
|
|
{ |
128
|
|
|
return [ |
129
|
|
|
'Content-Type' => 'application/json', |
130
|
|
|
'Accept' => 'application/json', |
131
|
|
|
'Authorization' => 'Bearer ' . $this->token, |
132
|
|
|
]; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* @return ReactClient |
137
|
|
|
*/ |
138
|
|
|
public function getHttpClient(): ReactClient |
139
|
|
|
{ |
140
|
|
|
return $this->httpClient; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @return UrlStorage |
145
|
|
|
*/ |
146
|
|
|
public function getRouter(): UrlStorage |
147
|
|
|
{ |
148
|
|
|
return $this->urlStorage; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @return \React\EventLoop\ExtEventLoop|\React\EventLoop\LibEventLoop|\React\EventLoop\LibEvLoop|\React\EventLoop\StreamSelectLoop |
153
|
|
|
*/ |
154
|
|
|
public function getEventLoop() |
155
|
|
|
{ |
156
|
|
|
return $this->loop; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* @param $route |
161
|
|
|
* @param array $args |
162
|
|
|
* @param string $method |
163
|
|
|
* @return Stream |
164
|
|
|
* @throws \InvalidArgumentException |
165
|
|
|
*/ |
166
|
|
|
public function stream($route, array $args = [], $method = 'GET'): Stream |
167
|
|
|
{ |
168
|
|
|
return new Stream($this, $route, $args, $method); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @param $route |
173
|
|
|
* @param array $args |
174
|
|
|
* @param null $content |
175
|
|
|
* @param string $method |
176
|
|
|
* @return array |
177
|
|
|
* @throws \InvalidArgumentException |
178
|
|
|
*/ |
179
|
|
|
public function request($route, array $args = [], $content = null, $method = 'GET') |
180
|
|
|
{ |
181
|
|
|
try { |
182
|
|
|
return (new Request($this, $route, $args, $method)) |
183
|
|
|
->sendParseJson($content); |
184
|
|
|
|
185
|
|
|
} catch (\Exception $e) { |
186
|
|
|
return $this->request($route, $args, $content, $method); |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return ClientInterface |
192
|
|
|
*/ |
193
|
|
|
public function run(): ClientInterface |
194
|
|
|
{ |
195
|
|
|
$this->loop->run(); |
196
|
|
|
|
197
|
|
|
return $this; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @param RoomInterface $room |
202
|
|
|
*/ |
203
|
|
|
public function listen(RoomInterface $room) |
204
|
|
|
{ |
205
|
|
|
$this |
206
|
|
|
->stream('messages', ['roomId' => $room->id()]) |
207
|
|
|
->on(Stream::EVENT_MESSAGE, function ($stream, $data) use($room) { |
208
|
|
|
$this->onMessage( |
209
|
|
|
$room->middleware(), |
210
|
|
|
Message::unguarded(function() use($room, $data) { |
211
|
|
|
return new Message( |
|
|
|
|
212
|
|
|
(new MessageMapper($room, $data))->toArray(), |
213
|
|
|
$room |
214
|
|
|
); |
215
|
|
|
}) |
216
|
|
|
); |
217
|
|
|
}) |
218
|
|
|
->on(Stream::EVENT_END, [$this, 'onClose']) |
219
|
|
|
->on(Stream::EVENT_ERROR, [$this, 'onError']); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return string |
224
|
|
|
*/ |
225
|
|
|
public function version() |
226
|
|
|
{ |
227
|
|
|
return static::VERSION; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param Storage $middleware |
232
|
|
|
* @param Message $message |
233
|
|
|
*/ |
234
|
|
|
public function onMessage(Storage $middleware, Message $message) |
235
|
|
|
{ |
236
|
|
|
try { |
237
|
|
|
$middleware->handle($message); |
238
|
|
|
} catch (\Exception $e) { |
239
|
|
|
$this->logException($e); |
240
|
|
|
} |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* @return \Gitter\Client |
245
|
|
|
*/ |
246
|
|
|
public function getGitterClient(): \Gitter\Client |
247
|
|
|
{ |
248
|
|
|
return $this->gitterClient; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* @TODO I do not know if it works |
253
|
|
|
* @param Stream $stream |
254
|
|
|
*/ |
255
|
|
|
protected function onClose(Stream $stream) |
256
|
|
|
{ |
257
|
|
|
$stream->reconnect(); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* @param Stream $stream |
262
|
|
|
* @param \Exception $e |
263
|
|
|
*/ |
264
|
|
|
protected function onError(Stream $stream, \Exception $e) |
265
|
|
|
{ |
266
|
|
|
$this->logException($e); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* @param \Exception $e |
271
|
|
|
*/ |
272
|
|
View Code Duplication |
protected function logException(\Exception $e) |
|
|
|
|
273
|
|
|
{ |
274
|
|
|
\Log::error( |
275
|
|
|
$e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine() . "\n" . |
276
|
|
|
$e->getTraceAsString() . "\n" . |
277
|
|
|
str_repeat('=', 80) . "\n" |
278
|
|
|
); |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @param RoomInterface $room |
283
|
|
|
* @param string $message |
284
|
|
|
*/ |
285
|
|
|
public function sendMessage(RoomInterface $room, $message) |
286
|
|
|
{ |
287
|
|
|
$this->request('message.send', ['roomId' => $room->id()], [ |
288
|
|
|
'text' => (string) $this->parser->parse($message), |
289
|
|
|
], 'POST'); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* @param string $id |
294
|
|
|
* |
295
|
|
|
* @return User |
296
|
|
|
*/ |
297
|
|
|
public function getUserById($id) |
298
|
|
|
{ |
299
|
|
|
return UserMapper::fromGitterObject( |
300
|
|
|
$this->request('user', ['userId' => $id])[0] |
301
|
|
|
); |
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.