// Bot.C -*- C++ -*-
// Copyright (c) 1997, 1998 Etienne BERNARD
-// Copyright (C) 2002 Clinton Ebadi
+// Copyright (C) 2002,2003,2005 Clinton Ebadi
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
-#include <fstream>
-#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <cstdio>
+
+#include <fstream>
+#include <algorithm>
+#include <iomanip>
+
+#include <dirent.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
+#ifdef USESCRIPTS
+#include <libguile.h>
+#endif
+
#include "Bot.H"
#include "DCCConnection.H"
-#include "StringTokenizer.H"
+#include "DCCChatConnection.H"
+#include "ChannelList.H"
+#include "DCCManager.H"
+#include "DCCPerson.H"
+#include "Parser.H"
+#include "Person.H"
+#include "Server.H"
#include "ServerConnection.H"
+#include "ServerList.H"
+#include "ShitList.H"
+#include "StringTokenizer.H"
+#include "User.H"
+#include "UserCommands.H"
+#include "UserList.H"
#include "Utils.H"
+
+#ifdef USESCRIPTS
+#include "BotInterp.H"
+#include "Interp.H"
+#endif
+
+unsigned int Bot::MAX_MESSAGES = 2;
+unsigned int Bot::MAX_NICKLENGTH = 9;
+
#define DEFAULT_NICKNAME "Bobot"
#define DEFAULT_USERNAME "bobot"
#define DEFAULT_IRCNAME "I'm a bobot++!"
#define DEFAULT_HELPFILENAME "bot.help"
#define DEFAULT_SCRIPTLOGFILENAME "script.log"
#define DEFAULT_LOGFILENAME "bot.log"
+#define DEFAULT_LOGDIR getenv ("HOME") + String("/.bobotpp/logs/")
#define DEFAULT_INITFILENAME "bot.init"
#ifdef USESCRIPTS
#define DEFAULT_AUTOEXECFILENAME "bot.autoexec"
userHost(""),
localIP(""),
commandChar(DEFAULT_COMMANDCHAR),
- configFileName(filename),
userListFileName(DEFAULT_USERLISTFILENAME),
shitListFileName(DEFAULT_SHITLISTFILENAME),
- logFileName(DEFAULT_LOGFILENAME),
helpFileName(DEFAULT_HELPFILENAME),
initFileName(DEFAULT_INITFILENAME),
+ connected(false),
+ debug(debug_on),
+ stop(false),
+ sentPing(false),
+ startTime(time(NULL)),
+ currentTime(startTime),
+ lastNickNameChange(startTime),
+ lastChannelJoin(startTime),
+ serverConnection(0),
+ sentUserhostID(0),
+ receivedUserhostID(0),
+ logFileName(DEFAULT_LOGFILENAME),
+ logs_dir (DEFAULT_LOGDIR),
+ configFileName (filename)
#ifdef USESCRIPTS
- scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME),
- autoexecFileName(DEFAULT_AUTOEXECFILENAME),
+ ,scriptLogFileName(DEFAULT_SCRIPTLOGFILENAME),
+ autoexecFileName(DEFAULT_AUTOEXECFILENAME)
#endif
- connected(false),
- debug(debug_on), stop(false), sentPing(false),
- startTime(time(NULL)), currentTime(startTime),
- lastNickNameChange(startTime), lastChannelJoin(startTime),
- serverConnection(0), sentUserhostID(0), receivedUserhostID(0)
{
- extern userFunctionsStruct userFunctionsInit[];
-
#ifdef HAVE_STL_CLEAR
wantedChannels.clear();
ignoredUserhosts.clear();
userhostMap.clear();
#endif
- for (int i = 0; userFunctionsInit[i].name[0] != '\0'; i++) {
- userFunctions.push_back(new
- userFunction(String(userFunctionsInit[i].name),
- userFunctionsInit[i].function,
- userFunctionsInit[i].minLevel,
- userFunctionsInit[i].needsChannelName));
- }
+ init_user_functions ();
+
+ set_log_dir (logs_dir);
+ set_log_file (logFileName);
+
-#if HAVE_IOSBASE
- logFile.open(logFileName, std::ios_base::out | std::ios_base::ate
- | std::ios_base::app);
-#else
- logFile.open(logFileName, ios::out | ios::ate
- | ios::app);
-#endif
- logLine("Starting log.");
channelList = new ChannelList();
serverList = new ServerList();
readConfig();
userList = new UserList(userListFileName);
shitList = new ShitList(shitListFileName);
- todoList = new TodoList();
+ dccConnections = new DCCManager ();
// Let's read the alias file
- std::ifstream initFile(initFileName);
-
- if (initFile) {
- String temp, alias, command;
- std::list<userFunction *>::iterator it;
- bool found = false;
- userFunction *u;
- int line = 0;
- while (initFile >> temp, temp.length() != 0) {
- line++;
- StringTokenizer st(temp);
- temp = temp.trim();
- if (temp[0]=='#') continue;
- if (st.countTokens(' ') != 2) {
- std::cerr << "Error when reading alias file (" << initFileName
- << ") line " << line << "...\n";
- continue;
- }
- alias = st.nextToken().toUpper();
- command = st.nextToken().toUpper();
-
- // Does the function already exist ?
- found = false;
- for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
- if (alias == (*it)->name) {
- found = true;
- break;
- }
- if (found) continue;
-
- // Check that the command exists
- found = false;
- for (it = userFunctions.begin(); it != userFunctions.end(); ++it)
- if (command == (*it)->name) {
- found = true;
- u = *it;
- break;
- }
- if (!found) continue;
-
- userFunctions.push_back (new
- userFunction((char *)(const char *)alias,
- u->function,
- u->minLevel,
- u->needsChannelName));
+ std::ifstream initFile(initFileName.c_str ());
+
+ if (initFile)
+ {
+ // FIXME: these variables are current String instead of
+ // std::string because String>> reads an entire line. This code
+ // needs to be rewritten to use std::string and std::getline (or
+ // better yet, be removed entirely once BotConfig is in place)
+ String temp, alias, command;
+ int line = 0;
+
+ while (initFile >> temp, temp.length() != 0)
+ {
+ StringTokenizer st(temp);
+
+ line++;
+ temp = Utils::trim_str (temp);
+
+ if (temp[0]=='#')
+ {
+ continue;
+ }
+
+ if (st.count_tokens (' ') != 2)
+ {
+ std::cerr << "Error when reading alias file (" << initFileName
+ << ") line " << line << "...\n";
+ continue;
+ }
+
+ alias = Utils::to_upper (st.next_token());
+ command = Utils::to_upper (st.next_token());
+
+ // Does the function already exist ?
+ if (!userFunctions[alias])
+ {
+ if (userFunction *u = userFunctions[command])
+ userFunctions[alias] =
+ new
+ userFunction(u->function,
+ u->minLevel,
+ u->needsChannelName);
+ }
+ }
}
- }
+
std::srand (std::time (0)); // srand for bot-random
+
#ifdef USESCRIPTS
- botInterp = new BotInterp(this, scriptLogFileName);
+ botInterp = new BotInterp(this, logs_dir + scriptLogFileName);
+ Interp::Startup2 (this);
botInterp->LoadScript(autoexecFileName);
#endif
}
Bot::~Bot()
{
- // TODO: is it ok to delete iterators!?!
-
Person *p;
while (spyList.size() != 0) {
p = (*spyList.begin()).second;
spyList.erase(spyList.begin());
delete p;
}
- DCCConnection *d;
- while (dccConnections.size() != 0) {
- d = *dccConnections.begin();
- dccConnections.erase(dccConnections.begin());
- delete d;
- }
- userFunction *u;
- while (userFunctions.size() != 0) {
- u = *userFunctions.begin();
- userFunctions.erase(userFunctions.begin());
- delete u;
- }
+ delete dccConnections;
+ destroy_user_functions ();
+
wantedChannel *w;
while (wantedChannels.size() != 0) {
w = (*wantedChannels.begin()).second;
shitList->save();
delete channelList;
delete userList;
- delete todoList;
delete serverList;
delete shitList;
delete serverConnection;
void
Bot::readConfig()
{
- std::ifstream file(configFileName);
+ std::ifstream file(configFileName.c_str ());
String temp;
int line = 1;
file >> temp;
- if (temp.length() == 0 || temp[0] == '#') {
+ if (temp.length() == 0 || temp[(unsigned int)0] == '#') {
line++;
continue;
}
StringTokenizer st(temp);
- String command = st.nextToken('=').trim().toUpper();
- String parameters = st.nextToken('=').trim();
+ String command = Utils::to_upper (Utils::trim_str (st.next_token('=')));
+ String parameters = Utils::trim_str (st.next_token('='));
if (command == "NICK" || command == "NICKNAME")
nickName = wantedNickName = parameters;
else if (command == "IRCNAME" || command == "REALNAME")
ircName = parameters;
else if (command == "CMDCHAR" || command == "COMMAND")
- commandChar = parameters[0];
+ commandChar = parameters[(unsigned int)0];
else if (command == "USERLIST")
userListFileName = parameters;
else if (command == "SHITLIST")
<< " I will use compatibility mode, but you're really"
<< " missing something.\n";
StringTokenizer st2(parameters);
- String name = st2.nextToken().toLower();
- String key = st2.nextToken();
+ String name = Utils::to_lower (st2.next_token());
+ String key = st2.next_token();
wantedChannels[name] = new wantedChannel("", "", key);
} else {
StringTokenizer st2(parameters);
- String name = st2.nextToken(':').toLower();
- String mode = st2.nextToken(':');
- String keep = st2.nextToken(':');
- String key = st2.nextToken(':');
+ String name = Utils::to_lower (st2.next_token(':'));
+ String mode = st2.next_token(':');
+ String keep = st2.next_token(':');
+ String key = st2.next_token(':');
wantedChannels[name] = new wantedChannel(mode, keep, key);
}
}
else if (command == "LOGFILE")
- logFileName = parameters;
+ {
+ if (parameters != logFileName)
+ {
+ if (parameters[(unsigned int)0] == '/')
+ {
+ StringTokenizer log_st (parameters);
+ std::string log_dir = "/";
+
+ for (unsigned int m = log_st.count_tokens ('/');
+ --m;
+ m > 0)
+ {
+ log_dir += log_st.next_token ('/') + "/";
+ }
+
+ std::cerr << "==" << log_dir << std::endl;
+
+ set_log_dir (log_dir);
+ set_log_file (log_st.rest ());
+ }
+ else
+ set_log_file (parameters);
+ }
+ }
#ifdef USESCRIPTS
else if (command == "SCRIPTLOGFILE")
scriptLogFileName = parameters;
initFileName = parameters;
else if (command == "LOCALIP")
localIP = parameters;
+ else if (command == "MAXNICKLENGTH")
+ MAX_NICKLENGTH = std::atoi (parameters.c_str ());
else if (command == "SERVER") {
if (parameters.indexOf(' ') == -1)
serverList->addServer(new Server(parameters));
else {
StringTokenizer st2(parameters);
- String name = st2.nextToken();
- int port = std::atoi(st2.nextToken());
+ String name = st2.next_token();
+ int port = std::atoi(st2.next_token().c_str());
serverList->addServer(new Server(name,
port,
- st2.nextToken()));
+ st2.next_token()));
}
}
+ else if (command == "")
+ {
+ // do nothing
+ }
else {
logLine(String("Syntax error in file ") + configFileName +
", line " + String((long)line));
{
nextServer();
- while (!stop) {
- waitForInput(); // This is the main event loop
- if (!serverConnection->queue->flush())
- nextServer();
- }
+ while (!stop)
+ {
+ waitForInput(); // This is the main event loop
+ dccConnections->checkStale ();
+
+ if (!serverConnection->queue->flush())
+ {
+ // Disconnected
+#ifdef USESCRIPTS
+ // Run hooks/disconnect
+ this->botInterp->RunHooks
+ (Hook::DISCONNECT,
+ serverConnection->server->getHostName (),
+ scm_list_n
+ (Utils::str2scm (serverConnection->server->getHostName ()),
+ SCM_BOOL_F));
+#endif
+ nextServer();
+ }
+ }
}
void
FD_SET(sock, &rd);
#endif
- for (std::list<DCCConnection *>::iterator it = dccConnections.begin();
- it != dccConnections.end(); ++it) {
- int s = (*it)->getFileDescriptor();
+ DCC_MAP* dccmap = &dccConnections->dcc_map;
+ for (DCC_MAP::iterator it = dccmap->begin ();
+ it != dccmap->end(); ++it) {
+ int s = it->second->dcc->getFileDescriptor();
#ifdef _HPUX_SOURCE
rd |= s;
#else
if (serverConnection->handleInput())
nextServer();
- std::list<DCCConnection *>::iterator it = dccConnections.begin();
- std::list<DCCConnection *>::iterator it2;
-
- while (it != dccConnections.end()) {
- it2 = it;
- ++it;
-#ifdef _HPUX_SOURCE
- if (rd & (*it2)->getFileDescriptor()) {
-#else
- if (FD_ISSET((*it2)->getFileDescriptor(), &rd)) {
-#endif
- if ((*it2)->handleInput()) {
- delete *it2;
- dccConnections.erase(it2);
- }
- }
- }
+ dccConnections->checkInput (rd);
}
- if (currentTime < std::time(NULL)) { // Actions that we do each second
- currentTime = std::time(NULL);
+ if (currentTime < std::time(0)) { // Actions that we do each second
+ currentTime = std::time(0);
for (std::map<String, unsigned int, std::less<String> >::iterator
it = ignoredUserhosts.begin();
it != ignoredUserhosts.end(); ++it)
if ((*it).second > 0)
(*it).second--;
- String line;
- while ((line = todoList->getNext()) != "") {
- serverConnection->queue->sendChannelMode(line);
- }
+ for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin ();
+ it != channelList->end ();
+ ++it)
+ {
+ it->second->purge_expired_bans ();
+ }
#ifdef USESCRIPTS
botInterp->RunTimers(currentTime);
-#endif
-#ifdef USESCRIPTS
tm *thisTime = localtime(¤tTime);
- if (thisTime->tm_sec == 0) {
- char s[6];
- sprintf(s, "%2d:%2d", thisTime->tm_hour, thisTime->tm_min);
- botInterp->RunHooks(Hook::TIMER, String(s),
- gh_list(Utils::string2SCM(String(s)), SCM_UNDEFINED));
- }
+ if (thisTime->tm_sec == 0)
+ {
+ char s[6];
+ std::snprintf(s, 6, "%02d:%02d", thisTime->tm_hour, thisTime->tm_min);
+
+ botInterp->RunHooks(Hook::TIMER, String(s),
+ scm_list_n (Utils::str2scm (std::string (s)),
+ SCM_UNDEFINED));
+ }
#endif
}
serverConnection->queue->sendJoin((*it).first, (*it).second->key);
}
- std::list<DCCConnection *>::iterator it2;
-
- for (std::list<DCCConnection *>::iterator it = dccConnections.begin();
- it != dccConnections.end(); ) {
- it2 = it;
- ++it;
- if ((*it2)->autoRemove && currentTime >= (std::time_t)((*it2)->lastSpoken + Bot::DCC_DELAY)) {
- delete *it2;
- dccConnections.erase(it2);
+ if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken
+ + Bot::PING_TIME) && !sentPing)
+ {
+ serverConnection->queue->sendPing("Testing connection");
+ sentPing = true;
}
- }
- if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::PING_TIME) && !sentPing) {
- serverConnection->queue->sendPing("Testing connection");
- sentPing = true;
- }
-
- if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken + Bot::TIMEOUT)) {
- sentPing = false;
- nextServer();
- }
+ if (currentTime >= (std::time_t)(serverConnection->serverLastSpoken
+ + Bot::TIMEOUT))
+ {
+ sentPing = false;
+ nextServer();
+ }
}
// We can change server if we will not lose op on a channel
it != channelList->end(); ++it) {
channel = (*it).first;
c = channelList->getChannel(channel);
- if (c->countOp == 1 &&
- c->count > 1 && this->iAmOp(channel))
+ if (c->operator_count () == 1 &&
+ c->user_count () > 1 && this->iAmOp(channel))
return false;
}
return true;
}
void
-Bot::addDCC(Person * from, unsigned long address, int port)
+Bot::addDCC(Person * from, unsigned long address, int port, int type)
{
- DCCConnection * d = new DCCConnection(this, from->getAddress(),
- address, port);
+ DCCConnection *d = 0;
+
+ if (type == CHAT)
+ {
+ d = new DCCChatConnection(this, from->getAddress (),
+ address, port);
+ }
+ else
+ {
+ return;
+ }
if (!d->connect())
- return;
+ {
+ logLine ("DCC Connection failed from " + from->getAddress ());
+ return;
+ }
- dccConnections.push_back(d);
+ logLine ("DCC CHAT accepted from" + from->getAddress ());
+ dccConnections->addConnection (d);
}
void
Bot::rehash()
{
- for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin();
+ for (std::map<String, Channel *, std::less<String> >::iterator it =
+ channelList->begin();
it != channelList->end(); ++it)
serverConnection->queue->sendWho((*it).first);
}
if (c && c->hasNick(nick))
- return c->getUser(nick)->userhost;
+ return c->getUser(nick).userhost;
unsigned long num = sentUserhostID++;
bool
Bot::iAmOp(String channel)
{
- User * me = channelList->getChannel(channel)->getUser(nickName);
- return (me->mode & User::OP_MODE);
+ return channelList->getChannel(channel)->getUser(nickName).mode & User::OP_MODE;
+}
+
+void
+Bot::init_user_functions ()
+{
+ // User Functions
+#define uf(f, l, b) new userFunction (f, l, b);
+ userFunctions["ACTION"] = uf (UserCommands::Action, User::USER, true);
+ userFunctions["ADDUSER"] = uf (UserCommands::AddUser, User::FRIEND, false);
+ userFunctions["ADDSERVER"] = uf (UserCommands::AddServer, User::FRIEND,
+ false);
+ userFunctions["ADDSHIT"] = uf (UserCommands::AddShit, User::FRIEND, false);
+ userFunctions["ALIAS"] = uf (UserCommands::Alias, User::MASTER, false);
+ userFunctions["BAN"] = uf (UserCommands::Ban, User::USER, true);
+ userFunctions["BANLIST"] = uf (UserCommands::BanList, User::USER, true);
+ userFunctions["CHANNELS"] =
+ uf (UserCommands::Channels, User::FRIEND, false);
+ userFunctions["CYCLE"] = uf (UserCommands::Cycle, User::FRIEND, true);
+ userFunctions["DCCLIST"] = uf (UserCommands::DCCList, User::FRIEND, false);
+ userFunctions["DEBAN"] = uf (UserCommands::Deban, User::USER, true);
+ userFunctions["DELSERVER"] = uf (UserCommands::DelServer, User::FRIEND,
+ false);
+ userFunctions["DELUSER"] = uf (UserCommands::DelUser, User::FRIEND, false);
+ userFunctions["DELSHIT"] = uf (UserCommands::DelShit, User::FRIEND, false);
+ userFunctions["DEOP"] = uf (UserCommands::Deop, User::TRUSTED_USER, true);
+ userFunctions["DIE"] = uf (UserCommands::Die, User::MASTER, false);
+ userFunctions["DO"] = uf (UserCommands::Do, User::MASTER, false);
+#ifdef USESCRIPTS
+ userFunctions["EXECUTE"] = uf (UserCommands::Execute, User::MASTER, false);
+#endif
+ userFunctions["HELP"] = uf (UserCommands::Help, User::NONE, false);
+ userFunctions["IDENT"] = uf (UserCommands::Ident, User::NONE, true);
+ userFunctions["INVITE"] = uf (UserCommands::Invite, User::USER, true);
+ userFunctions["JOIN"] = uf (UserCommands::Join, User::FRIEND, false);
+ userFunctions["KEEP"] = uf (UserCommands::Keep, User::FRIEND, true);
+ userFunctions["KICK"] = uf (UserCommands::Kick, User::USER, true);
+ userFunctions["KICKBAN"] = uf (UserCommands::KickBan, User::USER, true);
+ userFunctions["LOAD"] = uf (UserCommands::Load, User::FRIEND, false);
+#ifdef USESCRIPTS
+ userFunctions["LOADSCRIPT"] = uf (UserCommands::LoadScript, User::MASTER,
+ false);
+#endif
+ userFunctions["LOCK"] = uf (UserCommands::Lock, User::FRIEND, true);
+ userFunctions["MODE"] = uf (UserCommands::Mode, User::FRIEND, true);
+ userFunctions["MSG"] = uf (UserCommands::Msg, User::USER, false);
+ userFunctions["NAMES"] = uf (UserCommands::Names, User::USER, true);
+ userFunctions["NEXTSERVER"] = uf (UserCommands::NextServer, User::FRIEND,
+ false);
+ userFunctions["NICK"] = uf (UserCommands::Nick, User::FRIEND, false);
+ userFunctions["NSLOOKUP"] = uf (UserCommands::NsLookup, User::USER, false);
+ userFunctions["OP"] = uf (UserCommands::Op, User::TRUSTED_USER, true);
+ userFunctions["PART"] = uf (UserCommands::Part, User::FRIEND, true);
+ userFunctions["PASSWORD"] = uf (UserCommands::Password, User::USER, true);
+ userFunctions["RECONNECT"] =
+ uf (UserCommands::Reconnect, User::FRIEND, false);
+ userFunctions["RSPYMESSAGE"] =
+ uf (UserCommands::RSpyMessage, User::USER, false);
+ userFunctions["SAVE"] = uf (UserCommands::Save, User::FRIEND, false);
+ userFunctions["SAY"] = uf (UserCommands::Say, User::USER, true);
+ userFunctions["SERVER"] = uf (UserCommands::Server, User::FRIEND, false);
+ userFunctions["SERVERLIST"] =
+ uf (UserCommands::ServerList, User::FRIEND, false);
+ userFunctions["SETFLOODRATE"] =
+ uf (UserCommands::SetFloodRate, User::MASTER, false);
+ userFunctions["SETVERSION"] =
+ uf (UserCommands::SetVersion, User::MASTER, false);
+ userFunctions["SHITLIST"] =
+ uf (UserCommands::ShitList, User::FRIEND, false);
+ userFunctions["SPYLIST"] = uf (UserCommands::SpyList, User::USER, false);
+ userFunctions["SPYMESSAGE"] =
+ uf (UserCommands::SpyMessage, User::USER, false);
+ userFunctions["STATS"] = uf (UserCommands::Stats, User::FRIEND, true);
+ userFunctions["TBAN"] = uf (UserCommands::TBan, User::USER, true);
+ userFunctions["TKBAN"] = uf (UserCommands::TKBan, User::USER, true);
+ userFunctions["TOPIC"] = uf (UserCommands::Topic, User::USER, true);
+ userFunctions["UNLOCK"] = uf (UserCommands::Unlock, User::FRIEND, true);
+ userFunctions["USERLIST"] =
+ uf (UserCommands::UserList, User::FRIEND, false);
+ userFunctions["WHO"] = uf (UserCommands::Who, User::NONE, true);
+ userFunctions["WHOIS"] = uf (UserCommands::Whois, User::FRIEND, true);
+#undef uf
+}
+
+namespace
+{
+ void erase_userf (std::pair<std::string, class userFunction*> it)
+ {
+ delete it.second;
+ }
+}
+
+void
+Bot::destroy_user_functions ()
+{
+ std::for_each (userFunctions.begin (),
+ userFunctions.end (),
+ erase_userf);
+ userFunctions.erase (userFunctions.begin (),
+ userFunctions.end ());
+}
+
+void
+Bot::set_log_file (String name)
+{
+ logFileName = name;
+ logFile.close ();
+ logFile.clear ();
+#if HAVE_IOSBASE
+ logFile.open((logs_dir + logFileName).c_str (), std::ios_base::out |
+ std::ios_base::ate | std::ios_base::app);
+#else
+ logFile.open((logs_dir + logFileName).c_str (), ios::out | ios::ate
+ | ios::app);
+#endif
+
+ logLine("Starting log.");
+}
+
+void
+Bot::set_log_dir (String dir)
+{
+ logs_dir = dir;
+
+ DIR *temp = opendir (logs_dir.c_str ());
+
+ if (!temp)
+ {
+ mkdir (logs_dir.c_str (), S_IRWXU);
+ }
+ else
+ {
+ closedir (temp);
+ }
}