aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/web/twitter_api
diff options
context:
space:
mode:
authorRoger Braun <roger@rogerbraun.net>2017-05-07 19:28:23 +0200
committerRoger Braun <roger@rogerbraun.net>2017-05-07 19:28:23 +0200
commitb403ea4d2b69cef4434ad68babdfb402d8227847 (patch)
treed07607b3387c89f4310881132a9e10a5389a5439 /lib/pleroma/web/twitter_api
parenta9b2ad17596d1b6deca646239a95e94dc644ebf3 (diff)
parent60b4b0d725aefdca3eedd2d7708b0c96ee60c5f4 (diff)
downloadpleroma-b403ea4d2b69cef4434ad68babdfb402d8227847.tar.gz
Merge branch 'develop' into dtluna/pleroma-feature/unfollow-activity
Diffstat (limited to 'lib/pleroma/web/twitter_api')
-rw-r--r--lib/pleroma/web/twitter_api/representers/activity_representer.ex81
-rw-r--r--lib/pleroma/web/twitter_api/representers/user_representer.ex5
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex187
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex38
4 files changed, 178 insertions, 133 deletions
diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex
index f2bf93abb..affd43577 100644
--- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex
+++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex
@@ -1,17 +1,19 @@
defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter
alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ObjectRepresenter}
- alias Pleroma.Activity
-
+ alias Pleroma.{Activity, User}
+ alias Calendar.Strftime
+ alias Pleroma.Web.TwitterAPI.TwitterAPI
+ alias Pleroma.Wi
defp user_by_ap_id(user_list, ap_id) do
Enum.find(user_list, fn (%{ap_id: user_id}) -> ap_id == user_id end)
end
- def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor}} = activity, %{users: users, announced_activity: announced_activity} = opts) do
+ def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} = activity,
+ %{users: users, announced_activity: announced_activity} = opts) do
user = user_by_ap_id(users, actor)
- created_at = get_in(activity.data, ["published"])
- |> date_to_asctime
+ created_at = created_at |> date_to_asctime
text = "#{user.nickname} retweeted a status."
@@ -26,20 +28,21 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
"is_post_verb" => false,
"uri" => "tag:#{activity.data["id"]}:objectType=note",
"created_at" => created_at,
- "retweeted_status" => retweeted_status
+ "retweeted_status" => retweeted_status,
+ "statusnet_conversation_id" => conversation_id(announced_activity)
}
end
- def to_map(%Activity{data: %{"type" => "Like"}} = activity, %{user: user, liked_activity: liked_activity} = opts) do
- created_at = get_in(activity.data, ["published"])
- |> date_to_asctime
+ def to_map(%Activity{data: %{"type" => "Like", "published" => created_at}} = activity,
+ %{user: user, liked_activity: liked_activity} = opts) do
+ created_at = created_at |> date_to_asctime
text = "#{user.nickname} favorited a status."
%{
"id" => activity.id,
"user" => UserRepresenter.to_map(user, opts),
- "statusnet_html" => text, # TODO: add summary
+ "statusnet_html" => text,
"text" => text,
"is_local" => true,
"is_post_verb" => false,
@@ -49,16 +52,17 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
}
end
- def to_map(%Activity{data: %{"type" => "Follow"}} = activity, %{user: user} = opts) do
- created_at = get_in(activity.data, ["published"])
- |> date_to_asctime
+ def to_map(%Activity{data: %{"type" => "Follow", "published" => created_at, "object" => followed_id}} = activity, %{user: user} = opts) do
+ created_at = created_at |> date_to_asctime
+ followed = User.get_cached_by_ap_id(followed_id)
+ text = "#{user.nickname} started following #{followed.nickname}"
%{
"id" => activity.id,
"user" => UserRepresenter.to_map(user, opts),
"attentions" => [],
- "statusnet_html" => "", # TODO: add summary
- "text" => "",
+ "statusnet_html" => text,
+ "text" => text,
"is_local" => true,
"is_post_verb" => false,
"created_at" => created_at,
@@ -66,14 +70,12 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
}
end
- def to_map(%Activity{} = activity, %{user: user} = opts) do
- content = get_in(activity.data, ["object", "content"])
- created_at = get_in(activity.data, ["object", "published"])
- |> date_to_asctime
- like_count = get_in(activity.data, ["object", "like_count"]) || 0
- announcement_count = get_in(activity.data, ["object", "announcement_count"]) || 0
- favorited = opts[:for] && opts[:for].ap_id in (activity.data["object"]["likes"] || [])
- repeated = opts[:for] && opts[:for].ap_id in (activity.data["object"]["announcements"] || [])
+ def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = activity, %{user: user} = opts) do
+ created_at = object["published"] |> date_to_asctime
+ like_count = object["like_count"] || 0
+ announcement_count = object["announcement_count"] || 0
+ favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
+ repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
mentions = opts[:mentioned] || []
@@ -82,6 +84,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
|> Enum.filter(&(&1))
|> Enum.map(fn (user) -> UserRepresenter.to_map(user, opts) end)
+ conversation_id = conversation_id(activity)
+
%{
"id" => activity.id,
"user" => UserRepresenter.to_map(user, opts),
@@ -91,22 +95,41 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
"is_local" => true,
"is_post_verb" => true,
"created_at" => created_at,
- "in_reply_to_status_id" => activity.data["object"]["inReplyToStatusId"],
- "statusnet_conversation_id" => activity.data["object"]["statusnetConversationId"],
- "attachments" => (activity.data["object"]["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts),
+ "in_reply_to_status_id" => object["inReplyToStatusId"],
+ "statusnet_conversation_id" => conversation_id,
+ "attachments" => (object["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts),
"attentions" => attentions,
"fave_num" => like_count,
"repeat_num" => announcement_count,
- "favorited" => !!favorited,
- "repeated" => !!repeated,
+ "favorited" => to_boolean(favorited),
+ "repeated" => to_boolean(repeated),
}
end
+ def conversation_id(activity) do
+ with context when not is_nil(context) <- activity.data["context"] do
+ TwitterAPI.context_to_conversation_id(context)
+ else _e -> nil
+ end
+ end
+
defp date_to_asctime(date) do
with {:ok, date, _offset} <- date |> DateTime.from_iso8601 do
- Calendar.Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
+ Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
else _e ->
""
end
end
+
+ defp to_boolean(false) do
+ false
+ end
+
+ defp to_boolean(nil) do
+ false
+ end
+
+ defp to_boolean(_) do
+ true
+ end
end
diff --git a/lib/pleroma/web/twitter_api/representers/user_representer.ex b/lib/pleroma/web/twitter_api/representers/user_representer.ex
index ab7d6d353..493077413 100644
--- a/lib/pleroma/web/twitter_api/representers/user_representer.ex
+++ b/lib/pleroma/web/twitter_api/representers/user_representer.ex
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenter do
false
end
- user_info = User.user_info(user)
+ user_info = User.get_cached_user_info(user)
map = %{
"id" => user.id,
@@ -28,7 +28,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenter do
"profile_image_url_https" => image,
"profile_image_url_profile_size" => image,
"profile_image_url_original" => image,
- "rights" => %{}
+ "rights" => %{},
+ "statusnet_profile_url" => user.ap_id
}
map
diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex
index 8e2cd98ca..793a55250 100644
--- a/lib/pleroma/web/twitter_api/twitter_api.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api.ex
@@ -5,34 +5,77 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
import Ecto.Query
- def create_status(user = %User{}, data = %{}) do
- attachments = Enum.map(data["media_ids"] || [], fn (media_id) ->
- Repo.get(Object, media_id).data
- end)
+ def to_for_user_and_mentions(user, mentions) do
+ default_to = [
+ User.ap_followers(user),
+ "https://www.w3.org/ns/activitystreams#Public"
+ ]
- context = ActivityPub.generate_context_id
+ default_to ++ Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end)
+ end
- content = HtmlSanitizeEx.strip_tags(data["status"])
+ def format_input(text, mentions) do
+ HtmlSanitizeEx.strip_tags(text)
|> String.replace("\n", "<br>")
+ |> add_user_links(mentions)
+ end
- mentions = parse_mentions(content)
+ def attachments_from_ids(ids) do
+ Enum.map(ids || [], fn (media_id) ->
+ Repo.get(Object, media_id).data
+ end)
+ end
- default_to = [
- User.ap_followers(user),
- "https://www.w3.org/ns/activitystreams#Public"
- ]
+ def get_replied_to_activity(id) when not is_nil(id) do
+ Repo.get(Activity, id)
+ end
- to = default_to ++ Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end)
+ def get_replied_to_activity(_), do: nil
- content_html = add_user_links(content, mentions)
+ def add_attachments(text, attachments) do
+ attachment_text = Enum.map(attachments, fn
+ (%{"url" => [%{"href" => href} | _]}) ->
+ "<a href='#{href}'>#{href}</a>"
+ _ -> ""
+ end)
+ Enum.join([text | attachment_text], "<br>")
+ end
+
+ def create_status(%User{} = user, %{"status" => status} = data) do
+ attachments = attachments_from_ids(data["media_ids"])
+ context = ActivityPub.generate_context_id
+ mentions = parse_mentions(status)
+ content_html = status
+ |> format_input(mentions)
+ |> add_attachments(attachments)
+ to = to_for_user_and_mentions(user, mentions)
date = make_date()
- activity = %{
- "type" => "Create",
- "to" => to,
- "actor" => user.ap_id,
- "object" => %{
+ inReplyTo = get_replied_to_activity(data["in_reply_to_status_id"])
+
+ # Wire up reply info.
+ [to, context, object, additional] =
+ if inReplyTo do
+ context = inReplyTo.data["context"]
+ to = to ++ [inReplyTo.data["actor"]]
+
+ object = %{
+ "type" => "Note",
+ "to" => to,
+ "content" => content_html,
+ "published" => date,
+ "context" => context,
+ "attachment" => attachments,
+ "actor" => user.ap_id,
+ "inReplyTo" => inReplyTo.data["object"]["id"],
+ "inReplyToStatusId" => inReplyTo.id,
+ }
+ additional = %{}
+
+ [to, context, object, additional]
+ else
+ object = %{
"type" => "Note",
"to" => to,
"content" => content_html,
@@ -40,36 +83,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
"context" => context,
"attachment" => attachments,
"actor" => user.ap_id
- },
- "published" => date,
- "context" => context
- }
-
- # Wire up reply info.
- activity = with inReplyToId when not is_nil(inReplyToId) <- data["in_reply_to_status_id"],
- inReplyTo <- Repo.get(Activity, inReplyToId),
- context <- inReplyTo.data["context"]
- do
-
- to = activity["to"] ++ [inReplyTo.data["actor"]]
-
- activity
- |> put_in(["to"], to)
- |> put_in(["context"], context)
- |> put_in(["object", "context"], context)
- |> put_in(["object", "inReplyTo"], inReplyTo.data["object"]["id"])
- |> put_in(["object", "inReplyToStatusId"], inReplyToId)
- |> put_in(["statusnetConversationId"], inReplyTo.data["statusnetConversationId"])
- |> put_in(["object", "statusnetConversationId"], inReplyTo.data["statusnetConversationId"])
- else _e ->
- activity
- end
-
- with {:ok, activity} <- ActivityPub.insert(activity) do
- {:ok, activity} = add_conversation_id(activity)
- Pleroma.Web.Websub.publish(Pleroma.Web.OStatus.feed_path(user), user, activity)
- {:ok, activity}
+ }
+ [to, context, object, %{}]
end
+
+ ActivityPub.create(to, user, context, object, additional, data)
end
def fetch_friend_statuses(user, opts \\ %{}) do
@@ -78,6 +96,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
def fetch_public_statuses(user, opts \\ %{}) do
+ opts = Map.put(opts, "local_only", true)
+ ActivityPub.fetch_public_activities(opts)
+ |> activities_to_statuses(%{for: user})
+ end
+
+ def fetch_public_and_external_statuses(user, opts \\ %{}) do
ActivityPub.fetch_public_activities(opts)
|> activities_to_statuses(%{for: user})
end
@@ -93,18 +117,12 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
def fetch_conversation(user, id) do
- query = from activity in Activity,
- where: fragment("? @> ?", activity.data, ^%{ statusnetConversationId: id}),
- limit: 1
-
- with %Activity{} = activity <- Repo.one(query),
- context <- activity.data["context"],
+ with context when is_binary(context) <- conversation_id_to_context(id),
activities <- ActivityPub.fetch_activities_for_context(context),
statuses <- activities |> activities_to_statuses(%{for: user})
do
statuses
- else e ->
- IO.inspect(e)
+ else _e ->
[]
end
end
@@ -116,28 +134,23 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
def follow(%User{} = follower, params) do
- with { :ok, %User{} = followed } <- get_user(params),
- { :ok, follower } <- User.follow(follower, followed),
- { :ok, activity } <- ActivityPub.insert(%{
- "type" => "Follow",
- "actor" => follower.ap_id,
- "object" => followed.ap_id,
- "published" => make_date()
- })
+ with {:ok, %User{} = followed} <- get_user(params),
+ {:ok, follower} <- User.follow(follower, followed),
+ {:ok, activity} <- ActivityPub.follow(follower, followed)
do
- { :ok, follower, followed, activity }
+ {:ok, follower, followed, activity}
else
err -> err
end
end
-def unfollow(%User{} = follower, params) do
+ def unfollow(%User{} = follower, params) do
with { :ok, %User{} = unfollowed } <- get_user(params),
{ :ok, follower, follow_activity } <- User.unfollow(follower, unfollowed),
{ :ok, _activity } <- ActivityPub.insert(%{
"type" => "Undo",
"actor" => follower.ap_id,
- "object" => follow_activity, # get latest Follow for these users
+ "object" => follow_activity.data["id"], # get latest Follow for these users
"published" => make_date()
})
do
@@ -232,24 +245,6 @@ def unfollow(%User{} = follower, params) do
Enum.reduce(mentions, text, fn ({match, %User{ap_id: ap_id}}, text) -> String.replace(text, match, "<a href='#{ap_id}'>#{match}</a>") end)
end
- defp add_conversation_id(activity) do
- if is_integer(activity.data["statusnetConversationId"]) do
- {:ok, activity}
- else
- data = activity.data
- |> put_in(["object", "statusnetConversationId"], activity.id)
- |> put_in(["statusnetConversationId"], activity.id)
-
- object = Object.get_by_ap_id(activity.data["object"]["id"])
-
- changeset = Ecto.Changeset.change(object, data: data["object"])
- Repo.update(changeset)
-
- changeset = Ecto.Changeset.change(activity, data: data)
- Repo.update(changeset)
- end
- end
-
def register_user(params) do
params = %{
nickname: params["nickname"],
@@ -268,20 +263,20 @@ def unfollow(%User{} = follower, params) do
{:error, changeset} ->
errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|> Poison.encode!
- {:error, %{error: errors}}
+ {:error, %{error: errors}}
end
end
def get_user(user \\ nil, params) do
case params do
- %{ "user_id" => user_id } ->
+ %{"user_id" => user_id} ->
case target = Repo.get(User, user_id) do
nil ->
{:error, "No user with such user_id"}
_ ->
{:ok, target}
end
- %{ "screen_name" => nickname } ->
+ %{"screen_name" => nickname} ->
case target = Repo.get_by(User, nickname: nickname) do
nil ->
{:error, "No user with such screen_name"}
@@ -337,4 +332,22 @@ def unfollow(%User{} = follower, params) do
defp make_date do
DateTime.utc_now() |> DateTime.to_iso8601
end
+
+ def context_to_conversation_id(context) do
+ with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
+ id
+ else _e ->
+ changeset = Object.context_mapping(context)
+ {:ok, %{id: id}} = Repo.insert(changeset)
+ id
+ end
+ end
+
+ def conversation_id_to_context(id) do
+ with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
+ context
+ else _e ->
+ {:error, "No such conversation"}
+ end
+ end
end
diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
index b5b829ca0..96a5f2151 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -2,8 +2,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller
alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter}
- alias Pleroma.{Repo, Activity}
+ alias Pleroma.{Web, Repo, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Ecto.Changeset
def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
response = user |> UserRepresenter.to_json(%{for: user})
@@ -15,7 +16,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def status_update(%{assigns: %{user: user}} = conn, %{"status" => status_text} = status_data) do
if status_text |> String.trim |> String.length != 0 do
media_ids = extract_media_ids(status_data)
- {:ok, activity} = TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids ))
+ {:ok, activity} = TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids))
conn
|> json_reply(200, ActivityRepresenter.to_json(activity, %{user: user}))
else
@@ -41,6 +42,14 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
+ def public_and_external_timeline(%{assigns: %{user: user}} = conn, params) do
+ statuses = TwitterAPI.fetch_public_and_external_statuses(user, params)
+ {:ok, json} = Poison.encode(statuses)
+
+ conn
+ |> json_reply(200, json)
+ end
+
def public_timeline(%{assigns: %{user: user}} = conn, params) do
statuses = TwitterAPI.fetch_public_statuses(user, params)
{:ok, json} = Poison.encode(statuses)
@@ -79,34 +88,34 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def follow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.follow(user, params) do
- { :ok, user, followed, _activity } ->
+ {:ok, user, followed, _activity} ->
response = followed |> UserRepresenter.to_json(%{for: user})
conn
|> json_reply(200, response)
- { :error, msg } -> forbidden_json_reply(conn, msg)
+ {:error, msg} -> forbidden_json_reply(conn, msg)
end
end
def unfollow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.unfollow(user, params) do
- { :ok, user, unfollowed, } ->
+ {:ok, user, unfollowed} ->
response = unfollowed |> UserRepresenter.to_json(%{for: user})
conn
|> json_reply(200, response)
- { :error, msg } -> forbidden_json_reply(conn, msg)
+ {:error, msg} -> forbidden_json_reply(conn, msg)
end
end
- def fetch_status(%{assigns: %{user: user}} = conn, %{ "id" => id }) do
- response = TwitterAPI.fetch_status(user, id) |> Poison.encode!
+ def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
+ response = Poison.encode!(TwitterAPI.fetch_status(user, id))
conn
|> json_reply(200, response)
end
- def fetch_conversation(%{assigns: %{user: user}} = conn, %{ "id" => id }) do
+ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
id = String.to_integer(id)
- response = TwitterAPI.fetch_conversation(user, id) |> Poison.encode!
+ response = Poison.encode!(TwitterAPI.fetch_conversation(user, id))
conn
|> json_reply(200, response)
@@ -132,8 +141,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def config(conn, _params) do
response = %{
site: %{
- name: Pleroma.Web.base_url,
- server: Pleroma.Web.base_url,
+ name: Web.base_url,
+ server: Web.base_url,
textlimit: -1
}
}
@@ -188,11 +197,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, object} = ActivityPub.upload(params)
- change = Ecto.Changeset.change(user, %{avatar: object.data})
+ change = Changeset.change(user, %{avatar: object.data})
{:ok, user} = Repo.update(change)
- response = UserRepresenter.to_map(user, %{for: user})
- |> Poison.encode!
+ response = Poison.encode!(UserRepresenter.to_map(user, %{for: user}))
conn
|> json_reply(200, response)