I need to run my Delphi application in console mode so that I can run it on my VPS as a Wine emulated application on my Linux server so that it communicates via telnet to my Teamspeak server as serverquery. It needs to stay constantly stay connected and move players out of one channel into another channel, so passively using PHP is not an option. I know TS3 bots already exist, but none are programmed in Delphi for Teamspeak 3. The program works flawlessly in the Windows application, but just hangs in the console version.
I have indy setup IdTelnet1.ThreadedEvent := true in the datamodule which only seemed to somewhat help. I'm guessing somehow I need to talk with that thread, but not sure how.
I have tried to do it this way, but the program just hangs:
program TS3bot;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Unit1 in 'Unit1.pas' {DataModule1: TDataModule};
begin
try
DataModule1 := TDataModule1.Create(nil);
try
{ TODO -oUser -cConsole Main : Insert code here }
DataModule1.IdTelnet1.Connect;
DataModule1.IdTelnet1.TelnetThread.Start;
repeat
//
until (DataModule1.IdTelnet1.Connected = false);
DataModule1.IdTelnet1.TelnetThread.Stop;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
finally
writeln('Program ended.');
DataModule1.Free;
end;
end.
unit Unit1;
interface
uses
System.SysUtils, System.Classes, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdTelnet, IdGlobal;
type
TDataModule1 = class(TDataModule)
IdTelnet1: TIdTelnet;
procedure IdTelnet1Connected(Sender: TObject);
procedure IdTelnet1DataAvailable(Sender: TIdTelnet; const Buffer: TIdBytes);
procedure IdTelnet1Disconnected(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
{ Private declarations }
procedure processCommand(Command : string);
procedure processCommands;
procedure InterpetBuffer(Buffer: string);
public
{ Public declarations }
end;
Const
Elements = (3); //(Elements - 1)
ListOfOnConnectCommands : array [0..Elements] of string =
('login serverquery password',
'use 1',
'clientupdate client_nickname=NickNameServer',
'servernotifyregister event=server');
var
DataModule1: TDataModule1;
BufferNumber: integer = 0;
CommandSent : boolean = false;
CommandOK : boolean = false;
CommandNumber : integer = 0;
implementation
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
procedure pSplitIT(BreakString, BaseString: string; StringList: TStrings);
var
EndOfCurrentString: byte;
begin
StringList.Clear;
repeat
EndOfCurrentString := Pos(BreakString, BaseString);
if EndOfCurrentString = 0 then
StringList.add(BaseString)
else
StringList.add(Copy(BaseString, 1, EndOfCurrentString - 1));
BaseString := Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString);
until EndOfCurrentString = 0;
end;
procedure TDataModule1.processCommand(Command : string);
begin
writeln('processCommand: ' + Command);
IdTelnet1.SendString(Command);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
end;
procedure TDataModule1.processCommands;
var
MyString: string;
begin
if CommandNumber <= Elements then
begin
MyString := ListOfOnConnectCommands[CommandNumber];
writeln('processCommands: ' + MyString);
IdTelnet1.SendString(MyString);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
inc(CommandNumber);
//exit;
end;
end;
procedure TDataModule1.InterpetBuffer(Buffer: string);
var
MyTstringlist: Tstringlist;
MyBuffer: string;
I: integer;
clid: integer;
member, legionnaire, enteredGuestChannel: boolean;
begin
enteredGuestChannel := false;
member := false;
legionnaire := false;
inc(BufferNumber);
writeln('---------------------------------------------------------');
writeln('IdTelnet1DataAvailable BufferNumber: ' + BufferNumber.ToString);
writeln('---------------------------------------------------------');
if Pos('notifycliententerview',Buffer)>0 then
begin
writeln('----------------------');
writeln('EXIT notifycliententerview:');
writeln(Buffer);
MyTstringlist := Tstringlist.Create;
MyBuffer := Buffer;
pSplitIT(' ',MyBuffer,MyTstringlist);
writeln('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
writeln(MyTstringlist.Strings[I]);
if MyTstringlist.Strings[I] = 'ctid=45' then
begin
// client entered GUESTS CHANNEL, see if we can move them.
enteredGuestChannel := true;
end;
if Pos('client_servergroups=',MyTstringlist.Strings[I]) > 0 then
begin
if Pos('18',MyTstringlist.Strings[I]) > 0 then
begin
member := true;
end;
if Pos('19',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
if Pos('28',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
end;
//clid
if Pos('clid=',MyTstringlist.Strings[I]) > 0 then
begin
clid := StrToInt(copy(MyTstringlist.Strings[I],6,high(MyTstringlist.Strings[I])));
end;
end;
writeln('----------------------');
MyTstringlist.Free;
if ( (enteredGuestChannel = true)
and (member = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end
else if ( (enteredGuestChannel = true)
and (legionnaire = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end;
exit;
end;
// Create Returns in terminal
if Pos(#10#13,Buffer)>0 then
begin
MyTstringlist := Tstringlist.Create;
pSplitIT(#10#13,Buffer,MyTstringlist);
writeln('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
writeln(MyTstringlist.Strings[I]);
end;
MyTstringlist.Free;
end
else
begin
writeln(Buffer);
end;
if Pos('error id=0 msg=ok',Buffer)>0 then
begin
processCommands;
end;
writeln('');
end;
procedure TDataModule1.DataModuleDestroy(Sender: TObject);
begin
processCommand('logout');
processCommand('quit');
IdTelnet1.Disconnect(true);
end;
procedure TDataModule1.IdTelnet1Connected(Sender: TObject);
begin
writeln('IdTelnet1Connected');
// sleep(5000);
//writeln('processCommands:');
// processCommands;
end;
procedure TDataModule1.IdTelnet1DataAvailable(Sender: TIdTelnet;
const Buffer: TIdBytes);
begin
InterpetBuffer(bytestostring(Buffer));
end;
procedure TDataModule1.IdTelnet1Disconnected(Sender: TObject);
begin
writeln('IdTelnet1Disconnected');
end;
end.
Here is my original code from TForm:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdTelnet, IdGlobal, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Edit1: TEdit;
IdTelnet1: TIdTelnet;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Memo2: TMemo;
Timer1: TTimer;
Button4: TButton;
IdSchedulerOfThreadDefault1: TIdSchedulerOfThreadDefault;
procedure Button1Click(Sender: TObject);
procedure IdTelnet1Disconnected(Sender: TObject);
procedure IdTelnet1DataAvailable(Sender: TIdTelnet; const Buffer: TIdBytes);
procedure Button3Click(Sender: TObject);
procedure IdTelnet1Status(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
procedure IdTelnet1Connected(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure InterpetBuffer(buffer: string);
procedure processCommands;
procedure processCommand(Command : string);
public
{ Public declarations }
end;
Const
Elements = (3); //(Elements - 1)
ListOfOnConnectCommands : array [0..Elements] of string =
('login severquery password',
'use 1',
'clientupdate client_nickname=NickNameServer',
'servernotifyregister event=server');
var
Form1: TForm1;
BufferNumber: integer = 0;
CommandSent : boolean = false;
CommandOK : boolean = false;
CommandNumber : integer = 0;
implementation
{$R *.dfm}
procedure pSplitIT(BreakString, BaseString: string; StringList: TStrings);
var
EndOfCurrentString: byte;
begin
StringList.Clear;
repeat
EndOfCurrentString := Pos(BreakString, BaseString);
if EndOfCurrentString = 0 then
StringList.add(BaseString)
else
StringList.add(Copy(BaseString, 1, EndOfCurrentString - 1));
BaseString := Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString);
until EndOfCurrentString = 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
BufferNumber := 0;
IdTelnet1.Connect;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
MyString: string;
I: integer;
begin
MyString := edit1.Text;
for I := Low(MyString) to High(MyString) do
begin
IdTelnet1.SendCh(MyString[I]);
end;
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
edit1.Text := '';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
processCommand('logout');
processCommand('quit');
IdTelnet1.Disconnect(true);
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
processCommands;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
processCommand('logout');
processCommand('quit');
IdTelnet1.Disconnect(true);
end;
procedure TForm1.IdTelnet1Connected(Sender: TObject);
begin
memo1.Lines.Add('IdTelnet1Connected');
// Wait 1 second after connected to send commands.
Timer1.Enabled := true;
end;
procedure TForm1.InterpetBuffer(Buffer: string);
var
MyTstringlist: Tstringlist;
MyBuffer: string;
I: integer;
//ctid: integer;
clid: integer;
member, legionnaire, enteredGuestChannel: boolean;
begin
inc(BufferNumber);
memo1.Lines.Add('---------------------------------------------------------');
memo1.Lines.Add('IdTelnet1DataAvailable BufferNumber: ' + BufferNumber.ToString);
memo1.Lines.Add('---------------------------------------------------------');
OutputDebugString(PChar('IdTelnet1DataAvailable BufferNumber: ' + BufferNumber.ToString));
if Pos('notifycliententerview',Buffer)>0 then
begin
memo1.Lines.Add('----------------------');
memo1.Lines.Add('EXIT notifycliententerview:');
memo1.Lines.Add(Buffer);
MyTstringlist := Tstringlist.Create;
MyBuffer := Buffer;
pSplitIT(' ',MyBuffer,MyTstringlist);
memo1.Lines.Add('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
memo1.Lines.Add(MyTstringlist.Strings[I]);
if MyTstringlist.Strings[I] = 'ctid=45' then
begin
// client entered GUESTS CHANNEL, see if we can move them.
enteredGuestChannel := true;
end;
if Pos('client_servergroups=',MyTstringlist.Strings[I]) > 0 then
begin
if Pos('18',MyTstringlist.Strings[I]) > 0 then
begin
member := true;
end;
if Pos('19',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
if Pos('28',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
end;
//clid
if Pos('clid=',MyTstringlist.Strings[I]) > 0 then
begin
clid := StrToInt(copy(MyTstringlist.Strings[I],6,high(MyTstringlist.Strings[I])));
end;
end;
memo1.Lines.Add('----------------------');
MyTstringlist.Free;
if ( (enteredGuestChannel = true)
and (member = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end
else if ( (enteredGuestChannel = true)
and (legionnaire = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end;
exit;
end;
// Create Returns in terminal
if Pos(#10#13,Buffer)>0 then
begin
MyTstringlist := Tstringlist.Create;
pSplitIT(#10#13,Buffer,MyTstringlist);
memo1.Lines.Add('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
memo1.Lines.Add(MyTstringlist.Strings[I]);
end;
MyTstringlist.Free;
end
else
begin
memo1.Lines.Add(Buffer);
end;
if Pos('error id=0 msg=ok',Buffer)>0 then
begin
processCommands;
end;
memo1.Lines.Add('');
end;
procedure TForm1.processCommand(Command : string);
begin
memo2.Lines.Add('processCommand: ' + Command);
IdTelnet1.SendString(Command);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
end;
procedure TForm1.processCommands;
var
MyString: string;
begin
if CommandNumber <= Elements then
begin
MyString := ListOfOnConnectCommands[CommandNumber];
memo2.Lines.Add('processCommands: ' + MyString);
IdTelnet1.SendString(MyString);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
inc(CommandNumber);
end;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if IdTelnet1.Connected = false then
begin
memo2.Lines.Add('NOT CONNECTED YET...WAITING TO SEND COMMANDS.');
exit;
end;
processCommands;
Timer1.Enabled := false;
end;
procedure TForm1.IdTelnet1DataAvailable(Sender: TIdTelnet;
const Buffer: TIdBytes);
begin
InterpetBuffer(bytestostring(Buffer));
end;
procedure TForm1.IdTelnet1Disconnected(Sender: TObject);
begin
memo1.Lines.Add('IdTelnet1Disconnected');
end;
procedure TForm1.IdTelnet1Status(ASender: TObject; const AStatus: TIdStatus;
const AStatusText: string);
begin
memo1.Lines.Add('AStatusText: ' + AStatusText);
end;
end.
I expect the program to run in console just like the GUI windows version of itself, but I'm not exactly sure how to go about this with indy idTelnet. I have mainly converted it over to the best of my knowledge and what I could find on the internet (which isn't much). Somehow I need to figure out what is causing it to hang instead of processing telnet messages?
Okay, I did much research and found some code to help me with the console application.
I know Remy Lebeau said that Teamspeak doesn't use telnet protocol, but I used it and it works just fine.
The credit has to go to Tony Caduto on this webpage: http://www.44342.com/delphi-f1279-t5081-p1.htm follow the link and go to the bottom of the page. Search for the last reply by Tony Caduto.
This code works now!
Now I can run this on my VPS running linux via WINE! I just don't have the money to buy the more expensive version of Delphi that would run it on linux. I am going to wait until they release a version for small developers like myself that will allow us to run delphi applications on linux. Until then, WINE is the way.
edit: make sure you put IdTelnet1.TelnetThread.Loop := true; after IdTelnet1.Connect; or you get an error.
Here is the code:
program TS3bot;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Unit1 in 'Unit1.pas' {DataModule1: TDataModule};
begin
try
DataModule1 := TDataModule1.Create(nil);
with DataModule1 do
try
{ TODO -oUser -cConsole Main : Insert code here }
IdTelnet1.ThreadedEvent := true;
IdTelnet1.Connect;
IdTelnet1.TelnetThread.Loop := true;
while (IdTelnet1.Connected = true) do
begin
Sleep(60);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
finally
writeln('Program ended.');
freeandnil(DataModule1);
end;
end.
Here is the other part just in case it changed:
unit Unit1;
interface
uses
System.SysUtils, System.Classes, IdBaseComponent, IdComponent,
IdTCPConnection, IdTCPClient, IdTelnet, IdGlobal;
type
TDataModule1 = class(TDataModule)
IdTelnet1: TIdTelnet;
procedure IdTelnet1Connected(Sender: TObject);
procedure IdTelnet1DataAvailable(Sender: TIdTelnet; const Buffer: TIdBytes);
procedure IdTelnet1Disconnected(Sender: TObject);
procedure DataModuleDestroy(Sender: TObject);
private
{ Private declarations }
procedure processCommand(Command : string);
procedure processCommands;
procedure InterpetBuffer(Buffer: string);
public
{ Public declarations }
end;
Const
Elements = (3); //(Elements - 1)
ListOfOnConnectCommands : array [0..Elements] of string =
('login serverquery mypassword',
'use 1',
'clientupdate client_nickname=ServerNickName',
'servernotifyregister event=server');
var
DataModule1: TDataModule1;
BufferNumber: integer = 0;
CommandSent : boolean = false;
CommandOK : boolean = false;
CommandNumber : integer = 0;
implementation
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
procedure pSplitIT(BreakString, BaseString: string; StringList: TStrings);
var
EndOfCurrentString: byte;
begin
StringList.Clear;
repeat
EndOfCurrentString := Pos(BreakString, BaseString);
if EndOfCurrentString = 0 then
StringList.add(BaseString)
else
StringList.add(Copy(BaseString, 1, EndOfCurrentString - 1));
BaseString := Copy(BaseString, EndOfCurrentString + length(BreakString), length(BaseString) - EndOfCurrentString);
until EndOfCurrentString = 0;
end;
procedure TDataModule1.processCommand(Command : string);
begin
writeln('processCommand: ' + Command);
IdTelnet1.SendString(Command);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
end;
procedure TDataModule1.processCommands;
var
MyString: string;
begin
if CommandNumber <= Elements then
begin
MyString := ListOfOnConnectCommands[CommandNumber];
writeln('processCommands: ' + MyString);
IdTelnet1.SendString(MyString);
IdTelnet1.SendCh(#10);
IdTelnet1.SendCh(#13);
inc(CommandNumber);
//exit;
end;
end;
procedure TDataModule1.InterpetBuffer(Buffer: string);
var
MyTstringlist: Tstringlist;
MyBuffer: string;
I: integer;
clid: integer;
member, legionnaire, enteredGuestChannel: boolean;
begin
enteredGuestChannel := false;
member := false;
legionnaire := false;
inc(BufferNumber);
writeln('---------------------------------------------------------');
writeln('IdTelnet1DataAvailable BufferNumber: ' + BufferNumber.ToString);
writeln('---------------------------------------------------------');
if Pos('notifycliententerview',Buffer)>0 then
begin
writeln('----------------------');
writeln('EXIT notifycliententerview:');
writeln(Buffer);
MyTstringlist := Tstringlist.Create;
MyBuffer := Buffer;
pSplitIT(' ',MyBuffer,MyTstringlist);
writeln('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
writeln(MyTstringlist.Strings[I]);
if MyTstringlist.Strings[I] = 'ctid=45' then
begin
// client entered GUESTS CHANNEL, see if we can move them.
enteredGuestChannel := true;
end;
if Pos('client_servergroups=',MyTstringlist.Strings[I]) > 0 then
begin
if Pos('18',MyTstringlist.Strings[I]) > 0 then
begin
member := true;
end;
if Pos('19',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
if Pos('28',MyTstringlist.Strings[I]) > 0 then
begin
legionnaire := true;
end;
end;
//clid
if Pos('clid=',MyTstringlist.Strings[I]) > 0 then
begin
clid := StrToInt(copy(MyTstringlist.Strings[I],6,high(MyTstringlist.Strings[I])));
end;
end;
writeln('----------------------');
MyTstringlist.Free;
if ( (enteredGuestChannel = true)
and (member = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end
else if ( (enteredGuestChannel = true)
and (legionnaire = true) ) then
begin
processCommand('clientmove clid=' + clid.ToString + ' cid=47');
end;
exit;
end;
// Create Returns in terminal
if Pos(#10#13,Buffer)>0 then
begin
MyTstringlist := Tstringlist.Create;
pSplitIT(#10#13,Buffer,MyTstringlist);
writeln('COUNT: ' + MyTstringlist.Count.ToString);
for I := 0 to MyTstringlist.Count - 1 do
begin
writeln(MyTstringlist.Strings[I]);
end;
MyTstringlist.Free;
end
else
begin
writeln(Buffer);
end;
if Pos('error id=0 msg=ok',Buffer)>0 then
begin
processCommands;
end;
writeln('');
end;
procedure TDataModule1.DataModuleDestroy(Sender: TObject);
begin
processCommand('logout');
processCommand('quit');
IdTelnet1.Disconnect(true);
end;
procedure TDataModule1.IdTelnet1Connected(Sender: TObject);
begin
writeln('IdTelnet1Connected');
sleep(5000);
writeln('processCommands:');
processCommands;
end;
procedure TDataModule1.IdTelnet1DataAvailable(Sender: TIdTelnet;
const Buffer: TIdBytes);
begin
InterpetBuffer(bytestostring(Buffer));
end;
procedure TDataModule1.IdTelnet1Disconnected(Sender: TObject);
begin
writeln('IdTelnet1Disconnected');
end;
end.