diff --git a/src/bot/irclogbot.bot.pas b/src/bot/irclogbot.bot.pas index 568e85b..99fb9e0 100644 --- a/src/bot/irclogbot.bot.pas +++ b/src/bot/irclogbot.bot.pas @@ -11,6 +11,7 @@ interface , IdIRC , IRCLogBot.Config , IRCLogBot.Database +, IRCLogBot.Replay ; type @@ -24,6 +25,8 @@ TIRCLogBot = class(TObject) FDB: TDatabase; + FReplay: TReplayThread; + procedure OnConnected(Sender: TObject); procedure OnDisconnected(Sender: TObject); procedure OnNotice(ASender: TIdContext; const ANickname, AHost, @@ -51,7 +54,6 @@ implementation uses IRCLogBot.Common -, IRCLogBot.Replay ; @@ -59,18 +61,18 @@ implementation procedure TIRCLogBot.OnConnected(Sender: TObject); begin - debug('Connected to server'); + debug('Connected to server.'); end; procedure TIRCLogBot.OnDisconnected(Sender: TObject); begin - debug('Disconnected from server'); + debug('Disconnected from server.'); end; procedure TIRCLogBot.OnNotice(ASender: TIdContext; const ANickname, AHost, ATarget, ANotice: String); begin - debug('>> NOTICE: <%s:%s> (%s) "%s"', [ + debug('>> NOTICE: <%s:%s> (%s) "%s".', [ ANickname, AHost, ATarget, @@ -81,7 +83,7 @@ procedure TIRCLogBot.OnNotice(ASender: TIdContext; const ANickname, AHost, procedure TIRCLogBot.OnServerQuit(ASender: TIdContext; const ANickname, AHost, AServer, AReason: String); begin - debug('>> QUIT: <%s:%s> %s "%s"',[ + debug('>> QUIT: <%s:%s> %s "%s".',[ ANickname, AHost, AServer, @@ -92,14 +94,14 @@ procedure TIRCLogBot.OnServerQuit(ASender: TIdContext; const ANickname, AHost, procedure TIRCLogBot.OnJoin(ASender: TIdContext; const ANickname, AHost, AChannel: String); begin - debug('>> JOIN: <%s:%s> %s', [ + debug('>> JOIN: <%s:%s> %s.', [ ANickname, AHost, AChannel ]); if (ANickname = FConfig.NickName) and (AChannel = FConfig.Channel) then begin - debug('Successfully joined my channel'); + debug('Successfully joined my channel.'); FJoinedChannel:= True; FIRC.Say(AChannel, 'I have arrived!!! TADAAAAA!!! Send me a private message with ".help" to see what I can do for you.'); end; @@ -111,7 +113,7 @@ procedure TIRCLogBot.OnPrivateMessage(ASender: TIdContext; const ANickname, strings: TStringArray; count: Integer; begin - debug('>> PRIVMSG: <%s:%s>(%s) "%s"', [ + debug('>> PRIVMSG: <%s:%s>(%s) "%s".', [ ANickname, AHost, ATarget, @@ -119,7 +121,7 @@ procedure TIRCLogBot.OnPrivateMessage(ASender: TIdContext; const ANickname, ]); if ATarget = FConfig.Channel then begin - debug('Inserting: %s, %s, %s, %s', [ + debug('Inserting: %s, %s, %s, %s.', [ ANickname, AHost, ATarget, @@ -175,46 +177,51 @@ procedure TIRCLogBot.Help(const ATarget: String); begin debug('Help command.'); FIRC.Say(ATarget, 'Commands:'); - FIRC.Say(ATarget, '.help - This help information.'); + FIRC.Say(ATarget, '.help - This help information.'); FIRC.Say(ATarget, '.replay [count] - Raplays last lines. Default is last 10 lines.'); end; procedure TIRCLogBot.Replay(const ATarget: String; ACount: Integer); var - replayThread: TReplayThread; lines: TStringList; begin debug('Replay command(%d).', [ACount]); lines:= FDB.Get(ACount); - debug('Lines: %d', [lines.Count]); - replayThread:= TReplayThread.Create(FIRC, ATarget, lines); + debug('Lines: %d.', [lines.Count]); + FReplay.Add(ATarget, lines); lines.Free; end; procedure TIRCLogBot.Run; begin - debug('Connecting...'); try + debug('Connecting...'); FIRC.Connect; except on e:Exception do begin - debug('Error connecting: %s', [e.Message]); + debug('Error connecting: "%s".', [e.Message]); end; end; - debug('Joining channel: "%s"...', [FConfig.Channel]); try + debug('Joining channel: "%s"...', [FConfig.Channel]); FIRC.Join(FConfig.Channel); except on e:Exception do begin - debug('Error joining: %s', [e.Message]); + debug('Error joining: "%s".', [e.Message]); end; end; + debug('Starting Replay Thread.'); + FReplay:= TReplayThread.Create(FIRC); end; procedure TIRCLogBot.Shutdown; begin + debug('Terminating Replay Thread.'); + FReplay.Terminate; + debug('Waiting for Replay Thread to terminate...'); + FReplay.WaitFor; if FIRC.Connected then begin debug('Disconnecting...'); @@ -224,7 +231,7 @@ procedure TIRCLogBot.Shutdown; except on e:Exception do begin - debug('Error: %s', [e.Message]); + debug('Error disconnecting: "%s".', [e.Message]); end; end; end; @@ -257,7 +264,7 @@ constructor TIRCLogBot.Create(const AConfig: TBotConfig); except on e:Exception do begin - debug('Error creating db: ', [e.Message]); + debug('Error creating db: "%s".', [e.Message]); end; end; end; diff --git a/src/common/irclogbot.common.pas b/src/common/irclogbot.common.pas index e822b3e..b9558b6 100644 --- a/src/common/irclogbot.common.pas +++ b/src/common/irclogbot.common.pas @@ -23,7 +23,7 @@ procedure debug(const AMessage: String); begin if DebugOn then begin - dateTimeStr:= FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz:', Now); + dateTimeStr:= FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz: ', Now); WriteLn(Format('%s %s', [dateTimeStr, AMessage])); end; end; @@ -32,7 +32,7 @@ procedure debug(const AFormat: String; AValues: array of const); begin if DebugOn then begin - dateTimeStr:= FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz: ', Now); + dateTimeStr:= FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz: ', Now); WriteLn(Format(dateTimeStr+AFormat, AValues)); end; end; diff --git a/src/database/irclogbot.database.pas b/src/database/irclogbot.database.pas index 5185285..12a6baf 100644 --- a/src/database/irclogbot.database.pas +++ b/src/database/irclogbot.database.pas @@ -55,7 +55,7 @@ procedure TDatabase.SetupTables; on e:Exception do begin FTransaction.Rollback; - debug('Error setting tables: %s', [e.Message]); + debug('Error setting tables: "%s".', [e.Message]); end; end; end; @@ -68,7 +68,7 @@ procedure TDatabase.Insert(ANickName, AChannel, AMessage: String); debug('Starting Transaction...'); FTransaction.StartTransaction; try - debug('Inserting: <%s> [%s] "%s"', [ANickName, AChannel, AMessage]); + debug('Inserting: <%s> [%s] "%s".', [ANickName, AChannel, AMessage]); FConnection.ExecuteDirect(Format( 'INSERT INTO logs (nick, channel, message) VALUES (%s, %s, %s)', [QuotedStr(ANickName), QuotedStr(AChannel), QuotedStr(AMessage)])); @@ -80,7 +80,7 @@ procedure TDatabase.Insert(ANickName, AChannel, AMessage: String); on e:Exception do begin FTransaction.Rollback; - debug('Error inserting message: %s', [e.Message]); + debug('Error inserting message: "%s".', [e.Message]); end; end; finally @@ -112,7 +112,7 @@ function TDatabase.Get(ACount: Integer): TStringList; channel:= FQuery.FieldByName('channel').AsString; nick:= FQuery.FieldByName('nick').AsString; message:= FQuery.FieldByName('message').AsString; - debug('Retrieving: %s [%s] %s: %s', [ + debug('Retrieving: %s [%s] %s: %s.', [ date, channel, nick, @@ -136,7 +136,7 @@ function TDatabase.Get(ACount: Integer): TStringList; on e:Exception do begin FTransaction.Rollback; - debug('Error retrieving lines: %s', [e.Message]); + debug('Error retrieving lines: "%s".', [e.Message]); end; end; finally diff --git a/src/paslogbot.lpr b/src/paslogbot.lpr index f8249fd..447554e 100644 --- a/src/paslogbot.lpr +++ b/src/paslogbot.lpr @@ -132,7 +132,7 @@ procedure TPasLogBot.DoRun; //debug('Short Date: ' + DefaultFormatSettings.ShortDateFormat); //debug('Short Time: ' + DefaultFormatSettings.ShortTimeFormat); //debug('Date Sep: ' + DefaultFormatSettings.DateSeparator); - debug('Setting Date and Time formats'); + debug('Setting Date and Time formats.'); DefaultFormatSettings.ShortDateFormat:= 'yyyy/mm/dd'; DefaultFormatSettings.DateSeparator:= '/'; debug(Format('Attempting to read config from: "%s"...', [FConfigFile])); diff --git a/src/threads/irclogbot.replay.pas b/src/threads/irclogbot.replay.pas index 000ca03..7689e3b 100644 --- a/src/threads/irclogbot.replay.pas +++ b/src/threads/irclogbot.replay.pas @@ -8,6 +8,7 @@ interface Classes , SysUtils , SyncObjs +, Contnrs , IdIRC ; @@ -18,14 +19,14 @@ TReplayThread = class(TThread) private FCriticalSection: TCriticalSection; FIRC: TIdIRC; - FTarget: String; - FLines: TStringList; + FQueue: TFPObjectList; protected procedure Execute; override; public - constructor Create(AIRC: TIdIRC; const ATarget: String; - const ALines: TStringList); + constructor Create(AIRC: TIdIRC); destructor Destroy; override; + + procedure Add(const ANick: String; const AList: TStringList); published end; @@ -34,59 +35,120 @@ implementation uses IRCLogBot.Common ; +type +{ TReplayBundle } + TReplayBundle = class(TObject) + private + FNick: String; + FLines: TStringList; + protected + public + constructor Create(const ANick: String; const ALines: TStringList); + destructor Destroy; override; + + property Nick: String + read FNick; + property Lines: TStringList + read FLines; + published + end; + +{ TReplayBundle } + +constructor TReplayBundle.Create(const ANick: String; + const ALines: TStringList); +begin + FNick:= ANick; + FLines:= TStringList.Create; + FLines.Text:= ALines.Text; +end; + +destructor TReplayBundle.Destroy; +begin + FLines.Free; + inherited Destroy; +end; { TReplayThread } procedure TReplayThread.Execute; var + bundle: TReplayBundle; line: String; index: Integer = 0; begin - if not FIRC.Connected then + while not Terminated do begin - debug('Exiting replay thread due not being connected.'); - exit; - end; - try - FIRC.Say(FTarget, '!! --> To avoid triggering flooding, for each 5 lines, I will pause for 5 seconds <-- !!'); - FIRC.Say(FTarget, Format('*** Here are the last %d lines ***', [FLines.Count])); - for line in FLines do - begin - if (Terminated) or (not FIRC.Connected) then + FCriticalSection.Acquire; + try + bundle:= nil; + if FQueue.Count > 0 then begin - debug('Exiting replay thread due to termination or not being connected.'); - exit; + bundle:= TReplayBundle.Create( + (FQueue[0] as TReplayBundle).Nick, + (FQueue[0] as TReplayBundle).Lines + ); + FQueue.Delete(0); end; - debug('Sending #%d: "%s"', [index, line]); - Inc(index); - FIRC.Say(FTarget, line); - if (index mod 5) = 0 then + finally + FCriticalSection.Release; + end; + if Assigned(bundle) then + begin + FIRC.Say(bundle.Nick, '!! --> To avoid triggering flooding, for each 5 lines, I will pause for 5 seconds <-- !!'); + FIRC.Say(bundle.Nick, Format('*** Here are the last %d lines ***', [bundle.Lines.Count])); + index:= 1; + for line in bundle.Lines do begin - debug('Pausing'); - Sleep(5000); + debug('Sending #%d: "%s".', [index, line]); + Inc(index); + FIRC.Say(bundle.Nick, line); + if (index mod 5) = 0 then + begin + debug('Pausing...'); + Sleep(5000); + end; end; + FIRC.Say(bundle.Nick, Format('*** End of the last %d lines ***', [bundle.Lines.Count])); + end + else + begin + //debug('Nothing to do, sleeping...'); + Sleep(500); end; - FIRC.Say(FTarget, Format('*** End of the last %d lines ***', [FLines.Count])); + end; +end; + +procedure TReplayThread.Add(const ANick: String; const AList: TStringList); +var + bundle: TReplayBundle; +begin + FCriticalSection.Acquire; + try + bundle:= TReplayBundle.Create(ANick, AList); + debug(Format('Adding %d lines for "%s".', [ + AList.Count, + ANick + ])); + FQueue.Add(bundle); finally - FLines.Free; + FCriticalSection.Release; end; end; -constructor TReplayThread.Create(AIRC: TIdIRC; const ATarget: String; - const ALines: TStringList); +constructor TReplayThread.Create(AIRC: TIdIRC); begin inherited Create(True); FCriticalSection:= TCriticalSection.Create; + FQueue:= TFPObjectList.Create(True); FIRC:= AIRC; - FTarget:= ATarget; - FLines:= TStringList.Create; - FLines.Text := ALines.Text; FreeOnTerminate:= True; Start; end; destructor TReplayThread.Destroy; begin + FQueue.Free; FCriticalSection.Free; inherited Destroy; end;