diff --git a/FireWalletBrowser.bsdesign b/FireWalletBrowser.bsdesign
index e0e019f..fca0fa5 100644
Binary files a/FireWalletBrowser.bsdesign and b/FireWalletBrowser.bsdesign differ
diff --git a/main.py b/main.py
index 931ee51..4d6c8f0 100644
--- a/main.py
+++ b/main.py
@@ -11,6 +11,7 @@ from flask_qrcode import QRcode
import domainLookup
import urllib.parse
import importlib
+import plugin as plugins_module
dotenv.load_dotenv()
@@ -91,10 +92,17 @@ def index():
domain_count = len(domains)
domains = render.domains(domains)
+ plugins = ""
+ dashFunctions = plugins_module.getDashboardFunctions()
+ for function in dashFunctions:
+ functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{},account_module.check_account(request.cookies.get("account")))
+ plugins += render.plugin_output_dash(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
+
+
return render_template("index.html", account=account, available=available,
- total=total, pending=pending, domains=domains,
+ total=total, pending=pending, domains=domains, plugins=plugins,
domain_count=domain_count, sync=account_module.getNodeSync(),
sort_price=sort_price,sort_expiry=sort_expiry,
sort_domain=sort_domain,sort_price_next=sort_price_next,
@@ -320,10 +328,20 @@ def search():
dns = render.dns(dns)
txs = render.txs(txs)
+
+ plugins = "
"
+ # Execute domain plugins
+ searchFunctions = plugins_module.getSearchFunctions()
+ for function in searchFunctions:
+ functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{"domain":search_term},account_module.check_account(request.cookies.get("account")))
+ plugins += render.plugin_output(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
+
+ plugins += "
"
+
return render_template("search.html", account=account, sync=account_module.getNodeSync(),
search_term=search_term,domain=domain['info']['name'],
raw=domain,state=state, next=next, owner=owner,
- dns=dns, txs=txs)
+ dns=dns, txs=txs,plugins=plugins)
@app.route('/manage/')
def manage(domain: str):
@@ -373,20 +391,11 @@ def manage(domain: str):
plugins = ""
# Execute domain plugins
- plugin_links = os.listdir("plugins")
- for plugin in plugin_links:
- if os.path.isdir("plugins/" + plugin):
- module = importlib.import_module("plugins." + plugin + ".main")
- moduleFunctions = module.listFunctions()
- for moduleFunction in moduleFunctions:
- data = moduleFunctions[moduleFunction]
- if "type" in data:
- if data["type"] == "domain":
- # Run function
- print(data)
- functionOutput = module.runFunction(moduleFunction,{"domain":domain},account_module.check_account(request.cookies.get("account")))
- print(functionOutput)
- plugins += render.plugin_output(functionOutput,data['returns'])
+ domainFunctions = plugins_module.getDomainFunctions()
+ for function in domainFunctions:
+ functionOutput = plugins_module.runPluginFunction(function["plugin"],function["function"],{"domain":domain},account_module.check_account(request.cookies.get("account")))
+ plugins += render.plugin_output(functionOutput,plugins_module.getPluginFunctionReturns(function["plugin"],function["function"]))
+
plugins += "
"
@@ -1053,21 +1062,7 @@ def plugins_index():
if not account:
return redirect("/logout")
- plugin_links = os.listdir("plugins")
- plugins = []
- for plugin in plugin_links:
- if os.path.isdir("plugins/" + plugin):
- if os.path.isfile("plugins/" + plugin + "/"+plugin+".json"):
- with open("plugins/" + plugin + "/"+plugin+".json") as f:
- data = json.load(f)
- data['link'] = plugin
- if 'name' not in data:
- data['name'] = plugin
- if 'description' not in data:
- data['description'] = "No description provided"
- plugins.append(data)
-
- plugins = render.plugins(plugins)
+ plugins = render.plugins(plugins_module.listPlugins())
return render_template("plugins.html", account=account, sync=account_module.getNodeSync(),
plugins=plugins)
@@ -1082,37 +1077,22 @@ def plugin(plugin):
if not account:
return redirect("/logout")
- if not os.path.isdir("plugins/" + plugin):
+ if not plugins_module.pluginExists(plugin):
return redirect("/plugins")
- if not os.path.isfile("plugins/" + plugin + "/"+plugin+".json"):
- return redirect("/plugins")
+ data = plugins_module.getPluginData(plugin)
- with open("plugins/" + plugin + "/"+plugin+".json") as f:
- data = json.load(f)
- data['link'] = plugin
- if 'name' not in data:
- data['name'] = plugin
- if 'description' not in data:
- data['description'] = "No description provided"
-
-
- functions = []
-
- if os.path.isfile("plugins/" + plugin + "/main.py"):
- # Get plugin/main.py listfunctions()
- print("Loading plugin: " + plugin)
- module = importlib.import_module("plugins." + plugin + ".main")
- functions = module.listFunctions()
- functions = render.plugin_functions(functions,plugin)
+ functions = plugins_module.getPluginFunctions(plugin)
+ functions = render.plugin_functions(functions,plugin)
error = request.args.get("error")
if error == None:
error = ""
return render_template("plugin.html", account=account, sync=account_module.getNodeSync(),
- name=data['name'],description=data['description'],functions=functions,
- error=error)
+ name=data['name'],description=data['description'],
+ author=data['author'],version=data['version'],
+ functions=functions,error=error)
@app.route('/plugin//', methods=["POST"])
def plugin_function(plugin,function):
@@ -1124,59 +1104,45 @@ def plugin_function(plugin,function):
if not account:
return redirect("/logout")
- if not os.path.isdir("plugins/" + plugin):
+ if not plugins_module.pluginExists(plugin):
return redirect("/plugins")
- if not os.path.isfile("plugins/" + plugin + "/"+plugin+".json"):
- return redirect("/plugins")
+ data = plugins_module.getPluginData(plugin)
- with open("plugins/" + plugin + "/"+plugin+".json") as f:
- data = json.load(f)
- data['link'] = plugin
- if 'name' not in data:
- data['name'] = plugin
- if 'description' not in data:
- data['description'] = "No description provided"
-
- if os.path.isfile("plugins/" + plugin + "/main.py"):
- # Get plugin/main.py listfunctions()
- print("Loading plugin: " + plugin)
- module = importlib.import_module("plugins." + plugin + ".main")
- if function in module.listFunctions():
- inputs = module.listFunctions()[function]["params"]
- request_data = {}
- for input in inputs:
- request_data[input] = request.form.get(input)
-
- if inputs[input]['type'] == "address":
- # Handle hip2
- address_check = account_module.check_address(request_data[input],True,True)
- if not address_check:
- return redirect("/plugin/" + plugin + "?error=Invalid address")
- request_data[input] = address_check
- elif inputs[input]['type'] == "dns":
- # Handle URL encoding of DNS
- request_data[input] = urllib.parse.unquote(request_data[input])
-
-
-
-
- response = module.runFunction(function,request_data,request.cookies.get("account"))
- if not response:
- return redirect("/plugin/" + plugin + "?error=An error occurred")
- if 'error' in response:
- return redirect("/plugin/" + plugin + "?error=" + response['error'])
+ # Get plugin/main.py listfunctions()
+ if function in plugins_module.getPluginFunctions(plugin):
+ inputs = plugins_module.getPluginFunctionInputs(plugin,function)
+ request_data = {}
+ for input in inputs:
+ request_data[input] = request.form.get(input)
- response = render.plugin_output(response,module.listFunctions()[function]["returns"])
-
- return render_template("plugin-output.html", account=account, sync=account_module.getNodeSync(),
- name=data['name'],description=data['description'],output=response)
+ if inputs[input]['type'] == "address":
+ # Handle hip2
+ address_check = account_module.check_address(request_data[input],True,True)
+ if not address_check:
+ return redirect("/plugin/" + plugin + "?error=Invalid address")
+ request_data[input] = address_check
+ elif inputs[input]['type'] == "dns":
+ # Handle URL encoding of DNS
+ request_data[input] = urllib.parse.unquote(request_data[input])
- else:
- return jsonify({"error": "Function not found"})
- return jsonify({"error": "Plugin not found"})
+
+ response = plugins_module.runPluginFunction(plugin,function,request_data,request.cookies.get("account"))
+ if not response:
+ return redirect("/plugin/" + plugin + "?error=An error occurred")
+ if 'error' in response:
+ return redirect("/plugin/" + plugin + "?error=" + response['error'])
+
+ response = render.plugin_output(response,plugins_module.getPluginFunctionReturns(plugin,function))
+
+ return render_template("plugin-output.html", account=account, sync=account_module.getNodeSync(),
+ name=data['name'],description=data['description'],output=response)
+
+
+ else:
+ return jsonify({"error": "Function not found"})
#endregion
diff --git a/plugin.py b/plugin.py
new file mode 100644
index 0000000..10d784d
--- /dev/null
+++ b/plugin.py
@@ -0,0 +1,101 @@
+import os
+import json
+import importlib
+
+
+
+def listPlugins():
+ plugins = []
+ for file in os.listdir("plugins"):
+ if file.endswith(".py"):
+ if file != "main.py":
+ plugin = importlib.import_module("plugins."+file[:-3])
+ details = plugin.info
+ details["link"] = file[:-3]
+ plugins.append(details)
+ return plugins
+
+
+def pluginExists(plugin: str):
+ for file in os.listdir("plugins"):
+ if file == plugin+".py":
+ return True
+ return False
+
+def getPluginData(plugin: str):
+ plugin = importlib.import_module("plugins."+plugin)
+ return plugin.info
+
+def getPluginFunctions(plugin: str):
+ plugin = importlib.import_module("plugins."+plugin)
+ return plugin.functions
+
+def runPluginFunction(plugin: str, function: str, params: dict, authentication: str):
+ plugin_module = importlib.import_module("plugins."+plugin)
+ if function not in plugin_module.functions:
+ return {"error": "Function not found"}
+
+ if not hasattr(plugin_module, function):
+ return {"error": "Function not found"}
+
+ # Get the function object from the plugin module
+ plugin_function = getattr(plugin_module, function)
+
+ # Call the function with provided parameters
+ try:
+ result = plugin_function(params, authentication)
+ return result
+ except Exception as e:
+ print(f"Error running plugin: {e}")
+ return {"error": str(e)}
+ # return plugin.runFunction(function, params, authentication)
+
+def getPluginFunctionInputs(plugin: str, function: str):
+ plugin = importlib.import_module("plugins."+plugin)
+ return plugin.functions[function]["params"]
+
+def getPluginFunctionReturns(plugin: str, function: str):
+ plugin = importlib.import_module("plugins."+plugin)
+ return plugin.functions[function]["returns"]
+
+def getDomainFunctions():
+ plugins = listPlugins()
+ domainFunctions = []
+ for plugin in plugins:
+ functions = getPluginFunctions(plugin["link"])
+ for function in functions:
+ if functions[function]["type"] == "domain":
+ domainFunctions.append({
+ "plugin": plugin["link"],
+ "function": function,
+ "description": functions[function]["description"]
+ })
+ return domainFunctions
+
+def getSearchFunctions():
+ plugins = listPlugins()
+ searchFunctions = []
+ for plugin in plugins:
+ functions = getPluginFunctions(plugin["link"])
+ for function in functions:
+ if functions[function]["type"] == "search":
+ searchFunctions.append({
+ "plugin": plugin["link"],
+ "function": function,
+ "description": functions[function]["description"]
+ })
+ return searchFunctions
+
+def getDashboardFunctions():
+ plugins = listPlugins()
+ dashboardFunctions = []
+ for plugin in plugins:
+ functions = getPluginFunctions(plugin["link"])
+ for function in functions:
+ if functions[function]["type"] == "dashboard":
+ dashboardFunctions.append({
+ "plugin": plugin["link"],
+ "function": function,
+ "description": functions[function]["description"]
+ })
+ return dashboardFunctions
\ No newline at end of file
diff --git a/plugins.md b/plugins.md
index 6c05ffe..2142ee4 100644
--- a/plugins.md
+++ b/plugins.md
@@ -1,5 +1,24 @@
# Plugins
+## Types
+### Default
+Type: `default`
+This is the default type and is used when no type is specified.
+This type is displayed in the plugin page only.
+This is the onlu type of plugin that takes user input
+
+### Manage & Search
+For manage page use type: `domain`
+For search page use type: `search`
+
+This type is used for domain plugins. It shows in the manage domain page or the search page.
+It gets the `domain` paramater as the only input (in addition to authentication)
+
+### Dashboard
+This type is used for dashboard plugins.
+It shows in the dashboard page. It doesn't get any inputs other than the authentication
+
+
## Inputs
### Plain Text
diff --git a/plugins/domain/domain.json b/plugins/domain/domain.json
deleted file mode 100644
index f999f8b..0000000
--- a/plugins/domain/domain.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name":"Domains Plugin",
- "description":"This is plugin for the domain page"
-}
\ No newline at end of file
diff --git a/plugins/domain/main.py b/plugins/domain/main.py
deleted file mode 100644
index d6a296e..0000000
--- a/plugins/domain/main.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import json
-import account
-
-
-functions = {
- "bids": {
- "name": "Bid info",
- "type": "domain",
- "description": "Check when the domain was last updated",
- "params": {
- "domain": {
- "name":"domain to check",
- "type":"domain"
- }
- },
- "returns": {
- "highest":
- {
- "name": "Highest bid",
- "type": "text"
- },
- "paid":
- {
- "name": "Amount paid in auction",
- "type": "text"
- }
- }
- }
-}
-
-def listFunctions():
- return functions
-
-def runFunction(function, params, authentication):
- if function == "bids":
- return bids(params['domain'], authentication)
- else:
- return "Function not found"
-
-
-def bids(domain, authentication):
- wallet = authentication.split(":")[0]
- data = account.getDomain(domain)
- value = str(account.convertHNS(data['info']['value'])) + " HNS"
- highest = str(account.convertHNS(data['info']['highest'])) + " HNS"
-
-
- return {"highest": highest,"paid":value}
\ No newline at end of file
diff --git a/plugins/example/main.py b/plugins/example.py
similarity index 56%
rename from plugins/example/main.py
rename to plugins/example.py
index d9bda51..ccfdc1b 100644
--- a/plugins/example/main.py
+++ b/plugins/example.py
@@ -1,27 +1,22 @@
import json
import account
+import requests
+# Plugin Data
+info = {
+ "name": "Example Plugin",
+ "description": "This is a plugin to be used as an example",
+ "version": "1.0",
+ "author": "Nathan.Woodburn/"
+}
+
+
+# Functions
functions = {
- "check": {
- "name": "Domain Check",
- "description": "Check if domains in file are owned by the wallet",
- "params": {
- "domains": {
- "name":"List of domains to check",
- "type":"longText"
- }
- },
- "returns": {
- "domains":
- {
- "name": "List of owned domains",
- "type": "list"
- }
- }
- },
"search":{
"name": "Search Owned",
+ "type": "default",
"description": "Search for owned domains containing a string",
"params": {
"search": {
@@ -39,6 +34,7 @@ functions = {
},
"transfer":{
"name": "Bulk Transfer Domains",
+ "type": "default",
"description": "Transfer domains to another wallet",
"params": {
"address": {
@@ -63,6 +59,7 @@ functions = {
},
"dns":{
"name": "Set DNS for Domains",
+ "type": "default",
"description": "Set DNS for domains",
"params": {
"domains": {
@@ -84,26 +81,50 @@ functions = {
"type": "dns"
}
}
+ },
+ "niami": {
+ "name": "Niami info",
+ "type": "domain",
+ "description": "Check the domains niami rating",
+ "params": {},
+ "returns": {
+ "rating":
+ {
+ "name": "Niami Rating",
+ "type": "text"
+ }
+ }
+ },
+ "niamiSearch": {
+ "name": "Niami info",
+ "type": "search",
+ "description": "Check the domains niami rating",
+ "params": {},
+ "returns": {
+ "rating":
+ {
+ "name": "Niami Rating",
+ "type": "text"
+ }
+ }
+ },
+ "connections":{
+ "name": "HSD Connections",
+ "type": "dashboard",
+ "description": "Show the number of connections the HSD node is connected to",
+ "params": {},
+ "returns": {
+ "connections":
+ {
+ "name": "HSD Connections",
+ "type": "text"
+ }
+ }
}
}
-def listFunctions():
- return functions
-
-def runFunction(function, params, authentication):
- if function == "check":
- return check(params['domains'], authentication)
- elif function == "search":
- return search(params['search'], authentication)
- elif function == "transfer":
- return transfer(params['address'], params['domains'], authentication)
- elif function == "dns":
- return dns(params['domains'],params['dns'],authentication)
- else:
- return "Function not found"
-
-
-def check(domains, authentication):
+def check(params, authentication):
+ domains = params["domains"]
domains = domains.splitlines()
wallet = authentication.split(":")[0]
@@ -116,7 +137,8 @@ def check(domains, authentication):
return {"domains": domains}
-def search(search, authentication):
+def search(params, authentication):
+ search = params["search"]
wallet = authentication.split(":")[0]
owned = account.getDomains(wallet)
# Only keep owned domains ["name"]
@@ -127,8 +149,27 @@ def search(search, authentication):
return {"domains": domains}
-def transfer(address, domains, authentication):
+def transfer(params, authentication):
+ address = params["address"]
return {"hash":"f921ffe1bb01884bf515a8079073ee9381cb93a56b486694eda2cce0719f27c0","address":address}
-def dns(domains,dns,authentication):
- return {"hash":"f921ffe1bb01884bf515a8079073ee9381cb93a56b486694eda2cce0719f27c0","dns":dns}
\ No newline at end of file
+def dns(params,authentication):
+ dns = params["dns"]
+ return {"hash":"f921ffe1bb01884bf515a8079073ee9381cb93a56b486694eda2cce0719f27c0","dns":dns}
+
+def niami(params, authentication):
+ domain = params["domain"]
+ print(domain)
+ response = requests.get(f"https://api.handshake.niami.io/domain/{domain}")
+ print(response.text)
+ data = response.json()["data"]
+ rating = str(data["rating"]["score"]) + " (" + data["rating"]["rarity"] + ")"
+ return {"rating":rating}
+
+def niamiSearch(params, authentication):
+ return niami(params, authentication)
+
+
+def connections(params,authentication):
+ outbound = account.hsd.getInfo()['pool']['outbound']
+ return {"connections": outbound}
\ No newline at end of file
diff --git a/plugins/example/example.json b/plugins/example/example.json
deleted file mode 100644
index 6773397..0000000
--- a/plugins/example/example.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name":"Example Plugin",
- "description":"This is an example plugin"
-}
\ No newline at end of file
diff --git a/render.py b/render.py
index 75a3ae2..d430c76 100644
--- a/render.py
+++ b/render.py
@@ -204,7 +204,7 @@ def plugin_functions(functions, pluginName):
returns = returns.removesuffix(', ')
- functionType = "manual"
+ functionType = "default"
if "type" in functions[function]:
functionType = functions[function]["type"]
@@ -215,7 +215,7 @@ def plugin_functions(functions, pluginName):
html += f'{description}
'
html += f'Function type: {functionType.capitalize()}
'
- if functionType != "manual":
+ if functionType != "default":
html += f'Returns: {returns}
'
html += f''
html += f''
@@ -286,8 +286,17 @@ def plugin_output(outputs, returns):
html += f''
+ html += f''
+ return html
+
+def plugin_output_dash(outputs, returns):
+
+ html = ''
+
+ for returnOutput in returns:
+ html += render_template('components/dashboard-plugin.html', name=returns[returnOutput]["name"], output=outputs[returnOutput])
+
+
html += f''
-
-
-
+ html += f''
return html
\ No newline at end of file
diff --git a/templates/components/dashboard-plugin.html b/templates/components/dashboard-plugin.html
new file mode 100644
index 0000000..c6fdb75
--- /dev/null
+++ b/templates/components/dashboard-plugin.html
@@ -0,0 +1,8 @@
+
+
+
+
{{name}}
+
{{output}}
+
+
+
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index eb396d8..1f79865 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -126,7 +126,7 @@
-
+ {{plugins|safe}}
diff --git a/templates/plugin.html b/templates/plugin.html
index af6d813..38db15c 100644
--- a/templates/plugin.html
+++ b/templates/plugin.html
@@ -63,7 +63,8 @@
{{error}}
{{name}}
- {{description}}
{{functions|safe}}
+ {{description}}
+ Author: {{author}}
Version: {{version}}
{{functions|safe}}
-
+ {{plugins|safe}}