Initial commit
This commit is contained in:
commit
b063bbd350
21
.env.example
Normal file
21
.env.example
Normal file
@ -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'
|
12
.gitignore
vendored
Normal file
12
.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
.idea
|
||||
*.log
|
||||
tmp/
|
||||
|
||||
*.py[cod]
|
||||
*.egg
|
||||
build
|
||||
htmlcov
|
||||
|
||||
session.txt
|
||||
.env
|
14
.pre-commit-config.yaml
Normal file
14
.pre-commit-config.yaml
Normal file
@ -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
|
23
bofa.py
Normal file
23
bofa.py
Normal file
@ -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
|
155
bot.py
Normal file
155
bot.py
Normal file
@ -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()
|
8
nextcloud.py
Normal file
8
nextcloud.py
Normal file
@ -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))
|
51
org.py
Normal file
51
org.py
Normal file
@ -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()
|
28
prometeo.py
Normal file
28
prometeo.py
Normal file
@ -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")
|
40
pyproject.toml
Normal file
40
pyproject.toml
Normal file
@ -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
|
29
settings.py
Normal file
29
settings.py
Normal file
@ -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")
|
Loading…
x
Reference in New Issue
Block a user