Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Pleroma
pleroma
Commits
e42bc5f5
Commit
e42bc5f5
authored
May 20, 2020
by
lain
Browse files
Announcements: Handle through common pipeline.
parent
c7cdc553
Changes
11
Hide whitespace changes
Inline
Side-by-side
lib/pleroma/web/activity_pub/activity_pub.ex
View file @
e42bc5f5
...
...
@@ -356,36 +356,6 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
end
end
@spec
announce
(
User
.
t
(),
Object
.
t
(),
String
.
t
()
|
nil
,
boolean
(),
boolean
())
::
{
:ok
,
Activity
.
t
(),
Object
.
t
()}
|
{
:error
,
any
()}
def
announce
(
%
User
{
ap_id:
_
}
=
user
,
%
Object
{
data:
%{
"id"
=>
_
}}
=
object
,
activity_id
\\
nil
,
local
\\
true
,
public
\\
true
)
do
with
{
:ok
,
result
}
<-
Repo
.
transaction
(
fn
->
do_announce
(
user
,
object
,
activity_id
,
local
,
public
)
end
)
do
result
end
end
defp
do_announce
(
user
,
object
,
activity_id
,
local
,
public
)
do
with
true
<-
is_announceable?
(
object
,
user
,
public
),
object
<-
Object
.
get_by_id
(
object
.
id
),
announce_data
<-
make_announce_data
(
user
,
object
,
activity_id
,
public
),
{
:ok
,
activity
}
<-
insert
(
announce_data
,
local
),
{
:ok
,
object
}
<-
add_announce_to_object
(
activity
,
object
),
_
<-
notify_and_stream
(
activity
),
:ok
<-
maybe_federate
(
activity
)
do
{
:ok
,
activity
,
object
}
else
false
->
{
:error
,
false
}
{
:error
,
error
}
->
Repo
.
rollback
(
error
)
end
end
@spec
follow
(
User
.
t
(),
User
.
t
(),
String
.
t
()
|
nil
,
boolean
())
::
{
:ok
,
Activity
.
t
()}
|
{
:error
,
any
()}
def
follow
(
follower
,
followed
,
activity_id
\\
nil
,
local
\\
true
)
do
...
...
lib/pleroma/web/activity_pub/builder.ex
View file @
e42bc5f5
...
...
@@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias
Pleroma
.
Web
.
ActivityPub
.
Utils
alias
Pleroma
.
Web
.
ActivityPub
.
Visibility
require
Pleroma
.
Constants
@spec
emoji_react
(
User
.
t
(),
Object
.
t
(),
String
.
t
())
::
{
:ok
,
map
(),
keyword
()}
def
emoji_react
(
actor
,
object
,
emoji
)
do
with
{
:ok
,
data
,
meta
}
<-
object_action
(
actor
,
object
)
do
...
...
@@ -83,9 +85,17 @@ def like(actor, object) do
end
end
def
announce
(
actor
,
object
)
do
def
announce
(
actor
,
object
,
options
\\
[])
do
public?
=
Keyword
.
get
(
options
,
:public
,
false
)
to
=
[
actor
.
follower_address
,
object
.
data
[
"actor"
]]
to
=
if
public?
do
[
Pleroma
.
Constants
.
as_public
()
|
to
]
else
to
end
{
:ok
,
%{
"id"
=>
Utils
.
generate_activity_id
(),
...
...
@@ -93,7 +103,8 @@ def announce(actor, object) do
"object"
=>
object
.
data
[
"id"
],
"to"
=>
to
,
"context"
=>
object
.
data
[
"context"
],
"type"
=>
"Announce"
"type"
=>
"Announce"
,
"published"
=>
Utils
.
make_date
()
},
[]}
end
...
...
lib/pleroma/web/activity_pub/object_validator.ex
View file @
e42bc5f5
...
...
@@ -88,7 +88,7 @@ def fetch_actor(object) do
def
fetch_actor_and_object
(
object
)
do
fetch_actor
(
object
)
Object
.
normalize
(
object
[
"object"
])
Object
.
normalize
(
object
[
"object"
]
,
true
)
:ok
end
end
lib/pleroma/web/activity_pub/object_validators/announce_validator.ex
View file @
e42bc5f5
...
...
@@ -18,9 +18,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
field
(
:type
,
:string
)
field
(
:object
,
Types
.
ObjectID
)
field
(
:actor
,
Types
.
ObjectID
)
field
(
:context
,
:string
)
field
(
:context
,
:string
,
autogenerate:
{
Utils
,
:generate_context_id
,
[]}
)
field
(
:to
,
Types
.
Recipients
,
default:
[])
field
(
:cc
,
Types
.
Recipients
,
default:
[])
field
(
:published
,
Types
.
DateTime
)
end
def
cast_and_validate
(
data
)
do
...
...
@@ -47,7 +48,7 @@ def fix_after_cast(cng) do
def
validate_data
(
data_cng
)
do
data_cng
|>
validate_inclusion
(
:type
,
[
"Announce"
])
|>
validate_required
([
:id
,
:type
,
:object
,
:actor
,
:context
,
:to
,
:cc
])
|>
validate_required
([
:id
,
:type
,
:object
,
:actor
,
:to
,
:cc
])
|>
validate_actor_presence
()
|>
validate_object_presence
()
|>
validate_existing_announce
()
...
...
lib/pleroma/web/activity_pub/side_effects.ex
View file @
e42bc5f5
...
...
@@ -27,6 +27,18 @@ def handle(%{data: %{"type" => "Like"}} = object, meta) do
{
:ok
,
object
,
meta
}
end
# Tasks this handles:
# - Add announce to object
# - Set up notification
def
handle
(%{
data:
%{
"type"
=>
"Announce"
}}
=
object
,
meta
)
do
announced_object
=
Object
.
get_by_ap_id
(
object
.
data
[
"object"
])
Utils
.
add_announce_to_object
(
object
,
announced_object
)
Notification
.
create_notifications
(
object
)
{
:ok
,
object
,
meta
}
end
def
handle
(%{
data:
%{
"type"
=>
"Undo"
,
"object"
=>
undone_object
}}
=
object
,
meta
)
do
with
undone_object
<-
Activity
.
get_by_ap_id
(
undone_object
),
:ok
<-
handle_undoing
(
undone_object
)
do
...
...
lib/pleroma/web/activity_pub/transmogrifier.ex
View file @
e42bc5f5
...
...
@@ -662,7 +662,8 @@ def handle_incoming(
|>
handle_incoming
(
options
)
end
def
handle_incoming
(%{
"type"
=>
type
}
=
data
,
_options
)
when
type
in
[
"Like"
,
"EmojiReact"
]
do
def
handle_incoming
(%{
"type"
=>
type
}
=
data
,
_options
)
when
type
in
[
"Like"
,
"EmojiReact"
,
"Announce"
]
do
with
:ok
<-
ObjectValidator
.
fetch_actor_and_object
(
data
),
{
:ok
,
activity
,
_meta
}
<-
Pipeline
.
common_pipeline
(
data
,
local:
false
)
do
...
...
@@ -672,22 +673,6 @@ def handle_incoming(%{"type" => type} = data, _options) when type in ["Like", "E
end
end
def
handle_incoming
(
%{
"type"
=>
"Announce"
,
"object"
=>
object_id
,
"actor"
=>
_actor
,
"id"
=>
id
}
=
data
,
_options
)
do
with
actor
<-
Containment
.
get_actor
(
data
),
{
_
,
{
:ok
,
%
User
{}
=
actor
}}
<-
{
:fetch_user
,
User
.
get_or_fetch_by_ap_id
(
actor
)},
{
_
,
{
:ok
,
object
}}
<-
{
:get_embedded
,
get_embedded_obj_helper
(
object_id
,
actor
)},
public
<-
Visibility
.
is_public?
(
data
),
{
_
,
{
:ok
,
activity
,
_object
}}
<-
{
:announce
,
ActivityPub
.
announce
(
actor
,
object
,
id
,
false
,
public
)}
do
{
:ok
,
activity
}
else
e
->
{
:error
,
e
}
end
end
def
handle_incoming
(
%{
"type"
=>
"Update"
,
"object"
=>
%{
"type"
=>
object_type
}
=
object
,
"actor"
=>
actor_id
}
=
data
,
...
...
lib/pleroma/web/common_api/common_api.ex
View file @
e42bc5f5
...
...
@@ -127,18 +127,19 @@ def delete(activity_id, user) do
end
def
repeat
(
id
,
user
,
params
\\
%{})
do
with
%
Activity
{
data:
%{
"type"
=>
"Create"
}}
=
activity
<-
Activity
.
get_by_id
(
id
)
do
object
=
Object
.
normalize
(
activity
)
announce_activity
=
Utils
.
get_existing_announce
(
user
.
ap_id
,
object
)
public
=
public_announce?
(
object
,
params
)
if
announce_activity
do
{
:ok
,
announce_activity
,
object
}
else
ActivityPub
.
announce
(
user
,
object
,
nil
,
true
,
public
)
end
with
%
Activity
{
data:
%{
"type"
=>
"Create"
}}
=
activity
<-
Activity
.
get_by_id
(
id
),
object
=
%
Object
{}
<-
Object
.
normalize
(
activity
,
false
),
{
_
,
nil
}
<-
{
:existing_announce
,
Utils
.
get_existing_announce
(
user
.
ap_id
,
object
)},
public
=
public_announce?
(
object
,
params
),
{
:ok
,
announce
,
_
}
<-
Builder
.
announce
(
user
,
object
,
public:
public
),
{
:ok
,
activity
,
_
}
<-
Pipeline
.
common_pipeline
(
announce
,
local:
true
)
do
{
:ok
,
activity
}
else
_
->
{
:error
,
:not_found
}
{
:existing_announce
,
%
Activity
{}
=
announce
}
->
{
:ok
,
announce
}
_
->
{
:error
,
:not_found
}
end
end
...
...
test/fixtures/mastodon-note-object.json
View file @
e42bc5f5
{
"@context"
:[
"https://www.w3.org/ns/activitystreams"
,
"https://w3id.org/security/v1"
,{
"manuallyApprovesFollowers"
:
"as:manuallyApprovesFollowers"
,
"sensitive"
:
"as:sensitive"
,
"movedTo"
:
"as:movedTo"
,
"Hashtag"
:
"as:Hashtag"
,
"ostatus"
:
"http://ostatus.org#"
,
"atomUri"
:
"ostatus:atomUri"
,
"inReplyToAtomUri"
:
"ostatus:inReplyToAtomUri"
,
"conversation"
:
"ostatus:conversation"
,
"toot"
:
"http://joinmastodon.org/ns#"
,
"Emoji"
:
"toot:Emoji"
}],
"id"
:
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
,
"type"
:
"Note"
,
"summary"
:
null
,
"content"
:
"
\u
003cp
\u
003eyeah.
\u
003c/p
\u
003e"
,
"inReplyTo"
:
null
,
"published"
:
"2018-02-17T17:46:20Z"
,
"url"
:
"http://mastodon.example.org/@admin/99541947525187367"
,
"attributedTo"
:
"http://mastodon.example.org/users/admin"
,
"to"
:[
"https://www.w3.org/ns/activitystreams#Public"
],
"cc"
:[
"http://mastodon.example.org/users/admin/followers"
],
"sensitive"
:
false
,
"atomUri"
:
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
,
"inReplyToAtomUri"
:
null
,
"conversation"
:
"tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation"
,
"tag"
:[],
"attachment"
:
[
{
"@context"
:
[
"https://www.w3.org/ns/activitystreams"
,
"https://w3id.org/security/v1"
,
{
"url"
:
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg"
,
"type"
:
"Document"
,
"name"
:
null
,
"mediaType"
:
"image/jpeg"
"Emoji"
:
"toot:Emoji"
,
"Hashtag"
:
"as:Hashtag"
,
"atomUri"
:
"ostatus:atomUri"
,
"conversation"
:
"ostatus:conversation"
,
"inReplyToAtomUri"
:
"ostatus:inReplyToAtomUri"
,
"manuallyApprovesFollowers"
:
"as:manuallyApprovesFollowers"
,
"movedTo"
:
"as:movedTo"
,
"ostatus"
:
"http://ostatus.org#"
,
"sensitive"
:
"as:sensitive"
,
"toot"
:
"http://joinmastodon.org/ns#"
}
]}
],
"atomUri"
:
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
,
"attachment"
:
[
{
"mediaType"
:
"image/jpeg"
,
"name"
:
null
,
"type"
:
"Document"
,
"url"
:
"http://mastodon.example.org/system/media_attachments/files/000/000/002/original/334ce029e7bfb920.jpg"
}
],
"attributedTo"
:
"http://mastodon.example.org/users/admin"
,
"cc"
:
[
"http://mastodon.example.org/users/admin/followers"
],
"content"
:
"<p>yeah.</p>"
,
"conversation"
:
"tag:mastodon.example.org,2018-02-17:objectId=59:objectType=Conversation"
,
"id"
:
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
,
"inReplyTo"
:
null
,
"inReplyToAtomUri"
:
null
,
"published"
:
"2018-02-17T17:46:20Z"
,
"sensitive"
:
false
,
"summary"
:
null
,
"tag"
:
[],
"to"
:
[
"https://www.w3.org/ns/activitystreams#Public"
],
"type"
:
"Note"
,
"url"
:
"http://mastodon.example.org/@admin/99541947525187367"
}
test/web/activity_pub/side_effects_test.exs
View file @
e42bc5f5
...
...
@@ -289,4 +289,29 @@ test "creates a notification", %{like: like, poster: poster} do
assert
Repo
.
get_by
(
Notification
,
user_id:
poster
.
id
,
activity_id:
like
.
id
)
end
end
describe
"announce objects"
do
setup
do
poster
=
insert
(
:user
)
user
=
insert
(
:user
)
{
:ok
,
post
}
=
CommonAPI
.
post
(
poster
,
%{
status:
"hey"
})
{
:ok
,
announce_data
,
_meta
}
=
Builder
.
announce
(
user
,
post
.
object
)
{
:ok
,
announce
,
_meta
}
=
ActivityPub
.
persist
(
announce_data
,
local:
true
)
%{
announce:
announce
,
user:
user
,
poster:
poster
}
end
test
"add the announce to the original object"
,
%{
announce:
announce
,
user:
user
}
do
{
:ok
,
announce
,
_
}
=
SideEffects
.
handle
(
announce
)
object
=
Object
.
get_by_ap_id
(
announce
.
data
[
"object"
])
assert
object
.
data
[
"announcement_count"
]
==
1
assert
user
.
ap_id
in
object
.
data
[
"announcements"
]
end
test
"creates a notification"
,
%{
announce:
announce
,
poster:
poster
}
do
{
:ok
,
announce
,
_
}
=
SideEffects
.
handle
(
announce
)
assert
Repo
.
get_by
(
Notification
,
user_id:
poster
.
id
,
activity_id:
announce
.
id
)
end
end
end
test/web/activity_pub/transmogrifier/announce_handling_test.exs
View file @
e42bc5f5
...
...
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnnounceHandlingTest do
import
Pleroma
.
Factory
test
"it works for incoming honk announces"
do
_
user
=
insert
(
:user
,
ap_id:
"https://honktest/u/test"
,
local:
false
)
user
=
insert
(
:user
,
ap_id:
"https://honktest/u/test"
,
local:
false
)
other_user
=
insert
(
:user
)
{
:ok
,
post
}
=
CommonAPI
.
post
(
other_user
,
%{
status:
"bonkeronk"
})
...
...
@@ -28,6 +28,11 @@ test "it works for incoming honk announces" do
}
{
:ok
,
%
Activity
{
local:
false
}}
=
Transmogrifier
.
handle_incoming
(
announce
)
object
=
Object
.
get_by_ap_id
(
post
.
data
[
"object"
])
assert
length
(
object
.
data
[
"announcements"
])
==
1
assert
user
.
ap_id
in
object
.
data
[
"announcements"
]
end
test
"it works for incoming announces with actor being inlined (kroeg)"
do
...
...
@@ -48,8 +53,15 @@ test "it works for incoming announces with actor being inlined (kroeg)" do
end
test
"it works for incoming announces, fetching the announced object"
do
Tesla
.
Mock
.
mock_global
(
fn
env
->
apply
(
HttpRequestMock
,
:request
,
[
env
])
end
)
data
=
File
.
read!
(
"test/fixtures/mastodon-announce.json"
)
|>
Poison
.
decode!
()
data
=
File
.
read!
(
"test/fixtures/mastodon-announce.json"
)
|>
Poison
.
decode!
()
|>
Map
.
put
(
"object"
,
"http://mastodon.example.org/users/admin/statuses/99541947525187367"
)
Tesla
.
Mock
.
mock
(
fn
%{
method:
:get
}
->
%
Tesla
.
Env
{
status:
200
,
body:
File
.
read!
(
"test/fixtures/mastodon-note-object.json"
)}
end
)
_user
=
insert
(
:user
,
local:
false
,
ap_id:
data
[
"actor"
])
...
...
@@ -92,6 +104,8 @@ test "it works for incoming announces with an existing activity" do
assert
Activity
.
get_create_by_object_ap_id
(
data
[
"object"
])
.
id
==
activity
.
id
end
# Ignore inlined activities for now
@tag
skip:
true
test
"it works for incoming announces with an inlined activity"
do
data
=
File
.
read!
(
"test/fixtures/mastodon-announce-private.json"
)
...
...
test/web/common_api/common_api_test.exs
View file @
e42bc5f5
...
...
@@ -416,7 +416,8 @@ test "repeating a status" do
{
:ok
,
activity
}
=
CommonAPI
.
post
(
other_user
,
%{
status:
"cofe"
})
{
:ok
,
%
Activity
{},
_
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
{
:ok
,
%
Activity
{}
=
announce_activity
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
assert
Visibility
.
is_public?
(
announce_activity
)
end
test
"can't repeat a repeat"
do
...
...
@@ -424,9 +425,9 @@ test "can't repeat a repeat" do
other_user
=
insert
(
:user
)
{
:ok
,
activity
}
=
CommonAPI
.
post
(
other_user
,
%{
status:
"cofe"
})
{
:ok
,
%
Activity
{}
=
announce
,
_
}
=
CommonAPI
.
repeat
(
activity
.
id
,
other_user
)
{
:ok
,
%
Activity
{}
=
announce
}
=
CommonAPI
.
repeat
(
activity
.
id
,
other_user
)
refute
match?
({
:ok
,
%
Activity
{}
,
_
},
CommonAPI
.
repeat
(
announce
.
id
,
user
))
refute
match?
({
:ok
,
%
Activity
{}},
CommonAPI
.
repeat
(
announce
.
id
,
user
))
end
test
"repeating a status privately"
do
...
...
@@ -435,7 +436,7 @@ test "repeating a status privately" do
{
:ok
,
activity
}
=
CommonAPI
.
post
(
other_user
,
%{
status:
"cofe"
})
{
:ok
,
%
Activity
{}
=
announce_activity
,
_
}
=
{
:ok
,
%
Activity
{}
=
announce_activity
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
,
%{
visibility:
"private"
})
assert
Visibility
.
is_private?
(
announce_activity
)
...
...
@@ -458,8 +459,8 @@ test "retweeting a status twice returns the status" do
other_user
=
insert
(
:user
)
{
:ok
,
activity
}
=
CommonAPI
.
post
(
other_user
,
%{
status:
"cofe"
})
{
:ok
,
%
Activity
{}
=
announce
,
object
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
{
:ok
,
^
announce
,
^
object
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
{
:ok
,
%
Activity
{}
=
announce
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
{
:ok
,
^
announce
}
=
CommonAPI
.
repeat
(
activity
.
id
,
user
)
end
test
"favoriting a status twice returns ok, but without the like activity"
do
...
...
Claire
@Claire
mentioned in issue
#2422
·
Jan 15, 2021
mentioned in issue
#2422
mentioned in issue #2422
Toggle commit list
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment