latest version of ArchBlackmann
[reactos.git] / irc / ArchBlackmann / ArchBlackmann.cpp
index 5d9898d..382e14a 100644 (file)
 
 #include "File.h"
 #include "ssprintf.h"
+#include "trim.h"
 
 #include "IRCClient.h"
 
 using std::string;
 using std::vector;
 
-const char* ArchBlackmann = "ArchBlackmann";
+#if defined(_DEBUG) && 0
+const char* BOTNAME = "RoyBot";
+const char* CHANNEL = "#RoyBotTest";
+#else
+const char* BOTNAME = "ArchBlackmann";
+const char* CHANNEL = "#ReactOS";
+#endif
 
-vector<string> tech, module, dev, stru, period;
+//vector<string> tech, module, dev, stru, period, status, type, func, irql, curse, cursecop;
 
-void ImportList ( vector<string>& list, const char* filename )
+class List
 {
-       File f ( filename, "r" );
+public:
+       string name;
+       bool macro;
+       std::vector<std::string> list;
+       string tag;
+       int last;
+       List() { last = -1; }
+       List ( const char* _name, bool _macro ) : name(_name), macro(_macro)
+       {
+               tag = ssprintf("%%%s%%",_name);
+               last = -1;
+       }
+};
+
+vector<List> lists;
+vector<string> ops;
+
+void ImportList ( const char* listname, bool macro )
+{
+       lists.push_back ( List ( listname, macro ) );
+       List& list = lists.back();
+       File f ( ssprintf("%s.txt",listname).c_str(), "r" );
        string line;
        while ( f.next_line ( line, true ) )
-               list.push_back ( line );
+               list.list.push_back ( line );
 }
 
-const char* ListRand ( const vector<string>& list )
+const char* ListRand ( List& list )
 {
-       return list[rand()%list.size()].c_str();
+       vector<string>& l = list.list;
+       if ( !l.size() )
+       {
+               static string nothing;
+               nothing = ssprintf ( "<list '%s' empty>", list.name.c_str() );
+               return nothing.c_str();
+       }
+       else if ( l.size() == 1 )
+               return l[0].c_str();
+       int sel = list.last;
+       while ( sel == list.last )
+               sel = rand()%l.size();
+       list.last = sel;
+       return l[sel].c_str();
 }
 
