summaryrefslogtreecommitdiff
path: root/lib/lsg_irc/link_plugin
diff options
context:
space:
mode:
authorhref <href@random.sh>2020-07-07 21:39:10 +0200
committerhref <href@random.sh>2020-07-07 21:39:51 +0200
commitd6ee134a5957e299c3ad59011df320b3c41e6e61 (patch)
tree29567e6635466f8a3415a935b3cc8a777019f5bc /lib/lsg_irc/link_plugin
parentbleh (diff)
pouet
Diffstat (limited to '')
-rw-r--r--lib/lsg_irc/link_plugin.ex130
-rw-r--r--lib/lsg_irc/link_plugin/github.ex44
-rw-r--r--lib/lsg_irc/link_plugin/reddit_plugin.ex114
-rw-r--r--lib/lsg_irc/link_plugin/twitter.ex13
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