diff options
author | Jordan Bracco <href@random.sh> | 2022-12-20 02:13:47 +0000 |
---|---|---|
committer | Jordan Bracco <href@random.sh> | 2022-12-20 19:29:41 +0100 |
commit | 70b9bba56f5319361ce5a7df5c489b9c0d6905ce (patch) | |
tree | f9b4438965f4c5e3e1f3a6129904cbb9a37047f2 /lib/nola_plugins/link_plugin.ex | |
parent | Update repo URL, refs T77. (diff) |
Rename to Nola
Summary:
Nola rename cont. pt. 2. Refs T77.
`find lib -name "*.ex" -type f | xargs sed -i '' 's/LSG/Nola/g'`
Nola rename, cont. pt. 3. Refs T77.
`s/:lsg/:nola/g`
Nola rename, cont. pt. 4. Refs T77.
Nola rename, cont. pt. 5. Refs T77. Configs.
find config -type f | xargs sed -i '' 's/LSG/Nola/g'
find config -type f | xargs sed -i '' 's/lsg/nola/g'
BREAKING CHANGE: Config keys switch from `:lsg` to `:nola`
Nola rename, the end. pt 6. Refs T77.
Nola rename: The Big Move, Refs T77
Update repo URL, refs T77.
Nola rename: Nola.Plugins, refs T77
Maniphest Tasks: T77
Differential Revision: https://phab.random.sh/D3
Diffstat (limited to 'lib/nola_plugins/link_plugin.ex')
-rw-r--r-- | lib/nola_plugins/link_plugin.ex | 271 |
1 files changed, 0 insertions, 271 deletions
diff --git a/lib/nola_plugins/link_plugin.ex b/lib/nola_plugins/link_plugin.ex deleted file mode 100644 index dee78e8..0000000 --- a/lib/nola_plugins/link_plugin.ex +++ /dev/null @@ -1,271 +0,0 @@ -defmodule Nola.IRC.LinkPlugin do - @moduledoc """ - # Link Previewer - - An extensible link previewer for IRC. - - To extend the supported sites, create a new handler implementing the callbacks. - - See `link_plugin/` directory for examples. The first in list handler that returns true to the `match/2` callback will be used, - and if the handler returns `:error` or crashes, will fallback to the default preview. - - Unsupported websites will use the default link preview method, which is for html document the title, otherwise it'll use - the mimetype and size. - - ## Configuration: - - ``` - config :nola, Nola.IRC.LinkPlugin, - handlers: [ - Nola.IRC.LinkPlugin.Youtube: [ - invidious: true - ], - Nola.IRC.LinkPlugin.Twitter: [], - Nola.IRC.LinkPlugin.Imgur: [], - ] - ``` - - """ - - @ircdoc """ - # Link preview - - Previews links (just post a link!). - - Announces real URL after redirections and provides extended support for YouTube, Twitter and Imgur. - """ - def short_irc_doc, do: false - def irc_doc, do: @ircdoc - require Logger - - def start_link() do - GenServer.start_link(__MODULE__, [], name: __MODULE__) - end - - @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] - - def init([]) do - {:ok, _} = Registry.register(IRC.PubSub, "messages", [plugin: __MODULE__]) - #{:ok, _} = Registry.register(IRC.PubSub, "messages:telegram", [plugin: __MODULE__]) - Logger.info("Link handler started") - {:ok, %__MODULE__{}} - end - - def handle_info({:irc, :text, message = %{text: text}}, state) do - String.split(text) - |> Enum.map(fn(word) -> - if String.starts_with?(word, "http://") || String.starts_with?(word, "https://") 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 - [luri | _] -> - if luri.host == uri.host && luri.path == luri.path do - text - else - ["-> #{URI.to_string(luri)}", text] - end - end - if is_list(text) do - for line <- text, do: message.replyfun.(line) - else - message.replyfun.(text) - end - _ -> nil - end - end) - end - end - end) - {:noreply, state} - end - - def handle_info(msg, state) do - {:noreply, state} - end - - def terminate(_reason, state) do - :ok - end - - # 1. Match the first valid handler - # 2. Try to run the handler - # 3. If :error or crash, default link. - # If :skip, nothing - # 4. ? - - # Over five redirections: cancel. - def expand_link(acc = [_, _, _, _, _ | _]) do - {:ok, acc, "link redirects more than five times"} - end - - def expand_link(acc=[uri | _]) do - Logger.debug("link: expanding: #{inspect uri}") - handlers = Keyword.get(Application.get_env(:nola, __MODULE__, [handlers: []]), :handlers) - handler = Enum.reduce_while(handlers, nil, fn({module, opts}, acc) -> - Logger.debug("link: attempt expanding: #{inspect module} for #{inspect uri}") - module = Module.concat([module]) - case module.match(uri, opts) do - {true, params} -> {:halt, {module, params, opts}} - false -> {:cont, acc} - end - end) - run_expand(acc, handler) - end - - def run_expand(acc, nil) do - expand_default(acc) - end - - def run_expand(acc=[uri|_], {module, params, opts}) do - Logger.debug("link: expanding #{inspect uri} with #{inspect module}") - case module.expand(uri, params, opts) do - {:ok, data} -> {:ok, acc, data} - :error -> expand_default(acc) - :skip -> nil - end - rescue - e -> - Logger.error("link: rescued #{inspect uri} with #{inspect module}: #{inspect e}") - Logger.error(Exception.format(:error, e, __STACKTRACE__)) - expand_default(acc) - catch - e, b -> - Logger.error("link: catched #{inspect uri} with #{inspect module}: #{inspect {e, b}}") - expand_default(acc) - end - - defp get(url, headers \\ [], options \\ []) do - get_req(url, :hackney.get(url, headers, <<>>, options)) - end - - defp get_req(_, {:error, reason}) do - {:error, reason} - end - - defp get_req(url, {:ok, 200, headers, client}) do - headers = Enum.reduce(headers, %{}, fn({key, value}, acc) -> - Map.put(acc, String.downcase(key), value) - end) - content_type = Map.get(headers, "content-type", "application/octect-stream") - length = Map.get(headers, "content-length", "0") - {length, _} = Integer.parse(length) - - handlers = Keyword.get(Application.get_env(:nola, __MODULE__, [handlers: []]), :handlers) - handler = Enum.reduce_while(handlers, false, 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 - 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: #{human_size(length)}"} - 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: #{human_size(length)}"} - end - end - - defp get_req(_, {:ok, redirect, headers, client}) when redirect in 300..399 do - headers = Enum.reduce(headers, %{}, fn({key, value}, acc) -> - Map.put(acc, String.downcase(key), value) - end) - location = Map.get(headers, "location") - - :hackney.close(client) - {:redirect, location} - end - - defp get_req(_, {:ok, status, headers, client}) do - :hackney.close(client) - {:error, status, headers} - end - - 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, h, << acc::binary, data::binary >>) - :done -> - body = case mode do - :body -> acc - :file -> - {:ok, tmpfile} = Plug.Upload.random_file("linkplugin") - File.write!(tmpfile, acc) - tmpfile - end - handler.post_expand(url, body, params, opts) - {:error, reason} -> - {:ok, "failed to fetch body: #{inspect reason}"} - end - end - - defp get_body(_, len, client, h, _acc) do - :hackney.close(client) - IO.inspect(h) - {:ok, "Error: file over 30"} - end - - def expand_default(acc = [uri = %URI{scheme: scheme} | _]) when scheme in ["http", "https"] do - Logger.debug("link: expanding #{uri} with default") - 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} - expand_link([new_uri | acc]) - {:error, status, _headers} -> - text = Plug.Conn.Status.reason_phrase(status) - {:ok, acc, "Error: HTTP #{text} (#{status})"} - {:error, {:tls_alert, {:handshake_failure, err}}} -> - {:ok, acc, "TLS Error: #{to_string(err)}"} - {:error, reason} -> - {:ok, acc, "Error: #{to_string(reason)}"} - end - end - - # Unsupported scheme, came from a redirect. - def expand_default(acc = [uri | _]) do - {:ok, [uri], "-> #{URI.to_string(uri)}"} - end - - - defp human_size(bytes) do - bytes - |> FileSize.new(:b) - |> FileSize.scale() - |> FileSize.format() - end -end |