diff --git a/src/bot/irclogbot.bot.pas b/src/bot/irclogbot.bot.pas index 2705445..7a46f2f 100644 --- a/src/bot/irclogbot.bot.pas +++ b/src/bot/irclogbot.bot.pas @@ -41,6 +41,7 @@ TIRCLogBot = class(TObject) procedure Help(const ATarget: String); procedure Version(const ATarget: String); procedure Replay(const ATarget: String; ACount: Integer); + procedure Search(const ATarget, AQuery: String); protected public constructor Create(const AConfig: TBotConfig); @@ -73,7 +74,7 @@ procedure TIRCLogBot.OnDisconnected(Sender: TObject); procedure TIRCLogBot.OnNotice(ASender: TIdContext; const ANickname, AHost, ATarget, ANotice: String); begin - debug('>> NOTICE: <%s:%s> (%s) "%s".', [ + info('>> NOTICE: <%s:%s> (%s) "%s".', [ ANickname, AHost, ATarget, @@ -84,7 +85,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".',[ + info('>> QUIT: <%s:%s> %s "%s".',[ ANickname, AHost, AServer, @@ -95,7 +96,7 @@ procedure TIRCLogBot.OnServerQuit(ASender: TIdContext; const ANickname, AHost, procedure TIRCLogBot.OnJoin(ASender: TIdContext; const ANickname, AHost, AChannel: String); begin - debug('>> JOIN: <%s:%s> %s.', [ + info('>> JOIN: <%s:%s> %s.', [ ANickname, AHost, AChannel @@ -114,7 +115,7 @@ procedure TIRCLogBot.OnPrivateMessage(ASender: TIdContext; const ANickname, strings: TStringArray; count: Integer; begin - debug('>> PRIVMSG: <%s:%s>(%s) "%s".', [ + info('>> PRIVMSG: <%s:%s>(%s) "%s".', [ ANickname, AHost, ATarget, @@ -169,6 +170,19 @@ procedure TIRCLogBot.OnPrivateMessage(ASender: TIdContext; const ANickname, end; exit; end; + if Pos('.search', Trim(AMessage)) = 1 then + begin + strings:= AMessage.Split([' ']); + if Length(strings[1]) > 2 then + begin + Search(ANickname, strings[1]); + end + else + begin + FIRC.Say(ANickname, 'I will only search if query is 3 characters or more.'); + end; + exit; + end; end else begin @@ -185,13 +199,18 @@ procedure TIRCLogBot.Help(const ATarget: String); FIRC.Say(ATarget, 'Commands:'); FIRC.Say(ATarget, '.help - This help information.'); FIRC.Say(ATarget, '.version - Version and info about the bot.'); - FIRC.Say(ATarget, '.replay [count] - Raplays last lines. Default is last 10 lines.'); + FIRC.Say(ATarget, '.replay [count] - Replays last lines. Default is last 10 lines.'); + Sleep(5000); + FIRC.Say(ATarget, '.search [query] - Searches for in the logs.'); + FIRC.Say(ATarget, ' Will only search if is 3 characters or more.'); + FIRC.Say(ATarget, ' Will only use the first word after the command. No multi word search(yet?).'); + FIRC.Say(ATarget, ' Returns the last 10 lines with the searched .'); end; procedure TIRCLogBot.Version(const ATarget: String); begin debug('Version command.'); - FIRC.Say(ATarget, Format('Version: %s, %s',[ + FIRC.Say(ATarget, Format('Version: %s; Source: %s',[ cVersion, cRepoURL ])); @@ -208,10 +227,27 @@ procedure TIRCLogBot.Replay(const ATarget: String; ACount: Integer); lines.Free; end; +procedure TIRCLogBot.Search(const ATarget, AQuery: String); +var + lines: TStringList; +begin + debug('Search command: "%s"', [AQuery]); + lines:= FDB.Search(AQuery); + if lines.Count > 0 then + begin + FReplay.Add(ATarget, lines); + end + else + begin + FIRC.Say(ATarget, 'Your query returned no lines.'); + end; + lines.Free; +end; + procedure TIRCLogBot.Run; begin try - debug('Connecting...'); + info('Connecting...'); FIRC.Connect; except on e:Exception do @@ -220,7 +256,7 @@ procedure TIRCLogBot.Run; end; end; try - debug('Joining channel: "%s"...', [FConfig.Channel]); + info('Joining channel: "%s"...', [FConfig.Channel]); FIRC.Join(FConfig.Channel); except on e:Exception do @@ -234,13 +270,13 @@ procedure TIRCLogBot.Run; procedure TIRCLogBot.Shutdown; begin - debug('Terminating Replay Thread.'); + info('Terminating Replay Thread.'); FReplay.Terminate; - debug('Waiting for Replay Thread to terminate...'); + info('Waiting for Replay Thread to terminate...'); FReplay.WaitFor; if FIRC.Connected then begin - debug('Disconnecting...'); + info('Disconnecting...'); try if FJoinedChannel then FIRC.Say(FConfig.Channel, 'Boss sais I need to have a wee nap. See Y''All later...'); FIRC.Disconnect('ZzZzZzZzZzZzZzZz...'); diff --git a/src/common/irclogbot.common.pas b/src/common/irclogbot.common.pas index 9726279..82c25cd 100644 --- a/src/common/irclogbot.common.pas +++ b/src/common/irclogbot.common.pas @@ -15,20 +15,48 @@ interface var DebugOn: Boolean; +procedure info(const AMessage: String); overload; +procedure info(const AFormat: String; AValues: array of const);overload; procedure debug(const AMessage: String); overload; procedure debug(const AFormat: String; AValues: array of const);overload; implementation +type + TLogLevel = (llInfo, llDebug); + var dateTimeStr: String; +procedure log(const ALevel: TLogLevel; const AMessage: String); +begin + dateTimeStr:= FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz: ', Now); + case ALevel of + llInfo:begin + WriteLn(dateTimeStr, '[INF]: ', AMessage); + end; + llDebug:begin + WriteLn(dateTimeStr, '[DBG]: ', AMessage); + end; + end; + +end; + +procedure info(const AMessage: String); +begin + Log(llInfo, AMessage); +end; + +procedure info(const AFormat: String; AValues: array of const); +begin + Log(llInfo, Format(AFormat, AValues)); +end; + procedure debug(const AMessage: String); begin if DebugOn then begin - dateTimeStr:= FormatDateTime('yyyy/mm/dd hh:nn:ss.zzz: ', Now); - WriteLn(Format('%s %s', [dateTimeStr, AMessage])); + Log(llDebug, AMessage); end; end; @@ -36,8 +64,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); - WriteLn(Format(dateTimeStr+AFormat, AValues)); + Log(llDebug, Format(AFormat, AValues)); end; end; diff --git a/src/database/irclogbot.database.pas b/src/database/irclogbot.database.pas index 12a6baf..85ebfe3 100644 --- a/src/database/irclogbot.database.pas +++ b/src/database/irclogbot.database.pas @@ -25,11 +25,12 @@ TDatabase = class(TObject) procedure SetupTables; protected public - constructor Create(ADatabaseFile: String); + constructor Create(const ADatabaseFile: String); destructor Destroy; override; - procedure Insert(ANickName, AChannel, AMessage: String); - function Get(ACount: Integer): TStringList; + procedure Insert(const ANickName, AChannel, AMessage: String); + function Get(const ACount: Integer): TStringList; + function Search(const AQuery: String): TStringList; published end; @@ -60,7 +61,7 @@ procedure TDatabase.SetupTables; end; end; -procedure TDatabase.Insert(ANickName, AChannel, AMessage: String); +procedure TDatabase.Insert(const ANickName, AChannel, AMessage: String); begin FCriticalSection.Acquire; try @@ -88,14 +89,14 @@ procedure TDatabase.Insert(ANickName, AChannel, AMessage: String); end; end; -function TDatabase.Get(ACount: Integer): TStringList; +function TDatabase.Get(const ACount: Integer): TStringList; var date, channel, nick, message: String; begin Result:= TStringList.Create; FCriticalSection.Acquire; try - debug('Starting transaction.'); + debug('Starting GET transaction.'); FTransaction.StartTransaction; try try @@ -130,7 +131,7 @@ function TDatabase.Get(ACount: Integer): TStringList; end; finally FTransaction.EndTransaction; - debug('Transaction ended.'); + debug('Transaction GET ended.'); end; except on e:Exception do @@ -144,7 +145,67 @@ function TDatabase.Get(ACount: Integer): TStringList; end; end; -constructor TDatabase.Create(ADatabaseFile: String); +function TDatabase.Search(const AQuery: String): TStringList; +var + date, channel, nick, message: String; +begin + Result:= TStringList.Create; + FCriticalSection.Acquire; + try + debug('Starting SEARCH transaction.'); + FTransaction.StartTransaction; + try + try + FQuery.SQL.Text:= Format( + 'SELECT timestamp, nick, channel, message ' + + 'FROM logs ' + + 'WHERE message LIKE %s ' + + 'ORDER BY id DESC ' + + 'LIMIT %d', + [QuotedStr('%' + AQuery + '%'), 10] + ); + FQuery.Open; + if FQuery.RecordCount > 0 then + begin + FQuery.First; + repeat + date:= FQuery.FieldByName('timestamp').AsString; + channel:= FQuery.FieldByName('channel').AsString; + nick:= FQuery.FieldByName('nick').AsString; + message:= FQuery.FieldByName('message').AsString; + debug('Retrieving: %s [%s] %s: %s.', [ + date, + channel, + nick, + message + ]); + Result.Insert(0, Format('%s [%s] %s: %s',[ + date, + channel, + nick, + message + ])); + FQuery.Next; + until FQuery.EOF; + FQuery.Close; + end; + finally + FTransaction.EndTransaction; + debug('Transaction SEARCH ended.'); + end; + except + on e:Exception do + begin + FTransaction.Rollback; + debug('Error retrieving lines: "%s".', [e.Message]); + end; + end; + finally + FCriticalSection.Release; + end; +end; + +constructor TDatabase.Create(const ADatabaseFile: String); begin FCriticalSection:= TCriticalSection.Create; FConnection:= TSQLite3Connection.Create(nil); diff --git a/src/paslogbot.lpr b/src/paslogbot.lpr index 447554e..222fe09 100644 --- a/src/paslogbot.lpr +++ b/src/paslogbot.lpr @@ -152,7 +152,7 @@ procedure TPasLogBot.DoRun; debug('Creating IRC client...'); FIRCLogBot:= TIRCLogBot.Create(config); debug('Successfully created IRC client.'); - debug('Starting...'); + info('Starting...'); FIRCLogBot.Run; while not Terminated do begin @@ -161,7 +161,7 @@ procedure TPasLogBot.DoRun; FIRCLogBot.Shutdown; FIRCLogBot.Free; config.Free; - debug('Exiting.'); + info('Exiting.'); // stop program loop //Terminate; end; diff --git a/src/threads/irclogbot.replay.pas b/src/threads/irclogbot.replay.pas index 7689e3b..bed0746 100644 --- a/src/threads/irclogbot.replay.pas +++ b/src/threads/irclogbot.replay.pas @@ -110,6 +110,7 @@ procedure TReplayThread.Execute; end; end; FIRC.Say(bundle.Nick, Format('*** End of the last %d lines ***', [bundle.Lines.Count])); + bundle.Free; end else begin