diff --git a/LICENSE b/LICENSE
index ca89857..53ff67b 100644
--- a/LICENSE
+++ b/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
- Version 3, 29 June 2007
+The author has absolutely no fucking clue what the code in this project does.
+It might just fucking work or not, there is no third option.
- A bot to easen the daily routine with zucchetti virtual badge.
- Copyleft 2019
-
- 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 .
-
-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
-.
-
- 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
-.
+Everyone is permitted to copy, distribute, modify, merge, sell, publish,
+sublicense or whatever fuck they want with this software but at their OWN RISK.
+ 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.
diff --git a/README.rst b/README.rst
index 2309933..1b0f817 100644
--- a/README.rst
+++ b/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.
-* Free software: GNU General Public License v3
-* Documentation: https://bot-z.readthedocs.io.
+* Free software: GLWTS public licence.
Features
--------
-* TODO
+* Login/Logout
-Credits
----------
-
-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
+TODO
+----
+ * Check in/out
+ * systemd {unit, timer}
+ * APIs
+ * ...
diff --git a/bot_z/bot_z.py b/bot_z/bot_z.py
index 5b1cd25..facf719 100644
--- a/bot_z/bot_z.py
+++ b/bot_z/bot_z.py
@@ -1,7 +1,11 @@
# -*- 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 os
import time
@@ -15,10 +19,12 @@ from selenium.common.exceptions import WebDriverException, NoSuchElementExceptio
logging.basicConfig(
- level=logging.INFO,
+ level=os.environ.get('BOTZ_LOGLEVEL', logging.INFO),
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 _protection(self, *args, **kwargs):
@@ -30,11 +36,43 @@ def safely(f: T.Callable) -> T.Callable:
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):
def __init__(
self,
base_uri: str,
name: str = None,
+ timeout: int=20,
proxy: T.Optional[T.Tuple[str, int]] = None,
headless: bool = True,
debug: bool = False,
@@ -53,6 +91,7 @@ class Operator(wd.Firefox):
self.debug = debug
self.base_uri = base_uri
+ self.timeout = timedelta(seconds=timeout)
if proxy:
self.profile.set_preference('network.proxy.type', 1)
@@ -72,15 +111,29 @@ class Operator(wd.Firefox):
self._checked_in = False
@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.
"""
+ 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
- #self.get('{}/jsp/login.jsp'.format(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)
- time.sleep(3)
+ time.sleep(1)
# Username
user_form = self.find_element_by_name('m_cUserName')
# Password
@@ -91,24 +144,51 @@ class Operator(wd.Firefox):
user_form.send_keys(user)
pass_form.send_keys(password)
do_it = True
- if self.debug:
- _do_it = input("Really do the login? [y/n]").lower()
+ if self.debug and not force:
+ _do_it = input("Really do the login? [y/n] ").lower()
do_it = True if _do_it == "y" else False
if do_it:
- self.logger.debug("Before clicking: %s", self.current_url)
login_butt.submit()
- self.logger.debug("After clicking: %s", self.current_url)
+ time.sleep(5)
self.logger.debug("Login result: %s", self.title)
if 'Routine window' in self.title:
+ self.logger.debug("Reloading...")
self.refresh()
- try:
- self.find_element_by_xpath('//input[contains(@value, "Accedi")]')
- # TODO: reckon a proper timeout mechanism
- # based on cookie expire time
+ self.switch_to.alert.accept()
+ if is_present(self, '//a[contains(@class, "imgMenu_ctrl")]', self.timeout):
self._logged_in = True
self.logger.info("Login success for user: %s", user)
- except NoSuchElementException as e:
- self.logger.error("Login failed: %s", e)
+ else:
+ 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
def check_in(self, force: bool=False) -> None:
@@ -121,6 +201,8 @@ class Operator(wd.Firefox):
self.logger.warn("Already logged in!")
if not force:
return
+ enter_butt = self.find_element_by_xpath('//input[@value="Entrata"]')
+ enter_butt.submit()
# Click the check in button and change
# self._checked_in state in case of success
pass
@@ -136,9 +218,11 @@ class Operator(wd.Firefox):
self.logger.warn("Not yet logged in!")
if not force:
return
+ exit_butt = self.find_element_by_xpath('//input[@value="Uscita"]')
+ exit_butt.submit()
# Click the check in button and change
# self._checked_in state in case of success
pass
- def __del__(self):
+ def __del__(self) -> None:
self.quit()