latest version of ArchBlackmann
[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
16 using std::string;
17 using std::vector;
18
19 #if defined(_DEBUG) && 0
20 const char* BOTNAME = "RoyBot";
21 const char* CHANNEL = "#RoyBotTest";
22 #else
23 const char* BOTNAME = "ArchBlackmann";
24 const char* CHANNEL = "#ReactOS";
25 #endif
26
27 //vector<string> tech, module, dev, stru, period, status, type, func, irql, curse, cursecop;
28
29 class List
30 {
31 public:
32 string name;
33 bool macro;
34 std::vector<std::string> list;
35 string tag;
36 int last;
37 List() { last = -1; }
38 List ( const char* _name, bool _macro ) : name(_name), macro(_macro)
39 {
40 tag = ssprintf("%%%s%%",_name);
41 last = -1;
42 }
43 };
44
45 vector<List> lists;
46 vector<string> ops;
47
48 void ImportList ( const char* listname, bool macro )
49 {
50 lists.push_back ( List ( listname, macro ) );
51 List& list = lists.back();
52 File f ( ssprintf("%s.txt",listname).c_str(), "r" );
53 string line;
54 while ( f.next_line ( line, true ) )
55 list.list.push_back ( line );
56 }
57
58 const char* ListRand ( List& list )
59 {
60 vector<string>& l = list.list;
61 if ( !l.size() )
62 {
63 static string nothing;
64 nothing = ssprintf ( "<list '%s' empty>", list.name.c_str() );
65 return nothing.c_str();
66 }
67 else if ( l.size() == 1 )
68 return l[0].c_str();
69 int sel = list.last;
70 while ( sel == list.last )
71 sel = rand()%l.size();
72 list.last = sel;
73 return l[sel].c_str();
74 }
75
76 const char* ListRand ( int i )
77 {
78 return ListRand ( lists[i] );
79 }
80
81 int GetListIndex ( const char* listname )
82 {
83 for ( int i = 0; i < lists.size(); i++ )
84 {
85 if ( !stricmp ( lists[i].name.c_str(), listname ) )
86 return i;
87 }
88 return -1;
89 }
90
91 List& GetList ( const char* listname )
92 {
93 return lists[GetListIndex(listname)];
94 }
95
96 const char* ListRand ( const char* list )
97 {
98 int i = GetListIndex ( list );
99 if ( i < 0 )
100 return NULL;
101 return ListRand(i);
102 }
103
104 string TaggedReply ( const char* listname )
105 {
106 string t = ListRand(listname);
107 string out;
108 const char* p = t.c_str();
109 while ( *p )
110 {
111 if ( *p == '%' )
112 {
113 bool found = false;
114 for ( int i = 0; i < lists.size() && !found; i++ )
115 {
116 if ( lists[i].macro && !strnicmp ( p, lists[i].tag.c_str(), lists[i].tag.size() ) )
117 {
118 out += ListRand(i);
119 p += lists[i].tag.size();
120 found = true;
121 }
122 }
123 if ( !found )
124 out += *p++;
125 }
126 const char* p2 = strchr ( p, '%' );
127 if ( !p2 )
128 p2 = p + strlen(p);
129 if ( p2 > p )
130 {
131 out += string ( p, p2-p );
132 p = p2;
133 }
134 }
135 return out;
136 }
137
138 string gobble ( string& s, const char* delim )
139 {
140 const char* p = s.c_str();
141 p += strspn ( p, delim );
142 const char* p2 = strpbrk ( p, delim );
143 if ( !p2 ) p2 = p + strlen(p);
144 string out ( p, p2-p );
145 p2 += strspn ( p2, delim );
146 s = string ( p2 );
147 return out;
148 }
149
150 bool isop ( const string& who )
151 {
152 for ( int i = 0; i < ops.size(); i++ )
153 {
154 if ( ops[i] == who )
155 return true;
156 }
157 return false;
158 }
159
160 // do custom stuff with the IRCClient from your subclass via the provided callbacks...
161 class MyIRCClient : public IRCClient
162 {
163 File flog;
164 public:
165 MyIRCClient()
166 {
167 flog.open ( "arch.log", "w+" );
168 }
169 // see IRCClient.h for documentation on these callbacks...
170 bool OnConnected()
171 {
172 return true;
173 }
174 bool OnJoin ( const string& user, const string& channel )
175 {
176 printf ( "user '%s' joined channel '%s'\n", user.c_str(), channel.c_str() );
177 return true;
178 }
179 bool OnPart ( const std::string& user, const std::string& channel )
180 {
181 for ( int i = 0; i < ops.size(); i++ )
182 {
183 if ( ops[i] == user )
184 {
185 printf ( "remove '%s' to ops list\n", user.c_str() );
186 ops.erase ( &ops[i] );
187 }
188 }
189 return true;
190 }
191 bool OnNick ( const std::string& oldNick, const std::string& newNick )
192 {
193 for ( int i = 0; i < ops.size(); i++ )
194 {
195 if ( ops[i] == oldNick )
196 {
197 printf ( "op '%s' changed nick to '%s'\n", oldNick.c_str(), newNick.c_str() );
198 ops[i] = newNick;
199 return true;
200 }
201 }
202 return true;
203 }
204 bool OnEndChannelUsers ( const string& channel )
205 {
206 return true;
207 }
208 bool OnPrivMsg ( const string& from, const string& text )
209 {
210 printf ( "<%s> %s\n", from.c_str(), text.c_str() );
211 flog.printf ( "<%s> %s\n", from.c_str(), text.c_str() );
212 if ( strnicmp ( text.c_str(), "!say ", 5 ) || !isop(from) )
213 return PrivMsg ( from, "hey, your tongue doesn't belong there!" );
214 string say = trim(&text[5]);
215 if ( !strnicmp ( say.c_str(), "/me ", 4 ) )
216 return Action ( CHANNEL, trim(&say[4]) );
217 else
218 return PrivMsg ( CHANNEL, trim(say) );
219 }
220 bool OnChannelMsg ( const string& channel, const string& from, const string& text )
221 {
222 printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
223 flog.printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
224 bool found_name = false;
225 string text2 ( text );
226 strlwr ( &text2[0] );
227
228 if ( !strnicmp ( text.c_str(), BOTNAME, strlen(BOTNAME) ) )
229 found_name = true;
230 else if ( !strnicmp ( text.c_str(), "arch ", 5 ) )
231 found_name = true;
232
233 if ( found_name )
234 {
235 string s ( text );
236 gobble ( s, " \t" ); // remove bot name
237 found_name = true;
238 if ( s[0] == '!' )
239 {
240 bool from_op = isop(from);
241 string cmd = gobble ( s, " \t" );
242 if ( !from_op )
243 {
244 if ( cmd == "!grovel" )
245 {
246 string out = ssprintf(TaggedReply("nogrovel").c_str(),from.c_str());
247 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
248 return Action ( channel, &out[4] );
249 else
250 return PrivMsg ( channel, out );
251 }
252 return PrivMsg ( channel, ssprintf("%s: I don't take commands from non-ops",from.c_str()) );
253 }
254 if ( cmd == "!add" )
255 {
256 string listname = gobble ( s, " \t" );
257 int i = GetListIndex ( listname.c_str() );
258 if ( i == -1 )
259 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
260 List& list = lists[i];
261 if ( s[0] == '\"' || s[0] == '\'' )
262 {
263 char delim = s[0];
264 const char* p = &s[1];
265 const char* p2 = strchr ( p, delim );
266 if ( !p2 )
267 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
268 s = string ( p, p2-p );
269 }
270 for ( i = 0; i < list.list.size(); i++ )
271 {
272 if ( list.list[i] == s )
273 return PrivMsg ( channel, ssprintf("%s: entry already exists in list '%s'",from.c_str(),listname.c_str()) );
274 }
275 if ( !stricmp ( listname.c_str(), "curse" ) )
276 strlwr ( &s[0] );
277 list.list.push_back ( s );
278 {
279 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
280 for ( i = 0; i < list.list.size(); i++ )
281 f.printf ( "%s\n", list.list[i].c_str() );
282 }
283 return PrivMsg ( channel, ssprintf("%s: entry added to list '%s'",from.c_str(),listname.c_str()) );
284 }
285 else if ( cmd == "!remove" )
286 {
287 string listname = gobble ( s, " \t" );
288 int i = GetListIndex ( listname.c_str() );
289 if ( i == -1 )
290 return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
291 List& list = lists[i];
292 if ( s[0] == '\"' || s[0] == '\'' )
293 {
294 char delim = s[0];
295 const char* p = &s[1];
296 const char* p2 = strchr ( p, delim );
297 if ( !p2 )
298 return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
299 s = string ( p, p2-p );
300 }
301 for ( i = 0; i < list.list.size(); i++ )
302 {
303 if ( list.list[i] == s )
304 {
305 list.list.erase ( &list.list[i] );
306 {
307 File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
308 for ( i = 0; i < list.list.size(); i++ )
309 f.printf ( "%s\n", list.list[i].c_str() );
310 }
311 return PrivMsg ( channel, ssprintf("%s: entry removed from list '%s'",from.c_str(),listname.c_str()) );
312 }
313 }
314 return PrivMsg ( channel, ssprintf("%s: entry doesn't exist in list '%s'",from.c_str(),listname.c_str()) );
315 }
316 else if ( cmd == "!grovel" )
317 {
318 string out = ssprintf(TaggedReply("grovel").c_str(),from.c_str());
319 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
320 return Action ( channel, &out[4] );
321 else
322 return PrivMsg ( channel, out );
323 }
324 else if ( cmd == "!kiss" )
325 {
326 if ( s.size() )
327 return Action ( channel, ssprintf("kisses %s",s.c_str()) );
328 else
329 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
330 }
331 else if ( cmd == "!hug" )
332 {
333 if ( s.size() )
334 return Action ( channel, ssprintf("hugs %s",s.c_str()) );
335 else
336 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
337 }
338 else if ( cmd == "!give" )
339 {
340 string who = gobble(s," \t");
341 if ( who.size() && s.size() )
342 return Action ( channel, ssprintf("gives %s a %s",who.c_str(),s.c_str()) );
343 else
344 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
345 }
346 else
347 {
348 return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
349 }
350 }
351 }
352
353 bool found_curse = false;
354 static vector<string>& curse = GetList("curse").list;
355 text2 = ssprintf(" %s ",text2.c_str());
356 for ( int i = 0; i < curse.size() && !found_curse; i++ )
357 {
358 if ( strstr ( text2.c_str(), curse[i].c_str() ) )
359 found_curse = true;
360 }
361 if ( found_curse )
362 {
363 static List& cursecop = GetList("cursecop");
364 return PrivMsg ( channel, ssprintf("%s: %s", from.c_str(), ListRand(cursecop)) );
365 }
366 else if ( found_name )
367 {
368 string out = ssprintf("%s: %s", from.c_str(), TaggedReply("tech").c_str());
369 flog.printf ( "TECH-REPLY: %s\n", out.c_str() );
370 if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
371 return Action ( channel, &out[4] );
372 else
373 return PrivMsg ( channel, out );
374 }
375 return true;
376 }
377 bool OnChannelMode ( const string& channel, const string& mode )
378 {
379 //printf ( "OnChannelMode(%s,%s)\n", channel.c_str(), mode.c_str() );
380 return true;
381 }
382 bool OnUserModeInChannel ( const string& src, const string& channel, const string& mode, const string& target )
383 {
384 printf ( "OnUserModeInChannel(%s,%s,%s,%s)\n", src.c_str(), channel.c_str(), mode.c_str(), target.c_str() );
385 const char* p = mode.c_str();
386 if ( !p )
387 return true;
388 while ( *p )
389 {
390 switch ( *p++ )
391 {
392 case '+':
393 while ( *p != 0 && *p != ' ' )
394 {
395 if ( *p == 'o' )
396 {
397 printf ( "adding '%s' to ops list\n", target.c_str() );
398 ops.push_back ( target );
399 }
400 break;
401 }
402 break;
403 case '-':
404 while ( *p != 0 && *p != ' ' )
405 {
406 if ( *p == 'o' )
407 {
408 for ( int i = 0; i < ops.size(); i++ )
409 {
410 if ( ops[i] == target )
411 {
412 printf ( "remove '%s' to ops list\n", target.c_str() );
413 ops.erase ( &ops[i] );
414 }
415 }
416 break;
417 }
418 }
419 }
420 }
421 return true;
422 }
423 bool OnMode ( const string& user, const string& mode )
424 {
425 //printf ( "OnMode(%s,%s)\n", user.c_str(), mode.c_str() );
426 return true;
427 }
428 bool OnChannelUsers ( const string& channel, const vector<string>& users )
429 {
430 //printf ( "[%s has %i users]: ", channel.c_str(), users.size() );
431 for ( int i = 0; i < users.size(); i++ )
432 {
433 if ( users[i][0] == '@' )
434 ops.push_back ( &users[i][1] );
435 /*if ( i )
436 printf ( ", " );
437 printf ( "%s", users[i].c_str() );*/
438 }
439 //printf ( "\n" );
440 return true;
441 }
442 };
443
444 int main ( int argc, char** argv )
445 {
446 srand ( time(NULL) );
447
448 ImportList ( "dev", true );
449 ImportList ( "func", true );
450 ImportList ( "dev", true );
451 ImportList ( "func", true );
452 ImportList ( "irql", true );
453 ImportList ( "module", true );
454 ImportList ( "period", true );
455 ImportList ( "status", true );
456 ImportList ( "stru", true );
457 ImportList ( "type", true );
458
459 ImportList ( "tech", false );
460 ImportList ( "curse", false );
461 ImportList ( "cursecop", false );
462 ImportList ( "grovel", false );
463 ImportList ( "nogrovel", false );
464
465 #ifdef _DEBUG
466 printf ( "initializing IRCClient debugging\n" );
467 IRCClient::SetDebug ( true );
468 #endif//_DEBUG
469 printf ( "calling suStartup()\n" );
470 suStartup();
471 printf ( "creating IRCClient object\n" );
472 MyIRCClient irc;
473 printf ( "connecting to freenode\n" );
474
475 //const char* server = "212.204.214.114";
476 const char* server = "irc.freenode.net";
477
478 if ( !irc.Connect ( server ) ) // irc.freenode.net
479 {
480 printf ( "couldn't connect to server\n" );
481 return -1;
482 }
483 printf ( "sending user command\n" );
484 if ( !irc.User ( BOTNAME, "", "irc.freenode.net", BOTNAME ) )
485 {
486 printf ( "USER command failed\n" );
487 return -1;
488 }
489 printf ( "sending nick\n" );
490 if ( !irc.Nick ( BOTNAME ) )
491 {
492 printf ( "NICK command failed\n" );
493 return -1;
494 }
495 printf ( "setting mode\n" );
496 if ( !irc.Mode ( "+i" ) )
497 {
498 printf ( "MODE command failed\n" );
499 return -1;
500 }
501 printf ( "joining channel\n" );
502 if ( !irc.Join ( CHANNEL ) )
503 {
504 printf ( "JOIN command failed\n" );
505 return -1;
506 }
507 printf ( "entering irc client processor\n" );
508 irc.Run ( false ); // do the processing in this thread...
509 return 0;
510 }