From ec3372de0df040b88a97c9c42ea0a7868a708d8e Mon Sep 17 00:00:00 2001 From: Maximilian Zettler Date: Thu, 25 Nov 2021 14:15:17 +0100 Subject: [PATCH] allow savin of session cookies --- .gitignore | 1 + timebot.py | 71 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index af20338..906235a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ venv/ timebot.ini __pycache__/ .idea/ +.kekse diff --git a/timebot.py b/timebot.py index 4d2c068..5ca890f 100644 --- a/timebot.py +++ b/timebot.py @@ -3,6 +3,7 @@ import configparser import datetime import getpass import logging +import pickle import sys import requests as requests @@ -16,11 +17,14 @@ logging.basicConfig(level=logging.INFO) class TimeBot: - def __init__(self, baseurl: str, user: str, password: str): + def __init__(self, baseurl: str, user: str, password: str = None, ask_for_password: bool = False, + save_session: bool = False): self.logger = logging.getLogger(self.__class__.__name__) self.baseurl = self._sanitize_baseurl(baseurl) self.user = user self.password = password + self._ask_for_password = ask_for_password + self._save_session = save_session self._session = None self._current_user = None @@ -40,19 +44,29 @@ class TimeBot: """ if self._session is None: self._session = requests.Session() + try: + self._load_session_cookies(self._session) + except FileNotFoundError as e: + self.logger.error(e) + raise request = self._session.get(self.baseurl + "Employee/GetEmployeeList") if 400 <= request.status_code < 500: self._login(self._session) + self._save_session_cookies(self._session) else: request.raise_for_status() return self._session - def _login(self, session): + def _login(self, session: requests.Session): """ Obtain session cookie. :raises: on status code != 2xx """ + if self.password is None and self._ask_for_password: + self.password = self._get_password() + else: + raise Exception("could not obtain password") login_data = { "username": self.user, "password": self.password, @@ -60,6 +74,35 @@ class TimeBot: request = session.post(self.baseurl + "Account/LogOn", data=login_data) request.raise_for_status() + @staticmethod + def _get_password(): + """ + Ask the user for his password. + + :return: the users password + """ + return getpass.getpass("Enter your password: ") + + def _save_session_cookies(self, session: requests.Session): + """ + Save the session cookies as pickle file. + + :param requests.Session session: the requests session to extract the cookies from + """ + with open(".kekse", "wb") as f: + if self._save_session: + pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f) + + def _load_session_cookies(self, session: requests.Session): + """ + Load the session cookies from the pickle file and updates the given session. + + :param requests.Session session: the requests session which will be updated with the loaded cookies + """ + with open(".kekse", "rb") as f: + if self._save_session: + session.cookies.update(requests.utils.cookiejar_from_dict(pickle.load(f))) + def add_entry(self, punch_datetime: datetime.datetime, entry_code: int, note: str = None) -> requests.Response: """ Add mobatime entry. @@ -191,6 +234,7 @@ if __name__ == '__main__': parser.add_argument("-u", help="mobatime login user", required=True) parser.add_argument("-p", help="mobatime login user password", default=None) parser.add_argument("-c", help="config file", default="timebot.ini") + parser.add_argument("--save-cookies", help="save auth cookies to `./.kekse`", action="store_true", default=False) subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name') # subparser command: `punch` @@ -200,7 +244,8 @@ if __name__ == '__main__': help=f"type of time entry; this can be {', '.join(PUNCH_COMMANDS)}", default="punch_in", choices=PUNCH_COMMANDS) - parser_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`", default="now") + parser_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`", + default="now") # subparser command: `list-entries` parser_list_entries = subparsers.add_parser("list-entries", help="use this command to list your time entries") @@ -225,14 +270,11 @@ if __name__ == '__main__': if args.v: logger.setLevel(logging.DEBUG) - password = args.p - if password is None: - password = getpass.getpass("Enter your password: ") - config = configparser.ConfigParser() config.read(args.c) - tb = TimeBot(baseurl=config["general"]["baseurl"], user=args.u, password=password) + tb = TimeBot(baseurl=config["general"]["baseurl"], user=args.u, password=args.p, ask_for_password=args.save_cookies, + save_session=True) if args.subparser_name == "punch": if args.s == "now": punch_datetime = datetime.datetime.now() @@ -249,16 +291,16 @@ if __name__ == '__main__': last_punch = tb.get_entries(1, now.replace(hour=0, minute=0, second=0, microsecond=0)) method = None if not last_punch: - logger.info("could not detect any time entry for today... punching in") + logger.debug("could not detect any time entry for today... punching in") method = "punch_in" elif last_punch[0]["entryNumber"] == COMING_ENTRY_CODE_ID: - logger.info("your last entry was `punch_in`... starting break") + logger.debug("your last entry was `punch_in`... starting break") method = "break_start" elif last_punch[0]["entryNumber"] == BREAK_START_ENTRY_CODE_ID: - logger.info("your last entry was `break_start`... ending break") + logger.debug("your last entry was `break_start`... ending break") method = "break_end" elif last_punch[0]["entryNumber"] == BREAK_END_ENTRY_CODE_ID: - logger.info("your last entry was `break_end`... punching out") + logger.debug("your last entry was `break_end`... punching out") method = "punch_out" elif last_punch[0]["entryNumber"] == LEAVING_ENTRY_CODE_ID: logger.error("your last entry was `punch_out`... punching in again with this command is not supported") @@ -267,6 +309,11 @@ if __name__ == '__main__': logger.error("hit an unknown situation... detection failed; run with `-v` for more info") logger.debug(f"last entry was: {last_punch}") exit(1) + logger.info("running `{}` with date `{}` and time `{}`".format( + method, + now.strftime(SIMPLE_DATE_FORMAT), + now.strftime(SIMPLE_TIME_FORMAT), + )) getattr(tb, method)(now) elif args.subparser_name == "list-entries": end_date = None