Adjusted workflow.
This commit is contained in:
653
src/io/socket/rest/Spotify.cpp
Normal file
653
src/io/socket/rest/Spotify.cpp
Normal file
@@ -0,0 +1,653 @@
|
||||
#include "ehs/io/socket/rest/Spotify.h"
|
||||
#include "ehs/io/socket/DNS.h"
|
||||
#include "ehs/system/System.h"
|
||||
#include "ehs/URI.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
const Str_8 Spotify::trackUriPrefix = "https://open.spotify.com/track/";
|
||||
|
||||
Spotify::~Spotify()
|
||||
{
|
||||
client.Release();
|
||||
}
|
||||
|
||||
Spotify::Spotify()
|
||||
: forceVerify(false)
|
||||
{
|
||||
}
|
||||
|
||||
Spotify::Spotify(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array<Str_8>& scopes, const bool forceVerify)
|
||||
: client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify)
|
||||
{
|
||||
}
|
||||
|
||||
bool Spotify::Authorize()
|
||||
{
|
||||
Str_8 scopesFinal;
|
||||
|
||||
for (UInt_64 i = 0; i < scopes.Size(); ++i)
|
||||
{
|
||||
scopesFinal += scopes[i];
|
||||
|
||||
if (i < scopes.Size() - 1)
|
||||
scopesFinal += "%20";
|
||||
}
|
||||
|
||||
Str_8 rURI = URI::Encode(redURI);
|
||||
|
||||
Str_8 uri = "https://accounts.spotify.com/authorize?client_id=" + clientId + "&redirect_uri=" + rURI +
|
||||
"&response_type=code&show_dialog=" + (forceVerify ? "true" : "false") + "&scope=" +
|
||||
scopesFinal;
|
||||
|
||||
TCP server(AddrType::IPV4);
|
||||
server.Initialize();
|
||||
server.Bind(DNS::Resolve("localhost"), 65534);
|
||||
server.Listen();
|
||||
|
||||
System::OpenURI(uri);
|
||||
|
||||
TCP* cbClient = server.Accept();
|
||||
|
||||
Request cbReq = cbClient->RecvReq();
|
||||
|
||||
if (cbReq.GetResource() != "/callback")
|
||||
{
|
||||
Response resp(423, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody("<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Hostile Information Received</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Response resp(200, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody("<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Authentication Successful</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
server.Release();
|
||||
|
||||
SSL accounts(AddrType::IPV4);
|
||||
accounts.Initialize();
|
||||
accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port);
|
||||
|
||||
Request authReq(Verb::POST, "/api/token");
|
||||
authReq.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
authReq.BasicAuth(clientId, secret);
|
||||
authReq.AddToBody("grant_type", "authorization_code");
|
||||
authReq.AddToBody("code", cbReq.GetQuery("code"));
|
||||
authReq.AddToBody("redirect_uri", rURI);
|
||||
|
||||
accounts.SendReq(authReq);
|
||||
|
||||
Response authRes = accounts.RecvRes();
|
||||
|
||||
accounts.Release();
|
||||
|
||||
if (authRes.GetCode() == 400)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Could not authorize with Spotify because the client id was invalid.");
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (authRes.GetCode() == 403)
|
||||
{
|
||||
EHS_LOG_INT("Error", 1, "Could not authorize with Spotify because the secret was invalid.");
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (authRes.GetCode() != 200)
|
||||
{
|
||||
EHS_LOG_INT("Error", 2, "Could not authorize with Spotify with code " + Str_8::FromNum(authRes.GetCode()) + ".");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Json authResJson = authRes.GetJson();
|
||||
|
||||
JsonObj* value = (JsonObj*)authResJson.GetValue();
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
JsonVar* tokenVar = value->GetVar("access_token");
|
||||
if (!tokenVar)
|
||||
return false;
|
||||
|
||||
JsonVar* rTokenVar = value->GetVar("refresh_token");
|
||||
if (!rTokenVar)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*)tokenVar->GetValue())->value;
|
||||
rToken = ((JsonStr*)rTokenVar->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetVolume(const UInt_8 level)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/volume");
|
||||
req.AddQuery("volume_percent", Str_8::FromNum(level));
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Play()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/play");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.Release();
|
||||
client.Initialize();
|
||||
client.Connect("", SSL::HTTPS_Port);
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Pause()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/pause");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetRepeat(const SpotifyState state)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Str_8 result;
|
||||
switch (state)
|
||||
{
|
||||
case SpotifyState::TRACK:
|
||||
result = "track";
|
||||
break;
|
||||
case SpotifyState::CONTEXT:
|
||||
result = "context";
|
||||
break;
|
||||
case SpotifyState::OFF:
|
||||
result = "off";
|
||||
break;
|
||||
}
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/repeat");
|
||||
req.AddQuery("state", result);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetShuffle(const bool state)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/repeat");
|
||||
req.AddQuery("state", state ? "true" : "false");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SearchTrack(Vector<Str_8>& artists, Str_8& id, Str_8& name)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/search");
|
||||
|
||||
Str_8 q = "artist%3A";
|
||||
|
||||
for (UInt_64 i = 0; i < artists.Size(); ++i)
|
||||
{
|
||||
q += artists[i].ReplaceAll(" ", "+");
|
||||
if (i != artists.Size() - 1)
|
||||
q += "%2C+";
|
||||
}
|
||||
|
||||
q += "+track%3A" + name.ReplaceAll(" ", "+");
|
||||
|
||||
req.AddQuery("q", q);
|
||||
req.AddQuery("type", "track");
|
||||
req.AddQuery("limit", "1");
|
||||
req.AddQuery("offset", "0");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return SearchTrack(artists, name, id);
|
||||
}
|
||||
|
||||
Json body = res.GetJson();
|
||||
|
||||
JsonNum* total = (JsonNum*)body.RetrieveValue("tracks.total");
|
||||
if (!total || total->value == 0.0f)
|
||||
return 0;
|
||||
|
||||
JsonObj* item = (JsonObj*)body.RetrieveValue("tracks.items[0]");
|
||||
if (!item)
|
||||
return 0;
|
||||
|
||||
JsonVar* artistsVar = item->GetVar("artists");
|
||||
if (!artistsVar)
|
||||
return 0;
|
||||
|
||||
JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue();
|
||||
if (!artistsArray)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackIdVar = item->GetVar("id");
|
||||
if (!trackIdVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue();
|
||||
if (!trackIdValue)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackNameVar = item->GetVar("name");
|
||||
if (!trackNameVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue();
|
||||
if (!trackNameValue)
|
||||
return 0;
|
||||
|
||||
artists.Resize(artistsArray->Size());
|
||||
|
||||
for (UInt_64 i = 0; i < artistsArray->Size(); ++i)
|
||||
{
|
||||
JsonObj* artistObj = (JsonObj*)(*artistsArray)[i];
|
||||
|
||||
JsonVar* artistNameVar = artistObj->GetVar("name");
|
||||
|
||||
JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue();
|
||||
|
||||
artists[i] = artistNameValue->value;
|
||||
}
|
||||
|
||||
id = trackIdValue->value;
|
||||
|
||||
name = trackNameValue->value;
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::GetPlayingTrack(Vector<Str_8>& artists, Str_8& id, Str_8& name)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/me/player/currently-playing");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
|
||||
return GetPlayingTrack(artists, id, name);
|
||||
}
|
||||
|
||||
Json result = res.GetJson();
|
||||
|
||||
JsonObj* itemObj = (JsonObj*)result.RetrieveValue("item");
|
||||
if (!itemObj)
|
||||
return {};
|
||||
|
||||
JsonVar* artistsVar = itemObj->GetVar("artists");
|
||||
if (!artistsVar)
|
||||
return 0;
|
||||
|
||||
JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue();
|
||||
if (!artistsArray)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackIdVar = itemObj->GetVar("id");
|
||||
if (!trackIdVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue();
|
||||
if (!trackIdValue)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackNameVar = itemObj->GetVar("name");
|
||||
if (!trackNameVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue();
|
||||
if (!trackNameValue)
|
||||
return 0;
|
||||
|
||||
artists.Resize(artistsArray->Size());
|
||||
for (UInt_64 i = 0; i < artists.Size(); ++i)
|
||||
{
|
||||
JsonObj* artistObj = (JsonObj*)(*artistsArray)[i];
|
||||
|
||||
JsonVar* artistNameVar = artistObj->GetVar("name");
|
||||
|
||||
JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue();
|
||||
|
||||
artists[i] = artistNameValue->value;
|
||||
}
|
||||
|
||||
id = trackIdValue->value;
|
||||
|
||||
name = trackNameValue->value;
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::GetQueue(Array<Track>& tracks)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/me/player/queue");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return GetQueue(tracks);
|
||||
}
|
||||
|
||||
Json json = res.GetJson();
|
||||
|
||||
JsonObj* root = (JsonObj*)json.GetValue();
|
||||
|
||||
JsonVar* currentVar = root->GetVar("currently_playing");
|
||||
if (!currentVar->GetValue())
|
||||
return res.GetCode();
|
||||
|
||||
JsonObj* currentObj = (JsonObj*)currentVar->GetValue();
|
||||
|
||||
JsonArray* cArtists = (JsonArray*)currentObj->GetVar("artists")->GetValue();
|
||||
|
||||
JsonArray* queue = (JsonArray*)root->GetVar("queue")->GetValue();
|
||||
|
||||
tracks.Resize(queue->Size() + 1);
|
||||
tracks[0].artists.Resize(cArtists->Size());
|
||||
|
||||
for (UInt_64 i = 0; i < cArtists->Size(); ++i)
|
||||
tracks[0].artists[i] = ((JsonStr*)((JsonObj*)(*cArtists)[i])->GetVar("name")->GetValue())->value;
|
||||
|
||||
tracks[0].id = ((JsonStr*)currentObj->GetVar("id")->GetValue())->value;
|
||||
tracks[0].name = ((JsonStr*)currentObj->GetVar("name")->GetValue())->value;
|
||||
|
||||
for (UInt_64 i = 1; i < queue->Size(); ++i)
|
||||
{
|
||||
JsonObj* trackObj = (JsonObj*)(*queue)[i - 1];
|
||||
|
||||
JsonArray* artists = (JsonArray*)trackObj->GetVar("artists")->GetValue();
|
||||
tracks[i].artists.Resize(artists->Size());
|
||||
|
||||
for (UInt_64 a = 0; a < artists->Size(); ++a)
|
||||
tracks[i].artists[a] = ((JsonStr*)((JsonObj*)(*artists)[a])->GetVar("name")->GetValue())->value;
|
||||
|
||||
tracks[i].id = ((JsonStr*)trackObj->GetVar("id")->GetValue())->value;
|
||||
tracks[i].name = ((JsonStr*)trackObj->GetVar("name")->GetValue())->value;
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::QueueTrack(const Str_8& id)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/queue");
|
||||
req.AddQuery("uri", "spotify:track:" + id);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return QueueTrack(id);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::AddTracks(const Str_8& playlistId, const Array<Str_8>& trackIds, const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
JsonObj obj(0);
|
||||
|
||||
JsonArray tracks(trackIds.Size(), 0);
|
||||
for (UInt_64 i = 0; i < trackIds.Size(); ++i)
|
||||
tracks[i] = (JsonBase*)new JsonStr("spotify:track:" + trackIds[i]);
|
||||
obj.AddVar({"uris", tracks});
|
||||
obj.AddVar({"position", (float)pos});
|
||||
|
||||
Json json(obj);
|
||||
|
||||
Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks");
|
||||
req.BearerAuth(token);
|
||||
req.SetContentType(ContentType::APP_JSON);
|
||||
req.SetBody(json.ToStr(true));
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return AddTracks(playlistId, trackIds, pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::AddTrack(const Str_8& playlistId, const Str_8& trackId, const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks");
|
||||
req.AddQuery("position", Str_8::FromNum(pos));
|
||||
req.AddQuery("uris", "spotify:track:" + trackId);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return AddTrack(playlistId, trackId, pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Skip()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/next");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Skip();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Previous()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/previous");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Seek(const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/seek");
|
||||
req.AddQuery("position_ms", Str_8::FromNum(pos));
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Seek(pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
void Spotify::StartConnection()
|
||||
{
|
||||
client.Release();
|
||||
client.Initialize();
|
||||
client.Connect("api.spotify.com", SSL::HTTPS_Port);
|
||||
}
|
||||
|
||||
bool Spotify::ReAuthorize()
|
||||
{
|
||||
SSL accounts;
|
||||
accounts.Initialize();
|
||||
accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port);
|
||||
|
||||
Request reAuth(Verb::POST, "/api/token");
|
||||
reAuth.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
reAuth.BasicAuth(clientId, secret);
|
||||
reAuth.AddToBody("grant_type", "refresh_token");
|
||||
reAuth.AddToBody("refresh_token", rToken);
|
||||
|
||||
accounts.SendReq(reAuth);
|
||||
|
||||
Response res = accounts.RecvRes();
|
||||
|
||||
accounts.Release();
|
||||
|
||||
if (res.GetCode() != 200)
|
||||
{
|
||||
EHS_LOG_INT("Error", 0, "Failed to reauthorize with Spotify with code #" + Str_8::FromNum(res.GetCode()) + ".");
|
||||
client.Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
Json json = res.GetJson();
|
||||
|
||||
JsonObj* obj = (JsonObj*)json.GetValue();
|
||||
|
||||
JsonVar* tokenVar = obj->GetVar("access_token");
|
||||
if (!tokenVar)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*)tokenVar->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Spotify::IsActive() const
|
||||
{
|
||||
return client.IsConnected();
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetClientId() const
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetSecret() const
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetRedURI() const
|
||||
{
|
||||
return redURI;
|
||||
}
|
||||
|
||||
bool Spotify::IsVerificationForced() const
|
||||
{
|
||||
return forceVerify;
|
||||
}
|
||||
}
|
150
src/io/socket/rest/Twitch.cpp
Normal file
150
src/io/socket/rest/Twitch.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "ehs/io/socket/rest/Twitch.h"
|
||||
#include "ehs/io/socket/DNS.h"
|
||||
#include "ehs/system/System.h"
|
||||
#include "ehs/URI.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
Twitch::~Twitch()
|
||||
{
|
||||
client.Release();
|
||||
}
|
||||
|
||||
Twitch::Twitch()
|
||||
: forceVerify(false)
|
||||
{
|
||||
}
|
||||
|
||||
Twitch::Twitch(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array<Str_8>& scopes, const bool forceVerify)
|
||||
: client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify)
|
||||
{
|
||||
}
|
||||
|
||||
bool Twitch::Authorize()
|
||||
{
|
||||
Str_8 scopesFinal;
|
||||
|
||||
for (UInt_64 i = 0; i < scopes.Size(); ++i)
|
||||
{
|
||||
scopesFinal += scopes[i];
|
||||
|
||||
if (i < scopes.Size() - 1)
|
||||
scopesFinal += "%20";
|
||||
}
|
||||
|
||||
Str_8 rURI = URI::Encode(redURI);
|
||||
|
||||
Str_8 uri = "https://id.twitch.tv/oauth2/authorize?client_id=" + clientId + "&redirect_uri=" + rURI +
|
||||
"&response_type=code&force_verify=" + (forceVerify ? "true" : "false") + "&scope=" +
|
||||
scopesFinal;
|
||||
|
||||
TCP server(AddrType::IPV4);
|
||||
server.Bind(DNS::Resolve("localhost"), 65535);
|
||||
server.Listen();
|
||||
|
||||
System::OpenURI(uri);
|
||||
|
||||
TCP* cbClient = server.Accept();
|
||||
|
||||
Request cbReq = cbClient->RecvReq();
|
||||
|
||||
if (cbReq.GetResource() != "/callback")
|
||||
{
|
||||
Response resp(423, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody(
|
||||
"<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Hostile Information Received</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Response resp(200, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody(
|
||||
"<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Authentication Successful</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
server.Release();
|
||||
|
||||
client.Initialize();
|
||||
client.Connect("id.twitch.tv", SSL::HTTPS_Port);
|
||||
|
||||
Request authReq(Verb::POST, "/oauth2/token");
|
||||
authReq.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
authReq.AddToBody("client_id", clientId);
|
||||
authReq.AddToBody("client_secret", secret);
|
||||
authReq.AddToBody("code", cbReq.GetQuery("code"));
|
||||
authReq.AddToBody("grant_type", "authorization_code");
|
||||
authReq.AddToBody("redirect_uri", redURI);
|
||||
|
||||
client.SendReq(authReq);
|
||||
|
||||
Response authRes = client.RecvRes();
|
||||
if (authRes.GetCode() == 400)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
EHS_LOG_INT("Error", 0, "Could not authorize with Twitch because the client id was invalid.");
|
||||
|
||||
return false;
|
||||
} else if (authRes.GetCode() == 403)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
EHS_LOG_INT("Error", 1, "Could not authorize with Twitch because the secret was invalid.");
|
||||
|
||||
return false;
|
||||
} else if (authRes.GetCode() != 200)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
EHS_LOG_INT("Error", 2, "Could not authorize with Twitch.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Json authResJson = authRes.GetJson();
|
||||
|
||||
JsonObj* value = (JsonObj*) authResJson.GetValue();
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
JsonVar* var = value->GetVar("access_token");
|
||||
if (!var)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*) var->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetClientId() const
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetSecret() const
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetRedURI() const
|
||||
{
|
||||
return redURI;
|
||||
}
|
||||
|
||||
bool Twitch::IsVerificationForced() const
|
||||
{
|
||||
return forceVerify;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetToken() const
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
177
src/io/socket/rest/TwitchChat.cpp
Normal file
177
src/io/socket/rest/TwitchChat.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "ehs/io/socket/rest/TwitchChat.h"
|
||||
#include "ehs/io/socket/DNS.h"
|
||||
#include "ehs/io/Console.h"
|
||||
|
||||
namespace ehs
|
||||
{
|
||||
TwitchChat::~TwitchChat()
|
||||
{
|
||||
UnInitialize();
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat()
|
||||
: initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const Str_8& username)
|
||||
: username(username), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const Str_8& username, const Str_8& token)
|
||||
: username(username), token(token), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const TwitchChat& chat)
|
||||
: username(chat.username), token(chat.token), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat& TwitchChat::operator=(const TwitchChat& chat)
|
||||
{
|
||||
if (this == &chat)
|
||||
return *this;
|
||||
|
||||
client = TCP();
|
||||
username = chat.username;
|
||||
token = chat.token;
|
||||
channel = Str_8();
|
||||
initialized = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TwitchChat::SetToken(const Str_8& newToken)
|
||||
{
|
||||
token = newToken;
|
||||
}
|
||||
|
||||
void TwitchChat::Initialize()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
client = TCP(ehs::AddrType::IPV4);
|
||||
client.Connect(DNS::Resolve("irc.chat.twitch.tv"), 6667);
|
||||
client.SetBlocking(false);
|
||||
|
||||
Str_8 r("PASS oauth:" + token + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
r = "NICK " + username + "\r\n";
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void TwitchChat::UnInitialize()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
client.Release();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void TwitchChat::JoinChannel(const Str_8& newChannel)
|
||||
{
|
||||
if (!initialized || channel == newChannel)
|
||||
return;
|
||||
|
||||
channel = newChannel;
|
||||
|
||||
Str_8 r("Join #" + newChannel + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::LeaveChannel()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PART #" + channel + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::SendPong()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PONG :tmi.twitch.tv\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::SendMsg(const Str_8& msg)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PRIVMSG #" + channel + " :" + msg + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
//irc.SendStr(":" + username + "!" + username + "@" + username + ".tmi.twitch.tv PRIVMSG #" + username + " :" + msg + "\r\n");
|
||||
}
|
||||
|
||||
void TwitchChat::WhisperMsg(const Str_8& user, const Str_8& msg)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PRIVMSG #jtv :/w " + user + " " + msg + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::RecvMsg()
|
||||
{
|
||||
Str_8 result;
|
||||
|
||||
Byte received[1024];
|
||||
UInt_64 recvSize = 0;
|
||||
|
||||
do
|
||||
{
|
||||
recvSize = client.Receive(received, 1024);
|
||||
if (recvSize)
|
||||
result.Push((Char_8*) received, recvSize);
|
||||
else
|
||||
break;
|
||||
} while (!result.Find("\r\n", nullptr, SearchPattern::RIGHT_LEFT));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::GetUsername() const
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::GetChannel() const
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user