[HEADERS]
[reactos.git] / irc / ArchBlackmann / ArchBlackmann.cpp
1 // irc_test.cpp
2
3 #ifdef _MSC_VER
4 #pragma warning ( disable : 4786 )
5 #endif//_MSC_VER
6
7 #include <time.h>
8 #include <stdio.h>
9
10 #include "File.h"
11 #include "ssprintf.h"
12 #include "trim.h"
13
14 #include "IRCClient.h"
15 #include "config.h"
16
17 using std::string;
18 using std::vector;
19
20 //vector<string> tech, module, dev, stru, period, status, type, func, irql, curse, cursecop;
21
22 class List
23 {
24 public:
25 string name;
26 bool macro;
27 std::vector<std::string> list;
28 string tag;
29 int last;
30 List() { last = -1; }
31 List ( const char* _name, bool _macro ) : name(_name), macro(_macro)
32 {
33 tag = ssprintf("%%%s%%",_name);
34 last = -1;
35 }
36 };
37
38 vector<List> lists;
39 vector<string> ops;
40
41 void ImportList ( const char* listname, bool macro )
42 {
43 lists.push_back ( List ( listname, macro ) );
44 List& list = lists.back();
45 File f ( ssprintf("%s.txt",listname).c_str(), "r" );
46 string line;
47 while ( f.next_line ( line, true ) )
48 list.list.push_back ( line );
49 }
50
51 const char* ListRand ( List& list )
52 {
53 vector<string>& l = list.list;
54 if ( !l.size() )
55 {
56 static string nothing;
57 nothing = ssprintf ( "<list '%s' empty>", list.name.c_str() );
58 return nothing.c_str();
59 }
60 else if ( l.size() == 1 )
61 return l[0].c_str();
62 int sel = list.last;
63 while ( sel == list.last )
64 sel = rand()%l.size();
65 list.last = sel;
66 return l[sel].c_str();
67 }
68
69 const char* ListRand ( int i )
70 {
71 return ListRand ( lists[i] );
72 }
73
74 int GetListIndex ( const char* listname )
75 {
76 for ( int i = 0; i < lists.size(); i++ )
77 {
78 if ( !stricmp ( lists[i].name.c_str(), listname ) )
79 return i;
80 }
81 return -1;
82 }
83
84 List& GetList ( const char* listname )
85 {
86 return lists[GetListIndex(listname)];
87 }
88
89 const char* ListRand ( const char* list )
90 {
91 int i = GetListIndex ( list );
92 if ( i < 0 )
93 return NULL;
94 return ListRand(i);
95 }
96
97 string TaggedReply ( const char* listname )
98 {
99 string t = ListRand(listname);
100 string out;
101 const char* p = t.c_str();
102 while ( *p )
103 {
104 if ( *p == '%' )
105 {
106 bool found = false;
107 for ( int i = 0; i < lists.size() && !found; i++ )
108 {
109 if ( lists[i].macro && !strnicmp ( p, lists[i].tag.c_str(), lists[i].tag.size() ) )
110 {
111 out += ListRand(i);
112 p += lists[i].tag.size();
113 found = true;
114 }
115 }
116 if ( !found )
117 out += *p++;
118 }
119 const char* p2 = strchr ( p, '%' );
120 if ( !p2 )
121 p2 = p + strlen(p);
122 if ( p2 > p )
123 {
124 out += string ( p, p2-p );
125 p = p2;
126 }
127 }
128 return out;
129 }
130
131 string gobble ( string& s, const char* delim )
132 {
133 const char* p = s.c_str();
134 p += strspn ( p, delim );
135 const char* p2 = strpbrk ( p, delim );
136 if ( !p2 ) p2 = p + strlen(p);
137 string out ( p, p2-p );
138 p2 += strspn ( p2, delim );
139 s = string ( p2 );
140 return out;
141 }
142
143 bool isop ( const string& who )
144 {
145 for ( int i = 0; i < ops.size(); i++ )
146 {
147 if ( ops[i] == who )
148 return true;
149 }
150 return false;
151 }
152
153
154 // do custom stuff with the IRCClient from your subclass via the provided callbacks...
155 class MyIRCClient : public IRCClient
156 {
157 File flog;
158 clock_t brake_silence;
159
160 // wait another 30 mins to brake the silence
161 #define NOIDLE brake_silence = clock () + 30 * CLK_TCK * 60
162
163 void CheckIdle ( void )
164 {
165 while (true) // _inRun
166 {
167 while (clock() < brake_silence)
168 Sleep(10000);
169
170 string out = TaggedReply("idle");
171
172 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
173 Action ( CHANNEL, &out[4] );
174 else
175 PrivMsg ( CHANNEL, out );
176
177 NOIDLE;
178 }
179 }
180
181 static void THREADAPI CallMe ( MyIRCClient* irc )
182 {
183 irc->CheckIdle();
184 }
185
186 public:
187
188 MyIRCClient()
189 {
190 NOIDLE;
191 ThreadPool::Instance().Launch ( (ThreadPoolFunc*)MyIRCClient::CallMe, this );
192 flog.open ( "arch.log", "r+" );
193 }
194 // see IRCClient.h for documentation on these callbacks...
195 bool OnConnected()
196 {
197 return true;
198 }
199 bool OnJoin ( const string& user, const string& channel )
200 {
201 //printf ( "user '%s' joined channel '%s'\n", user.c_str(), channel.c_str() );
202 return true;
203 }
204 bool OnPart ( const std::string& user, const std::string& channel )
205 {
206 std::vector<std::string>::iterator it = ops.begin();
207 for ( ; it != ops.end(); it++ )
208 {
209 if ( *it == user )
210 {
211 printf ( "remove '%s' to ops list\n", user.c_str() );
212 ops.erase ( it );
213 }
214 }
215 return true;
216 }
217 bool OnNick ( const std::string& oldNick, const std::string& newNick )
218 {
219 for ( int i = 0; i < ops.size(); i++ )
220 {
221 if ( ops[i] == oldNick )
222 {
223 printf ( "op '%s' changed nick to '%s'\n", oldNick.c_str(), newNick.c_str() );
224 ops[i] = newNick;
225 return true;
226 }
227 }
228 return true;
229 }
230 bool OnEndChannelUsers ( const string& channel )
231 {
232 return true;
233 }
234 bool OnPrivMsg ( const string& from, const string& text )
235 {
236 //flog.flush();
237 printf ( "<%s> %s\n", from.c_str(), text.c_str() );
238 flog.printf ( "<%s> %s\n", from.c_str(), text.c_str() );
239
240 if ( !isop(from) )
241 return PrivMsg ( from, "hey, your tongue doesn't belong there!" );
242
243 else if ( strnicmp ( text.c_str(), "!say ", 5 ) )
244 return PrivMsg ( from, "Talk to me on normal Chanel" );
245
246 string say = trim(&text[5]);
247
248 if ( !strnicmp ( say.c_str(), "/me ", 4 ) )
249 return Action ( CHANNEL, trim(&say[4]) );
250 else
251 return PrivMsg ( CHANNEL, trim(say) );
252 }
253 bool OnChannelMsg ( const string& channel, const string& from, const string& text )
254 {
255 fflush ( flog );
256 printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
257 flog.printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
258 NOIDLE; // add 30 mins till idle
259
260 bool found_name = false;
261 string text2 ( text );
262 strlwr ( &text2[0] );
263
264 if ( !strnicmp ( text.c_str(), BOTNAME, strlen(BOTNAME) ) )
265 found_name = true;
266
267 string s ( text );
268
269 if ( found_name )
270 gobble ( s, " \t" ); // remove bot name
271
272 // command
273 if ( s[0] == '!' )
274 {
275 bool from_op = isop(from);
276 string cmd = gobble ( s, " \t" );
277
278 // from all
279 if ( false && cmd == "!svn" && from == "TechBot" ) // || cmd == "!help" && !TechBotOnline
280 {
281 PrivMsg ( channel, "For my help try !what." );
282 }
283
284 // from normel user
285 else if ( !from_op )
286 {
287 if ( cmd == "!grovel" )
288 {
289 string out = ssprintf(TaggedReply("nogrovel").c_str(),from.c_str());
290 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
291 return Action ( channel, &out[4] );
292 else
293 return PrivMsg ( channel, out );
294 }
295
296 else if ( cmd == "!what" )
297 {
298 return PrivMsg ( channel, ssprintf("For you, %s, I only support the \"!grovel\" command.", from.c_str()).c_str() );
299 }
300
301 else if ( cmd == "!grovel" || cmd == "!kiss" || cmd == "!hug"
302 || cmd == "!give" || cmd == "!what" || cmd == "!add" || cmd == "!remove" )
303 {
304 PrivMsg ( channel, ssprintf("%s: I only take commands from ops",from.c_str()) );
305 }
306 }
307
308 // from op
309 else if ( cmd == "!grovel" )
310 {
311 string out = ssprintf(TaggedReply("grovel").c_str(),from.c_str());
312 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
313 return Action ( channel, &out[4] );
314 else
315 return PrivMsg ( channel, out );
316 }
317 else if ( cmd == "!kiss" )
318 {
319 if ( s.size() )
320 return Action ( channel, ssprintf("kisses %s",s.c_str()) );
321 else
322 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
323 }
324 else if ( cmd == "!hug" )
325 {
326 if ( s.size() )
327 return Action ( channel, ssprintf("hugs %s",s.c_str()) );
328 else
329 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
330 }
331 else if ( cmd == "!give" )
332 {
333 string who = gobble(s," \t");
334 if ( who.size() && s.size() )
335 return Action ( channel, ssprintf("gives %s a %s",who.c_str(),s.c_str()) );
336 else
337 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
338 }
339 else if ( cmd == "!what" )
340 {
341 PrivMsg ( channel, "For ops I support the following commands:" );
342 PrivMsg ( channel, "!grovel" );
343 PrivMsg ( channel, "!kiss" );
344 PrivMsg ( channel, "!hug" );
345 PrivMsg ( channel, "!give" );
346 PrivMsg ( channel, "!say (the input is a private message)" );
347 PrivMsg ( channel, "!add" );
348 PrivMsg ( channel, "!remove" );
349 PrivMsg ( channel, " - for more info see wiki" );
350 }
351 else if ( cmd == "!add" )
352 {
353 string listname = gobble ( s, " \t" );
354 int i = GetListIndex ( listname.c_str() );
355 if ( i == -1 )
356 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
357 List& list = lists[i];
358 if ( s[0] == '\"' || s[0] == '\'' )
359 {
360 char delim = s[0];
361 const char* p = &s[1];
362 const char* p2 = strchr ( p, delim );
363 if ( !p2 )
364 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
365 s = string ( p, p2-p );
366 }
367 for ( i = 0; i < list.list.size(); i++ )
368 {
369 if ( list.list[i] == s )
370 return PrivMsg ( channel, ssprintf("%s: entry already exists in list '%s'",from.c_str(),listname.c_str()) );
371 }
372 if ( !stricmp ( listname.c_str(), "curse" ) )
373 strlwr ( &s[0] );
374 list.list.push_back ( s );
375 {
376 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
377 for ( i = 0; i < list.list.size(); i++ )
378 f.printf ( "%s\n", list.list[i].c_str() );
379 }
380 return PrivMsg ( channel, ssprintf("%s: entry added to list '%s'",from.c_str(),listname.c_str()) );
381 }
382 else if ( cmd == "!remove" )
383 {
384 string listname = gobble ( s, " \t" );
385 int i = GetListIndex ( listname.c_str() );
386 if ( i == -1 )
387 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
388 List& list = lists[i];
389 if ( s[0] == '\"' || s[0] == '\'' )
390 {
391 char delim = s[0];
392 const char* p = &s[1];
393 const char* p2 = strchr ( p, delim );
394 if ( !p2 )
395 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
396 s = string ( p, p2-p );
397 }
398 std::vector<std::string>::iterator it = list.list.begin();
399 for ( ; it != list.list.end(); it++ )
400 {
401 if ( *it == s )
402 {
403 list.list.erase ( it );
404 {
405 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
406 it = list.list.begin();
407 for ( ; it < list.list.end(); it++ )
408 f.printf ( "%s\n", it->c_str() );
409 }
410 return PrivMsg ( channel, ssprintf("%s: entry removed from list '%s'",from.c_str(),listname.c_str()) );
411 }
412 }
413 return PrivMsg ( channel, ssprintf("%s: entry doesn't exist in list '%s'",from.c_str(),listname.c_str()) );
414 }
415 else
416 {
417 if (found_name)
418 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
419 }
420
421 } // if (command)
422
423 bool found_curse = false;
424 static vector<string>& curse = GetList("curse").list;
425 text2 = " " + text2 + " ";
426
427 for ( int i = 0; i < curse.size() && !found_curse; i++ )
428 {
429 if ( strstr ( text2.c_str(), curse[i].c_str() ) )
430 found_curse = true;
431 }
432 if ( found_curse )
433 {
434 static List& cursecop = GetList("cursecop");
435 return PrivMsg ( channel, ssprintf("%s: %s", from.c_str(), ListRand(cursecop)) );
436 }
437
438 string botname (BOTNAME);
439 strlwr ( &botname[0] );
440 //botname = " " + botname + " ";
441
442 if ( strstr(text2.c_str(), botname.c_str()) || strstr(text2.c_str(), " arch ") || found_name )
443 {
444 string out = ssprintf("%s: %s", from.c_str(), TaggedReply("tech").c_str());
445 flog.printf ( "TECH-REPLY: %s\n", out.c_str() );
446 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
447 return Action ( channel, &out[4] );
448 else
449 return PrivMsg ( channel, out );
450 }
451 return true;
452
453 } // On Chanel Message
454
455 bool OnChannelMode ( const string& channel, const string& mode )
456 {
457 //printf ( "OnChannelMode(%s,%s)\n", channel.c_str(), mode.c_str() );
458 return true;
459 }
460 bool OnUserModeInChannel ( const string& src, const string& channel, const string& mode, const string& target )
461 {
462 printf ( "OnUserModeInChannel(%s,%s,%s,%s)\n", src.c_str(), channel.c_str(), mode.c_str(), target.c_str() );
463 const char* p = mode.c_str();
464 if ( !p )
465 return true;
466 while ( *p )
467 {
468 switch ( *p++ )
469 {
470 case '+':
471 while ( *p != 0 && *p != ' ' )
472 {
473 if ( *p == 'o' )
474 {
475 printf ( "adding '%s' to ops list\n", target.c_str() );
476 ops.push_back ( target );
477 }
478 break;
479 }
480 break;
481 case '-':
482 while ( *p != 0 && *p != ' ' )
483 {
484 if ( *p == 'o' )
485 {
486 std::vector<std::string>::iterator it = ops.begin();
487 for ( ; it != ops.end(); it++ )
488 {
489 if ( *it == target )
490 {
491 printf ( "remove '%s' to ops list\n", target.c_str() );
492 ops.erase ( it );
493 }
494 }
495 break;
496 }
497 }
498 }
499 }
500 return true;
501 }
502 bool OnMode ( const string& user, const string& mode )
503 {
504 //printf ( "OnMode(%s,%s)\n", user.c_str(), mode.c_str() );
505 return true;
506 }
507 bool OnChannelUsers ( const string& channel, const vector<string>& users )
508 {
509 //printf ( "[%s has %i users]: ", channel.c_str(), users.size() );
510 for ( int i = 0; i < users.size(); i++ )
511 {
512 if ( users[i][0] == '@' )
513 ops.push_back ( &users[i][1] );
514 /*if ( i )
515 printf ( ", " );
516 printf ( "%s", users[i].c_str() );*/
517 }
518 //printf ( "\n" );
519 return true;
520 }
521 bool OnKick ( void )
522 {
523 Join(CHANNEL);
524 return true;
525 }
526 bool OnBanned ( const std::string& channel )
527 {
528 Sleep(10000);
529 return Join(CHANNEL);
530 }
531 };
532
533 int main ( int argc, char** argv )
534 {
535 srand ( time(NULL) );
536
537 ImportList ( "dev", true );
538 ImportList ( "func", true );
539 ImportList ( "dev", true );
540 ImportList ( "func", true );
541 ImportList ( "irql", true );
542 ImportList ( "module", true );
543 ImportList ( "period", true );
544 ImportList ( "status", true );
545 ImportList ( "stru", true );
546 ImportList ( "type", true );
547
548 ImportList ( "tech", false );
549 ImportList ( "curse", false );
550 ImportList ( "cursecop", false );
551 ImportList ( "grovel", false );
552 ImportList ( "nogrovel", false );
553 ImportList ( "idle", false );
554
555 #ifdef _DEBUG
556 printf ( "initializing IRCClient debugging\n" );
557 IRCClient::SetDebug ( true );
558 #endif//_DEBUG
559
560 while (true)
561 {
562 printf ( "calling suStartup()\n" );
563 suStartup();
564 printf ( "creating IRCClient object\n" );
565 MyIRCClient irc;
566 printf ( "connecting to freenode\n" );
567
568 if ( !irc.Connect ( SERVER ) ) // irc.freenode.net
569 {
570 printf ( "couldn't connect to server\n" );
571 Sleep(10000);
572 continue;
573 }
574 printf ( "sending user command\n" );
575 if ( !irc.User ( BOTNAME, "reactos.com", SERVER, "ArchBlackmann" ) )
576 {
577 printf ( "USER command failed, retying ...\n" );
578 Sleep(10000);
579 continue;
580 }
581 printf ( "sending nick\n" );
582 if ( !irc.Nick ( BOTNAME ) )
583 {
584 printf ( "NICK command failed, retying ...\n" );
585 Sleep(10000);
586 continue;
587 }
588 printf ( "setting mode\n" );
589 if ( !irc.Mode ( MODE ) )
590 {
591 printf ( "MODE command failed, retying ...\n" );
592 Sleep(10000);
593 continue;
594 }
595 printf ( "joining channel\n" );
596 if ( !irc.Join ( CHANNEL ) )
597 {
598 printf ( "JOIN command failed, retying ...\n" );
599 Sleep(10000);
600 continue;
601 }
602
603 printf ( "entering irc client processor\n" );
604 irc.Run ( false ); // do the processing in this thread...
605 }
606
607 return 0;
608 }