mirror of
https://github.com/elisiariocouto/leggen.git
synced 2025-12-13 19:32:25 +00:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
798a8f1880 | ||
|
|
7401ca62d2 | ||
|
|
e46634cf27 | ||
|
|
7b48bc080c | ||
|
|
0cb339366c | ||
|
|
3d36198b06 | ||
|
|
2352ea9e58 | ||
|
|
b559376116 | ||
|
|
cb6682ea2e | ||
|
|
6d2f1b7b2f | ||
|
|
fcb0f1edd7 | ||
|
|
0c8f68adfd | ||
|
|
7f71589af1 | ||
|
|
f7ef4b32ca | ||
|
|
ee30bff5ef |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -162,3 +162,4 @@ data/
|
||||
docker-compose.dev.yml
|
||||
nocodb/
|
||||
sql/
|
||||
leggen.db
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 24.2.0
|
||||
rev: 24.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3.12
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: "v0.2.1"
|
||||
rev: "v0.3.4"
|
||||
hooks:
|
||||
- id: ruff
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -1,3 +1,58 @@
|
||||
## 0.5.0 (2024/03/29)
|
||||
|
||||
### Features
|
||||
|
||||
- **notifications:** Add support for Telegram notifications. ([7401ca62](https://github.com/elisiariocouto/leggen/commit/7401ca62d2ff23c4100ed9d1c8b7450289337553))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Rename docker-compose.yml to compose.yml and remove obsolete 'version' key. ([e46634cf](https://github.com/elisiariocouto/leggen/commit/e46634cf27046bfc8d638a0cd4910a4a8a42648a))
|
||||
|
||||
|
||||
## 0.4.0 (2024/03/28)
|
||||
|
||||
### Features
|
||||
|
||||
- **notifications:** Add support for transaction filter and notifications via Discord. ([0cb33936](https://github.com/elisiariocouto/leggen/commit/0cb339366cc5965223144d2829312d9416d4bc46))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- **deps-dev:** Bump black from 24.2.0 to 24.3.0 ([2352ea9e](https://github.com/elisiariocouto/leggen/commit/2352ea9e58f14250b819e02fa59879e7ff200764))
|
||||
- Update dependencies. ([3d36198b](https://github.com/elisiariocouto/leggen/commit/3d36198b06eebc9d7480eb020d1a713e8637b31a))
|
||||
|
||||
|
||||
## 0.3.0 (2024/03/08)
|
||||
|
||||
### Documentation
|
||||
|
||||
- Improve README.md. ([cb6682ea](https://github.com/elisiariocouto/leggen/commit/cb6682ea2e7e842806f668fdf4ed34fd0278fd04))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- **commands:** Add new `leggen bank delete` command to delete a bank connection. ([fcb0f1ed](https://github.com/elisiariocouto/leggen/commit/fcb0f1edd7f7ebd556ee31912ba25ee0b01d7edc))
|
||||
- **commands/bank/add:** Add all supported GoCardless country ISO codes. ([0c8f68ad](https://github.com/elisiariocouto/leggen/commit/0c8f68adfddbda08ee90c58e1c69035a0f873a40))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update dependencies. ([6d2f1b7b](https://github.com/elisiariocouto/leggen/commit/6d2f1b7b2f2bf4e4e6d64804adccd74dfb38dcf6))
|
||||
|
||||
|
||||
## 0.2.3 (2024/03/06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Print HTTP response body on errors. ([ee30bff5](https://github.com/elisiariocouto/leggen/commit/ee30bff5ef0e40245004e1811a3a62c9caf4f30f))
|
||||
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- Update dependencies. ([f7ef4b32](https://github.com/elisiariocouto/leggen/commit/f7ef4b32cae347ae05ae763cb169d6b6c09bde99))
|
||||
|
||||
|
||||
## 0.2.2 (2024/03/01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
72
README.md
72
README.md
@@ -9,37 +9,75 @@ Having a simple CLI tool to connect to banks and list transactions can be very u
|
||||
Having your bank data in a database, gives you the power to backup, analyze and create reports with your data.
|
||||
|
||||
## 🛠️ Technologies
|
||||
- Python: for the CLI
|
||||
- [GoCardless Open Banking API](https://developer.gocardless.com/bank-account-data/overview): for connecting to banks
|
||||
|
||||
### 📦 Storage
|
||||
- [SQLite](https://www.sqlite.org): for storing transactions, simple and easy to use
|
||||
- [NocoDB](https://github.com/nocodb/nocodb): for visualizing and querying transactions, a simple and easy to use interface for SQLite
|
||||
- [Ofelia](https://github.com/mcuadros/ofelia): for scheduling regular syncs with the database when using Docker
|
||||
- [MongoDB](https://www.mongodb.com/docs/): alternative store for transactions, good balance between performance and query capabilities
|
||||
|
||||
### ⏰ Scheduling
|
||||
- [Ofelia](https://github.com/mcuadros/ofelia): for scheduling regular syncs with the database when using Docker
|
||||
|
||||
### 📊 Visualization
|
||||
- [NocoDB](https://github.com/nocodb/nocodb): for visualizing and querying transactions, a simple and easy to use interface for SQLite
|
||||
|
||||
## ✨ Features
|
||||
- Connect to banks using GoCardless Open Banking API
|
||||
- List all connected banks and their status
|
||||
- List all connected banks and their statuses
|
||||
- List balances of all connected accounts
|
||||
- List transactions for all connected accounts
|
||||
- Sync all transactions with a MongoDB database
|
||||
- Sync all transactions with a SQLite and/or MongoDB database
|
||||
- Visualize and query transactions using NocoDB
|
||||
- Schedule regular syncs with the database using Ofelia
|
||||
- Send notifications to Discord and/or Telegram when transactions match certain filters
|
||||
|
||||
## 🚀 Installation and Configuration
|
||||
|
||||
In order to use `leggen`, you need to create a GoCardless account. GoCardless is a service that provides access to Open Banking APIs. You can create an account at https://gocardless.com/bank-account-data/.
|
||||
|
||||
After creating an account and getting your API keys, the best way is to use the [compose file](docker-compose.yml). Open the file and adapt it to your needs. Then run the following command:
|
||||
After creating an account and getting your API keys, the best way is to use the [compose file](compose.yml). Open the file and adapt it to your needs.
|
||||
|
||||
### Example Configuration
|
||||
|
||||
Create a configuration file at with the following content:
|
||||
|
||||
```toml
|
||||
[gocardless]
|
||||
key = "your-api-key"
|
||||
secret = "your-secret-key"
|
||||
url = "https://bankaccountdata.gocardless.com/api/v2"
|
||||
|
||||
[database]
|
||||
sqlite = true
|
||||
mongodb = true
|
||||
|
||||
[database.mongodb]
|
||||
uri = "mongodb://localhost:27017"
|
||||
|
||||
[notifications.discord]
|
||||
webhook = "https://discord.com/api/webhooks/..."
|
||||
|
||||
[notifications.telegram]
|
||||
# See gist for telegram instructions
|
||||
# https://gist.github.com/nafiesl/4ad622f344cd1dc3bb1ecbe468ff9f8a
|
||||
token = "12345:abcdefghijklmnopqrstuvxwyz"
|
||||
chat-id = 12345
|
||||
|
||||
[filters.case-insensitive]
|
||||
filter1 = "company-name"
|
||||
```
|
||||
|
||||
### Running Leggen with Docker
|
||||
|
||||
After adapting the compose file, run the following command:
|
||||
|
||||
```bash
|
||||
$ docker compose up -d
|
||||
```
|
||||
|
||||
The leggen container will exit, this is expected. Now you can run the following command to create the configuration file:
|
||||
The leggen container will exit, this is expected since you didn't connect any bank accounts yet.
|
||||
|
||||
```bash
|
||||
$ docker compose run leggen init
|
||||
```
|
||||
|
||||
Now you need to connect your bank accounts. Run the following command and follow the instructions:
|
||||
Run the following command and follow the instructions:
|
||||
|
||||
```bash
|
||||
$ docker compose run leggen bank add
|
||||
@@ -60,18 +98,20 @@ Usage: leggen [OPTIONS] COMMAND [ARGS]...
|
||||
Leggen: An Open Banking CLI
|
||||
|
||||
Options:
|
||||
--version Show the version and exit.
|
||||
-h, --help Show this message and exit.
|
||||
--version Show the version and exit.
|
||||
-c, --config FILE Path to TOML configuration file
|
||||
[env var: LEGGEN_CONFIG_FILE;
|
||||
default: ~/.config/leggen/config.toml]
|
||||
-h, --help Show this message and exit.
|
||||
|
||||
Command Groups:
|
||||
bank Manage banks connections
|
||||
|
||||
Commands:
|
||||
balances List balances of all connected accounts
|
||||
init Create configuration file
|
||||
status List all connected banks and their status
|
||||
sync Sync all transactions with database
|
||||
transactions List transactions for an account
|
||||
transactions List transactions
|
||||
```
|
||||
|
||||
## ⚠️ Caveats
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
# Defaults to `sync` command.
|
||||
leggen:
|
||||
image: elisiariocouto/leggen:latest
|
||||
command: sync
|
||||
restart: "no"
|
||||
environment:
|
||||
LEGGEN_GC_API_KEY: "changeme"
|
||||
LEGGEN_GC_API_SECRET: "changeme"
|
||||
# Uncomment the following lines if you use MongoDB
|
||||
# LEGGEN_MONGO_URI: "mongodb://leggen:changeme@mongo:27017/leggen"
|
||||
volumes:
|
||||
- "./leggen:/root/.config/leggen"
|
||||
- "./leggen:/root/.config/leggen" # Default configuration file should be in this directory, named `config.toml`
|
||||
- "./db:/app"
|
||||
|
||||
nocodb:
|
||||
@@ -14,7 +14,42 @@ def add(ctx):
|
||||
"""
|
||||
country = click.prompt(
|
||||
"Bank Country",
|
||||
type=click.Choice(["PT", "GB"], case_sensitive=True),
|
||||
type=click.Choice(
|
||||
[
|
||||
"AT",
|
||||
"BE",
|
||||
"BG",
|
||||
"HR",
|
||||
"CY",
|
||||
"CZ",
|
||||
"DK",
|
||||
"EE",
|
||||
"FI",
|
||||
"FR",
|
||||
"DE",
|
||||
"GR",
|
||||
"HU",
|
||||
"IS",
|
||||
"IE",
|
||||
"IT",
|
||||
"LV",
|
||||
"LI",
|
||||
"LT",
|
||||
"LU",
|
||||
"MT",
|
||||
"NL",
|
||||
"NO",
|
||||
"PL",
|
||||
"PT",
|
||||
"RO",
|
||||
"SK",
|
||||
"SI",
|
||||
"ES",
|
||||
"SE",
|
||||
"GB",
|
||||
],
|
||||
case_sensitive=True,
|
||||
),
|
||||
default="PT",
|
||||
)
|
||||
info(f"Getting bank list for country: {country}")
|
||||
|
||||
26
leggen/commands/bank/delete.py
Normal file
26
leggen/commands/bank/delete.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import click
|
||||
|
||||
from leggen.main import cli
|
||||
from leggen.utils.network import delete as http_delete
|
||||
from leggen.utils.text import info, success
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.argument("requisition_id", type=str, required=True, metavar="REQUISITION_ID")
|
||||
@click.pass_context
|
||||
def delete(ctx, requisition_id: str):
|
||||
"""
|
||||
Delete bank connection
|
||||
|
||||
REQUISITION_ID: The ID of the Bank Requisition to delete
|
||||
|
||||
Check `leggen status` to get the REQUISITION_ID
|
||||
"""
|
||||
info(f"Deleting Bank Requisition: {requisition_id}")
|
||||
|
||||
_ = http_delete(
|
||||
ctx,
|
||||
f"/requisitions/{requisition_id}",
|
||||
)
|
||||
|
||||
success(f"Bank Requisition {requisition_id} deleted")
|
||||
@@ -1,72 +0,0 @@
|
||||
import click
|
||||
|
||||
from leggen.main import cli
|
||||
from leggen.utils.auth import get_token
|
||||
from leggen.utils.config import save_config
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option(
|
||||
"--api-key",
|
||||
prompt=True,
|
||||
help="GoCardless API Key",
|
||||
envvar="LEGGEN_GC_API_KEY",
|
||||
show_envvar=True,
|
||||
)
|
||||
@click.option(
|
||||
"--api-secret",
|
||||
prompt=True,
|
||||
help="GoCardless API Secret",
|
||||
hide_input=True,
|
||||
envvar="LEGGEN_GC_API_SECRET",
|
||||
show_envvar=True,
|
||||
)
|
||||
@click.option(
|
||||
"--api-url",
|
||||
default="https://bankaccountdata.gocardless.com/api/v2",
|
||||
help="GoCardless API URL",
|
||||
show_default=True,
|
||||
envvar="LEGGEN_GC_API_URL",
|
||||
show_envvar=True,
|
||||
)
|
||||
@click.option(
|
||||
"--sqlite/--mongo",
|
||||
prompt=True,
|
||||
default=True,
|
||||
help="Use SQLite or MongoDB",
|
||||
show_default=True,
|
||||
)
|
||||
@click.option(
|
||||
"--mongo-uri",
|
||||
prompt=True,
|
||||
help="MongoDB URI",
|
||||
envvar="LEGGEN_MONGO_URI",
|
||||
show_envvar=True,
|
||||
default="mongodb://localhost:27017",
|
||||
)
|
||||
@click.pass_context
|
||||
def init(
|
||||
ctx: click.Context,
|
||||
api_key: str,
|
||||
api_secret: str,
|
||||
api_url: str,
|
||||
sqlite: bool,
|
||||
mongo_uri: str,
|
||||
):
|
||||
"""
|
||||
Create configuration file
|
||||
"""
|
||||
config = {
|
||||
"api_key": api_key,
|
||||
"api_secret": api_secret,
|
||||
"api_url": api_url,
|
||||
"sqlite": sqlite,
|
||||
"mongo_uri": mongo_uri,
|
||||
}
|
||||
|
||||
# Just make sure this API credentials are valid
|
||||
# if so, it will save the token in the auth file
|
||||
_ = get_token(config)
|
||||
|
||||
# Save the configuration
|
||||
save_config(config)
|
||||
@@ -22,6 +22,7 @@ def status(ctx: click.Context):
|
||||
"Bank": r["institution_id"],
|
||||
"Status": REQUISITION_STATUS.get(r["status"], "UNKNOWN"),
|
||||
"Created at": datefmt(r["created"]),
|
||||
"Requisition ID": r["id"],
|
||||
}
|
||||
)
|
||||
accounts.update(r.get("accounts", []))
|
||||
|
||||
@@ -1,107 +1,12 @@
|
||||
from datetime import datetime
|
||||
|
||||
import click
|
||||
|
||||
from leggen.main import cli
|
||||
from leggen.utils.mongo import save_transactions as save_transactions_mongo
|
||||
from leggen.utils.database import save_transactions
|
||||
from leggen.utils.network import get
|
||||
from leggen.utils.sqlite import save_transactions as save_transactions_sqlite
|
||||
from leggen.utils.notifications import send_notification
|
||||
from leggen.utils.text import error, info
|
||||
|
||||
|
||||
def save_transactions(ctx: click.Context, account: str):
|
||||
info(f"[{account}] Getting account details")
|
||||
account_info = get(ctx, f"/accounts/{account}")
|
||||
|
||||
info(f"[{account}] Getting transactions")
|
||||
transactions = []
|
||||
|
||||
account_transactions = get(ctx, f"/accounts/{account}/transactions/").get(
|
||||
"transactions", []
|
||||
)
|
||||
|
||||
for transaction in account_transactions.get("booked", []):
|
||||
booked_date = transaction.get("bookingDateTime") or transaction.get(
|
||||
"bookingDate"
|
||||
)
|
||||
value_date = transaction.get("valueDateTime") or transaction.get("valueDate")
|
||||
if booked_date and value_date:
|
||||
min_date = min(
|
||||
datetime.fromisoformat(booked_date), datetime.fromisoformat(value_date)
|
||||
)
|
||||
else:
|
||||
min_date = datetime.fromisoformat(booked_date or value_date)
|
||||
|
||||
transactionValue = float(
|
||||
transaction.get("transactionAmount", {}).get("amount", 0)
|
||||
)
|
||||
currency = transaction.get("transactionAmount", {}).get("currency", "")
|
||||
|
||||
description = transaction.get(
|
||||
"remittanceInformationUnstructured",
|
||||
",".join(transaction.get("remittanceInformationUnstructuredArray", [])),
|
||||
)
|
||||
|
||||
t = {
|
||||
"internalTransactionId": transaction.get("internalTransactionId"),
|
||||
"institutionId": account_info["institution_id"],
|
||||
"iban": account_info.get("iban", "N/A"),
|
||||
"transactionDate": min_date,
|
||||
"description": description,
|
||||
"transactionValue": transactionValue,
|
||||
"transactionCurrency": currency,
|
||||
"transactionStatus": "booked",
|
||||
"accountId": account,
|
||||
"rawTransaction": transaction,
|
||||
}
|
||||
transactions.append(t)
|
||||
|
||||
for transaction in account_transactions.get("pending", []):
|
||||
booked_date = transaction.get("bookingDateTime") or transaction.get(
|
||||
"bookingDate"
|
||||
)
|
||||
value_date = transaction.get("valueDateTime") or transaction.get("valueDate")
|
||||
if booked_date and value_date:
|
||||
min_date = min(
|
||||
datetime.fromisoformat(booked_date), datetime.fromisoformat(value_date)
|
||||
)
|
||||
else:
|
||||
min_date = datetime.fromisoformat(booked_date or value_date)
|
||||
|
||||
transactionValue = float(
|
||||
transaction.get("transactionAmount", {}).get("amount", 0)
|
||||
)
|
||||
currency = transaction.get("transactionAmount", {}).get("currency", "")
|
||||
|
||||
description = transaction.get(
|
||||
"remittanceInformationUnstructured",
|
||||
",".join(transaction.get("remittanceInformationUnstructuredArray", [])),
|
||||
)
|
||||
|
||||
t = {
|
||||
"internalTransactionId": transaction.get("internalTransactionId"),
|
||||
"institutionId": account_info["institution_id"],
|
||||
"iban": account_info.get("iban", "N/A"),
|
||||
"transactionDate": min_date,
|
||||
"description": description,
|
||||
"transactionValue": transactionValue,
|
||||
"transactionCurrency": currency,
|
||||
"transactionStatus": "pending",
|
||||
"accountId": account,
|
||||
"rawTransaction": transaction,
|
||||
}
|
||||
transactions.append(t)
|
||||
|
||||
sqlite = ctx.obj["sqlite"]
|
||||
info(
|
||||
f"[{account}] Fetched {len(transactions)} transactions, saving to {'SQLite' if sqlite else 'MongoDB'}"
|
||||
)
|
||||
if sqlite:
|
||||
save_transactions_sqlite(ctx, account, transactions)
|
||||
else:
|
||||
save_transactions_mongo(ctx, account, transactions)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def sync(ctx: click.Context):
|
||||
@@ -118,6 +23,12 @@ def sync(ctx: click.Context):
|
||||
|
||||
for account in accounts:
|
||||
try:
|
||||
save_transactions(ctx, account)
|
||||
new_transactions = save_transactions(ctx, account)
|
||||
except Exception as e:
|
||||
error(f"[{account}] Error: Sync failed, skipping account. Exception: {e}")
|
||||
error(f"[{account}] Error: Sync failed, skipping account, exception: {e}")
|
||||
continue
|
||||
try:
|
||||
send_notification(ctx, new_transactions)
|
||||
except Exception as e:
|
||||
error(f"[{account}] Error: Notification failed, exception: {e}")
|
||||
continue
|
||||
|
||||
@@ -5,9 +5,9 @@ from pymongo.errors import DuplicateKeyError
|
||||
from leggen.utils.text import success, warning
|
||||
|
||||
|
||||
def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
def persist_transactions(ctx: click.Context, account: str, transactions: list) -> list:
|
||||
# Connect to MongoDB
|
||||
mongo_uri = ctx.obj["mongo_uri"]
|
||||
mongo_uri = ctx.obj.get("database", {}).get("mongodb", {}).get("uri")
|
||||
client = MongoClient(mongo_uri)
|
||||
db = client["leggen"]
|
||||
transactions_collection = db["transactions"]
|
||||
@@ -16,17 +16,20 @@ def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
transactions_collection.create_index("internalTransactionId", unique=True)
|
||||
|
||||
# Insert transactions into MongoDB
|
||||
new_transactions_count = 0
|
||||
duplicates_count = 0
|
||||
|
||||
new_transactions = []
|
||||
|
||||
for transaction in transactions:
|
||||
try:
|
||||
transactions_collection.insert_one(transaction)
|
||||
new_transactions_count += 1
|
||||
new_transactions.append(transaction)
|
||||
except DuplicateKeyError:
|
||||
# A transaction with the same ID already exists, skip insertion
|
||||
duplicates_count += 1
|
||||
|
||||
success(f"[{account}] Inserted {new_transactions_count} new transactions")
|
||||
success(f"[{account}] Inserted {len(new_transactions)} new transactions")
|
||||
if duplicates_count:
|
||||
warning(f"[{account}] Skipped {duplicates_count} duplicate transactions")
|
||||
|
||||
return new_transactions
|
||||
@@ -7,7 +7,7 @@ import click
|
||||
from leggen.utils.text import success, warning
|
||||
|
||||
|
||||
def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
def persist_transactions(ctx: click.Context, account: str, transactions: list) -> list:
|
||||
# Path to your SQLite database file
|
||||
|
||||
# Connect to SQLite database
|
||||
@@ -31,7 +31,6 @@ def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
)
|
||||
|
||||
# Insert transactions into SQLite database
|
||||
new_transactions_count = 0
|
||||
duplicates_count = 0
|
||||
|
||||
# Prepare an SQL statement for inserting data
|
||||
@@ -48,6 +47,8 @@ def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
rawTransaction
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"""
|
||||
|
||||
new_transactions = []
|
||||
|
||||
for transaction in transactions:
|
||||
try:
|
||||
cursor.execute(
|
||||
@@ -65,8 +66,7 @@ def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
json.dumps(transaction["rawTransaction"]),
|
||||
),
|
||||
)
|
||||
|
||||
new_transactions_count += 1
|
||||
new_transactions.append(transaction)
|
||||
except IntegrityError:
|
||||
# A transaction with the same ID already exists, indicating a duplicate
|
||||
duplicates_count += 1
|
||||
@@ -75,6 +75,8 @@ def save_transactions(ctx: click.Context, account: str, transactions: list):
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
success(f"[{account}] Inserted {new_transactions_count} new transactions")
|
||||
success(f"[{account}] Inserted {len(new_transactions)} new transactions")
|
||||
if duplicates_count:
|
||||
warning(f"[{account}] Skipped {duplicates_count} duplicate transactions")
|
||||
|
||||
return new_transactions
|
||||
@@ -74,30 +74,33 @@ class Group(click.Group):
|
||||
return getattr(mod, name)
|
||||
|
||||
|
||||
@click.group(cls=Group, context_settings={"help_option_names": ["-h", "--help"]})
|
||||
@click.option(
|
||||
"-c",
|
||||
"--config",
|
||||
type=click.Path(dir_okay=False),
|
||||
default=click.get_app_dir("leggen") / Path("config.toml"),
|
||||
show_default=True,
|
||||
callback=load_config,
|
||||
is_eager=True,
|
||||
expose_value=False,
|
||||
envvar="LEGGEN_CONFIG_FILE",
|
||||
show_envvar=True,
|
||||
help="Path to TOML configuration file",
|
||||
)
|
||||
@click.group(
|
||||
cls=Group,
|
||||
context_settings={"help_option_names": ["-h", "--help"]},
|
||||
)
|
||||
@click.version_option(package_name="leggen")
|
||||
@click.pass_context
|
||||
def cli(ctx: click.Context):
|
||||
"""
|
||||
Leggen: An Open Banking CLI
|
||||
"""
|
||||
ctx.ensure_object(dict)
|
||||
|
||||
# Do not require authentication when printing help messages
|
||||
if "--help" in sys.argv[1:] or "-h" in sys.argv[1:]:
|
||||
return
|
||||
|
||||
# or when running the init command
|
||||
if ctx.invoked_subcommand == "init":
|
||||
if (click.get_app_dir("leggen") / Path("config.json")).is_file():
|
||||
click.confirm(
|
||||
"Configuration file already exists. Do you want to overwrite it?",
|
||||
abort=True,
|
||||
)
|
||||
return
|
||||
config = load_config()
|
||||
token = get_token(config)
|
||||
ctx.obj["api_url"] = config["api_url"]
|
||||
ctx.obj["sqlite"] = config["sqlite"]
|
||||
ctx.obj["mongo_uri"] = config["mongo_uri"]
|
||||
token = get_token(ctx)
|
||||
ctx.obj["headers"] = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
33
leggen/notifications/discord.py
Normal file
33
leggen/notifications/discord.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import click
|
||||
from discord_webhook import DiscordEmbed, DiscordWebhook
|
||||
|
||||
from leggen.utils.text import info
|
||||
|
||||
|
||||
def send_message(ctx: click.Context, transactions: list):
|
||||
info(f"Got {len(transactions)} new transactions, sending message to Discord")
|
||||
webhook = DiscordWebhook(url=ctx.obj["notifications"]["discord"]["webhook"])
|
||||
|
||||
embed = DiscordEmbed(
|
||||
title="",
|
||||
description=f"{len(transactions)} new transaction matches",
|
||||
color="03b2f8",
|
||||
)
|
||||
embed.set_author(
|
||||
name="Leggen",
|
||||
url="https://github.com/elisiariocouto/leggen",
|
||||
)
|
||||
embed.set_footer(text="Case-insensitive filters")
|
||||
embed.set_timestamp()
|
||||
for transaction in transactions:
|
||||
embed.add_embed_field(
|
||||
name=transaction["name"],
|
||||
value=f"{transaction['value']}{transaction['currency']} ({transaction['date']})",
|
||||
)
|
||||
|
||||
webhook.add_embed(embed)
|
||||
response = webhook.execute()
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except Exception as e:
|
||||
raise Exception(f"Discord notification failed: {e}\n{response.text}") from e
|
||||
43
leggen/notifications/telegram.py
Normal file
43
leggen/notifications/telegram.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import click
|
||||
import requests
|
||||
|
||||
from leggen.utils.text import info
|
||||
|
||||
|
||||
def escape_markdown(text: str) -> str:
|
||||
return (
|
||||
str(text)
|
||||
.replace("-", "\\-")
|
||||
.replace("#", "\\#")
|
||||
.replace(".", "\\.")
|
||||
.replace("$", "\\$")
|
||||
.replace("+", "\\+")
|
||||
)
|
||||
|
||||
|
||||
def send_message(ctx: click.Context, transactions: list):
|
||||
token = ctx.obj["notifications"]["telegram"]["api-key"]
|
||||
chat_id = ctx.obj["notifications"]["telegram"]["chat-id"]
|
||||
bot_url = f"https://api.telegram.org/bot{token}/sendMessage"
|
||||
info(f"Got {len(transactions)} new transactions, sending message to Telegram")
|
||||
message = "*💲 [Leggen](https://github.com/elisiariocouto/leggen)*\n"
|
||||
message += f"{len(transactions)} new transaction matches\n\n"
|
||||
|
||||
for transaction in transactions:
|
||||
message += f"*Name*: {transaction['name']}\n"
|
||||
message += f"*Value*: {transaction['value']}{transaction['currency']}\n"
|
||||
message += f"*Date*: {transaction['date']}\n\n"
|
||||
|
||||
res = requests.post(
|
||||
bot_url,
|
||||
json={
|
||||
"chat_id": chat_id,
|
||||
"text": escape_markdown(message),
|
||||
"parse_mode": "MarkdownV2",
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except Exception as e:
|
||||
raise Exception(f"Telegram notification failed: {e}\n{res.text}") from e
|
||||
@@ -7,13 +7,16 @@ import requests
|
||||
from leggen.utils.text import warning
|
||||
|
||||
|
||||
def create_token(config: dict) -> str:
|
||||
def create_token(ctx: click.Context) -> str:
|
||||
"""
|
||||
Create a new token
|
||||
"""
|
||||
res = requests.post(
|
||||
f"{config['api_url']}/token/new/",
|
||||
json={"secret_id": config["api_key"], "secret_key": config["api_secret"]},
|
||||
f"{ctx.obj['gocardless']['url']}/token/new/",
|
||||
json={
|
||||
"secret_id": ctx.obj["gocardless"]["key"],
|
||||
"secret_key": ctx.obj["gocardless"]["secret"],
|
||||
},
|
||||
)
|
||||
res.raise_for_status()
|
||||
auth = res.json()
|
||||
@@ -21,7 +24,7 @@ def create_token(config: dict) -> str:
|
||||
return auth["access"]
|
||||
|
||||
|
||||
def get_token(config: dict) -> str:
|
||||
def get_token(ctx: click.Context) -> str:
|
||||
"""
|
||||
Get the token from the auth file or request a new one
|
||||
"""
|
||||
@@ -30,10 +33,11 @@ def get_token(config: dict) -> str:
|
||||
with click.open_file(str(auth_file), "r") as f:
|
||||
auth = json.load(f)
|
||||
if not auth.get("access"):
|
||||
return create_token(config)
|
||||
return create_token(ctx)
|
||||
|
||||
res = requests.post(
|
||||
f"{config['api_url']}/token/refresh/", json={"refresh": auth["refresh"]}
|
||||
f"{ctx.obj['gocardless']['url']}/token/refresh/",
|
||||
json={"refresh": auth["refresh"]},
|
||||
)
|
||||
try:
|
||||
res.raise_for_status()
|
||||
@@ -44,9 +48,9 @@ def get_token(config: dict) -> str:
|
||||
warning(
|
||||
f"Token probably expired, requesting a new one.\nResponse: {res.status_code}\n{res.text}"
|
||||
)
|
||||
return create_token(config)
|
||||
return create_token(ctx)
|
||||
else:
|
||||
return create_token(config)
|
||||
return create_token(ctx)
|
||||
|
||||
|
||||
def save_auth(d: dict):
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import click
|
||||
import tomllib
|
||||
|
||||
from leggen.utils.text import error, info
|
||||
from leggen.utils.text import error
|
||||
|
||||
|
||||
def save_config(d: dict):
|
||||
Path.mkdir(Path(click.get_app_dir("leggen")), exist_ok=True)
|
||||
config_file = click.get_app_dir("leggen") / Path("config.json")
|
||||
|
||||
with click.open_file(str(config_file), "w") as f:
|
||||
json.dump(d, f)
|
||||
info(f"Wrote configuration file at '{config_file}'")
|
||||
|
||||
|
||||
def load_config() -> dict:
|
||||
config_file = click.get_app_dir("leggen") / Path("config.json")
|
||||
def load_config(ctx: click.Context, _, filename):
|
||||
try:
|
||||
with click.open_file(str(config_file), "r") as f:
|
||||
config = json.load(f)
|
||||
return config
|
||||
with click.open_file(str(filename), "rb") as f:
|
||||
# TODO: Implement configuration file validation (use pydantic?)
|
||||
ctx.obj = tomllib.load(f)
|
||||
except FileNotFoundError:
|
||||
error(
|
||||
"Configuration file not found. Run `leggen init` to configure your account."
|
||||
"Configuration file not found. Provide a valid configuration file path with leggen --config <path> or LEGGEN_CONFIG=<path> environment variable."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
112
leggen/utils/database.py
Normal file
112
leggen/utils/database.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from datetime import datetime
|
||||
|
||||
import click
|
||||
|
||||
import leggen.database.mongo as mongodb_engine
|
||||
import leggen.database.sqlite as sqlite_engine
|
||||
from leggen.utils.network import get
|
||||
from leggen.utils.text import info, warning
|
||||
|
||||
|
||||
def persist_transactions(ctx: click.Context, account: str, transactions: list) -> list:
|
||||
sqlite = ctx.obj.get("database", {}).get("sqlite", False)
|
||||
mongodb = ctx.obj.get("database", {}).get("mongodb", False)
|
||||
|
||||
if not sqlite and not mongodb:
|
||||
warning("No database engine is enabled, skipping transaction saving")
|
||||
# WARNING: This will return the transactions list as is, without saving it to any database
|
||||
# Possible duplicate notifications will be sent if the filters are enabled
|
||||
return transactions
|
||||
|
||||
if sqlite:
|
||||
info(f"[{account}] Fetched {len(transactions)} transactions, saving to SQLite")
|
||||
return sqlite_engine.persist_transactions(ctx, account, transactions)
|
||||
else:
|
||||
info(f"[{account}] Fetched {len(transactions)} transactions, saving to MongoDB")
|
||||
return mongodb_engine.persist_transactions(ctx, account, transactions)
|
||||
|
||||
|
||||
def save_transactions(ctx: click.Context, account: str) -> list:
|
||||
info(f"[{account}] Getting account details")
|
||||
account_info = get(ctx, f"/accounts/{account}")
|
||||
|
||||
info(f"[{account}] Getting transactions")
|
||||
transactions = []
|
||||
|
||||
account_transactions = get(ctx, f"/accounts/{account}/transactions/").get(
|
||||
"transactions", []
|
||||
)
|
||||
|
||||
for transaction in account_transactions.get("booked", []):
|
||||
booked_date = transaction.get("bookingDateTime") or transaction.get(
|
||||
"bookingDate"
|
||||
)
|
||||
value_date = transaction.get("valueDateTime") or transaction.get("valueDate")
|
||||
if booked_date and value_date:
|
||||
min_date = min(
|
||||
datetime.fromisoformat(booked_date), datetime.fromisoformat(value_date)
|
||||
)
|
||||
else:
|
||||
min_date = datetime.fromisoformat(booked_date or value_date)
|
||||
|
||||
transactionValue = float(
|
||||
transaction.get("transactionAmount", {}).get("amount", 0)
|
||||
)
|
||||
currency = transaction.get("transactionAmount", {}).get("currency", "")
|
||||
|
||||
description = transaction.get(
|
||||
"remittanceInformationUnstructured",
|
||||
",".join(transaction.get("remittanceInformationUnstructuredArray", [])),
|
||||
)
|
||||
|
||||
t = {
|
||||
"internalTransactionId": transaction.get("internalTransactionId"),
|
||||
"institutionId": account_info["institution_id"],
|
||||
"iban": account_info.get("iban", "N/A"),
|
||||
"transactionDate": min_date,
|
||||
"description": description,
|
||||
"transactionValue": transactionValue,
|
||||
"transactionCurrency": currency,
|
||||
"transactionStatus": "booked",
|
||||
"accountId": account,
|
||||
"rawTransaction": transaction,
|
||||
}
|
||||
transactions.append(t)
|
||||
|
||||
for transaction in account_transactions.get("pending", []):
|
||||
booked_date = transaction.get("bookingDateTime") or transaction.get(
|
||||
"bookingDate"
|
||||
)
|
||||
value_date = transaction.get("valueDateTime") or transaction.get("valueDate")
|
||||
if booked_date and value_date:
|
||||
min_date = min(
|
||||
datetime.fromisoformat(booked_date), datetime.fromisoformat(value_date)
|
||||
)
|
||||
else:
|
||||
min_date = datetime.fromisoformat(booked_date or value_date)
|
||||
|
||||
transactionValue = float(
|
||||
transaction.get("transactionAmount", {}).get("amount", 0)
|
||||
)
|
||||
currency = transaction.get("transactionAmount", {}).get("currency", "")
|
||||
|
||||
description = transaction.get(
|
||||
"remittanceInformationUnstructured",
|
||||
",".join(transaction.get("remittanceInformationUnstructuredArray", [])),
|
||||
)
|
||||
|
||||
t = {
|
||||
"internalTransactionId": transaction.get("internalTransactionId"),
|
||||
"institutionId": account_info["institution_id"],
|
||||
"iban": account_info.get("iban", "N/A"),
|
||||
"transactionDate": min_date,
|
||||
"description": description,
|
||||
"transactionValue": transactionValue,
|
||||
"transactionCurrency": currency,
|
||||
"transactionStatus": "pending",
|
||||
"accountId": account,
|
||||
"rawTransaction": transaction,
|
||||
}
|
||||
transactions.append(t)
|
||||
|
||||
return persist_transactions(ctx, account, transactions)
|
||||
@@ -9,12 +9,12 @@ def get(ctx: click.Context, path: str, params: dict = {}):
|
||||
GET request to the GoCardless API
|
||||
"""
|
||||
|
||||
url = f"{ctx.obj['api_url']}{path}"
|
||||
url = f"{ctx.obj['gocardless']['url']}{path}"
|
||||
res = requests.get(url, headers=ctx.obj["headers"], params=params)
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except Exception as e:
|
||||
error(f"Error: {e}")
|
||||
error(f"Error: {e}\n{res.text}")
|
||||
ctx.abort()
|
||||
return res.json()
|
||||
|
||||
@@ -24,12 +24,12 @@ def post(ctx: click.Context, path: str, data: dict = {}):
|
||||
POST request to the GoCardless API
|
||||
"""
|
||||
|
||||
url = f"{ctx.obj['api_url']}{path}"
|
||||
url = f"{ctx.obj['gocardless']['url']}{path}"
|
||||
res = requests.post(url, headers=ctx.obj["headers"], json=data)
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except Exception as e:
|
||||
error(f"Error: {e}")
|
||||
error(f"Error: {e}\n{res.text}")
|
||||
ctx.abort()
|
||||
return res.json()
|
||||
|
||||
@@ -39,12 +39,26 @@ def put(ctx: click.Context, path: str, data: dict = {}):
|
||||
PUT request to the GoCardless API
|
||||
"""
|
||||
|
||||
url = f"{ctx.obj['api_url']}{path}"
|
||||
url = f"{ctx.obj['gocardless']['url']}{path}"
|
||||
res = requests.put(url, headers=ctx.obj["headers"], json=data)
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except Exception as e:
|
||||
error(f"Error: {e}")
|
||||
error(res.text)
|
||||
error(f"Error: {e}\n{res.text}")
|
||||
ctx.abort()
|
||||
return res.json()
|
||||
|
||||
|
||||
def delete(ctx: click.Context, path: str):
|
||||
"""
|
||||
DELETE request to the GoCardless API
|
||||
"""
|
||||
|
||||
url = f"{ctx.obj['gocardless']['url']}{path}"
|
||||
res = requests.delete(url, headers=ctx.obj["headers"])
|
||||
try:
|
||||
res.raise_for_status()
|
||||
except Exception as e:
|
||||
error(f"Error: {e}\n{res.text}")
|
||||
ctx.abort()
|
||||
return res.json()
|
||||
|
||||
46
leggen/utils/notifications.py
Normal file
46
leggen/utils/notifications.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import click
|
||||
|
||||
import leggen.notifications.discord as discord
|
||||
import leggen.notifications.telegram as telegram
|
||||
from leggen.utils.text import info, warning
|
||||
|
||||
|
||||
def send_notification(ctx: click.Context, transactions: list):
|
||||
if ctx.obj.get("filters") is None:
|
||||
warning("No filters are enabled, skipping notifications")
|
||||
return
|
||||
|
||||
filters_case_insensitive = ctx.obj.get("filters", {}).get("case-insensitive", {})
|
||||
|
||||
# Add transaction to the list of transactions to be sent as a notification
|
||||
notification_transactions = []
|
||||
for transaction in transactions:
|
||||
for _, v in filters_case_insensitive.items():
|
||||
if v.lower() in transaction["description"].lower():
|
||||
notification_transactions.append(
|
||||
{
|
||||
"name": transaction["description"],
|
||||
"value": transaction["transactionValue"],
|
||||
"currency": transaction["transactionCurrency"],
|
||||
"date": transaction["transactionDate"],
|
||||
}
|
||||
)
|
||||
|
||||
if len(notification_transactions) == 0:
|
||||
warning("No transactions matched the filters, skipping notifications")
|
||||
return
|
||||
|
||||
discord_enabled = ctx.obj.get("notifications", {}).get("discord", False)
|
||||
telegram_enabled = ctx.obj.get("notifications", {}).get("telegram", False)
|
||||
|
||||
if not discord_enabled and not telegram_enabled:
|
||||
warning("No notification engine is enabled, skipping notifications")
|
||||
return
|
||||
|
||||
if discord_enabled:
|
||||
info(f"Sending {len(notification_transactions)} transactions to Discord")
|
||||
discord.send_message(ctx, notification_transactions)
|
||||
|
||||
if telegram_enabled:
|
||||
info(f"Sending {len(notification_transactions)} transactions to Telegram")
|
||||
telegram.send_message(ctx, notification_transactions)
|
||||
342
poetry.lock
generated
342
poetry.lock
generated
@@ -1,34 +1,34 @@
|
||||
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.1.1"
|
||||
version = "24.3.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"},
|
||||
{file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"},
|
||||
{file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"},
|
||||
{file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"},
|
||||
{file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"},
|
||||
{file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"},
|
||||
{file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"},
|
||||
{file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"},
|
||||
{file = "black-24.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e2c8dfa14677f90d976f68e0c923947ae68fa3961d61ee30976c388adc0b02c8"},
|
||||
{file = "black-24.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a21725862d0e855ae05da1dd25e3825ed712eaaccef6b03017fe0853a01aa45e"},
|
||||
{file = "black-24.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07204d078e25327aad9ed2c64790d681238686bce254c910de640c7cc4fc3aa6"},
|
||||
{file = "black-24.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a83fe522d9698d8f9a101b860b1ee154c1d25f8a82ceb807d319f085b2627c5b"},
|
||||
{file = "black-24.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:08b34e85170d368c37ca7bf81cf67ac863c9d1963b2c1780c39102187ec8dd62"},
|
||||
{file = "black-24.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7258c27115c1e3b5de9ac6c4f9957e3ee2c02c0b39222a24dc7aa03ba0e986f5"},
|
||||
{file = "black-24.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40657e1b78212d582a0edecafef133cf1dd02e6677f539b669db4746150d38f6"},
|
||||
{file = "black-24.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e298d588744efda02379521a19639ebcd314fba7a49be22136204d7ed1782717"},
|
||||
{file = "black-24.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:34afe9da5056aa123b8bfda1664bfe6fb4e9c6f311d8e4a6eb089da9a9173bf9"},
|
||||
{file = "black-24.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:854c06fb86fd854140f37fb24dbf10621f5dab9e3b0c29a690ba595e3d543024"},
|
||||
{file = "black-24.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3897ae5a21ca132efa219c029cce5e6bfc9c3d34ed7e892113d199c0b1b444a2"},
|
||||
{file = "black-24.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:ecba2a15dfb2d97105be74bbfe5128bc5e9fa8477d8c46766505c1dda5883aac"},
|
||||
{file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"},
|
||||
{file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
|
||||
{file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
|
||||
{file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
|
||||
{file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
|
||||
{file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
|
||||
{file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
|
||||
{file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
|
||||
{file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
|
||||
{file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
|
||||
{file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
|
||||
{file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
|
||||
{file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
|
||||
{file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -46,13 +46,13 @@ uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2023.11.17"
|
||||
version = "2024.2.2"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
|
||||
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
|
||||
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
|
||||
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -190,6 +190,23 @@ files = [
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "discord-webhook"
|
||||
version = "1.3.1"
|
||||
description = "Easily send Discord webhooks with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.10,<4.0"
|
||||
files = [
|
||||
{file = "discord_webhook-1.3.1-py3-none-any.whl", hash = "sha256:ede07028316de76d24eb811836e2b818b2017510da786777adcb0d5970e7af79"},
|
||||
{file = "discord_webhook-1.3.1.tar.gz", hash = "sha256:ee3e0f3ea4f3dc8dc42be91f75b894a01624c6c13fea28e23ebcf9a6c9a304f7"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
requests = ">=2.28.1,<3.0.0"
|
||||
|
||||
[package.extras]
|
||||
async = ["httpx (>=0.23.0,<0.24.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.8"
|
||||
@@ -203,13 +220,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "dnspython"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
description = "DNS toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "dnspython-2.6.0-py3-none-any.whl", hash = "sha256:44c40af3bffed66e3307cea9ab667fd583e138ecc0777b18f262a9dae034e5fa"},
|
||||
{file = "dnspython-2.6.0.tar.gz", hash = "sha256:233f871ff384d84c33b2eaf4358ffe7f8927eae3b257ad8467f9bdba7e7ac6bc"},
|
||||
{file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"},
|
||||
{file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -223,29 +240,29 @@ wmi = ["wmi (>=1.5.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.13.1"
|
||||
version = "3.13.3"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
|
||||
{file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
|
||||
{file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"},
|
||||
{file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||
typing = ["typing-extensions (>=4.8)"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.5.33"
|
||||
version = "2.5.35"
|
||||
description = "File identification library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
|
||||
{file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
|
||||
{file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"},
|
||||
{file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -307,13 +324,13 @@ setuptools = "*"
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.2"
|
||||
version = "24.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -329,28 +346,28 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.1.0"
|
||||
version = "4.2.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
|
||||
{file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
|
||||
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
||||
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
|
||||
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "3.6.0"
|
||||
version = "3.7.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-3.6.0-py2.py3-none-any.whl", hash = "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376"},
|
||||
{file = "pre_commit-3.6.0.tar.gz", hash = "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d"},
|
||||
{file = "pre_commit-3.7.0-py2.py3-none-any.whl", hash = "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab"},
|
||||
{file = "pre_commit-3.7.0.tar.gz", hash = "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -362,93 +379,93 @@ virtualenv = ">=20.10.0"
|
||||
|
||||
[[package]]
|
||||
name = "pymongo"
|
||||
version = "4.6.1"
|
||||
version = "4.6.2"
|
||||
description = "Python driver for MongoDB <http://www.mongodb.org>"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pymongo-4.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4344c30025210b9fa80ec257b0e0aab5aa1d5cca91daa70d82ab97b482cc038e"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux1_i686.whl", hash = "sha256:1c5654bb8bb2bdb10e7a0bc3c193dd8b49a960b9eebc4381ff5a2043f4c3c441"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:eaf2f65190c506def2581219572b9c70b8250615dc918b3b7c218361a51ec42e"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:262356ea5fcb13d35fb2ab6009d3927bafb9504ef02339338634fffd8a9f1ae4"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:2dd2f6960ee3c9360bed7fb3c678be0ca2d00f877068556785ec2eb6b73d2414"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:ff925f1cca42e933376d09ddc254598f8c5fcd36efc5cac0118bb36c36217c41"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:3cadf7f4c8e94d8a77874b54a63c80af01f4d48c4b669c8b6867f86a07ba994f"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55dac73316e7e8c2616ba2e6f62b750918e9e0ae0b2053699d66ca27a7790105"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:154b361dcb358ad377d5d40df41ee35f1cc14c8691b50511547c12404f89b5cb"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2940aa20e9cc328e8ddeacea8b9a6f5ddafe0b087fedad928912e787c65b4909"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:010bc9aa90fd06e5cc52c8fac2c2fd4ef1b5f990d9638548dde178005770a5e8"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e470fa4bace5f50076c32f4b3cc182b31303b4fefb9b87f990144515d572820b"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-win32.whl", hash = "sha256:da08ea09eefa6b960c2dd9a68ec47949235485c623621eb1d6c02b46765322ac"},
|
||||
{file = "pymongo-4.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:13d613c866f9f07d51180f9a7da54ef491d130f169e999c27e7633abe8619ec9"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6a0ae7a48a6ef82ceb98a366948874834b86c84e288dbd55600c1abfc3ac1d88"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bd94c503271e79917b27c6e77f7c5474da6930b3fb9e70a12e68c2dff386b9a"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2d4ccac3053b84a09251da8f5350bb684cbbf8c8c01eda6b5418417d0a8ab198"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:349093675a2d3759e4fb42b596afffa2b2518c890492563d7905fac503b20daa"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88beb444fb438385e53dc9110852910ec2a22f0eab7dd489e827038fdc19ed8d"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8e62d06e90f60ea2a3d463ae51401475568b995bafaffd81767d208d84d7bb1"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-win32.whl", hash = "sha256:5556e306713e2522e460287615d26c0af0fe5ed9d4f431dad35c6624c5d277e9"},
|
||||
{file = "pymongo-4.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:b10d8cda9fc2fcdcfa4a000aa10413a2bf8b575852cd07cb8a595ed09689ca98"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b435b13bb8e36be11b75f7384a34eefe487fe87a6267172964628e2b14ecf0a7"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e438417ce1dc5b758742e12661d800482200b042d03512a8f31f6aaa9137ad40"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b47ebd89e69fbf33d1c2df79759d7162fc80c7652dacfec136dae1c9b3afac7"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbed8cccebe1169d45cedf00461b2842652d476d2897fd1c42cf41b635d88746"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c30a9e06041fbd7a7590693ec5e407aa8737ad91912a1e70176aff92e5c99d20"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8729dbf25eb32ad0dc0b9bd5e6a0d0b7e5c2dc8ec06ad171088e1896b522a74"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-win32.whl", hash = "sha256:3177f783ae7e08aaf7b2802e0df4e4b13903520e8380915e6337cdc7a6ff01d8"},
|
||||
{file = "pymongo-4.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:00c199e1c593e2c8b033136d7a08f0c376452bac8a896c923fcd6f419e07bdd2"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6dcc95f4bb9ed793714b43f4f23a7b0c57e4ef47414162297d6f650213512c19"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:13552ca505366df74e3e2f0a4f27c363928f3dff0eef9f281eb81af7f29bc3c5"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:77e0df59b1a4994ad30c6d746992ae887f9756a43fc25dec2db515d94cf0222d"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3a7f02a58a0c2912734105e05dedbee4f7507e6f1bd132ebad520be0b11d46fd"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:026a24a36394dc8930cbcb1d19d5eb35205ef3c838a7e619e04bd170713972e7"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:3b287e814a01deddb59b88549c1e0c87cefacd798d4afc0c8bd6042d1c3d48aa"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:9a710c184ba845afb05a6f876edac8f27783ba70e52d5eaf939f121fc13b2f59"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:30b2c9caf3e55c2e323565d1f3b7e7881ab87db16997dc0cbca7c52885ed2347"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff62ba8ff70f01ab4fe0ae36b2cb0b5d1f42e73dfc81ddf0758cd9f77331ad25"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:547dc5d7f834b1deefda51aedb11a7af9c51c45e689e44e14aa85d44147c7657"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1de3c6faf948f3edd4e738abdb4b76572b4f4fdfc1fed4dad02427e70c5a6219"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2831e05ce0a4df10c4ac5399ef50b9a621f90894c2a4d2945dc5658765514ed"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:144a31391a39a390efce0c5ebcaf4bf112114af4384c90163f402cec5ede476b"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33bb16a07d3cc4e0aea37b242097cd5f7a156312012455c2fa8ca396953b11c4"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b7b1a83ce514700276a46af3d9e481ec381f05b64939effc9065afe18456a6b9"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-win32.whl", hash = "sha256:3071ec998cc3d7b4944377e5f1217c2c44b811fae16f9a495c7a1ce9b42fb038"},
|
||||
{file = "pymongo-4.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:2346450a075625c4d6166b40a013b605a38b6b6168ce2232b192a37fb200d588"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:061598cbc6abe2f382ab64c9caa83faa2f4c51256f732cdd890bcc6e63bfb67e"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d483793a384c550c2d12cb794ede294d303b42beff75f3b3081f57196660edaf"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f9756f1d25454ba6a3c2f1ef8b7ddec23e5cdeae3dc3c3377243ae37a383db00"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ed23b0e2dac6f84f44c8494fbceefe6eb5c35db5c1099f56ab78fc0d94ab3af"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:3d18a9b9b858ee140c15c5bfcb3e66e47e2a70a03272c2e72adda2482f76a6ad"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:c258dbacfff1224f13576147df16ce3c02024a0d792fd0323ac01bed5d3c545d"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:f7acc03a4f1154ba2643edeb13658d08598fe6e490c3dd96a241b94f09801626"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:76013fef1c9cd1cd00d55efde516c154aa169f2bf059b197c263a255ba8a9ddf"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0e6a6c807fa887a0c51cc24fe7ea51bb9e496fe88f00d7930063372c3664c3"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd1fa413f8b9ba30140de198e4f408ffbba6396864c7554e0867aa7363eb58b2"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d219b4508f71d762368caec1fc180960569766049bbc4d38174f05e8ef2fe5b"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27b81ecf18031998ad7db53b960d1347f8f29e8b7cb5ea7b4394726468e4295e"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56816e43c92c2fa8c11dc2a686f0ca248bea7902f4a067fa6cbc77853b0f041e"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef801027629c5b511cf2ba13b9be29bfee36ae834b2d95d9877818479cdc99ea"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d4c2be9760b112b1caf649b4977b81b69893d75aa86caf4f0f398447be871f3c"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-win32.whl", hash = "sha256:39d77d8bbb392fa443831e6d4ae534237b1f4eee6aa186f0cdb4e334ba89536e"},
|
||||
{file = "pymongo-4.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:4497d49d785482cc1a44a0ddf8830b036a468c088e72a05217f5b60a9e025012"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:69247f7a2835fc0984bbf0892e6022e9a36aec70e187fcfe6cae6a373eb8c4de"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7bb0e9049e81def6829d09558ad12d16d0454c26cabe6efc3658e544460688d9"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6a1810c2cbde714decf40f811d1edc0dae45506eb37298fd9d4247b8801509fe"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:e2aced6fb2f5261b47d267cb40060b73b6527e64afe54f6497844c9affed5fd0"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d0355cff58a4ed6d5e5f6b9c3693f52de0784aa0c17119394e2a8e376ce489d4"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:3c74f4725485f0a7a3862cfd374cc1b740cebe4c133e0c1425984bcdcce0f4bb"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:9c79d597fb3a7c93d7c26924db7497eba06d58f88f58e586aa69b2ad89fee0f8"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8ec75f35f62571a43e31e7bd11749d974c1b5cd5ea4a8388725d579263c0fdf6"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e641f931c5cd95b376fd3c59db52770e17bec2bf86ef16cc83b3906c054845"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9aafd036f6f2e5ad109aec92f8dbfcbe76cff16bad683eb6dd18013739c0b3ae"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f2b856518bfcfa316c8dae3d7b412aecacf2e8ba30b149f5eb3b63128d703b9"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec31adc2e988fd7db3ab509954791bbc5a452a03c85e45b804b4bfc31fa221d"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9167e735379ec43d8eafa3fd675bfbb12e2c0464f98960586e9447d2cf2c7a83"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1461199b07903fc1424709efafe379205bf5f738144b1a50a08b0396357b5abf"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3094c7d2f820eecabadae76bfec02669567bbdd1730eabce10a5764778564f7b"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-win32.whl", hash = "sha256:c91ea3915425bd4111cb1b74511cdc56d1d16a683a48bf2a5a96b6a6c0f297f7"},
|
||||
{file = "pymongo-4.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ef102a67ede70e1721fe27f75073b5314911dbb9bc27cde0a1c402a11531e7bd"},
|
||||
{file = "pymongo-4.6.1.tar.gz", hash = "sha256:31dab1f3e1d0cdd57e8df01b645f52d43cc1b653ed3afd535d2891f4fc4f9712"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7640d176ee5b0afec76a1bda3684995cb731b2af7fcfd7c7ef8dc271c5d689af"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux1_i686.whl", hash = "sha256:4e2129ec8f72806751b621470ac5d26aaa18fae4194796621508fa0e6068278a"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:c43205e85cbcbdf03cff62ad8f50426dd9d20134a915cfb626d805bab89a1844"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:91ddf95cedca12f115fbc5f442b841e81197d85aa3cc30b82aee3635a5208af2"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:0fbdbf2fba1b4f5f1522e9f11e21c306e095b59a83340a69e908f8ed9b450070"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:097791d5a8d44e2444e0c8c4d6e14570ac11e22bcb833808885a5db081c3dc2a"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:e0b208ebec3b47ee78a5c836e2e885e8c1e10f8ffd101aaec3d63997a4bdcd04"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1849fd6f1917b4dc5dbf744b2f18e41e0538d08dd8e9ba9efa811c5149d665a3"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa0bbbfbd1f8ebbd5facaa10f9f333b20027b240af012748555148943616fdf3"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4522ad69a4ab0e1b46a8367d62ad3865b8cd54cf77518c157631dac1fdc97584"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397949a9cc85e4a1452f80b7f7f2175d557237177120954eff00bf79553e89d3"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d511db310f43222bc58d811037b176b4b88dc2b4617478c5ef01fea404f8601"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-win32.whl", hash = "sha256:991e406db5da4d89fb220a94d8caaf974ffe14ce6b095957bae9273c609784a0"},
|
||||
{file = "pymongo-4.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:94637941fe343000f728e28d3fe04f1f52aec6376b67b85583026ff8dab2a0e0"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:84593447a5c5fe7a59ba86b72c2c89d813fbac71c07757acdf162fbfd5d005b9"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9aebddb2ec2128d5fc2fe3aee6319afef8697e0374f8a1fcca3449d6f625e7b4"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f706c1a644ed33eaea91df0a8fb687ce572b53eeb4ff9b89270cb0247e5d0e1"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18c422e6b08fa370ed9d8670c67e78d01f50d6517cec4522aa8627014dfa38b6"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d002ae456a15b1d790a78bb84f87af21af1cb716a63efb2c446ab6bcbbc48ca"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f86ba0c781b497a3c9c886765d7b6402a0e3ae079dd517365044c89cd7abb06"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-win32.whl", hash = "sha256:ac20dd0c7b42555837c86f5ea46505f35af20a08b9cf5770cd1834288d8bd1b4"},
|
||||
{file = "pymongo-4.6.2-cp311-cp311-win_amd64.whl", hash = "sha256:e78af59fd0eb262c2a5f7c7d7e3b95e8596a75480d31087ca5f02f2d4c6acd19"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6125f73503407792c8b3f80165f8ab88a4e448d7d9234c762681a4d0b446fcb4"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba052446a14bd714ec83ca4e77d0d97904f33cd046d7bb60712a6be25eb31dbb"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b65433c90e07dc252b4a55dfd885ca0df94b1cf77c5b8709953ec1983aadc03"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2160d9c8cd20ce1f76a893f0daf7c0d38af093f36f1b5c9f3dcf3e08f7142814"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f251f287e6d42daa3654b686ce1fcb6d74bf13b3907c3ae25954978c70f2cd4"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7d227a60b00925dd3aeae4675575af89c661a8e89a1f7d1677e57eba4a3693c"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-win32.whl", hash = "sha256:311794ef3ccae374aaef95792c36b0e5c06e8d5cf04a1bdb1b2bf14619ac881f"},
|
||||
{file = "pymongo-4.6.2-cp312-cp312-win_amd64.whl", hash = "sha256:f673b64a0884edcc56073bda0b363428dc1bf4eb1b5e7d0b689f7ec6173edad6"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:fe010154dfa9e428bd2fb3e9325eff2216ab20a69ccbd6b5cac6785ca2989161"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f5f4cd2969197e25b67e24d5b8aa2452d381861d2791d06c493eaa0b9c9fcfe"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c9519c9d341983f3a1bd19628fecb1d72a48d8666cf344549879f2e63f54463b"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c68bf4a399e37798f1b5aa4f6c02886188ef465f4ac0b305a607b7579413e366"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a509db602462eb736666989739215b4b7d8f4bb8ac31d0bffd4be9eae96c63ef"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:362a5adf6f3f938a8ff220a4c4aaa93e84ef932a409abecd837c617d17a5990f"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:ee30a9d4c27a88042d0636aca0275788af09cc237ae365cd6ebb34524bddb9cc"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:477914e13501bb1d4608339ee5bb618be056d2d0e7267727623516cfa902e652"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd343ca44982d480f1e39372c48e8e263fc6f32e9af2be456298f146a3db715"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3797e0a628534e07a36544d2bfa69e251a578c6d013e975e9e3ed2ac41f2d95"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97d81d357e1a2a248b3494d52ebc8bf15d223ee89d59ee63becc434e07438a24"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed694c0d1977cb54281cb808bc2b247c17fb64b678a6352d3b77eb678ebe1bd9"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ceaaff4b812ae368cf9774989dea81b9bbb71e5bed666feca6a9f3087c03e49"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7dd63f7c2b3727541f7f37d0fb78d9942eb12a866180fbeb898714420aad74e2"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e571434633f99a81e081738721bb38e697345281ed2f79c2f290f809ba3fbb2f"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:3e9f6e2f3da0a6af854a3e959a6962b5f8b43bbb8113cd0bff0421c5059b3106"},
|
||||
{file = "pymongo-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:3a5280f496297537301e78bde250c96fadf4945e7b2c397d8bb8921861dd236d"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:5f6bcd2d012d82d25191a911a239fd05a8a72e8c5a7d81d056c0f3520cad14d1"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:4fa30494601a6271a8b416554bd7cde7b2a848230f0ec03e3f08d84565b4bf8c"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bea62f03a50f363265a7a651b4e2a4429b4f138c1864b2d83d4bf6f9851994be"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b2d445f1cf147331947cc35ec10342f898329f29dd1947a3f8aeaf7e0e6878d1"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:5db133d6ec7a4f7fc7e2bd098e4df23d7ad949f7be47b27b515c9fb9301c61e4"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:9eec7140cf7513aa770ea51505d312000c7416626a828de24318fdcc9ac3214c"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:5379ca6fd325387a34cda440aec2bd031b5ef0b0aa2e23b4981945cff1dab84c"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:579508536113dbd4c56e4738955a18847e8a6c41bf3c0b4ab18b51d81a6b7be8"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3bae553ca39ed52db099d76acd5e8566096064dc7614c34c9359bb239ec4081"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0257e0eebb50f242ca28a92ef195889a6ad03dcdde5bf1c7ab9f38b7e810801"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbafe3a1df21eeadb003c38fc02c1abf567648b6477ec50c4a3c042dca205371"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaecfafb407feb6f562c7f2f5b91f22bfacba6dd739116b1912788cff7124c4a"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e942945e9112075a84d2e2d6e0d0c98833cdcdfe48eb8952b917f996025c7ffa"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f7b98f8d2cf3eeebde738d080ae9b4276d7250912d9751046a9ac1efc9b1ce2"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8110b78fc4b37dced85081d56795ecbee6a7937966e918e05e33a3900e8ea07d"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-win32.whl", hash = "sha256:df813f0c2c02281720ccce225edf39dc37855bf72cdfde6f789a1d1cf32ffb4b"},
|
||||
{file = "pymongo-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:64ec3e2dcab9af61bdbfcb1dd863c70d1b0c220b8e8ac11df8b57f80ee0402b3"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bff601fbfcecd2166d9a2b70777c2985cb9689e2befb3278d91f7f93a0456cae"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f1febca6f79e91feafc572906871805bd9c271b6a2d98a8bb5499b6ace0befed"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d788cb5cc947d78934be26eef1623c78cec3729dc93a30c23f049b361aa6d835"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5c2f258489de12a65b81e1b803a531ee8cf633fa416ae84de65cd5f82d2ceb37"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:fb24abcd50501b25d33a074c1790a1389b6460d2509e4b240d03fd2e5c79f463"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:4d982c6db1da7cf3018183891883660ad085de97f21490d314385373f775915b"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:b2dd8c874927a27995f64a3b44c890e8a944c98dec1ba79eab50e07f1e3f801b"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4993593de44c741d1e9f230f221fe623179f500765f9855936e4ff6f33571bad"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:658f6c028edaeb02761ebcaca8d44d519c22594b2a51dcbc9bd2432aa93319e3"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68109c13176749fbbbbbdb94dd4a58dcc604db6ea43ee300b2602154aebdd55f"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:707d28a822b918acf941cff590affaddb42a5d640614d71367c8956623a80cbc"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f251db26c239aec2a4d57fbe869e0a27b7f6b5384ec6bf54aeb4a6a5e7408234"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57c05f2e310701fc17ae358caafd99b1830014e316f0242d13ab6c01db0ab1c2"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b575fbe6396bbf21e4d0e5fd2e3cdb656dc90c930b6c5532192e9a89814f72d"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ca5877754f3fa6e4fe5aacf5c404575f04c2d9efc8d22ed39576ed9098d555c8"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-win32.whl", hash = "sha256:8caa73fb19070008e851a589b744aaa38edd1366e2487284c61158c77fdf72af"},
|
||||
{file = "pymongo-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:3e03c732cb64b96849310e1d8688fb70d75e2571385485bf2f1e7ad1d309fa53"},
|
||||
{file = "pymongo-4.6.2.tar.gz", hash = "sha256:ab7d01ac832a1663dad592ccbd92bb0f0775bc8f98a1923c5e1a7d7fead495af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -535,45 +552,45 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.1.14"
|
||||
version = "0.3.4"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:96f76536df9b26622755c12ed8680f159817be2f725c17ed9305b472a757cdbb"},
|
||||
{file = "ruff-0.1.14-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab3f71f64498c7241123bb5a768544cf42821d2a537f894b22457a543d3ca7a9"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060156ecc572b8f984fd20fd8b0fcb692dd5d837b7606e968334ab7ff0090ab"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a53d8e35313d7b67eb3db15a66c08434809107659226a90dcd7acb2afa55faea"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bea9be712b8f5b4ebed40e1949379cfb2a7d907f42921cf9ab3aae07e6fba9eb"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2270504d629a0b064247983cbc495bed277f372fb9eaba41e5cf51f7ba705a6a"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80258bb3b8909b1700610dfabef7876423eed1bc930fe177c71c414921898efa"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:653230dd00aaf449eb5ff25d10a6e03bc3006813e2cb99799e568f55482e5cae"},
|
||||
{file = "ruff-0.1.14-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b3acc6c4e6928459ba9eb7459dd4f0c4bf266a053c863d72a44c33246bfdbf"},
|
||||
{file = "ruff-0.1.14-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6b3dadc9522d0eccc060699a9816e8127b27addbb4697fc0c08611e4e6aeb8b5"},
|
||||
{file = "ruff-0.1.14-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1c8eca1a47b4150dc0fbec7fe68fc91c695aed798532a18dbb1424e61e9b721f"},
|
||||
{file = "ruff-0.1.14-py3-none-musllinux_1_2_i686.whl", hash = "sha256:62ce2ae46303ee896fc6811f63d6dabf8d9c389da0f3e3f2bce8bc7f15ef5488"},
|
||||
{file = "ruff-0.1.14-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b2027dde79d217b211d725fc833e8965dc90a16d0d3213f1298f97465956661b"},
|
||||
{file = "ruff-0.1.14-py3-none-win32.whl", hash = "sha256:722bafc299145575a63bbd6b5069cb643eaa62546a5b6398f82b3e4403329cab"},
|
||||
{file = "ruff-0.1.14-py3-none-win_amd64.whl", hash = "sha256:e3d241aa61f92b0805a7082bd89a9990826448e4d0398f0e2bc8f05c75c63d99"},
|
||||
{file = "ruff-0.1.14-py3-none-win_arm64.whl", hash = "sha256:269302b31ade4cde6cf6f9dd58ea593773a37ed3f7b97e793c8594b262466b67"},
|
||||
{file = "ruff-0.1.14.tar.gz", hash = "sha256:ad3f8088b2dfd884820289a06ab718cde7d38b94972212cc4ba90d5fbc9955f3"},
|
||||
{file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"},
|
||||
{file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"},
|
||||
{file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"},
|
||||
{file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"},
|
||||
{file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"},
|
||||
{file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"},
|
||||
{file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"},
|
||||
{file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "69.0.3"
|
||||
version = "69.2.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
|
||||
{file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
|
||||
{file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"},
|
||||
{file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "tabulate"
|
||||
@@ -591,29 +608,30 @@ widechars = ["wcwidth"]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.1.0"
|
||||
version = "2.2.1"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"},
|
||||
{file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
|
||||
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
|
||||
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
|
||||
h2 = ["h2 (>=4,<5)"]
|
||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.25.0"
|
||||
version = "20.25.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
|
||||
{file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
|
||||
{file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"},
|
||||
{file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -642,4 +660,4 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.12"
|
||||
content-hash = "5dd54a205612068bfd2126aca1a10a6ff43f491b445021d485b9de513a06e163"
|
||||
content-hash = "066efa29ad01e47892f68c061ebfc81eea6f8a541bb266ccef47011fdd4f5e89"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "leggen"
|
||||
version = "0.2.2"
|
||||
version = "0.5.0"
|
||||
description = "An Open Banking CLI"
|
||||
authors = ["Elisiário Couto <elisiario@couto.io>"]
|
||||
readme = "README.md"
|
||||
@@ -34,9 +34,10 @@ requests = "^2.31.0"
|
||||
loguru = "^0.7.2"
|
||||
tabulate = "^0.9.0"
|
||||
pymongo = "^4.6.1"
|
||||
discord-webhook = "^1.3.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.1.14"
|
||||
ruff = "^0.3.0"
|
||||
pre-commit = "^3.6.0"
|
||||
black = "^24.1.1"
|
||||
|
||||
@@ -48,5 +49,5 @@ requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
ignore = ["E501", "B008", "B006"]
|
||||
extend-select = ["B", "C4", "PIE", "T20", "SIM", "TCH"]
|
||||
lint.ignore = ["E501", "B008", "B006"]
|
||||
lint.extend-select = ["B", "C4", "PIE", "T20", "SIM", "TCH"]
|
||||
|
||||
Reference in New Issue
Block a user