TwitchREST.cpp 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469
  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. }