-string TechReply()
+const char* ListRand ( int i )
 {
-       string t = ListRand(tech);
+       return ListRand ( lists[i] );
+}
+
+int GetListIndex ( const char* listname )
+{
+       for ( int i = 0; i < lists.size(); i++ )
+       {
+               if ( !stricmp ( lists[i].name.c_str(), listname ) )
+                       return i;
+       }
+       return -1;
+}
+
+List& GetList ( const char* listname )
+{
+       return lists[GetListIndex(listname)];
+}
+
+const char* ListRand ( const char* list )
+{
+       int i = GetListIndex ( list );
+       if ( i < 0 )
+               return NULL;
+       return ListRand(i);
+}
+
+string TaggedReply ( const char* listname )
+{
+       string t = ListRand(listname);
        string out;
        const char* p = t.c_str();
        while ( *p )
        {
                if ( *p == '%' )
                {
-                       if ( !strnicmp ( p, "%dev%", 5 ) )
-                       {
-                               out += ListRand(dev);
-                               p += 5;
-                       }
-                       else if ( !strnicmp ( p, "%period%", 8 ) )
-                       {
-                               out += ListRand(period);
-                               p += 8;
-                       }
-                       else if ( !strnicmp ( p, "%module%", 8 ) )
-                       {
-                               out += ListRand(module);
-                               p += 8;
-                       }
-                       else if ( !strnicmp ( p, "%stru%", 6 ) )
+                       bool found = false;
+                       for ( int i = 0; i < lists.size() && !found; i++ )
                        {
-                               out += ListRand(stru);
-                               p += 6;
+                               if ( lists[i].macro && !strnicmp ( p, lists[i].tag.c_str(), lists[i].tag.size() ) )
+                               {
+                                       out += ListRand(i);
+                                       p += lists[i].tag.size();
+                                       found = true;
+                               }
                        }
-                       else
+                       if ( !found )
                                out += *p++;
                }
                const char* p2 = strchr ( p, '%' );
@@ -76,6 +135,28 @@ string TechReply()
        return out;
 }
 
+string gobble ( string& s, const char* delim )
+{
+       const char* p = s.c_str();
+       p += strspn ( p, delim );
+       const char* p2 = strpbrk ( p, delim );
+       if ( !p2 ) p2 = p + strlen(p);
+       string out ( p, p2-p );
+       p2 += strspn ( p2, delim );
+       s = string ( p2 );
+       return out;
+}
+
+bool isop ( const string& who )
+{
+       for ( int i = 0; i < ops.size(); i++ )
+       {
+               if ( ops[i] == who )
+                       return true;
+       }
+       return false;
+}
+
 // do custom stuff with the IRCClient from your subclass via the provided callbacks...
 class MyIRCClient : public IRCClient
 {
@@ -92,6 +173,32 @@ public:
        }
        bool OnJoin ( const string& user, const string& channel )
        {
+               printf ( "user '%s' joined channel '%s'\n", user.c_str(), channel.c_str() );
+               return true;
+       }
+       bool OnPart ( const std::string& user, const std::string& channel )
+       {
+               for ( int i = 0; i < ops.size(); i++ )
+               {
+                       if ( ops[i] == user )
+                       {
+                               printf ( "remove '%s' to ops list\n", user.c_str() );
+                               ops.erase ( &ops[i] );
+                       }
+               }
+               return true;
+       }
+       bool OnNick ( const std::string& oldNick, const std::string& newNick )
+       {
+               for ( int i = 0; i < ops.size(); i++ )
+               {
+                       if ( ops[i] == oldNick )
+                       {
+                               printf ( "op '%s' changed nick to '%s'\n", oldNick.c_str(), newNick.c_str() );
+                               ops[i] = newNick;
+                               return true;
+                       }
+               }
                return true;
        }
        bool OnEndChannelUsers ( const string& channel )
@@ -102,19 +209,168 @@ public:
        {
                printf ( "<%s> %s\n", from.c_str(), text.c_str() );
                flog.printf ( "<%s> %s\n", from.c_str(), text.c_str() );
-               return PrivMsg ( from, "hey, your tongue doesn't belong there!" );
+               if ( strnicmp ( text.c_str(), "!say ", 5 ) || !isop(from) )
+                       return PrivMsg ( from, "hey, your tongue doesn't belong there!" );
+               string say = trim(&text[5]);
+               if ( !strnicmp ( say.c_str(), "/me ", 4 ) )
+                       return Action ( CHANNEL, trim(&say[4]) );
+               else
+                       return PrivMsg ( CHANNEL, trim(say) );
        }
        bool OnChannelMsg ( const string& channel, const string& from, const string& text )
        {
                printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
                flog.printf ( "%s <%s> %s\n", channel.c_str(), from.c_str(), text.c_str() );
-               string text2(text);
+               bool found_name = false;
+               string text2 ( text );
                strlwr ( &text2[0] );
-               if ( !strnicmp ( text2.c_str(), ArchBlackmann, strlen(ArchBlackmann) ) )
+
+               if ( !strnicmp ( text.c_str(), BOTNAME, strlen(BOTNAME) ) )
+                       found_name = true;
+               else if ( !strnicmp ( text.c_str(), "arch ", 5 ) )
+                       found_name = true;
+
+               if ( found_name )
                {
-                       string reply = ssprintf("%s: %s", from.c_str(), TechReply().c_str());
-                       flog.printf ( "TECH-REPLY: %s\n", reply.c_str() );
-                       return PrivMsg ( channel, reply );
+                       string s ( text );
+                       gobble ( s, " \t" ); // remove bot name
+                       found_name = true;
+                       if ( s[0] == '!' )
+                       {
+                               bool from_op = isop(from);
+                               string cmd = gobble ( s, " \t" );
+                               if ( !from_op )
+                               {
+                                       if ( cmd == "!grovel" )
+                                       {
+                                               string out = ssprintf(TaggedReply("nogrovel").c_str(),from.c_str());
+                                               if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
+                                                       return Action ( channel, &out[4] );
+                                               else
+                                                       return PrivMsg ( channel, out );
+                                       }
+                                       return PrivMsg ( channel, ssprintf("%s: I don't take commands from non-ops",from.c_str()) );
+                               }
+                               if ( cmd == "!add" )
+                               {
+                                       string listname = gobble ( s, " \t" );
+                                       int i = GetListIndex ( listname.c_str() );
+                                       if ( i == -1 )
+                                               return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
+                                       List& list = lists[i];
+                                       if ( s[0] == '\"' || s[0] == '\'' )
+                                       {
+                                               char delim = s[0];
+                                               const char* p = &s[1];
+                                               const char* p2 = strchr ( p, delim );
+                                               if ( !p2 )
+                                                       return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
+                                               s = string ( p, p2-p );
+                                       }
+                                       for ( i = 0; i < list.list.size(); i++ )
+                                       {
+                                               if ( list.list[i] == s )
+                                                       return PrivMsg ( channel, ssprintf("%s: entry already exists in list '%s'",from.c_str(),listname.c_str()) );
+                                       }
+                                       if ( !stricmp ( listname.c_str(), "curse" ) )
+                                               strlwr ( &s[0] );
+                                       list.list.push_back ( s );
+                                       {
+                                               File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
+                                               for ( i = 0; i < list.list.size(); i++ )
+                                                       f.printf ( "%s\n", list.list[i].c_str() );
+                                       }
+                                       return PrivMsg ( channel, ssprintf("%s: entry added to list '%s'",from.c_str(),listname.c_str()) );
+                               }
+                               else if ( cmd == "!remove" )
+                               {
+                                       string listname = gobble ( s, " \t" );
+                                       int i = GetListIndex ( listname.c_str() );
+                                       if ( i == -1 )
+                                               return PrivMsg ( channel, ssprintf("%s: I don't have a list named '%s'",from.c_str(),listname.c_str()) );
+                                       List& list = lists[i];
+                                       if ( s[0] == '\"' || s[0] == '\'' )
+                                       {
+                                               char delim = s[0];
+                                               const char* p = &s[1];
+                                               const char* p2 = strchr ( p, delim );
+                                               if ( !p2 )
+                                                       return PrivMsg ( channel, ssprintf("%s: Couldn't add, unmatched quotes",from.c_str()) );
+                                               s = string ( p, p2-p );
+                                       }
+                                       for ( i = 0; i < list.list.size(); i++ )
+                                       {
+                                               if ( list.list[i] == s )
+                                               {
+                                                       list.list.erase ( &list.list[i] );
+                                                       {
+                                                               File f ( ssprintf("%s.txt",list.name.c_str()), "w" );
+                                                               for ( i = 0; i < list.list.size(); i++ )
+                                                                       f.printf ( "%s\n", list.list[i].c_str() );
+                                                       }
+                                                       return PrivMsg ( channel, ssprintf("%s: entry removed from list '%s'",from.c_str(),listname.c_str()) );
+                                               }
+                                       }
+                                       return PrivMsg ( channel, ssprintf("%s: entry doesn't exist in list '%s'",from.c_str(),listname.c_str()) );
+                               }
+                               else if ( cmd == "!grovel" )
+                               {
+                                       string out = ssprintf(TaggedReply("grovel").c_str(),from.c_str());
+                                       if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
+                                               return Action ( channel, &out[4] );
+                                       else
+                                               return PrivMsg ( channel, out );
+                               }
+                               else if ( cmd == "!kiss" )
+                               {
+                                       if ( s.size() )
+                                               return Action ( channel, ssprintf("kisses %s",s.c_str()) );
+                                       else
+                                               return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
+                               }
+                               else if ( cmd == "!hug" )
+                               {
+                                       if ( s.size() )
+                                               return Action ( channel, ssprintf("hugs %s",s.c_str()) );
+                                       else
+                                               return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
+                               }
+                               else if ( cmd == "!give" )
+                               {
+                                       string who = gobble(s," \t");
+                                       if ( who.size() && s.size() )
+                                               return Action ( channel, ssprintf("gives %s a %s",who.c_str(),s.c_str()) );
+                                       else
+                                               return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
+                               }
+                               else
+                               {
+                                       return PrivMsg ( channel, ssprintf("%s: huh?",from.c_str()) );
+                               }
+                       }
+               }
+
+               bool found_curse = false;
+               static vector<string>& curse = GetList("curse").list;
+               text2 = ssprintf(" %s ",text2.c_str());
+               for ( int i = 0; i < curse.size() && !found_curse; i++ )
+               {
+                       if ( strstr ( text2.c_str(), curse[i].c_str() ) )
+                               found_curse = true;
+               }
+               if ( found_curse )
+               {
+                       static List& cursecop = GetList("cursecop");
+                       return PrivMsg ( channel, ssprintf("%s: %s", from.c_str(), ListRand(cursecop)) );
+               }
+               else if ( found_name )
+               {
+                       string out = ssprintf("%s: %s", from.c_str(), TaggedReply("tech").c_str());
+                       flog.printf ( "TECH-REPLY: %s\n", out.c_str() );
+                       if ( !strnicmp ( out.c_str(), "/me ", 4 ) )
+                               return Action ( channel, &out[4] );
+                       else
+                               return PrivMsg ( channel, out );
                }
                return true;
        }
@@ -123,9 +379,45 @@ public:
                //printf ( "OnChannelMode(%s,%s)\n", channel.c_str(), mode.c_str() );
                return true;
        }
