Skip to content

Commit

Permalink
Added Guard page handler
Browse files Browse the repository at this point in the history
  • Loading branch information
m-haisham committed Feb 25, 2021
1 parent 59cf7f1 commit e979e32
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ except GuardException:
pass
```

[Read more] on handling Guard

[Read more]: https://github.com/mHaisham/webnovelbot/tree/master/webnovel/handlers

### `manual=True`

When manual is true the process would be expecting user input during the above mentioned situations.
Expand Down
1 change: 1 addition & 0 deletions webnovel/decorators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .chainable import chainable
from .deprecated import deprecated
from .redirect import redirect
from .signin import require_signin
25 changes: 25 additions & 0 deletions webnovel/decorators/chainable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from functools import wraps


def chainable(func):
"""
enables chaining of methods
this makes the return of the function to be the class or `self`
example usage:
@chainable
def a_class_method(self):
...
"""

@wraps(func)
def wrapper(*args, **kwargs):
func(*args, **kwargs)

# when used with a method, the first arg is always the class
return args[0]

return wrapper
4 changes: 4 additions & 0 deletions webnovel/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ class GuardException(Exception):

class CaptchaException(Exception):
pass


class ValidationError(Exception):
pass
68 changes: 68 additions & 0 deletions webnovel/handlers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Handlers

They provide a convenient way to interact with some of the more obscure pages

## GuardHandler

As is named this handler handles most/all the interactions that are possible with the guard page

```python
from webnovel.handlers import GuardHandler

try:
webnovel.signin(USER_EMAIL, USER_PASS)
except CaptchaException:
pass
except GuardException:
handler = GuardHandler(webnovel)
success = handler.input('code').confirm().wait_until_confirmed()
```

Note that `input` and `confirm` are chainable while `wait_until_confirmed` returns a boolean.

### `wait_until_confirmed()`

checks for redirect and error and informs which is triggered first.

- `True` when redirected
- `False` when error is encountered

**Below is a full list of available methods**

- `@chainable input(code)` it takes the authentication code and writes it in authentication field

- `@chainable confirm()` press the confirmation button

- `@chainable resend()` press the resend button

- `back()` press the back button

- `wait_until_confirmed()` described [above](#wait_until_confirmed)

### ActionChains

you may also create you own chain of events using action chains and the elements exposed

- `input_element`
- `confirm_buttom`
- `resend_button`
- `back_button`

The example below has the same functionality as that above, but it uses `ActionChains`

```python
from selenium.webdriver.common.action_chains import ActionChains

from webnovel.handlers import GuardHandler

...
except GuardException:
handler = GuardHandler(webnovel)

ActionChains(handler.driver)\
.send_keys_to_element(handler.input_element, 'code')\
.click(handler.confirm_buttom)\
.perform()

success = handler.wait_until_confirmed()
```
1 change: 1 addition & 0 deletions webnovel/handlers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .guard import GuardHandler
61 changes: 61 additions & 0 deletions webnovel/handlers/guard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from selenium.common.exceptions import NoSuchElementException

from .handler import IHandler
from ..bot import GUARD_URL
from ..decorators import chainable


class GuardHandler(IHandler):
def __init__(self, bot):
"""
:param bot: webnovel bot
:raises ValueError: if current url is not a guard url
"""
super(GuardHandler, self).__init__(bot)

if not self.driver.current_url.startswith(GUARD_URL):
raise ValueError('current page is not a guard')

# make sure its fully loaded
self.wait_for('.codeInfo > input')

self.input_element = self.get('.codeInfo > input')
self.confirm_buttom = self.get('#checkTrust')
self.resend_button = self.get('#resTrustEmail')
self.back_button = self.get('.m-main-hd a')

@chainable
def input(self, code: str):
"""
inputs code into authentication input field
:param code: authentication code
"""
self.input_element.send_keys(code)

@chainable
def confirm(self):
""" press confirm button """
self.confirm_buttom.click()

@chainable
def resend(self):
""" press resend email button """
self.resend_button.click()

def back(self):
""" press back button """
self.back_button.click()

def wait_until_confirmed(self):
self.wait.until(lambda driver: (
not self.driver.current_url.startswith(GUARD_URL)
or self.driver.find_elements_by_css_selector('.error_tip._on')
))

try:
self.get('.error_tip._on')
except NoSuchElementException:
return True
else:
return False
32 changes: 32 additions & 0 deletions webnovel/handlers/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait

from ..bot import WebnovelBot


class IHandler:
bot: WebnovelBot
driver: WebDriver

def __init__(self, bot):
self.bot = bot
self.driver = bot.driver

self.wait = WebDriverWait(self.driver, self.bot.timeout)

def get(self, selector):
return self.driver.find_element_by_css_selector(selector)

def wait_for(self, selector):
self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
)

def wait_and_get(self, selector):
self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
)

return self.driver.find_element_by_css_selector(selector)

0 comments on commit e979e32

Please sign in to comment.