4fd649c92093575e6ba83e6abf1979ae2d6be122
[akkoma] / lib / pleroma / web / ostatus / ostatus.ex
1 defmodule Pleroma.Web.OStatus do
2 import Ecto.Query
3 require Logger
4
5 alias Pleroma.{Repo, User, Web}
6 alias Pleroma.Web.ActivityPub.ActivityPub
7
8 def feed_path(user) do
9 "#{user.ap_id}/feed.atom"
10 end
11
12 def pubsub_path(user) do
13 "#{Web.base_url}/push/hub/#{user.nickname}"
14 end
15
16 def salmon_path(user) do
17 "#{user.ap_id}/salmon"
18 end
19
20 def handle_incoming(xml_string) do
21 {doc, _rest} = :xmerl_scan.string(to_charlist(xml_string))
22
23 {:xmlObj, :string, object_type } = :xmerl_xpath.string('string(/entry/activity:object-type[1])', doc)
24
25 case object_type do
26 'http://activitystrea.ms/schema/1.0/note' ->
27 handle_note(doc)
28 _ ->
29 Logger.error("Couldn't parse incoming document")
30 end
31 end
32
33 # TODO
34 # Parse mention
35 # wire up replies
36 # Set correct context
37 # Set correct statusnet ids.
38 def handle_note(doc) do
39 content_html = string_from_xpath("/entry/content[1]", doc)
40
41 [author] = :xmerl_xpath.string('/entry/author[1]', doc)
42 {:ok, actor} = find_or_make_user(author)
43
44 context = ActivityPub.generate_context_id
45
46 to = [
47 "https://www.w3.org/ns/activitystreams#Public"
48 ]
49
50 date = string_from_xpath("/entry/published", doc)
51
52 object = %{
53 "type" => "Note",
54 "to" => to,
55 "content" => content_html,
56 "published" => date,
57 "context" => context,
58 "actor" => actor.ap_id
59 }
60
61 ActivityPub.create(to, actor, context, object, %{}, date)
62 end
63
64 def find_or_make(author, doc) do
65 query = from user in User,
66 where: user.local == false and fragment("? @> ?", user.info, ^%{ostatus_uri: author})
67
68 user = Repo.one(query)
69
70 if is_nil(user) do
71 make_user(doc)
72 else
73 {:ok, user}
74 end
75 end
76
77 def find_or_make_user(author_doc) do
78 {:xmlObj, :string, uri } = :xmerl_xpath.string('string(/author[1]/uri)', author_doc)
79
80 query = from user in User,
81 where: user.local == false and fragment("? @> ?", user.info, ^%{ostatus_uri: to_string(uri)})
82
83 user = Repo.one(query)
84
85 if is_nil(user) do
86 make_user(author_doc)
87 else
88 {:ok, user}
89 end
90 end
91
92 defp string_from_xpath(xpath, doc) do
93 {:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc)
94
95 res = res
96 |> to_string
97 |> String.trim
98
99 if res == "", do: nil, else: res
100 end
101
102 def make_user(author_doc) do
103 author = string_from_xpath("/author[1]/uri", author_doc)
104 name = string_from_xpath("/author[1]/name", author_doc)
105 preferredUsername = string_from_xpath("/author[1]/poco:preferredUsername", author_doc)
106 displayName = string_from_xpath("/author[1]/poco:displayName", author_doc)
107 avatar = make_avatar_object(author_doc)
108
109 data = %{
110 local: false,
111 name: preferredUsername || name,
112 nickname: displayName || name,
113 ap_id: author,
114 info: %{
115 "ostatus_uri" => author,
116 "host" => URI.parse(author).host,
117 "system" => "ostatus"
118 },
119 avatar: avatar
120 }
121
122 Repo.insert(Ecto.Changeset.change(%User{}, data))
123 end
124
125 # TODO: Just takes the first one for now.
126 defp make_avatar_object(author_doc) do
127 href = string_from_xpath("/author[1]/link[@rel=\"avatar\"]/@href", author_doc)
128 type = string_from_xpath("/author[1]/link[@rel=\"avatar\"]/@type", author_doc)
129
130 if href do
131 %{
132 "type" => "Image",
133 "url" =>
134 [%{
135 "type" => "Link",
136 "mediaType" => type,
137 "href" => href
138 }]
139 }
140 else
141 nil
142 end
143 end
144 end