diff options
author | href <href@random.sh> | 2020-07-07 21:39:10 +0200 |
---|---|---|
committer | href <href@random.sh> | 2020-07-07 21:39:51 +0200 |
commit | d6ee134a5957e299c3ad59011df320b3c41e6e61 (patch) | |
tree | 29567e6635466f8a3415a935b3cc8a777019f5bc /lib/lsg_irc/link_plugin | |
parent | bleh (diff) |
pouet
Diffstat (limited to '')
-rw-r--r-- | lib/lsg_irc/link_plugin.ex | 130 | ||||
-rw-r--r-- | lib/lsg_irc/link_plugin/github.ex | 44 | ||||
-rw-r--r-- | lib/lsg_irc/link_plugin/reddit_plugin.ex | 114 | ||||
-rw-r--r-- | lib/lsg_irc/link_plugin/twitter.ex | 13 |
4 files changed, 279 insertions, 22 deletions
diff --git a/lib/lsg_irc/link_plugin.ex b/lib/lsg_irc/link_plugin.ex index bc9764a..97835e4 100644 --- a/lib/lsg_irc/link_plugin.ex +++ b/lib/lsg_irc/link_plugin.ex @@ -39,7 +39,7 @@ defmodule LSG.IRC.LinkPlugin do require Logger def start_link() do - GenServer.start_link(__MODULE__, []) + GenServer.start_link(__MODULE__, [], name: __MODULE__) end @callback match(uri :: URI.t, options :: Keyword.t) :: {true, params :: Map.t} | false @@ -49,6 +49,7 @@ defmodule LSG.IRC.LinkPlugin do def init([]) do {:ok, _} = Registry.register(IRC.PubSub, "message", []) + #{:ok, _} = Registry.register(IRC.PubSub, "message:telegram", []) Logger.info("Link handler started") {:ok, %__MODULE__{}} end @@ -66,7 +67,12 @@ defmodule LSG.IRC.LinkPlugin do [uri] -> text [uri | _] -> ["-> #{URI.to_string(uri)}", text] end - message.replyfun.(text) + IO.inspect(text) + if is_list(text) do + for line <- text, do: message.replyfun.(line) + else + message.replyfun.(text) + end _ -> nil end end) @@ -91,8 +97,8 @@ defmodule LSG.IRC.LinkPlugin do # 4. ? # Over five redirections: cancel. - def expand_link([_, _, _, _, _, _ | _]) do - :error + def expand_link(acc = [_, _, _, _, _ | _]) do + {:ok, acc, "link redirects more than five times"} end def expand_link(acc=[uri | _]) do @@ -128,14 +134,14 @@ defmodule LSG.IRC.LinkPlugin do end defp get(url, headers \\ [], options \\ []) do - get_req(:hackney.get(url, headers, <<>>, options)) + get_req(url, :hackney.get(url, headers, <<>>, options)) end - defp get_req({:error, reason}) do + defp get_req(_, {:error, reason}) do {:error, reason} end - defp get_req({:ok, 200, headers, client}) do + 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) @@ -145,14 +151,14 @@ defmodule LSG.IRC.LinkPlugin do cond do String.starts_with?(content_type, "text/html") && length <= 30_000_000 -> - get_body(30_000_000, client, <<>>) + get_body(url, 30_000_000, client, <<>>) true -> :hackney.close(client) {:ok, "file: #{content_type}, size: #{length} bytes"} end end - defp get_req({:ok, redirect, headers, client}) when redirect in 300..399 do + 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) @@ -162,32 +168,70 @@ defmodule LSG.IRC.LinkPlugin do {:redirect, location} end - defp get_req({:ok, status, headers, client}) do + defp get_req(_, {:ok, status, headers, client}) do :hackney.close(client) {:error, status, headers} end - defp get_body(len, client, acc) when len >= byte_size(acc) do + defp get_body(url, len, client, acc) when len >= byte_size(acc) do case :hackney.stream_body(client) do {:ok, data} -> - get_body(len, client, << acc::binary, data::binary >>) + get_body(url, len, client, << acc::binary, data::binary >>) :done -> html = Floki.parse(acc) - title = case Floki.find(html, "title") do - [{"title", [], [title]} | _] -> - String.trim(title) - _ -> - nil + 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) end - {:ok, title} + {:ok, text} {: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 :hackney.close(client) - {:ok, "mais il rentrera jamais en ram ce fichier !"} + {:ok, "Error: file over 30"} end def expand_default(acc = [uri = %URI{scheme: scheme} | _]) when scheme in ["http", "https"] do @@ -201,9 +245,10 @@ defmodule LSG.IRC.LinkPlugin do new_uri = %URI{new_uri | scheme: scheme, authority: uri.authority, host: uri.host, port: uri.port} expand_link([new_uri | acc]) {:error, status, _headers} -> - {:ok, acc, "Error #{status}"} + text = Plug.Conn.Status.reason_phrase(status) + {:ok, acc, "Error: HTTP #{text} (#{status})"} {:error, reason} -> - {:ok, acc, "Error #{to_string(reason)}"} + {:ok, acc, "Error: #{to_string(reason)}"} end end @@ -212,4 +257,47 @@ 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 diff --git a/lib/lsg_irc/link_plugin/github.ex b/lib/lsg_irc/link_plugin/github.ex new file mode 100644 index 0000000..c7444c2 --- /dev/null +++ b/lib/lsg_irc/link_plugin/github.ex @@ -0,0 +1,44 @@ +defmodule LSG.IRC.LinkPlugin.Github do + @behaviour LSG.IRC.LinkPlugin + + def match(uri = %URI{host: "github.com", path: path}, _) do + case String.split(path, "/") do + ["", user, repo] -> + {true, %{user: user, repo: repo, path: "#{user}/#{repo}"}} + _ -> + false + end + end + + def match(_, _), do: false + + def expand(_uri, %{user: user, repo: repo}, _opts) do + case HTTPoison.get("https://api.github.com/repos/#{user}/#{repo}") do + {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + {:ok, json} = Jason.decode(body) + src = json["source"]["full_name"] + disabled = if(json["disabled"], do: " (disabled)", else: "") + archived = if(json["archived"], do: " (archived)", else: "") + fork = if src && src != json["full_name"] do + " (⑂ #{json["source"]["full_name"]})" + else + "" + end + start = "#{json["full_name"]}#{disabled}#{archived}#{fork} - #{json["description"]}" + tags = for(t <- json["topics"]||[], do: "##{t}") |> Enum.intersperse(", ") |> Enum.join("") + lang = if(json["language"], do: "#{json["language"]} - ", else: "") + issues = if(json["open_issues_count"], do: "#{json["open_issues_count"]} issues - ", else: "") + last_push = if at = json["pushed_at"] do + {:ok, date, _} = DateTime.from_iso8601(at) + " - last pushed #{DateTime.to_string(date)}" + else + "" + end + network = "#{lang}#{issues}#{json["stargazers_count"]} stars - #{json["subscribers_count"]} watchers - #{json["forks_count"]} forks#{last_push}" + {:ok, [start, tags, network]} + other -> + :error + end + end + +end diff --git a/lib/lsg_irc/link_plugin/reddit_plugin.ex b/lib/lsg_irc/link_plugin/reddit_plugin.ex new file mode 100644 index 0000000..a7f5235 --- /dev/null +++ b/lib/lsg_irc/link_plugin/reddit_plugin.ex @@ -0,0 +1,114 @@ +defmodule LSG.IRC.LinkPlugin.Reddit do + @behaviour LSG.IRC.LinkPlugin + + def match(uri = %URI{host: "reddit.com", path: path}, _) do + case String.split(path, "/") do + ["", "r", sub, "comments", post_id, _slug] -> + {true, %{mode: :post, path: path, sub: sub, post_id: post_id}} + ["", "r", sub, "comments", post_id, _slug, ""] -> + {true, %{mode: :post, path: path, sub: sub, post_id: post_id}} + ["", "r", sub, ""] -> + {true, %{mode: :sub, path: path, sub: sub}} + ["", "r", sub] -> + {true, %{mode: :sub, path: path, sub: sub}} +# ["", "u", user] -> +# {true, %{mode: :user, path: path, user: user}} + _ -> + false + end + end + + def match(uri = %URI{host: host, path: path}, opts) do + if String.ends_with?(host, ".reddit.com") do + match(%URI{uri | host: "reddit.com"}, opts) + else + false + end + end + + def expand(_, %{mode: :sub, sub: sub}, _opts) do + url = "https://api.reddit.com/r/#{sub}/about" + case HTTPoison.get(url) do + {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + sr = Jason.decode!(body) + |> Map.get("data") + |> IO.inspect(limit: :infinity) + description = Map.get(sr, "public_description")||Map.get(sr, "description", "") + |> String.split("\n") + |> List.first() + name = if title = Map.get(sr, "title") do + Map.get(sr, "display_name_prefixed") <> ": " <> title + else + Map.get(sr, "display_name_prefixed") + end + nsfw = if Map.get(sr, "over18") do + "[NSFW] " + else + "" + end + quarantine = if Map.get(sr, "quarantine") do + "[Quarantined] " + else + "" + end + count = "#{Map.get(sr, "subscribers")} subscribers, #{Map.get(sr, "active_user_count")} active" + preview = "#{quarantine}#{nsfw}#{name} — #{description} (#{count})" + {:ok, preview} + _ -> + :error + end + end + + def expand(_uri, %{mode: :post, path: path, sub: sub, post_id: post_id}, _opts) do + case HTTPoison.get("https://api.reddit.com#{path}?sr_detail=true") do + {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + json = Jason.decode!(body) + op = List.first(json) + |> Map.get("data") + |> Map.get("children") + |> List.first() + |> Map.get("data") + |> IO.inspect(limit: :infinity) + sr = get_in(op, ["sr_detail", "display_name_prefixed"]) + {self?, url} = if Map.get(op, "selftext") == "" do + {false, Map.get(op, "url")} + else + {true, nil} + end + + self_str = if(self?, do: "text", else: url) + up = Map.get(op, "ups") + down = Map.get(op, "downs") + comments = Map.get(op, "num_comments") + nsfw = if Map.get(op, "over_18") do + "[NSFW] " + else + "" + end + state = cond do + Map.get(op, "hidden") -> "hidden" + Map.get(op, "archived") -> "archived" + Map.get(op, "locked") -> "locked" + Map.get(op, "quarantine") -> "quarantined" + Map.get(op, "removed_by") || Map.get(op, "removed_by_category") -> "removed" + Map.get(op, "banned_by") -> "banned" + Map.get(op, "pinned") -> "pinned" + Map.get(op, "stickied") -> "stickied" + true -> nil + end + flair = if flair = Map.get(op, "link_flair_text") do + "[#{flair}] " + else + "" + end + title = "#{nsfw}#{sr}: #{flair}#{Map.get(op, "title")}" + state_str = if(state, do: "#{state}, ") + content = "by u/#{Map.get(op, "author")} - #{state_str}#{up} up, #{down} down, #{comments} comments - #{self_str}" + + {:ok, [title, content]} + err -> + :error + end + end + +end diff --git a/lib/lsg_irc/link_plugin/twitter.ex b/lib/lsg_irc/link_plugin/twitter.ex index 04dea7c..a6b6e29 100644 --- a/lib/lsg_irc/link_plugin/twitter.ex +++ b/lib/lsg_irc/link_plugin/twitter.ex @@ -55,6 +55,17 @@ defmodule LSG.IRC.LinkPlugin.Twitter do {:ok, at} = Timex.parse(tweet.created_at, "%a %b %e %H:%M:%S %z %Y", :strftime) {:ok, format} = Timex.format(at, "{relative}", :relative) + replyto = if tweet.in_reply_to_status_id do + replyurl = "https://twitter.com/#{tweet.in_reply_to_screen_name}/status/#{tweet.in_reply_to_status_id}" + if tweet.in_reply_to_screen_name == tweet.user.screen_name do + "— continued from #{replyurl}" + else + "— replying to #{replyurl}" + end + else + "" + end + quoted = if tweet.quoted_status do quote_url = "https://twitter.com/#{tweet.quoted_status.user.screen_name}/status/#{tweet.quoted_status.id}" full_text = expand_twitter_text(tweet.quoted_status) @@ -66,7 +77,7 @@ defmodule LSG.IRC.LinkPlugin.Twitter do foot = "— #{format} - #{tweet.retweet_count} retweets - #{tweet.favorite_count} likes" - text = ["#{tweet.user.name} (@#{tweet.user.screen_name}):"] ++ text ++ quoted ++ [foot] + text = ["#{tweet.user.name} (@#{tweet.user.screen_name}):", replyto] ++ text ++ quoted ++ [foot] {:ok, text} end |