179 lines
5.6 KiB
Nim
Executable File
179 lines
5.6 KiB
Nim
Executable File
# SPDX-License-Identifier: AGPL-3.0-only
|
|
import strutils, strformat, options, json, sequtils, times
|
|
import ".."/[types, formatters, utils]
|
|
|
|
proc formatTweetForMastoAPI*(tweet: Tweet, cfg: Config, prefs: Prefs): string =
|
|
var content = replaceUrls(tweet.text, prefs)
|
|
|
|
if tweet.quote.isSome():
|
|
let
|
|
quote = get(tweet.quote)
|
|
quoteContent = replaceUrls(quote.text, prefs)
|
|
quoteUrl = &"{getUrlPrefix(cfg)}/i/status/{quote.id}"
|
|
content &= &"\n\n<blockquote><b>↘ <a href=\"{quoteUrl}\">{quote.user.fullName} (@{quote.user.username})</a></b>\n{quoteContent}"
|
|
|
|
if quote.video.isSome() or quote.gif.isSome():
|
|
content &= "\n📹"
|
|
if quote.gif.isSome():
|
|
content &= " (GIF)"
|
|
elif quote.photos.len > 0:
|
|
content &= "\n🖼️"
|
|
if quote.photos.len > 1:
|
|
content &= &" ({quote.photos.len})"
|
|
|
|
content &= "</blockquote>"
|
|
|
|
if tweet.birdwatch.isSome():
|
|
let
|
|
note = get(tweet.birdwatch)
|
|
noteContent = replaceUrls(note.text, prefs)
|
|
content &= &"\n<blockquote><b>ⓘ {note.title}</b>\n{noteContent}</blockquote>"
|
|
|
|
result = content.replace("\n", "<br>")
|
|
|
|
proc getActivityStream*(tweet: Tweet, cfg: Config, prefs: Prefs): JsonNode =
|
|
let
|
|
tweetUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.id}"
|
|
tweetContent = formatTweetForMastoAPI(tweet, cfg, prefs)
|
|
var media: seq[JsonNode] = @[]
|
|
|
|
if tweet.photos.len > 0:
|
|
for url in tweet.photos:
|
|
let image = getUrlPrefix(cfg) & getPicUrl(url)
|
|
var mediaObj = newJObject()
|
|
|
|
mediaObj["type"] = %"Document"
|
|
mediaObj["mediaType"] = %"image/png"
|
|
mediaObj["url"] = %image
|
|
mediaObj["name"] = newJNull() # FIXME a11y
|
|
|
|
media.add(mediaObj)
|
|
|
|
if tweet.video.isSome():
|
|
let
|
|
videoObj = get(tweet.video)
|
|
vars = videoObj.variants.filterIt(it.contentType == mp4)
|
|
var mediaObj = newJObject()
|
|
|
|
mediaObj["type"] = %"Document"
|
|
mediaObj["mediaType"] = %"video/mp4"
|
|
mediaObj["url"] = %vars[^1].url
|
|
mediaObj["name"] = newJNull() # FIXME a11y
|
|
|
|
media.add(mediaObj)
|
|
elif tweet.gif.isSome():
|
|
let gif = get(tweet.gif)
|
|
var mediaObj = newJObject()
|
|
|
|
mediaObj["type"] = %"Document"
|
|
mediaObj["mediaType"] = %"video/mp4"
|
|
mediaObj["url"] = %(&"https://{gif.url}")
|
|
mediaObj["name"] = newJNull() # FIXME a11y
|
|
|
|
media.add(mediaObj)
|
|
|
|
var context: seq[JsonNode] = @[]
|
|
let contextUrl: JsonNode = %"https://www.w3.org/ns/activitystreams"
|
|
context.add(contextUrl)
|
|
let asProps: JsonNode = %*{
|
|
"ostatus": "http://ostatus.org#",
|
|
"atomUri": "ostatus:atomUri",
|
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
|
"conversation": "ostatus:conversation",
|
|
"sensitive": "as:sensitive",
|
|
}
|
|
context.add(asProps)
|
|
|
|
var postJson = newJObject()
|
|
postJson["@context"] = %context
|
|
postJson["id"] = %tweetUrl
|
|
postJson["type"] = %"Note"
|
|
postJson["summary"] = newJNull()
|
|
if tweet.replyId != 0:
|
|
let replyUrl = &"{getUrlPrefix(cfg)}/i/status/{tweet.replyId}"
|
|
postJson["inReplyTo"] = %replyUrl
|
|
postJson["inReplyToAtomUri"] = %replyUrl
|
|
else:
|
|
postJson["inReplyTo"] = newJNull()
|
|
postJson["inReplyToAtomUri"] = newJNull()
|
|
postJson["published"] = %($tweet.time)
|
|
postJson["url"] = %tweetUrl
|
|
postJson["attributedTo"] = %(&"{getUrlPrefix(cfg)}/users/{tweet.user.username}")
|
|
postJson["to"] = newJArray()
|
|
postJson["cc"] = %(@["https://www.w3.org/ns/activitystreams#Public"])
|
|
postJson["sensitive"] = %false # FIXME
|
|
postJson["atomUri"] = %tweetUrl
|
|
postJson["conversation"] = %""
|
|
postJson["content"] = %tweetContent
|
|
postJson["contentMap"] = %*{
|
|
"en": tweetContent
|
|
}
|
|
postJson["attachment"] = %media
|
|
postJson["tag"] = newJArray() # TODO: parse?
|
|
postJson["replies"] = newJObject()
|
|
|
|
result = postJson
|
|
|
|
proc getActivityStream*(user: User, cfg: Config, prefs: Prefs): JsonNode =
|
|
let userUrl = &"{getUrlPrefix(cfg)}/{user.username}"
|
|
|
|
var context: seq[JsonNode] = @[]
|
|
let contextUrl: JsonNode = %"https://www.w3.org/ns/activitystreams"
|
|
context.add(contextUrl)
|
|
let contextUrl2: JsonNode = %"https://w3id.org/security/v1"
|
|
context.add(contextUrl2)
|
|
|
|
let contextAka: JsonNode = %*{
|
|
"@id": "as:alsoKnownAs",
|
|
"@type": "@id"
|
|
}
|
|
let contextMovedTo = %*{
|
|
"@id": "as:movedTo",
|
|
"@type": "@id"
|
|
}
|
|
var asProps: JsonNode = %*{
|
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
|
"schema": "http://schema.org#",
|
|
"PropertyValue": "schema:PropertyValue",
|
|
"value": "schema:value",
|
|
}
|
|
asProps["alsoKnownAs"] = contextAka
|
|
asProps["movedTo"] = contextMovedTo
|
|
context.add(asProps)
|
|
|
|
var userJson = newJObject()
|
|
userJson["@context"] = %context
|
|
userJson["id"] = %userUrl
|
|
userJson["type"] = %"Person"
|
|
userJson["following"] = %(userUrl & "/following")
|
|
userJson["followers"] = %(userUrl & "/followers")
|
|
userJson["inbox"] = newJNull()
|
|
userJson["outbox"] = newJNull()
|
|
userJson["featured"] = newJNull()
|
|
userJson["featuredTags"] = newJNull()
|
|
userJson["preferredUsername"] = %user.username
|
|
userJson["name"] = %user.fullname
|
|
userJson["summary"] = %user.bio
|
|
userJson["url"] = %userUrl
|
|
userJson["manuallyApprovesFollowers"] = %user.protected
|
|
userJson["discoverable"] = %true
|
|
userJson["indexable"] = %false
|
|
userJson["published"] = %($user.joinDate)
|
|
userJson["memorial"] = %false
|
|
userJson["publicKey"] = newJNull()
|
|
userJson["tag"] = newJArray()
|
|
userJson["attachment"] = newJArray()
|
|
userJson["endpoints"] = newJObject()
|
|
userJson["icon"] = %*{
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": getUrlPrefix(cfg) & getPicUrl(user.userPic)
|
|
}
|
|
userJson["image"] = %*{
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": getUrlPrefix(cfg) & getPicUrl(user.banner)
|
|
}
|
|
|
|
result = userJson
|