1 #pragma once 2 3 #include "common/Aliases.hpp" 4 #include "common/NetworkRequest.hpp" 5 #include "providers/twitch/TwitchEmotes.hpp" 6 7 #include <QJsonArray> 8 #include <QString> 9 #include <QStringList> 10 #include <QUrl> 11 #include <QUrlQuery> 12 #include <boost/noncopyable.hpp> 13 #include <boost/optional.hpp> 14 15 #include <functional> 16 #include <vector> 17 18 namespace chatterino { 19 20 using HelixFailureCallback = std::function<void()>; 21 template <typename... T> 22 using ResultCallback = std::function<void(T...)>; 23 24 struct HelixUser { 25 QString id; 26 QString login; 27 QString displayName; 28 QString createdAt; 29 QString description; 30 QString profileImageUrl; 31 int viewCount; 32 HelixUserchatterino::HelixUser33 explicit HelixUser(QJsonObject jsonObject) 34 : id(jsonObject.value("id").toString()) 35 , login(jsonObject.value("login").toString()) 36 , displayName(jsonObject.value("display_name").toString()) 37 , createdAt(jsonObject.value("created_at").toString()) 38 , description(jsonObject.value("description").toString()) 39 , profileImageUrl(jsonObject.value("profile_image_url").toString()) 40 , viewCount(jsonObject.value("view_count").toInt()) 41 { 42 } 43 }; 44 45 struct HelixUsersFollowsRecord { 46 QString fromId; 47 QString fromName; 48 QString toId; 49 QString toName; 50 QString followedAt; // date time object 51 HelixUsersFollowsRecordchatterino::HelixUsersFollowsRecord52 HelixUsersFollowsRecord() 53 : fromId("") 54 , fromName("") 55 , toId("") 56 , toName("") 57 , followedAt("") 58 { 59 } 60 HelixUsersFollowsRecordchatterino::HelixUsersFollowsRecord61 explicit HelixUsersFollowsRecord(QJsonObject jsonObject) 62 : fromId(jsonObject.value("from_id").toString()) 63 , fromName(jsonObject.value("from_name").toString()) 64 , toId(jsonObject.value("to_id").toString()) 65 , toName(jsonObject.value("to_name").toString()) 66 , followedAt(jsonObject.value("followed_at").toString()) 67 { 68 } 69 }; 70 71 struct HelixUsersFollowsResponse { 72 int total; 73 std::vector<HelixUsersFollowsRecord> data; HelixUsersFollowsResponsechatterino::HelixUsersFollowsResponse74 explicit HelixUsersFollowsResponse(QJsonObject jsonObject) 75 : total(jsonObject.value("total").toInt()) 76 { 77 const auto &jsonData = jsonObject.value("data").toArray(); 78 std::transform(jsonData.begin(), jsonData.end(), 79 std::back_inserter(this->data), 80 [](const QJsonValue &record) { 81 return HelixUsersFollowsRecord(record.toObject()); 82 }); 83 } 84 }; 85 86 struct HelixStream { 87 QString id; // stream id 88 QString userId; 89 QString userName; 90 QString gameId; 91 QString type; 92 QString title; 93 int viewerCount; 94 QString startedAt; 95 QString language; 96 QString thumbnailUrl; 97 HelixStreamchatterino::HelixStream98 HelixStream() 99 : id("") 100 , userId("") 101 , userName("") 102 , gameId("") 103 , type("") 104 , title("") 105 , viewerCount() 106 , startedAt("") 107 , language("") 108 , thumbnailUrl("") 109 { 110 } 111 HelixStreamchatterino::HelixStream112 explicit HelixStream(QJsonObject jsonObject) 113 : id(jsonObject.value("id").toString()) 114 , userId(jsonObject.value("user_id").toString()) 115 , userName(jsonObject.value("user_name").toString()) 116 , gameId(jsonObject.value("game_id").toString()) 117 , type(jsonObject.value("type").toString()) 118 , title(jsonObject.value("title").toString()) 119 , viewerCount(jsonObject.value("viewer_count").toInt()) 120 , startedAt(jsonObject.value("started_at").toString()) 121 , language(jsonObject.value("language").toString()) 122 , thumbnailUrl(jsonObject.value("thumbnail_url").toString()) 123 { 124 } 125 }; 126 127 struct HelixGame { 128 QString id; // stream id 129 QString name; 130 QString boxArtUrl; 131 HelixGamechatterino::HelixGame132 explicit HelixGame(QJsonObject jsonObject) 133 : id(jsonObject.value("id").toString()) 134 , name(jsonObject.value("name").toString()) 135 , boxArtUrl(jsonObject.value("box_art_url").toString()) 136 { 137 } 138 }; 139 140 struct HelixClip { 141 QString id; // clip slug 142 QString editUrl; 143 HelixClipchatterino::HelixClip144 explicit HelixClip(QJsonObject jsonObject) 145 : id(jsonObject.value("id").toString()) 146 , editUrl(jsonObject.value("edit_url").toString()) 147 { 148 } 149 }; 150 151 struct HelixChannel { 152 QString userId; 153 QString name; 154 QString language; 155 QString gameId; 156 QString gameName; 157 QString title; 158 HelixChannelchatterino::HelixChannel159 explicit HelixChannel(QJsonObject jsonObject) 160 : userId(jsonObject.value("broadcaster_id").toString()) 161 , name(jsonObject.value("broadcaster_name").toString()) 162 , language(jsonObject.value("broadcaster_language").toString()) 163 , gameId(jsonObject.value("game_id").toString()) 164 , gameName(jsonObject.value("game_name").toString()) 165 , title(jsonObject.value("title").toString()) 166 { 167 } 168 }; 169 170 struct HelixStreamMarker { 171 QString createdAt; 172 QString description; 173 QString id; 174 int positionSeconds; 175 HelixStreamMarkerchatterino::HelixStreamMarker176 explicit HelixStreamMarker(QJsonObject jsonObject) 177 : createdAt(jsonObject.value("created_at").toString()) 178 , description(jsonObject.value("description").toString()) 179 , id(jsonObject.value("id").toString()) 180 , positionSeconds(jsonObject.value("position_seconds").toInt()) 181 { 182 } 183 }; 184 185 struct HelixBlock { 186 QString userId; 187 QString userName; 188 QString displayName; 189 HelixBlockchatterino::HelixBlock190 explicit HelixBlock(QJsonObject jsonObject) 191 : userId(jsonObject.value("user_id").toString()) 192 , userName(jsonObject.value("user_login").toString()) 193 , displayName(jsonObject.value("display_name").toString()) 194 { 195 } 196 }; 197 198 struct HelixCheermoteImage { 199 Url imageURL1x; 200 Url imageURL2x; 201 Url imageURL4x; 202 HelixCheermoteImagechatterino::HelixCheermoteImage203 explicit HelixCheermoteImage(QJsonObject jsonObject) 204 : imageURL1x(Url{jsonObject.value("1").toString()}) 205 , imageURL2x(Url{jsonObject.value("2").toString()}) 206 , imageURL4x(Url{jsonObject.value("4").toString()}) 207 { 208 } 209 }; 210 211 struct HelixCheermoteTier { 212 QString id; 213 QString color; 214 int minBits; 215 HelixCheermoteImage darkAnimated; 216 HelixCheermoteImage darkStatic; 217 HelixCheermoteImage lightAnimated; 218 HelixCheermoteImage lightStatic; 219 HelixCheermoteTierchatterino::HelixCheermoteTier220 explicit HelixCheermoteTier(QJsonObject jsonObject) 221 : id(jsonObject.value("id").toString()) 222 , color(jsonObject.value("color").toString()) 223 , minBits(jsonObject.value("min_bits").toInt()) 224 , darkAnimated(jsonObject.value("images") 225 .toObject() 226 .value("dark") 227 .toObject() 228 .value("animated") 229 .toObject()) 230 , darkStatic(jsonObject.value("images") 231 .toObject() 232 .value("dark") 233 .toObject() 234 .value("static") 235 .toObject()) 236 , lightAnimated(jsonObject.value("images") 237 .toObject() 238 .value("light") 239 .toObject() 240 .value("animated") 241 .toObject()) 242 , lightStatic(jsonObject.value("images") 243 .toObject() 244 .value("light") 245 .toObject() 246 .value("static") 247 .toObject()) 248 { 249 } 250 }; 251 252 struct HelixCheermoteSet { 253 QString prefix; 254 QString type; 255 std::vector<HelixCheermoteTier> tiers; 256 HelixCheermoteSetchatterino::HelixCheermoteSet257 explicit HelixCheermoteSet(QJsonObject jsonObject) 258 : prefix(jsonObject.value("prefix").toString()) 259 , type(jsonObject.value("type").toString()) 260 { 261 for (const auto &tier : jsonObject.value("tiers").toArray()) 262 { 263 this->tiers.emplace_back(tier.toObject()); 264 } 265 } 266 }; 267 268 struct HelixEmoteSetData { 269 QString setId; 270 QString ownerId; 271 QString emoteType; 272 HelixEmoteSetDatachatterino::HelixEmoteSetData273 explicit HelixEmoteSetData(QJsonObject jsonObject) 274 : setId(jsonObject.value("emote_set_id").toString()) 275 , ownerId(jsonObject.value("owner_id").toString()) 276 , emoteType(jsonObject.value("emote_type").toString()) 277 { 278 } 279 }; 280 281 struct HelixChannelEmote { 282 const QString emoteId; 283 const QString name; 284 const QString type; 285 const QString setId; 286 const QString url; 287 HelixChannelEmotechatterino::HelixChannelEmote288 explicit HelixChannelEmote(QJsonObject jsonObject) 289 : emoteId(jsonObject.value("id").toString()) 290 , name(jsonObject.value("name").toString()) 291 , type(jsonObject.value("emote_type").toString()) 292 , setId(jsonObject.value("emote_set_id").toString()) 293 , url(QString(TWITCH_EMOTE_TEMPLATE) 294 .replace("{id}", this->emoteId) 295 .replace("{scale}", "3.0")) 296 { 297 } 298 }; 299 300 enum class HelixClipError { 301 Unknown, 302 ClipsDisabled, 303 UserNotAuthenticated, 304 }; 305 306 enum class HelixStreamMarkerError { 307 Unknown, 308 UserNotAuthorized, 309 UserNotAuthenticated, 310 }; 311 312 enum class HelixAutoModMessageError { 313 Unknown, 314 MessageAlreadyProcessed, 315 UserNotAuthenticated, 316 UserNotAuthorized, 317 MessageNotFound, 318 }; 319 320 class Helix final : boost::noncopyable 321 { 322 public: 323 // https://dev.twitch.tv/docs/api/reference#get-users 324 void fetchUsers(QStringList userIds, QStringList userLogins, 325 ResultCallback<std::vector<HelixUser>> successCallback, 326 HelixFailureCallback failureCallback); 327 void getUserByName(QString userName, 328 ResultCallback<HelixUser> successCallback, 329 HelixFailureCallback failureCallback); 330 void getUserById(QString userId, ResultCallback<HelixUser> successCallback, 331 HelixFailureCallback failureCallback); 332 333 // https://dev.twitch.tv/docs/api/reference#get-users-follows 334 void fetchUsersFollows( 335 QString fromId, QString toId, 336 ResultCallback<HelixUsersFollowsResponse> successCallback, 337 HelixFailureCallback failureCallback); 338 339 void getUserFollowers( 340 QString userId, 341 ResultCallback<HelixUsersFollowsResponse> successCallback, 342 HelixFailureCallback failureCallback); 343 344 // https://dev.twitch.tv/docs/api/reference#get-streams 345 void fetchStreams(QStringList userIds, QStringList userLogins, 346 ResultCallback<std::vector<HelixStream>> successCallback, 347 HelixFailureCallback failureCallback); 348 349 void getStreamById(QString userId, 350 ResultCallback<bool, HelixStream> successCallback, 351 HelixFailureCallback failureCallback); 352 353 void getStreamByName(QString userName, 354 ResultCallback<bool, HelixStream> successCallback, 355 HelixFailureCallback failureCallback); 356 357 // https://dev.twitch.tv/docs/api/reference#get-games 358 void fetchGames(QStringList gameIds, QStringList gameNames, 359 ResultCallback<std::vector<HelixGame>> successCallback, 360 HelixFailureCallback failureCallback); 361 362 // https://dev.twitch.tv/docs/api/reference#search-categories 363 void searchGames(QString gameName, 364 ResultCallback<std::vector<HelixGame>> successCallback, 365 HelixFailureCallback failureCallback); 366 367 void getGameById(QString gameId, ResultCallback<HelixGame> successCallback, 368 HelixFailureCallback failureCallback); 369 370 // https://dev.twitch.tv/docs/api/reference#create-clip 371 void createClip(QString channelId, 372 ResultCallback<HelixClip> successCallback, 373 std::function<void(HelixClipError)> failureCallback, 374 std::function<void()> finallyCallback); 375 376 // https://dev.twitch.tv/docs/api/reference#get-channel-information 377 void getChannel(QString broadcasterId, 378 ResultCallback<HelixChannel> successCallback, 379 HelixFailureCallback failureCallback); 380 381 // https://dev.twitch.tv/docs/api/reference/#create-stream-marker 382 void createStreamMarker( 383 QString broadcasterId, QString description, 384 ResultCallback<HelixStreamMarker> successCallback, 385 std::function<void(HelixStreamMarkerError)> failureCallback); 386 387 // https://dev.twitch.tv/docs/api/reference#get-user-block-list 388 void loadBlocks(QString userId, 389 ResultCallback<std::vector<HelixBlock>> successCallback, 390 HelixFailureCallback failureCallback); 391 392 // https://dev.twitch.tv/docs/api/reference#block-user 393 void blockUser(QString targetUserId, std::function<void()> successCallback, 394 HelixFailureCallback failureCallback); 395 396 // https://dev.twitch.tv/docs/api/reference#unblock-user 397 void unblockUser(QString targetUserId, 398 std::function<void()> successCallback, 399 HelixFailureCallback failureCallback); 400 401 // https://dev.twitch.tv/docs/api/reference#modify-channel-information 402 void updateChannel(QString broadcasterId, QString gameId, QString language, 403 QString title, 404 std::function<void(NetworkResult)> successCallback, 405 HelixFailureCallback failureCallback); 406 407 // https://dev.twitch.tv/docs/api/reference#manage-held-automod-messages 408 void manageAutoModMessages( 409 QString userID, QString msgID, QString action, 410 std::function<void()> successCallback, 411 std::function<void(HelixAutoModMessageError)> failureCallback); 412 413 // https://dev.twitch.tv/docs/api/reference/#get-cheermotes 414 void getCheermotes( 415 QString broadcasterId, 416 ResultCallback<std::vector<HelixCheermoteSet>> successCallback, 417 HelixFailureCallback failureCallback); 418 419 // https://dev.twitch.tv/docs/api/reference#get-emote-sets 420 void getEmoteSetData(QString emoteSetId, 421 ResultCallback<HelixEmoteSetData> successCallback, 422 HelixFailureCallback failureCallback); 423 424 // https://dev.twitch.tv/docs/api/reference#get-channel-emotes 425 void getChannelEmotes( 426 QString broadcasterId, 427 ResultCallback<std::vector<HelixChannelEmote>> successCallback, 428 HelixFailureCallback failureCallback); 429 430 void update(QString clientId, QString oauthToken); 431 432 static void initialize(); 433 434 private: 435 NetworkRequest makeRequest(QString url, QUrlQuery urlQuery); 436 437 QString clientId; 438 QString oauthToken; 439 }; 440 441 Helix *getHelix(); 442 443 } // namespace chatterino 444