Some new features like Login, Config.h and Idle message
[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 for ( int i = 0; i < ops.size(); i++ )
207 {
208 if ( ops[i] == user )
209 {
210 printf ( "remove '%s' to ops list\n", user.c_str() );
211 ops.erase ( &ops[i] );
212 }
213 }
214 return true;
215 }
216 bool OnNick ( const std::string& oldNick, const std::string& newNick )
217 {
218 for ( int i = 0; i < ops.size(); i++ )
219 {
220 if ( ops[i] == oldNick )
221 {
222 printf ( "op '%s' changed nick to '%s'\n", oldNick.c_str(), newNick.c_str() );
223 ops[i] = newNick;
224 return true;
225 }
226 }
227 return true;
228 }
229 bool OnEndChannelUsers ( const string& channel )
230 {
231 return true;
232 }
233 bool OnPrivMsg ( const string& from, const string& text )
234 {
235 //flog.flush();
236 printf ( "<%s> %s\n", from.c_str(), text.c_str() );
237 flog.printf ( "<%s> %s\n", from.c_str(), text.c_str() );
238
239 if ( !isop(from) )
240 return PrivMsg ( from, "hey, your tongue doesn't belong there!" );
241
242 else if ( strnicmp ( text.c_str(), "!say ", 5 ) )
243 return PrivMsg ( from, "Talk to me on normal Chanel" );
244
245 string say = trim(&text[5]);
246
247 if ( !strnicmp ( say.c_str(), "/me ", 4 ) )
248 return Action ( CHANNEL, trim(&say[4]) );
249 else
250 return PrivMsg ( CHANNEL, trim(say) );
251 }
252 bool OnChannelMsg ( const string& channel, const string& from, const string& text )
253 {
254 fflush ( flog );
255 printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
256 flog.printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
257 NOIDLE; // add 30 mins till idle
258
259 bool found_name = false;
260 string text2 ( text );
261 strlwr ( &text2[0] );
262
263 if ( !strnicmp ( text.c_str(), BOTNAME, strlen(BOTNAME) ) )
264 found_name = true;
265
266 string s ( text );
267
268 if ( found_name )
269 gobble ( s, " \t" ); // remove bot name
270
271 // command
272 if ( s[0] == '!' )
273 {
274 bool from_op = isop(from);
275 string cmd = gobble ( s, " \t" );
276
277 // from all
278 if ( false && cmd == "!svn" && from == "TechBot" ) // || cmd == "!help" && !TechBotOnline
279 {
280 PrivMsg ( channel, "For my help try !what." );
281 }
282
283 // from normel user
284 else if ( !from_op )
285 {
286 if ( cmd == "!grovel" )
287 {
288 string out = ssprintf(TaggedReply("nogrovel").c_str(),from.c_str());
289 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
290 return Action ( channel, &out[4] );
291 else
292 return PrivMsg ( channel, out );
293 }
294
295 else if ( cmd == "!what" )
296 {
297 return PrivMsg ( channel, ssprintf("For you, %s, I only support the \"!grovel\" command.", from.c_str()).c_str() );
298 }
299
300 else if ( cmd == "!grovel" || cmd == "!kiss" || cmd == "!hug"
301 || cmd == "!give" || cmd == "!what" || cmd == "!add" || cmd == "!remove" )
302 {
303 PrivMsg ( channel, ssprintf("%s: I only take commands from ops",from.c_str()) );
304 }
305 }
306
307 // from op
308 else if ( cmd == "!grovel" )
309 {
310 string out = ssprintf(TaggedReply("grovel").c_str(),from.c_str());
311 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
312 return Action ( channel, &out[4] );
313 else
314 return PrivMsg ( channel, out );
315 }
316 else if ( cmd == "!kiss" )
317 {
318 if ( s.size() )
319 return Action ( channel, ssprintf("kisses %s",s.c_str()) );
320 else
321 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
322 }
323 else if ( cmd == "!hug" )
324 {
325 if ( s.size() )
326 return Action ( channel, ssprintf("hugs %s",s.c_str()) );
327 else
328 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
329 }
330 else if ( cmd == "!give" )
331 {
332 string who = gobble(s," \t");
333 if ( who.size() && s.size() )
334 return Action ( channel, ssprintf("gives %s a %s",who.c_str(),s.c_str()) );
335 else
336 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
337 }
338 else if ( cmd == "!what" )
339 {
340 PrivMsg ( channel, "For ops I support the following commands:" );
341 PrivMsg ( channel, "!grovel" );
342 PrivMsg ( channel, "!kiss" );
343 PrivMsg ( channel, "!hug" );
344 PrivMsg ( channel, "!give" );
345 PrivMsg ( channel, "!say (the input is a private message)" );
346 PrivMsg ( channel, "!add" );
347 PrivMsg ( channel, "!remove" );
348 PrivMsg ( channel, " - for more info see wiki" );
349 }
350 else if ( cmd == "!add" )
351 {
352 string listname = gobble ( s, " \t" );
353 int i = GetListIndex ( listname.c_str() );
354 if ( i == -1 )
355 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
356 List& list = lists[i];
357 if ( s[0] == '\"' || s[0] == '\'' )
358 {
359 char delim = s[0];
360 const char* p = &s[1];
361 const char* p2 = strchr ( p, delim );
362 if ( !p2 )
363 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
364 s = string ( p, p2-p );
365 }
366 for ( i = 0; i < list.list.size(); i++ )
367 {
368 if ( list.list[i] == s )
369 return PrivMsg ( channel, ssprintf("%s: entry already exists in list '%s'",from.c_str(),listname.c_str()) );
370 }
371 if ( !stricmp ( listname.c_str(), "curse" ) )
372 strlwr ( &s[0] );
373 list.list.push_back ( s );
374 {
375 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
376 for ( i = 0; i < list.list.size(); i++ )
377 f.printf ( "%s\n", list.list[i].c_str() );
378 }
379 return PrivMsg ( channel, ssprintf("%s: entry added to list '%s'",from.c_str(),listname.c_str()) );
380 }
381 else if ( cmd == "!remove" )
382 {
383 string listname = gobble ( s, " \t" );
384 int i = GetListIndex ( listname.c_str() );
385 if ( i == -1 )
386 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
387 List& list = lists[i];
388 if ( s[0] == '\"' || s[0] == '\'' )
389 {
390 char delim = s[0];
391 const char* p = &s[1];
392 const char* p2 = strchr ( p, delim );
393 if ( !p2 )
394 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
395 s = string ( p, p2-p );
396 }
397 for ( i = 0; i < list.list.size(); i++ )
398 {
399 if ( list.list[i] == s )
400 {
401 list.list.erase ( &list.list[i] );
402 {
403 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
404 for ( i = 0; i < list.list.size(); i++ )
405 f.printf ( "%s\n", list.list[i].c_str() );
406 }
407 return PrivMsg ( channel, ssprintf("%s: entry removed from list '%s'",from.c_str(),listname.c_str()) );
408 }
409 }
410 return PrivMsg ( channel, ssprintf("%s: entry doesn't exist in list '%s'",from.c_str(),listname.c_str()) );
411 }
412 else
413 {
414 if (found_name)
415 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
416 }
417
418 } // if (command)
419
420 bool found_curse = false;
421 static vector<string>& curse = GetList("curse").list;
422 text2 = " " + text2 + " ";
423
424 for ( int i = 0; i < curse.size() && !found_curse; i++ )
425 {
426 if ( strstr ( text2.c_str(), curse[i].c_str() ) )
427 found_curse = true;
428 }
429 if ( found_curse )
430 {
431 static List& cursecop = GetList("cursecop");
432 return PrivMsg ( channel, ssprintf("%s: %s", from.c_str(), ListRand(cursecop)) );
433 }
434
435 string botname (BOTNAME);
436 strlwr ( &botname[0] );
437 //botname = " " + botname + " ";
438
439 if ( strstr(text2.c_str(), botname.c_str()) || strstr(text2.c_str(), " arch ") || found_name )
440 {
441 string out = ssprintf("%s: %s", from.c_str(), TaggedReply("tech").c_str());
442 flog.printf ( "TECH-REPLY: %s\n", out.c_str() );
443 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
444 return Action ( channel, &out[4] );
445 else
446 return PrivMsg ( channel, out );
447 }
448 return true;
449
450 } // On Chanel Message
451
452 bool OnChannelMode ( const string& channel, const string& mode )
453 {
454 //printf ( "OnChannelMode(%s,%s)\n", channel.c_str(), mode.c_str() );
455 return true;
456 }
457 bool OnUserModeInChannel ( const string& src, const string& channel, const string& mode, const string& target )
458 {
459 printf ( "OnUserModeInChannel(%s,%s,%s,%s)\n", src.c_str(), channel.c_str(), mode.c_str(), target.c_str() );
460 const char* p = mode.c_str();
461 if ( !p )
462 return true;
463 while ( *p )
464 {
465 switch ( *p++ )
466 {
467 case '+':
468 while ( *p != 0 && *p != ' ' )
469 {
470 if ( *p == 'o' )
471 {
472 printf ( "adding '%s' to ops list\n", target.c_str() );
473 ops.push_back ( target );
474 }
475 break;
476 }
477 break;
478 case '-':
479 while ( *p != 0 && *p != ' ' )
480 {
481 if ( *p == 'o' )
482 {
483 for ( int i = 0; i < ops.size(); i++ )
484 {
485 if ( ops[i] == target )
486 {
487 printf ( "remove '%s' to ops list\n", target.c_str() );
488 ops.erase ( &ops[i] );
489 }
490 }
491 break;
492 }
493 }
494 }
495 }
496 return true;
497 }
498 bool OnMode ( const string& user, const string& mode )
499 {
500 //printf ( "OnMode(%s,%s)\n", user.c_str(), mode.c_str() );
501 return true;
502 }
503 bool OnChannelUsers ( const string& channel, const vector<string>& users )
504 {
505 //printf ( "[%s has %i users]: ", channel.c_str(), users.size() );
506 for ( int i = 0; i < users.size(); i++ )
507 {
508 if ( users[i][0] == '@' )
509 ops.push_back ( &users[i][1] );
510 /*if ( i )
511 printf ( ", " );
512 printf ( "%s", users[i].c_str() );*/
513 }
514 //printf ( "\n" );
515 return true;
516 }
517 bool OnKick ( void )
518 {
519 Join(CHANNEL);
520 return true;
521 }
522 bool OnBanned ( const std::string& channel )
523 {
524 Sleep(10000);
525 return Join(CHANNEL);
526 }
527 };
528
529 int main ( int argc, char** argv )
530 {
531 srand ( time(NULL) );
532
533 ImportList ( "dev", true );
534 ImportList ( "func", true );
535 ImportList ( "dev", true );
536 ImportList ( "func", true );
537 ImportList ( "irql", true );
538 ImportList ( "module", true );
539 ImportList ( "period", true );
540 ImportList ( "status", true );
541 ImportList ( "stru", true );
542 ImportList ( "type", true );
543
544 ImportList ( "tech", false );
545 ImportList ( "curse", false );
546 ImportList ( "cursecop", false );
547 ImportList ( "grovel", false );
548 ImportList ( "nogrovel", false );
549 ImportList ( "idle", false );
550
551 #ifdef _DEBUG
552 printf ( "initializing IRCClient debugging\n" );
553 IRCClient::SetDebug ( true );
554 #endif//_DEBUG
555
556 while (true)
557 {
558 printf ( "calling suStartup()\n" );
559 suStartup();
560 printf ( "creating IRCClient object\n" );
561 MyIRCClient irc;
562 printf ( "connecting to freenode\n" );
563
564 if ( !irc.Connect ( SERVER ) ) // irc.freenode.net
565 {
566 printf ( "couldn't connect to server\n" );
567 Sleep(10000);
568 continue;
569 }
570 printf ( "sending user command\n" );
571 if ( !irc.User ( BOTNAME, "reactos.com", SERVER, "ArchBlackmann" ) )
572 {
573 printf ( "USER command failed, retying ...\n" );
574 Sleep(10000);
575 continue;
576 }
577 printf ( "sending nick\n" );
578 if ( !irc.Nick ( BOTNAME ) )
579 {
580 printf ( "NICK command failed, retying ...\n" );
581 Sleep(10000);
582 continue;
583 }
584 printf ( "setting mode\n" );
585 if ( !irc.Mode ( MODE ) )
586 {
587 printf ( "MODE command failed, retying ...\n" );
588 Sleep(10000);
589 continue;
590 }
591 printf ( "joining channel\n" );
592 if ( !irc.Join ( CHANNEL ) )
593 {
594 printf ( "JOIN command failed, retying ...\n" );
595 Sleep(10000);
596 continue;
597 }
598
599 printf ( "entering irc client processor\n" );
600 irc.Run ( false ); // do the processing in this thread...
601 }
602
603 return 0;
604 }