rott/kf_sources/XInterface/Classes/IRCLink.uc
2026-07-14 20:27:09 +07:00

611 lines
14 KiB
Ucode

class IRCLink extends BufferedTCPLink;
var IpAddr ServerIpAddr;
var string ServerAddress;
var int ServerPort;
var string NickName;
var string UserIdent;
var string FullName;
var string DefaultChannel;
var localized string InvalidAddressText;
var localized string ErrorBindingText;
var localized string ResolveFailedText;
var localized string ConnectedText;
var localized string ConnectingToText;
var localized string TimeOutError;
var localized string InviteString;
var localized string NickInUseText;
var localized string NickInvalidText;
var IRC_System SystemPage;
var string DisconnectReason;
var string VersionString;
var transient float SinceLastLevCheck;
function PostBeginPlay()
{
Super.PostBeginPlay();
Disable('Tick');
}
function Connect(IRC_System InSystemPage, string InServer, string InNickName, string InUserIdent, string InFullName, string InDefaultChannel)
{
local int i;
log("IRCLink Connect:"$InServer$InNickName$InUserIdent$InFullName$InDefaultChannel,'IRC');
SystemPage = InSystemPage;
NickName = InNickName;
FullName = InFullName;
UserIdent = InUserIdent;
DefaultChannel = InDefaultChannel;
i = InStr(InServer, ":");
if(i == -1)
{
ServerAddress = InServer;
ServerPort = 6667;
}
else
{
ServerAddress = Left(InServer, i);
ServerPort = Int(Mid(InServer, i+1));
}
ResetBuffer();
ServerIpAddr.Port = ServerPort;
SetTimer(20, False);
SystemPage.SystemText( ConnectingToText@ServerAddress );
Resolve( ServerAddress );
}
function string ChopLeft(string Text)
{
while(Text != "" && InStr(": !", Left(Text, 1)) != -1)
Text = Mid(Text, 1);
return Text;
}
function string RemoveNickPrefix(string Nick)
{
while(Nick != "" && InStr(":@+%", Left(Nick, 1)) != -1)
Nick = Mid(Nick, 1);
return Nick;
}
function string Chop(string Text)
{
while(Text != "" && InStr(": !", Left(Text, 1)) != -1)
Text = Mid(Text, 1);
while(Text != "" && InStr(": !", Right(Text, 1)) != -1)
Text = Left(Text, Len(Text)-1);
return Text;
}
function Resolved( IpAddr Addr )
{
// Set the address
ServerIpAddr.Addr = Addr.Addr;
// Handle failure.
if( ServerIpAddr.Addr == 0 )
{
if(SystemPage != None)
{
SystemPage.SystemText( InvalidAddressText );
SystemPage.Disconnect();
}
return;
}
// Display success message.
Log( "UT2004 IRCLink: Server is "$ServerAddress$":"$ServerIpAddr.Port,'IRC' );
// Bind the local port.
if( BindPort() == 0 )
{
if(SystemPage != None)
{
SystemPage.SystemText( ErrorBindingText );
SystemPage.Disconnect();
}
return;
}
Open( ServerIpAddr );
}
event Closed()
{
if(SystemPage != None)
SystemPage.Disconnect();
}
// Host resolution failue.
function ResolveFailed()
{
if(SystemPage != None)
{
SystemPage.SystemText(ResolveFailedText);
SystemPage.Disconnect();
}
}
event Timer()
{
if(SystemPage != None)
{
SystemPage.SystemText( TimeOutError );
SystemPage.Disconnect();
log("Failed to resolve "$ServerAddress,'IRC');
}
return;
}
event Opened()
{
SetTimer(0, False);
if(SystemPage != None)
SystemPage.SystemText(ConnectedText);
Enable('Tick');
GotoState('LoggingIn');
}
function Tick(float DeltaTime)
{
local string Line;
DoBufferQueueIO();
if(ReadBufferedLine(Line))
ProcessInput(Line);
Super.Tick(DeltaTime);
if( GetStateName() == 'LoggedIn' )
{
// Every 5 seconds, keep the IRC client away string up to date.
SinceLastLevCheck += DeltaTime;
if(SinceLastLevCheck > 5)
{
if(SystemPage != None)
SystemPage.UpdateAway();
SinceLastLevCheck = 0;
}
}
}
function SendCommandText(string Text)
{
local int i, j;
local string Cmd, Temp, Temp2;
// Process fake commands like /MSG
if(Left(Text, 4) ~= "MSG ")
Text = "PRIVMSG "$Mid(Text, 4);
if(Left(Text, 6) ~= "LEAVE ")
Text = "PART "$Mid(Text, 6);
// Add colons for commands: PRIVMSG, QUIT, KILL, KICK, NOTICE
i = InStr(Text, " ");
if(i != -1)
{
Cmd = Caps(Left(Text, i));
Text = Cmd$Mid(Text, i);
switch(Cmd)
{
// CMD nick :message
case "PRIVMSG":
case "NOTICE":
case "KILL":
Temp = ChopLeft(Mid(Text, i+1));
i = InStr(Temp, " ");
if(i != -1)
Text = Cmd@Left(Temp, i)$" :"$ChopLeft(Mid(Temp, i+1));
break;
// hack for CTCP
case "CTCP":
Temp = ChopLeft(Mid(Text, i+1));
i = InStr(Temp, " ");
if(i != -1)
Text = "PRIVMSG "$Left(Temp, i)$" :"$Chr(1)$ChopLeft(Mid(Temp, i+1))$Chr(1);
break;
// CMD #channel nick :message
case "KICK":
Temp = ChopLeft(Mid(Text, i+1));
i = InStr(Temp, " ");
if(i != -1)
{
Temp2 = ChopLeft(Mid(Temp, i+1));
j = InStr(Temp2, " ");
if(j != -1)
Text = Cmd@Left(Temp, i)@Left(Temp2, j)$" :"$ChopLeft(Mid(Temp2, j+1));
}
break;
// CMD :message
case "QUIT":
Text = Cmd$" :"$ChopLeft(Mid(Text, i+1));
break;
}
}
else
Text = Caps(Text);
SendBufferedData(Text$CRLF);
}
function SendChannelText(string Channel, string Text)
{
SendBufferedData("PRIVMSG "$Channel$" :"$Text$CRLF);
}
function SendChannelAction(string Channel, string Text)
{
SendBufferedData("PRIVMSG "$Channel$" :"$Chr(1)$"ACTION "$Text$Chr(1)$CRLF);
}
function ProcessInput(string Line)
{
// Respond to PING
if(Left(Line, 5) == "PING ")
SendBufferedData("PONG "$Mid(Line, 5)$CRLF);
}
state LoggingIn
{
function Timer()
{
SendBufferedData("NICK "$NickName$CRLF);
SetTimer(1, false);
}
function ProcessInput(string Line)
{
local string Temp;
local IRC_NewNick NewNickDialog;
log("LoggingIn: "$Line,'IRC');
Temp = ParseDelimited(Line, " ", 2);
if(ParseDelimited(Line, " ", 1)== "ERROR")
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, ":", 2, True)));
if( Temp == "433" )
{
// Nick in use
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
SetTimer(0, false);
// If we dont have it up already..
if( IRC_NewNick(SystemPage.Controller.TopPage()) == None )
{
// Pop up 'choose a new nickname' dialog
if( SystemPage.Controller.OpenMenu("xinterface.IRC_NewNick") )
{
NewNickDialog = IRC_NewNick(SystemPage.Controller.TopPage());
NewNickDialog.NewNickPrompt.Caption = NickInUseText;
NewNickDialog.SystemPage = SystemPage;
}
}
}
else
if( Temp=="432" )
{
// Invalid nick
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
SetTimer(0, false);
if( IRC_NewNick(SystemPage.Controller.TopPage()) == None )
{
if( SystemPage.Controller.OpenMenu("xinterface.IRC_NewNick") )
{
NewNickDialog = IRC_NewNick(SystemPage.Controller.TopPage());
NewNickDialog.NewNickPrompt.Caption = NickInvalidText;
NewNickDialog.SystemPage = SystemPage;
}
}
}
else
if( Int(Temp) != 0 )
{
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 3, True)));
SetTimer(0, False);
GotoState('LoggedIn');
}
Global.ProcessInput(Line);
}
function SendCommandText(string Text)
{
Global.SendCommandText(Text);
if(ParseDelimited(Text, " ", 1) ~= "NICK")
{
SystemPage.ChangedNick(NickName, Chop(ParseDelimited(Text, " ", 2)));
}
}
Begin:
SendBufferedData("USER "$UserIdent$" localhost "$ServerAddress$" :"$FullName$CRLF);
SendBufferedData("NICK "$NickName$CRLF);
SetTimer(1, false);
}
state LoggedIn
{
function ProcessInput(string Line)
{
local string Temp, Temp2, Temp3;
local bool bAddModifier;
local int i;
local string Command;
Global.ProcessInput(Line);
if( SystemPage == None )
return;
//log("ProcessInput: "$Line,'IRC');
Command = ParseDelimited(Line, " ", 2);
if(ParseDelimited(Line, " ", 1) == "ERROR")
{
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, ":", 2, True)));
}
else if(Command == "JOIN")
{
Temp = ParseDelimited(Line, ":!", 2);
//Log("JOIN:"$Line$" Temp:"$Temp$" NickName:"$NickName);
if(Temp ~= NickName)
Temp = "";
SystemPage.JoinedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
}
else if(Command == "PART")
{
Temp = ParseDelimited(Line, ":!", 2);
if(Temp ~= NickName)
Temp = "";
SystemPage.PartedChannel(Chop(ParseDelimited(Line, " ", 3)), Temp);
}
else if(Command == "NICK")
{
SystemPage.ChangedNick(ParseDelimited(Line, ":!", 2), Chop(ParseDelimited(Line, " ", 3)));
}
else if(Command == "QUIT")
{
SystemPage.UserQuit(ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, " ", 3, True)));
}
else if(Command == "353") // NAMES
{
Temp2 = ParseDelimited(Line, "#", 2);
Temp2 = ParseDelimited(Temp2, " :", 1);
Temp = ParseDelimited(Line, ":", 3, True);
while(Temp != "")
{
// Nickname
Temp3 = ParseDelimited(Temp, " ", 1);
SystemPage.UserInChannel("#"$Temp2, RemoveNickPrefix(Temp3));
if(Left(Temp3, 1) == "@")
SystemPage.ChangeOp("#"$Temp2, RemoveNickPrefix(Temp3), True);
else
if(Left(Temp3, 1) == "%")
SystemPage.ChangeHalfOp("#"$Temp2, RemoveNickPrefix(Temp3), True);
else
if(Left(Temp3, 1) == "+")
SystemPage.ChangeVoice("#"$Temp2, RemoveNickPrefix(Temp3), True);
Temp = ParseDelimited(Temp, " ", 2, True);
}
}
else if(Command == "333") // Channel formed info
{
}
else if(Command == "366") // End of NAMES
{
}
else if(Command == "331") // RPL_NOTOPIC
{
}
else if(Command == "332") // RPL_TOPIC
{
Temp = Chop(ParseDelimited(Line, " ", 4));
Temp2 = Chop(ParseDelimited(Line, " ", 5));
SystemPage.ChangeTopic(Temp, Temp2);
}
else if(Command == "341") // RPL_INVITING
{
}
else if(Command == "301") // RPL_AWAY
{
SystemPage.IsAway(Chop(ParseDelimited(Line, " ", 4)), ChopLeft(ParseDelimited(Line, ":", 3, True)));
}
else if(Command == "NOTICE")
{
Temp = ParseDelimited(Line, ": ", 2);
Temp2 = ParseDelimited(Line, ":! ", 2);
if(InStr(Temp, "!") != -1 && InStr(Temp2, ".") == -1)
{
// it's a Nick.
Temp = ChopLeft(ParseDelimited(Line, " ", 4, True));
if(Asc(Left(Temp, 1)) == 1 && Asc(Right(Temp, 1)) == 1)
SystemPage.CTCP("", Temp2, Mid(Temp, 1, Len(Temp) - 2));
else
SystemPage.UserNotice(Temp2, Temp);
}
else
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
}
else if(Int(Command)!= 0)
{
SystemPage.SystemText(ChopLeft(ParseDelimited(Line, " ", 4, True)));
}
else if(Command == "MODE")
{
// channel mode
Temp = Chop(ParseDelimited(Line, " ", 4));
// channel
Temp3 = Chop(ParseDelimited(Line, " ", 3));
i = 5;
bAddModifier = True;
while(Temp != "")
{
Temp2 = Left(Temp, 1);
if(Temp2 == "+")
bAddModifier = True;
if(Temp2 == "-")
bAddModifier = False;
if(Temp2 == "o")
{
SystemPage.ChangeOp(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
i++;
}
if(Temp2 == "h")
{
SystemPage.ChangeHalfOp(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
i++;
}
if(Temp2 == "v")
{
SystemPage.ChangeVoice(Temp3, Chop(ParseDelimited(Line, " ", i)), bAddModifier);
i++;
}
Temp = Mid(Temp, 1);
}
SystemPage.ChangeMode(Temp3, ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, " ", 4, True)));
}
else if(Command == "KICK")
{
// FIXME: handle multiple kicks in a single message
SystemPage.KickUser(Chop(ParseDelimited(Line, " ", 3)), Chop(ParseDelimited(Line, " ", 4)), ParseDelimited(Line, ":!", 2), ChopLeft(ParseDelimited(Line, ":", 3, True)));
}
else if(Command == "INVITE")
{
SystemPage.SystemText(ParseDelimited(Line, ":!", 2)@InviteString@ParseDelimited(Line, ":", 3));
}
else if(Command == "PRIVMSG")
{
Temp = Chop(ParseDelimited(Line, " ", 3));
Temp2 = ChopLeft(ParseDelimited(Line, " ", 4, True));
if(Mid(Temp2, 1, 7) == "ACTION " && Asc(Left(Temp2, 1))==1 && Asc(Right(Temp2, 1))==1)
{
Temp2 = Mid(Temp2, 8);
Temp2 = Left(Temp2, Len(Temp2) - 1);
if(Temp != "" && InStr("&#@", Left(Temp, 1)) != -1)
SystemPage.ChannelAction(Temp, ParseDelimited(Line, ":!", 2), Temp2);
else
SystemPage.PrivateAction(ParseDelimited(Line, ":!", 2), Temp2);
}
else
if(Asc(Left(Temp2, 1))==1 && Asc(Right(Temp2, 1))==1)
{
Temp2 = Mid(Temp2, 1, Len(Temp2) - 2);
switch(Temp2)
{
case "VERSION":
// if _RO_
SendBufferedData("NOTICE "$ParseDelimited(Line, ":!", 2)$" :"$Chr(1)$"VERSION "$VersionString$Level.ROVersion$Chr(1)$CRLF);
//else
//SendBufferedData("NOTICE "$ParseDelimited(Line, ":!", 2)$" :"$Chr(1)$"VERSION "$VersionString$Level.EngineVersion$Chr(1)$CRLF);
SystemPage.CTCP(Temp, ParseDelimited(Line, ":!", 2), Temp2);
break;
default:
SystemPage.CTCP(Temp, ParseDelimited(Line, ":!", 2), Temp2);
break;
}
}
else
{
if(Temp != "" && InStr("&#@", Left(Temp, 1)) != -1)
SystemPage.ChannelText(Temp, ParseDelimited(Line, ":!", 2), Temp2);
else
SystemPage.PrivateText(ParseDelimited(Line, ":!", 2), Temp2);
}
}
else if(Command == "TOPIC")
{
Temp = Chop(ParseDelimited(Line, " ", 3));
Temp2 = ChopLeft(ParseDelimited(Line, " ", 4, True));
SystemPage.ChangeTopic(Temp, Temp2);
}
//SystemPage.RefreshUI();
}
Begin:
if(DefaultChannel != "")
JoinChannel(DefaultChannel);
}
function JoinChannel(string Channel)
{
log("IRCLink: JoinChannel: "$Channel,'IRC');
if(Left(Channel, 1) == "#")
SendBufferedData("JOIN "$Channel$CRLF);
}
function PartChannel(string Channel)
{
if(Left(Channel, 1) == "#")
SendBufferedData("PART "$Channel$CRLF);
}
function SetNick(string NewNick)
{
SendBufferedData("NICK "$NewNick$CRLF);
}
function SetAway(string AwayText)
{
SendBufferedData("AWAY :"$AwayText$CRLF);
}
function DestroyLink()
{
SystemPage = None;
if(IsConnected())
{
SendText("QUIT :"$DisconnectReason$CRLF);
Close();
}
else
Destroy();
}
defaultproperties
{
InvalidAddressText="Invalid server address, aborting."
ErrorBindingText="Error binding local port, aborting."
TimeOutError="Timeout connecting to server."
ResolveFailedText="Failed to resolve server address, aborting."
ConnectedText="Connected."
ConnectingToText="Connecting to"
DisconnectReason="Disconnected"
VersionString="Killing Floor IRC Client version "
InviteString="invites you to join"
NickInUseText="Nickname Already In Use"
NickInvalidText="Nickname Is Invalid"
}