Passed
Push — master ( 53c413...1211a5 )
by Taosikai
15:08
created

Client::setEventDispatcher()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of the slince/smartqq package.
4
 *
5
 * (c) Slince <[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 Slince\SmartQQ;
12
13
use GuzzleHttp\Client as HttpClient;
14
use GuzzleHttp\Cookie\CookieJar;
15
use GuzzleHttp\Psr7\Request as HttpRequest;
16
use GuzzleHttp\Psr7\Response as HttpResponse;
17
use Slince\EventDispatcher\Dispatcher;
18
use Slince\EventDispatcher\DispatcherInterface;
19
use Slince\SmartQQ\Entity;
20
use Slince\SmartQQ\Exception\InvalidArgumentException;
21
use Slince\SmartQQ\Exception\RuntimeException;
22
use Slince\SmartQQ\Message\Request\FriendMessage;
23
use Slince\SmartQQ\Message\Request\GroupMessage;
24
use Slince\SmartQQ\Message\Request\Message as RequestMessage;
25
use Slince\SmartQQ\Message\Response\Message as ResponseMessage;
26
use Slince\SmartQQ\Request;
27
28
29
class Client
30
{
31
    /**
32
     * @var Credential
33
     */
34
    protected $credential;
35
36
    /**
37
     * 客户端id(固定值).
38
     *
39
     * @var int
40
     */
41
    protected static $clientId = 53999199;
42
43
    /**
44
     * 获取ptwebqq的地址
45
     *
46
     * @var string
47
     */
48
    protected $certificationUrl;
49
50
    /**
51
     * @var HttpClient
52
     */
53
    protected $httpClient;
54
55
    /**
56
     * @var DispatcherInterface
57
     */
58
    protected $eventDispatcher;
59
60
    /**
61
     * @var CookieJar
62
     */
63
    protected $cookies;
64
65
    /**
66
     * @var MessageHandler
67
     */
68
    protected $messageHandler;
69
70
    public function __construct(
71
        Credential $credential = null,
72
        HttpClient $httpClient = null,
73
        DispatcherInterface $eventDispatcher = null
74
    )
75
    {
76
        if (!is_null($credential)) {
77
            $this->setCredential($credential);
78
        }
79
        if (is_null($httpClient)) {
80
            $httpClient = new HttpClient([
81
                'verify' => false,
82
            ]);
83
        }
84
        if (is_null($eventDispatcher)) {
85
            $eventDispatcher = new Dispatcher();
86
        }
87
        $this->httpClient = $httpClient;
88
        $this->eventDispatcher = $eventDispatcher;
89
    }
90
91
    /**
92
     * 开启登录流程自行获取凭证
93
     *
94
     * @param string $loginQRImage 二维码图片位置
95
     *
96
     * @return Credential
97
     */
98
    public function login($loginQRImage)
99
    {
100
        //如果登录则重置cookie
101
        $this->cookies = new CookieJar();
102
        $qrSign = $this->makeQrCodeImage($loginQRImage);
103
        $ptQrToken = Utils::hash33($qrSign);
104
        while (true) {
105
            $status = $this->verifyQrCodeStatus($ptQrToken);
106
            if (Request\VerifyQrCodeRequest::STATUS_EXPIRED == $status) {
107
                $qrSign = $this->makeQrCodeImage($loginQRImage);
108
                $ptQrToken = Utils::hash33($qrSign);
109
            } elseif (Request\VerifyQrCodeRequest::STATUS_CERTIFICATION == $status) {
110
                //授权成功跳出状态检查
111
                break;
112
            }
113
            sleep(1);
114
        }
115
        $ptWebQQ = $this->getPtWebQQ($this->certificationUrl);
116
        $vfWebQQ = $this->getVfWebQQ($ptWebQQ);
117
        list($uin, $pSessionId) = $this->getUinAndPSessionId($ptWebQQ);
118
        $this->credential = new Credential($ptWebQQ, $vfWebQQ, $pSessionId, $uin, static::$clientId, $this->cookies);
119
        //获取在线状态避免103
120
        $this->getFriendsOnlineStatus();
121
122
        return $this->credential;
123
    }
124
125
    /**
126
     * 创建登录所需的二维码
127
     *
128
     * @param string $loginQRImage
129
     *
130
     * @return string
131
     */
132
    protected function makeQrCodeImage($loginQRImage)
133
    {
134
        $response = $this->sendRequest(new Request\GetQrCodeRequest());
135
        Utils::getFilesystem()->dumpFile($loginQRImage, $response->getBody());
136
        foreach ($this->getCookies() as $cookie) {
137
            if (0 == strcasecmp($cookie->getName(), 'qrsig')) {
138
                return $cookie->getValue();
139
            }
140
        }
141
        throw new RuntimeException('Can not find parameter [qrsig]');
142
    }
143
144
    /**
145
     * 验证二维码状态
146
     *
147
     * @param int $ptQrToken qr token
148
     *
149
     * @return int
150
     */
151
    protected function verifyQrCodeStatus($ptQrToken)
152
    {
153
        $request = new Request\VerifyQrCodeRequest($ptQrToken);
154
        $response = $this->sendRequest($request);
155
        if (false !== strpos($response->getBody(), '未失效')) {
156
            $status = Request\VerifyQrCodeRequest::STATUS_UNEXPIRED;
157
        } elseif (false !== strpos($response->getBody(), '已失效')) {
158
            $status = Request\VerifyQrCodeRequest::STATUS_EXPIRED;
159
        } elseif (false !== strpos($response->getBody(), '认证中')) {
160
            $status = Request\VerifyQrCodeRequest::STATUS_ACCREDITATION;
161
        } else {
162
            $status = Request\VerifyQrCodeRequest::STATUS_CERTIFICATION;
163
            //找出认证url
164
            if (preg_match("#'(http.+)'#U", strval($response->getBody()), $matches)) {
165
                $this->certificationUrl = trim($matches[1]);
166
            } else {
167
                throw new RuntimeException('Can not find certification url');
168
            }
169
        }
170
171
        return $status;
172
    }
173
174
    /**
175
     * 获取ptwebqq的参数值
176
     *
177
     * @param string $certificationUrl
178
     *
179
     * @return string
180
     */
181
    protected function getPtWebQQ($certificationUrl)
182
    {
183
        $request = new Request\GetPtWebQQRequest();
184
        $request->setUri($certificationUrl);
185
        $this->sendRequest($request);
186
        foreach ($this->getCookies() as $cookie) {
187
            if (0 == strcasecmp($cookie->getName(), 'ptwebqq')) {
188
                return $cookie->getValue();
189
            }
190
        }
191
        throw new RuntimeException('Can not find parameter [ptwebqq]');
192
    }
193
194
    /**
195
     * @param string $ptWebQQ
196
     *
197
     * @return string
198
     */
199
    protected function getVfWebQQ($ptWebQQ)
200
    {
201
        $request = new Request\GetVfWebQQRequest($ptWebQQ);
202
        $response = $this->sendRequest($request);
203
204
        return Request\GetVfWebQQRequest::parseResponse($response);
205
    }
206
207
    /**
208
     * 获取pessionid和uin.
209
     *
210
     * @param string $ptWebQQ
211
     *
212
     * @return array
213
     */
214
    protected function getUinAndPSessionId($ptWebQQ)
215
    {
216
        $request = new Request\GetUinAndPsessionidRequest([
217
            'ptwebqq' => $ptWebQQ,
218
            'clientid' => static::$clientId,
219
            'psessionid' => '',
220
            'status' => 'online',
221
        ]);
222
        $response = $this->sendRequest($request);
223
224
        return Request\GetUinAndPsessionidRequest::parseResponse($response);
225
    }
226
227
    /**
228
     * @param Credential $credential
229
     */
230
    public function setCredential(Credential $credential)
231
    {
232
        $this->cookies = $credential->getCookies();
233
        $this->credential = $credential;
234
    }
235
236
    /**
237
     * @return Credential
238
     */
239
    public function getCredential()
240
    {
241
        if (!$this->credential) {
242
            throw new InvalidArgumentException('Please login first or set a credential');
243
        }
244
245
        return $this->credential;
246
    }
247
248
    /**
249
     * @return CookieJar
250
     */
251
    public function getCookies()
252
    {
253
        return $this->cookies;
254
    }
255
256
    /**
257
     * @param HttpClient $httpClient
258
     */
259
    public function setHttpClient($httpClient)
260
    {
261
        $this->httpClient = $httpClient;
262
    }
263
264
    /**
265
     * @return HttpClient
266
     */
267
    public function getHttpClient()
268
    {
269
        return $this->httpClient;
270
    }
271
272
    /**
273
     * @param DispatcherInterface $eventDispatcher
274
     */
275
    public function setEventDispatcher($eventDispatcher)
276
    {
277
        $this->eventDispatcher = $eventDispatcher;
278
    }
279
280
    /**
281
     * @return DispatcherInterface
282
     */
283
    public function getEventDispatcher()
284
    {
285
        return $this->eventDispatcher;
286
    }
287
288
    /**
289
     * 获取所有的群.
290
     *
291
     * @return EntityCollection
292
     */
293
    public function getGroups()
294
    {
295
        $request = new Request\GetGroupsRequest($this->getCredential());
296
        $response = $this->sendRequest($request);
297
298
        return Request\GetGroupsRequest::parseResponse($response);
299
    }
300
301
    /**
302
     * 获取群详细信息.
303
     *
304
     * @param Entity\Group $group
305
     *
306
     * @return Entity\GroupDetail
307
     */
308
    public function getGroupDetail(Entity\Group $group)
309
    {
310
        $request = new Request\GetGroupDetailRequest($group, $this->getCredential());
311
        $response = $this->sendRequest($request);
312
313
        return Request\GetGroupDetailRequest::parseResponse($response);
314
    }
315
316
    /**
317
     * 获取所有讨论组.
318
     *
319
     * @return EntityCollection
320
     */
321
    public function getDiscusses()
322
    {
323
        $request = new Request\GetDiscussesRequest($this->getCredential());
324
        $response = $this->sendRequest($request);
325
326
        return Request\GetDiscussesRequest::parseResponse($response);
327
    }
328
329
    /**
330
     * 获取讨论组详情.
331
     *
332
     * @param Entity\Discuss $discuss
333
     *
334
     * @return Entity\DiscussDetail
335
     */
336
    public function getDiscussDetail(Entity\Discuss $discuss)
337
    {
338
        $request = new Request\GetDiscussDetailRequest($discuss, $this->getCredential());
339
        $response = $this->sendRequest($request);
340
341
        return Request\GetDiscussDetailRequest::parseResponse($response);
342
    }
343
344
    /**
345
     * 获取所有的好友.
346
     *
347
     * @return EntityCollection|Entity\Friend[]
348
     */
349
    public function getFriends()
350
    {
351
        $request = new Request\GetFriendsRequest($this->getCredential());
352
        $response = $this->sendRequest($request);
353
354
        return Request\GetFriendsRequest::parseResponse($response);
355
    }
356
357
    /**
358
     * 获取好友的详细信息.
359
     *
360
     * @param Entity\Friend $friend
361
     *
362
     * @return Entity\Profile
363
     */
364
    public function getFriendDetail(Entity\Friend $friend)
365
    {
366
        $request = new Request\GetFriendDetailRequest($friend, $this->getCredential());
367
        $response = $this->sendRequest($request);
368
369
        return Request\GetFriendDetailRequest::parseResponse($response);
370
    }
371
372
    /**
373
     * 获取好友的QQ号.
374
     *
375
     * @param Entity\Friend $friend
376
     *
377
     * @return int
378
     * @deprecated 此接口 Smartqq 官方已经不再提供
379
     */
380
    public function getFriendQQ(Entity\Friend $friend)
381
    {
382
        @trigger_error('The api is not supported now',E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
383
384
        $request = new Request\GetQQRequest($friend, $this->getCredential());
385
        $response = $this->sendRequest($request);
386
        $qq = Request\GetQQRequest::parseResponse($response);
387
        $friend->setQq($qq);
388
389
        return $qq;
390
    }
391
392
    /**
393
     * 获取好友的个性签名.
394
     *
395
     * @param Entity\Friend $friend
396
     *
397
     * @return string
398
     */
399
    public function getFriendLnick(Entity\Friend $friend)
400
    {
401
        $request = new Request\GetLnickRequest($friend, $this->getCredential());
402
        $response = $this->sendRequest($request);
403
404
        return Request\GetLnickRequest::parseResponse($response, $friend);
405
    }
406
407
    /**
408
     * 获取好友在线状态
409
     *
410
     * @return EntityCollection
411
     */
412
    public function getFriendsOnlineStatus()
413
    {
414
        $request = new Request\GetFriendsOnlineStatusRequest($this->getCredential());
415
        $response = $this->sendRequest($request);
416
417
        return Request\GetFriendsOnlineStatusRequest::parseResponse($response);
418
    }
419
420
    /**
421
     * 获取最近的会话.
422
     *
423
     * @return EntityCollection
424
     */
425
    public function getRecentList()
426
    {
427
        $request = new Request\GetRecentListRequest($this->getCredential());
428
        $response = $this->sendRequest($request);
429
430
        return Request\GetRecentListRequest::parseResponse($response);
431
    }
432
433
    /**
434
     * 获取当前登录用户信息.
435
     *
436
     * @return Entity\Profile
437
     */
438
    public function getCurrentUserInfo()
439
    {
440
        $request = new Request\GetCurrentUserRequest();
441
        $response = $this->sendRequest($request);
442
443
        return Request\GetCurrentUserRequest::parseResponse($response);
444
    }
445
446
    /**
447
     * 轮询消息,
448
     * client并不会组装信息,只是将接口返回的信息完整抽象并返回
449
     * 如果需要查询信息对应的数据,如发送人、发送群,请自行获取.
450
     *
451
     * @return ResponseMessage[]
452
     */
453
    public function pollMessages()
454
    {
455
        $request = new Request\PollMessagesRequest($this->getCredential());
456
        $response = $this->sendRequest($request);
457
458
        return Request\PollMessagesRequest::parseResponse($response);
459
    }
460
461
    /**
462
     * 获取 Message handler.
463
     *
464
     * @return MessageHandler
465
     */
466
    public function getMessageHandler()
467
    {
468
        if (null !== $this->messageHandler) {
469
            return $this->messageHandler;
470
        }
471
        return $this->messageHandler = new MessageHandler($this);
472
    }
473
474
    /**
475
     * 发送消息,包括好友消息,群消息,讨论组消息.
476
     *
477
     * @param RequestMessage $message
478
     *
479
     * @return bool
480
     */
481
    public function sendMessage(RequestMessage $message)
482
    {
483
        if ($message instanceof FriendMessage) {
484
            $request = new Request\SendFriendMessageRequest($message, $this->getCredential());
485
        } elseif ($message instanceof GroupMessage) {
486
            $request = new Request\SendGroupMessageRequest($message, $this->getCredential());
487
        } else {
488
            $request = new Request\SendDiscusMessageRequest($message, $this->getCredential());
0 ignored issues
show
Compatibility introduced by
$message of type object<Slince\SmartQQ\Message\Request\Message> is not a sub-type of object<Slince\SmartQQ\Me...Request\DiscussMessage>. It seems like you assume a child class of the class Slince\SmartQQ\Message\Request\Message to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
489
        }
490
        $response = $this->sendRequest($request);
491
492
        return Request\SendMessageRequest::parseResponse($response);
493
    }
494
495
496
    /**
497
     * @param Request\RequestInterface $request
498
     *
499
     * @return HttpResponse
500
     */
501
    protected function sendRequest(Request\RequestInterface $request)
502
    {
503
        $options = [
504
            'cookies' => $this->getCookies(),
505
        ];
506
        if ($parameters = $request->getParameters()) {
507
            if (Request\RequestInterface::REQUEST_METHOD_GET == $request->getMethod()) {
508
                $options['query'] = $parameters;
509
            } else {
510
                $options['form_params'] = $parameters;
511
            }
512
        }
513
        //如果有referer需要伪造该信息
514
        if ($referer = $request->getReferer()) {
515
            $options['headers'] = [
516
                'Referer' => $referer,
517
            ];
518
        }
519
        $response = $this->httpClient->send(
520
            new HttpRequest($request->getMethod(), $request->getUri()),
521
            $options
522
        );
523
524
        return $response;
525
    }
526
}
527