You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
17 KiB
417 lines
17 KiB
# /path/to/server.py
|
|
from jinja2 import Environment, FileSystemLoader, Template
|
|
from datetime import timedelta, datetime
|
|
from urllib.parse import unquote
|
|
|
|
from sanic.response import redirect
|
|
from sanic.response import json as sjson, text
|
|
from sanic.response import html
|
|
from sanic import Sanic
|
|
from sanic import response
|
|
|
|
from dateutil import parser
|
|
from random import randrange, randint, shuffle
|
|
import json
|
|
import os.path
|
|
import qrcode
|
|
import httpx
|
|
import os
|
|
import gdshortener
|
|
import re
|
|
import time
|
|
|
|
from automatedChecks import get_exchange_score
|
|
from utils.ratesApi import getAllRates
|
|
|
|
# Generate QR Codes for donation addresses
|
|
from dotenv import load_dotenv
|
|
import qrcode
|
|
load_dotenv()
|
|
xmrDonations = os.getenv("XMR_DONATION_ADDRESS")
|
|
btcDonations = os.getenv("BTC_DONATION_ADDRESS")
|
|
|
|
btcqr = qrcode.make(btcDonations)
|
|
btcqr.save("static/img/qrcodes/btc.png")
|
|
xmrqr = qrcode.make(xmrDonations)
|
|
xmrqr.save("static/img/qrcodes/xmr.png")
|
|
FIRST_RUN=datetime.utcnow()
|
|
|
|
base_dir = os.path.abspath(os.path.dirname(__name__))
|
|
static_dir = os.path.join(base_dir, 'static')
|
|
templates_dir = os.path.join(base_dir, 'templates')
|
|
data_dir = os.path.join(base_dir, 'data')
|
|
env = Environment(loader=FileSystemLoader(templates_dir), autoescape=True)
|
|
|
|
app = Sanic(__name__)
|
|
app.static('/static', static_dir)
|
|
|
|
def keep_dupes(iterable, times):
|
|
seen = []
|
|
dupes = []
|
|
result = []
|
|
for item in iterable:
|
|
if item in seen and item not in dupes:
|
|
if 'times' not in item:
|
|
item['times'] = 1
|
|
item['times'] += 1
|
|
if item['times'] == times:
|
|
result.append(item)
|
|
dupes.append(item)
|
|
else:
|
|
seen.append(item)
|
|
return result
|
|
|
|
# filename = ""
|
|
@app.route("/", name="root")
|
|
@app.route("/index", name="index")
|
|
async def index(request):
|
|
oneweekago = datetime.today() - timedelta(days=7)
|
|
with open(f'{data_dir}/changes.html', 'r') as f:
|
|
cf = f.read()
|
|
d = re.search('<h1>(.+?)</h1>', cf)
|
|
last_update = d.group(1)
|
|
if(request.args):
|
|
args = list(request.args.keys())
|
|
template = env.get_template('index.html')
|
|
f = open(f'{data_dir}/exchanges.json')
|
|
data = json.load(f)
|
|
exchanges = []
|
|
for exchange in data['exchanges']:
|
|
exchange['listing-date'] = parser.parse(exchange['listing-date'])
|
|
if isinstance(exchange['url'], list):
|
|
exchange['url'] = exchange['url'][randint(0, len(exchange['url'])-1)]
|
|
for arg in args:
|
|
if exchange[arg]:
|
|
exchanges.append(exchange)
|
|
if len(args) > 1:
|
|
exchanges = keep_dupes(exchanges, len(args))
|
|
return html(template.render(date=oneweekago, data=exchanges,
|
|
title="exchange",
|
|
active=0,
|
|
last_update=last_update,
|
|
filters=args,
|
|
subtitle="Find best <strong>NON-KYC</strong> online services."))
|
|
else:
|
|
template = env.get_template('index.html')
|
|
f = open(f'{data_dir}/exchanges.json')
|
|
data = json.load(f)
|
|
data['exchanges'] = sorted(data['exchanges'], key=lambda k: k['score'], reverse=True)
|
|
|
|
# Randomly sort exchanges within they rating range
|
|
exchangesSorted = []
|
|
for s in range(11):
|
|
tempRange = []
|
|
for e in data['exchanges']:
|
|
if e['score'] == s:
|
|
tempRange.append(e)
|
|
if len(tempRange) > 0:
|
|
shuffle(tempRange)
|
|
exchangesSorted = tempRange + exchangesSorted
|
|
|
|
data['exchanges'] = exchangesSorted
|
|
|
|
for e in data['exchanges']:
|
|
e['listing-date'] = parser.parse(e['listing-date'])
|
|
if isinstance(e['url'], list):
|
|
e['url'] = e['url'][randint(0, len(e['url'])-1)]
|
|
return html(template.render(date=oneweekago, data=data['exchanges'],
|
|
title="exchange",
|
|
last_update=last_update,
|
|
active=0,
|
|
subtitle="Find best <strong>NON-KYC</strong> online services."))
|
|
|
|
@app.route("/services", name="services")
|
|
async def services(request):
|
|
oneweekago = datetime.today() - timedelta(days=7)
|
|
if(request.args):
|
|
args = list(request.args.keys())
|
|
if 'type' in request.args:
|
|
args.remove('type')
|
|
_type = request.args['type'][0]
|
|
else:
|
|
_type = False
|
|
|
|
template = env.get_template('index.html')
|
|
f = open(f'{data_dir}/services.json')
|
|
data = json.load(f)
|
|
|
|
services = {}
|
|
if len(args) > 0:
|
|
for key in data['services'].keys():
|
|
services = services | {key:[]}
|
|
for service in data['services'][key]:
|
|
# Treat the info
|
|
service['listing-date'] = parser.parse(service['listing-date'])
|
|
if isinstance(service['url'], list):
|
|
service['url'] = service['url'][randint(0, len(service['url'])-1)]
|
|
# For each filter parameter
|
|
for arg in args:
|
|
# If the service has it
|
|
if service[arg]:
|
|
# If there is no type filter, we just add it
|
|
if not _type:
|
|
services[key].append(service)
|
|
else:
|
|
# If there is a type filter we check if the service is from that type
|
|
if _type == key:
|
|
services[key].append(service)
|
|
|
|
else:
|
|
for key in data['services'].keys():
|
|
services = services | {key:[]}
|
|
for service in data['services'][key]:
|
|
service['listing-date'] = parser.parse(service['listing-date'])
|
|
if isinstance(service['url'], list):
|
|
service['url'] = service['url'][randint(0, len(service['url'])-1)]
|
|
if not _type:
|
|
services[key].append(service)
|
|
else:
|
|
# If there is a type filter we check if the service is from that type
|
|
if _type == key:
|
|
services[key].append(service)
|
|
|
|
if not _type: _type="Type"
|
|
for key in data['services'].keys():
|
|
shuffle(data['services'][key])
|
|
services = {k:v for k,v in services.items() if v}
|
|
return html(template.render(date=oneweekago, data=services,
|
|
title="service",
|
|
active=1,
|
|
filters=list(request.args.keys()),
|
|
_type=_type,
|
|
subtitle="Find best <strong>NON-KYC</strong> online services."))
|
|
|
|
template = env.get_template('index.html')
|
|
f = open(f'{data_dir}/services.json')
|
|
data = json.load(f)
|
|
for key in data['services'].keys():
|
|
shuffle(data['services'][key])
|
|
#data['services'] = sorted(data['services'], key=lambda k: k['name'], reverse=False)
|
|
for s in data['services'][key]:
|
|
s['listing-date'] = parser.parse(s['listing-date'])
|
|
|
|
# Remove empty values (i.e. EML: [])
|
|
data['services'] = {k:v for k,v in data['services'].items() if v}
|
|
return html(template.render(date=oneweekago, data=data['services'],
|
|
title="service",
|
|
active=1,
|
|
_type="Type",
|
|
subtitle="Find best <strong>NON-KYC</strong> online services."))
|
|
|
|
@app.route("/about", name="about")
|
|
async def about(request):
|
|
oneweekago = datetime.today() - timedelta(days=7)
|
|
template = env.get_template('about.html')
|
|
donations = {
|
|
"xmr": xmrDonations,
|
|
"nano": "nano_3txph9t1dwr778143me6ifaeo4ddyjcwfs4wymca5dtfdwdqjmff8nfhixgj",
|
|
"ltc": "MMSW3AnzHbxnmVeXzGjnNgHf6h62qpR9VA",
|
|
"btc": btcDonations
|
|
}
|
|
return html(template.render(date=oneweekago, title="KYC? Not me!",
|
|
subtitle="About KYCNOT.ME",
|
|
active=2,
|
|
support=donations))
|
|
|
|
|
|
@app.route("/exchange/<name>")
|
|
async def exchange(request, name=None):
|
|
oneweekago = datetime.today() - timedelta(days=7)
|
|
if(name):
|
|
f = open(f'{data_dir}/exchanges.json')
|
|
data = json.load(f)
|
|
for exchange in data['exchanges']:
|
|
if ''.join(exchange['name'].split()).lower() == name:
|
|
template = env.get_template('exchange.html')
|
|
if isinstance(exchange['url'], list):
|
|
exchange['url'] = exchange['url'][randint(0, len(exchange['url'])-1)]
|
|
|
|
return html(template.render(date=oneweekago, status=200, item=exchange, title="KYC? Not me!", active=0))
|
|
return(f"{name} does not exist")
|
|
|
|
|
|
@app.route("/service/<name>")
|
|
async def service(request, name=None):
|
|
oneweekago = datetime.today() - timedelta(days=7)
|
|
if(name):
|
|
template = env.get_template('services.html')
|
|
f = open(f'{data_dir}/services.json')
|
|
data = json.load(f)
|
|
for key in data['services'].keys():
|
|
for service in data['services'][key]:
|
|
if service['name'].replace(' ', '').lower() == name:
|
|
tpinfo = {"score": False}
|
|
template = env.get_template('service.html')
|
|
return html(template.render(date=oneweekago, item=service, tpinfo=tpinfo, active=1))
|
|
return(f"{name} does not exist")
|
|
|
|
@app.route("/scores", name="scores")
|
|
async def key(request, name=None):
|
|
with open("data/score_summary.txt") as f:
|
|
data = f.read()
|
|
return text(data)
|
|
|
|
|
|
@app.route("/requests", name="requests_status")
|
|
async def key(request, name=None):
|
|
with open("data/status.txt") as f:
|
|
data = f.read()
|
|
return text(data)
|
|
|
|
|
|
@app.route("/key", name="key")
|
|
@app.route("/pgp", name="pgp")
|
|
async def key(request, name=None):
|
|
key = """
|
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
|
|
|
mDMEYwdv5BYJKwYBBAHaRw8BAQdAAw6xG7G1lMkQi+ph/eCGrSPmretZ1eJ3X6It
|
|
C+o4jH20F1BsdWphIDxwbHVqYUBreWNub3QubWU+iJAEExYIADgWIQQFUj1t5ZFX
|
|
WwpaRM4VlC7bg/WdVwUCYwdv5AIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAK
|
|
CRAVlC7bg/WdV/oxAQCuLgpICVFf+Qf4ywF3ETYls+ah1ILY4rzax66hGo9nHQD7
|
|
BPLc6ahF9acCdT1ZpH+Xv6IvKg7Aj+tK7C5p0I9RFQ24OARjB2/kEgorBgEEAZdV
|
|
AQUBAQdAZTc270z91hRjhFNMe8LTmytIRYul93gn2xG8cMco9jsDAQgHiHgEGBYI
|
|
ACAWIQQFUj1t5ZFXWwpaRM4VlC7bg/WdVwUCYwdv5AIbDAAKCRAVlC7bg/WdVw96
|
|
AP9IBOUCrd3oeqf4lJERrLikGSt/DsqMb54akDMHcU9AfgEA57F9XgVXt1w3RgfR
|
|
IkqtmcqulMiQtws0Q3jBTJsKewk=
|
|
=J+yt
|
|
-----END PGP PUBLIC KEY BLOCK-----
|
|
|
|
This is my PGP key.
|
|
|
|
It is also uploaded to Codeberg here, please compare it: https://codeberg.org/pluja/kycnot.me/src/branch/ui-redesign/data
|
|
|
|
If you don't know how to use PGP, you can easily verify any signed messages here: https://pgptool.org/
|
|
"""
|
|
return text(key)
|
|
|
|
@app.route("/changelog")
|
|
async def changelog(request, name=None):
|
|
with open(f'{data_dir}/changes.html', "r") as f:
|
|
changes = f.read()
|
|
return html(changes)
|
|
|
|
def set_default(obj):
|
|
if isinstance(obj, set):
|
|
return list(obj)
|
|
raise TypeError
|
|
|
|
|
|
@app.route("/rates", name="rates", methods=['POST', 'GET'])
|
|
async def rates(request):
|
|
if(request.args):
|
|
args = request.args
|
|
rates = await getAllRates(args['from_ticker'][0], args['to_ticker'][0], args['from_amount'][0])
|
|
#rates = compareRates(args['from_ticker'][0], args['to_ticker'][0], float(args['from_amount'][0]))
|
|
|
|
#f = open(f'{data_dir}/exchanges.json')
|
|
#data = json.load(f)
|
|
#exchanges = data['exchanges']
|
|
exchanges = {}
|
|
|
|
template = env.get_template('rates.html')
|
|
return(html(template.render(rates=rates,
|
|
exchanges=exchanges,
|
|
from_amount=args['from_amount'][0],
|
|
from_ticker=args['from_ticker'][0],
|
|
to_ticker=args['to_ticker'][0])))
|
|
else:
|
|
template = env.get_template('rates.html')
|
|
return html(template.render(rates=False, exchanges=False))
|
|
|
|
@app.route("/generator", name="generator", methods=['POST', 'GET'])
|
|
async def generator(request):
|
|
if(request.args):
|
|
args = request.args
|
|
if len(args) > 1:
|
|
if 'generate' in args:
|
|
if 'Exchange' in args['type']:
|
|
item_json = {
|
|
"name": args['name'][0],
|
|
"verified": False,
|
|
"btc": args['btc'][0],
|
|
"xmr": args['xmr'][0],
|
|
"lnn": args['lnn'][0],
|
|
"cash": args['cash'][0],
|
|
"p2p": args['p2p'][0],
|
|
"tor-only": args['tor-only'][0],
|
|
"tor": args['tor'][0],
|
|
"refunds": args['refunds'][0],
|
|
"open-source": args['open-source'][0],
|
|
"custodial": args['custodial'][0],
|
|
"javascript": args['javascript'][0],
|
|
"no-registration": args['no-registration'][0],
|
|
"personal-info": args['personal-info'][0],
|
|
"buy": args['buy'][0],
|
|
"app": args['app'][0],
|
|
"exchange": args['exchange'][0],
|
|
"meta-description": False,
|
|
"short-description": args['short-d'][0],
|
|
"long-description": args['large-d'][0],
|
|
"comments": False,
|
|
"kyc-check": False,
|
|
"kyc-type": int(args['kyc-type'][0]),
|
|
"score": False,
|
|
"suspicious-tos": False,
|
|
"tor-onion": args['tor-url'][0],
|
|
"url": args['url'][0],
|
|
"referral": False,
|
|
"tos-urls": [
|
|
args['tos-url'][0]
|
|
],
|
|
"third-party-connections": False,
|
|
"score-boost": 0,
|
|
"listing-date": str(datetime.today().strftime("%Y-%m-%d")),
|
|
"api": args['api'][0],
|
|
"api-url": args['api-url'][0],
|
|
"status": 200
|
|
}
|
|
item_json['score'] = get_exchange_score(item_json)
|
|
|
|
else:
|
|
item_json = {
|
|
"name": args['name'][0],
|
|
"url": args['url'][0],
|
|
"referral_url": False,
|
|
"tos-url": args['tos-url'][0],
|
|
"short-description": args['short-d'][0],
|
|
"long-description": args['large-d'][0],
|
|
"meta-description": "",
|
|
"btc": args['btc'][0],
|
|
"xmr": args['xmr'][0],
|
|
"lnn": args['lnn'][0],
|
|
"cash": args['cash'][0],
|
|
"app": args['app'][0],
|
|
"comments": False,
|
|
"verified": False,
|
|
"no-registration": args['no-registration'][0],
|
|
"personal-info": args['personal-info'][0],
|
|
"tor": args['tor'][0],
|
|
"tor-only": args['tor-only'][0],
|
|
"tor-onion": args['tor-url'][0],
|
|
"open-source": args['open-source'][0],
|
|
"tags": args['tags'],
|
|
"javascript": args['javascript'][0],
|
|
"listing-date": str(datetime.today().strftime("%Y-%m-%d")),
|
|
"api": False
|
|
}
|
|
print(str(item_json).replace("False", 'false').replace("True", "true").replace("'", '"').replace('"false"', 'false').replace('"true"', "true"))
|
|
jsonString = str(item_json).replace("False", 'false').replace("True", "true").replace("'", '"').replace('"false"', 'false').replace('"true"', "true")
|
|
return sjson(json.loads(jsonString))
|
|
else:
|
|
s = gdshortener.ISGDShortener()
|
|
name = args['name'][0].replace(" ", "_")
|
|
rurl = request.url.split('/')[3]
|
|
baseurl = "https://kycnot.me/"
|
|
if "Exchange" in args['type']:
|
|
shurl = s.shorten(f"{baseurl}{str(rurl)}&type=Exchange&generate=True", custom_url = f"{randint(0,999999999999)}_kycnot")[0]
|
|
else:
|
|
shurl = s.shorten(f"{baseurl}{str(rurl)}&type=Service&generate=True", custom_url = f"{randint(0,999999999999)}_kycnot")[0]
|
|
return(html(f"Copy the following URL to fill the request: <b><pre>{shurl}</pre></b>"))
|
|
else:
|
|
_type = args['type'][0]
|
|
else:
|
|
_type='Exchange'
|
|
template = env.get_template('generator.html')
|
|
return(html(template.render(_type=_type)))
|