359 lines
11 KiB
Elixir
359 lines
11 KiB
Elixir
defmodule Nulla.ActivityPub do
|
|
@spec context() :: list()
|
|
defp context do
|
|
[
|
|
"https://www.w3.org/ns/activitystreams",
|
|
"https://w3id.org/security/v1",
|
|
Jason.OrderedObject.new(
|
|
manuallyApprovesFollowers: "as:manuallyApprovesFollowers",
|
|
toot: "http://joinmastodon.org/ns#",
|
|
featured: %{"@id" => "toot:featured", "@type" => "@id"},
|
|
featuredTags: %{"@id" => "toot:featuredTags", "@type" => "@id"},
|
|
alsoKnownAs: %{"@id" => "as:alsoKnownAs", "@type" => "@id"},
|
|
movedTo: %{"@id" => "as:movedTo", "@type" => "@id"},
|
|
schema: "http://schema.org#",
|
|
PropertyValue: "schema:PropertyValue",
|
|
value: "schema:value",
|
|
discoverable: "toot:discoverable",
|
|
suspended: "toot:suspended",
|
|
memorial: "toot:memorial",
|
|
indexable: "toot:indexable",
|
|
attributionDomains: %{"@id" => "toot:attributionDomains", "@type" => "@id"},
|
|
Hashtag: "as:Hashtag",
|
|
vcard: "http://www.w3.org/2006/vcard/ns#"
|
|
)
|
|
]
|
|
end
|
|
|
|
@spec user(String.t(), Nulla.Models.User.t()) :: Jason.OrderedObject.t()
|
|
def user(domain, user) do
|
|
Jason.OrderedObject.new(
|
|
"@context": context(),
|
|
id: "https://#{domain}/@#{user.username}",
|
|
type: "Person",
|
|
following: "https://#{domain}/@#{user.username}/following",
|
|
followers: "https://#{domain}/@#{user.username}/followers",
|
|
inbox: "https://#{domain}/@#{user.username}/inbox",
|
|
outbox: "https://#{domain}/@#{user.username}/outbox",
|
|
featured: "https://#{domain}/@#{user.username}/collections/featured",
|
|
preferredUsername: user.username,
|
|
name: user.realname,
|
|
summary: user.bio,
|
|
url: "https://#{domain}/@#{user.username}",
|
|
manuallyApprovesFollowers: user.follow_approval,
|
|
discoverable: user.is_discoverable,
|
|
indexable: user.is_indexable,
|
|
published: DateTime.to_iso8601(user.inserted_at),
|
|
memorial: user.is_memorial,
|
|
publicKey:
|
|
Jason.OrderedObject.new(
|
|
id: "https://#{domain}/@#{user.username}#main-key",
|
|
owner: "https://#{domain}/@#{user.username}",
|
|
publicKeyPem: user.public_key
|
|
),
|
|
tag:
|
|
Enum.map(user.tags, fn tag ->
|
|
Jason.OrderedObject.new(
|
|
type: "Hashtag",
|
|
href: "https://#{domain}/tags/#{tag}",
|
|
name: "##{tag}"
|
|
)
|
|
end),
|
|
attachment:
|
|
Enum.map(user.fields, fn {name, value} ->
|
|
Jason.OrderedObject.new(
|
|
type: "PropertyValue",
|
|
name: name,
|
|
value: value
|
|
)
|
|
end),
|
|
endpoints: Jason.OrderedObject.new(sharedInbox: "https://#{domain}/inbox"),
|
|
icon:
|
|
Jason.OrderedObject.new(
|
|
type: "Image",
|
|
mediaType: MIME.from_path(user.avatar),
|
|
url: "https://#{domain}/files/#{user.avatar}"
|
|
),
|
|
image:
|
|
Jason.OrderedObject.new(
|
|
type: "Image",
|
|
mediaType: MIME.from_path(user.banner),
|
|
url: "https://#{domain}/files/#{user.banner}"
|
|
),
|
|
"vcard:bday": user.birthday,
|
|
"vcard:Address": user.location
|
|
)
|
|
end
|
|
|
|
@spec note(String.t(), Nulla.Models.Note.t()) :: Jason.OrderedObject.t()
|
|
def note(domain, note) do
|
|
attachment =
|
|
case note.media_attachments do
|
|
[] ->
|
|
[]
|
|
|
|
attachments ->
|
|
[
|
|
attachment:
|
|
Enum.map(attachments, fn att ->
|
|
Jason.OrderedObject.new(
|
|
type: "Document",
|
|
mediaType: att.mime_type,
|
|
url: "https://#{domain}/files/#{att.file}"
|
|
)
|
|
end)
|
|
]
|
|
end
|
|
|
|
Jason.OrderedObject.new(
|
|
"@context": [
|
|
"https://www.w3.org/ns/activitystreams",
|
|
Jason.OrderedObject.new(sensitive: "as:sensitive")
|
|
],
|
|
id: "https://#{domain}/@#{note.user.username}/#{note.id}",
|
|
type: "Note",
|
|
summary: nil,
|
|
inReplyTo: nil,
|
|
published: note.inserted_at,
|
|
url: "https://#{domain}/@#{note.user.username}/#{note.id}",
|
|
attributedTo: "https://#{domain}/@#{note.user.username}",
|
|
to: [
|
|
"https://www.w3.org/ns/activitystreams#Public"
|
|
],
|
|
cc: [
|
|
"https://#{domain}/@#{note.user.username}/followers"
|
|
],
|
|
sensetive: false,
|
|
content: note.content,
|
|
contentMap: Jason.OrderedObject.new("#{note.language}": note.content),
|
|
attachment: attachment
|
|
)
|
|
end
|
|
|
|
@spec activity(String.t(), Nulla.Models.Activity.t()) :: Jason.OrderedObject.t()
|
|
def activity(domain, activity) do
|
|
Jason.OrderedObject.new(
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/activities/#{activity.id}",
|
|
type: activity.type,
|
|
actor: activity.actor,
|
|
object: activity.object
|
|
)
|
|
end
|
|
|
|
@spec following(String.t(), Nulla.Models.User.t(), Integer.t()) :: Jason.OrderedObject.t()
|
|
def following(domain, user, total) do
|
|
Jason.OrderedObject.new(
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/@#{user.username}/following",
|
|
type: "OrderedCollection",
|
|
totalItems: total,
|
|
first: "https://#{domain}/@#{user.username}/following?page=1"
|
|
)
|
|
end
|
|
|
|
@spec following(
|
|
String.t(),
|
|
Nulla.Models.User.t(),
|
|
Integer.t(),
|
|
List.t(),
|
|
Integer.t(),
|
|
Integer.t()
|
|
) :: Jason.OrderedObject.t()
|
|
def following(domain, user, total, following_list, page, offset)
|
|
when is_integer(page) and page > 0 do
|
|
data = [
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/@#{user.username}/following?page=#{page}",
|
|
type: "OrderedCollectionPage",
|
|
totalItems: total,
|
|
next: "https://#{domain}/@#{user.username}/following?page=#{page + 1}",
|
|
prev: "https://#{domain}/@#{user.username}/following?page=#{page - 1}",
|
|
partOf: "https://#{domain}/@#{user.username}/following",
|
|
orderedItems: following_list
|
|
]
|
|
|
|
data =
|
|
if page <= 1 do
|
|
Keyword.delete(data, :prev)
|
|
else
|
|
data
|
|
end
|
|
|
|
data =
|
|
if page * offset > total do
|
|
data
|
|
|> Keyword.delete(:next)
|
|
|> Keyword.delete(:prev)
|
|
else
|
|
data
|
|
end
|
|
|
|
Jason.OrderedObject.new(data)
|
|
end
|
|
|
|
@spec followers(String.t(), Nulla.Models.User.t(), Integer.t()) :: Jason.OrderedObject.t()
|
|
def followers(domain, user, total) do
|
|
Jason.OrderedObject.new(
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/@#{user.username}/followers",
|
|
type: "OrderedCollection",
|
|
totalItems: total,
|
|
first: "https://#{domain}/@#{user.username}/followers?page=1"
|
|
)
|
|
end
|
|
|
|
@spec followers(
|
|
String.t(),
|
|
Nulla.Models.User.t(),
|
|
Integer.t(),
|
|
List.t(),
|
|
Integer.t(),
|
|
Integer.t()
|
|
) :: Jason.OrderedObject.t()
|
|
def followers(domain, user, total, followers_list, page, offset)
|
|
when is_integer(page) and page > 0 do
|
|
data = [
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/@#{user.username}/followers?page=#{page}",
|
|
type: "OrderedCollectionPage",
|
|
totalItems: total,
|
|
next: "https://#{domain}/@#{user.username}/followers?page=#{page + 1}",
|
|
prev: "https://#{domain}/@#{user.username}/followers?page=#{page - 1}",
|
|
partOf: "https://#{domain}/@#{user.username}/followers",
|
|
orderedItems: followers_list
|
|
]
|
|
|
|
data =
|
|
if page <= 1 do
|
|
Keyword.delete(data, :prev)
|
|
else
|
|
data
|
|
end
|
|
|
|
data =
|
|
if page * offset > total do
|
|
data
|
|
|> Keyword.delete(:next)
|
|
|> Keyword.delete(:prev)
|
|
else
|
|
data
|
|
end
|
|
|
|
Jason.OrderedObject.new(data)
|
|
end
|
|
|
|
@spec webfinger(String.t(), String.t(), String.t()) :: Jason.OrderedObject.t()
|
|
def webfinger(domain, username, resource) do
|
|
Jason.OrderedObject.new(
|
|
subject: resource,
|
|
links: [
|
|
Jason.OrderedObject.new(
|
|
rel: "self",
|
|
type: "application/activity+json",
|
|
href: "https://#{domain}/@#{username}"
|
|
)
|
|
]
|
|
)
|
|
end
|
|
|
|
@spec nodeinfo(String.t()) :: Jason.OrderedObject.t()
|
|
def nodeinfo(domain) do
|
|
Jason.OrderedObject.new(
|
|
links: [
|
|
Jason.OrderedObject.new(
|
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
|
|
href: "https://#{domain}/nodeinfo/2.0"
|
|
)
|
|
]
|
|
)
|
|
end
|
|
|
|
@spec nodeinfo(String.t(), Map.t(), Nulla.Models.InstanceSettings.t()) ::
|
|
Jason.OrderedObject.t()
|
|
def nodeinfo(version, users, instance) do
|
|
Jason.OrderedObject.new(
|
|
version: "2.0",
|
|
software:
|
|
Jason.OrderedObject.new(
|
|
name: "nulla",
|
|
version: version
|
|
),
|
|
protocols: [
|
|
"activitypub"
|
|
],
|
|
services:
|
|
Jason.OrderedObject.new(
|
|
outbound: [],
|
|
inbound: []
|
|
),
|
|
usage:
|
|
Jason.OrderedObject.new(
|
|
users:
|
|
Jason.OrderedObject.new(
|
|
total: users.total,
|
|
activeMonth: users.month,
|
|
activeHalfyear: users.halfyear
|
|
)
|
|
),
|
|
openRegistrations: instance.registration,
|
|
metadata:
|
|
Jason.OrderedObject.new(
|
|
nodeName: instance.name,
|
|
nodeDescription: instance.description
|
|
)
|
|
)
|
|
end
|
|
|
|
@spec outbox(String.t(), String.t(), Integer.t()) :: Jason.OrderedObject.t()
|
|
def outbox(domain, username, total) do
|
|
Jason.OrderedObject.new(
|
|
"@context": "https://www.w3.org/ns/activitystreams",
|
|
id: "https://#{domain}/@#{username}/outbox",
|
|
type: "OrderedCollection",
|
|
totalItems: total,
|
|
first: "https://#{domain}/@#{username}/outbox?page=true",
|
|
last: "https://#{domain}/@#{username}/outbox?min_id=0&page=true"
|
|
)
|
|
end
|
|
|
|
@spec outbox(String.t(), Integer.t(), Integer.t(), String.t(), List.t()) ::
|
|
Jason.OrderedObject.t()
|
|
def outbox(domain, username, max_id, min_id, items) do
|
|
Jason.OrderedObject.new(
|
|
"@context": [
|
|
"https://www.w3.org/ns/activitystreams",
|
|
Jason.OrderedObject.new(
|
|
sensitive: "as:sensitive",
|
|
Hashtag: "as:Hashtag"
|
|
)
|
|
],
|
|
id: "https://#{domain}/@#{username}/outbox?page=true",
|
|
type: "OrderedCollectionPage",
|
|
next: "https://#{domain}/@#{username}/outbox?max_id=#{max_id}&page=true",
|
|
prev: "https://#{domain}/@#{username}/outbox?min_id=#{min_id}&page=true",
|
|
partOf: "https://#{domain}/@#{username}/outbox",
|
|
orderedItems: items
|
|
)
|
|
end
|
|
|
|
@spec render_activity(String.t(), Note.t()) :: Jason.OrderedObject.t()
|
|
def render_activity(domain, note) do
|
|
Jason.OrderedObject.new(
|
|
id: "https://#{domain}/@#{note.user.username}/#{note.id}/activity",
|
|
type: "Create",
|
|
actor: "https://#{domain}/@#{note.user.username}",
|
|
published: note.inserted_at |> DateTime.to_iso8601(),
|
|
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
|
object:
|
|
Jason.OrderedObject.new(
|
|
id: "https://#{domain}/@#{note.user.username}/#{note.id}",
|
|
type: "Note",
|
|
content: note.content,
|
|
published: note.inserted_at |> DateTime.to_iso8601(),
|
|
attributedTo: "https://#{domain}/@#{note.user.username}",
|
|
to: ["https://www.w3.org/ns/activitystreams#Public"]
|
|
)
|
|
)
|
|
end
|
|
end
|