from functools import cache import json from flask import ( Flask, make_response, redirect, request, jsonify, render_template, send_from_directory, send_file, ) import os import json import requests from datetime import datetime import dotenv import re dotenv.load_dotenv() app = Flask(__name__) errors = { "no_domain": "No domain provided", "invalid_domain": "Invalid domain provided (make sure the domain is just the TLD)", "api_error": "API error", "not_anyone": "The domain is not owned by an anyone can renew script", } allowed_owners = [ "hs1qu3nrzrjkd783ftpk7l4hvpa96aazx5dddw66hgs2zuukckcchrqsw3f8kc" ] def find(name, path): for root, dirs, files in os.walk(path): if name in files: return os.path.join(root, name) # Assets routes @app.route("/assets/") def send_assets(path): if path.endswith(".json"): return send_from_directory( "templates/assets", path, mimetype="application/json" ) if os.path.isfile("templates/assets/" + path): return send_from_directory("templates/assets", path) # Try looking in one of the directories filename: str = path.split("/")[-1] if ( filename.endswith(".png") or filename.endswith(".jpg") or filename.endswith(".jpeg") or filename.endswith(".svg") ): if os.path.isfile("templates/assets/img/" + filename): return send_from_directory("templates/assets/img", filename) if os.path.isfile("templates/assets/img/favicon/" + filename): return send_from_directory("templates/assets/img/favicon", filename) return render_template("404.html"), 404 # region Special routes @app.route("/favicon.png") def faviconPNG(): return send_from_directory("templates/assets/img", "favicon.png") @app.route("/.well-known/") def wellknown(path): # Try to proxy to https://nathan.woodburn.au/.well-known/ req = requests.get(f"https://nathan.woodburn.au/.well-known/{path}") return make_response( req.content, 200, {"Content-Type": req.headers["Content-Type"]} ) # endregion # region Main routes @app.route("/") def index(): if request.args.get("error"): return render_template("index.html", error=errors[request.args.get("error")]) if request.args.get("message"): return render_template("index.html", message=request.args.get("message")) return render_template("index.html") @app.route("/renew", methods=["POST"]) def renew(): domain = request.form.get("domain") if not domain: return redirect("/?error=no_domain") # Double check the domain is valid domain = domain.lower() domain = domain.removeprefix(".") domain = domain.removesuffix("/") if domain.count(".") > 0 or domain.count("/") > 0: return redirect("/?error=invalid_domain") # Regex to check if the TLD is valid (only letters and numbers (or xn--...) if not re.match(r"^[a-zA-Z0-9-]+$", domain): return redirect("/?error=invalid_domain") # Check the owner is correct req = requests.get(f"https://api.niami.io/hsd/{domain}") if req.status_code != 200: return redirect("/?error=api_error") req = req.json() if req["success"] != True: return redirect("/?error=api_error") if req["data"]["owner_tx_data"]["address"] not in allowed_owners: return redirect("/?error=not_anyone") return redirect(f"https://pay.hns.au/p/renew?amount=10&data={domain}&redirect=https://renew.hns.au/?message=Pending:%20The%20domain%20will%20renew%20when%20the%20payment%20has%20arrived") @app.route("/renew/", methods=["POST"]) def renew_path(path: str): # Read path from env renew_path = os.getenv("RENEW_PATH") # Verify path if renew_path != path: return jsonify({"error": "Invalid path"}), 400 # get post data data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 # Get amount amount = data["amount"] if not amount: return jsonify({"error": "No amount provided"}), 400 if amount < 10: return jsonify({"error": "Amount too low"}), 400 # GET HS-anyone api route api = os.getenv("API") req = requests.post(api, json={"domain": data["data"]}) if req.status_code != 200: return jsonify({"error": "API error"}), 400 req = req.json() output = {"success": True,"message": f'Renewing {data["data"]}',"output":req} # Send discord webhook webhook = os.getenv("DISCORD") if webhook: # Parse output message = "Renewing " + data["data"] if req["error"] != "": message += "\n\nError: " + req["error"] message += "\n\nTX: https://hns.cymon.de/tx/" + req["output"].split("'")[1] requests.post(webhook, json={"content": json.dumps(message)}) return jsonify(output) @app.route("/") def catch_all(path: str): if os.path.isfile("templates/" + path): return render_template(path) # Try with .html if os.path.isfile("templates/" + path + ".html"): return render_template(path + ".html") if os.path.isfile("templates/" + path.strip("/") + ".html"): return render_template(path.strip("/") + ".html") # Try to find a file matching if path.count("/") < 1: # Try to find a file matching filename = find(path, "templates") if filename: return send_file(filename) return render_template("404.html"), 404 # endregion # region Error Catching # 404 catch all @app.errorhandler(404) def not_found(e): return render_template("404.html"), 404 # endregion if __name__ == "__main__": app.run(debug=True, port=5000, host="0.0.0.0")