3
0

TwitchREST.cpp 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/std/time.h>
  9. #include <AzCore/std/smart_ptr/make_shared.h>
  10. #include <AzCore/std/string/conversions.h>
  11. #include <Twitch/TwitchBus.h>
  12. #include <HttpRequestor/HttpRequestorBus.h>
  13. #include "TwitchREST.h"
  14. namespace Twitch
  15. {
  16. ITwitchRESTPtr ITwitchREST::Alloc()
  17. {
  18. return AZStd::make_shared<TwitchREST>();
  19. }
  20. const char * TwitchREST::kProtocol("https");
  21. const char * TwitchREST::kBasePath("api.twitch.tv");
  22. const char * TwitchREST::kVer("v5");
  23. const char * TwitchREST::kKraken("kraken");
  24. const char * TwitchREST::kAuthType("OAuth ");
  25. const char * TwitchREST::kAcceptType("application/vnd.twitchtv.v5+json");
  26. TwitchREST::TwitchREST()
  27. {
  28. // all names listed below comply with Twitch's naming rules, Do not change the case or
  29. // spelling of the return values! Also do not put in the ::Unknown strings, placehold only!
  30. m_availabilityMap[PresenceAvailability::Idle] = "idle";
  31. m_availabilityMap[PresenceAvailability::Online] = "online";
  32. m_activityTypeMap[PresenceActivityType::Watching] = "watching";
  33. m_activityTypeMap[PresenceActivityType::Playing] = "playing";
  34. m_activityTypeMap[PresenceActivityType::Broadcasting] = "broadcasting";
  35. }
  36. void TwitchREST::FlushEvents()
  37. {
  38. TwitchNotifyBus::ExecuteQueuedEvents();
  39. }
  40. void TwitchREST::GetUser(ReceiptID& receipt)
  41. {
  42. AZStd::string url( BuildKrakenURL("user") );
  43. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& json, Aws::Http::HttpResponseCode httpCode)
  44. {
  45. ResultCode rc(ResultCode::TwitchRESTError);
  46. UserInfo userinfo;
  47. if (httpCode == Aws::Http::HttpResponseCode::OK)
  48. {
  49. rc = ResultCode::Success;
  50. SafeGetUserInfo(userinfo, json);
  51. }
  52. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetUser, UserInfoValue(userinfo, receipt, rc));
  53. });
  54. }
  55. void TwitchREST::ResetFriendsNotificationCount(const ReceiptID& receipt, const AZStd::string& friendID)
  56. {
  57. AZStd::string url( BuildBaseURL("users", friendID) + "/friends/notifications");
  58. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_DELETE, GetDefaultHeaders(), [receipt](const Aws::Utils::Json::JsonView& /*json*/, Aws::Http::HttpResponseCode httpCode)
  59. {
  60. ResultCode rc(ResultCode::TwitchRESTError);
  61. if (httpCode == Aws::Http::HttpResponseCode::NO_CONTENT) // 204: NO_CONTENT The server successfully processed the request and is not returning any content
  62. {
  63. rc = ResultCode::Success;
  64. }
  65. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::ResetFriendsNotificationCountNotify, Int64Value(static_cast<AZ::s64>(httpCode), receipt, rc));
  66. });
  67. }
  68. void TwitchREST::GetFriendNotificationCount(const ReceiptID& receipt, const AZStd::string& friendID)
  69. {
  70. AZStd::string url(BuildBaseURL("users", friendID) + "/friends/notifications");
  71. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt](const Aws::Utils::Json::JsonView& json, Aws::Http::HttpResponseCode httpCode)
  72. {
  73. ResultCode rc(ResultCode::TwitchRESTError);
  74. AZ::s64 count = 0;
  75. if (httpCode == Aws::Http::HttpResponseCode::OK)
  76. {
  77. rc = ResultCode::Success;
  78. count = json.GetInteger("count");
  79. }
  80. else
  81. {
  82. count = static_cast<AZ::s64>(httpCode);
  83. }
  84. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetFriendNotificationCount, Int64Value(count, receipt, rc));
  85. });
  86. }
  87. void TwitchREST::GetFriendRecommendations(const ReceiptID& receipt, const AZStd::string& friendID)
  88. {
  89. AZStd::string url(BuildBaseURL("users", friendID) + "/friends/recommendations");
  90. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  91. {
  92. ResultCode rc(ResultCode::TwitchRESTError);
  93. FriendRecommendationList returnRecommendations;
  94. if (httpCode == Aws::Http::HttpResponseCode::OK)
  95. {
  96. rc = ResultCode::Success;
  97. Aws::Utils::Array<Aws::Utils::Json::JsonView> recommendations = jsonDoc.GetArray("recommendations");
  98. for(size_t index =0; index<recommendations.GetLength(); index++)
  99. {
  100. FriendRecommendation fr;
  101. Aws::Utils::Json::JsonView item = recommendations.GetItem(index);
  102. fr.Reason = item.GetString("reason").c_str();
  103. SafeGetUserInfoFromUserContainer(fr.User, item);
  104. returnRecommendations.push_back(fr);
  105. }
  106. }
  107. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetFriendRecommendations, FriendRecommendationValue(returnRecommendations, receipt, rc));
  108. });
  109. }
  110. void TwitchREST::GetFriends(const ReceiptID& receipt, const AZStd::string& friendID, const AZStd::string& cursor)
  111. {
  112. AZStd::string url(BuildBaseURL("users", friendID) + "/friends/relationships");
  113. HttpRequestor::Headers headers( GetDefaultHeaders() );
  114. AddToHeader(headers, "limit", 256ULL);
  115. if( !cursor.empty() )
  116. AddToHeader(headers, "cursor", cursor);
  117. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, headers, [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  118. {
  119. ResultCode rc(ResultCode::TwitchRESTError);
  120. GetFriendReturn friendReturn;
  121. if (httpCode == Aws::Http::HttpResponseCode::OK)
  122. {
  123. rc = ResultCode::Success;
  124. SafeGetJSONString(friendReturn.Cursor, "cursor", jsonDoc);
  125. Aws::Utils::Array<Aws::Utils::Json::JsonView> recommendations = jsonDoc.GetArray("friends");
  126. for (size_t index = 0; index < recommendations.GetLength(); index++)
  127. {
  128. FriendInfo fi;
  129. Aws::Utils::Json::JsonView item = recommendations.GetItem(index);
  130. SafeGetJSONString(fi.CreatedDate, "created_at", item);
  131. SafeGetUserInfoFromUserContainer(fi.User, item);
  132. friendReturn.Friends.push_back(fi);
  133. }
  134. }
  135. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetFriends, GetFriendValue(friendReturn, receipt, rc));
  136. });
  137. }
  138. void TwitchREST::GetFriendStatus(const ReceiptID& receipt, const AZStd::string& sourceFriendID, const AZStd::string& targetFriendID)
  139. {
  140. AZStd::string url(BuildBaseURL("users", sourceFriendID) + "/friends/relationships/" + targetFriendID);
  141. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  142. {
  143. ResultCode rc(ResultCode::TwitchRESTError);
  144. FriendStatus friendStatus;
  145. if (httpCode == Aws::Http::HttpResponseCode::OK)
  146. {
  147. rc = ResultCode::Success;
  148. SafeGetJSONString(friendStatus.Status, "status", jsonDoc);
  149. SafeGetUserInfoFromUserContainer(friendStatus.User, jsonDoc);
  150. }
  151. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetFriendStatus, FriendStatusValue(friendStatus, receipt, rc));
  152. });
  153. }
  154. void TwitchREST::AcceptFriendRequest(const ReceiptID& receipt, const AZStd::string& friendID)
  155. {
  156. AZStd::string url(BuildBaseURL("users") + "/friends/relationships/" + friendID);
  157. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_PUT, GetDefaultHeaders(), [receipt]([[maybe_unused]] const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  158. {
  159. ResultCode rc(ResultCode::TwitchRESTError);
  160. if (httpCode == Aws::Http::HttpResponseCode::CREATED)
  161. {
  162. rc = ResultCode::Success;
  163. }
  164. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::AcceptFriendRequest, Int64Value(static_cast<AZ::s64>(httpCode), receipt, rc));
  165. });
  166. }
  167. void TwitchREST::GetFriendRequests(const ReceiptID& receipt, const AZStd::string& cursor)
  168. {
  169. AZStd::string url(BuildBaseURL("users") + "/friends/requests");
  170. HttpRequestor::Headers headers(GetDefaultHeaders());
  171. AddToHeader(headers, "limit", 512ULL);
  172. if (!cursor.empty())
  173. AddToHeader(headers, "cursor", cursor);
  174. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, headers, [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  175. {
  176. ResultCode rc(ResultCode::TwitchRESTError);
  177. FriendRequestResult requestResult;
  178. if (httpCode == Aws::Http::HttpResponseCode::OK)
  179. {
  180. rc = ResultCode::Success;
  181. SafeGetJSONString(requestResult.Cursor, "cursor", jsonDoc);
  182. SafeGetJSONu64(requestResult.Total, "total", jsonDoc);
  183. Aws::Utils::Array<Aws::Utils::Json::JsonView> recommendations = jsonDoc.GetArray("requests");
  184. for (size_t index = 0; index < recommendations.GetLength(); index++)
  185. {
  186. Aws::Utils::Json::JsonView item = recommendations.GetItem(index);
  187. FriendRequest fr;
  188. SafeGetJSONbool(fr.IsRecommended, "is_recommended", item);
  189. SafeGetJSONbool(fr.IsStranger, "is_stranger", item);
  190. SafeGetJSONString(fr.NonStrangerReason, "non_stranger_reason", item);
  191. SafeGetJSONString(fr.RequestedDate, "requested_at", item);
  192. SafeGetUserInfoFromUserContainer(fr.User, item);
  193. requestResult.Requests.push_back(fr);
  194. }
  195. }
  196. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetFriendRequests, FriendRequestValue(requestResult, receipt, rc));
  197. });
  198. }
  199. void TwitchREST::CreateFriendRequest(const ReceiptID& receipt, const AZStd::string& friendID)
  200. {
  201. AZStd::string url(BuildBaseURL("users") + "/friends/requests/" + friendID);
  202. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_PUT, GetDefaultHeaders(), [receipt]([[maybe_unused]] const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  203. {
  204. ResultCode rc(ResultCode::TwitchRESTError);
  205. if (httpCode == Aws::Http::HttpResponseCode::CREATED)
  206. {
  207. rc = ResultCode::Success;
  208. }
  209. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::CreateFriendRequest, Int64Value(static_cast<AZ::s64>(httpCode), receipt, rc));
  210. });
  211. }
  212. void TwitchREST::DeclineFriendRequest(const ReceiptID& receipt, const AZStd::string& friendID)
  213. {
  214. AZStd::string url(BuildBaseURL("users") + "/friends/requests/" + friendID);
  215. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_DELETE, GetDefaultHeaders(), [receipt]([[maybe_unused]] const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  216. {
  217. ResultCode rc(ResultCode::TwitchRESTError);
  218. if (httpCode == Aws::Http::HttpResponseCode::CREATED)
  219. {
  220. rc = ResultCode::Success;
  221. }
  222. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::DeclineFriendRequest, Int64Value(static_cast<AZ::s64>(httpCode), receipt, rc));
  223. });
  224. }
  225. void TwitchREST::UpdatePresenceStatus(const ReceiptID& receipt, PresenceAvailability availability, PresenceActivityType activityType, const AZStd::string& gameContext)
  226. {
  227. // we need to get the twitch channel this user is on, and that call requires its own receipt
  228. ReceiptID gcReceipt;
  229. InternalGetChannel(gcReceipt, [receipt, availability, activityType, gameContext, this](const ChannelInfo& channelInfo, const ReceiptID&, ResultCode)
  230. {
  231. AZStd::string url(BuildBaseURL("users") + "/status");
  232. HttpRequestor::Headers headers(GetDefaultHeaders());
  233. AddToHeader(headers, "Content-Type", "application/json");
  234. AZStd::string appID;
  235. TwitchRequestBus::BroadcastResult(appID, &TwitchRequests::GetApplicationID);
  236. Aws::Utils::Json::JsonValue jsonActivity;
  237. jsonActivity.WithString("type", GetPresenceActivityTypeName(activityType).c_str());
  238. jsonActivity.WithString("channel_id", channelInfo.Id.c_str());
  239. jsonActivity.WithString("game_id", appID.c_str());
  240. if ( (activityType == PresenceActivityType::Playing) && !gameContext.empty() )
  241. {
  242. Aws::Utils::Json::JsonValue jsonGameContext(Aws::String(gameContext.c_str()));
  243. if( jsonGameContext.WasParseSuccessful() )
  244. jsonActivity.WithObject("game_context", AZStd::move(jsonGameContext));
  245. }
  246. AZStd::string sessionID;
  247. TwitchRequestBus::BroadcastResult(sessionID, &TwitchRequests::GetSessionID);
  248. Aws::Utils::Json::JsonValue jsonBody;
  249. jsonBody.WithString("session_id", sessionID.c_str());
  250. jsonBody.WithString("availability", GetPresenceAvailabilityName(availability).c_str());
  251. jsonBody.WithObject("activities", AZStd::move(jsonActivity));
  252. Aws::String body(jsonBody.View().WriteCompact());
  253. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_POST, headers, body.c_str(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  254. {
  255. ResultCode rc(ResultCode::TwitchRESTError);
  256. AZ::s64 pollIntervalSeconds = 0;
  257. if (httpCode == Aws::Http::HttpResponseCode::OK)
  258. {
  259. rc = ResultCode::Success;
  260. SafeGetJSONs64(pollIntervalSeconds, "poll_interval_seconds", jsonDoc);
  261. }
  262. else
  263. {
  264. pollIntervalSeconds = static_cast<AZ::s64>(httpCode);
  265. }
  266. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::UpdatePresenceStatus, Int64Value(pollIntervalSeconds, receipt, rc));
  267. });
  268. });
  269. }
  270. void TwitchREST::GetPresenceStatusofFriends(const ReceiptID& receipt)
  271. {
  272. AZStd::string url(BuildBaseURL("users") + "/status/friends");
  273. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  274. {
  275. ResultCode rc(ResultCode::TwitchRESTError);
  276. PresenceStatusList statusList;
  277. if (httpCode == Aws::Http::HttpResponseCode::OK)
  278. {
  279. rc = ResultCode::Success;
  280. Aws::Utils::Array<Aws::Utils::Json::JsonView> recommendations = jsonDoc.GetArray("data");
  281. for (size_t index = 0; index < recommendations.GetLength(); index++)
  282. {
  283. Aws::Utils::Json::JsonView item = recommendations.GetItem(index);
  284. PresenceStatus ps;
  285. SafeGetJSONs64(ps.Index, "index", item);
  286. SafeGetJSONs64(ps.UpdatedDate, "UpdatedDate", item);
  287. SafeGetJSONString(ps.UserID, "user_id", item);
  288. SafeGetPresenceActivityType(ps.ActivityType, item);
  289. SafeGetPresenceAvailability(ps.Availability, item);
  290. statusList.push_back(ps);
  291. }
  292. }
  293. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetPresenceStatusofFriends, PresenceStatusValue(statusList, receipt, rc));
  294. });
  295. }
  296. void TwitchREST::GetPresenceSettings(const ReceiptID& receipt)
  297. {
  298. AZStd::string url(BuildBaseURL("users") + "/status/settings");
  299. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  300. {
  301. ResultCode rc(ResultCode::TwitchRESTError);
  302. PresenceSettings presenceSettings;
  303. if (httpCode == Aws::Http::HttpResponseCode::OK)
  304. {
  305. rc = ResultCode::Success;
  306. SafeGetJSONbool(presenceSettings.IsInvisible, "is_invisible", jsonDoc);
  307. SafeGetJSONbool(presenceSettings.ShareActivity, "share_activity", jsonDoc);
  308. }
  309. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetPresenceSettings, PresenceSettingsValue(presenceSettings, receipt, rc));
  310. });
  311. }
  312. void TwitchREST::UpdatePresenceSettings(const ReceiptID& receipt, bool isInvisible, bool shareActivity)
  313. {
  314. AZStd::string url(BuildBaseURL("users") + "/status/settings");
  315. HttpRequestor::Headers headers(GetDefaultHeaders());
  316. AddToHeader(headers, "Content-Type", "application/json");
  317. Aws::Utils::Json::JsonValue jsonBody;
  318. jsonBody.WithBool("is_invisible", isInvisible);
  319. jsonBody.WithBool("share_activity", shareActivity);
  320. Aws::String body(jsonBody.View().WriteCompact());
  321. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_POST, headers, body.c_str(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  322. {
  323. ResultCode rc(ResultCode::TwitchRESTError);
  324. PresenceSettings presenceSettings;
  325. if (httpCode == Aws::Http::HttpResponseCode::OK)
  326. {
  327. rc = ResultCode::Success;
  328. SafeGetJSONbool(presenceSettings.IsInvisible, "is_invisible", jsonDoc);
  329. SafeGetJSONbool(presenceSettings.ShareActivity, "share_activity", jsonDoc);
  330. }
  331. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::UpdatePresenceSettings, PresenceSettingsValue(presenceSettings, receipt, rc));
  332. });
  333. }
  334. void TwitchREST::GetChannel(const ReceiptID& receipt)
  335. {
  336. InternalGetChannel(receipt, [](const ChannelInfo& channelInfo, const ReceiptID& receipt, ResultCode rc)
  337. {
  338. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannel, ChannelInfoValue(channelInfo, receipt, rc));
  339. });
  340. }
  341. void TwitchREST::GetChannelbyID(const ReceiptID& receipt, const AZStd::string& channelID)
  342. {
  343. AZStd::string url(BuildKrakenURL("channels") + "/" + (channelID.empty() ? "0000000" : channelID));
  344. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetClientIDHeader(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  345. {
  346. ResultCode rc(ResultCode::TwitchRESTError);
  347. ChannelInfo channelInfo;
  348. if (httpCode == Aws::Http::HttpResponseCode::OK)
  349. {
  350. rc = ResultCode::Success;
  351. channelInfo.NumItemsRecieved = SafeGetChannelInfo(channelInfo, jsonDoc);
  352. }
  353. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelbyID, ChannelInfoValue(channelInfo, receipt, rc));
  354. });
  355. }
  356. void TwitchREST::UpdateChannel(const ReceiptID& receipt, const ChannelUpdateInfo& channelUpdateInfo)
  357. {
  358. /*
  359. ** sanity check here, at least once of these must be set to update.
  360. */
  361. if( ( !channelUpdateInfo.ChannelFeedEnabled.ToBeUpdated() ) &&
  362. ( !channelUpdateInfo.Delay.ToBeUpdated()) &&
  363. ( !channelUpdateInfo.GameName.ToBeUpdated()) &&
  364. ( !channelUpdateInfo.Status.ToBeUpdated()))
  365. {
  366. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::UpdateChannel, ChannelInfoValue(ChannelInfo(), receipt, ResultCode::TwitchChannelNoUpdatesToMake));
  367. }
  368. // we need to get the twitch channel this user is on, and that call requires its own receipt
  369. ReceiptID gcReceipt;
  370. InternalGetChannel(gcReceipt, [receipt, channelUpdateInfo, this](const ChannelInfo& channelInfo, const ReceiptID&, ResultCode)
  371. {
  372. AZStd::string url(BuildKrakenURL("channels") + "/" + channelInfo.Id);
  373. HttpRequestor::Headers headers(GetDefaultHeaders());
  374. AddToHeader(headers, "Content-Type", "application/json");
  375. Aws::Utils::Json::JsonValue jsonChannel;
  376. if (channelUpdateInfo.Status.ToBeUpdated())
  377. {
  378. jsonChannel.WithString("status", channelUpdateInfo.Status.GetValue().c_str());
  379. }
  380. if (channelUpdateInfo.GameName.ToBeUpdated())
  381. {
  382. jsonChannel.WithString("game", channelUpdateInfo.GameName.GetValue().c_str());
  383. }
  384. if (channelUpdateInfo.Delay.ToBeUpdated())
  385. {
  386. jsonChannel.WithString("delay", AZStd::to_string(channelUpdateInfo.Delay.GetValue()).c_str());
  387. }
  388. if (channelUpdateInfo.ChannelFeedEnabled.ToBeUpdated())
  389. {
  390. jsonChannel.WithBool("channel_feed_enabled", channelUpdateInfo.ChannelFeedEnabled.GetValue());
  391. }
  392. Aws::Utils::Json::JsonValue jsonBody;
  393. jsonBody.WithObject("channel", AZStd::move(jsonChannel));
  394. Aws::String body(jsonBody.View().WriteCompact());
  395. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_PUT, headers, body.c_str(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  396. {
  397. ResultCode rc(ResultCode::TwitchRESTError);
  398. ChannelInfo retChannelInfo;
  399. if (httpCode == Aws::Http::HttpResponseCode::OK)
  400. {
  401. rc = ResultCode::Success;
  402. retChannelInfo.NumItemsRecieved = SafeGetChannelInfo(retChannelInfo, jsonDoc);
  403. }
  404. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::UpdateChannel, ChannelInfoValue(retChannelInfo, receipt, rc));
  405. });
  406. });
  407. }
  408. void TwitchREST::GetChannelEditors(ReceiptID& receipt, const AZStd::string& channelID)
  409. {
  410. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/editors");
  411. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  412. {
  413. ResultCode rc(ResultCode::TwitchRESTError);
  414. UserInfoList userList;
  415. if (httpCode == Aws::Http::HttpResponseCode::OK)
  416. {
  417. rc = ResultCode::Success;
  418. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonUserArray = jsonDoc.GetArray("users");
  419. for (size_t index = 0; index < jsonUserArray.GetLength(); index++)
  420. {
  421. Aws::Utils::Json::JsonView item = jsonUserArray.GetItem(index);
  422. UserInfo ui;
  423. SafeGetUserInfo(ui, item);
  424. userList.push_back(ui);
  425. }
  426. }
  427. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelEditors, UserInfoListValue(userList, receipt, rc));
  428. });
  429. }
  430. void TwitchREST::GetChannelFollowers(ReceiptID& receipt, const AZStd::string& channelID, const AZStd::string& cursor, AZ::u64 offset)
  431. {
  432. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/follows?limit=100");
  433. if( !cursor.empty() )
  434. {
  435. url += "&cursor=";
  436. url += cursor;
  437. url += "&offset=";
  438. url += AZStd::to_string(offset);
  439. }
  440. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetClientIDHeader(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  441. {
  442. ResultCode rc(ResultCode::TwitchRESTError);
  443. FollowerResult followerResult;
  444. if (httpCode == Aws::Http::HttpResponseCode::OK)
  445. {
  446. rc = ResultCode::Success;
  447. SafeGetJSONString(followerResult.Cursor, "_cursor", jsonDoc);
  448. SafeGetJSONu64(followerResult.Total, "_total", jsonDoc);
  449. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonFollowsArray = jsonDoc.GetArray("follows");
  450. for (size_t index = 0; index < jsonFollowsArray.GetLength(); index++)
  451. {
  452. Aws::Utils::Json::JsonView item = jsonFollowsArray.GetItem(index);
  453. Follower follower;
  454. SafeGetJSONString(follower.CreatedDate, "created_at", item);
  455. SafeGetJSONbool(follower.Notifications, "notifications", item);
  456. SafeGetUserInfoFromUserContainer(follower.User, item);
  457. followerResult.Followers.push_back(follower);
  458. }
  459. }
  460. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelFollowers, FollowerResultValue(followerResult, receipt, rc));
  461. });
  462. }
  463. void TwitchREST::GetChannelTeams(ReceiptID& receipt, const AZStd::string& channelID)
  464. {
  465. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/teams");
  466. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetClientIDHeader(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  467. {
  468. ResultCode rc(ResultCode::TwitchRESTError);
  469. TeamInfoList teamInfoList;
  470. if (httpCode == Aws::Http::HttpResponseCode::OK)
  471. {
  472. rc = ResultCode::Success;
  473. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonArray = jsonDoc.GetArray("teams");
  474. for (size_t index = 0; index < jsonArray.GetLength(); index++)
  475. {
  476. Aws::Utils::Json::JsonView item = jsonArray.GetItem(index);
  477. TeamInfo teamInfo;
  478. SafeGetTeamInfo(teamInfo, item);
  479. teamInfoList.push_back(teamInfo);
  480. }
  481. }
  482. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelTeams, ChannelTeamValue(teamInfoList, receipt, rc));
  483. });
  484. }
  485. void TwitchREST::GetChannelSubscribers(ReceiptID& receipt, const AZStd::string& channelID, AZ::u64 offset)
  486. {
  487. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/subscriptions?limit=100");
  488. if (offset > 0)
  489. {
  490. url += "&offset=";
  491. url += AZStd::to_string(offset);
  492. }
  493. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  494. {
  495. ResultCode rc(ResultCode::TwitchRESTError);
  496. Subscription subscription;
  497. if (httpCode == Aws::Http::HttpResponseCode::OK)
  498. {
  499. rc = ResultCode::Success;
  500. SafeGetJSONu64(subscription.Total, "_total", jsonDoc);
  501. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonSubscriptionsArray = jsonDoc.GetArray("subscriptions");
  502. for (size_t index = 0; index < jsonSubscriptionsArray.GetLength(); index++)
  503. {
  504. Aws::Utils::Json::JsonView item = jsonSubscriptionsArray.GetItem(index);
  505. SubscriberInfo si;
  506. SafeGetJSONString(si.ID, "_id", item);
  507. SafeGetJSONString(si.CreatedDate, "created_at", item);
  508. SafeGetUserInfoFromUserContainer(si.User, item);
  509. subscription.Subscribers.push_back(si);
  510. }
  511. }
  512. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelSubscribers, SubscriberValue(subscription, receipt, rc));
  513. });
  514. }
  515. void TwitchREST::CheckChannelSubscriptionbyUser(ReceiptID& receipt, const AZStd::string& channelID, const AZStd::string& userID)
  516. {
  517. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/subscriptions/" + userID);
  518. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  519. {
  520. ResultCode rc(ResultCode::TwitchRESTError);
  521. SubscriberInfo si;
  522. if (httpCode == Aws::Http::HttpResponseCode::OK)
  523. {
  524. rc = ResultCode::Success;
  525. SafeGetJSONString(si.ID, "_id", jsonDoc);
  526. SafeGetJSONString(si.CreatedDate, "created_at", jsonDoc);
  527. SafeGetUserInfoFromUserContainer(si.User, jsonDoc);
  528. }
  529. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::CheckChannelSubscriptionbyUser, SubscriberbyUserValue(si, receipt, rc));
  530. });
  531. }
  532. void TwitchREST::GetChannelVideos(ReceiptID& receipt, const AZStd::string& channelID, BroadCastType boradcastType, const AZStd::string& language, AZ::u64 offset)
  533. {
  534. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/videos?limit=100");
  535. if (offset > 0)
  536. {
  537. url += "&offset=";
  538. url += AZStd::to_string(offset);
  539. }
  540. AZStd::string bt( GetBroadCastTypeNameFromType(boradcastType) );
  541. if( !bt.empty() )
  542. {
  543. url += "&broadcast_type=";
  544. url += bt;
  545. }
  546. if( !language.empty() )
  547. {
  548. url += "&language=";
  549. url += language;
  550. }
  551. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  552. {
  553. ResultCode rc(ResultCode::TwitchRESTError);
  554. VideoReturn videoReturn;
  555. if (httpCode == Aws::Http::HttpResponseCode::OK)
  556. {
  557. rc = ResultCode::Success;
  558. SafeGetJSONu64(videoReturn.Total, "_total", jsonDoc);
  559. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonVideosArray = jsonDoc.GetArray("videos");
  560. for (size_t index = 0; index < jsonVideosArray.GetLength(); index++)
  561. {
  562. Aws::Utils::Json::JsonView item = jsonVideosArray.GetItem(index);
  563. VideoInfo vi;
  564. SafeGetJSONString(vi.ID, "_id", item);
  565. SafeGetJSONu64(vi.BroadcastID, "broadcast_id", item);
  566. SafeGetJSONBroadCastType(vi.Type, "broadcast_type", item);
  567. SafeGetJSONVideoChannel(vi.Channel, item);
  568. SafeGetJSONString(vi.CreatedDate, "created_at", item);
  569. SafeGetJSONString(vi.Description, "description", item);
  570. SafeGetJSONString(vi.DescriptionHTML, "description_html", item);
  571. SafeGetJSONVideoFPS(vi.FPS, item);
  572. SafeGetJSONString(vi.Game, "game", item);
  573. SafeGetJSONString(vi.Language, "language", item);
  574. SafeGetJSONu64(vi.Length, "length", item);
  575. SafeGetJSONVideoPreview(vi.Preview, item);
  576. SafeGetJSONString(vi.PublishedDate, "published_at", item);
  577. SafeGetJSONVideoResolutions(vi.Resolutions, item);
  578. SafeGetJSONString(vi.Status, "status", item);
  579. SafeGetJSONString(vi.TagList, "tag_list", item);
  580. SafeGetJSONVideoThumbnails(vi.Thumbnails, item);
  581. SafeGetJSONString(vi.Title, "title", item);
  582. SafeGetJSONString(vi.URL, "url", item);
  583. SafeGetJSONString(vi.Viewable, "viewable", item);
  584. SafeGetJSONString(vi.ViewableAt, "viewable_at", item);
  585. SafeGetJSONu64(vi.Views, "views", item);
  586. videoReturn.Videos.push_back(vi);
  587. }
  588. }
  589. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::GetChannelVideos, VideoReturnValue(videoReturn, receipt, rc));
  590. });
  591. }
  592. void TwitchREST::StartChannelCommercial(ReceiptID& receipt, const AZStd::string& channelID, CommercialLength length)
  593. {
  594. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/commercial");
  595. HttpRequestor::Headers headers( GetDefaultHeaders() );
  596. AddToHeader(headers, "Content-Type", "application/json");
  597. Aws::Utils::Json::JsonValue jsonBody;
  598. jsonBody.WithInt64("duration", GetComercialLength(length) );
  599. Aws::String body(jsonBody.View().WriteCompact());
  600. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_POST, headers, body.c_str(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  601. {
  602. ResultCode rc(ResultCode::TwitchRESTError);
  603. StartChannelCommercialResult cr;
  604. if (httpCode == Aws::Http::HttpResponseCode::OK)
  605. {
  606. rc = ResultCode::Success;
  607. SafeGetJSONu64(cr.Duration, "duration", jsonDoc);
  608. SafeGetJSONString(cr.Message, "message", jsonDoc);
  609. SafeGetJSONu64(cr.RetryAfter, "retryafter", jsonDoc);
  610. }
  611. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::StartChannelCommercial, StartChannelCommercialValue(cr, receipt, rc));
  612. });
  613. }
  614. void TwitchREST::ResetChannelStreamKey(ReceiptID& receipt, const AZStd::string& channelID)
  615. {
  616. AZStd::string url(BuildKrakenURL("channels") + "/" + channelID + "/stream_key");
  617. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_DELETE, GetDefaultHeaders(), [receipt, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  618. {
  619. ResultCode rc(ResultCode::TwitchRESTError);
  620. ChannelInfo ci;
  621. if (httpCode == Aws::Http::HttpResponseCode::OK)
  622. {
  623. rc = ResultCode::Success;
  624. SafeGetChannelInfo(ci, jsonDoc);
  625. }
  626. TwitchNotifyBus::QueueBroadcast(&TwitchNotifyBus::Events::ResetChannelStreamKey, ChannelInfoValue(ci, receipt, rc));
  627. });
  628. }
  629. bool TwitchREST::IsValidGameContext(const AZStd::string& gameContext) const
  630. {
  631. bool isValid = false;
  632. /*
  633. ** Insure gameContext is a valid JSON object, and that means there must be a string!
  634. */
  635. if( !gameContext.empty() )
  636. {
  637. Aws::Utils::Json::JsonValue json(Aws::String(gameContext.c_str()));
  638. isValid = json.WasParseSuccessful();
  639. }
  640. return isValid;
  641. }
  642. void TwitchREST::AddHTTPRequest(const AZStd::string& URI, Aws::Http::HttpMethod method, const HttpRequestor::Headers & headers, const HttpRequestor::Callback & callback)
  643. {
  644. HttpRequestor::HttpRequestorRequestBus::Broadcast(&HttpRequestor::HttpRequestorRequests::AddRequestWithHeaders, URI, method, headers, callback);
  645. }
  646. void TwitchREST::AddHTTPRequest(const AZStd::string& URI, Aws::Http::HttpMethod method, const HttpRequestor::Headers & headers, const AZStd::string& body, const HttpRequestor::Callback & callback)
  647. {
  648. HttpRequestor::HttpRequestorRequestBus::Broadcast(&HttpRequestor::HttpRequestorRequests::AddRequestWithHeadersAndBody, URI, method, headers, body, callback);
  649. }
  650. void TwitchREST::InternalGetChannel(const ReceiptID& receipt, const GetChannelCallback& callback)
  651. {
  652. AZStd::string url(BuildKrakenURL("channel"));
  653. AddHTTPRequest(url, Aws::Http::HttpMethod::HTTP_GET, GetDefaultHeaders(), [receipt, callback, this](const Aws::Utils::Json::JsonView& jsonDoc, Aws::Http::HttpResponseCode httpCode)
  654. {
  655. ResultCode rc(ResultCode::TwitchRESTError);
  656. ChannelInfo channelInfo;
  657. if (httpCode == Aws::Http::HttpResponseCode::OK)
  658. {
  659. rc = ResultCode::Success;
  660. channelInfo.NumItemsRecieved = SafeGetChannelInfo(channelInfo, jsonDoc);
  661. }
  662. callback(channelInfo, receipt, rc);
  663. });
  664. }
  665. AZStd::string TwitchREST::BuildBaseURL(const AZStd::string& family, const AZStd::string& friendID /*= ""*/) const
  666. {
  667. /*
  668. ** return a URL something like https://api.twitch.tv/v5/<family>/<clientid>
  669. */
  670. AZStd::string url(kProtocol);
  671. AZStd::string clientID;
  672. // if user id is empty, then get the current client id for the logged in user.
  673. if( friendID.empty() )
  674. {
  675. TwitchRequestBus::BroadcastResult(clientID, &TwitchRequests::GetUserID);
  676. }
  677. else
  678. {
  679. clientID = friendID;
  680. }
  681. url += "://";
  682. url += kBasePath; // kBasePath("api.twitch.tv");
  683. url += "/";
  684. url += kVer; // kVer("v5");
  685. url += "/";
  686. url += family;
  687. url += "/";
  688. url += clientID;
  689. return url;
  690. }
  691. AZStd::string TwitchREST::BuildKrakenURL(const AZStd::string& family) const
  692. {
  693. /*
  694. ** return a URL something like https://api.twitch.tv/kraken/<family>
  695. */
  696. AZStd::string url(kProtocol);
  697. url += "://";
  698. url += kBasePath; // kBasePath("api.twitch.tv");
  699. url += "/";
  700. url += kKraken; // kKraken("kraken");
  701. url += "/";
  702. url += family;
  703. return url;
  704. }
  705. HttpRequestor::Headers TwitchREST::GetDefaultHeaders()
  706. {
  707. HttpRequestor::Headers hdrs;
  708. AddAcceptToHeader(hdrs);
  709. AddOAuthtHeader(hdrs);
  710. AddClientIDHeader(hdrs);
  711. return hdrs;
  712. }
  713. HttpRequestor::Headers TwitchREST::GetClientIDHeader()
  714. {
  715. HttpRequestor::Headers hdrs;
  716. AddAcceptToHeader(hdrs);
  717. AddClientIDHeader(hdrs);
  718. return hdrs;
  719. }
  720. void TwitchREST::AddOAuthtHeader(HttpRequestor::Headers& headers)
  721. {
  722. AZStd::string oAuthToken;
  723. TwitchRequestBus::BroadcastResult(oAuthToken, &TwitchRequests::GetOAuthToken);
  724. headers["Authorization"] = kAuthType + oAuthToken;
  725. }
  726. // return the application id in a header, the rest docs refer this as the client-id, poorly named)
  727. void TwitchREST::AddClientIDHeader(HttpRequestor::Headers& headers)
  728. {
  729. AZStd::string appID;
  730. TwitchRequestBus::BroadcastResult(appID, &TwitchRequests::GetApplicationID);
  731. headers["Client-ID"] = appID;
  732. }
  733. void TwitchREST::AddAcceptToHeader(HttpRequestor::Headers& headers)
  734. {
  735. headers["Accept"] = kAcceptType;
  736. }
  737. void TwitchREST::AddToHeader(HttpRequestor::Headers& headers, const AZStd::string& name, const AZStd::string& key) const
  738. {
  739. headers[name] = key;
  740. }
  741. void TwitchREST::AddToHeader(HttpRequestor::Headers& headers, const AZStd::string& name, AZ::s64 key) const
  742. {
  743. AddToHeader(headers, name, AZStd::to_string(key));
  744. }
  745. void TwitchREST::AddToHeader(HttpRequestor::Headers& headers, const AZStd::string& name, AZ::u64 key) const
  746. {
  747. AddToHeader(headers, name, AZStd::to_string(key));
  748. }
  749. AZ::u64 TwitchREST::SafeGetUserInfoFromUserContainer(UserInfo& userInfo, const Aws::Utils::Json::JsonView& jsonInfo) const
  750. {
  751. AZ::u64 itemCount = 0;
  752. if (jsonInfo.ValueExists("user") )
  753. {
  754. Aws::Utils::Json::JsonView jsonUser(jsonInfo.GetObject("user"));
  755. itemCount = SafeGetUserInfo(userInfo, jsonUser);
  756. }
  757. return itemCount;
  758. }
  759. AZ::u64 TwitchREST::SafeGetUserInfo(UserInfo& userInfo, const Aws::Utils::Json::JsonView& jsonInfo) const
  760. {
  761. AZ::u64 itemCount = 0;
  762. itemCount += SafeGetJSONString(userInfo.ID, "_id", jsonInfo);
  763. itemCount += SafeGetJSONString(userInfo.Bio, "bio", jsonInfo);
  764. itemCount += SafeGetJSONString(userInfo.CreatedDate, "created_at", jsonInfo);
  765. itemCount += SafeGetJSONString(userInfo.DisplayName, "display_name", jsonInfo);
  766. itemCount += SafeGetJSONString(userInfo.EMail, "email", jsonInfo);
  767. itemCount += SafeGetJSONString(userInfo.Logo, "logo", jsonInfo);
  768. itemCount += SafeGetJSONString(userInfo.Name, "name", jsonInfo);
  769. itemCount += SafeGetJSONString(userInfo.ProfileBanner, "profile_banner", jsonInfo);
  770. itemCount += SafeGetJSONString(userInfo.ProfileBannerBackgroundColor, "profile_banner_background_color", jsonInfo);
  771. itemCount += SafeGetJSONString(userInfo.Type, "type", jsonInfo);
  772. itemCount += SafeGetJSONString(userInfo.UpdatedDate, "updated_at", jsonInfo);
  773. itemCount += SafeGetJSONbool(userInfo.EMailVerified, "email_verified", jsonInfo);
  774. itemCount += SafeGetJSONbool(userInfo.Partnered, "partnered", jsonInfo);
  775. itemCount += SafeGetJSONbool(userInfo.TwitterConnected, "twitter_connected", jsonInfo);
  776. itemCount += SafeGetUserNotifications(userInfo.Notifications, jsonInfo);
  777. return itemCount;
  778. }
  779. bool TwitchREST::SafeGetJSONString(AZStd::string& value, const char *key, const Aws::Utils::Json::JsonView& json) const
  780. {
  781. bool success = false;
  782. if ( (key != nullptr)&& json.ValueExists(key) )
  783. {
  784. success = true;
  785. value = json.GetString(key).c_str();
  786. }
  787. return success;
  788. }
  789. bool TwitchREST::SafeGetJSONu64(AZ::u64& value, const char *key, const Aws::Utils::Json::JsonView& json) const
  790. {
  791. bool success = false;
  792. if ((key != nullptr)&& json.ValueExists(key))
  793. {
  794. success = true;
  795. value = static_cast<AZ::u64>(json.GetInt64(key));
  796. }
  797. return success;
  798. }
  799. bool TwitchREST::SafeGetJSONs64(AZ::s64& value, const char *key, const Aws::Utils::Json::JsonView& json) const
  800. {
  801. bool success = false;
  802. if ((key != nullptr)&& json.ValueExists(key))
  803. {
  804. success = true;
  805. value = json.GetInt64(key);
  806. }
  807. return success;
  808. }
  809. bool TwitchREST::SafeGetJSONbool(bool& value, const char *key, const Aws::Utils::Json::JsonView& json) const
  810. {
  811. bool success = false;
  812. if ((key != nullptr)&& json.ValueExists(key))
  813. {
  814. success = true;
  815. value = json.GetBool(key);
  816. }
  817. return success;
  818. }
  819. bool TwitchREST::SafeGetJSONdouble(double& value, const char* key, const Aws::Utils::Json::JsonView& json) const
  820. {
  821. bool success = false;
  822. if ((key != nullptr) && json.ValueExists(key))
  823. {
  824. success = true;
  825. value = json.GetDouble(key);
  826. }
  827. return success;
  828. }
  829. AZ::u64 TwitchREST::SafeGetUserNotifications(UserNotifications& userNotifications, const Aws::Utils::Json::JsonView& json) const
  830. {
  831. // assumes the json value contains:
  832. // "notifications": { "email": false, "push" : true }
  833. AZ::u64 numItems = 0;
  834. if (json.ValueExists("notifications"))
  835. {
  836. Aws::Utils::Json::JsonView jsonNotifications(json.GetObject("notifications"));
  837. numItems += SafeGetJSONbool(userNotifications.EMail, "email", jsonNotifications);
  838. numItems += SafeGetJSONbool(userNotifications.Push, "push", jsonNotifications);
  839. }
  840. return numItems;
  841. }
  842. bool TwitchREST::SafeGetPresenceActivityType(PresenceActivityType& activityType, const Aws::Utils::Json::JsonView& json) const
  843. {
  844. bool success = false;
  845. AZStd::string name;
  846. if (SafeGetJSONString(name, "availability", json))
  847. {
  848. activityType = GetPresenceActivityType(name);
  849. success = true;
  850. }
  851. return success;
  852. }
  853. bool TwitchREST::SafeGetPresenceAvailability(PresenceAvailability& availability, const Aws::Utils::Json::JsonView& json) const
  854. {
  855. // assumes the json doc contains
  856. // "activity": { "type": "none"}
  857. bool success = false;
  858. if( json.ValueExists("activity") )
  859. {
  860. Aws::Utils::Json::JsonView jsonActivity( json.GetObject("activity") );
  861. AZStd::string typeName;
  862. if( SafeGetJSONString(typeName, "type", jsonActivity) )
  863. {
  864. availability = GetPresenceAvailability(typeName);
  865. success = true;
  866. }
  867. }
  868. return success;
  869. }
  870. AZ::u64 TwitchREST::SafeGetChannelInfo(ChannelInfo& channelInfo, const Aws::Utils::Json::JsonView& json) const
  871. {
  872. AZ::u64 itemCount = 0;
  873. itemCount += SafeGetJSONString(channelInfo.Id, "_id", json);
  874. itemCount += SafeGetJSONString(channelInfo.BroadcasterLanguage, "broadcaster_language", json);
  875. itemCount += SafeGetJSONString(channelInfo.CreatedDate, "created_at", json);
  876. itemCount += SafeGetJSONString(channelInfo.DisplayName, "display_name", json);
  877. itemCount += SafeGetJSONString(channelInfo.eMail, "email", json); // only returned when invoked via GetChannel
  878. itemCount += SafeGetJSONu64(channelInfo.NumFollowers, "followers", json);
  879. itemCount += SafeGetJSONString(channelInfo.GameName, "game", json);
  880. itemCount += SafeGetJSONString(channelInfo.Lanugage, "language", json);
  881. itemCount += SafeGetJSONString(channelInfo.Logo, "logo", json);
  882. itemCount += SafeGetJSONbool(channelInfo.Mature, "mature", json);
  883. itemCount += SafeGetJSONString(channelInfo.Name, "name", json);
  884. itemCount += SafeGetJSONbool(channelInfo.Partner, "partner", json);
  885. itemCount += SafeGetJSONString(channelInfo.ProfileBanner, "profile_banner", json);
  886. itemCount += SafeGetJSONString(channelInfo.ProfileBannerBackgroundColor, "profile_banner_background_color", json);
  887. itemCount += SafeGetJSONString(channelInfo.Status, "status", json);
  888. itemCount += SafeGetJSONString(channelInfo.StreamKey, "stream_key", json); // only returned when invoked via GetChannel
  889. itemCount += SafeGetJSONString(channelInfo.UpdatedDate, "updated_at", json);
  890. itemCount += SafeGetJSONString(channelInfo.URL, "url", json);
  891. itemCount += SafeGetJSONString(channelInfo.VideoBanner, "video_banner", json);
  892. itemCount += SafeGetJSONu64(channelInfo.NumViews, "views", json);
  893. return itemCount;
  894. }
  895. AZ::u64 TwitchREST::SafeGetTeamInfo(TeamInfo& teamInfo, const Aws::Utils::Json::JsonView& json) const
  896. {
  897. AZ::u64 itemCount = 0;
  898. itemCount += SafeGetJSONString(teamInfo.ID, "_id", json);
  899. itemCount += SafeGetJSONString(teamInfo.Background, "background", json);
  900. itemCount += SafeGetJSONString(teamInfo.Banner, "banner", json);
  901. itemCount += SafeGetJSONString(teamInfo.CreatedDate, "created_at", json);
  902. itemCount += SafeGetJSONString(teamInfo.DisplayName, "display_name", json);
  903. itemCount += SafeGetJSONString(teamInfo.Info, "info", json);
  904. itemCount += SafeGetJSONString(teamInfo.Logo, "logo", json);
  905. itemCount += SafeGetJSONString(teamInfo.Name, "name", json);
  906. itemCount += SafeGetJSONString(teamInfo.UpdatedDate, "updated_at", json);
  907. return itemCount;
  908. }
  909. bool TwitchREST::SafeGetJSONBroadCastType(BroadCastType& type, [[maybe_unused]] const char*key, const Aws::Utils::Json::JsonView& json) const
  910. {
  911. bool success = false;
  912. AZStd::string typeName;
  913. if( SafeGetJSONString(typeName, "broadcast_type", json) )
  914. {
  915. BroadCastType tempType = GetBroadCastTypeFromName(typeName);
  916. if (tempType != BroadCastType::Default)
  917. {
  918. success = true;
  919. type = tempType;
  920. }
  921. }
  922. return success;
  923. }
  924. bool TwitchREST::SafeGetJSONVideoChannel(VideoChannelInfo& channelInfo, const Aws::Utils::Json::JsonView& json) const
  925. {
  926. // assumes the json doc contains
  927. // "channel": { "_id": "20694610", "display_name" : "Towelliee", "name" : "towelliee" }
  928. bool success = false;
  929. if (json.ValueExists("channel"))
  930. {
  931. Aws::Utils::Json::JsonView jsonChannel(json.GetObject("channel"));
  932. SafeGetJSONString(channelInfo.ID, "_id", jsonChannel);
  933. SafeGetJSONString(channelInfo.DisplayName, "display_name", jsonChannel);
  934. SafeGetJSONString(channelInfo.Name, "name", jsonChannel);
  935. success = true;
  936. }
  937. return success;
  938. }
  939. bool TwitchREST::SafeGetJSONVideoFPS(FPSInfo & fps, const Aws::Utils::Json::JsonView& json) const
  940. {
  941. // assumes the json doc contains
  942. // "fps": { "chunked": 59.9997939597903, "high" : 30.2491085172346, "low" : 30.249192959941, "medium" : 30.2491085172346, "mobile" : 30.249192959941 }
  943. bool success = false;
  944. if (json.ValueExists("fps"))
  945. {
  946. Aws::Utils::Json::JsonView jsonFPS(json.GetObject("fps"));
  947. SafeGetJSONdouble(fps.Chunked, "chunked", jsonFPS);
  948. SafeGetJSONdouble(fps.High, "high", jsonFPS);
  949. SafeGetJSONdouble(fps.Low, "low", jsonFPS);
  950. SafeGetJSONdouble(fps.Medium, "medium", jsonFPS);
  951. SafeGetJSONdouble(fps.Mobile, "mobile", jsonFPS);
  952. success = true;
  953. }
  954. return success;
  955. }
  956. bool TwitchREST::SafeGetJSONVideoPreview(PreviewInfo& preview, const Aws::Utils::Json::JsonView& json) const
  957. {
  958. // assumes the json doc contains
  959. // "preview": { "large": "https://.../thumb102381501-640x360.jpg","medium" : "https://s...180.jpg","small" : "https://...81501-80x45.jpg", "template" : "https://.../thumb102381501-{width}x{height}.jpg" }
  960. bool success = false;
  961. if (json.ValueExists("preview"))
  962. {
  963. Aws::Utils::Json::JsonView jsonValue(json.GetObject("preview"));
  964. SafeGetJSONString(preview.Large, "large", jsonValue);
  965. SafeGetJSONString(preview.Medium, "medium", jsonValue);
  966. SafeGetJSONString(preview.Small, "small", jsonValue);
  967. SafeGetJSONString(preview.Template, "template", jsonValue);
  968. success = true;
  969. }
  970. return success;
  971. }
  972. bool TwitchREST::SafeGetJSONVideoResolutions(ResolutionsInfo& resolutions, const Aws::Utils::Json::JsonView& json) const
  973. {
  974. // assumes the json doc contains
  975. // "resolutions": {"chunked": "1920x1080","high" : "1280x720","low" : "640x360","medium" : "852x480","mobile" : "400x226"}
  976. bool success = false;
  977. if (json.ValueExists("resolutions"))
  978. {
  979. Aws::Utils::Json::JsonView jsonValue(json.GetObject("resolutions"));
  980. SafeGetJSONString(resolutions.Chunked, "chunked", jsonValue);
  981. SafeGetJSONString(resolutions.High, "high", jsonValue);
  982. SafeGetJSONString(resolutions.Low, "low", jsonValue);
  983. SafeGetJSONString(resolutions.Medium, "medium", jsonValue);
  984. SafeGetJSONString(resolutions.Mobile, "mobile", jsonValue);
  985. success = true;
  986. }
  987. return success;
  988. }
  989. bool TwitchREST::SafeGetJSONVideoThumbnailInfo(ThumbnailInfo& info, const char *key, const Aws::Utils::Json::JsonView& json) const
  990. {
  991. // assumes the json doc contains
  992. // "<key>": [{"type": "generated", "url" : "https://.../thumb102381501-640x360.jpg"}],
  993. bool success = false;
  994. if (json.ValueExists(key))
  995. {
  996. success = true;
  997. Aws::Utils::Array<Aws::Utils::Json::JsonView> jsonArray( json.GetArray(key) );
  998. for (size_t index = 0; index < jsonArray.GetLength(); index++)
  999. {
  1000. Aws::Utils::Json::JsonView item(jsonArray.GetItem(index));
  1001. AZStd::string temp;
  1002. if( SafeGetJSONString(temp, "type", item) )
  1003. {
  1004. info.Type = temp;
  1005. temp.clear();
  1006. }
  1007. if (SafeGetJSONString(temp, "url", item))
  1008. {
  1009. info.Url = temp;
  1010. temp.clear();
  1011. }
  1012. }
  1013. }
  1014. return success;
  1015. }
  1016. bool TwitchREST::SafeGetJSONVideoThumbnails(ThumbnailsInfo& thumbnails, const Aws::Utils::Json::JsonView& json) const
  1017. {
  1018. // assumes the json doc contains
  1019. // "thumbnails": {"large": [{"type": "...", "url" : "..."}],"medium" : [{"type": "...", "url" : "..."}],"small" : [{"type": "...", "url" : "..."}],"template" : [{"type": "...", "url" : "..."}] }
  1020. bool success = false;
  1021. if (json.ValueExists("thumbnails"))
  1022. {
  1023. Aws::Utils::Json::JsonView jsonValue(json.GetObject("thumbnails"));
  1024. SafeGetJSONVideoThumbnailInfo(thumbnails.Large, "large", jsonValue);
  1025. SafeGetJSONVideoThumbnailInfo(thumbnails.Medium, "medium", jsonValue);
  1026. SafeGetJSONVideoThumbnailInfo(thumbnails.Small, "small", jsonValue);
  1027. SafeGetJSONVideoThumbnailInfo(thumbnails.Template, "template", jsonValue);
  1028. success = true;
  1029. }
  1030. return success;
  1031. }
  1032. bool TwitchREST::SafeGetChannelCommunityInfo(CommunityInfo & info, const Aws::Utils::Json::JsonView& json) const
  1033. {
  1034. // assumes the json doc contains
  1035. // { "_id": "", "avatar_image_url": "", "cover_image_url": "", "description": "","description_html": "","language": "", "name": "", "owner_id": "", "rules": "", "rules_html": "", "summary": "" }
  1036. AZ::u64 itemCount = 0;
  1037. itemCount += SafeGetJSONString(info.ID, "_id", json);
  1038. itemCount += SafeGetJSONString(info.AvatarImageURL, "avatar_image_url", json);
  1039. itemCount += SafeGetJSONString(info.CoverImageURL, "cover_image_url", json);
  1040. itemCount += SafeGetJSONString(info.Description, "description", json);
  1041. itemCount += SafeGetJSONString(info.DescriptionHTML, "description_html", json);
  1042. itemCount += SafeGetJSONString(info.Language, "language", json);
  1043. itemCount += SafeGetJSONString(info.Name, "name", json);
  1044. itemCount += SafeGetJSONString(info.OwnerID, "owner_id", json);
  1045. itemCount += SafeGetJSONString(info.Rules, "rules", json);
  1046. itemCount += SafeGetJSONString(info.RulesHTML, "rules_html", json);
  1047. itemCount += SafeGetJSONString(info.Summary, "summary", json);
  1048. return (itemCount > 0);
  1049. }
  1050. AZStd::string TwitchREST::GetPresenceAvailabilityName(PresenceAvailability availability) const
  1051. {
  1052. auto itr = m_availabilityMap.find(availability);
  1053. if( itr != m_availabilityMap.end() )
  1054. {
  1055. return itr->second;
  1056. }
  1057. return "";
  1058. }
  1059. AZStd::string TwitchREST::GetPresenceActivityTypeName(PresenceActivityType activityType) const
  1060. {
  1061. auto itr = m_activityTypeMap.find(activityType);
  1062. if (itr != m_activityTypeMap.end())
  1063. return itr->second;
  1064. return "";
  1065. }
  1066. PresenceAvailability TwitchREST::GetPresenceAvailability(const AZStd::string& name) const
  1067. {
  1068. for(const auto& i: m_availabilityMap)
  1069. if( i.second == name)
  1070. return i.first;
  1071. return PresenceAvailability::Unknown;
  1072. }
  1073. PresenceActivityType TwitchREST::GetPresenceActivityType(const AZStd::string& name) const
  1074. {
  1075. for (const auto& i : m_activityTypeMap)
  1076. if (i.second == name)
  1077. return i.first;
  1078. return PresenceActivityType::Unknown;
  1079. }
  1080. AZStd::string TwitchREST::GetBroadCastTypeNameFromType(BroadCastType type) const
  1081. {
  1082. AZStd::string name;
  1083. AZ::u64 bits = static_cast<AZ::u64>(type);
  1084. if( bits & static_cast<AZ::u64>(BroadCastType::Archive) )
  1085. {
  1086. name += "archive";
  1087. }
  1088. if( bits & static_cast<AZ::u64>(BroadCastType::Highlight) )
  1089. {
  1090. if( !name.empty() )
  1091. name += ",";
  1092. name += "highlight";
  1093. }
  1094. if (bits & static_cast<AZ::u64>(BroadCastType::Upload))
  1095. {
  1096. if (!name.empty())
  1097. name += ",";
  1098. name += "upload";
  1099. }
  1100. return name;
  1101. }
  1102. BroadCastType TwitchREST::GetBroadCastTypeFromName(const AZStd::string& name) const
  1103. {
  1104. AZ::u64 bits = 0;
  1105. if ( name.find("archive") != AZStd::string::npos)
  1106. bits |= static_cast<AZ::u64>(BroadCastType::Archive);
  1107. if (name.find("highlight") != AZStd::string::npos)
  1108. bits |= static_cast<AZ::u64>(BroadCastType::Highlight);
  1109. if (name.find("upload") != AZStd::string::npos)
  1110. bits |= static_cast<AZ::u64>(BroadCastType::Upload);
  1111. return static_cast<BroadCastType>(bits);
  1112. }
  1113. AZ::u64 TwitchREST::GetComercialLength(CommercialLength length) const
  1114. {
  1115. AZ::u64 lengthInSeconds = 0;
  1116. if (length == CommercialLength::T60Seconds)
  1117. lengthInSeconds = 60;
  1118. else if (length == CommercialLength::T90Seconds)
  1119. lengthInSeconds = 90;
  1120. else if (length == CommercialLength::T120Seconds)
  1121. lengthInSeconds = 120;
  1122. else if (length == CommercialLength::T150Seconds)
  1123. lengthInSeconds = 150;
  1124. else if (length == CommercialLength::T180Seconds)
  1125. lengthInSeconds = 180;
  1126. else
  1127. lengthInSeconds = 30; // default is CommercialLength::T30Seconds
  1128. return lengthInSeconds;
  1129. }
  1130. }