Add `duktape.cr` and the `Plugin` class

This commit is contained in:
Alex Ling 2020-07-21 09:30:45 +00:00
parent a7f4e161de
commit a812e3ed46
4 changed files with 165 additions and 0 deletions

View File

@ -20,6 +20,10 @@ shards:
github: crystal-lang/crystal-db
version: 0.9.0
duktape:
github: jessedoyle/duktape.cr
version: 0.20.0
exception_page:
github: crystal-loot/exception_page
version: 0.1.4
@ -36,6 +40,10 @@ shards:
github: jeromegn/kilt
version: 0.4.0
myhtml:
github: kostya/myhtml
version: 1.5.1
radix:
github: luislavena/radix
version: 0.3.9

View File

@ -27,3 +27,8 @@ dependencies:
github: crystal-ameba/ameba
clim:
github: at-grandpa/clim
duktape:
github: jessedoyle/duktape.cr
version: ~> 0.20.0
myhtml:
github: kostya/myhtml

View File

@ -16,6 +16,7 @@ class Config
property log_level : String = "info"
property upload_path : String = File.expand_path "~/mango/uploads",
home: true
property plugin_path : String = File.expand_path "~/mango/plugins"
property mangadex = Hash(String, String | Int32).new
@[YAML::Field(ignore: true)]

151
src/plugin/plugin.cr Normal file
View File

@ -0,0 +1,151 @@
require "duktape/runtime"
require "myhtml"
require "http"
class Plugin
class Error < ::Exception
end
class MetadataError < Error
end
class PluginException < Error
end
class SyntaxError < Error
end
{% for name in ["id", "title", "author", "version", "placeholder"] %}
getter {{name.id}} = ""
{% end %}
getter wait_seconds : UInt64 = 0
def self.list
dir = Config.current.plugin_path
Dir.children(dir)
.select do |f|
fp = File.join dir, f
File.file?(fp) && File.extname(fp) == ".js"
end
.map do |f|
File.basename f, ".js"
end
end
def initialize(@path : String)
@rt = Duktape::Runtime.new do |sbx|
sbx.push_global_object
sbx.del_prop_string -1, "print"
sbx.del_prop_string -1, "alert"
sbx.del_prop_string -1, "console"
def_helper_functions sbx
end
js = File.open @path, &.gets_to_end
eval js
begin
data = eval_json "metadata"
{% for name in ["id", "title", "author", "version", "placeholder"] %}
@{{name.id}} = data[{{name}}].as_s
{% end %}
@wait_seconds = data["wait_seconds"].as_i.to_u64
rescue e
raise MetadataError.new "Failed to retrieve metadata from plugin " \
"at #{@path}. Error: #{e.message}"
end
end
def search(query : String)
eval_json "search('#{query}')"
end
def select_chapter(id : String)
eval_json "selectChapter('#{id}')"
end
def next_page
eval_json "nextPage()"
end
private def eval(str)
@rt.eval str
rescue e : Duktape::SyntaxError
raise SyntaxError.new e.message
rescue e : Duktape::Error
raise Error.new e.message
end
private def eval_json(str)
JSON.parse eval(str).as String
end
private def def_helper_functions(sbx)
sbx.push_object
sbx.push_proc LibDUK::VARARGS do |ptr|
env = Duktape::Sandbox.new ptr
url = env.require_string 0
headers = HTTP::Headers.new
if env.get_top == 2
env.enum 1, LibDUK::Enum::OwnPropertiesOnly
while env.next -1, true
k = env.require_string -2
v = env.require_string -1
headers.add k, v
env.pop_2
end
end
res = HTTP::Client.get url, headers
body = res.body
env.push_string body
env.call_success
end
sbx.put_prop_string -2, "get"
sbx.push_proc 2 do |ptr|
env = Duktape::Sandbox.new ptr
html = env.require_string 0
selector = env.require_string 1
myhtml = Myhtml::Parser.new html
json = myhtml.css(selector).map(&.to_html).to_a.to_json
env.push_string json
env.call_success
end
sbx.put_prop_string -2, "css"
sbx.push_proc 1 do |ptr|
env = Duktape::Sandbox.new ptr
html = env.require_string 0
myhtml = Myhtml::Parser.new html
root = myhtml.root
str = ""
str = root.inner_text if root
env.push_string str
env.call_success
end
sbx.put_prop_string -2, "innerText"
sbx.push_proc 1 do |ptr|
env = Duktape::Sandbox.new ptr
msg = env.require_string 0
env.call_success
raise PluginException.new msg
end
sbx.put_prop_string -2, "raise"
sbx.put_prop_string -2, "mango"
end
end