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