#include "mastodon_instance.h" #include "src/activitypub/appost.h" #include "src/activitypub/apquestion.h" #include "src/settings_interface.h" #include "src/types.h" #include #include #include #include #include APPost* post_from_json(const QJsonObject &status); MastodonInstance::MastodonInstance() : instance(SettingsInterface::quick_read_setting("net/instance/address").toStdString(), SettingsInterface::quick_read_setting("net/instance/token").toStdString()), connection(instance) { } MastodonInstance::~MastodonInstance() { if (obtain_token) delete obtain_token; } QString MastodonInstance::oauth2_step1() { obtain_token = new mastodonpp::Instance::ObtainToken{instance}; auto answer{obtain_token->step_1("ActorViewer", "read", "https://twilightsparkle.space")}; if (answer) return answer.body.c_str(); else return ""; } Instance::OAuth2Step2 MastodonInstance::oauth2_step2(const QString &auth_code) { if (!obtain_token) return {false}; auto answer{obtain_token->step_2(auth_code.toStdString())}; QString token; if (answer) { token = answer.body.c_str(); } delete obtain_token; obtain_token = nullptr; return {(bool)answer, token}; } APPost* MastodonInstance::get_post_from_url(const QString &url) { auto answer{connection.get(mastodonpp::API::v2::search, { {"q", QUrl::toPercentEncoding(url).toStdString()}, {"type", "statuses"}, {"limit", "1"}, {"resolve", "true"} })}; if (answer) { QJsonDocument doc = QJsonDocument::fromJson(answer.body.c_str()); QJsonObject obj = doc.object(); qDebug() << doc; if (obj.contains("statuses")) { QJsonObject status = obj["statuses"].toArray().first().toObject(); if (status.isEmpty()) return new APPost({.content="invalid"}); // Invalid return post_from_json(status); } } return new APPost({.content="connection error"}); } // Create a filled APPost object from a Status entity (https://docs.joinmastodon.org/entities/Status/) // We have to return a pointer as else I can't get derived classes of APPost to render properly: the additional methods are ignored. // Furthermore, using pointers removes unecessary copying, even if that can be reduced by return value optimization. APPost* post_from_json(const QJsonObject &status) { APObjectFields fields; bool is_question = status.contains("poll"); fields.by_actor = status["account"]["url"].toString(); fields.reply_to_url = status["in_reply_to_id"].toString(); // id ≠ url fields.languages.append(status["language"].toString()); fields.published = status["created_at"].toString(); fields.object_url = status["uri"].toString(); fields.web_url = status["url"].toString(); if (status["sensitive"].toBool(true)) fields.summary = status["spoiler_text"].toString(); if (status.contains("content")) fields.content = status["content"].toString(); if (status.contains("visibility")) { QString visibility = status["visibility"].toString(); if (visibility == "public") fields.visibility = StatusType::PUBLIC; else if (visibility == "unlisted") fields.visibility = StatusType::UNLISTED; else if (visibility == "private") fields.visibility = StatusType::PRIVATE; else if (visibility == "direct") fields.visibility = StatusType::DIRECT; else fields.visibility = StatusType::UNKNOWN; } if (QJsonArray medias = status["media_attachments"].toArray(); not medias.isEmpty()) { for (QJsonValueRef media : medias) { if (not media.isObject()) continue; QJsonObject attrib = media.toObject(); fields.attachments.push_back({ // FIXME: we use the preview to lower bandwidth but it would be nicer to have APAttachment fields for differencing between qualities. .path = attrib["preview_url"].toString(), // FIXME: we don't have any better attribute to work with .filename = attrib["id"].toString(), .media_type = attrib["type"].toString(), .name = attrib["description"].toString() // TODO: we don't use the blurhash yet… }); } } if (is_question) { QJsonObject poll = status["poll"].toObject(); fields.question.end_time = poll["expires_at"].toString(); fields.question.closed_time = poll["expires_at"].toString(); fields.question.total_votes = poll["votes_count"].toInt(); for (QJsonValueRef option : poll["options"].toArray()) { QJsonObject element = option.toObject(); fields.question.poll_options.push_back({element["title"].toString(), element["votes_count"].toInt()}); } // TODO: have an boolean attribute for defining if the poll is multiple choice or not, int for the voters_count and bool/int for `voted` and `own_votes` } if (is_question) return new APQuestion(fields); else return new APPost(fields); }