|
|
|
@ -3,25 +3,34 @@
|
|
|
|
|
# Copyright (c) 2020, dsc@xmr.pm
|
|
|
|
|
|
|
|
|
|
import json
|
|
|
|
|
from typing import Union
|
|
|
|
|
|
|
|
|
|
import aiohttp
|
|
|
|
|
from bs4 import BeautifulSoup
|
|
|
|
|
from aiohttp_socks import ProxyType, ProxyConnector, ChainProxyConnector
|
|
|
|
|
from fapi.utils import broadcast_blockheight, broadcast_nodes, httpget, BlockHeight
|
|
|
|
|
from fapi.utils import broadcast_nodes, httpget, BlockHeight
|
|
|
|
|
|
|
|
|
|
import settings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FeatherApi:
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def redis_get(key):
|
|
|
|
|
async def redis_get(key: str) -> dict:
|
|
|
|
|
from fapi.factory import app, cache
|
|
|
|
|
try:
|
|
|
|
|
data = await cache.get(key)
|
|
|
|
|
data = await cache.get(f"{settings.crypto_symbol}_{key}")
|
|
|
|
|
if data:
|
|
|
|
|
return json.loads(data)
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"Redis error: {ex}")
|
|
|
|
|
app.logger.error(f"Redis SET error: {ex}")
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def redis_set(key: str, val: Union[list, dict]) -> None:
|
|
|
|
|
from fapi.factory import app, cache
|
|
|
|
|
try:
|
|
|
|
|
await cache.set(f"{settings.crypto_symbol}_{key}", json.dumps(val))
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"Redis GET error: {ex}")
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def xmrto_rates():
|
|
|
|
@ -75,7 +84,6 @@ class FeatherApi:
|
|
|
|
|
return crypto_rates
|
|
|
|
|
|
|
|
|
|
# grab WOW price while we're at it...
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
_result = await httpget(settings.urls["crypto_wow_rates"])
|
|
|
|
|
if not _result:
|
|
|
|
@ -92,7 +100,7 @@ class FeatherApi:
|
|
|
|
|
"price_change_percentage_24h": 0.0
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await cache.set("crypto_rates", json.dumps(crypto_rates))
|
|
|
|
|
await FeatherApi.redis_set("crypto_rates", crypto_rates)
|
|
|
|
|
return crypto_rates
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@ -137,12 +145,11 @@ class FeatherApi:
|
|
|
|
|
result = await httpget(settings.urls["fiat_rates"], json=True)
|
|
|
|
|
if not result:
|
|
|
|
|
raise Exception("empty response")
|
|
|
|
|
await cache.set("fiat_rates", json.dumps(result))
|
|
|
|
|
await FeatherApi.redis_set("fiat_rates", result)
|
|
|
|
|
return result
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"error parsing fiat_rates blob: {ex}")
|
|
|
|
|
|
|
|
|
|
# old cache
|
|
|
|
|
app.logger.warning("USING OLD CACHE FOR FIAT RATES")
|
|
|
|
|
return fiat_rates
|
|
|
|
|
|
|
|
|
@ -166,6 +173,62 @@ class FeatherApi:
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def wfs():
|
|
|
|
|
# wownero-funding-system
|
|
|
|
|
from fapi.factory import app, cache
|
|
|
|
|
wfs = await FeatherApi.redis_get("wfs")
|
|
|
|
|
if wfs and app.config["DEBUG"]:
|
|
|
|
|
return wfs
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
blob = await httpget(f"{settings.urls['wfs']}?offset=0&limit=1&status=2")
|
|
|
|
|
if "data" not in blob:
|
|
|
|
|
raise Exception("invalid json response")
|
|
|
|
|
await FeatherApi.redis_set("wfs", blob)
|
|
|
|
|
return blob
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"error fetching wfs JSON: {ex}")
|
|
|
|
|
|
|
|
|
|
# old cache
|
|
|
|
|
app.logger.warning("USING OLD CACHE FOR WFS")
|
|
|
|
|
return wfs
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def after_wfs(data):
|
|
|
|
|
from fapi.factory import app, cache, api_data, connected_websockets
|
|
|
|
|
if not data:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
proposals = []
|
|
|
|
|
for p in data['data']:
|
|
|
|
|
item = {
|
|
|
|
|
"address": p["addr_donation"],
|
|
|
|
|
"url": f"https://funding.wownero.com/proposal/{p['id']}",
|
|
|
|
|
"state": "FUNDING-REQUIRED",
|
|
|
|
|
"date": p['date_posted'],
|
|
|
|
|
"title": p['headline'],
|
|
|
|
|
'target_amount': p['funds_target'],
|
|
|
|
|
'raised_amount': round(p['funds_target'] / 100 * p['funded_pct'], 2),
|
|
|
|
|
'contributors': 0,
|
|
|
|
|
'percentage_funded': round(p['funded_pct'], 2),
|
|
|
|
|
'author': p['user']
|
|
|
|
|
}
|
|
|
|
|
proposals.append(item)
|
|
|
|
|
data = proposals
|
|
|
|
|
|
|
|
|
|
_data = api_data.get("wfs", {})
|
|
|
|
|
_data = json.dumps(_data, sort_keys=True, indent=4)
|
|
|
|
|
if json.dumps(data, sort_keys=True, indent=4) == _data:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
api_data["wfs"] = data
|
|
|
|
|
for queue in connected_websockets:
|
|
|
|
|
await queue.put({
|
|
|
|
|
"cmd": "wfs",
|
|
|
|
|
"data": api_data["wfs"]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def ccs():
|
|
|
|
|
# CCS JSON api is broken ;x https://hackerone.com/reports/934231
|
|
|
|
@ -235,7 +298,7 @@ class FeatherApi:
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"error parsing a ccs item: {ex}")
|
|
|
|
|
|
|
|
|
|
await cache.set("ccs", json.dumps(data))
|
|
|
|
|
await FeatherApi.redis_set("ccs", data)
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@ -264,7 +327,7 @@ class FeatherApi:
|
|
|
|
|
return reddit
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
blob = await httpget(settings.urls["reddit"])
|
|
|
|
|
blob = await httpget(settings.urls[f"reddit_{settings.crypto_symbol}"])
|
|
|
|
|
if not blob:
|
|
|
|
|
raise Exception("no data from url")
|
|
|
|
|
blob = [{
|
|
|
|
@ -276,7 +339,7 @@ class FeatherApi:
|
|
|
|
|
|
|
|
|
|
# success
|
|
|
|
|
if blob:
|
|
|
|
|
await cache.set("reddit", json.dumps(blob))
|
|
|
|
|
await FeatherApi.redis_set("reddit", blob)
|
|
|
|
|
return blob
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"error parsing reddit blob: {ex}")
|
|
|
|
@ -302,6 +365,16 @@ class FeatherApi:
|
|
|
|
|
"data": api_data["reddit"]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def wowheight():
|
|
|
|
|
from fapi.factory import app, cache
|
|
|
|
|
data = {"mainnet": 0, "stagenet": 0}
|
|
|
|
|
try:
|
|
|
|
|
data['mainnet'] = await BlockHeight.wowheight()
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
app.logger.error(f"Could not fetch blockheight for wownero: {ex}")
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def blockheight():
|
|
|
|
|
from fapi.factory import app, cache
|
|
|
|
@ -334,7 +407,14 @@ class FeatherApi:
|
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
if changed:
|
|
|
|
|
await broadcast_blockheight()
|
|
|
|
|
from fapi.factory import connected_websockets, api_data
|
|
|
|
|
for queue in connected_websockets:
|
|
|
|
|
await queue.put({
|
|
|
|
|
"cmd": "blockheights",
|
|
|
|
|
"data": {
|
|
|
|
|
"height": api_data.get("blockheights", {})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
async def check_nodes():
|
|
|
|
|