-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,3 +22,7 @@ class GuardException(Exception): | |
|
||
class CaptchaException(Exception): | ||
pass | ||
|
||
|
||
class ValidationError(Exception): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .guard import GuardHandler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |