diff options
Diffstat (limited to '')
-rw-r--r-- | lib/lsg_irc/link_plugin.ex | 147 |
1 files changed, 50 insertions, 97 deletions
diff --git a/lib/lsg_irc/link_plugin.ex b/lib/lsg_irc/link_plugin.ex index 97835e4..ea6df0c 100644 --- a/lib/lsg_irc/link_plugin.ex +++ b/lib/lsg_irc/link_plugin.ex @@ -44,6 +44,10 @@ defmodule LSG.IRC.LinkPlugin do @callback match(uri :: URI.t, options :: Keyword.t) :: {true, params :: Map.t} | false @callback expand(uri :: URI.t, params :: Map.t, options :: Keyword.t) :: {:ok, lines :: [] | String.t} | :error + @callback post_match(uri :: URI.t, content_type :: binary, headers :: [], opts :: Keyword.t) :: {:body | :file, params :: Map.t} | false + @callback post_expand(uri :: URI.t, body :: binary() | Path.t, params :: Map.t, options :: Keyword.t) :: {:ok, lines :: [] | String.t} | :error + + @optional_callbacks [expand: 3, post_expand: 4] defstruct [:client] @@ -61,13 +65,18 @@ defmodule LSG.IRC.LinkPlugin do uri = URI.parse(word) if uri.scheme && uri.host do spawn(fn() -> + :timer.kill_after(:timer.seconds(30)) case expand_link([uri]) do {:ok, uris, text} -> text = case uris do [uri] -> text - [uri | _] -> ["-> #{URI.to_string(uri)}", text] + [luri | _] -> + if luri.host == uri.host && luri.path == luri.path do + text + else + ["-> #{URI.to_string(luri)}", text] + end end - IO.inspect(text) if is_list(text) do for line <- text, do: message.replyfun.(line) else @@ -149,9 +158,34 @@ defmodule LSG.IRC.LinkPlugin do length = Map.get(headers, "content-length", "0") {length, _} = Integer.parse(length) + handlers = Keyword.get(Application.get_env(:lsg, __MODULE__, [handlers: []]), :handlers) + handler = Enum.reduce_while(handlers, nil, fn({module, opts}, acc) -> + module = Module.concat([module]) + try do + case module.post_match(url, content_type, headers, opts) do + {mode, params} when mode in [:body, :file] -> {:halt, {module, params, opts, mode}} + false -> {:cont, acc} + end + rescue + e -> + Logger.error(inspect(e)) + {:cont, false} + catch + e, b -> + Logger.error(inspect({b})) + {:cont, false} + end + end) + cond do - String.starts_with?(content_type, "text/html") && length <= 30_000_000 -> - get_body(url, 30_000_000, client, <<>>) + handler != false and length <= 30_000_000 -> + case get_body(url, 30_000_000, client, handler, <<>>) do + {:ok, _} = ok -> ok + :error -> + {:ok, "file: #{content_type}, size: #{length} bytes"} + end + #String.starts_with?(content_type, "text/html") && length <= 30_000_000 -> + # get_body(url, 30_000_000, client, <<>>) true -> :hackney.close(client) {:ok, "file: #{content_type}, size: #{length} bytes"} @@ -173,76 +207,38 @@ defmodule LSG.IRC.LinkPlugin do {:error, status, headers} end - defp get_body(url, len, client, acc) when len >= byte_size(acc) do + defp get_body(url, len, client, {handler, params, opts, mode} = h, acc) when len >= byte_size(acc) do case :hackney.stream_body(client) do {:ok, data} -> - get_body(url, len, client, << acc::binary, data::binary >>) + get_body(url, len, client, h, << acc::binary, data::binary >>) :done -> - html = Floki.parse(acc) - title = collect_title(html) - opengraph = collect_open_graph(html) - itemprops = collect_itemprops(html) - Logger.debug("OG: #{inspect opengraph}") - text = if Map.has_key?(opengraph, "title") && Map.has_key?(opengraph, "description") do - sitename = if sn = Map.get(opengraph, "site_name") do - "#{sn}" - else - "" - end - paywall? = if Map.get(opengraph, "article:content_tier", Map.get(itemprops, "article:content_tier", "free")) == "free" do - "" - else - "[paywall] " - end - section = if section = Map.get(opengraph, "article:section", Map.get(itemprops, "article:section", nil)) do - ": #{section}" - else - "" - end - date = case DateTime.from_iso8601(Map.get(opengraph, "article:published_time", Map.get(itemprops, "article:published_time", ""))) do - {:ok, date, _} -> - "#{Timex.format!(date, "%d/%m/%y", :strftime)}. " - _ -> - "" - end - uri = URI.parse(url) - - prefix = "#{paywall?}#{Map.get(opengraph, "site_name", uri.host)}#{section}" - prefix = unless prefix == "" do - "#{prefix} — " - else - "" - end - [clean_text("#{prefix}#{Map.get(opengraph, "title")}")] ++ IRC.splitlong(clean_text("#{date}#{Map.get(opengraph, "description")}")) - else - clean_text(title) + body = case mode do + :body -> acc + :file -> + {:ok, tmpfile} = Plug.Upload.random_file("linkplugin") + File.write!(tmpfile, acc) + tmpfile end - {:ok, text} + handler.post_expand(url, body, params, opts) {:error, reason} -> {:ok, "failed to fetch body: #{inspect reason}"} end end - defp clean_text(text) do - text - |> String.replace("\n", " ") - |> HtmlEntities.decode() - end - - defp get_body(len, client, _acc) do + defp get_body(_, len, client, _, _acc) do :hackney.close(client) {:ok, "Error: file over 30"} end def expand_default(acc = [uri = %URI{scheme: scheme} | _]) when scheme in ["http", "https"] do - headers = [] + headers = [{"user-agent", "DmzBot (like TwitterBot)"}] options = [follow_redirect: false, max_body_length: 30_000_000] case get(URI.to_string(uri), headers, options) do {:ok, text} -> {:ok, acc, text} {:redirect, link} -> new_uri = URI.parse(link) - new_uri = %URI{new_uri | scheme: scheme, authority: uri.authority, host: uri.host, port: uri.port} + #new_uri = %URI{new_uri | scheme: scheme, authority: uri.authority, host: uri.host, port: uri.port} expand_link([new_uri | acc]) {:error, status, _headers} -> text = Plug.Conn.Status.reason_phrase(status) @@ -257,47 +253,4 @@ defmodule LSG.IRC.LinkPlugin do {:ok, [uri], "-> #{URI.to_string(uri)}"} end - defp collect_title(html) do - case Floki.find(html, "title") do - [{"title", [], [title]} | _] -> - String.trim(title) - _ -> - nil - end - end - - defp collect_open_graph(html) do - Enum.reduce(Floki.find(html, "head meta"), %{}, fn(tag, acc) -> - case tag do - {"meta", values, []} -> - name = List.keyfind(values, "property", 0, {nil, nil}) |> elem(1) - content = List.keyfind(values, "content", 0, {nil, nil}) |> elem(1) - case name do - "og:" <> key -> - Map.put(acc, key, content) - "article:"<>_ -> - Map.put(acc, name, content) - _other -> acc - end - _other -> acc - end - end) - end - - defp collect_itemprops(html) do - Enum.reduce(Floki.find(html, "[itemprop]"), %{}, fn(tag, acc) -> - case tag do - {"meta", values, []} -> - name = List.keyfind(values, "itemprop", 0, {nil, nil}) |> elem(1) - content = List.keyfind(values, "content", 0, {nil, nil}) |> elem(1) - case name do - "article:" <> key -> - Map.put(acc, name, content) - _other -> acc - end - _other -> acc - end - end) - end - end |