{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

-- |
-- Module: Captcha.TwoCaptcha.Internal
-- Copyright: (c) 2022 Edward Yang
-- License: MIT
--
-- This module is for internal-use and does not follow pvp versioning policies.
module Captcha.TwoCaptcha.Internal where

import Captcha.Internal (getProxyAddress, getProxyPassword, getProxyPort, getProxyUsername)
import Captcha.Internal.Monad (HasCaptchaEnv)
import Captcha.Internal.Monad.Class (CaptchaId (CaptchaId, unCaptchaId), CaptchaRequest (request), CaptchaResponse (parseResult), MonadCaptcha (CaptchaError, createTask, getTask, solve))
import Captcha.Internal.Request (get)
import Captcha.Internal.Types (HasApiKey (apiKey), HasPollingInterval (pollingInterval), HasProtocol (protocol), HasProxy (proxy), HasTimeoutDuration (timeoutDuration), Proxy)
import Captcha.TwoCaptcha.Internal.Error (TwoCaptchaError (NetworkError, TimeoutError, TwoCaptchaResponseError, UnknownError, UnknownResponseError), TwoCaptchaErrorCode (BadDuplicates, CaptchaNotReady, CaptchaUnsolvable), parseError)
import Control.Error (ExceptT (ExceptT), note, runExceptT)
import Control.Lens (preview, view, (&), (.~), (^.), (^?), _Just)
import Control.Monad (liftM2, (<=<))
import Control.Monad.Reader (MonadReader)
import Data.Aeson (Value)
import Data.Aeson.Lens (key, _Integer, _String, _Value)
import Data.Bifunctor (Bifunctor (bimap))
import Data.ByteString.Lazy (ByteString)
import Data.Maybe (fromMaybe, maybeToList)
import Data.String.Conversions (cs)
import Data.String.Interpolate (i)
import Data.Text (Text, toUpper)
import Data.Text.Read (decimal)
import Network.HTTP.Client (HttpException)
import Network.Wreq (Options, Response, defaults, param, responseBody)
import Time (Microsecond, Millisecond, Time (Time), threadDelay, toNum)
import UnliftIO (MonadUnliftIO, timeout, try)

-- | Used for picking 'MonadCaptcha' instances for 2Captcha.
data TwoCaptcha

-- | Parse the http response into the captcha answer, handling any errors found.
parseResponse :: (Value -> Maybe Value) -> Either HttpException (Response ByteString) -> Either TwoCaptchaError Value
parseResponse :: (Value -> Maybe Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError Value
parseResponse Value -> Maybe Value
f Either HttpException (Response ByteString)
response =
  (((Integer, Text, Text) -> Either TwoCaptchaError Value)
 -> Either TwoCaptchaError (Integer, Text, Text)
 -> Either TwoCaptchaError Value)
-> (Value -> (Integer, Text, Text) -> Either TwoCaptchaError Value)
-> (Value -> Either TwoCaptchaError (Integer, Text, Text))
-> Value
-> Either TwoCaptchaError Value
forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 ((Integer, Text, Text) -> Either TwoCaptchaError Value)
-> Either TwoCaptchaError (Integer, Text, Text)
-> Either TwoCaptchaError Value
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
(=<<) Value -> (Integer, Text, Text) -> Either TwoCaptchaError Value
adaptError Value -> Either TwoCaptchaError (Integer, Text, Text)
forall s.
AsValue s =>
s -> Either TwoCaptchaError (Integer, Text, Text)
parseStatus (Value -> Either TwoCaptchaError Value)
-> Either TwoCaptchaError Value -> Either TwoCaptchaError Value
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Either TwoCaptchaError Value
parseBody
  where
    errorFooter :: Text
errorFooter = Text
"This is likely due to a change in 2Captcha's API and will need to be fixed." :: Text
    missingResponse :: Text
missingResponse = [i|The response body is missing. #{errorFooter}|]
    invalidResponse :: Text
invalidResponse = [i|Unable to parse 2Captcha response. #{errorFooter}|]
    parseBody :: Either TwoCaptchaError Value
parseBody = TwoCaptchaError -> Maybe Value -> Either TwoCaptchaError Value
forall a b. a -> Maybe b -> Either a b
note (Text -> TwoCaptchaError
UnknownError Text
missingResponse) (Maybe Value -> Either TwoCaptchaError Value)
-> Either TwoCaptchaError (Maybe Value)
-> Either TwoCaptchaError Value
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (HttpException -> TwoCaptchaError)
-> (Response ByteString -> Maybe Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError (Maybe Value)
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap HttpException -> TwoCaptchaError
NetworkError (Getting (First Value) (Response ByteString) Value
-> Response ByteString -> Maybe Value
forall s (m :: * -> *) a.
MonadReader s m =>
Getting (First a) s a -> m (Maybe a)
preview (Getting (First Value) (Response ByteString) Value
 -> Response ByteString -> Maybe Value)
-> Getting (First Value) (Response ByteString) Value
-> Response ByteString
-> Maybe Value
forall a b. (a -> b) -> a -> b
$ (ByteString -> Const (First Value) ByteString)
-> Response ByteString -> Const (First Value) (Response ByteString)
forall body0 body1.
Lens (Response body0) (Response body1) body0 body1
responseBody ((ByteString -> Const (First Value) ByteString)
 -> Response ByteString
 -> Const (First Value) (Response ByteString))
-> ((Value -> Const (First Value) Value)
    -> ByteString -> Const (First Value) ByteString)
-> Getting (First Value) (Response ByteString) Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Value -> Const (First Value) Value)
-> ByteString -> Const (First Value) ByteString
forall t. AsValue t => Prism' t Value
_Value) Either HttpException (Response ByteString)
response
    parseStatus :: s -> Either TwoCaptchaError (Integer, Text, Text)
parseStatus s
body = TwoCaptchaError
-> Maybe (Integer, Text, Text)
-> Either TwoCaptchaError (Integer, Text, Text)
forall a b. a -> Maybe b -> Either a b
note (Text -> TwoCaptchaError
UnknownError Text
"2Captcha response body is missing a status/request field.") (Maybe (Integer, Text, Text)
 -> Either TwoCaptchaError (Integer, Text, Text))
-> Maybe (Integer, Text, Text)
-> Either TwoCaptchaError (Integer, Text, Text)
forall a b. (a -> b) -> a -> b
$ do
      Integer
status <- s
body s -> Getting (First Integer) s Integer -> Maybe Integer
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' s Value
forall t. AsValue t => Text -> Traversal' t Value
key Text
"status" ((Value -> Const (First Integer) Value)
 -> s -> Const (First Integer) s)
-> ((Integer -> Const (First Integer) Integer)
    -> Value -> Const (First Integer) Value)
-> Getting (First Integer) s Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Integer -> Const (First Integer) Integer)
-> Value -> Const (First Integer) Value
forall t. AsNumber t => Prism' t Integer
_Integer
      Text
code <- s
body s -> Getting (First Text) s Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' s Value
forall t. AsValue t => Text -> Traversal' t Value
key Text
"request" ((Value -> Const (First Text) Value) -> s -> Const (First Text) s)
-> ((Text -> Const (First Text) Text)
    -> Value -> Const (First Text) Value)
-> Getting (First Text) s Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Const (First Text) Text)
-> Value -> Const (First Text) Value
forall t. AsPrimitive t => Prism' t Text
_String
      (Integer, Text, Text) -> Maybe (Integer, Text, Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
status, Text
code, Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
forall a. Monoid a => a
mempty (s
body s -> Getting (First Text) s Text -> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? Text -> Traversal' s Value
forall t. AsValue t => Text -> Traversal' t Value
key Text
"error_text" ((Value -> Const (First Text) Value) -> s -> Const (First Text) s)
-> ((Text -> Const (First Text) Text)
    -> Value -> Const (First Text) Value)
-> Getting (First Text) s Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Const (First Text) Text)
-> Value -> Const (First Text) Value
forall t. AsPrimitive t => Prism' t Text
_String))
    adaptError :: Value -> (Integer, Text, Text) -> Either TwoCaptchaError Value
adaptError Value
body (Integer
status, Text
code, Text
errorText)
      | Integer
status Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
0 =
        case Text -> Maybe TwoCaptchaErrorCode
parseError Text
code of
          Just TwoCaptchaErrorCode
exception -> TwoCaptchaError -> Either TwoCaptchaError Value
forall a b. a -> Either a b
Left (TwoCaptchaError -> Either TwoCaptchaError Value)
-> TwoCaptchaError -> Either TwoCaptchaError Value
forall a b. (a -> b) -> a -> b
$ TwoCaptchaErrorCode -> TwoCaptchaError
TwoCaptchaResponseError TwoCaptchaErrorCode
exception
          Maybe TwoCaptchaErrorCode
Nothing -> TwoCaptchaError -> Either TwoCaptchaError Value
forall a b. a -> Either a b
Left (TwoCaptchaError -> Either TwoCaptchaError Value)
-> TwoCaptchaError -> Either TwoCaptchaError Value
forall a b. (a -> b) -> a -> b
$ Text -> Text -> TwoCaptchaError
UnknownResponseError Text
code Text
errorText
      | Bool
otherwise = TwoCaptchaError -> Maybe Value -> Either TwoCaptchaError Value
forall a b. a -> Maybe b -> Either a b
note (Text -> TwoCaptchaError
UnknownError Text
invalidResponse) (Value -> Maybe Value
f Value
body)

instance (HasCaptchaEnv r, MonadReader r m, MonadUnliftIO m) => MonadCaptcha TwoCaptcha r m where
  type CaptchaError TwoCaptcha r m = TwoCaptchaError

  createTask :: forall ctx. CaptchaRequest TwoCaptcha ctx r m => ctx -> m (Either TwoCaptchaError (CaptchaId ctx))
  createTask :: ctx -> m (Either TwoCaptchaError (CaptchaId ctx))
createTask ctx
captcha =
    (Value -> Either TwoCaptchaError (CaptchaId ctx)
parseCaptchaId (Value -> Either TwoCaptchaError (CaptchaId ctx))
-> (Either HttpException (Response ByteString)
    -> Either TwoCaptchaError Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError (CaptchaId ctx)
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< (Value -> Maybe Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError Value
parseResponse (Getting (First Value) Value Value -> Value -> Maybe Value
forall s (m :: * -> *) a.
MonadReader s m =>
Getting (First a) s a -> m (Maybe a)
preview (Getting (First Value) Value Value -> Value -> Maybe Value)
-> Getting (First Value) Value Value -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Text -> Traversal' Value Value
forall t. AsValue t => Text -> Traversal' t Value
key Text
"request")) (Either HttpException (Response ByteString)
 -> Either TwoCaptchaError (CaptchaId ctx))
-> m (Either HttpException (Response ByteString))
-> m (Either TwoCaptchaError (CaptchaId ctx))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m (Response ByteString)
-> m (Either HttpException (Response ByteString))
forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> m (Either e a)
try (ctx -> Text -> m (Response ByteString)
forall api ctx r (m :: * -> *).
CaptchaRequest api ctx r m =>
ctx -> Text -> m (Response ByteString)
request @TwoCaptcha @ctx @r @m ctx
captcha Text
url)
    where
      url :: Text
url = Text
"https://2captcha.com/in.php"
      captchaIdMissing :: Text
captchaIdMissing = [i|2Captcha did not send a captcha id after creating a task.|]
      captchaIdInvalid :: src -> dst
captchaIdInvalid src
captchaId = [i|CaptchaId is not an Integer: #{captchaId}|]
      parseCaptchaId :: Value -> Either TwoCaptchaError (CaptchaId ctx)
parseCaptchaId Value
captchaId =
        (String -> TwoCaptchaError)
-> ((Integer, Text) -> CaptchaId ctx)
-> Either String (Integer, Text)
-> Either TwoCaptchaError (CaptchaId ctx)
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (TwoCaptchaError -> String -> TwoCaptchaError
forall a b. a -> b -> a
const (TwoCaptchaError -> String -> TwoCaptchaError)
-> (Text -> TwoCaptchaError) -> Text -> String -> TwoCaptchaError
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> TwoCaptchaError
UnknownError (Text -> String -> TwoCaptchaError)
-> Text -> String -> TwoCaptchaError
forall a b. (a -> b) -> a -> b
$ Value -> Text
forall dst src.
Interpolatable (IsCustomSink dst) src dst =>
src -> dst
captchaIdInvalid Value
captchaId) (Integer -> CaptchaId ctx
forall ctx. Integer -> CaptchaId ctx
CaptchaId (Integer -> CaptchaId ctx)
-> ((Integer, Text) -> Integer) -> (Integer, Text) -> CaptchaId ctx
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Integer, Text) -> Integer
forall a b. (a, b) -> a
fst) (Either String (Integer, Text)
 -> Either TwoCaptchaError (CaptchaId ctx))
-> (Text -> Either String (Integer, Text))
-> Text
-> Either TwoCaptchaError (CaptchaId ctx)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String (Integer, Text)
forall a. Integral a => Reader a
decimal
          (Text -> Either TwoCaptchaError (CaptchaId ctx))
-> Either TwoCaptchaError Text
-> Either TwoCaptchaError (CaptchaId ctx)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< TwoCaptchaError -> Maybe Text -> Either TwoCaptchaError Text
forall a b. a -> Maybe b -> Either a b
note (Text -> TwoCaptchaError
UnknownError Text
captchaIdMissing) (Value
captchaId Value
-> ((Text -> Const (First Text) Text)
    -> Value -> Const (First Text) Value)
-> Maybe Text
forall s a. s -> Getting (First a) s a -> Maybe a
^? (Text -> Const (First Text) Text)
-> Value -> Const (First Text) Value
forall t. AsPrimitive t => Prism' t Text
_String)

  getTask :: forall ctx. CaptchaResponse TwoCaptcha ctx => Text -> CaptchaId ctx -> m (Either TwoCaptchaError Text)
  getTask :: Text -> CaptchaId ctx -> m (Either TwoCaptchaError Text)
getTask Text
apiKey CaptchaId ctx
captchaId =
    (Value -> Text)
-> Either TwoCaptchaError Value -> Either TwoCaptchaError Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Getting Text Value Text -> Value -> Text
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting Text Value Text
forall t. AsPrimitive t => Prism' t Text
_String) (Either TwoCaptchaError Value -> Either TwoCaptchaError Text)
-> (Either HttpException (Response ByteString)
    -> Either TwoCaptchaError Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Value -> Maybe Value)
-> Either HttpException (Response ByteString)
-> Either TwoCaptchaError Value
parseResponse (CaptchaResponse TwoCaptcha ctx => Value -> Maybe Value
forall api ctx. CaptchaResponse api ctx => Value -> Maybe Value
parseResult @TwoCaptcha @ctx) (Either HttpException (Response ByteString)
 -> Either TwoCaptchaError Text)
-> m (Either HttpException (Response ByteString))
-> m (Either TwoCaptchaError Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m (Response ByteString)
-> m (Either HttpException (Response ByteString))
forall (m :: * -> *) e a.
(MonadUnliftIO m, Exception e) =>
m a -> m (Either e a)
try (Options -> Text -> m (Response ByteString)
forall r (m :: * -> *).
(HasCaptchaEnv r, MonadReader r m, MonadIO m) =>
Options -> Text -> m (Response ByteString)
get Options
options Text
url)
    where
      url :: Text
url = Text
"https://2captcha.com/res.php"
      options :: Options
options =
        Options
defaultOptions
          Options -> (Options -> Options) -> Options
forall a b. a -> (a -> b) -> b
& Text -> Lens' Options [Text]
param Text
"key" (([Text] -> Identity [Text]) -> Options -> Identity Options)
-> [Text] -> Options -> Options
forall s t a b. ASetter s t a b -> b -> s -> t
.~ [Text
apiKey]
          Options -> (Options -> Options) -> Options
forall a b. a -> (a -> b) -> b
& Text -> Lens' Options [Text]
param Text
"id" (([Text] -> Identity [Text]) -> Options -> Identity Options)
-> [Text] -> Options -> Options
forall s t a b. ASetter s t a b -> b -> s -> t
.~ [String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (String -> Text) -> (Integer -> String) -> Integer -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> String
forall a. Show a => a -> String
show (Integer -> Text) -> Integer -> Text
forall a b. (a -> b) -> a -> b
$ CaptchaId ctx -> Integer
forall ctx. CaptchaId ctx -> Integer
unCaptchaId CaptchaId ctx
captchaId]
          Options -> (Options -> Options) -> Options
forall a b. a -> (a -> b) -> b
& Text -> Lens' Options [Text]
param Text
"action" (([Text] -> Identity [Text]) -> Options -> Identity Options)
-> [Text] -> Options -> Options
forall s t a b. ASetter s t a b -> b -> s -> t
.~ [Text
"get"]

  solve ::
    forall ctx.
    ( CaptchaRequest TwoCaptcha ctx r m,
      CaptchaResponse TwoCaptcha ctx,
      HasApiKey ctx Text,
      HasPollingInterval ctx (Maybe (Time Millisecond)),
      HasTimeoutDuration ctx (Maybe (Time Millisecond))
    ) =>
    ctx ->
    m (Either TwoCaptchaError Text)
  solve :: ctx -> m (Either TwoCaptchaError Text)
solve ctx
captcha =
    Maybe (Time (1 :% 1000))
-> m (Either TwoCaptchaError Text)
-> m (Either TwoCaptchaError Text)
forall (f :: * -> *) (unit :: Rat) b.
(MonadUnliftIO f, KnownRat unit,
 KnownRat (DivRat unit (1 :% 1000000))) =>
Maybe (Time unit)
-> f (Either TwoCaptchaError b) -> f (Either TwoCaptchaError b)
handleTimeout (ctx
captcha ctx
-> Getting
     (Maybe (Time (1 :% 1000))) ctx (Maybe (Time (1 :% 1000)))
-> Maybe (Time (1 :% 1000))
forall s a. s -> Getting a s a -> a
^. Getting (Maybe (Time (1 :% 1000))) ctx (Maybe (Time (1 :% 1000)))
forall s a. HasTimeoutDuration s a => Lens' s a
timeoutDuration) (m (Either TwoCaptchaError Text)
 -> m (Either TwoCaptchaError Text))
-> (ExceptT TwoCaptchaError m Text
    -> m (Either TwoCaptchaError Text))
-> ExceptT TwoCaptchaError m Text
-> m (Either TwoCaptchaError Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExceptT TwoCaptchaError m Text -> m (Either TwoCaptchaError Text)
forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT TwoCaptchaError m Text -> m (Either TwoCaptchaError Text))
-> ExceptT TwoCaptchaError m Text
-> m (Either TwoCaptchaError Text)
forall a b. (a -> b) -> a -> b
$
      m (Either TwoCaptchaError Text) -> ExceptT TwoCaptchaError m Text
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either TwoCaptchaError Text) -> ExceptT TwoCaptchaError m Text)
-> (CaptchaId ctx -> m (Either TwoCaptchaError Text))
-> CaptchaId ctx
-> ExceptT TwoCaptchaError m Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CaptchaId ctx -> m (Either TwoCaptchaError Text)
pollResult (CaptchaId ctx -> ExceptT TwoCaptchaError m Text)
-> ExceptT TwoCaptchaError m (CaptchaId ctx)
-> ExceptT TwoCaptchaError m Text
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< m (Either TwoCaptchaError (CaptchaId ctx))
-> ExceptT TwoCaptchaError m (CaptchaId ctx)
forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
ExceptT (ctx -> m (Either (CaptchaError TwoCaptcha r m) (CaptchaId ctx))
forall api r (m :: * -> *) ctx.
(MonadCaptcha api r m, CaptchaRequest api ctx r m) =>
ctx -> m (Either (CaptchaError api r m) (CaptchaId ctx))
createTask @TwoCaptcha @r @m ctx
captcha)
    where
      handleTimeout :: Maybe (Time unit)
-> f (Either TwoCaptchaError b) -> f (Either TwoCaptchaError b)
handleTimeout (Just Time unit
duration) f (Either TwoCaptchaError b)
f = Either TwoCaptchaError b
-> Maybe (Either TwoCaptchaError b) -> Either TwoCaptchaError b
forall a. a -> Maybe a -> a
fromMaybe (TwoCaptchaError -> Either TwoCaptchaError b
forall a b. a -> Either a b
Left TwoCaptchaError
TimeoutError) (Maybe (Either TwoCaptchaError b) -> Either TwoCaptchaError b)
-> f (Maybe (Either TwoCaptchaError b))
-> f (Either TwoCaptchaError b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int
-> f (Either TwoCaptchaError b)
-> f (Maybe (Either TwoCaptchaError b))
forall (m :: * -> *) a.
MonadUnliftIO m =>
Int -> m a -> m (Maybe a)
timeout (Time unit -> Int
forall (unitTo :: Rat) n (unit :: Rat).
(KnownDivRat unit unitTo, Num n) =>
Time unit -> n
toNum @Microsecond Time unit
duration) f (Either TwoCaptchaError b)
f
      handleTimeout Maybe (Time unit)
Nothing f (Either TwoCaptchaError b)
f = f (Either TwoCaptchaError b)
f
      pollResult :: CaptchaId ctx -> m (Either TwoCaptchaError Text)
pollResult CaptchaId ctx
captchaId =
        Time (1 :% 1000) -> m ()
forall (unit :: Rat) (m :: * -> *).
(KnownDivRat unit Microsecond, MonadIO m) =>
Time unit -> m ()
threadDelay (Time (1 :% 1000) -> Maybe (Time (1 :% 1000)) -> Time (1 :% 1000)
forall a. a -> Maybe a -> a
fromMaybe (RatioNat -> Time Millisecond
forall (rat :: Rat). RatioNat -> Time rat
Time @Millisecond RatioNat
10_000) (ctx
captcha ctx
-> Getting
     (Maybe (Time (1 :% 1000))) ctx (Maybe (Time (1 :% 1000)))
-> Maybe (Time (1 :% 1000))
forall s a. s -> Getting a s a -> a
^. Getting (Maybe (Time (1 :% 1000))) ctx (Maybe (Time (1 :% 1000)))
forall s a. HasPollingInterval s a => Lens' s a
pollingInterval))
          m ()
-> m (Either TwoCaptchaError Text)
-> m (Either TwoCaptchaError Text)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Text
-> CaptchaId ctx -> m (Either (CaptchaError TwoCaptcha r m) Text)
forall api r (m :: * -> *) ctx.
(MonadCaptcha api r m, CaptchaResponse api ctx) =>
Text -> CaptchaId ctx -> m (Either (CaptchaError api r m) Text)
getTask @TwoCaptcha @r @m @ctx (ctx
captcha ctx -> Getting Text ctx Text -> Text
forall s a. s -> Getting a s a -> a
^. Getting Text ctx Text
forall s a. HasApiKey s a => Lens' s a
apiKey) CaptchaId ctx
captchaId
          m (Either TwoCaptchaError Text)
-> (Either TwoCaptchaError Text -> m (Either TwoCaptchaError Text))
-> m (Either TwoCaptchaError Text)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
            Left (TwoCaptchaResponseError TwoCaptchaErrorCode
CaptchaNotReady) -> CaptchaId ctx -> m (Either TwoCaptchaError Text)
pollResult CaptchaId ctx
captchaId
            -- 2Captcha sends this error when users fail enough times.
            Left (TwoCaptchaResponseError TwoCaptchaErrorCode
BadDuplicates) -> ctx -> m (Either (CaptchaError TwoCaptcha r m) Text)
forall api r (m :: * -> *) ctx.
(MonadCaptcha api r m, CaptchaRequest api ctx r m,
 CaptchaResponse api ctx, HasApiKey ctx Text,
 HasPollingInterval ctx (Maybe (Time Millisecond)),
 HasTimeoutDuration ctx (Maybe (Time Millisecond))) =>
ctx -> m (Either (CaptchaError api r m) Text)
solve @TwoCaptcha ctx
captcha
            -- 2Captcha sends this error when the captcha isn't solved quick enough.
            Left (TwoCaptchaResponseError TwoCaptchaErrorCode
CaptchaUnsolvable) -> ctx -> m (Either (CaptchaError TwoCaptcha r m) Text)
forall api r (m :: * -> *) ctx.
(MonadCaptcha api r m, CaptchaRequest api ctx r m,
 CaptchaResponse api ctx, HasApiKey ctx Text,
 HasPollingInterval ctx (Maybe (Time Millisecond)),
 HasTimeoutDuration ctx (Maybe (Time Millisecond))) =>
ctx -> m (Either (CaptchaError api r m) Text)
solve @TwoCaptcha ctx
captcha
            Either TwoCaptchaError Text
x -> Either TwoCaptchaError Text -> m (Either TwoCaptchaError Text)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Either TwoCaptchaError Text
x

instance CaptchaResponse TwoCaptcha ctx where
  parseResult :: Value -> Maybe Value
parseResult = Getting (First Value) Value Value -> Value -> Maybe Value
forall s (m :: * -> *) a.
MonadReader s m =>
Getting (First a) s a -> m (Maybe a)
preview (Getting (First Value) Value Value -> Value -> Maybe Value)
-> Getting (First Value) Value Value -> Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Text -> Traversal' Value Value
forall t. AsValue t => Text -> Traversal' t Value
key Text
"request"

-- | Parse the proxy type to its textual representation.
parseProxyType :: HasProxy a (Maybe Proxy) => a -> [Text]
parseProxyType :: a -> [Text]
parseProxyType a
captcha = Maybe Text -> [Text]
forall a. Maybe a -> [a]
maybeToList (Maybe Text -> [Text]) -> Maybe Text -> [Text]
forall a b. (a -> b) -> a -> b
$ Text -> Text
toUpper (Text -> Text) -> (ProxyProtocol -> Text) -> ProxyProtocol -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (String -> Text)
-> (ProxyProtocol -> String) -> ProxyProtocol -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ProxyProtocol -> String
forall a. Show a => a -> String
show (ProxyProtocol -> Text) -> Maybe ProxyProtocol -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a
captcha a
-> Getting (First ProxyProtocol) a ProxyProtocol
-> Maybe ProxyProtocol
forall s a. s -> Getting (First a) s a -> Maybe a
^? (Maybe Proxy -> Const (First ProxyProtocol) (Maybe Proxy))
-> a -> Const (First ProxyProtocol) a
forall s a. HasProxy s a => Lens' s a
proxy ((Maybe Proxy -> Const (First ProxyProtocol) (Maybe Proxy))
 -> a -> Const (First ProxyProtocol) a)
-> ((ProxyProtocol -> Const (First ProxyProtocol) ProxyProtocol)
    -> Maybe Proxy -> Const (First ProxyProtocol) (Maybe Proxy))
-> Getting (First ProxyProtocol) a ProxyProtocol
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Proxy -> Const (First ProxyProtocol) Proxy)
-> Maybe Proxy -> Const (First ProxyProtocol) (Maybe Proxy)
forall a b. Prism (Maybe a) (Maybe b) a b
_Just ((Proxy -> Const (First ProxyProtocol) Proxy)
 -> Maybe Proxy -> Const (First ProxyProtocol) (Maybe Proxy))
-> ((ProxyProtocol -> Const (First ProxyProtocol) ProxyProtocol)
    -> Proxy -> Const (First ProxyProtocol) Proxy)
-> (ProxyProtocol -> Const (First ProxyProtocol) ProxyProtocol)
-> Maybe Proxy
-> Const (First ProxyProtocol) (Maybe Proxy)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ProxyProtocol -> Const (First ProxyProtocol) ProxyProtocol)
-> Proxy -> Const (First ProxyProtocol) Proxy
forall s a. HasProtocol s a => Lens' s a
protocol

