File indexing completed on 2024-12-08 12:55:17
0001 // SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu> 0002 // SPDX-License-Identifier: GPL-3.0-or-later 0003 0004 #pragma once 0005 0006 #include "admin/adminaccountinfo.h" 0007 #include "admin/federationinfo.h" 0008 #include "identity.h" 0009 #include "preferences.h" 0010 #include "timeline/post.h" 0011 0012 #include <QJsonDocument> 0013 #include <QJsonObject> 0014 #include <QObject> 0015 0016 class Attachment; 0017 class Notification; 0018 class QNetworkReply; 0019 class QHttpMultiPart; 0020 class QFile; 0021 class Preferences; 0022 class AccountConfig; 0023 0024 /// Represents an account, which could possibly be real or a mock for testing. 0025 /// Also handles most of the API work, and account actions. 0026 class AbstractAccount : public QObject 0027 { 0028 Q_OBJECT 0029 0030 Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged) 0031 0032 Q_PROPERTY(QString instanceUri READ instanceUri CONSTANT) 0033 Q_PROPERTY(int maxPostLength READ maxPostLength NOTIFY fetchedInstanceMetadata) 0034 Q_PROPERTY(int maxPollOptions READ maxPollOptions NOTIFY fetchedInstanceMetadata) 0035 Q_PROPERTY(QString instanceName READ instanceName NOTIFY fetchedInstanceMetadata) 0036 Q_PROPERTY(QUrl authorizeUrl READ getAuthorizeUrl NOTIFY registered) 0037 Q_PROPERTY(Identity *identity READ identity NOTIFY identityChanged) 0038 Q_PROPERTY(Preferences *preferences READ preferences CONSTANT) 0039 Q_PROPERTY(bool hasFollowRequests READ hasFollowRequests NOTIFY hasFollowRequestsChanged) 0040 0041 public: 0042 AbstractAccount(QObject *parent, const QString &instanceUri); 0043 AbstractAccount(QObject *parent); 0044 0045 /// Register the application to the mastodon server 0046 void registerApplication(const QString &appName, const QString &website); 0047 0048 /// Check if the application is registered 0049 /// \see registerApplication 0050 bool isRegistered() const; 0051 0052 /// Get the oauth2 authorization url 0053 Q_INVOKABLE QUrl getAuthorizeUrl() const; 0054 0055 /// Get the oauth2 token url 0056 QUrl getTokenUrl() const; 0057 0058 /// Set the oauth2 token 0059 Q_INVOKABLE void setToken(const QString &authcode); 0060 0061 /// Check if the account has a token set 0062 /// \see setToken 0063 bool haveToken() const; 0064 0065 /// Check if the account has a username yet 0066 bool hasName() const; 0067 0068 /// Check if the account has an instance uri set 0069 bool hasInstanceUrl() const; 0070 0071 /// Verifies the token with the instance and if successful, loads identity information for the account 0072 virtual void validateToken() = 0; 0073 0074 /// Returns the server-side preferences 0075 Preferences *preferences() const; 0076 0077 /// Return the username of the account 0078 /// \see setUsername 0079 QString username() const; 0080 0081 /// Sets the username for the account 0082 void setUsername(const QString &name); 0083 0084 /// Fetches instance-specific metadata like max post length, allowed content types, etc 0085 void fetchInstanceMetadata(); 0086 0087 /// Returns the instance URI 0088 /// \see setInstanceUri 0089 QString instanceUri() const; 0090 0091 /// Sets the instance URI for the account 0092 void setInstanceUri(const QString &instance_uri); 0093 0094 /// Returns the max allowable length of posts in characters 0095 size_t maxPostLength() const; 0096 0097 /// Returns the maximum number of poll options 0098 size_t maxPollOptions() const; 0099 0100 /// Returns the amount of characters that URLs take 0101 /// Any URL that appears in a post will only be counted by this limit 0102 size_t charactersReservedPerUrl() const; 0103 0104 /// Returns the title set by the instance 0105 QString instanceName() const; 0106 0107 /// Returns the identity of the account 0108 Identity *identity(); 0109 0110 /// Looks up an identity specific to this account (like relationships) using an accountId 0111 /// and optionally a JSON document containing identity information. 0112 std::shared_ptr<Identity> identityLookup(const QString &accountId, const QJsonObject &doc); 0113 0114 /// Checks if the accountId exists in the account's identity cache 0115 bool identityCached(const QString &accountId) const; 0116 0117 /// Get identity of the admin::account 0118 std::shared_ptr<AdminAccountInfo> adminIdentityLookup(const QString &accountId, const QJsonObject &doc); 0119 0120 /// Invalidates the account 0121 void invalidate(); 0122 0123 /// Favorite a post 0124 /// \see unfavorite 0125 void favorite(Post *p); 0126 0127 /// Unfavorite a post 0128 /// \see favorite 0129 void unfavorite(Post *p); 0130 0131 /// Boost (also known as reblog, or repeat) a post 0132 /// \see unrepeat 0133 void repeat(Post *p); 0134 0135 /// Unboost a post 0136 /// \see repeat 0137 void unrepeat(Post *p); 0138 0139 /// Bookmark a post 0140 /// \see unbookmark 0141 void bookmark(Post *p); 0142 0143 /// Unbookmark a post 0144 /// \see bookmark 0145 void unbookmark(Post *p); 0146 0147 /// Pin a post 0148 /// \see unpin 0149 void pin(Post *p); 0150 0151 /// Unpin a post 0152 /// \see pin 0153 void unpin(Post *p); 0154 0155 /// Returns a streaming url for \p stream 0156 QUrl streamingUrl(const QString &stream); 0157 0158 /// Invalidates a post 0159 void invalidatePost(Post *p); 0160 0161 /// Types of formatting that we may use is determined primarily by the server metadata, this is a simple enough 0162 /// way to determine what formats are accepted. 0163 enum AllowedContentType { PlainText = 1 << 0, Markdown = 1 << 1, Html = 1 << 2, BBCode = 1 << 3 }; 0164 0165 /// Return the allowed content types of the account's instance 0166 AllowedContentType allowedContentTypes() const 0167 { 0168 return m_allowedContentTypes; 0169 } 0170 0171 /// Return a well-formed URL of an API path 0172 QUrl apiUrl(const QString &path) const; 0173 0174 /// Make an HTTP GET request to the mastodon server 0175 /// \param url The url of the request 0176 /// \param authenticated Whether the request should be authentificated 0177 /// \param parent The parent object that calls get() or the callback belongs to 0178 /// \param callback The callback that should be executed if the request is successful 0179 /// \param errorCallback The callback that should be executed if the request is not successful 0180 virtual void get(const QUrl &url, 0181 bool authenticated, 0182 QObject *parent, 0183 std::function<void(QNetworkReply *)> callback, 0184 std::function<void(QNetworkReply *)> errorCallback = nullptr) = 0; 0185 0186 /// Make an HTTP POST request to the mastodon server 0187 /// \param url The url of the request 0188 /// \param doc The request body as JSON 0189 /// \param parent The parent object that calls get() or the callback belongs to 0190 /// \param callback The callback that should be executed if the request is successful 0191 virtual void post(const QUrl &url, 0192 const QJsonDocument &doc, 0193 bool authenticated, 0194 QObject *parent, 0195 std::function<void(QNetworkReply *)> callback, 0196 std::function<void(QNetworkReply *)> errorCallback = nullptr, 0197 QHash<QByteArray, QByteArray> headers = {}) = 0; 0198 0199 /// Make an HTTP POST request to the mastodon server 0200 /// \param url The url of the request 0201 /// \param doc The request body as form-data 0202 /// \param authenticated Whether the request should be authentificated 0203 /// \param parent The parent object that calls get() or the callback belongs to 0204 /// \param callback The callback that should be executed if the request is successful 0205 virtual void post(const QUrl &url, const QUrlQuery &formdata, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0206 0207 virtual QNetworkReply *post(const QUrl &url, QHttpMultiPart *message, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0208 virtual void put(const QUrl &url, const QJsonDocument &doc, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0209 virtual void patch(const QUrl &url, QHttpMultiPart *multiPart, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)>) = 0; 0210 virtual void deleteResource(const QUrl &url, bool authenticated, QObject *parent, std::function<void(QNetworkReply *)> callback) = 0; 0211 0212 /// Upload a file 0213 virtual QNetworkReply *upload(const QUrl &filename, std::function<void(QNetworkReply *)> callback) = 0; 0214 0215 /// Write account to settings 0216 virtual void writeToSettings() = 0; 0217 0218 /// Read account from settings 0219 virtual void buildFromSettings(const AccountConfig &settings) = 0; 0220 0221 /// Check if the account has any follow requests 0222 virtual bool hasFollowRequests() const = 0; 0223 0224 /// Check against the server for any new follow requests 0225 virtual void checkForFollowRequests() = 0; 0226 0227 /// Follow the given account. Can also be used to update whether to show reblogs or enable notifications. 0228 /// @param Identity identity The account to follow 0229 /// @param bool reblogs Receive this account's reblogs in home timeline? Defaults to true. 0230 /// @param bool notify Receive notifications when this account posts a status? Defaults to false. 0231 Q_INVOKABLE void followAccount(Identity *identity, bool reblogs = true, bool notify = false); 0232 0233 /// Unfollow the given account. 0234 /// @param Identity identity The account to unfollow 0235 Q_INVOKABLE void unfollowAccount(Identity *identity); 0236 0237 /// Block the given account. 0238 /// @param Identity identity The account to block 0239 Q_INVOKABLE void blockAccount(Identity *identity); 0240 0241 /// Unblock the given account. 0242 /// @param Identity identity The account to unblock 0243 Q_INVOKABLE void unblockAccount(Identity *identity); 0244 0245 /// Mute the given account. 0246 /// @param Identity identity The account to mute 0247 /// @param bool notifications Whether notifications should also be muted, by default true 0248 /// @param int duration How long the mute should last, in seconds. Defaults to 0 (indefinite). 0249 Q_INVOKABLE void muteAccount(Identity *identity, bool notifications = true, int duration = 0); 0250 0251 /// Unmute the given account. 0252 /// @param Identity identity The account to unmute 0253 Q_INVOKABLE void unmuteAccount(Identity *identity); 0254 0255 /// Add the given account to the user's featured profiles. 0256 /// @param Identity identity The account to feature 0257 Q_INVOKABLE void featureAccount(Identity *identity); 0258 0259 /// Remove the given account from the user's featured profiles. 0260 /// @param Identity identity The account to unfeature 0261 Q_INVOKABLE void unfeatureAccount(Identity *identity); 0262 0263 /// Sets a private note on a user. 0264 /// @param Identity identity The account to annotate 0265 /// @param QString note The note to add to the account. Leave empty to remove the existing note. 0266 Q_INVOKABLE void addNote(Identity *identity, const QString ¬e); 0267 0268 /// Returns the preferred settings group name for this Account which includes the username and the instance uri. 0269 QString settingsGroupName() const; 0270 0271 /// Returns the preferred key name for the client secret 0272 QString clientSecretKey() const; 0273 0274 /// Returns the preferred key name for the access token 0275 QString accessTokenKey() const; 0276 0277 /// Type of account action 0278 enum AccountAction { 0279 Follow, ///< Follow the account 0280 Unfollow, ///< Unfollow the account 0281 Block, ///< Block the account 0282 Unblock, ///< Unlock the account 0283 Mute, ///< Mute the account 0284 Unmute, ///< Unmute the account 0285 Feature, ///< Feature the account 0286 Unfeature, ///< Unfeature the account 0287 Note, ///< Update the note for the account 0288 }; 0289 0290 /// Type of streaming event 0291 enum StreamingEventType { 0292 UpdateEvent, ///< A new Status has appeared. 0293 DeleteEvent, ///< A status has been deleted. 0294 NotificationEvent, ///< A new notification has appeared. 0295 FiltersChangedEvent, ///< Keyword filters have been changed. 0296 ConversationEvent, ///< A direct conversation has been updated. 0297 AnnouncementEvent, ///< An announcement has been published. 0298 AnnouncementRedactedEvent, ///< An announcement has received an emoji reaction. 0299 AnnouncementDeletedEvent, ///< An announcement has been deleted. 0300 StatusUpdatedEvent, ///< A Status has been edited. 0301 EncryptedMessageChangedEvent, ///< An encrypted message has been received. 0302 }; 0303 0304 Q_SIGNALS: 0305 /// Emitted when the account is authenticated 0306 /// \see validateToken 0307 void authenticated(bool successful); 0308 0309 /// Emitted when the application is successfully registered to the server 0310 /// \see registerApplication 0311 void registered(); 0312 0313 /// Emitted when the account's own identity has been updated 0314 void identityChanged(); 0315 0316 /// Emitted when the requested timeline has been fetched 0317 void fetchedTimeline(const QString &timelineName, QList<Post *> posts); 0318 0319 /// Emitted when th=e account has been invalidated 0320 /// \see invalidate 0321 void invalidated(); 0322 0323 /// Emitted when the account's username has been changed 0324 /// \see setUsername 0325 void usernameChanged(); 0326 0327 /// Emitted when the instance metadata has been fetched 0328 /// \see fetchInstanceMetadata 0329 void fetchedInstanceMetadata(); 0330 0331 /// Emitted when a post has been invalidated 0332 /// \see invalidatePost 0333 void invalidatedPost(Post *p); 0334 0335 /// Emitted when a notification has been received 0336 void notification(std::shared_ptr<Notification> n); 0337 0338 /// Emitted when an error occurred when performing an API request 0339 void errorOccured(const QString &errorMessage); 0340 0341 /// Emitted when a streaming event has been received 0342 void streamingEvent(AbstractAccount::StreamingEventType eventType, const QByteArray &payload); 0343 0344 /// Emitted when the account has follow requests 0345 void hasFollowRequestsChanged(); 0346 0347 protected: 0348 QString m_name; 0349 QString m_instance_uri; 0350 QString m_token; 0351 QString m_client_id; 0352 QString m_client_secret; 0353 size_t m_maxPostLength; 0354 size_t m_maxPollOptions; 0355 size_t m_charactersReservedPerUrl; 0356 QString m_instance_name; 0357 std::shared_ptr<Identity> m_identity; 0358 std::shared_ptr<AdminAccountInfo> m_adminIdentity; 0359 AdminAccountInfo *m_federationIdentity; 0360 AllowedContentType m_allowedContentTypes; 0361 Preferences *m_preferences = nullptr; 0362 0363 // OAuth authorization 0364 QUrlQuery buildOAuthQuery() const; 0365 0366 // updates and notifications 0367 void handleUpdate(const QJsonDocument &doc, const QString &target); 0368 void handleNotification(const QJsonDocument &doc); 0369 0370 void mutatePost(Post *p, const QString &verb, bool deliver_home = false); 0371 QMap<QString, std::shared_ptr<Identity>> m_identityCache; 0372 QMap<QString, std::shared_ptr<AdminAccountInfo>> m_adminIdentityCache; 0373 0374 void executeAction(Identity *i, AccountAction accountAction, const QJsonObject &extraArguments = {}); 0375 };