mirror of
https://github.com/NovaOSS/nova-api.git
synced 2024-11-25 16:43:58 +01:00
Added finances endpoint
This commit is contained in:
parent
453c6e7430
commit
1384b3add8
1
api/cache/crypto_prices.json
vendored
Normal file
1
api/cache/crypto_prices.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"LTC": 64.665, "_last_updated": 1695334741.4905503, "BTC": 26583.485, "MATIC": 0.52075, "XMR": 146.46058828041404, "ADA": 0.2455, "USDT": 1.000005, "ETH": 1586.115, "USD": 1.0, "EUR": 1.0662838016640013}
|
0
api/models.json → api/cache/models.json
vendored
0
api/models.json → api/cache/models.json
vendored
68
api/core.py
68
api/core.py
|
@ -8,9 +8,12 @@ sys.path.append(project_root)
|
||||||
|
|
||||||
# the code above is to allow importing from the root folder
|
# the code above is to allow importing from the root folder
|
||||||
|
|
||||||
|
import time
|
||||||
import json
|
import json
|
||||||
import hmac
|
import hmac
|
||||||
|
import httpx
|
||||||
import fastapi
|
import fastapi
|
||||||
|
import functools
|
||||||
|
|
||||||
from dhooks import Webhook, Embed
|
from dhooks import Webhook, Embed
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
@ -18,7 +21,7 @@ from dotenv import load_dotenv
|
||||||
import checks.client
|
import checks.client
|
||||||
|
|
||||||
from helpers import errors
|
from helpers import errors
|
||||||
from db.users import UserManager
|
from db import users, finances
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
router = fastapi.APIRouter(tags=['core'])
|
router = fastapi.APIRouter(tags=['core'])
|
||||||
|
@ -62,9 +65,7 @@ async def get_users(discord_id: int, incoming_request: fastapi.Request):
|
||||||
auth = await check_core_auth(incoming_request)
|
auth = await check_core_auth(incoming_request)
|
||||||
if auth: return auth
|
if auth: return auth
|
||||||
|
|
||||||
# Get user by discord ID
|
user = await users.manager.user_by_discord_id(discord_id)
|
||||||
manager = UserManager()
|
|
||||||
user = await manager.user_by_discord_id(discord_id)
|
|
||||||
if not user:
|
if not user:
|
||||||
return await errors.error(404, 'Discord user not found in the API database.', 'Check the `discord_id` parameter.')
|
return await errors.error(404, 'Discord user not found in the API database.', 'Check the `discord_id` parameter.')
|
||||||
|
|
||||||
|
@ -88,9 +89,7 @@ async def create_user(incoming_request: fastapi.Request):
|
||||||
except (json.decoder.JSONDecodeError, AttributeError):
|
except (json.decoder.JSONDecodeError, AttributeError):
|
||||||
return await errors.error(400, 'Invalid or no payload received.', 'The payload must be a JSON object with a `discord_id` key.')
|
return await errors.error(400, 'Invalid or no payload received.', 'The payload must be a JSON object with a `discord_id` key.')
|
||||||
|
|
||||||
# Create the user
|
user = await users.manager.create(discord_id)
|
||||||
manager = UserManager()
|
|
||||||
user = await manager.create(discord_id)
|
|
||||||
await new_user_webhook(user)
|
await new_user_webhook(user)
|
||||||
|
|
||||||
user['_id'] = str(user['_id'])
|
user['_id'] = str(user['_id'])
|
||||||
|
@ -114,9 +113,7 @@ async def update_user(incoming_request: fastapi.Request):
|
||||||
'The payload must be a JSON object with a `discord_id` key and an `updates` key.'
|
'The payload must be a JSON object with a `discord_id` key and an `updates` key.'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the user
|
user = await users.manager.update_by_discord_id(discord_id, updates)
|
||||||
manager = UserManager()
|
|
||||||
user = await manager.update_by_discord_id(discord_id, updates)
|
|
||||||
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
@ -147,3 +144,54 @@ async def run_checks(incoming_request: fastapi.Request):
|
||||||
results[func.__name__] = result
|
results[func.__name__] = result
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
async def get_crypto_price(cryptocurrency: str) -> float:
|
||||||
|
"""Gets the price of a cryptocurrency using coinbase's API."""
|
||||||
|
|
||||||
|
if os.path.exists('cache/crypto_prices.json'):
|
||||||
|
with open('cache/crypto_prices.json', 'r') as f:
|
||||||
|
cache = json.load(f)
|
||||||
|
else:
|
||||||
|
cache = {}
|
||||||
|
|
||||||
|
is_old = time.time() - cache.get('_last_updated', 0) > 60 * 60
|
||||||
|
|
||||||
|
if is_old or cryptocurrency not in cache:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.get(f'https://api.coinbase.com/v2/prices/{cryptocurrency}-USD/spot')
|
||||||
|
usd_price = float(response.json()['data']['amount'])
|
||||||
|
|
||||||
|
cache[cryptocurrency] = usd_price
|
||||||
|
cache['_last_updated'] = time.time()
|
||||||
|
|
||||||
|
with open('cache/crypto_prices.json', 'w') as f:
|
||||||
|
json.dump(cache, f)
|
||||||
|
|
||||||
|
return cache[cryptocurrency]
|
||||||
|
|
||||||
|
@router.get('/finances')
|
||||||
|
async def get_finances(incoming_request: fastapi.Request):
|
||||||
|
"""Return financial information. Requires a core API key."""
|
||||||
|
|
||||||
|
auth_error = await check_core_auth(incoming_request)
|
||||||
|
if auth_error: return auth_error
|
||||||
|
|
||||||
|
transactions = await finances.manager.get_entire_financial_history()
|
||||||
|
|
||||||
|
for table in transactions:
|
||||||
|
for transaction in transactions[table]:
|
||||||
|
currency = transaction['currency']
|
||||||
|
|
||||||
|
if '-' in currency:
|
||||||
|
currency = currency.split('-')[0]
|
||||||
|
|
||||||
|
amount = transaction['amount']
|
||||||
|
|
||||||
|
if currency == 'mBTC':
|
||||||
|
currency = 'BTC'
|
||||||
|
amount = transaction['amount'] / 1000
|
||||||
|
|
||||||
|
amount_in_usd = await get_crypto_price(currency) * amount
|
||||||
|
transactions[table][transactions[table].index(transaction)]['amount_usd'] = amount_in_usd
|
||||||
|
|
||||||
|
return transactions
|
||||||
|
|
39
api/db/finances.py
Normal file
39
api/db/finances.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import os
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from motor.motor_asyncio import AsyncIOMotorClient
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
class FinanceManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.conn = AsyncIOMotorClient(os.environ['MONGO_URI'])
|
||||||
|
|
||||||
|
async def _get_collection(self, collection_name: str):
|
||||||
|
return self.conn['finances'][collection_name]
|
||||||
|
|
||||||
|
async def get_entire_financial_history(self):
|
||||||
|
donations_db = await self._get_collection('donations')
|
||||||
|
expenses_db = await self._get_collection('expenses')
|
||||||
|
|
||||||
|
# turn both into JSON-like lists of dicts at once (make sure to fix the _id)
|
||||||
|
history = {'donations': [], 'expenses': []}
|
||||||
|
|
||||||
|
async for donation in donations_db.find():
|
||||||
|
donation['_id'] = str(donation['_id'])
|
||||||
|
history['donations'].append(donation)
|
||||||
|
|
||||||
|
async for expense in expenses_db.find():
|
||||||
|
expense['_id'] = str(expense['_id'])
|
||||||
|
history['expenses'].append(expense)
|
||||||
|
|
||||||
|
# sort all by timestamp
|
||||||
|
history['donations'] = sorted(history['donations'], key=lambda x: x['timestamp'])
|
||||||
|
|
||||||
|
return history
|
||||||
|
|
||||||
|
manager = FinanceManager()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print(asyncio.run(manager.get_entire_financial_history()))
|
|
@ -19,7 +19,7 @@ from helpers import tokens, errors, network
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
users = UserManager()
|
users = UserManager()
|
||||||
models_list = json.load(open('models.json', encoding='utf8'))
|
models_list = json.load(open('cache/models.json', encoding='utf8'))
|
||||||
models = [model['id'] for model in models_list['data']]
|
models = [model['id'] for model in models_list['data']]
|
||||||
|
|
||||||
with open('config/config.yml', encoding='utf8') as f:
|
with open('config/config.yml', encoding='utf8') as f:
|
||||||
|
|
Loading…
Reference in a new issue