-       bool OnUserModeInChannel ( const string& src, const string& channel, const string& user, const string& mode )
+       bool OnUserModeInChannel ( const string& src, const string& channel, const string& mode, const string& target )
        {
-               //printf ( "OnUserModeInChannel(%s,%s%s,%s)\n", src.c_str(), channel.c_str(), user.c_str(), mode.c_str() );
+               printf ( "OnUserModeInChannel(%s,%s,%s,%s)\n", src.c_str(), channel.c_str(), mode.c_str(), target.c_str() );
+               const char* p = mode.c_str();
+               if ( !p )
+                       return true;
+               while ( *p )
+               {
+                       switch ( *p++ )
+                       {
+                       case '+':
+                               while ( *p != 0 && *p != ' ' )
+                               {
+                                       if ( *p == 'o' )
+                                       {
+                                               printf ( "adding '%s' to ops list\n", target.c_str() );
+                                               ops.push_back ( target );
+                                       }
+                                       break;
+                               }
+                               break;
+                       case '-':
+                               while ( *p != 0 && *p != ' ' )
+                               {
+                                       if ( *p == 'o' )
+                                       {
+                                               for ( int i = 0; i < ops.size(); i++ )
+                                               {
+                                                       if ( ops[i] == target )
+                                                       {
+                                                               printf ( "remove '%s' to ops list\n", target.c_str() );
+                                                               ops.erase ( &ops[i] );
+                                                       }
+                                               }
+                                               break;
+                                       }
+                               }
+                       }
+               }
                return true;
        }
        bool OnMode ( const string& user, const string& mode )
