Completed
Push — master ( cb0170...e93dba )
by Dmitry
01:58
created

Client::recognizeRecaptchaV2()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 14
nc 4
nop 3
1
<?php
2
3
namespace Rucaptcha;
4
5
use GuzzleHttp\RequestOptions;
6
use Rucaptcha\Exception\ErrorResponseException;
7
use Rucaptcha\Exception\RuntimeException;
8
9
/**
10
 * Class Client
11
 *
12
 * @package Rucaptcha
13
 * @author Dmitry Gladyshev <[email protected]>
14
 */
15
class Client extends GenericClient
16
{
17
    const STATUS_OK_REPORT_RECORDED = 'OK_REPORT_RECORDED';
18
19
    /**
20
     * @var int
21
     */
22
    protected $recaptchaRTimeout = 15;
23
24
    /**
25
     * @var string
26
     */
27
    protected $serverBaseUri = 'http://rucaptcha.com';
28
29
    /**
30
     * Your application ID in Rucaptcha catalog.
31
     * The value `1013` is ID of this library. Set in false if you want to turn off sending any ID.
32
     *
33
     * @see https://rucaptcha.com/software/view/php-api-client
34
     * @var string
35
     */
36
    protected $softId = '1013';
37
38
    /**
39
     * @inheritdoc
40
     */
41
    public function sendCaptcha($content, array $extra = [])
42
    {
43
        if ($this->softId && !isset($extra[Extra::SOFT_ID])) {
44
            $extra[Extra::SOFT_ID] = $this->softId;
45
        }
46
        return parent::sendCaptcha($content, $extra);
47
    }
48
49
    /**
50
     * Bulk captcha result.
51
     *
52
     * @param int[] $captchaIds         # Captcha task Ids array
53
     * @return string[]                 # Array $captchaId => $captchaText or false if is not ready
54
     * @throws ErrorResponseException
55
     */
56
    public function getCaptchaResultBulk(array $captchaIds)
57
    {
58
        $response = $this->getHttpClient()->request('GET', '/res.php?' . http_build_query([
59
                'key' => $this->apiKey,
60
                'action' => 'get',
61
                'ids' => join(',', $captchaIds)
62
            ]));
63
64
        $captchaTexts = $response->getBody()->__toString();
65
66
        $this->getLogger()->info("Got bulk response: `{$captchaTexts}`.");
67
68
        $captchaTexts = explode("|", $captchaTexts);
69
70
        $result = [];
71
72
        foreach ($captchaTexts as $index => $captchaText) {
73
            $captchaText = html_entity_decode(trim($captchaText));
74
            $result[$captchaIds[$index]] =
75
                ($captchaText == self::STATUS_CAPTCHA_NOT_READY) ? false : $captchaText;
76
        }
77
78
        return $result;
79
    }
80
81
    /**
82
     * Returns balance of account.
83
     *
84
     * @return string
85
     */
86
    public function getBalance()
87
    {
88
        $response = $this
89
            ->getHttpClient()
90
            ->request('GET', "/res.php?key={$this->apiKey}&action=getbalance");
91
92
        return $response->getBody()->__toString();
93
    }
94
95
    /**
96
     * Report of wrong recognition.
97
     *
98
     * @param string $captchaId
99
     * @return bool
100
     * @throws ErrorResponseException
101
     */
102
    public function badCaptcha($captchaId)
103
    {
104
        $response = $this
105
            ->getHttpClient()
106
            ->request('GET', "/res.php?key={$this->apiKey}&action=reportbad&id={$captchaId}");
107
108
        $responseText = $response->getBody()->__toString();
109
110
        if ($responseText === self::STATUS_OK_REPORT_RECORDED) {
111
            return true;
112
        }
113
114
        throw new ErrorResponseException(
115
            $this->getErrorMessage($responseText) ?: $responseText,
116
            $this->getErrorCode($responseText) ?: 0
117
        );
118
    }
119
120
    /**
121
     * Returns server health data.
122
     *
123
     * @param string|string[] $paramsList   # List of metrics to be returned
124
     * @return array                        # Array of load metrics $metric => $value formatted
125
     */
126
    public function getLoad($paramsList = ['waiting', 'load', 'minbid', 'averageRecognitionTime'])
127
    {
128
        $parser = $this->getLoadXml();
129
130
        if (is_string($paramsList)) {
131
            return $parser->$paramsList->__toString();
132
        }
133
134
        $statusData = [];
135
136
        foreach ($paramsList as $item) {
137
            $statusData[$item] = $parser->$item->__toString();
138
        }
139
140
        return $statusData;
141
    }
142
143
    /**
144
     * Returns load data as XML.
145
     *
146
     * @return \SimpleXMLElement
147
     */
148
    public function getLoadXml()
149
    {
150
        $response = $this
151
            ->getHttpClient()
152
            ->request('GET', "/load.php");
153
154
        return new \SimpleXMLElement($response->getBody()->__toString());
155
    }
156
157
    /**
158
     * @param string $captchaId     # Captcha task ID
159
     * @return array | false        # Solved captcha and cost array or false if captcha is not ready
160
     * @throws ErrorResponseException
161
     */
162
    public function getCaptchaResultWithCost($captchaId)
163
    {
164
        $response = $this
165
            ->getHttpClient()
166
            ->request('GET', "/res.php?key={$this->apiKey}&action=get2&id={$captchaId}");
167
168
        $responseText = $response->getBody()->__toString();
169
170
        if ($responseText === self::STATUS_CAPTCHA_NOT_READY) {
171
            return false;
172
        }
173
174
        if (strpos($responseText, 'OK|') !== false) {
175
            $this->getLogger()->info("Got OK response: `{$responseText}`.");
176
            $data = explode('|', $responseText);
177
            return [
178
                'captcha' => html_entity_decode(trim($data[1])),
179
                'cost' => html_entity_decode(trim($data[2])),
180
            ];
181
        }
182
183
        throw new ErrorResponseException(
184
            $this->getErrorMessage($responseText) ?: $responseText,
185
            $this->getErrorCode($responseText) ?: 0
186
        );
187
    }
188
189
    /**
190
     * Add pingback url to rucaptcha whitelist.
191
     *
192
     * @param string $url
193
     * @return bool                     # true if added and exception if fail
194
     * @throws ErrorResponseException
195
     */
196
    public function addPingback($url)
197
    {
198
        $response = $this
199
            ->getHttpClient()
200
            ->request('GET', "/res.php?key={$this->apiKey}&action=add_pingback&addr={$url}");
201
202
        $responseText = $response->getBody()->__toString();
203
204
        if ($responseText === self::STATUS_OK) {
205
            return true;
206
        }
207
208
        throw new ErrorResponseException(
209
            $this->getErrorMessage($responseText) ?: $responseText,
210
            $this->getErrorCode($responseText) ?: 0
211
        );
212
    }
213
214
    /**
215
     * Returns pingback whitelist items.
216
     *
217
     * @return string[]                 # List of urls
218
     * @throws ErrorResponseException
219
     */
220
    public function getPingbacks()
221
    {
222
        $response = $this
223
            ->getHttpClient()
224
            ->request('GET', "/res.php?key={$this->apiKey}&action=get_pingback");
225
226
        $responseText = $response->getBody()->__toString();
227
228
        if (strpos($responseText, 'OK|') !== false) {
229
            $data = explode('|', $responseText);
230
            unset($data[0]);
231
            return empty($data[1]) ? [] : array_values($data);
232
        }
233
234
        throw new ErrorResponseException(
235
            $this->getErrorMessage($responseText) ?: $responseText,
236
            $this->getErrorCode($responseText) ?: 0
237
        );
238
    }
239
240
    /**
241
     * Remove pingback url from whitelist.
242
     *
243
     * @param string $uri
244
     * @return bool
245
     * @throws ErrorResponseException
246
     */
247
    public function deletePingback($uri)
248
    {
249
        $response = $this
250
            ->getHttpClient()
251
            ->request('GET', "/res.php?key={$this->apiKey}&action=del_pingback&addr={$uri}");
252
253
        $responseText = $response->getBody()->__toString();
254
255
        if ($responseText === self::STATUS_OK) {
256
            return true;
257
        }
258
        throw new ErrorResponseException(
259
            $this->getErrorMessage($responseText) ?: $responseText,
260
            $this->getErrorCode($responseText) ?: 0
261
        );
262
    }
263
264
    /**
265
     * Truncate pingback whitelist.
266
     *
267
     * @return bool
268
     * @throws ErrorResponseException
269
     */
270
    public function deleteAllPingbacks()
271
    {
272
        return $this->deletePingback('all');
273
    }
274
275
    /* Recaptcha v2 */
276
277
    public function sendRecapthaV2($googleKey, $pageUrl, $extra = [])
278
    {
279
        $this->getLogger()->info("Try send google key (recaptcha)  on {$this->serverBaseUri}/in.php");
280
281
        $response = $this->getHttpClient()->request('POST', "/in.php", [
282
            RequestOptions::QUERY => array_merge($extra, [
283
                'method' => 'userrecaptcha',
284
                'key' => $this->apiKey,
285
                'googlekey' => $googleKey,
286
                'pageurl' => $pageUrl
287
            ])
288
        ]);
289
290
        $responseText = $response->getBody()->__toString();
291
292
        if (strpos($responseText, 'OK|') !== false) {
293
            $this->lastCaptchaId = explode("|", $responseText)[1];
294
            $this->getLogger()->info("Sending success. Got captcha id `{$this->lastCaptchaId}`.");
295
            return $this->lastCaptchaId;
296
        }
297
298
        throw new ErrorResponseException($this->getErrorMessage($responseText) ?: "Unknown error: `{$responseText}`.");
299
    }
300
301
    /**
302
     * @param string $googleKey
303
     * @param string $pageUrl
304
     * @param array $extra      # Captcha options
305
     * @return string           # Code to place in hidden form
306
     * @throws RuntimeException
307
     */
308
    public function recognizeRecaptchaV2($googleKey, $pageUrl, $extra = [])
309
    {
310
        $captchaId = $this->sendRecapthaV2($googleKey, $pageUrl, $extra);
311
        $startTime = time();
312
313
        while (true) {
314
            $this->getLogger()->info("Waiting {$this->rTimeout} sec.");
315
316
            sleep($this->recaptchaRTimeout);
317
318
            if (time() - $startTime >= $this->mTimeout) {
319
                throw new RuntimeException("Captcha waiting timeout.");
320
            }
321
322
            $result = $this->getCaptchaResult($captchaId);
323
324
            if ($result === false) {
325
                continue;
326
            }
327
328
            $this->getLogger()->info("Elapsed " . (time()-$startTime) . " second(s).");
329
330
            return $result;
331
        }
332
333
        throw new RuntimeException('Unknown recognition logic error.');
334
    }
335
336
    /**
337
     * Match error code by response.
338
     *
339
     * @param string $responseText
340
     * @return int
341
     */
342
    private function getErrorCode($responseText)
343
    {
344
        if (preg_match('/ERROR:\s*(\d{0,4})/ui', $responseText, $matches)) {
345
            return intval($matches[1]);
346
        }
347
        return 0;
348
    }
349
}
350