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