@@ -135,14 +427,16 @@ public:
        }
        bool OnChannelUsers ( const string& channel, const vector<string>& users )
        {
-               printf ( "[%s has %i users]: ", channel.c_str(), users.size() );
+               //printf ( "[%s has %i users]: ", channel.c_str(), users.size() );
                for ( int i = 0; i < users.size(); i++ )
                {
-                       if ( i )
+                       if ( users[i][0] == '@' )
+                               ops.push_back ( &users[i][1] );
+                       /*if ( i )
                                printf ( ", " );
-                       printf ( "%s", users[i].c_str() );
+                       printf ( "%s", users[i].c_str() );*/
                }
-               printf ( "\n" );
+               //printf ( "\n" );
                return true;
        }
 };
@@ -150,32 +444,50 @@ public:
 int main ( int argc, char** argv )
 {
        srand ( time(NULL) );
-       ImportList ( tech, "tech.txt" );
-       ImportList ( stru, "stru.txt" );
-       ImportList ( dev, "dev.txt" );
-       ImportList ( period, "period.txt" );
-       ImportList ( module, "module.txt" );
 
+       ImportList ( "dev", true );
+       ImportList ( "func", true );
+       ImportList ( "dev", true );
+       ImportList ( "func", true );
+       ImportList ( "irql", true );
+       ImportList ( "module", true );
+       ImportList ( "period", true );
+       ImportList ( "status", true );
+       ImportList ( "stru", true );
+       ImportList ( "type", true );
+
+       ImportList ( "tech", false );
+       ImportList ( "curse", false );
+       ImportList ( "cursecop", false );
+       ImportList ( "grovel", false );
+       ImportList ( "nogrovel", false );
+
+#ifdef _DEBUG
        printf ( "initializing IRCClient debugging\n" );
        IRCClient::SetDebug ( true );
+#endif//_DEBUG
        printf ( "calling suStartup()\n" );
        suStartup();
        printf ( "creating IRCClient object\n" );
        MyIRCClient irc;
        printf ( "connecting to freenode\n" );
-       if ( !irc.Connect ( "212.204.214.114" ) ) // irc.freenode.net
+
+       //const char* server = "212.204.214.114";
+       const char* server = "irc.freenode.net";
+
+       if ( !irc.Connect ( server ) ) // irc.freenode.net
        {
                printf ( "couldn't connect to server\n" );
                return -1;
        }
        printf ( "sending user command\n" );
-       if ( !irc.User ( "ArchBlackmann", "", "irc.freenode.net", "Arch Blackmann" ) )
+       if ( !irc.User ( BOTNAME, "", "irc.freenode.net", BOTNAME ) )
        {
                printf ( "USER command failed\n" );
                return -1;
        }
        printf ( "sending nick\n" );
-       if ( !irc.Nick ( "ArchBlackmann" ) )
+       if ( !irc.Nick ( BOTNAME ) )
        {
                printf ( "NICK command failed\n" );
                return -1;
@@ -186,8 +498,8 @@ int main ( int argc, char** argv )
                printf ( "MODE command failed\n" );
                return -1;
        }
-       printf ( "joining #ReactOS\n" );
-       if ( !irc.Join ( "#ReactOS" ) )
+       printf ( "joining channel\n" );
+       if ( !irc.Join ( CHANNEL ) )
        {
                printf ( "JOIN command failed\n" );
                return -1;