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