From eef52773ab46dc23e7954fe4f290ba8635a353e6 Mon Sep 17 00:00:00 2001 From: Gabriel Augendre Date: Sat, 4 Dec 2021 16:42:59 +0100 Subject: [PATCH] Implement sending reconciled balance via sms (free mobile) --- ofx_processor/processors/bpvf.py | 6 +++--- ofx_processor/processors/ce.py | 6 +++--- ofx_processor/processors/lcl.py | 6 +++--- ofx_processor/utils/base_ofx.py | 29 +++++++++++++++++++++++++---- ofx_processor/utils/config.py | 16 ++++++++++++++++ ofx_processor/utils/utils.py | 15 +++++++++------ pyproject.toml | 2 +- 7 files changed, 60 insertions(+), 20 deletions(-) diff --git a/ofx_processor/processors/bpvf.py b/ofx_processor/processors/bpvf.py index cc97dd9..f97e80b 100644 --- a/ofx_processor/processors/bpvf.py +++ b/ofx_processor/processors/bpvf.py @@ -33,9 +33,9 @@ class BpvfProcessor(OfxBaseProcessor): command_name = "bpvf" -def main(filename, keep, send): +def main(filename, keep, send_method): """Import BPVF bank statement (OFX file).""" processor = BpvfProcessor(filename) - if send: - processor.send_reconciled_amount() + if send_method: + processor.send_reconciled_amount(send_method) processor.push_to_ynab(keep) diff --git a/ofx_processor/processors/ce.py b/ofx_processor/processors/ce.py index 00058a1..ebc3579 100644 --- a/ofx_processor/processors/ce.py +++ b/ofx_processor/processors/ce.py @@ -30,9 +30,9 @@ class CeProcessor(OfxBaseProcessor): line_class = CeLine -def main(filename, keep, send): +def main(filename, keep, send_method): """Import CE bank statement (OFX file).""" processor = CeProcessor(filename) - if send: - processor.send_reconciled_amount() + if send_method: + processor.send_reconciled_amount(send_method) processor.push_to_ynab(keep) diff --git a/ofx_processor/processors/lcl.py b/ofx_processor/processors/lcl.py index e497094..2c19037 100644 --- a/ofx_processor/processors/lcl.py +++ b/ofx_processor/processors/lcl.py @@ -80,7 +80,7 @@ class LclProcessor(OfxBaseProcessor): return ofx -def main(filename, keep, download, send): +def main(filename, keep, download, send_method): """Import LCL bank statement (OFX file).""" if download: if filename: @@ -91,6 +91,6 @@ def main(filename, keep, download, send): ) filename = LclDownloader().download() processor = LclProcessor(filename) - if send: - processor.send_reconciled_amount() + if send_method: + processor.send_reconciled_amount(send_method) processor.push_to_ynab(keep) diff --git a/ofx_processor/utils/base_ofx.py b/ofx_processor/utils/base_ofx.py index ebcfb23..a0c1858 100644 --- a/ofx_processor/utils/base_ofx.py +++ b/ofx_processor/utils/base_ofx.py @@ -33,10 +33,13 @@ class OfxBaseProcessor(BaseProcessor): ofx = self._parse_file() return ofx.statements[0].transactions - def send_reconciled_amount(self): + def send_reconciled_amount(self, method): amount = self._get_reconciled_amount() - click.secho(f"Reconciled balance: {amount}. Sending...", fg="blue") - self._send_mail(amount) + click.secho(f"Reconciled balance: {amount}. Sending via {method}...", fg="blue") + if method == "email": + self._send_mail(amount) + elif method == "sms": + self._send_sms(amount) def _get_reconciled_amount(self) -> Decimal: ofx = self._parse_file() @@ -57,7 +60,7 @@ class OfxBaseProcessor(BaseProcessor): if not config.email_setup: click.secho("Email is not properly setup", fg="yellow") return - return requests.post( + res = requests.post( f"https://api.mailgun.net/v3/{config.mailgun_domain}/messages", auth=("api", config.mailgun_api_key), data={ @@ -67,3 +70,21 @@ class OfxBaseProcessor(BaseProcessor): "text": f"Here's your reconciled balance: {amount}", }, ) + if res.status_code >= 400: + click.secho("Error while sending email", fg="yellow") + + def _send_sms(self, amount: Decimal): + config = get_config(self.account_name) + if not config.sms_setup: + click.secho("SMS is not properly setup", fg="yellow") + return + res = requests.post( + f"https://smsapi.free-mobile.fr/sendmsg", + json={ + "user": config.sms_user, + "pass": config.sms_key, + "msg": f"Reconciled balance: {amount}", + }, + ) + if res.status_code >= 400: + click.secho("Error while sending SMS", fg="yellow") diff --git a/ofx_processor/utils/config.py b/ofx_processor/utils/config.py index 7ab1747..393d7ca 100644 --- a/ofx_processor/utils/config.py +++ b/ofx_processor/utils/config.py @@ -65,6 +65,8 @@ class Config: mailgun_domain: Optional[str] = None mailgun_from: Optional[str] = None email_recipient: Optional[str] = None + sms_user: Optional[str] = None + sms_key: Optional[str] = None @property def email_setup(self) -> bool: @@ -78,6 +80,16 @@ class Config: ] ) + @property + def sms_setup(self) -> bool: + """Return true if all fields are setup for sms.""" + return all( + [ + self.sms_user, + self.sms_key, + ] + ) + def get_config(account: str) -> Config: config = configparser.ConfigParser() @@ -108,6 +120,8 @@ def get_config(account: str) -> Config: mailgun_domain = section.get("mailgun_domain") mailgun_from = section.get("mailgun_from") email_recipient = section.get("email_recipient") + sms_user = section.get("sms_user") + sms_key = section.get("sms_key") except KeyError as e: return handle_config_file_error(config_file, e) @@ -121,6 +135,8 @@ def get_config(account: str) -> Config: mailgun_domain, mailgun_from, email_recipient, + sms_user, + sms_key, ) diff --git a/ofx_processor/utils/utils.py b/ofx_processor/utils/utils.py index 47049e5..412aa4b 100644 --- a/ofx_processor/utils/utils.py +++ b/ofx_processor/utils/utils.py @@ -14,12 +14,15 @@ ARG_TO_OPTION = { default=False, show_default=True, ), - "send": click.option( - "send", - "--send/--no-send", - "--send-email/--no-send-email", - help="Send the reconciled amount via email.", - default=False, + "send_method": click.option( + "send_method", + "-s", + "--send", + help=( + "Send the reconciled amount via the chosen method. " + "Accepted methods: sms, email" + ), + default="", show_default=True, ), "download": click.option( diff --git a/pyproject.toml b/pyproject.toml index f4cd232..cb6fa7e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ofx-processor" -version = "3.3.0" +version = "3.4.0" description = "Personal ofx processor" readme = "README.md" license = "GPL-3.0-or-later"