[project @ 2002-08-08 15:21:08 by unknown_lamer]
[clinton/bobotpp.git] / source / Bot.C
CommitLineData
cb21075d 1// Bot.C -*- C++ -*-
2// Copyright (c) 1997, 1998 Etienne BERNARD
3// Copyright (C) 2002 Clinton Ebadi
4
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// any later version.
9
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18
19#include <fstream>
91dddabd 20#include <algorithm>
cb21075d 21#include <iomanip>
22#include <cstring>
23#include <cstdlib>
24#include <cstdio>
25#include <sys/time.h>
26#include <sys/types.h>
27#include <unistd.h>
28
29#include "Bot.H"
30#include "DCCConnection.H"
31#include "StringTokenizer.H"
32#include "ServerConnection.H"
33#include "Utils.H"
e07b6b46 34#include "UserCommands.H"
6530edbf 35#include "DCCManager.H"
cb21075d 36
37#define DEFAULT_NICKNAME "Bobot"
38#define DEFAULT_USERNAME "bobot"
39#define DEFAULT_IRCNAME "I'm a bobot++!"
40#define DEFAULT_COMMANDCHAR '!'
41#define DEFAULT_USERLISTFILENAME "bot.users"
42#define DEFAULT_SHITLISTFILENAME "bot.shit"
43#define DEFAULT_HELPFILENAME "bot.help"
44#define DEFAULT_SCRIPTLOGFILENAME "script.log"
45#define DEFAULT_LOGFILENAME "bot.log"
439869bf 46#define DEFAULT_LOGDIR getenv ("HOME") + String("/.bobotpp/logs/")
cb21075d 47#define DEFAULT_INITFILENAME "bot.init"
48#ifdef USESCRIPTS
49#define DEFAULT_AUTOEXECFILENAME "bot.autoexec"
50#endif
51
52Bot::Bot(String filename, bool debug_on)
53 : nickName(DEFAULT_NICKNAME),
54 wantedNickName(DEFAULT_NICKNAME),
55 userName(DEFAULT_USERNAME),
56 ircName(DEFAULT_IRCNAME),
57 versionString(VERSION_STRING),
58 userHost(""),
59 localIP(""),
60 commandChar(DEFAULT_COMMANDCHAR),
61 configFileName(filename),
62 userListFileName(DEFAULT_USERLISTFILENAME),
63 shitListFileName(DEFAULT_SHITLISTFILENAME),
64 logFileName(DEFAULT_LOGFILENAME),
439869bf 65 logs_dir (DEFAULT_LOGDIR),
cb21075d 66 helpFileName(DEFAULT_HELPFILENAME),
67 initFileName(DEFAULT_INITFILENAME),
68#ifdef USESCRIPTS
69 scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME),
70 autoexecFileName(DEFAULT_AUTOEXECFILENAME),
71#endif
72 connected(false),
73 debug(debug_on), stop(false), sentPing(false),
74 startTime(time(NULL)), currentTime(startTime),
75 lastNickNameChange(startTime), lastChannelJoin(startTime),
76 serverConnection(0), sentUserhostID(0), receivedUserhostID(0)
77{
cb21075d 78#ifdef HAVE_STL_CLEAR
79 wantedChannels.clear();
80 ignoredUserhosts.clear();
81 spyList.clear();
82 userhostMap.clear();
83#endif
84
e07b6b46 85 init_user_functions ();
cb21075d 86
be3612f3 87 set_log_dir (logs_dir);
88 set_log_file (logFileName);
89
90
cb21075d 91 channelList = new ChannelList();
92 serverList = new ServerList();
93 readConfig();
94 userList = new UserList(userListFileName);
95 shitList = new ShitList(shitListFileName);
96 todoList = new TodoList();
6530edbf 97 dccConnections = new DCCManager ();
cb21075d 98
99 // Let's read the alias file
100 std::ifstream initFile(initFileName);
101
102 if (initFile) {
103 String temp, alias, command;
cb21075d 104 int line = 0;
105 while (initFile >> temp, temp.length() != 0) {
106 line++;
107 StringTokenizer st(temp);
108 temp = temp.trim();
109 if (temp[0]=='#') continue;
110 if (st.countTokens(' ') != 2) {
111 std::cerr << "Error when reading alias file (" << initFileName
112 << ") line " << line << "...\n";
113 continue;
114 }
115 alias = st.nextToken().toUpper();
116 command = st.nextToken().toUpper();
117
118 // Does the function already exist ?
e07b6b46 119 if (!userFunctions[alias])
120 {
121 if (userFunction *u = userFunctions[command])
122 userFunctions[alias] =
123 new
124 userFunction(u->function,
125 u->minLevel,
126 u->needsChannelName);
127 }
cb21075d 128 }
129 }
130
e07b6b46 131
cb21075d 132 std::srand (std::time (0)); // srand for bot-random
133#ifdef USESCRIPTS
439869bf 134 botInterp = new BotInterp(this, logs_dir + scriptLogFileName);
cb21075d 135 botInterp->LoadScript(autoexecFileName);
136#endif
137}
138
139Bot::~Bot()
140{
cb21075d 141 Person *p;
142 while (spyList.size() != 0) {
143 p = (*spyList.begin()).second;
144 spyList.erase(spyList.begin());
145 delete p;
146 }
6530edbf 147 delete dccConnections;
e07b6b46 148 destroy_user_functions ();
149
cb21075d 150 wantedChannel *w;
151 while (wantedChannels.size() != 0) {
152 w = (*wantedChannels.begin()).second;
153 wantedChannels.erase(wantedChannels.begin());
154 delete w;
155 }
156 userList->save();
157 shitList->save();
158 delete channelList;
159 delete userList;
160 delete todoList;
161 delete serverList;
162 delete shitList;
163 delete serverConnection;
164 logLine("Stopping log.");
165 logFile.close();
166}
167
168void
169Bot::logLine(String line)
170{
171 tm *d;
172 std::time_t current_time = time(0);
173
174 d = localtime(&current_time);
175 logFile << "[" << std::setfill('0') << std::setw(2)
176 << d->tm_mday << "/" << std::setfill('0') << std::setw(2)
177 << d->tm_mon + 1 << "/"
178 << d->tm_year + 1900 << " - " << std::setfill('0') << std::setw(2)
179 << d->tm_hour << ":" << std::setfill('0') << std::setw(2)
180 << d->tm_min << ":" << std::setfill('0') << std::setw(2)
181 << d->tm_sec << "] "
182 << line
183 << std::endl;
184}
185
186void
187Bot::readConfig()
188{
189 std::ifstream file(configFileName);
190 String temp;
191 int line = 1;
192
193 if (!file) {
194 logLine(String("I cannot find the file ") + configFileName);
195 return;
196 }
197
198 while (!file.eof()) {
199
200 file >> temp;
201
202 if (temp.length() == 0 || temp[0] == '#') {
203 line++;
204 continue;
205 }
206
207 StringTokenizer st(temp);
208 String command = st.nextToken('=').trim().toUpper();
209 String parameters = st.nextToken('=').trim();
210
211 if (command == "NICK" || command == "NICKNAME")
212 nickName = wantedNickName = parameters;
213 else if (command == "USERNAME")
214 userName = parameters;
215 else if (command == "IRCNAME" || command == "REALNAME")
216 ircName = parameters;
217 else if (command == "CMDCHAR" || command == "COMMAND")
218 commandChar = parameters[0];
219 else if (command == "USERLIST")
220 userListFileName = parameters;
221 else if (command == "SHITLIST")
222 shitListFileName = parameters;
223 else if (command == "CHANNEL") {
224 if (parameters.indexOf(':') == -1) {
225 std::cout << "Warning. The 'channel' syntax has changed."
226 << " Please see the README file for more information."
227 << " I will use compatibility mode, but you're really"
228 << " missing something.\n";
229 StringTokenizer st2(parameters);
230 String name = st2.nextToken().toLower();
231 String key = st2.nextToken();
232 wantedChannels[name] = new wantedChannel("", "", key);
233 } else {
234 StringTokenizer st2(parameters);
235 String name = st2.nextToken(':').toLower();
236 String mode = st2.nextToken(':');
237 String keep = st2.nextToken(':');
238 String key = st2.nextToken(':');
239 wantedChannels[name] = new wantedChannel(mode, keep, key);
240 }
241 }
242 else if (command == "LOGFILE")
be3612f3 243 {
fd7440f1 244 if (parameters != logFileName)
be3612f3 245 {
fd7440f1 246 if (parameters[0] == '/')
247 {
248 set_log_dir ("/");
249 set_log_file (parameters.subString (1));
250 }
251 else
252 set_log_file (parameters);
be3612f3 253 }
be3612f3 254 }
cb21075d 255#ifdef USESCRIPTS
256 else if (command == "SCRIPTLOGFILE")
257 scriptLogFileName = parameters;
258 else if (command == "AUTOEXECFILE")
259 autoexecFileName = parameters;
260#endif
261 else if (command == "INITFILE")
262 initFileName = parameters;
263 else if (command == "LOCALIP")
264 localIP = parameters;
265 else if (command == "SERVER") {
266 if (parameters.indexOf(' ') == -1)
267 serverList->addServer(new Server(parameters));
268 else {
269 StringTokenizer st2(parameters);
270 String name = st2.nextToken();
271 int port = std::atoi(st2.nextToken());
272 serverList->addServer(new Server(name,
273 port,
274 st2.nextToken()));
275 }
276 }
277 else {
278 logLine(String("Syntax error in file ") + configFileName +
279 ", line " + String((long)line));
280 file.close();
281 std::exit(1);
282 }
283
284 line++;
285 }
286
287 file.close();
288}
289
290void
291Bot::run()
292{
293 nextServer();
294
295 while (!stop) {
296 waitForInput(); // This is the main event loop
6530edbf 297 dccConnections->checkStale ();
cb21075d 298 if (!serverConnection->queue->flush())
299 nextServer();
300 }
301}
302
303void
304Bot::waitForInput()
305{
306#ifdef _HPUX_SOURCE
307 int rd;
308#else
309 fd_set rd;
310#endif
311 struct timeval timer;
312
313 int sock = serverConnection->getFileDescriptor();
314 int maxSocketNumber = sock;
315
316#ifdef _HPUX_SOURCE
317 rd = sock;
318#else
319 FD_ZERO(&rd);
320 FD_SET(sock, &rd);
321#endif
322
6530edbf 323 DCC_MAP* dccmap = dccConnections->MAP ();
324 for (DCC_MAP::iterator it = dccmap->begin ();
325 it != dccmap->end(); ++it) {
326 int s = it->second->DCC()->getFileDescriptor();
cb21075d 327#ifdef _HPUX_SOURCE
328 rd |= s;
329#else
330 FD_SET(s, &rd);
331#endif
332 if (s > maxSocketNumber)
333 maxSocketNumber = s;
334 }
335
336 timer.tv_sec = 1;
337 timer.tv_usec = 0;
338
339 switch (select(maxSocketNumber + 1, &rd, NULL, NULL, &timer)) {
340 case 0: /* timeout */
341 break;
342 case -1: /* error */
343 break;
344 default: /* normal */
345#ifdef _HPUX_SOURCE
346 if (rd & sock)
347#else
348 if (FD_ISSET(sock, &rd))
349#endif
350 if (serverConnection->handleInput())
351 nextServer();
352
6530edbf 353// std::list<DCCConnection *>::iterator it = dccConnections.begin();
354// std::list<DCCConnection *>::iterator it2;
355
356// while (it != dccConnections.end()) {
357// it2 = it;
358// ++it;
359// #ifdef _HPUX_SOURCE
360// if (rd & (*it2)->getFileDescriptor()) {
361// #else
362// if (FD_ISSET((*it2)->getFileDescriptor(), &rd)) {
363// #endif
364// if ((*it2)->handleInput()) {
365// delete *it2;
366// dccConnections.erase(it2);
367// }
368// }
369// }
370// }
371 dccConnections->checkInput (rd);
cb21075d 372 }
373
374 if (currentTime < std::time(NULL)) { // Actions that we do each second
375 currentTime = std::time(NULL);
376 for (std::map<String, unsigned int, std::less<String> >::iterator
377 it = ignoredUserhosts.begin();
378 it != ignoredUserhosts.end(); ++it)
379 if ((*it).second > 0)
380 (*it).second--;
381
382 String line;
383 while ((line = todoList->getNext()) != "") {
384 serverConnection->queue->sendChannelMode(line);
385 }
386#ifdef USESCRIPTS
387 botInterp->RunTimers(currentTime);
388#endif
389
390#ifdef USESCRIPTS
391 tm *thisTime = localtime(&currentTime);
392 if (thisTime->tm_sec == 0) {
393 char s[6];
394 sprintf(s, "%2d:%2d", thisTime->tm_hour, thisTime->tm_min);
395 botInterp->RunHooks(Hook::TIMER, String(s),
396 gh_list(Utils::string2SCM(String(s)), SCM_UNDEFINED));
397 }
398#endif
399
400 }
401
402 if (currentTime >= (time_t)(lastNickNameChange + Bot::NICK_CHANGE) &&
403 nickName != wantedNickName) {
404 lastNickNameChange = currentTime;
405 serverConnection->queue->sendNick(wantedNickName);
406 }
407
408 if (currentTime >= (std::time_t)(lastChannelJoin + Bot::CHANNEL_JOIN)) {
409 lastChannelJoin = currentTime;
410 for (std::map<String, wantedChannel *, std::less<String> >::iterator it =
411 wantedChannels.begin(); it != wantedChannels.end();
412 ++it)
413 if (channelList->getChannel((*it).first) == 0)
414 serverConnection->queue->sendJoin((*it).first, (*it).second->key);
415 }
416
6530edbf 417// std::list<DCCConnection *>::iterator it2;
cb21075d 418
6530edbf 419// for (std::list<DCCConnection *>::iterator it = dccConnections.begin();
420// it != dccConnections.end(); ) {
421// it2 = it;
422// ++it;
423// if ((*it2)->autoRemove && currentTime >= (std::time_t)((*it2)->lastSpoken + Bot::DCC_DELAY)) {
424// delete *it2;
425// dccConnections.erase(it2);
426// }
427// }
cb21075d 428
429 if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::PING_TIME) && !sentPing) {
430 serverConnection->queue->sendPing("Testing connection");
431 sentPing = true;
432 }
433
434 if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::TIMEOUT)) {
435 sentPing = false;
436 nextServer();
437 }
438}
439
440// We can change server if we will not lose op on a channel
441bool
442Bot::canChangeServer()
443{
444 String channel;
445 Channel *c;
446
447 for (std::map<String, Channel *, std::less<String> >::iterator it =
448 channelList->begin();
449 it != channelList->end(); ++it) {
450 channel = (*it).first;
451 c = channelList->getChannel(channel);
452 if (c->countOp == 1 &&
453 c->count > 1 && this->iAmOp(channel))
454 return false;
455 }
456 return true;
457}
458
459void
460Bot::nextServer()
461{
462 bool cont = false;
463
464 if (channelList)
465 channelList->clear();
466
467 if (serverConnection)
468 userList->removeFirst();
469
470 delete serverConnection;
471
472 do {
473 Server * s = serverList->nextServer();
474 if (!s) {
475 std::cout << "No server found. Exiting..." << std::endl;
476 std::exit(1);
477 }
478 serverConnection = new ServerConnection(this, s, localIP);
479 if (!serverConnection->connect()) {
480 cont = true;
481 // We sleep 10 seconds, to avoid connection flood
482 sleep(10);
483 delete serverConnection;
484 } else {
485 cont = false;
486 }
487 } while (cont);
488}
489
490void
491Bot::reconnect()
492{
493 if (channelList)
494 channelList->clear();
495
496 userList->removeFirst();
497
498 delete serverConnection;
499
500 serverConnection =
501 new ServerConnection(this, serverList->currentServer(), localIP);
502
503 serverConnection->connect();
504}
505
506void
507Bot::connect(int serverNumber)
508{
509 if (channelList)
510 channelList->clear();
511
512 userList->removeFirst();
513
514 delete serverConnection;
515
516 serverConnection =
517 new ServerConnection(this, serverList->get(serverNumber), localIP);
518
519 serverConnection->connect();
520}
521
522void
523Bot::addDCC(Person * from, unsigned long address, int port)
524{
fed59248 525 DCCConnection * d = new DCCConnection(this, from->getAddress (),
cb21075d 526 address, port);
527
528 if (!d->connect())
fd7440f1 529 {
530 logLine ("DCC Connection failed from " + from->getAddress ());
531 return;
532 }
fed59248 533 logLine ("DCC CHAT accepted from" + from->getAddress ());
6530edbf 534 dccConnections->addConnection (d);
cb21075d 535}
536
537void
538Bot::rehash()
539{
fed59248 540 for (std::map<String, Channel *, std::less<String> >::iterator it =
541 channelList->begin();
cb21075d 542 it != channelList->end(); ++it)
543 serverConnection->queue->sendWho((*it).first);
544}
545
546String
547Bot::getUserhost(String channel, String nick)
548{
549 Channel *c;
550
551 if (channel == "")
552 c = 0;
553 else
554 c = channelList->getChannel(channel);
555
556 nick = nick.toLower();
557
558
559 if (c && c->hasNick(nick))
560 return c->getUser(nick)->userhost;
561
562 unsigned long num = sentUserhostID++;
563
564 serverConnection->queue->sendUserhost(nick);
565 userhostMap[num] = "+";
566
567 while (userhostMap[num] == "+") {
568 waitForInput();
569 serverConnection->queue->flush();
570 }
571
572 // We have got our answer
573 String res = userhostMap[num];
574 userhostMap.erase(num);
575
576 return res;
577}
578
579bool
580Bot::iAmOp(String channel)
581{
582 User * me = channelList->getChannel(channel)->getUser(nickName);
583 return (me->mode & User::OP_MODE);
584}
e07b6b46 585
586void
587Bot::init_user_functions ()
588{
589 // User Functions
590#define uf(f, l, b) new userFunction (f, l, b);
591 userFunctions["ACTION"] = uf (UserCommands::Action, User::USER, true);
592 userFunctions["ADDUSER"] = uf (UserCommands::AddUser, User::FRIEND, false);
593 userFunctions["ADDSERVER"] = uf (UserCommands::AddServer, User::FRIEND,
594 false);
595 userFunctions["ADDSHIT"] = uf (UserCommands::AddShit, User::FRIEND, false);
596 userFunctions["ALIAS"] = uf (UserCommands::Alias, User::MASTER, false);
597 userFunctions["BAN"] = uf (UserCommands::Ban, User::USER, true);
598 userFunctions["BANLIST"] = uf (UserCommands::BanList, User::USER, true);
599 userFunctions["CHANNELS"] =
600 uf (UserCommands::Channels, User::FRIEND, false);
601 userFunctions["CYCLE"] = uf (UserCommands::Cycle, User::FRIEND, true);
602 userFunctions["DCCLIST"] = uf (UserCommands::DCCList, User::FRIEND, false);
603 userFunctions["DEBAN"] = uf (UserCommands::Deban, User::USER, true);
604 userFunctions["DELSERVER"] = uf (UserCommands::DelServer, User::FRIEND,
605 false);
606 userFunctions["DELUSER"] = uf (UserCommands::DelUser, User::FRIEND, false);
607 userFunctions["DELSHIT"] = uf (UserCommands::DelShit, User::FRIEND, false);
608 userFunctions["DEOP"] = uf (UserCommands::Deop, User::TRUSTED_USER, true);
609 userFunctions["DIE"] = uf (UserCommands::Die, User::MASTER, false);
610 userFunctions["DO"] = uf (UserCommands::Do, User::MASTER, false);
611#ifdef USESCRIPTS
612 userFunctions["EXECUTE"] = uf (UserCommands::Execute, User::MASTER, false);
613#endif
614 userFunctions["HELP"] = uf (UserCommands::Help, User::NONE, false);
615 userFunctions["IDENT"] = uf (UserCommands::Ident, User::NONE, true);
616 userFunctions["INVITE"] = uf (UserCommands::Invite, User::USER, true);
617 userFunctions["JOIN"] = uf (UserCommands::Join, User::FRIEND, false);
618 userFunctions["KEEP"] = uf (UserCommands::Keep, User::FRIEND, true);
619 userFunctions["KICK"] = uf (UserCommands::Kick, User::USER, true);
620 userFunctions["KICKBAN"] = uf (UserCommands::KickBan, User::USER, true);
621 userFunctions["LOAD"] = uf (UserCommands::Load, User::FRIEND, false);
622#ifdef USESCRIPTS
623 userFunctions["LOADSCRIPT"] = uf (UserCommands::LoadScript, User::MASTER,
624 false);
625#endif
626 userFunctions["LOCK"] = uf (UserCommands::Lock, User::FRIEND, true);
627 userFunctions["MODE"] = uf (UserCommands::Mode, User::FRIEND, true);
628 userFunctions["MSG"] = uf (UserCommands::Msg, User::USER, false);
629 userFunctions["NAMES"] = uf (UserCommands::Names, User::USER, true);
630 userFunctions["NEXTSERVER"] = uf (UserCommands::NextServer, User::FRIEND,
631 false);
632 userFunctions["NICK"] = uf (UserCommands::Nick, User::FRIEND, false);
633 userFunctions["NSLOOKUP"] = uf (UserCommands::NsLookup, User::USER, false);
634 userFunctions["OP"] = uf (UserCommands::Op, User::TRUSTED_USER, true);
635 userFunctions["PART"] = uf (UserCommands::Part, User::FRIEND, true);
636 userFunctions["PASSWORD"] = uf (UserCommands::Password, User::USER, true);
637 userFunctions["RECONNECT"] =
638 uf (UserCommands::Reconnect, User::FRIEND, false);
639 userFunctions["RSPYMESSAGE"] =
640 uf (UserCommands::RSpyMessage, User::USER, false);
641 userFunctions["SAVE"] = uf (UserCommands::Save, User::FRIEND, false);
642 userFunctions["SAY"] = uf (UserCommands::Say, User::USER, true);
643 userFunctions["SERVER"] = uf (UserCommands::Server, User::FRIEND, false);
644 userFunctions["SERVERLIST"] =
645 uf (UserCommands::ServerList, User::FRIEND, false);
646 userFunctions["SETVERSION"] =
647 uf (UserCommands::SetVersion, User::MASTER, false);
648 userFunctions["SHITLIST"] =
649 uf (UserCommands::ShitList, User::FRIEND, false);
650 userFunctions["SPYLIST"] = uf (UserCommands::SpyList, User::USER, false);
651 userFunctions["SPYMESSAGE"] =
652 uf (UserCommands::SpyMessage, User::USER, false);
653 userFunctions["STATS"] = uf (UserCommands::Stats, User::FRIEND, true);
654 userFunctions["TBAN"] = uf (UserCommands::TBan, User::USER, true);
655 userFunctions["TKBAN"] = uf (UserCommands::TKBan, User::USER, true);
656 userFunctions["TOPIC"] = uf (UserCommands::Topic, User::USER, true);
657 userFunctions["UNLOCK"] = uf (UserCommands::Unlock, User::FRIEND, true);
658 userFunctions["USERLIST"] =
659 uf (UserCommands::UserList, User::FRIEND, false);
660 userFunctions["WHO"] = uf (UserCommands::Who, User::NONE, true);
661 userFunctions["WHOIS"] = uf (UserCommands::Whois, User::FRIEND, true);
662#undef uf
663}
664
665namespace
666{
667 void erase_userf (std::pair<std::string, class userFunction*> it)
668 {
669 delete it.second;
670 }
671}
672
673void
674Bot::destroy_user_functions ()
675{
91dddabd 676 std::for_each (userFunctions.begin (),
e07b6b46 677 userFunctions.end (),
678 erase_userf);
679 userFunctions.erase (userFunctions.begin (),
680 userFunctions.end ());
681}
be3612f3 682
683void
684Bot::set_log_file (String name)
685{
686 logFileName = name;
fd7440f1 687 logFile.close ();
688 logFile.clear ();
be3612f3 689#if HAVE_IOSBASE
690 logFile.open(logs_dir + logFileName, std::ios_base::out |
691 std::ios_base::ate | std::ios_base::app);
692#else
693 logFile.open(logs_dir + logFileName, ios::out | ios::ate
694 | ios::app);
695#endif
696
697 logLine("Starting log.");
698}
699
700void
701Bot::set_log_dir (String dir)
702{
703 logs_dir = dir;
704}