2017-03-21 10:53:20 -06:00
defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
use Pleroma.DataCase
alias Pleroma.Builders . { UserBuilder , ActivityBuilder }
2017-09-15 06:17:36 -06:00
alias Pleroma.Web.TwitterAPI . { TwitterAPI , UserView }
alias Pleroma.Web.CommonAPI.Utils
2017-03-30 09:07:03 -06:00
alias Pleroma . { Activity , User , Object , Repo }
2017-06-19 15:12:37 -06:00
alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
2017-04-14 10:15:15 -06:00
alias Pleroma.Web.ActivityPub.ActivityPub
2017-03-21 10:53:20 -06:00
2017-04-13 08:19:07 -06:00
import Pleroma.Factory
2017-03-21 10:53:20 -06:00
test " create a status " do
2017-07-22 09:42:15 -06:00
user = insert ( :user )
2017-04-03 10:28:19 -06:00
_mentioned_user = UserBuilder . insert ( %{ nickname : " shp " , ap_id : " shp " } )
2017-03-30 09:07:03 -06:00
object_data = %{
" type " = > " Image " ,
" url " = > [
%{
" type " = > " Link " ,
" mediaType " = > " image/jpg " ,
" href " = > " http://example.org/image.jpg "
}
] ,
" uuid " = > 1
}
object = Repo . insert! ( % Object { data : object_data } )
2017-03-21 10:53:20 -06:00
input = %{
2017-11-20 09:53:21 -07:00
" status " = > " Hello again, @shp.<script></script> \n This is on another :moominmamma: line. # 2hu # epic # phantasmagoric " ,
2017-03-30 09:07:03 -06:00
" media_ids " = > [ object . id ]
2017-03-21 10:53:20 -06:00
}
{ :ok , activity = % Activity { } } = TwitterAPI . create_status ( user , input )
2017-12-11 09:41:03 -07:00
assert get_in ( activity . data , [ " object " , " content " ] ) == " Hello again, <span><a href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :moominmamma: line. # 2hu # epic # phantasmagoric<br><a href= \" http://example.org/image.jpg \" class='attachment'>image.jpg</a> "
2017-03-21 11:17:35 -06:00
assert get_in ( activity . data , [ " object " , " type " ] ) == " Note "
2017-04-13 06:56:19 -06:00
assert get_in ( activity . data , [ " object " , " actor " ] ) == user . ap_id
2017-04-03 10:28:19 -06:00
assert get_in ( activity . data , [ " actor " ] ) == user . ap_id
2017-03-21 11:17:35 -06:00
assert Enum . member? ( get_in ( activity . data , [ " to " ] ) , User . ap_followers ( user ) )
assert Enum . member? ( get_in ( activity . data , [ " to " ] ) , " https://www.w3.org/ns/activitystreams # Public " )
2017-04-03 10:28:19 -06:00
assert Enum . member? ( get_in ( activity . data , [ " to " ] ) , " shp " )
2017-05-02 02:43:35 -06:00
assert activity . local == true
2017-03-23 16:34:10 -06:00
2017-11-20 09:53:21 -07:00
assert %{ " moominmamma " = > " http://localhost:4001/finmoji/128px/moominmamma-128.png " } = activity . data [ " object " ] [ " emoji " ]
2017-05-18 07:16:49 -06:00
# hashtags
assert activity . data [ " object " ] [ " tag " ] == [ " 2hu " , " epic " , " phantasmagoric " ]
2017-04-30 06:02:04 -06:00
# Add a context
2017-03-23 16:34:10 -06:00
assert is_binary ( get_in ( activity . data , [ " context " ] ) )
assert is_binary ( get_in ( activity . data , [ " object " , " context " ] ) )
2017-03-30 09:07:03 -06:00
2017-03-30 10:07:38 -06:00
assert is_list ( activity . data [ " object " ] [ " attachment " ] )
2017-04-14 10:59:11 -06:00
assert activity . data [ " object " ] == Object . get_by_ap_id ( activity . data [ " object " ] [ " id " ] ) . data
2017-07-22 09:42:15 -06:00
user = User . get_by_ap_id ( user . ap_id )
assert user . info [ " note_count " ] == 1
2017-03-23 16:34:10 -06:00
end
test " create a status that is a reply " do
2017-07-22 09:42:15 -06:00
user = insert ( :user )
2017-03-23 16:34:10 -06:00
input = %{
" status " = > " Hello again. "
}
{ :ok , activity = % Activity { } } = TwitterAPI . create_status ( user , input )
input = %{
" status " = > " Here's your (you). " ,
" in_reply_to_status_id " = > activity . id
}
{ :ok , reply = % Activity { } } = TwitterAPI . create_status ( user , input )
assert get_in ( reply . data , [ " context " ] ) == get_in ( activity . data , [ " context " ] )
assert get_in ( reply . data , [ " object " , " context " ] ) == get_in ( activity . data , [ " object " , " context " ] )
assert get_in ( reply . data , [ " object " , " inReplyTo " ] ) == get_in ( activity . data , [ " object " , " id " ] )
assert get_in ( reply . data , [ " object " , " inReplyToStatusId " ] ) == activity . id
2017-07-22 09:42:15 -06:00
assert Enum . member? ( get_in ( reply . data , [ " to " ] ) , user . ap_id )
2017-03-21 10:53:20 -06:00
end
2017-05-02 06:12:43 -06:00
test " fetch public statuses, excluding remote ones. " do
2017-03-21 10:53:20 -06:00
%{ public : activity , user : user } = ActivityBuilder . public_and_non_public
2017-05-02 06:12:43 -06:00
insert ( :note_activity , %{ local : false } )
2017-04-16 07:28:28 -06:00
follower = insert ( :user , following : [ User . ap_followers ( user ) ] )
2017-03-23 08:51:34 -06:00
statuses = TwitterAPI . fetch_public_statuses ( follower )
2017-03-21 10:53:20 -06:00
assert length ( statuses ) == 1
2017-03-23 08:51:34 -06:00
assert Enum . at ( statuses , 0 ) == ActivityRepresenter . to_map ( activity , %{ user : user , for : follower } )
2017-03-21 10:53:20 -06:00
end
2017-03-22 09:51:20 -06:00
2017-05-02 06:12:43 -06:00
test " fetch whole known network statuses " do
%{ public : activity , user : user } = ActivityBuilder . public_and_non_public
insert ( :note_activity , %{ local : false } )
follower = insert ( :user , following : [ User . ap_followers ( user ) ] )
statuses = TwitterAPI . fetch_public_and_external_statuses ( follower )
assert length ( statuses ) == 2
assert Enum . at ( statuses , 0 ) == ActivityRepresenter . to_map ( activity , %{ user : user , for : follower } )
end
2017-03-22 09:51:20 -06:00
test " fetch friends' statuses " do
2017-04-16 07:28:28 -06:00
user = insert ( :user , %{ following : [ " someguy/followers " ] } )
2017-03-22 09:51:20 -06:00
{ :ok , activity } = ActivityBuilder . insert ( %{ " to " = > [ " someguy/followers " ] } )
2017-04-16 07:28:28 -06:00
{ :ok , direct_activity } = ActivityBuilder . insert ( %{ " to " = > [ user . ap_id ] } )
2017-03-22 09:51:20 -06:00
statuses = TwitterAPI . fetch_friend_statuses ( user )
activity_user = Repo . get_by ( User , ap_id : activity . data [ " actor " ] )
2017-04-16 07:28:28 -06:00
direct_activity_user = Repo . get_by ( User , ap_id : direct_activity . data [ " actor " ] )
2017-03-22 09:51:20 -06:00
2017-04-12 08:45:23 -06:00
assert length ( statuses ) == 2
2017-03-22 09:51:20 -06:00
assert Enum . at ( statuses , 0 ) == ActivityRepresenter . to_map ( activity , %{ user : activity_user } )
2017-04-16 07:28:28 -06:00
assert Enum . at ( statuses , 1 ) == ActivityRepresenter . to_map ( direct_activity , %{ user : direct_activity_user , mentioned : [ user ] } )
2017-03-22 09:51:20 -06:00
end
2017-03-22 11:36:08 -06:00
2017-04-20 04:53:53 -06:00
test " fetch user's mentions " do
user = insert ( :user )
{ :ok , activity } = ActivityBuilder . insert ( %{ " to " = > [ user . ap_id ] } )
activity_user = Repo . get_by ( User , ap_id : activity . data [ " actor " ] )
statuses = TwitterAPI . fetch_mentions ( user )
assert length ( statuses ) == 1
assert Enum . at ( statuses , 0 ) == ActivityRepresenter . to_map ( activity , %{ user : activity_user , mentioned : [ user ] } )
2017-03-22 09:51:20 -06:00
end
2017-03-22 11:36:08 -06:00
2017-04-16 07:44:30 -06:00
test " get a user by params " do
user1_result = { :ok , user1 } = UserBuilder . insert ( %{ ap_id : " some id " , email : " test@pleroma " } )
{ :ok , user2 } = UserBuilder . insert ( %{ ap_id : " some other id " , nickname : " testname2 " , email : " test2@pleroma " } )
2017-04-16 08:05:48 -06:00
assert { :error , " You need to specify screen_name or user_id " } == TwitterAPI . get_user ( nil , nil )
2017-04-16 07:44:30 -06:00
assert user1_result == TwitterAPI . get_user ( nil , %{ " user_id " = > user1 . id } )
2017-06-12 09:12:55 -06:00
assert user1_result == TwitterAPI . get_user ( nil , %{ " user_id " = > user1 . nickname } )
2017-04-16 07:44:30 -06:00
assert user1_result == TwitterAPI . get_user ( nil , %{ " screen_name " = > user1 . nickname } )
assert user1_result == TwitterAPI . get_user ( user1 , nil )
assert user1_result == TwitterAPI . get_user ( user2 , %{ " user_id " = > user1 . id } )
assert user1_result == TwitterAPI . get_user ( user2 , %{ " screen_name " = > user1 . nickname } )
assert { :error , " No user with such screen_name " } == TwitterAPI . get_user ( nil , %{ " screen_name " = > " Satan " } )
assert { :error , " No user with such user_id " } == TwitterAPI . get_user ( nil , %{ " user_id " = > 666 } )
end
2017-04-14 07:09:13 -06:00
test " fetch user's statuses " do
2017-04-16 07:44:30 -06:00
{ :ok , user1 } = UserBuilder . insert ( %{ ap_id : " some id " , email : " test@pleroma " } )
{ :ok , user2 } = UserBuilder . insert ( %{ ap_id : " some other id " , nickname : " testname2 " , email : " test2@pleroma " } )
2017-04-14 07:09:13 -06:00
{ :ok , status1 } = ActivityBuilder . insert ( %{ " id " = > 1 } , %{ user : user1 } )
{ :ok , status2 } = ActivityBuilder . insert ( %{ " id " = > 2 } , %{ user : user2 } )
2017-04-16 07:44:30 -06:00
user1_statuses = TwitterAPI . fetch_user_statuses ( user1 , %{ " actor_id " = > user1 . ap_id } )
2017-04-14 07:09:13 -06:00
assert length ( user1_statuses ) == 1
assert Enum . at ( user1_statuses , 0 ) == ActivityRepresenter . to_map ( status1 , %{ user : user1 } )
2017-04-16 07:44:30 -06:00
user2_statuses = TwitterAPI . fetch_user_statuses ( user1 , %{ " actor_id " = > user2 . ap_id } )
2017-04-14 07:09:13 -06:00
assert length ( user2_statuses ) == 1
assert Enum . at ( user2_statuses , 0 ) == ActivityRepresenter . to_map ( status2 , %{ user : user2 } )
end
2017-03-23 18:16:28 -06:00
test " fetch a single status " do
{ :ok , activity } = ActivityBuilder . insert ( )
{ :ok , user } = UserBuilder . insert ( )
actor = Repo . get_by! ( User , ap_id : activity . data [ " actor " ] )
status = TwitterAPI . fetch_status ( user , activity . id )
assert status == ActivityRepresenter . to_map ( activity , %{ for : user , user : actor } )
end
2017-04-10 07:38:21 -06:00
test " Follow another user using user_id " do
2017-04-16 07:28:28 -06:00
user = insert ( :user )
2017-04-16 08:18:34 -06:00
followed = insert ( :user )
2017-03-22 11:36:08 -06:00
2017-04-20 01:39:18 -06:00
{ :ok , user , followed , _activity } = TwitterAPI . follow ( user , %{ " user_id " = > followed . id } )
2017-11-19 22:52:02 -07:00
assert User . ap_followers ( followed ) in user . following
2017-03-22 11:36:08 -06:00
2017-04-20 01:39:18 -06:00
{ :error , msg } = TwitterAPI . follow ( user , %{ " user_id " = > followed . id } )
assert msg == " Could not follow user: #{ followed . nickname } is already on your list. "
2017-04-10 07:38:21 -06:00
end
test " Follow another user using screen_name " do
2017-04-16 08:12:28 -06:00
user = insert ( :user )
2017-04-20 01:39:18 -06:00
followed = insert ( :user )
2017-03-22 11:36:08 -06:00
2017-04-20 01:39:18 -06:00
{ :ok , user , followed , _activity } = TwitterAPI . follow ( user , %{ " screen_name " = > followed . nickname } )
2017-11-19 22:52:02 -07:00
assert User . ap_followers ( followed ) in user . following
2017-04-12 08:34:36 -06:00
2017-07-22 09:42:15 -06:00
followed = User . get_by_ap_id ( followed . ap_id )
assert followed . info [ " follower_count " ] == 1
2017-04-20 01:39:18 -06:00
{ :error , msg } = TwitterAPI . follow ( user , %{ " screen_name " = > followed . nickname } )
2017-04-12 08:34:36 -06:00
assert msg == " Could not follow user: #{ followed . nickname } is already on your list. "
2017-03-22 11:36:08 -06:00
end
2017-03-23 06:13:09 -06:00
2017-04-20 01:46:27 -06:00
test " Unfollow another user using user_id " do
unfollowed = insert ( :user )
user = insert ( :user , %{ following : [ User . ap_followers ( unfollowed ) ] } )
2017-05-07 11:28:23 -06:00
ActivityPub . follow ( user , unfollowed )
2017-04-10 07:45:47 -06:00
2017-04-20 01:46:27 -06:00
{ :ok , user , unfollowed } = TwitterAPI . unfollow ( user , %{ " user_id " = > unfollowed . id } )
2017-04-10 07:45:47 -06:00
assert user . following == [ ]
2017-03-23 06:13:09 -06:00
2017-04-20 01:46:27 -06:00
{ :error , msg } = TwitterAPI . unfollow ( user , %{ " user_id " = > unfollowed . id } )
assert msg == " Not subscribed! "
2017-04-10 07:45:47 -06:00
end
2017-03-23 06:13:09 -06:00
2017-04-10 07:45:47 -06:00
test " Unfollow another user using screen_name " do
2017-04-20 01:46:27 -06:00
unfollowed = insert ( :user )
user = insert ( :user , %{ following : [ User . ap_followers ( unfollowed ) ] } )
2017-03-23 06:13:09 -06:00
2017-05-07 11:28:23 -06:00
ActivityPub . follow ( user , unfollowed )
2017-04-20 01:46:27 -06:00
{ :ok , user , unfollowed } = TwitterAPI . unfollow ( user , %{ " screen_name " = > unfollowed . nickname } )
2017-03-23 06:13:09 -06:00
assert user . following == [ ]
2017-04-20 01:46:27 -06:00
{ :error , msg } = TwitterAPI . unfollow ( user , %{ " screen_name " = > unfollowed . nickname } )
2017-04-12 08:34:36 -06:00
assert msg == " Not subscribed! "
2017-03-23 06:13:09 -06:00
end
2017-03-28 09:22:44 -06:00
2017-11-07 15:04:53 -07:00
test " Block another user using user_id " do
user = insert ( :user )
blocked = insert ( :user )
{ :ok , user , blocked } = TwitterAPI . block ( user , %{ " user_id " = > blocked . id } )
assert User . blocks? ( user , blocked )
end
test " Block another user using screen_name " do
user = insert ( :user )
blocked = insert ( :user )
{ :ok , user , blocked } = TwitterAPI . block ( user , %{ " screen_name " = > blocked . nickname } )
assert User . blocks? ( user , blocked )
end
test " Unblock another user using user_id " do
unblocked = insert ( :user )
user = insert ( :user )
User . block ( user , unblocked )
{ :ok , user , unblocked } = TwitterAPI . unblock ( user , %{ " user_id " = > unblocked . id } )
assert user . info [ " blocks " ] == [ ]
end
test " Unblock another user using screen_name " do
unblocked = insert ( :user )
user = insert ( :user )
User . block ( user , unblocked )
{ :ok , user , unblocked } = TwitterAPI . unblock ( user , %{ " screen_name " = > unblocked . nickname } )
assert user . info [ " blocks " ] == [ ]
end
2017-03-28 09:22:44 -06:00
test " fetch statuses in a context using the conversation id " do
{ :ok , user } = UserBuilder . insert ( )
2017-06-30 08:41:09 -06:00
{ :ok , activity } = ActivityBuilder . insert ( %{ " type " = > " Create " , " context " = > " 2hu " } )
{ :ok , activity_two } = ActivityBuilder . insert ( %{ " type " = > " Create " , " context " = > " 2hu " } )
{ :ok , _activity_three } = ActivityBuilder . insert ( %{ " type " = > " Create " , " context " = > " 3hu " } )
2017-03-28 09:22:44 -06:00
2017-04-30 05:53:26 -06:00
{ :ok , object } = Object . context_mapping ( " 2hu " ) |> Repo . insert
statuses = TwitterAPI . fetch_conversation ( user , object . id )
2017-03-28 09:22:44 -06:00
assert length ( statuses ) == 2
2017-06-30 08:30:19 -06:00
assert Enum . at ( statuses , 1 ) [ " id " ] == activity . id
assert Enum . at ( statuses , 0 ) [ " id " ] == activity_two . id
2017-03-28 09:22:44 -06:00
end
2017-03-28 18:05:51 -06:00
test " upload a file " do
file = % Plug.Upload { content_type : " image/jpg " , path : Path . absname ( " test/fixtures/image.jpg " ) , filename : " an_image.jpg " }
response = TwitterAPI . upload ( file )
assert is_binary ( response )
end
2017-04-03 10:28:19 -06:00
test " it adds user links to an existing text " do
2017-05-15 10:25:21 -06:00
text = " @gsimg According to @archaeme, that is @daggsy. Also hello @archaeme@archae.me "
2017-04-03 10:28:19 -06:00
2017-04-16 07:28:28 -06:00
gsimg = insert ( :user , %{ nickname : " gsimg " } )
archaeme = insert ( :user , %{ nickname : " archaeme " } )
2017-05-15 10:25:21 -06:00
archaeme_remote = insert ( :user , %{ nickname : " archaeme@archae.me " } )
2017-04-03 10:28:19 -06:00
2017-06-14 05:58:56 -06:00
mentions = Pleroma.Formatter . parse_mentions ( text )
2017-12-11 09:41:03 -07:00
expected_text = " <span><a href=' #{ gsimg . ap_id } '>@<span>gsimg</span></a></span> According to <span><a href=' #{ archaeme . ap_id } '>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a href=' #{ archaeme_remote . ap_id } '>@<span>archaeme</span></a></span> "
2017-04-03 10:28:19 -06:00
2017-05-17 10:00:20 -06:00
assert Utils . add_user_links ( text , mentions ) == expected_text
2017-04-03 10:28:19 -06:00
end
2017-04-13 08:19:07 -06:00
test " it favorites a status, returns the updated status " do
user = insert ( :user )
note_activity = insert ( :note_activity )
activity_user = Repo . get_by! ( User , ap_id : note_activity . data [ " actor " ] )
2017-09-09 10:09:37 -06:00
{ :ok , status } = TwitterAPI . fav ( user , note_activity . id )
2017-04-13 08:19:07 -06:00
updated_activity = Activity . get_by_ap_id ( note_activity . data [ " id " ] )
assert status == ActivityRepresenter . to_map ( updated_activity , %{ user : activity_user , for : user } )
end
2017-04-14 09:13:51 -06:00
2017-04-14 10:15:15 -06:00
test " it unfavorites a status, returns the updated status " do
user = insert ( :user )
note_activity = insert ( :note_activity )
activity_user = Repo . get_by! ( User , ap_id : note_activity . data [ " actor " ] )
object = Object . get_by_ap_id ( note_activity . data [ " object " ] [ " id " ] )
2017-04-14 10:27:17 -06:00
{ :ok , _like_activity , _object } = ActivityPub . like ( user , object )
2017-04-14 10:15:15 -06:00
updated_activity = Activity . get_by_ap_id ( note_activity . data [ " id " ] )
assert ActivityRepresenter . to_map ( updated_activity , %{ user : activity_user , for : user } ) [ " fave_num " ] == 1
2017-09-09 10:30:02 -06:00
{ :ok , status } = TwitterAPI . unfav ( user , note_activity . id )
2017-04-14 10:15:15 -06:00
assert status [ " fave_num " ] == 0
end
2017-04-15 05:54:46 -06:00
test " it retweets a status and returns the retweet " do
user = insert ( :user )
note_activity = insert ( :note_activity )
activity_user = Repo . get_by! ( User , ap_id : note_activity . data [ " actor " ] )
2017-09-09 09:48:57 -06:00
{ :ok , status } = TwitterAPI . repeat ( user , note_activity . id )
2017-04-15 05:54:46 -06:00
updated_activity = Activity . get_by_ap_id ( note_activity . data [ " id " ] )
assert status == ActivityRepresenter . to_map ( updated_activity , %{ user : activity_user , for : user } )
end
2017-04-16 02:25:27 -06:00
test " it registers a new user and returns the user. " do
data = %{
" nickname " = > " lain " ,
" email " = > " lain@wired.jp " ,
" fullname " = > " lain iwakura " ,
" bio " = > " close the world. " ,
" password " = > " bear " ,
" confirm " = > " bear "
}
{ :ok , user } = TwitterAPI . register_user ( data )
fetched_user = Repo . get_by ( User , nickname : " lain " )
2017-06-19 15:12:37 -06:00
assert UserView . render ( " show.json " , %{ user : user } ) == UserView . render ( " show.json " , %{ user : fetched_user } )
2017-04-16 02:25:27 -06:00
end
test " it returns the error on registration problems " do
data = %{
" nickname " = > " lain " ,
" email " = > " lain@wired.jp " ,
" fullname " = > " lain iwakura " ,
" bio " = > " close the world. " ,
" password " = > " bear "
}
{ :error , error_object } = TwitterAPI . register_user ( data )
assert is_binary ( error_object [ :error ] )
refute Repo . get_by ( User , nickname : " lain " )
end
2017-04-30 05:53:26 -06:00
test " it assigns an integer conversation_id " do
note_activity = insert ( :note_activity )
user = User . get_cached_by_ap_id ( note_activity . data [ " actor " ] )
status = ActivityRepresenter . to_map ( note_activity , %{ user : user } )
assert is_number ( status [ " statusnet_conversation_id " ] )
end
2017-04-14 09:13:51 -06:00
setup do
2017-04-17 03:36:17 -06:00
Supervisor . terminate_child ( Pleroma.Supervisor , Cachex )
Supervisor . restart_child ( Pleroma.Supervisor , Cachex )
2017-04-14 09:13:51 -06:00
:ok
end
2017-04-30 05:53:26 -06:00
describe " context_to_conversation_id " do
test " creates a mapping object " do
conversation_id = TwitterAPI . context_to_conversation_id ( " random context " )
object = Object . get_by_ap_id ( " random context " )
assert conversation_id == object . id
end
test " returns an existing mapping for an existing object " do
{ :ok , object } = Object . context_mapping ( " random context " ) |> Repo . insert
conversation_id = TwitterAPI . context_to_conversation_id ( " random context " )
assert conversation_id == object . id
end
end
2017-05-10 10:44:57 -06:00
describe " fetching a user by uri " do
test " fetches a user by uri " do
2017-06-18 09:20:39 -06:00
id = " https://mastodon.social/users/lambadalambda "
2017-05-10 10:44:57 -06:00
user = insert ( :user )
2017-06-18 09:20:39 -06:00
{ :ok , represented } = TwitterAPI . get_external_profile ( user , id )
remote = User . get_by_ap_id ( id )
2017-05-10 10:44:57 -06:00
2017-10-31 10:57:26 -06:00
assert represented [ " id " ] == UserView . render ( " show.json " , %{ user : remote , for : user } ) [ " id " ]
2017-06-18 09:20:39 -06:00
# Also fetches the feed.
2018-01-16 06:31:03 -07:00
# assert Activity.get_create_activity_by_object_ap_id("tag:mastodon.social,2017-04-05:objectId=1641750:objectType=Status")
2017-05-10 10:44:57 -06:00
end
end
2017-03-21 10:53:20 -06:00
end