-- | Parse the proxy into the format: username:password@address:port
parseProxy :: HasProxy a (Maybe Proxy) => a -> [Text]
parseProxy :: a -> [Text]
parseProxy a
captcha = Maybe Text -> [Text]
forall a. Maybe a -> [a]
maybeToList (Maybe Text -> [Text]) -> Maybe Text -> [Text]
forall a b. (a -> b) -> a -> b
$ do
  let auth :: Maybe Text
auth = do
        Text
username <- a -> Maybe Text
forall a. HasProxy a (Maybe Proxy) => a -> Maybe Text
getProxyUsername a
captcha
        Text
password <- a -> Maybe Text
forall a. HasProxy a (Maybe Proxy) => a -> Maybe Text
getProxyPassword a
captcha
        Text -> Maybe Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure [i|#{username}:#{password}@|]
  Text
address <- a -> Maybe Text
forall a. HasProxy a (Maybe Proxy) => a -> Maybe Text
getProxyAddress a
captcha
  Int
port <- a -> Maybe Int
forall a. HasProxy a (Maybe Proxy) => a -> Maybe Int
getProxyPort a
captcha
  Text -> Maybe Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
forall a. Monoid a => a
mempty Maybe Text
auth Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> [i|#{address}:#{port}|]

-- | Default option parameters when making 2Captcha requests.
defaultOptions :: Options
defaultOptions :: Options
defaultOptions =
  Options
defaults
    Options -> (Options -> Options) -> Options
forall a b. a -> (a -> b) -> b
& Text -> Lens' Options [Text]
param Text
"json" (([Text] -> Identity [Text]) -> Options -> Identity Options)
-> [Text] -> Options -> Options
forall s t a b. ASetter s t a b -> b -> s -> t
.~ [Text
"1"]
    Options -> (Options -> Options) -> Options
forall a b. a -> (a -> b) -> b
& Text -> Lens' Options [Text]
param Text
"soft_id" (([Text] -> Identity [Text]) -> Options -> Identity Options)
-> [Text] -> Options -> Options
forall s t a b. ASetter s t a b -> b -> s -> t
.~ [Text
"3283"]