summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Gonzalez <roger@rogs.me>2022-08-01 21:52:56 -0300
committerRoger Gonzalez <roger@rogs.me>2022-08-01 21:52:56 -0300
commitb063bbd35039216cc14a5a5419b9c56b61d65d1b (patch)
tree758536a6076556a14b16612323a08ff959a7cfaf
Initial commit
-rw-r--r--.env.example21
-rw-r--r--.flake82
-rw-r--r--.gitignore12
-rw-r--r--.pre-commit-config.yaml14
-rw-r--r--bofa.py23
-rw-r--r--bot.py155
-rw-r--r--nextcloud.py8
-rw-r--r--org.py51
-rw-r--r--prometeo.py28
-rw-r--r--pyproject.toml40
-rw-r--r--settings.py29
11 files changed, 383 insertions, 0 deletions
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..2912d3c
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,21 @@
+MATRIX_URL="https://my-matrix-url"
+MATRIX_USER="@botusername:my.server"
+MATRIX_PASSWORD="mybotpassword"
+MATRIX_USERNAME="myownusername"
+BANK_ACCOUNT_NUMBERS="bankaccount1,bankaccount2"
+BROU_USERNAME="brou_username"
+BROU_PASSWORD="mybroupassword69420"
+ITAU_USERNAME="itau_username"
+ITAU_PASSWORD="myitaupassword69420"
+
+EXPENSES_FILENAME="my/expenses/filename.org"
+
+NEXTCLOUD_URL="https://my-nextcloud-url.com"
+NEXTCLOUD_USERNAME="mynextcloudusername"
+NEXTCLOUD_PASSWORD="mynextcloudpassword"
+
+ORG_CAPTURE_FILENAME="my/capture/filename.org"
+ORG_PLAN_FILENAME="my/plan-{filename}.org"
+
+PROMETEO_API_KEY="myprometeoapikey"
+PROMETEO_URL='https://myprometeourl.com'
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..c836869
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,2 @@
+[flake8]
+max-line-length = 110
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d213028
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+.DS_Store
+.idea
+*.log
+tmp/
+
+*.py[cod]
+*.egg
+build
+htmlcov
+
+session.txt
+.env
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..d22621d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,14 @@
+repos:
+- repo: https://github.com/timothycrosley/isort
+ rev: 5.9.3
+ hooks:
+ - id: isort
+- repo: https://github.com/ambv/black
+ rev: 22.3.0
+ hooks:
+ - id: black
+ language_version: python3
+- repo: https://gitlab.com/pycqa/flake8
+ rev: 3.9.2
+ hooks:
+ - id: flake8
diff --git a/bofa.py b/bofa.py
new file mode 100644
index 0000000..3d47411
--- /dev/null
+++ b/bofa.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+from pyexcel_ods3 import get_data
+
+from nextcloud import NextCloudConnection
+from settings import EXPENSES_FILENAME
+
+
+class BofaData(NextCloudConnection):
+ def get(self) -> None:
+ with self.client.open(EXPENSES_FILENAME, mode="rb") as expenses:
+ data = get_data(expenses)
+ main_page = data.get("Main")
+
+ headlines = main_page[0]
+ amounts = main_page[1]
+
+ formatted_data = {}
+
+ for i in range(len(headlines)):
+ formatted_data[headlines[i]] = amounts[i]
+
+ return formatted_data
diff --git a/bot.py b/bot.py
new file mode 100644
index 0000000..6480c24
--- /dev/null
+++ b/bot.py
@@ -0,0 +1,155 @@
+import simplematrixbotlib as botlib
+
+from bofa import BofaData
+from org import OrgData
+from prometeo import get_bank_information
+from settings import (
+ BANK_ACCOUNT_NUMBERS,
+ BROU_PASSWORD,
+ BROU_USERNAME,
+ ITAU_PASSWORD,
+ ITAU_USERNAME,
+ MATRIX_PASSWORD,
+ MATRIX_URL,
+ MATRIX_USER,
+ MATRIX_USERNAME,
+)
+
+creds = botlib.Creds(MATRIX_URL, MATRIX_USER, MATRIX_PASSWORD)
+bot = botlib.Bot(creds)
+
+PREFIX = "!"
+
+
+@bot.listener.on_message_event
+async def todo(room, message):
+ """
+ Function that adds new TODOs
+ Usage:
+ user: !todo title - objective - extra (optional)
+ bot: TODO added!
+ """
+ match = botlib.MessageMatch(room, message, bot, PREFIX)
+ if match.is_not_from_this_bot() and match.prefix() and match.command("todo"):
+ user = message.sender
+
+ if user == MATRIX_USERNAME:
+ message = " ".join(message.body.split(" ")[1:])
+ room_id = room.room_id
+ splitted_message = message.split("-")
+
+ try:
+ todo_title = splitted_message[0].strip()
+ todo_objective = splitted_message[1].strip()
+ except IndexError:
+ return await bot.api.send_text_message(
+ room_id,
+ "An objective is needed. The correct format is 'Title - Objective - Extra (optional)'",
+ )
+
+ try:
+ todo_extra = splitted_message[2].strip()
+ except IndexError:
+ todo_extra = ""
+
+ print(f"Room: {room_id}, User: {user}, Message: {message}")
+ OrgData().add_new_todo(todo_title, todo_objective, todo_extra)
+ await bot.api.send_text_message(room_id, "TODO added!")
+
+
+@bot.listener.on_message_event
+async def list_todos(room, message):
+ """
+ Function that lists today's plan
+ Usage:
+ user: !list [free,work]
+ bot: [prints a list with today's todos]
+ """
+ match = botlib.MessageMatch(room, message, bot, PREFIX)
+ if match.is_not_from_this_bot() and match.prefix() and match.command("list"):
+ user = message.sender
+
+ if user == MATRIX_USERNAME:
+ message = " ".join(message.body.split(" ")[1:]).strip() or "free"
+ room_id = room.room_id
+
+ if message not in ["free", "work"]:
+ return await bot.api.send_text_message(
+ room_id,
+ f'"{message}" not accepted. Accepted options are "free" and "work"',
+ )
+
+ print(f"Room: {room_id}, User: {user}, Message: {message}")
+ plan = OrgData().list_plan(message)
+ await bot.api.send_text_message(room_id, plan)
+
+
+@bot.listener.on_message_event
+async def list_bofa_status(room, message):
+ """
+ Function that lists bofa status
+ Usage:
+ user: !bofa
+ bot: [prints bofa current status]
+ """
+ match = botlib.MessageMatch(room, message, bot, PREFIX)
+ if match.is_not_from_this_bot() and match.prefix() and match.command("bofa"):
+ user = message.sender
+
+ if user == MATRIX_USERNAME:
+ room_id = room.room_id
+
+ print(f"Room: {room_id}, User: {user}, Message: bofa")
+
+ bofa_data = BofaData().get()
+
+ return_data = ""
+ for person, amount in bofa_data.items():
+ if amount != "0 USD":
+ return_data += f"{person}: {amount}\n"
+
+ await bot.api.send_text_message(room_id, return_data)
+
+
+def generate_return_data_from_account(bank: str, accounts: list) -> str:
+ bank_message = f"{bank.upper()}\nNot available. Try again later!\n"
+
+ for account in accounts:
+ if account.get("number", "") in BANK_ACCOUNT_NUMBERS:
+ account_number = account.get("number", "")
+ balance = account.get("balance", "")
+
+ bank_message = f"{bank.upper()}\nAccount Number: {account_number}\nBalance: {balance} USD\n"
+ return bank_message
+
+
+@bot.listener.on_message_event
+async def list_bank_information(room, message):
+ """
+ Function that lists banks information
+ Usage:
+ user: !banks
+ bot: [prints current status of banks]
+ """
+ match = botlib.MessageMatch(room, message, bot, PREFIX)
+ if match.is_not_from_this_bot() and match.prefix() and match.command("banks"):
+ user = message.sender
+
+ if user == MATRIX_USERNAME:
+ room_id = room.room_id
+
+ print(f"Room: {room_id}, User: {user}, Message: banks")
+ await bot.api.send_text_message(room_id, "Looking for bank data, just a sec...")
+
+ brou_accounts = get_bank_information("brou", BROU_USERNAME, BROU_PASSWORD)
+ itau_accounts = get_bank_information("itau", ITAU_USERNAME, ITAU_PASSWORD)
+
+ return_data = ""
+ return_data += generate_return_data_from_account("brou", brou_accounts)
+ return_data += "\n"
+ return_data += generate_return_data_from_account("itau", itau_accounts)
+
+ await bot.api.send_text_message(room_id, return_data)
+
+
+bot.run()
diff --git a/nextcloud.py b/nextcloud.py
new file mode 100644
index 0000000..5a530df
--- /dev/null
+++ b/nextcloud.py
@@ -0,0 +1,8 @@
+from webdav4.client import Client
+
+from settings import NEXTCLOUD_PASSWORD, NEXTCLOUD_URL, NEXTCLOUD_USERNAME
+
+
+class NextCloudConnection:
+ def __init__(self):
+ self.client = Client(NEXTCLOUD_URL, auth=(NEXTCLOUD_USERNAME, NEXTCLOUD_PASSWORD))
diff --git a/org.py b/org.py
new file mode 100644
index 0000000..c3a802b
--- /dev/null
+++ b/org.py
@@ -0,0 +1,51 @@
+import locale
+import os
+from datetime import datetime
+
+from orgparse import loads
+
+from nextcloud import NextCloudConnection
+from settings import ORG_CAPTURE_FILENAME, ORG_PLAN_FILENAME
+
+locale.setlocale(locale.LC_ALL, "es_ES.utf8")
+
+
+class OrgData(NextCloudConnection):
+ def _generate_today(self):
+ today = datetime.today()
+ today_ymd = today.strftime("%Y-%m-%d")
+ today_day = today.strftime("%a").lower()
+ today_hour = today.strftime("%H:%M")
+ return f"{today_ymd} {today_day} {today_hour}"
+
+ def add_new_todo(self, description: str, outcome: str, extra: str) -> None:
+
+ self.client.download_file(ORG_CAPTURE_FILENAME, "./capture.org")
+
+ today = self._generate_today()
+
+ todo_template = f"""
+** TODO {description} :NEW: :BOT:
+ Desired outcome: {outcome}
+ :LOGBOOK:
+ - Added: [{today}]
+ :END:
+
+ {extra}
+
+"""
+
+ with open("./capture.org", "a") as capture_file:
+ capture_file.write(todo_template)
+
+ self.client.upload_file("./capture.org", ORG_CAPTURE_FILENAME, overwrite=True)
+
+ os.remove("./capture.org")
+
+ def list_plan(self, filename: str) -> str:
+ with self.client.open(ORG_PLAN_FILENAME.replace("{filename}", filename), mode="r") as agenda:
+ plan = agenda.read()
+
+ plan = loads(plan)
+
+ return plan[-1].get_body()
diff --git a/prometeo.py b/prometeo.py
new file mode 100644
index 0000000..58bd4f8
--- /dev/null
+++ b/prometeo.py
@@ -0,0 +1,28 @@
+import requests
+
+from settings import PROMETEO_API_KEY, PROMETEO_URL
+
+
+def get_bank_information(bank: str, username: str, password: str) -> list:
+ headers = {
+ "Accept": "application/json",
+ "Content-Type": "application/x-www-form-urlencoded",
+ "X-API-Key": PROMETEO_API_KEY,
+ }
+
+ login = requests.post(
+ f"{PROMETEO_URL}/login/",
+ data={
+ "provider": bank,
+ "username": username,
+ "password": password,
+ },
+ headers=headers,
+ )
+
+ key = login.json().get("key")
+
+ account_data = requests.get(f"{PROMETEO_URL}/account/?key={key}", headers=headers).json()
+ requests.get(f"{PROMETEO_URL}/logout/?key={key}", headers=headers)
+
+ return account_data.get("accounts")
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..d119c32
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,40 @@
+[tool.black]
+line-length = 121
+target-version = ['py37', 'py38', 'py310']
+include = '\.pyi?$'
+# regex below includes default list from isort, for parity
+exclude = '''
+/(
+ \.bzr
+ | \.direnv
+ | \.eggs
+ | \.git
+ | \.hg
+ | \.mypy_cache
+ | \.nox
+ | \.pants\.d
+ | \.svn
+ | \.tox
+ | \.venv
+ | _build
+ | buck-out
+ | build
+ | dist
+ | node_modules
+ | venv
+ | \.idea
+ | dockerdata
+ | static
+)/
+'''
+
+[tool.isort]
+# these are black-compatible settings
+multi_line_output = 3
+include_trailing_comma = true
+force_grid_wrap = 0
+use_parentheses = true
+ensure_newline_before_comments = true
+line_length = 121
+skip = "dockerdata,.idea,static"
+filter_files = true
diff --git a/settings.py b/settings.py
new file mode 100644
index 0000000..1f3819a
--- /dev/null
+++ b/settings.py
@@ -0,0 +1,29 @@
+import os
+
+from dotenv import load_dotenv
+
+load_dotenv()
+
+MATRIX_URL = os.environ.get("MATRIX_URL")
+MATRIX_USER = os.environ.get("MATRIX_USER")
+MATRIX_PASSWORD = os.environ.get("MATRIX_PASSWORD")
+MATRIX_USERNAME = os.environ.get("MATRIX_USERNAME")
+
+BANK_ACCOUNT_NUMBERS = os.environ.get("BANK_ACCOUNT_NUMBERS").split(",")
+
+BROU_USERNAME = os.environ.get("BROU_USERNAME")
+BROU_PASSWORD = os.environ.get("BROU_PASSWORD")
+ITAU_USERNAME = os.environ.get("ITAU_USERNAME")
+ITAU_PASSWORD = os.environ.get("ITAU_PASSWORD")
+
+EXPENSES_FILENAME = os.environ.get("EXPENSES_FILENAME")
+
+NEXTCLOUD_URL = os.environ.get("NEXTCLOUD_URL")
+NEXTCLOUD_USERNAME = os.environ.get("NEXTCLOUD_USERNAME")
+NEXTCLOUD_PASSWORD = os.environ.get("NEXTCLOUD_PASSWORD")
+
+ORG_CAPTURE_FILENAME = os.environ.get("ORG_CAPTURE_FILENAME")
+ORG_PLAN_FILENAME = os.environ.get("ORG_PLAN_FILENAME")
+
+PROMETEO_API_KEY = os.environ.get("PROMETEO_API_KEY")
+PROMETEO_URL = os.environ.get("PROMETEO_URL")