login/logout mostly working.
This commit is contained in:
parent
f6c55655a2
commit
23c41237ff
49
LICENSE
49
LICENSE
|
@ -1,35 +1,22 @@
|
||||||
|
GLWTS(Good Luck With That Shit) Public License
|
||||||
|
Copyright (c) Every-fucking-one, except the Author
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
The author has absolutely no fucking clue what the code in this project does.
|
||||||
Version 3, 29 June 2007
|
It might just fucking work or not, there is no third option.
|
||||||
|
|
||||||
A bot to easen the daily routine with zucchetti virtual badge.
|
Everyone is permitted to copy, distribute, modify, merge, sell, publish,
|
||||||
Copyleft 2019
|
sublicense or whatever fuck they want with this software but at their OWN RISK.
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or school,
|
|
||||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
|
||||||
For more information on this, and how to apply and follow the GNU GPL, see
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
The GNU General Public License does not permit incorporating your program
|
|
||||||
into proprietary programs. If your program is a subroutine library, you
|
|
||||||
may consider it more useful to permit linking proprietary applications with
|
|
||||||
the library. If this is what you want to do, use the GNU Lesser General
|
|
||||||
Public License instead of this License. But first, please read
|
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
||||||
|
|
||||||
|
|
||||||
|
GOOD LUCK WITH THAT SHIT PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION, AND MODIFICATION
|
||||||
|
|
||||||
|
0. You just DO WHATEVER THE FUCK YOU WANT TO as long as you NEVER LEAVE A
|
||||||
|
FUCKING TRACE TO TRACK THE AUTHOR of the original product to blame for or held
|
||||||
|
responsible.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Good luck and Godspeed.
|
||||||
|
|
33
README.rst
33
README.rst
|
@ -3,38 +3,21 @@ Bot_Z
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/bot_z.svg
|
|
||||||
:target: https://pypi.python.org/pypi/bot_z
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/travis/lbarcaroli/bot_z.svg
|
|
||||||
:target: https://travis-ci.org/lbarcaroli/bot_z
|
|
||||||
|
|
||||||
.. image:: https://readthedocs.org/projects/bot-z/badge/?version=latest
|
|
||||||
:target: https://bot-z.readthedocs.io/en/latest/?badge=latest
|
|
||||||
:alt: Documentation Status
|
|
||||||
|
|
||||||
.. image:: https://pyup.io/repos/github/lbarcaroli/bot_z/shield.svg
|
|
||||||
:target: https://pyup.io/repos/github/lbarcaroli/bot_z/
|
|
||||||
:alt: Updates
|
|
||||||
|
|
||||||
|
|
||||||
A bot to easen the daily routine with zucchetti virtual badge.
|
A bot to easen the daily routine with zucchetti virtual badge.
|
||||||
|
|
||||||
|
|
||||||
* Free software: GNU General Public License v3
|
* Free software: GLWTS public licence.
|
||||||
* Documentation: https://bot-z.readthedocs.io.
|
|
||||||
|
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
|
|
||||||
* TODO
|
* Login/Logout
|
||||||
|
|
||||||
Credits
|
TODO
|
||||||
---------
|
----
|
||||||
|
|
||||||
This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.
|
|
||||||
|
|
||||||
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
|
|
||||||
.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage
|
|
||||||
|
|
||||||
|
* Check in/out
|
||||||
|
* systemd {unit, timer}
|
||||||
|
* APIs
|
||||||
|
* ...
|
||||||
|
|
116
bot_z/bot_z.py
116
bot_z/bot_z.py
|
@ -1,7 +1,11 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""Main module."""
|
"""
|
||||||
|
Operator is the main object that interacts with the foreign
|
||||||
|
service. It exposes methods to login, logout and check in and out.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
@ -15,10 +19,12 @@ from selenium.common.exceptions import WebDriverException, NoSuchElementExceptio
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=os.environ.get('BOTZ_LOGLEVEL', logging.INFO),
|
||||||
format='%(levelname)s: [%(name)s] -> %(message)s'
|
format='%(levelname)s: [%(name)s] -> %(message)s'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
m_logger = logging.getLogger(__name__)
|
||||||
|
m_logger.debug("Init at debug")
|
||||||
|
|
||||||
def safely(f: T.Callable) -> T.Callable:
|
def safely(f: T.Callable) -> T.Callable:
|
||||||
def _protection(self, *args, **kwargs):
|
def _protection(self, *args, **kwargs):
|
||||||
|
@ -30,11 +36,43 @@ def safely(f: T.Callable) -> T.Callable:
|
||||||
return _protection
|
return _protection
|
||||||
|
|
||||||
|
|
||||||
|
def _is_present(driver: wd.Firefox, xpath: str) -> bool:
|
||||||
|
try:
|
||||||
|
driver.find_element_by_xpath(xpath)
|
||||||
|
return True
|
||||||
|
except NoSuchElementException:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_present(driver: wd.Firefox,
|
||||||
|
xpath: str,
|
||||||
|
timeout: T.Optional[timedelta]=None
|
||||||
|
) -> bool:
|
||||||
|
"""
|
||||||
|
Helper function. If an element is present in the DOM tree,
|
||||||
|
returns true. False otherwise.
|
||||||
|
"""
|
||||||
|
if timeout is None:
|
||||||
|
return _is_present(driver, xpath)
|
||||||
|
|
||||||
|
_now = datetime.now()
|
||||||
|
_elapsed = timedelta(seconds=0)
|
||||||
|
while _elapsed < timeout:
|
||||||
|
m_logger.debug("Not yet present: %s", xpath)
|
||||||
|
if _is_present(driver, xpath):
|
||||||
|
m_logger.debug("Present: %s", xpath)
|
||||||
|
return True
|
||||||
|
time.sleep(0.5)
|
||||||
|
_elapsed = datetime.now() - _now
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Operator(wd.Firefox):
|
class Operator(wd.Firefox):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
base_uri: str,
|
base_uri: str,
|
||||||
name: str = None,
|
name: str = None,
|
||||||
|
timeout: int=20,
|
||||||
proxy: T.Optional[T.Tuple[str, int]] = None,
|
proxy: T.Optional[T.Tuple[str, int]] = None,
|
||||||
headless: bool = True,
|
headless: bool = True,
|
||||||
debug: bool = False,
|
debug: bool = False,
|
||||||
|
@ -53,6 +91,7 @@ class Operator(wd.Firefox):
|
||||||
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.base_uri = base_uri
|
self.base_uri = base_uri
|
||||||
|
self.timeout = timedelta(seconds=timeout)
|
||||||
|
|
||||||
if proxy:
|
if proxy:
|
||||||
self.profile.set_preference('network.proxy.type', 1)
|
self.profile.set_preference('network.proxy.type', 1)
|
||||||
|
@ -72,15 +111,29 @@ class Operator(wd.Firefox):
|
||||||
self._checked_in = False
|
self._checked_in = False
|
||||||
|
|
||||||
@safely
|
@safely
|
||||||
def do_login(self, user: str, password: str) -> None:
|
def login(self, user: str, password: str, force: bool=False) -> None:
|
||||||
"""
|
"""
|
||||||
Do the login and proceed.
|
Do the login and proceed.
|
||||||
"""
|
"""
|
||||||
|
if self._logged_in:
|
||||||
|
self.logger.warning("Already logged in: %s", user)
|
||||||
|
if not force:
|
||||||
|
return
|
||||||
|
self.logger.info("Forcing login: %s", user)
|
||||||
# Retrieve login page
|
# Retrieve login page
|
||||||
#self.get('{}/jsp/login.jsp'.format(self.base_uri))
|
|
||||||
self.get(self.base_uri)
|
self.get(self.base_uri)
|
||||||
|
_correct_url = 'cpccchk' in self.current_url
|
||||||
|
_now = datetime.now()
|
||||||
|
_elapsed = timedelta(seconds=0)
|
||||||
|
while not _correct_url:
|
||||||
|
self.logger.debug("Not yet on login page: %s", self.current_url)
|
||||||
|
time.sleep(0.5)
|
||||||
|
_correct_url = 'cpccchk' in self.current_url
|
||||||
|
_elapsed = datetime.now() - _now
|
||||||
|
if _elapsed > self.timeout:
|
||||||
|
break
|
||||||
self.logger.debug("After login get: %s", self.current_url)
|
self.logger.debug("After login get: %s", self.current_url)
|
||||||
time.sleep(3)
|
time.sleep(1)
|
||||||
# Username
|
# Username
|
||||||
user_form = self.find_element_by_name('m_cUserName')
|
user_form = self.find_element_by_name('m_cUserName')
|
||||||
# Password
|
# Password
|
||||||
|
@ -91,24 +144,51 @@ class Operator(wd.Firefox):
|
||||||
user_form.send_keys(user)
|
user_form.send_keys(user)
|
||||||
pass_form.send_keys(password)
|
pass_form.send_keys(password)
|
||||||
do_it = True
|
do_it = True
|
||||||
if self.debug:
|
if self.debug and not force:
|
||||||
_do_it = input("Really do the login? [y/n]").lower()
|
_do_it = input("Really do the login? [y/n] ").lower()
|
||||||
do_it = True if _do_it == "y" else False
|
do_it = True if _do_it == "y" else False
|
||||||
if do_it:
|
if do_it:
|
||||||
self.logger.debug("Before clicking: %s", self.current_url)
|
|
||||||
login_butt.submit()
|
login_butt.submit()
|
||||||
self.logger.debug("After clicking: %s", self.current_url)
|
time.sleep(5)
|
||||||
self.logger.debug("Login result: %s", self.title)
|
self.logger.debug("Login result: %s", self.title)
|
||||||
if 'Routine window' in self.title:
|
if 'Routine window' in self.title:
|
||||||
|
self.logger.debug("Reloading...")
|
||||||
self.refresh()
|
self.refresh()
|
||||||
try:
|
self.switch_to.alert.accept()
|
||||||
self.find_element_by_xpath('//input[contains(@value, "Accedi")]')
|
if is_present(self, '//a[contains(@class, "imgMenu_ctrl")]', self.timeout):
|
||||||
# TODO: reckon a proper timeout mechanism
|
|
||||||
# based on cookie expire time
|
|
||||||
self._logged_in = True
|
self._logged_in = True
|
||||||
self.logger.info("Login success for user: %s", user)
|
self.logger.info("Login success for user: %s", user)
|
||||||
except NoSuchElementException as e:
|
else:
|
||||||
self.logger.error("Login failed: %s", e)
|
self.logger.error("Login failed: %s", user)
|
||||||
|
|
||||||
|
@safely
|
||||||
|
def logout(self, user: str, force: bool=False) -> None:
|
||||||
|
"""
|
||||||
|
Do the logout.
|
||||||
|
"""
|
||||||
|
if not self._logged_in:
|
||||||
|
self.logger.warning("Not yet logged in for user: %s", user)
|
||||||
|
if not force:
|
||||||
|
return
|
||||||
|
self.logger.info("Forcing logout: %s", user)
|
||||||
|
# Find the Profile menu and open it
|
||||||
|
profile_butt = self.find_element_by_xpath('//span[contains(@id, "imgNoPhotoLabel")]')
|
||||||
|
profile_butt.click()
|
||||||
|
time.sleep(1)
|
||||||
|
# Find the logout button
|
||||||
|
logout_butt = self.find_element_by_xpath('//input[@value="Logout user"]')
|
||||||
|
logout_butt.click()
|
||||||
|
if "jsp/usut_wapplogout_portlet.jsp" in self.current_url:
|
||||||
|
self.logger.info("User successfully logged out: %s", user)
|
||||||
|
else:
|
||||||
|
self.logger.warning("Logout failed: %s", user)
|
||||||
|
|
||||||
|
def logged_in(self):
|
||||||
|
"""
|
||||||
|
Check if already logged in. Checks if page is '/jsp/home.jsp'
|
||||||
|
and if login cookie is set (and not expired).
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
@safely
|
@safely
|
||||||
def check_in(self, force: bool=False) -> None:
|
def check_in(self, force: bool=False) -> None:
|
||||||
|
@ -121,6 +201,8 @@ class Operator(wd.Firefox):
|
||||||
self.logger.warn("Already logged in!")
|
self.logger.warn("Already logged in!")
|
||||||
if not force:
|
if not force:
|
||||||
return
|
return
|
||||||
|
enter_butt = self.find_element_by_xpath('//input[@value="Entrata"]')
|
||||||
|
enter_butt.submit()
|
||||||
# Click the check in button and change
|
# Click the check in button and change
|
||||||
# self._checked_in state in case of success
|
# self._checked_in state in case of success
|
||||||
pass
|
pass
|
||||||
|
@ -136,9 +218,11 @@ class Operator(wd.Firefox):
|
||||||
self.logger.warn("Not yet logged in!")
|
self.logger.warn("Not yet logged in!")
|
||||||
if not force:
|
if not force:
|
||||||
return
|
return
|
||||||
|
exit_butt = self.find_element_by_xpath('//input[@value="Uscita"]')
|
||||||
|
exit_butt.submit()
|
||||||
# Click the check in button and change
|
# Click the check in button and change
|
||||||
# self._checked_in state in case of success
|
# self._checked_in state in case of success
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self) -> None:
|
||||||
self.quit()
|
self.quit()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user