I was interested in this specific task as well so I thought I’d give it a try and I just got it working, thanks to the tutorial you have linked, Ramas Socket-Listener-Tutorial (awesome as always!) and thistutorial, using the following code. There might be a better way to do it, but it works fine as far as I have tested it (Especially the parsing is not very optimized, since I don’t know the IRC format very well, I just did a quick google search and tried some things to get it working).
I put it into GameMode as this was the easiest way for me to test it but it should work in any other class as well.
#pragma once
#include "GameFramework/GameMode.h"
#include "Networking.h"
#include "TwitchTestGameMode.generated.h"
/**
*
*/
UCLASS()
class TWITCH_TEST_API ATwitchTestGameMode : public AGameMode
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
private:
FSocket* ListenerSocket;
FSocket* ConnectionSocket;
FTimerHandle timerHandle;
void SocketListener();
void SendLogin();
bool SendString(FString msg);
void ParseMessage(FString msg);
void ReceivedChatMessage(FString UserName, FString message);
};
#include "TwitchTest.h"
#include <string>
#include "TwitchTestGameMode.h"
void ATwitchTestGameMode::BeginPlay()
{
Super::BeginPlay();
FIPv4Endpoint Endpoint(FIPv4Address(127, 0, 0, 1), 6667);
FSocket* ListenerSocket = FTcpSocketBuilder(TEXT("TwitchListener"))
.AsReusable()
.BoundToEndpoint(Endpoint)
.Listening(8);
//Set Buffer Size
int32 NewSize = 0;
ListenerSocket->SetReceiveBufferSize(2 * 1024 * 1024, NewSize);
GetWorldTimerManager().SetTimer(timerHandle, this, &ATwitchTestGameMode::SocketListener, 0.01, true);
SendLogin();
}
void ATwitchTestGameMode::SocketListener()
{
TArray<uint8> ReceivedData;
uint32 Size;
bool Received = false;
while (ListenerSocket->HasPendingData(Size))
{
Received = true;
ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u));
int32 Read = 0;
ListenerSocket->Recv(ReceivedData.GetData(), ReceivedData.Num(), Read);
}
if (Received)
{
const std::string cstr(reinterpret_cast<const char*>(ReceivedData.GetData()), ReceivedData.Num());
FString fs(cstr.c_str());
ParseMessage(fs);
}
}
void ATwitchTestGameMode::ParseMessage(FString msg)
{
TArray<FString> lines;
msg.ParseIntoArrayLines(lines);
for (FString fs : lines)
{
TArray<FString> parts;
fs.ParseIntoArray(parts, TEXT(":"));
TArray<FString> meta;
parts[0].ParseIntoArrayWS(meta);
if (parts.Num() >= 2)
{
if (meta[0] == TEXT("PING"))
{
SendString(TEXT("PONG :tmi.twitch.tv"));
}
else if (meta.Num() == 3 && meta[1] == TEXT("PRIVMSG"))
{
FString message=parts[1];
if (parts.Num() > 2)
{
for (int i = 2; i < parts.Num(); i++)
{
message += TEXT(":") + parts*;
}
}
FString username;
FString tmp;
meta[0].Split(TEXT("!"), &username, &tmp);
ReceivedChatMessage(username, message);
continue;
}
}
}
}
void ATwitchTestGameMode::ReceivedChatMessage(FString UserName, FString message)
{
UE_LOG(LogTemp, Warning, TEXT("%s: %s"), *UserName, *message);
}
void ATwitchTestGameMode::SendLogin()
{
auto ResolveInfo = ISocketSubsystem::Get()->GetHostByName("irc.twitch.tv");
while (!ResolveInfo->IsComplete());
if (ResolveInfo->GetErrorCode() != 0)
{
UE_LOG(LogTemp, Warning, TEXT("Couldn't resolve hostname."));
return;
}
const FInternetAddr* Addr = &ResolveInfo->GetResolvedAddress();
uint32 OutIP = 0;
Addr->GetIp(OutIP);
int32 port = 6667;
TSharedRef<FInternetAddr> addr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
addr->SetIp(OutIP);
addr->SetPort(port);
ListenerSocket = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateSocket(NAME_Stream, TEXT("default"), false);
bool connected = ListenerSocket->Connect(*addr);
if (!connected)
{
UE_LOG(LogTemp, Warning, TEXT("Failed to connect."));
if (ListenerSocket)
ListenerSocket->Close();
return;
}
SendString(TEXT("PASS [oauth]"));
SendString(TEXT("NICK [twitch_name]"));
//SendString(TEXT("JOIN #[channel_name]"));
}
bool ATwitchTestGameMode::SendString(FString msg)
{
FString serialized = msg + TEXT("
");
TCHAR *serializedChar = serialized.GetCharArray().GetData();
int32 size = FCString::Strlen(serializedChar);
int32 sent = 0;
return ListenerSocket->Send((uint8*)TCHAR_TO_UTF8(serializedChar), size, sent);
}
And you need to add Sockets and Networking to your Build.cs file.
PublicDependencyModuleNames.AddRange(new string] { "Core", "CoreUObject", "Engine", "InputCore", "Sockets", "Networking" });
Of course you have to replace [oauth] and [twitch_name] with your corresponding oauth and twitch_name, without the square brackets.