├── .gitignore ├── Background ├── Points.h └── User.h ├── CMakeLists.txt ├── CustomCommands ├── CustomCommandManager.cpp ├── CustomCommands.h ├── ccmd_adminonly.h ├── ccmd_isadmin.h └── ccmd_time.h ├── Lib ├── exception.h ├── lib.cpp ├── lib.h ├── list.h └── mSingleton.h ├── Network ├── httpRequest.cpp └── httpRequest.h ├── PhantomBot.cpp ├── PhantomBot.h ├── PhantomBot.sln ├── Platform ├── Linux │ ├── linuxSocket.cpp │ ├── linuxSocket.h │ ├── linuxTime.h │ ├── loadLinux.h │ └── platformLinux.h ├── Windows │ ├── loadWindows.h │ ├── load_winh.h │ ├── platformWindows.h │ ├── windowsSocket.cpp │ ├── windowsSocket.h │ ├── windowsTime.cpp │ └── windowsTime.h ├── platform.h ├── platformNetwork.h ├── platformTime.cpp ├── platformTime.h └── platformTypes.h ├── README.md ├── TwitchAPI ├── FollowersCheck.h └── TwitchAPIRequest.h ├── TwitchCommands ├── TwitchCommandProcess.h ├── TwitchPing.cpp ├── TwitchPrivMsg.cpp └── TwitchUserState.cpp ├── TwitchIRC ├── Admin.cpp ├── Admin.h ├── TwitchCommandLimit.cpp ├── TwitchCommandLimit.h ├── TwitchIRC.cpp └── TwitchIRC.h ├── adminusers.txt ├── biicode.cmake ├── botconfig.txt ├── chatCommandDefinitions.h └── include.h /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/PhantomBot/v14/.suo 2 | *.db 3 | PhantomBot.VC.VC.opendb 4 | *.vcxproj 5 | PhantomBot.vcxproj.filters 6 | *.exe 7 | *.pdb 8 | *.obj 9 | *.tlog 10 | *.log 11 | *.iobj 12 | PhantomBotLog.txt 13 | Release/PhantomBot.ipdb 14 | *.dev -------------------------------------------------------------------------------- /Background/Points.h: -------------------------------------------------------------------------------- 1 | /** 2 | Points.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ -------------------------------------------------------------------------------- /Background/User.h: -------------------------------------------------------------------------------- 1 | /** 2 | User.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef USER_H 8 | #define USER_H 9 | 10 | #include "../include.h" 11 | 12 | class User { 13 | public: 14 | User(string n); 15 | ~User(); 16 | 17 | private: 18 | string name; 19 | }; 20 | 21 | #endif //USER_H -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt 2 | # Used for building the example code on any CMake supported system. 3 | # This software is licensed under the MIT license. Please refer to 4 | # LICENSE.txt for more information. 5 | 6 | #IF(BIICODE) 7 | # INCLUDE("biicode.cmake") 8 | # RETURN() 9 | #ENDIF() 10 | 11 | CMAKE_MINIMUM_REQUIRED (VERSION 2.6) 12 | PROJECT (PhantomBot) 13 | 14 | # If we're on GCC, set the c++11 compilation flag. 15 | IF (CMAKE_COMPILER_IS_GNUCXX) 16 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -pthread -std=c++11") 17 | ENDIF (CMAKE_COMPILER_IS_GNUCXX) 18 | 19 | SET (EX_BUILDLOCATION "PhantomBot") 20 | 21 | INCLUDE_DIRECTORIES ("Platform/" "Lib/" "Network/" "TwitchAPI/" "Background/" "TwitchCommands/" "TwitchIRC/" "CustomCommands/") 22 | FILE(GLOB_RECURSE PLATFORM_SOURCES "Platform/*.cpp") 23 | FILE(GLOB_RECURSE LIB_SOURCES "Lib/*.cpp") 24 | FILE(GLOB_RECURSE NETWORK_SOURCES "Network/*.cpp") 25 | FILE(GLOB_RECURSE TAPI_SOURCES "TwitchAPI/*.cpp") 26 | FILE(GLOB_RECURSE BG_SOURCES "Background/*.cpp") 27 | FILE(GLOB_RECURSE TCOMMANDS_SOURCES "TwitchCommands/*.cpp") 28 | FILE(GLOB_RECURSE TIRC_SOURCES "TwitchIRC/*.cpp") 29 | FILE(GLOB_RECURSE CCMDS_SOURCES "CustomCommands/*.cpp") 30 | 31 | ADD_EXECUTABLE (${EX_BUILDLOCATION} "PhantomBot.cpp" ${PLATFORM_SOURCES} ${LIB_SOURCES} 32 | ${NETWORK_SOURCES} ${TAPI_SOURCES} ${BG_SOURCES} ${TCOMMANDS_SOURCES} ${TIRC_SOURCES} ${CCMDS_SOURCES}) 33 | 34 | -------------------------------------------------------------------------------- /CustomCommands/CustomCommandManager.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | CustomCommandManager.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "CustomCommands.h" 8 | 9 | CustomCommandManager::CustomCommandManager() { 10 | 11 | } 12 | 13 | CustomCommandManager::~CustomCommandManager() { 14 | 15 | } 16 | 17 | void CustomCommandManager::AddCommand(string trigger, CustomCommand *cmd) { 18 | CCMD custom(trigger, cmd); 19 | commandList.push_back(custom); 20 | } 21 | 22 | void CustomCommandManager::Process(string input) { 23 | //Split the message to find the trigger 24 | string username, message; 25 | Lib::stripMessage(input, username, message); 26 | for(SIZE_T i = 0; i < commandList.size(); i++) { 27 | if(commandList[i].trigger.compare(message.substr(0, commandList[i].trigger.length())) == 0) { 28 | commandList[i].instance->Fire(input); 29 | } 30 | } 31 | } 32 | 33 | CustomCommandManager &CustomCommandManager::fetchInstance() { 34 | if(managedSingleton::instance() == NULL) { 35 | managedSingleton::createInstance(); 36 | } 37 | return *(managedSingleton::instance()); 38 | } 39 | -------------------------------------------------------------------------------- /CustomCommands/CustomCommands.h: -------------------------------------------------------------------------------- 1 | /** 2 | CustomCommands.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _CUSTOMCOMMANDS_H 8 | #define _CUSTOMCOMMANDS_H 9 | 10 | #include "../include.h" 11 | #include "../Lib/lib.h" 12 | #include "../TwitchIRC/TwitchCommandLimit.h" 13 | 14 | /* 15 | CustomCommand Class 16 | Class instance inherited by others, used to define a custom command instance 17 | */ 18 | class CustomCommand { 19 | public: 20 | /* Public Class Methods */ 21 | //Constructor 22 | CustomCommand() { } 23 | //Destructor 24 | virtual ~CustomCommand() { } 25 | //Run the command 26 | virtual void Fire(string input) = 0; 27 | }; 28 | 29 | /* 30 | CustomCommandManager Class 31 | Class instance that manages the entire collection of custom commands 32 | */ 33 | class CustomCommandManager { 34 | //Forward Dec for CCMD 35 | struct CCMD; 36 | 37 | public: 38 | /* Public Class Methods */ 39 | //Constructor 40 | CustomCommandManager(); 41 | //Destructor 42 | ~CustomCommandManager(); 43 | //Add a command to the IRC Bot 44 | void AddCommand(string trigger, CustomCommand *cmd); 45 | //Process the input message 46 | void Process(string input); 47 | //Fetch the singleton instance 48 | static CustomCommandManager &fetchInstance(); 49 | 50 | private: 51 | /* Private Class Members */ 52 | //CCMD Structure: Double type-defs the command and trigger 53 | struct CCMD { 54 | CCMD(string t, CustomCommand *i) : trigger(t), instance(i) { } 55 | 56 | //The chat message that triggers the command 57 | string trigger; 58 | //The command itself 59 | CustomCommand *instance; 60 | }; 61 | //Vector instance of Commands 62 | vector commandList; 63 | }; 64 | 65 | #endif //_CUSTOMCOMMANDS_H 66 | -------------------------------------------------------------------------------- /CustomCommands/ccmd_adminonly.h: -------------------------------------------------------------------------------- 1 | /** 2 | adminonly.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _CCMD_ADMINONLY_H 8 | #define _CCMD_ADMINONLY_H 9 | 10 | #include "CustomCommands.h" 11 | 12 | class Command_AdminOnly : public CustomCommand { 13 | public: 14 | /* Public Class Methods */ 15 | //Constructor 16 | Command_AdminOnly() : CustomCommand() { } 17 | //Destructor 18 | virtual ~Command_AdminOnly() { } 19 | //Run the command 20 | virtual void Fire(string input) { 21 | string name, message; 22 | Lib::stripMessage(input, name, message); 23 | if(!Admin::fetchInstance().CheckAdminStatus(name)) { 24 | return; 25 | } 26 | //Get the input after the command 27 | SIZE_T postCmd = message.find("!adminonly") + 10; 28 | S32 v = atoi(message.substr(postCmd, message.length()).c_str()); 29 | TwitchCommandLimit::fetchInstance().setAdminMode(v <= 0 ? false : true); 30 | //Post-process 31 | string pFlg = TwitchCommandLimit::fetchInstance().AdminOnlyMode() ? "Enabled" : "Disabled"; 32 | string response = Lib::formatChatMessage("Admin-Only Command Mode " + pFlg + " by " + name + "."); 33 | TwitchCommandLimit::fetchInstance().AddCommand(response); 34 | } 35 | }; 36 | 37 | #endif //_CCMD_ADMINONLY_H 38 | -------------------------------------------------------------------------------- /CustomCommands/ccmd_isadmin.h: -------------------------------------------------------------------------------- 1 | /** 2 | isadmin.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _CCMD_ISADMIN_H 8 | #define _CCMD_ISADMIN_H 9 | 10 | #include "CustomCommands.h" 11 | 12 | class Command_IsAdmin : public CustomCommand { 13 | public: 14 | /* Public Class Methods */ 15 | //Constructor 16 | Command_IsAdmin() : CustomCommand() { } 17 | //Destructor 18 | virtual ~Command_IsAdmin() { } 19 | //Run the command 20 | virtual void Fire(string input) { 21 | string name, message; 22 | Lib::stripMessage(input, name, message); 23 | 24 | bool isAdmin = Admin::fetchInstance().CheckAdminStatus(name); 25 | string response = Lib::formatChatMessage(name + (isAdmin ? " is" : " is not") + " flagged as an administrator."); 26 | TwitchCommandLimit::fetchInstance().AddCommand(response); 27 | } 28 | }; 29 | 30 | #endif //_CCMD_ISADMIN_H 31 | -------------------------------------------------------------------------------- /CustomCommands/ccmd_time.h: -------------------------------------------------------------------------------- 1 | /** 2 | time.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _CCMD_TIME_H 8 | #define _CCMD_TIME_H 9 | 10 | #include "CustomCommands.h" 11 | 12 | class Command_Time : public CustomCommand { 13 | public: 14 | /* Public Class Methods */ 15 | //Constructor 16 | Command_Time() : CustomCommand() { } 17 | //Destructor 18 | virtual ~Command_Time() { } 19 | //Run the command 20 | virtual void Fire(string input) { 21 | string response = Lib::formatChatMessage("It is currently " + Lib::currentTime() + "."); 22 | TwitchCommandLimit::fetchInstance().AddCommand(response); 23 | } 24 | }; 25 | 26 | #endif //_CCMD_TIME_H 27 | -------------------------------------------------------------------------------- /Lib/exception.h: -------------------------------------------------------------------------------- 1 | /** 2 | exception.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _EXCEPTION_H_ 8 | #define _EXCEPTION_H_ 9 | 10 | class Exception : public std::exception { 11 | public: 12 | Exception(const char *text) : type(text) { 13 | 14 | } 15 | 16 | virtual ~Exception() throw() { } 17 | 18 | virtual const char *what() const throw() { 19 | return type; 20 | } 21 | 22 | private: 23 | const char *type; 24 | }; 25 | 26 | #endif //_EXCEPTION_H_ 27 | -------------------------------------------------------------------------------- /Lib/lib.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | lib.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "lib.h" 8 | 9 | namespace Lib { 10 | 11 | Socket *createSocketAndConnect(UFC32 addr, U32 port) { 12 | Socket *s = new Socket(); 13 | if(!s->create()) { 14 | cout << "Lib::createSocketAndConnect(): Creation Failed" << endl; 15 | return NULL; 16 | } 17 | if(!s->connect(addr, port)) { 18 | cout << "Lib::createSocketAndConnect(): Connection Failed" << endl; 19 | return NULL; 20 | } 21 | return s; 22 | } 23 | 24 | string currentTime() { 25 | string currentTime = Time::fetchTimestamp(); 26 | return currentTime; 27 | } 28 | 29 | const U64 fetchTime() { 30 | TimeVars tv; 31 | 32 | Time::utcTime(tv); 33 | 34 | unsigned long long millisecondsSinceEpoch = 35 | (unsigned long long)(tv.seconds) * 1000 + 36 | (unsigned long long)(tv.miliseconds) / 1000; 37 | 38 | return millisecondsSinceEpoch; 39 | } 40 | 41 | void writeToLog(string logLocation, string message) { 42 | fstream f(logLocation.c_str(), fstream::app | fstream::out); 43 | string output; 44 | output = "[" + currentTime() + "]: " + message + "\n"; 45 | f.write(output.c_str(), output.size()); 46 | f.close(); 47 | } 48 | 49 | string formatForPrint(string incoming) { 50 | string out = incoming; 51 | out.erase(remove(out.begin(), out.end(), '\r'), out.end()); 52 | out.erase(remove(out.begin(), out.end(), '\n'), out.end()); 53 | return out; 54 | } 55 | 56 | string formatChatMessage(const string message) { 57 | const string format = "PRIVMSG " + TwitchCommandLimit::fetchInstance().Channel() + " :" + message + "\r\n"; 58 | return format; 59 | } 60 | 61 | void stripMessage(string incoming, string &username, string &message) { 62 | string cName = TwitchCommandLimit::fetchInstance().Channel(); 63 | SIZE_T nameBegin = incoming.find("display-name=")+13; 64 | SIZE_T nameEnd = incoming.find(";", nameBegin); 65 | SIZE_T messageStart = incoming.find(cName + " :") + cName.size() + 2; 66 | //The correct format is :NAME!, test here 67 | username = incoming.substr(nameBegin, (nameEnd - nameBegin)); 68 | //Fetch the message content 69 | if(messageStart != string::npos) { 70 | for(SIZE_T i = messageStart; i < incoming.size(); i++) { 71 | message.push_back(incoming[i]); 72 | } 73 | } 74 | } 75 | 76 | bool validateExpr(const string incoming, const string validTokens) { 77 | return incoming.find_first_not_of(validTokens) == string::npos; 78 | } 79 | 80 | void tokenizeString(const string incoming, const vector tokens, queue &out) { 81 | UFC32 str = incoming.c_str(); 82 | do { 83 | UFC32 begin = str; 84 | for(SIZE_T i = 0; i < tokens.size(); i++) { 85 | while(*str != tokens[i] && *str) { 86 | str++; 87 | } 88 | } 89 | out.push(string(begin, str)); 90 | } while (0 != *str++); 91 | 92 | } 93 | 94 | string strTrim(const string &s) { 95 | string::const_iterator it = s.begin(); 96 | while (it != s.end() && isspace(*it)) { 97 | it++; 98 | } 99 | string::const_reverse_iterator rit = s.rbegin(); 100 | while (rit.base() != it && isspace(*rit)) { 101 | rit++; 102 | } 103 | return string(it, rit.base()); 104 | } 105 | 106 | }; 107 | -------------------------------------------------------------------------------- /Lib/lib.h: -------------------------------------------------------------------------------- 1 | /** 2 | lib.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _LIB_H 8 | #define _LIB_H 9 | 10 | #include "../include.h" 11 | #include "../TwitchIRC/TwitchCommandLimit.h" 12 | 13 | #define LIMIT_16 ((signed short)0x7FFF) 14 | 15 | /* 16 | Lib Class 17 | Collection of support and utility functions 18 | */ 19 | namespace Lib { 20 | //Create a socket and connect to specified instance 21 | Socket *createSocketAndConnect(UFC32 addr, U32 port); 22 | //Fetch the current timestring 23 | string currentTime(); 24 | //Fetch UTC time, unsigned integer 25 | const U64 fetchTime(); 26 | //Write a log line 27 | void writeToLog(string logLocation, string message); 28 | //Format an incoming command line to strip out /r/n for proper output 29 | string formatForPrint(string incoming); 30 | //Format a chat message 31 | string formatChatMessage(const string message); 32 | //Strip the contents of a message into username / message 33 | void stripMessage(string incoming, string &username, string &message); 34 | //Validate an expression 35 | bool validateExpr(const string incoming, const string validTokens); 36 | //Tokenize a string based on input parameters 37 | void tokenizeString(const string incoming, const vector tokens, queue &out); 38 | //Trim spaces from a string 39 | string strTrim(const string &s); 40 | }; 41 | 42 | 43 | #endif //_LIB_H 44 | -------------------------------------------------------------------------------- /Lib/list.h: -------------------------------------------------------------------------------- 1 | /** 2 | list.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _LIST_H 8 | #define _LIST_H 9 | 10 | template class ListNode { 11 | public: 12 | ListNode(const T& inData) { 13 | data = inData; 14 | next = NULL; 15 | prev = NULL; 16 | } 17 | 18 | ListNode() { 19 | next = NULL; 20 | prev = NULL; 21 | } 22 | 23 | ~ListNode() { 24 | if(next != NULL) { 25 | delete next; 26 | } 27 | } 28 | 29 | T &getData() { 30 | return data; 31 | } 32 | 33 | void setNext(ListNode *n) { 34 | next = n; 35 | } 36 | 37 | void setPrevious(ListNode *n) { 38 | prev = n; 39 | } 40 | 41 | ListNode *getNext() { 42 | return next; 43 | } 44 | 45 | ListNode *getPrevious() { 46 | return prev; 47 | } 48 | 49 | void removeFromList() { 50 | next->prev = prev; 51 | prev->next = next; 52 | } 53 | 54 | ListNode *insertBefore(const T& toInsert, ListNode *node) { 55 | ListNode *newNode = new ListNode(toInsert); 56 | if(node == NULL) { 57 | return newNode; 58 | } 59 | else { 60 | ListNode *prev = node->getPrevious(); 61 | node->setPrevious(newNode); 62 | if(prev != NULL) { 63 | prev->setNext(newNode); 64 | } 65 | } 66 | return newNode; 67 | } 68 | 69 | ListNode *insertAfter(ListNode *node) { 70 | ListNode *nextNode = node->getNext(); 71 | if(nextNode != NULL) { 72 | nextNode->setPrevious(this); 73 | } 74 | node->setNext(this); 75 | return this; 76 | } 77 | 78 | ListNode& operator=(const T &inData) { 79 | data = inData; 80 | return *this; 81 | } 82 | 83 | private: 84 | ListNode *next; 85 | ListNode *prev; 86 | T data; 87 | 88 | }; 89 | 90 | template class List { 91 | typedef ListNode node; 92 | 93 | public: 94 | List() : first(NULL), current(NULL), last(NULL), endOfList(false), count(0) { 95 | 96 | } 97 | 98 | ~List() { 99 | //A big problem with a lot of Linked List implementations is they leave 100 | // big chunks of memory lying around. Clean's the list prior to deletion. 101 | if(first != NULL && last != NULL) { 102 | node *tmp = last, tmp2; 103 | while(tmp != NULL) { 104 | tmp2 = tmp->getPrevious(); 105 | delete tmp; 106 | tmp = tmp2; 107 | } 108 | } 109 | } 110 | 111 | const unsigned int size() { 112 | return count; 113 | } 114 | 115 | node *getFirst() { 116 | return first; 117 | } 118 | 119 | node *getCurrent() { 120 | if(current == NULL) { 121 | current = first; 122 | } 123 | node *cN = current; 124 | current = current->getNext(); 125 | return cN; 126 | } 127 | 128 | node *getLast() { 129 | return last; 130 | } 131 | 132 | bool EOL() { 133 | if(current == NULL) { 134 | endOfList = true; 135 | current = first; 136 | return true; 137 | } 138 | if(current == first && endOfList) { 139 | endOfList = false; 140 | } 141 | return endOfList; 142 | } 143 | 144 | void pushFront(const T &inData) { 145 | node *newNode = new ListNode(inData); 146 | if(first != NULL) { 147 | first->setPrevious(newNode); 148 | newNode->setNext(first); 149 | } 150 | if(last == NULL) { 151 | last = first; 152 | } 153 | first = newNode; 154 | count++; 155 | } 156 | 157 | void pushBack(const T &inData) { 158 | node *newNode = new ListNode(inData); 159 | if(last != NULL) { 160 | last->setNext(newNode); 161 | newNode->setPrevious(last); 162 | last = newNode; 163 | count++; 164 | } 165 | if(first == NULL && last == NULL) { 166 | first = newNode; 167 | last == newNode; 168 | count = 1; 169 | } 170 | } 171 | 172 | node *findInList(const T &inData) { 173 | if(first != NULL) { 174 | node *c = first; 175 | while(c != NULL) { 176 | if(c->getData() == inData) { 177 | return c; 178 | } 179 | c = c->getNext(); 180 | } 181 | } 182 | return NULL; 183 | } 184 | 185 | void resetPos() { 186 | endOfList = false; 187 | current = first; 188 | } 189 | 190 | void recount() { 191 | unsigned int total = 0; 192 | node *c = first; 193 | while(c != NULL) { 194 | c = c->getNext(); 195 | total++; 196 | } 197 | count = total; 198 | } 199 | 200 | void removeFromList(node *n) { 201 | n->getNext()->setPrevious(n->getPrevious()); 202 | n->getPrevious()->setNext(n->getNext()); 203 | n->setNext(NULL); 204 | n->setPrevious(NULL); 205 | delete n; 206 | count--; 207 | } 208 | 209 | private: 210 | bool endOfList; 211 | unsigned int count; 212 | 213 | node *first; 214 | node *current; 215 | node *last; 216 | }; 217 | 218 | #endif //_LIST_H 219 | -------------------------------------------------------------------------------- /Lib/mSingleton.h: -------------------------------------------------------------------------------- 1 | /** 2 | mSingleton.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _MANAGEDSINGLETON_H 8 | #define _MANAGEDSINGLETON_H 9 | 10 | template class managedSingleton { 11 | public: 12 | static void createInstance() { 13 | if(mSingleton != NULL) { 14 | //ToDo: Throw an assert error here 15 | return; 16 | } 17 | mSingleton = new T(); 18 | } 19 | 20 | static void destroyInstance() { 21 | if(mSingleton == NULL) { 22 | return; 23 | } 24 | SendToHell(mSingleton); 25 | } 26 | 27 | static const char * getInstanceName() { 28 | return "UNNAMED_SINGLETON_INSTACE"; 29 | } 30 | 31 | static T* instance() { 32 | return mSingleton ? mSingleton : NULL; 33 | } 34 | 35 | private: 36 | static T* mSingleton; 37 | 38 | }; 39 | template T* managedSingleton::mSingleton = NULL; 40 | 41 | #endif //_MANAGEDSINGLETON_H 42 | -------------------------------------------------------------------------------- /Network/httpRequest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | httpRequest.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | /* 8 | #include "httpRequest.h" 9 | 10 | HTTPRequest::HTTPRequest() { 11 | sent = false; 12 | revd = false; 13 | } 14 | 15 | HTTPRequest::~HTTPRequest() { 16 | 17 | } 18 | 19 | bool HTTPRequest::makeSocket() { 20 | _msock = socket(AF_INET, SOCK_STREAM, 0); 21 | //Failed? 22 | if(!IsValid()) { 23 | return false; 24 | } 25 | S32 on = 1; 26 | if(::setsockopt(_msock, SOL_SOCKET, SO_REUSEADDR, (UFC32)&on, sizeof(on)) == -1) { 27 | return false; 28 | } 29 | return true; 30 | } 31 | 32 | bool HTTPRequest::sendGETRequest(string url, string remainingAddr) { 33 | sent = false; 34 | revd = false; 35 | 36 | if(_msock != -1) { 37 | //Close existing connection, let's not get interrupted here! 38 | ::close(_msock); 39 | } 40 | if(_msock == -1) { 41 | do { 42 | makeSocket(); 43 | }while(_msock == -1); 44 | } 45 | //Pre-connect event 46 | struct hostent *host = ::gethostbyname(url); 47 | if(host == null || host->h_addr == NULL) { 48 | cout << "HTTPRequest::sendGETRequest(): Invalid host." << endl; 49 | return false; 50 | } 51 | bzero(&_maddr, sizeof(_maddr)); 52 | _maddr.sin_family = AF_INET; 53 | _maddr.sin_port = htons(_HTTP_PORT); 54 | memcpy(&_maddr.sin_addr, host->h_addr, host->h_length); 55 | //Connect... 56 | if(::connect(sock, (struct sockaddr *)&_maddr, sizeof(_maddr)) < 0) { 57 | ::close(_msock); 58 | cout << "HTTPRequest::sendGETRequest(): Cannot connect." << endl; 59 | return false; 60 | } 61 | //Prepare GET request for server. 62 | request = ""; 63 | request += "GET " + remainingAddr + " HTTP/1.1\r\n"; 64 | request += "Host: " + url + "\r\n"; 65 | request += "Connection: Close\r\n"; 66 | request += "\r\n\r\n"; 67 | //Send the request 68 | if(::send(_msock, request.c_str(), request.length(), 0) != (S32)request.length()) { 69 | cout << "HTTPRequest::sendGETRequest(): Something went wrong in the send buffer." << endl; 70 | return false; 71 | } 72 | sent = true; 73 | //Recieve 74 | while(::read(_msock, &sendBuffer, _SEND_BUFFER_SIZE) > 0) { 75 | response += sendBuffer; 76 | } 77 | revd = true; 78 | return true; 79 | } 80 | */ 81 | -------------------------------------------------------------------------------- /Network/httpRequest.h: -------------------------------------------------------------------------------- 1 | /** 2 | httpRequest.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | /* 8 | #ifndef _HTTPREQUEST_H 9 | #define _HTTPREQUEST_H 10 | 11 | #define _HTTP_PORT 80 12 | #define _SEND_BUFFER_SIZE 1024 13 | 14 | class HTTPRequest { 15 | public: 16 | HTTPRequest(); 17 | ~HTTPRequest(); 18 | 19 | bool makeSocket(); 20 | 21 | bool sendGETRequest(string url, string remainingAddr); 22 | //bool sendPOSTRequest(string address, string payload); 23 | 24 | bool sendComplete() { return sent; } 25 | bool responseComplete() { return revd; } 26 | 27 | const string payload() { return request; } 28 | const string received() { return response; } 29 | 30 | private: 31 | ACHAR sendBuffer[_SEND_BUFFER_SIZE]; 32 | string request; 33 | string response; 34 | 35 | S32 _msock; 36 | sockaddr_in _maddr; 37 | 38 | bool sent; 39 | bool revd; 40 | }; 41 | 42 | #endif //_HTTPREQUEST_H 43 | 44 | */ -------------------------------------------------------------------------------- /PhantomBot.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | PhantomBot.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "PhantomBot.h" 8 | 9 | 10 | /* 11 | PhantomBot Class 12 | */ 13 | PhantomBot::PhantomBot() : irc(NULL) { 14 | initialized = false; 15 | wantsQuit = false; 16 | } 17 | 18 | PhantomBot::~PhantomBot() { 19 | 20 | } 21 | 22 | PhantomBot &PhantomBot::fetchInstance() { 23 | if (managedSingleton::instance() == NULL) { 24 | managedSingleton::createInstance(); 25 | } 26 | return *(managedSingleton::instance()); 27 | } 28 | 29 | 30 | void PhantomBot::init(vector &conf) { 31 | if (initialized) { 32 | return; 33 | } 34 | //Initialize IRC Module and Input Module 35 | irc = new TwitchIRC(conf[0].c_str(), conf[0].c_str(), conf[4].c_str(), conf[1].c_str(), (U32)atoi(conf[2].c_str()), conf[3].c_str()); 36 | initialized = true; 37 | run(); 38 | } 39 | 40 | void PhantomBot::run() { 41 | while (irc->SocketActive()) { 42 | irc->Update(); 43 | } 44 | } 45 | 46 | /* 47 | 48 | MAIN METHOD 49 | ENTRY POINT 50 | 51 | */ 52 | 53 | int main(void) { 54 | srand((U32)time(NULL)); 55 | //read the config 56 | ACHAR line[512]; 57 | vector config; 58 | fstream f("botconfig.txt", ios::in); 59 | while(!f.eof()) { 60 | f.getline(line, 512); 61 | config.push_back(line); 62 | memset(&line[0], 0, sizeof(line)); 63 | } 64 | //Set stuff up... 65 | if(config.size() >= 5) { 66 | PhantomBot::fetchInstance().init(config); 67 | } 68 | else { 69 | cout << "There was an error in your 'botconfig.txt' file, contact Phantom139 for support" << endl; 70 | } 71 | cout << "Closing program..." << endl; 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /PhantomBot.h: -------------------------------------------------------------------------------- 1 | /** 2 | PhantomBot.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "include.h" 8 | #include "TwitchIRC/TwitchIRC.h" 9 | 10 | #ifndef PHANTOM_BOT_H 11 | #define PHANTOM_BOT_H 12 | 13 | class PhantomBot { 14 | public: 15 | PhantomBot(); 16 | ~PhantomBot(); 17 | 18 | static PhantomBot &fetchInstance(); 19 | void init(vector &config); 20 | 21 | private: 22 | void run(); 23 | 24 | bool initialized; 25 | bool wantsQuit; 26 | 27 | TwitchIRC *irc; 28 | }; 29 | 30 | #endif //PHANTOM_BOT_H -------------------------------------------------------------------------------- /PhantomBot.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PhantomBot", "PhantomBot.vcxproj", "{B43A5652-4046-4916-9BA9-704B10786B5C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Debug|x64.ActiveCfg = Debug|x64 17 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Debug|x64.Build.0 = Debug|x64 18 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Debug|x86.Build.0 = Debug|Win32 20 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Release|x64.ActiveCfg = Release|x64 21 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Release|x64.Build.0 = Release|x64 22 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Release|x86.ActiveCfg = Release|Win32 23 | {B43A5652-4046-4916-9BA9-704B10786B5C}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Platform/Linux/linuxSocket.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | linuxSocket.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "../../include.h" 8 | #ifdef PHANTOMBOT_LINUX 9 | 10 | Socket::Socket() : sObj(-1) { 11 | inBuff = new ACHAR[_MAXRECV]; 12 | } 13 | 14 | Socket::~Socket() { 15 | 16 | } 17 | 18 | bool Socket::create() { 19 | sObj = socket(AF_INET, SOCK_STREAM, 0); 20 | //Failed? 21 | if (!isValidSocket()) { 22 | return false; 23 | } 24 | //Init the Socket 25 | S32 on = 1; 26 | if (setsockopt(sObj, SOL_SOCKET, SO_REUSEADDR, (UFC32)&on, sizeof(on)) == -1) { 27 | //Init failed 28 | return false; 29 | } 30 | //Done! 31 | return true; 32 | } 33 | 34 | bool Socket::bind(const U32 port) { 35 | if (!isValidSocket()) { 36 | return false; 37 | } 38 | _mAddr.sin_family = AF_INET; 39 | _mAddr.sin_addr.s_addr = INADDR_ANY; 40 | _mAddr.sin_port = htons(port); 41 | //Bind the port 42 | S32 bRet = ::bind(sObj, (struct sockaddr *)&_mAddr, sizeof(_mAddr)); 43 | if (bRet == -1) { 44 | return false; 45 | } 46 | return true; 47 | } 48 | 49 | bool Socket::listen() const { 50 | if (!isValidSocket()) { 51 | return false; 52 | } 53 | S32 lRet = ::listen(sObj, _MAXCONNECTIONS); 54 | if (lRet == -1) { 55 | return false; 56 | } 57 | return true; 58 | } 59 | 60 | bool Socket::accept(Socket &s) { 61 | S32 aLen = sizeof(_mAddr); 62 | s.sObj = ::accept(sObj, (sockaddr *)&_mAddr, (socklen_t *)&aLen); 63 | if (s.sObj <= 0) { 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | SocketCode Socket::connect(UFC32 host, const U32 port) { 70 | if (!isValidSocket()) { 71 | return InvalidSocket; 72 | } 73 | struct hostent *he; 74 | if ((he = gethostbyname(host)) == NULL) { 75 | cout << "Socket::connect(): Failed to convert " << host << " to an IP" << endl; 76 | } 77 | _mAddr.sin_family = AF_INET; 78 | _mAddr.sin_port = htons(port); 79 | memcpy(&_mAddr.sin_addr, he->h_addr_list[0], he->h_length); 80 | S32 status = ::connect(sObj, (sockaddr *)&_mAddr, sizeof(_mAddr)); 81 | if (status == 0) { 82 | onConnected(); 83 | return Connected; 84 | } 85 | cout << "Socket::connect(): Failed to connect: " << status << ", errno " << errno << endl; 86 | onConnectFailed(); 87 | return ConnectionFailed; 88 | } 89 | 90 | bool Socket::close() { 91 | if (!isValidSocket()) { 92 | return false; 93 | } 94 | onSelfDisconnect(); 95 | S32 retCode = ::close(sObj); 96 | sObj = -1; 97 | return retCode == 0; 98 | } 99 | 100 | bool Socket::shutdown() { 101 | if (!isValidSocket()) { 102 | return false; 103 | } 104 | S32 retCode = ::shutdown(sObj, SHUT_RDWR); 105 | if (retCode != 0) { 106 | cout << "Socket::shutdown(): Failed to shut down socket: " << errno << endl; 107 | ::close(sObj); 108 | sObj = -1; 109 | return false; 110 | } 111 | onSelfDisconnect(); 112 | return true; 113 | } 114 | 115 | bool Socket::send(UFC32 message) const { 116 | if (!isValidSocket()) { 117 | return false; 118 | } 119 | S32 sRet = ::send(sObj, message, strlen(message), MSG_NOSIGNAL); 120 | if (sRet == -1) { 121 | return false; 122 | } 123 | return true; 124 | } 125 | 126 | SocketCode Socket::receive(ACHAR *buffer, S32 bufferSize, S32 *bytesRead) const { 127 | memset(inBuff, NULL, sizeof(inBuff)); 128 | *bytesRead = ::recv(sObj, (ACHAR *)inBuff, bufferSize, 0); 129 | strcpy(buffer, inBuff); 130 | // 131 | switch (*bytesRead) { 132 | case -1: 133 | if(errno != 11) { 134 | cout << "Socket::Recieve() Status: -1: " << errno << "\n"; 135 | return RecieveError; 136 | } 137 | else { 138 | return Timeout; 139 | } 140 | 141 | case 0: 142 | cout << "Socket::Recieve(): Server issued disconnect command.\n"; 143 | //onServerDisconect(); //TO-Do: Fix const Socket error... 144 | return Disconnected; 145 | } 146 | return NoError; 147 | } 148 | 149 | void Socket::setNonBlocking(const bool status) { 150 | S32 options; 151 | options = fcntl(sObj, F_GETFL); 152 | if (options < 0) { 153 | return; 154 | } 155 | if (status) { 156 | options = (options | O_NONBLOCK); 157 | } 158 | else { 159 | options = (options & ~O_NONBLOCK); 160 | } 161 | fcntl(sObj, F_SETFL, options); 162 | } 163 | 164 | bool Socket::isValidSocket() const { 165 | return sObj != -1; 166 | } 167 | 168 | void Socket::onConnected() { 169 | 170 | } 171 | 172 | void Socket::onConnectFailed() { 173 | 174 | } 175 | 176 | void Socket::onSelfDisconnect() { 177 | 178 | } 179 | 180 | void Socket::onServerDisconect() { 181 | 182 | } 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /Platform/Linux/linuxSocket.h: -------------------------------------------------------------------------------- 1 | /** 2 | linuxSocket.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_LINUX) 8 | 9 | #ifndef PLATFORM_LINUX_SOCKET_H 10 | #define PLATFORM_LINUX_SOCKET_H 11 | 12 | class Socket : public GeneralSocket { 13 | public: 14 | Socket(); 15 | virtual ~Socket(); 16 | 17 | virtual bool create(); 18 | virtual bool bind(const U32 port); 19 | virtual bool listen() const; 20 | virtual bool accept(Socket &s); 21 | virtual SocketCode connect(UFC32 host, const U32 port); 22 | virtual bool close(); 23 | virtual bool shutdown(); 24 | virtual bool send(UFC32 message) const; 25 | virtual SocketCode receive(ACHAR *buffer, S32 bufferSize, S32 *bytesRead) const; 26 | virtual void setNonBlocking(const bool status = true); 27 | virtual bool isValidSocket() const; 28 | 29 | //Callbacks 30 | virtual void onConnected(); 31 | virtual void onConnectFailed(); 32 | virtual void onSelfDisconnect(); 33 | virtual void onServerDisconect(); 34 | 35 | private: 36 | S32 sObj; 37 | sockaddr_in _mAddr; 38 | }; 39 | 40 | #endif //PLATFORM_LINUX_SOCKET_H 41 | 42 | #endif //PHANTOMBOT_LINUX -------------------------------------------------------------------------------- /Platform/Linux/linuxTime.h: -------------------------------------------------------------------------------- 1 | /** 2 | linuxTime.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_LINUX) 8 | 9 | #ifndef LINUX_TIME_H 10 | #define LINUX_TIME_H 11 | 12 | class Time : public GTime { 13 | public: 14 | 15 | static time_t makeGMTime(TimeVars &c) { 16 | struct tm tm; 17 | memset(&tm, 0, sizeof(tm)); 18 | 19 | tm.tm_year = c.years - 1900; 20 | tm.tm_mon = c.months - 1; 21 | tm.tm_mday = c.dayNum; 22 | tm.tm_hour = c.hours; 23 | tm.tm_min = c.minutes; 24 | tm.tm_sec = c.seconds; 25 | tm.tm_isdst = -1; 26 | 27 | return mktime(&tm); 28 | } 29 | 30 | }; 31 | 32 | #endif //LINUX_TIME_H 33 | 34 | #endif //PHANTOMBOT_LINUX 35 | -------------------------------------------------------------------------------- /Platform/Linux/loadLinux.h: -------------------------------------------------------------------------------- 1 | /** 2 | loadLinux.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | -------------------------------------------------------------------------------- /Platform/Linux/platformLinux.h: -------------------------------------------------------------------------------- 1 | /** 2 | platformLinux.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_LINUX) 8 | 9 | #ifndef PLATFORM_LINUX_H 10 | #define PLATFORM_LINUX_H 11 | 12 | struct PlatformTypes : public GPlatformTypes { 13 | #if defined(_LINUX64) || defined(_LP64) 14 | #define USING_64_BIT 15 | #else 16 | #undef USING_64_BIT 17 | #endif 18 | 19 | typedef unsigned int DWORD; 20 | typedef __SIZE_TYPE__ SIZE_T; 21 | }; 22 | 23 | #define USE_SYS_TIME 1 24 | 25 | #endif //PLATFORM_LINUX_H 26 | 27 | #endif //PHANTOMBOT_LINUX -------------------------------------------------------------------------------- /Platform/Windows/loadWindows.h: -------------------------------------------------------------------------------- 1 | /** 2 | loadWindows.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "load_winh.h" 12 | 13 | #pragma comment (lib, "Ws2_32.lib") 14 | #pragma comment (lib, "Mswsock.lib") 15 | #pragma comment (lib, "AdvApi32.lib") -------------------------------------------------------------------------------- /Platform/Windows/load_winh.h: -------------------------------------------------------------------------------- 1 | /** 2 | load_winh.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | /** 8 | * References: http://msdn.microsoft.com/en-us/library/windows/desktop/aa383745(v=vs.85).aspx 9 | * http://stackoverflow.com/questions/1394910/how-to-tame-the-windows-headers-useful-defines 10 | **/ 11 | 12 | #pragma once 13 | 14 | #define WIN32_LEAN_AND_MEAN 15 | 16 | #define NOGDICAPMASKS 17 | #define NOVIRTUALKEYCODES 18 | #define NOWINMESSAGES 19 | #define NOWINSTYLES 20 | #define NOSYSMETRICS 21 | #define NOMENUS 22 | #define NOICONS 23 | #define NOKEYSTATES 24 | #define NOSYSCOMMANDS 25 | #define NORASTEROPS 26 | #define NOSHOWWINDOW 27 | #define OEMRESOURCE 28 | #define NOATOM 29 | #define NOCLIPBOARD 30 | #define NOCOLOR 31 | #define NOCTLMGR 32 | #define NODRAWTEXT 33 | #define NOGDI 34 | #define NOKERNEL 35 | #define NOUSER 36 | #define NONLS 37 | #define NOMB 38 | #define NOMEMMGR 39 | #define NOMETAFILE 40 | #define NOMINMAX 41 | #define NOMSG 42 | #define NOOPENFILE 43 | #define NOSCROLL 44 | #define NOSERVICE 45 | #define NOSOUND 46 | #define NOTEXTMETRIC 47 | #define NOWH 48 | #define NOWINOFFSETS 49 | #define NOCOMM 50 | #define NOKANJI 51 | #define NOHELP 52 | #define NOPROFILER 53 | #define NODEFERWINDOWPOS 54 | #define NOMCX 55 | #define NOCRYPT 56 | #define NOTAPE 57 | #define NOIMAGE 58 | #define NOPROXYSTUB 59 | #define NORPC 60 | 61 | #include -------------------------------------------------------------------------------- /Platform/Windows/platformWindows.h: -------------------------------------------------------------------------------- 1 | /** 2 | platformWindows.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_WINDOWS) 8 | 9 | #ifndef PLATFORM_WINDOWS_H 10 | #define PLATFORM_WINDOWS_H 11 | 12 | struct PlatformTypes : public GPlatformTypes { 13 | #ifdef _WIN64 14 | typedef unsigned __int64 SIZE_T; 15 | typedef __int64 SIZE_T_S; 16 | #define USING_64_BIT 17 | #else 18 | typedef unsigned long SIZE_T; 19 | typedef long SIZE_T_S; 20 | #undef USING_64_BIT 21 | #endif 22 | }; 23 | 24 | #define USE_SYS_TIME 0 25 | 26 | #endif //PLATFORM_WINDOWS_H 27 | 28 | #endif //PHANTOMBOT_WINDOWS -------------------------------------------------------------------------------- /Platform/Windows/windowsSocket.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | windowsSocket.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "../../include.h" 8 | #ifdef PHANTOMBOT_WINDOWS 9 | 10 | Socket::Socket() : sObj(INVALID_SOCKET) { 11 | inBuff = new ACHAR[_MAXRECV]; 12 | } 13 | 14 | Socket::~Socket() { 15 | 16 | } 17 | 18 | bool Socket::create() { 19 | WSAData wsa; 20 | int wsaResult; 21 | 22 | wsaResult = WSAStartup(MAKEWORD(2, 2), &wsa); 23 | if (wsaResult != 0) { 24 | cout << "Socket::create(): Failed to initialize WSA object (WSA Error: " << wsaResult << ")" << endl; 25 | return false; 26 | } 27 | sObj = socket(AF_INET, SOCK_STREAM, 0); 28 | if (sObj == INVALID_SOCKET) { 29 | WSACleanup(); 30 | cout << "Socket::create(): Failed to create socket, INVALID_SOCKET code thrown. (" << WSAGetLastError() << ")" << endl; 31 | return false; 32 | } 33 | //Try to init. 34 | S32 on = 1; 35 | if(setsockopt(sObj, SOL_SOCKET, SO_REUSEADDR, (UFC32)&on, sizeof(on)) == -1) { 36 | WSACleanup(); 37 | cout << "Socket::create(): Failed to initialize socket." << endl; 38 | return false; 39 | } 40 | //Good! 41 | return true; 42 | } 43 | 44 | bool Socket::bind(const U32 port) { 45 | if (!isValidSocket()) { 46 | return false; 47 | } 48 | _mAddr.sin_family = AF_INET; 49 | _mAddr.sin_addr.s_addr = INADDR_ANY; 50 | _mAddr.sin_port = htons(port); 51 | //Bind the port 52 | S32 bRet = ::bind(sObj, (struct sockaddr *)&_mAddr, sizeof(_mAddr)); 53 | if (bRet == -1) { 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | bool Socket::listen() const { 60 | if (!isValidSocket()) { 61 | return false; 62 | } 63 | S32 lRet = ::listen(sObj, _MAXCONNECTIONS); 64 | if (lRet == -1) { 65 | return false; 66 | } 67 | return true; 68 | } 69 | 70 | bool Socket::accept(Socket &s) { 71 | S32 aLen = sizeof(_mAddr); 72 | s.sObj = ::accept(sObj, (sockaddr *)&_mAddr, (socklen_t *)&aLen); 73 | if (s.sObj <= 0) { 74 | return false; 75 | } 76 | return true; 77 | } 78 | 79 | SocketCode Socket::connect(UFC32 host, const U32 port) { 80 | if (!isValidSocket()) { 81 | return InvalidSocket; 82 | } 83 | struct hostent *he; 84 | if ((he = gethostbyname(host)) == NULL) { 85 | cout << "Socket::connect(): Failed to convert " << host << " to an IP" << endl; 86 | } 87 | _mAddr.sin_family = AF_INET; 88 | _mAddr.sin_port = htons(port); 89 | memcpy(&_mAddr.sin_addr, he->h_addr_list[0], he->h_length); 90 | S32 status = ::connect(sObj, (sockaddr *)&_mAddr, sizeof(_mAddr)); 91 | if (status == 0) { 92 | onConnected(); 93 | return Connected; 94 | } 95 | cout << "Socket::connect(): Failed to connect: " << status << ", errno " << errno << endl; 96 | onConnectFailed(); 97 | return ConnectionFailed; 98 | } 99 | 100 | bool Socket::close() { 101 | if (!isValidSocket()) { 102 | return false; 103 | } 104 | onSelfDisconnect(); 105 | S32 retCode = ::closesocket(sObj); 106 | sObj = INVALID_SOCKET; 107 | WSACleanup(); 108 | return retCode == 0; 109 | } 110 | 111 | bool Socket::shutdown() { 112 | if (!isValidSocket()) { 113 | return false; 114 | } 115 | S32 retCode = ::shutdown(sObj, SD_SEND); 116 | if (retCode == SOCKET_ERROR) { 117 | cout << "Socket::shutdown(): Failed to shut down socket: " << WSAGetLastError() << endl; 118 | ::closesocket(sObj); 119 | sObj = INVALID_SOCKET; 120 | WSACleanup(); 121 | return false; 122 | } 123 | onSelfDisconnect(); 124 | return true; 125 | } 126 | 127 | bool Socket::send(UFC32 message) const { 128 | if (!isValidSocket()) { 129 | return false; 130 | } 131 | S32 sRet = ::send(sObj, message, strlen(message), 0); 132 | if (sRet == -1) { 133 | cout << "Socket::send(): Failed to send message: " << WSAGetLastError() << endl; 134 | return false; 135 | } 136 | return true; 137 | } 138 | 139 | SocketCode Socket::receive(ACHAR *buffer, S32 bufferSize, S32 *bytesRead) const { 140 | memset(inBuff, NULL, sizeof(inBuff)); 141 | *bytesRead = ::recv(sObj, (ACHAR *)inBuff, bufferSize, 0); 142 | strcpy(buffer, inBuff); 143 | // 144 | switch (*bytesRead) { 145 | case -1: 146 | if(WSAGetLastError() == WSAETIMEDOUT || WSAGetLastError() == WSAEWOULDBLOCK) { 147 | return Timeout; 148 | } 149 | else { 150 | cout << "Socket::Recieve() Status: -1: " << WSAGetLastError() << "\n"; 151 | return RecieveError; 152 | } 153 | 154 | case 0: 155 | cout << "Socket::Recieve(): Server issued disconnect command.\n"; 156 | //onServerDisconect(); //TO-Do: Fix const Socket error... 157 | return Disconnected; 158 | } 159 | return NoError; 160 | } 161 | 162 | void Socket::setNonBlocking(const bool status) { 163 | UL block = status; 164 | S32 opt = ioctlsocket(sObj, FIONBIO, &block); 165 | if (opt) { 166 | cout << "Socket::setNonBlocking(): Failed with error " << WSAGetLastError() << endl; 167 | } 168 | } 169 | 170 | bool Socket::isValidSocket() const { 171 | return sObj != INVALID_SOCKET; 172 | } 173 | 174 | void Socket::onConnected() { 175 | 176 | } 177 | 178 | void Socket::onConnectFailed() { 179 | 180 | } 181 | 182 | void Socket::onSelfDisconnect() { 183 | 184 | } 185 | 186 | void Socket::onServerDisconect() { 187 | 188 | } 189 | 190 | #endif 191 | -------------------------------------------------------------------------------- /Platform/Windows/windowsSocket.h: -------------------------------------------------------------------------------- 1 | /** 2 | windowsSocket.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_WINDOWS) 8 | 9 | #ifndef PLATFORM_WINDOWS_SOCKET_H 10 | #define PLATFORM_WINDOWS_SOCKET_H 11 | 12 | class Socket : public GeneralSocket { 13 | public: 14 | Socket(); 15 | virtual ~Socket(); 16 | 17 | virtual bool create(); 18 | virtual bool bind(const U32 port); 19 | virtual bool listen() const; 20 | virtual bool accept(Socket &s); 21 | virtual SocketCode connect(UFC32 host, const U32 port); 22 | virtual bool close(); 23 | virtual bool shutdown(); 24 | virtual bool send(UFC32 message) const; 25 | virtual SocketCode receive(ACHAR *buffer, S32 bufferSize, S32 *bytesRead) const; 26 | virtual void setNonBlocking(const bool status = true); 27 | virtual bool isValidSocket() const; 28 | 29 | //Callbacks 30 | virtual void onConnected(); 31 | virtual void onConnectFailed(); 32 | virtual void onSelfDisconnect(); 33 | virtual void onServerDisconect(); 34 | 35 | private: 36 | SOCKET sObj; 37 | sockaddr_in _mAddr; 38 | }; 39 | 40 | #endif //PLATFORM_WINDOWS_SOCKET_H 41 | 42 | #endif //PHANTOMBOT_WINDOWS -------------------------------------------------------------------------------- /Platform/Windows/windowsTime.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | windowsTime.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "../../include.h" 8 | #ifdef PHANTOMBOT_WINDOWS 9 | 10 | void Time::sysTime(TimeVars &c) { 11 | SYSTEMTIME sTime; 12 | GetLocalTime(&sTime); 13 | 14 | c.years = (S32)sTime.wYear; 15 | c.months = (S32)sTime.wMonth; 16 | c.dayOfWeek = (S32)sTime.wDayOfWeek; 17 | c.dayNum = (S32)sTime.wDay; 18 | c.hours = (S32)sTime.wHour; 19 | c.minutes = (S32)sTime.wMinute; 20 | c.seconds = (S32)sTime.wSecond; 21 | c.miliseconds = (S32)sTime.wMilliseconds; 22 | } 23 | 24 | void Time::utcTime(TimeVars &c) { 25 | SYSTEMTIME sTime; 26 | GetSystemTime(&sTime); 27 | 28 | c.years = (S32)sTime.wYear; 29 | c.months = (S32)sTime.wMonth; 30 | c.dayOfWeek = (S32)sTime.wDayOfWeek; 31 | c.dayNum = (S32)sTime.wDay; 32 | c.hours = (S32)sTime.wHour; 33 | c.minutes = (S32)sTime.wMinute; 34 | c.seconds = (S32)sTime.wSecond; 35 | c.miliseconds = (S32)sTime.wMilliseconds; 36 | } 37 | 38 | time_t Time::makeGMTime(TimeVars &c) { 39 | struct tm tm; 40 | memset(&tm, 0, sizeof(tm)); 41 | 42 | tm.tm_year = c.years - 1900; 43 | tm.tm_mon = c.months - 1; 44 | tm.tm_mday = c.dayNum; 45 | tm.tm_hour = c.hours; 46 | tm.tm_min = c.minutes; 47 | tm.tm_sec = c.seconds; 48 | tm.tm_isdst = -1; 49 | 50 | return mktime(&tm); 51 | } 52 | 53 | #endif -------------------------------------------------------------------------------- /Platform/Windows/windowsTime.h: -------------------------------------------------------------------------------- 1 | /** 2 | windowsTime.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #if defined(PHANTOMBOT_WINDOWS) 8 | 9 | #ifndef WINDOWS_TIME_H 10 | #define WINDOWS_TIME_H 11 | 12 | class Time : public GTime { 13 | public: 14 | static void sysTime(TimeVars &c); 15 | static void utcTime(TimeVars &c); 16 | static time_t makeGMTime(TimeVars &c); 17 | }; 18 | 19 | #endif //WINDOWS_TIME_H 20 | 21 | #endif //PHANTOMBOT_WINDOWS -------------------------------------------------------------------------------- /Platform/platform.h: -------------------------------------------------------------------------------- 1 | /** 2 | platform.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | /* 8 | The cross-platform nature of this bot is very simplistic as 9 | only two sections of the bot require cross-platform functioning, 10 | this being the network connection (Sockets) and some of the 11 | simple threading capabilities. 12 | 13 | With the advent of C++11 to the project, the STL handles most 14 | of the threading requirements for Linux, leaving only the sockets 15 | 16 | This small library uses my work from my custom 2D game engine project, 17 | Galactic 2D (https://github.com/PhantomGamesDevelopment/Galactic-2D/blob/master/Source/EngineCore/PlatformCore/platform.h) 18 | to create a simple platform wrapper instance for the project. 19 | */ 20 | 21 | #include "platformTypes.h" 22 | 23 | #ifndef PLATFORM_H 24 | #define PLATFORM_H 25 | 26 | /* 27 | This Platform Script handles the loading of the 28 | background functioning which enables some basic 29 | cross-platform functionality. 30 | */ 31 | 32 | //Step 1: Detect Platform 33 | /* Simple means to detect the platform */ 34 | #if defined(_WIN64) || defined(_WIN32) 35 | #define PHANTOMBOT_WINDOWS 1 36 | #include "Windows/platformWindows.h" 37 | #elif defined(__linux) || defined(__unix) 38 | #define PHANTOMBOT_LINUX 1 39 | #include "Linux/platformLinux.h" 40 | #else 41 | #error "Undefined or unsupported platform instance, please see platform.h to add in capabilities for your platform." 42 | #endif 43 | 44 | //Step 2: Define wrapper pointer classes and typedefs such that the rest of the project may use 45 | typedef PlatformTypes::s_int_8 S8; 46 | typedef PlatformTypes::s_int_16 S16; 47 | typedef PlatformTypes::s_int_32 S32; 48 | typedef PlatformTypes::s_int_64 S64; 49 | typedef PlatformTypes::u_int_8 U8; 50 | typedef PlatformTypes::u_int_16 U16; 51 | typedef PlatformTypes::u_int_32 U32; 52 | typedef PlatformTypes::u_int_64 U64; 53 | typedef PlatformTypes::A_CHAR ACHAR; 54 | typedef PlatformTypes::W_CHAR WCHAR; 55 | typedef PlatformTypes::T_CHAR TWCHAR; 56 | typedef PlatformTypes::char_8 UTF8; 57 | typedef PlatformTypes::char_16 UTF16; 58 | typedef PlatformTypes::char_32 UTF32; 59 | typedef PlatformTypes::SIZE_T SIZE_T; 60 | typedef PlatformTypes::SIZE_T_S SIZE_T_S; 61 | typedef PlatformTypes::s_long SL; 62 | typedef PlatformTypes::u_long UL; 63 | //Additional Types 64 | typedef const ACHAR *UFC32; 65 | 66 | //Step 3: Load Generic Platform Classes (Pure Virtuals) 67 | #include "platformTime.h" 68 | #include "platformNetwork.h" 69 | 70 | //Step 4: Load Platform Specific Info and override generic classes with platform specific wrappers 71 | #if defined(PHANTOMBOT_WINDOWS) 72 | #include "Windows/loadWindows.h" 73 | #include "Windows/windowsTime.h" 74 | #include "Windows/windowsSocket.h" 75 | #elif defined(PHANTOMBOT_LINUX) 76 | #include "Linux/loadLinux.h" 77 | #include "Linux/linuxTime.h" 78 | #include "Linux/linuxSocket.h" 79 | #else 80 | #error "How did you get here?" 81 | #endif 82 | 83 | #endif //PLATFORM_H -------------------------------------------------------------------------------- /Platform/platformNetwork.h: -------------------------------------------------------------------------------- 1 | /** 2 | platformNetwork.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef PLATFORM_NETWORK_H 8 | #define PLATFORM_NETWORK_H 9 | 10 | #ifndef _MAXHOSTNAME 11 | #define _MAXHOSTNAME 200 12 | #endif 13 | 14 | #ifndef _MAXCONNECTIONS 15 | #define _MAXCONNECTIONS 5 16 | #endif 17 | 18 | #ifndef _MAXRECV 19 | #define _MAXRECV 1024 20 | #endif 21 | 22 | class GeneralSocket { 23 | public: 24 | //Enumeration 25 | enum SocketReturnCode { 26 | Disconnected = 0, 27 | Connected, 28 | ConnectionFailed, 29 | NoError, 30 | RecieveError, 31 | Timeout, 32 | InvalidSocket, 33 | Unknown 34 | }; 35 | 36 | GeneralSocket() { } 37 | virtual ~GeneralSocket() { } 38 | 39 | //General Methods 40 | virtual bool create() = 0; 41 | virtual bool bind(const U32 port) = 0; 42 | virtual bool listen() const = 0; 43 | virtual bool accept(GeneralSocket &s) { return false; } 44 | virtual SocketReturnCode connect(UFC32 host, const U32 port) = 0; 45 | virtual bool close() = 0; 46 | virtual bool shutdown() = 0; 47 | virtual bool send(UFC32 message) const = 0; 48 | virtual SocketReturnCode receive(ACHAR *buffer, S32 bufferSize, S32 *bytesRead) const = 0; 49 | virtual void setNonBlocking(const bool status = true) = 0; 50 | virtual bool isValidSocket() const = 0; 51 | 52 | //Callbacks 53 | virtual void onConnected() { } 54 | virtual void onConnectFailed() { } 55 | virtual void onSelfDisconnect() { } 56 | virtual void onServerDisconect() { } 57 | 58 | protected: 59 | ACHAR *inBuff; 60 | }; 61 | 62 | //Easy Access Typedef 63 | typedef GeneralSocket::SocketReturnCode SocketCode; 64 | 65 | #endif //PLATFORM_NETWORK_H 66 | -------------------------------------------------------------------------------- /Platform/platformTime.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | platformTime.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "../include.h" 8 | 9 | UFC32 GTime::fetchTimestamp() { 10 | static ACHAR timestamp[1024]; 11 | *timestamp = NULL; 12 | fetchDateString(timestamp); 13 | strcat((ACHAR *)timestamp, " "); 14 | fetchSysTime(timestamp + strlen((UFC32)timestamp)); 15 | return timestamp; 16 | } 17 | 18 | void GTime::fetchSysTime(ACHAR *dst) { 19 | TimeVars t; 20 | Time::sysTime(t); 21 | ACHAR buffer[64]; 22 | std::sprintf(buffer, "%02d:%02d:02d", t.hours, t.minutes, t.seconds); 23 | strcpy(dst, buffer); 24 | } 25 | 26 | void GTime::fetchDateString(ACHAR *dst) { 27 | TimeVars t; 28 | Time::sysTime(t); 29 | ACHAR buffer[64]; 30 | std::sprintf(buffer, "%02d/%02d/%02d", t.months, t.dayNum, (t.years % 100)); 31 | strcpy(dst, buffer); 32 | } 33 | 34 | #if USE_SYS_TIME == 1 35 | 36 | void GTime::sysTime(TimeVars &c) { 37 | timeval tv; 38 | gettimeofday(&tv, NULL); 39 | tm local; 40 | localtime_r(&tv.tv_sec, &local); 41 | 42 | c.years = local.tm_year + 1900; 43 | c.months = local.tm_mon + 1; 44 | c.dayOfWeek = local.tm_wday; 45 | c.dayNum = local.tm_mday; 46 | c.hours = local.tm_hour; 47 | c.minutes = local.tm_min; 48 | c.seconds = local.tm_sec; 49 | c.miliseconds = tv.tv_usec / 1000; 50 | } 51 | 52 | void GTime::utcTime(TimeVars &c) { 53 | timeval tv; 54 | gettimeofday(&tv, NULL); 55 | tm local; 56 | gmtime_r(&tv.tv_sec, &local); 57 | 58 | c.years = local.tm_year + 1900; 59 | c.months = local.tm_mon + 1; 60 | c.dayOfWeek = local.tm_wday; 61 | c.dayNum = local.tm_mday; 62 | c.hours = local.tm_hour; 63 | c.minutes = local.tm_min; 64 | c.seconds = local.tm_sec; 65 | c.miliseconds = tv.tv_usec / 1000; 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /Platform/platformTime.h: -------------------------------------------------------------------------------- 1 | /** 2 | platformTime.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef PLATFORM_TIME_H 8 | #define PLATFORM_TIME_H 9 | 10 | #if USE_SYS_TIME == 1 11 | #include 12 | #endif 13 | 14 | struct TimeVars { 15 | 16 | S32 years; 17 | S32 months; 18 | S32 dayOfWeek; 19 | S32 dayNum; 20 | S32 hours; 21 | S32 minutes; 22 | S32 seconds; 23 | S32 miliseconds; 24 | 25 | TimeVars() : years(0), months(0), dayOfWeek(0), dayNum(0), hours(0), minutes(0), seconds(0), miliseconds(0) { } 26 | 27 | TimeVars(S32 yrs, S32 mo, S32 dow, S32 day, S32 hr, S32 min, S32 sec, S32 ms) : 28 | years(yrs), months(mo), dayOfWeek(dow), dayNum(day), hours(hr), minutes(min), seconds(sec), miliseconds(ms) { } 29 | 30 | //Compare two time values and return a result similar to strcmp() where smaller == -1, equal == 0, and larger == 1 31 | S32 compare(const TimeVars &c) { 32 | S32 cYears = (years > c.years) ? 1000000 : (years == c.years ? 0 : -1000000); 33 | S32 cMonths = (months > c.months) ? 100000 : (months == c.months ? 0 : -100000); 34 | S32 cDayNumber = (dayNum > c.dayNum) ? 10000 : (dayNum == c.dayNum ? 0 : -10000); 35 | S32 cHours = (hours > c.hours) ? 1000 : (hours == c.hours ? 0 : -1000); 36 | S32 cMinutes = (minutes > c.minutes) ? 100 : (minutes == c.minutes ? 0 : -100); 37 | S32 cSeconds = (seconds > c.seconds) ? 10 : (seconds == c.seconds ? 0 : -10); 38 | S32 cMiliseconds = (miliseconds > c.miliseconds) ? 1 : (miliseconds == c.miliseconds ? 0 : -1); 39 | //Add everything up! 40 | S32 result = cYears + cMonths + cDayNumber + cHours + cMinutes + cSeconds + cMiliseconds; 41 | if (result > 0) { 42 | return 1; 43 | } 44 | else if (result < 0) { 45 | return -1; 46 | } 47 | return 0; 48 | } 49 | 50 | bool operator==(const TimeVars &t) { 51 | return !!(compare(t) == 0); 52 | } 53 | 54 | bool operator!=(const TimeVars &t) { 55 | return !!(compare(t) != 0); 56 | } 57 | 58 | bool operator>(const TimeVars &t) { 59 | return !!(compare(t) == 1); 60 | } 61 | 62 | bool operator>=(const TimeVars &t) { 63 | return compare(t) >= 0; 64 | } 65 | 66 | bool operator<(const TimeVars &t) { 67 | return compare(t) == -1 ? true : false; 68 | } 69 | 70 | bool operator<=(const TimeVars &t) { 71 | return compare(t) <= 0; 72 | } 73 | 74 | }; 75 | 76 | class GTime { 77 | public: 78 | static UFC32 fetchTimestamp(); 79 | static void fetchSysTime(ACHAR *dst); 80 | static void fetchDateString(ACHAR *dst); 81 | 82 | static time_t makeGMTime(TimeVars &c); 83 | 84 | /* If we have access to , define the time overlay */ 85 | #if USE_SYS_TIME == 1 86 | static void sysTime(TimeVars &c); 87 | static void utcTime(TimeVars &c); 88 | #endif 89 | }; 90 | 91 | 92 | #endif //PLATFORM_TIME_H -------------------------------------------------------------------------------- /Platform/platformTypes.h: -------------------------------------------------------------------------------- 1 | /** 2 | platformTypes.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef PLATFORM_TYPES_H 8 | #define PLATFORM_TYPES_H 9 | 10 | /* 11 | Windows and Linux handle size_t differently, so we need to make 12 | sure we handle that ourselves here. This simple template definition 13 | will ensure that the types are the same on both platforms 14 | */ 15 | template struct sizettype { 16 | //The empty definition, force a compile error. 17 | }; 18 | template struct sizettype { 19 | typedef bittype_32 sizet_ptr; 20 | }; 21 | template struct sizettype { 22 | typedef bittype_64 sizet_ptr; 23 | }; 24 | 25 | /* 26 | Note: This implementation follows the same route as does my 27 | Galactic-2D engine project. Define a list of "generic" types, 28 | and then a list of platform-dependent types before overriding 29 | all types with the standard list that the remainder of the 30 | project will use. 31 | */ 32 | struct GPlatformTypes { 33 | //Signed Types 34 | typedef signed char s_int_8; 35 | typedef signed short int s_int_16; 36 | typedef signed int s_int_32; 37 | typedef signed long long s_int_64; 38 | 39 | //Unsigned Types 40 | typedef unsigned char u_int_8; 41 | typedef unsigned short int u_int_16; 42 | typedef unsigned int u_int_32; 43 | typedef unsigned long long u_int_64; 44 | 45 | //Characters 46 | typedef char A_CHAR; 47 | typedef wchar_t W_CHAR; 48 | typedef W_CHAR T_CHAR; 49 | typedef u_int_8 char_8; 50 | typedef u_int_16 char_16; 51 | typedef u_int_32 char_32; 52 | 53 | //size_t 54 | typedef sizettype::sizet_ptr U_SIZET_PTR; 55 | typedef sizettype::sizet_ptr S_SIZET_PTR; 56 | typedef U_SIZET_PTR SIZE_T; 57 | typedef S_SIZET_PTR SIZE_T_S; 58 | 59 | //others 60 | typedef signed long s_long; 61 | typedef unsigned long u_long; 62 | }; 63 | 64 | #endif //PLATFORM_TYPES_H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Phantom Bot Twitch IRC Project 2 | # CURRENT VERSION: Alpha 5 (Development Build) 3 | ## By Robert C. Fritzen (Phantom139) 4 | ## Supported Platforms: Windows 10, Linux (Ubuntu 16.04 Tested) 5 | 6 | ### Prerequisites ### 7 | No Libraries Required!!! This project uses all native C++11 code. 8 | 9 | * To Compile on Linux: You'll just need the 'cmake' package to build it! 10 | * To Compile on Windows 10: You'll need either cmake or MSVS 2015. 11 | 12 | ### Setup Instructions (Linux) ### 13 | Drop the files into a directory and open a terminal. cd to the directory and run the command 'cmake . && make' to compile, then run the code with ./PhantomBot 14 | 15 | ### Setup Instructions (Windows) ### 16 | Drop the files into a directory. Open Visual Studio and add all of the files to a project to compile. Run the compiled executable. 17 | 18 | ### Configuration Files ### 19 | There are two bot config files you can use. 20 | 21 | 1. botconfig.txt contains information you'll need to obtain to run the bot You'll need to insert your account name associated with the bot, and the channel name of the channel you'd like the bot to be active in. Finally, you'll need to obtain your oauth code from Twitch. To obtain this, log into your bot account and naviagte to this link: http://twitchapps.com/tmi/ Copy the contents of the page and then paste it into the oauth line. 22 | 2. adminusers.txt is a line delimited file of usernames who will have "admin status" on bot commands. These users will have elevated permissions which allow access to built in administrative functions such as !debugmode, !reset, !disconnect, and other commands you may choose to add to the bot. (You can just as easily add multiple levels of admin to the bot by customizing the Admin.h/.cpp files to do so. 23 | 24 | ### Features ### 25 | This is a very basic Twitch IRC bot with a very flexible and easy to navigate code-base. At it's core, this bot has the following features: 26 | 27 | * Connection via native Sockets 28 | * Message Parsing System to Inquire and Parse Twitch IRC Commands 29 | * Command Limit Instance to prevent the bot from exceeding the limits imposed by the Twitch IRC 30 | * Logging system for both command and chat logging 31 | * Admin User Interface to have quick access to defining users with administrative capabilities for in-stream commands 32 | * Custom Command System to allow quick and easy deployment of custom chat commands 33 | 34 | ### Custom Chat Commands ### 35 | As mentioned in the prior section, this bot has a very easy to use custom chat command interface. To create a custom chat command, follow the below instructions. 36 | 37 | First and foremost, create a new C++ Header file, it would help you to follow the ccmd_x.h naming convention as not to differentiate from the other files in that directory. The bot uses a abstract class instance named CustomCommand, from which you will need to inherit. The class has a pure virtual function void Fire(string input) which must be overriden. At the most basic, here is a sample instance to use: 38 | ```c++ 39 | /** 40 | ccmd_mycommand.h 41 | PhantomBot Project 42 | By: Robert F. (Phantom139) 43 | **/ 44 | 45 | #ifndef _CCMD_MYCOMMAND_H 46 | #define _CCMD_MYCOMMAND_H 47 | 48 | #include "CustomCommands.h" 49 | 50 | class Command_MyCommand : public CustomCommand { 51 | public: 52 | /* Public Class Methods */ 53 | //Constructor 54 | Command_MyCommand() : CustomCommand() { } 55 | //Destructor 56 | virtual ~Command_MyCommand() { } 57 | //Run the command 58 | virtual void Fire(string input) { 59 | string response = Lib::formatChatMessage("Hello World!!!"); 60 | TwitchCommandLimit::fetchInstance().AddCommand(response); 61 | } 62 | }; 63 | 64 | #endif //_CCMD_MYCOMMAND_H 65 | ``` 66 | 67 | Once you have created a custom command, you'll need to add it to the command parser, which is located in the chatCommandDefinitions.h file in the base directory. Inside this file, you'll see a group of header include statements which point to the commands included with this bot, you'll need to add your header file, like so: 68 | ```c++ 69 | //Headers to all of your custom commands (Insert the path(s) to your custom command files here) 70 | #include "CustomCommands/ccmd_time.h" 71 | #include "CustomCommands/ccmd_isadmin.h" 72 | #include "CustomCommands/ccmd_mycommand.h" //<--- ADD YOUR COMMAND HERE 73 | ``` 74 | 75 | Finally in the class instance below, you'll need to tell the bot to trigger your custom command upon a message. We'll use !hello for our example here, to do so, scroll down and add your command to the bot like so: 76 | ```c++ 77 | //Initalize the instance 78 | void init() { 79 | if(initialized == true) { 80 | return; 81 | } 82 | initialized = true; 83 | CustomCommandManager::fetchInstance().AddCommand("!time", new Command_Time()); 84 | CustomCommandManager::fetchInstance().AddCommand("!isadmin", new Command_IsAdmin()); 85 | CustomCommandManager::fetchInstance().AddCommand("!hello", new Command_MyCommand()); 86 | } 87 | ``` 88 | 89 | And that's about it! From this, you should be able to make pretty much anything your coding capabilities allow you to do. Best of luck! 90 | 91 | ### Bug Reporting ### 92 | Please report and bugs or problems on the Issues page here. Please use the flags appropriately. Duplicate or non-problem bugs will be removed. 93 | -------------------------------------------------------------------------------- /TwitchAPI/FollowersCheck.h: -------------------------------------------------------------------------------- 1 | /** 2 | FollowersCheck.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ -------------------------------------------------------------------------------- /TwitchAPI/TwitchAPIRequest.h: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchAPIRequest.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ -------------------------------------------------------------------------------- /TwitchCommands/TwitchCommandProcess.h: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchCommandProcess.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _TWITCHCOMMANDPROCESS_H 8 | #define _TWITCHCOMMANDPROCESS_H 9 | 10 | #include "../include.h" 11 | #include "../Lib/lib.h" 12 | #include "../TwitchIRC/TwitchCommandLimit.h" 13 | #include "../CustomCommands/CustomCommands.h" 14 | 15 | /* 16 | TwitchCommand Class 17 | Virtual class instance for processing commands sent from the TwitchIRC server 18 | */ 19 | class TwitchCommand { 20 | public: 21 | /* Public Class Methods */ 22 | //Constructor 23 | TwitchCommand(string n) : name(n) { } 24 | //Destructor 25 | virtual ~TwitchCommand() { } 26 | 27 | //Process the command 28 | virtual bool Process(const string incoming) = 0; 29 | //Return the name of this command instance 30 | virtual const string Name() { return name; }; 31 | 32 | protected: 33 | string name; 34 | }; 35 | 36 | /* 37 | TwitchPing Class 38 | Handles the PING message sent by Twitch 39 | */ 40 | class TwitchPing : public TwitchCommand { 41 | public: 42 | /* Public Class Methods */ 43 | //Constructor 44 | TwitchPing() : TwitchCommand("Ping") { } 45 | //Destructor 46 | virtual ~TwitchPing() { } 47 | //Process the command 48 | virtual bool Process(const string incoming); 49 | //Fetch singleton instance 50 | static TwitchPing &fetchInstance(); 51 | }; 52 | 53 | /* 54 | TwitchPrivMsg Class 55 | Handles the PRIVMSG message sent by Twitch 56 | */ 57 | class TwitchPrivMsg : public TwitchCommand { 58 | public: 59 | /* Public Class Methods */ 60 | //Constructor 61 | TwitchPrivMsg() : TwitchCommand("PrivMsg") { } 62 | //Process the command 63 | virtual bool Process(const string incoming); 64 | //Fetch singleton instance 65 | static TwitchPrivMsg &fetchInstance(); 66 | 67 | private: 68 | /* Private Class Methods */ 69 | //Format a chat message 70 | string formatChatMessage(const string message); 71 | }; 72 | 73 | /* 74 | TwitchUserState Class 75 | Handles the USERSTATE message sent by Twitch 76 | */ 77 | class TwitchUserState : public TwitchCommand { 78 | public: 79 | /* Public Class Methods */ 80 | //Constructor 81 | TwitchUserState() : TwitchCommand("UserState") { } 82 | //Process the command 83 | virtual bool Process(const string incoming); 84 | //Fetch singleton instance 85 | static TwitchUserState &fetchInstance(); 86 | }; 87 | 88 | #endif //_TWITCHCOMMANDPROCESS_H 89 | -------------------------------------------------------------------------------- /TwitchCommands/TwitchPing.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchPing.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "TwitchCommandProcess.h" 8 | 9 | bool TwitchPing::Process(const string incoming) { 10 | cout << "BOT: Recieved PING request, responding..." << endl; 11 | Lib::writeToLog("PhantomBotLog.txt", "{Twitch} Recieved PING request, responding..."); 12 | //const string response = "PONG " + TwitchCommandLimit::fetchInstance().Channel(); 13 | const string response = "PONG :tmi.twitch.tv\r\n"; 14 | TwitchCommandLimit::fetchInstance().PushCommand(response); 15 | return true; 16 | } 17 | 18 | TwitchPing &TwitchPing::fetchInstance() { 19 | if(managedSingleton::instance() == NULL) { 20 | managedSingleton::createInstance(); 21 | } 22 | return *(managedSingleton::instance()); 23 | } 24 | -------------------------------------------------------------------------------- /TwitchCommands/TwitchPrivMsg.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchPrivMsg.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "TwitchCommandProcess.h" 8 | 9 | bool TwitchPrivMsg::Process(const string incoming) { 10 | string name, message; 11 | //Strip the username and their message 12 | Lib::stripMessage(incoming, name, message); 13 | //Push to log / console 14 | cout << "Chat: " << name << ": " << message << endl; 15 | Lib::writeToLog("PhantomBotLog.txt", "{Chat} " + name + ": " + message); 16 | 17 | //Process commands here, but check if we're only doing it for admin users 18 | if(TwitchCommandLimit::fetchInstance().AdminOnlyMode()) { 19 | if(!Admin::fetchInstance().CheckAdminStatus(name)) { 20 | //Out of luck, kill here... 21 | return true; 22 | } 23 | } 24 | CustomCommandManager::fetchInstance().Process(incoming); 25 | 26 | return true; 27 | } 28 | 29 | TwitchPrivMsg &TwitchPrivMsg::fetchInstance() { 30 | if(managedSingleton::instance() == NULL) { 31 | managedSingleton::createInstance(); 32 | } 33 | return *(managedSingleton::instance()); 34 | } 35 | -------------------------------------------------------------------------------- /TwitchCommands/TwitchUserState.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchUserState.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "TwitchCommandProcess.h" 8 | 9 | bool TwitchUserState::Process(const string incoming) { 10 | cout << "BOT: Recieved USERSTATE Command: '" << Lib::formatForPrint(incoming).c_str() << "'." << endl; 11 | Lib::writeToLog("PhantomBotLog.txt", "{Twitch} Recieved USERSTATE Command: '" + Lib::formatForPrint(incoming) + "'."); 12 | TwitchCommandLimit::fetchInstance().ProcessUserState(incoming); 13 | return true; 14 | } 15 | 16 | TwitchUserState &TwitchUserState::fetchInstance() { 17 | if(managedSingleton::instance() == NULL) { 18 | managedSingleton::createInstance(); 19 | } 20 | return *(managedSingleton::instance()); 21 | } 22 | -------------------------------------------------------------------------------- /TwitchIRC/Admin.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | Admin.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "Admin.h" 8 | 9 | Admin::Admin() { 10 | UpdateAdminList(); 11 | } 12 | 13 | Admin::~Admin() { 14 | 15 | } 16 | 17 | bool Admin::CheckAdminStatus(string username) { 18 | for(SIZE_T i = 0; i < adminList.size(); i++) { 19 | if(username.compare(adminList[i]) == 0) { 20 | return true; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | void Admin::UpdateAdminList() { 27 | adminList.clear(); 28 | fstream f("adminusers.txt", ios::in); 29 | ACHAR line[512]; 30 | while(!f.eof()) { 31 | f.getline(line, 512); 32 | adminList.push_back(line); 33 | memset(&line[0], 0, sizeof(line)); 34 | } 35 | } 36 | 37 | Admin &Admin::fetchInstance() { 38 | if(managedSingleton::instance() == NULL) { 39 | managedSingleton::createInstance(); 40 | } 41 | return *(managedSingleton::instance()); 42 | } 43 | -------------------------------------------------------------------------------- /TwitchIRC/Admin.h: -------------------------------------------------------------------------------- 1 | /** 2 | Admin.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _TWITCHADMIN_H 8 | #define _TWITCHADMIN_H 9 | 10 | #include "../include.h" 11 | #include "../Lib/lib.h" 12 | 13 | /* 14 | Admin Class 15 | Handles actions for bot administration 16 | */ 17 | class Admin { 18 | public: 19 | /* Public Class Methods */ 20 | //Constructor 21 | Admin(); 22 | //Destructor 23 | ~Admin(); 24 | //Check admin status 25 | bool CheckAdminStatus(string username); 26 | //Update the admin list 27 | void UpdateAdminList(); 28 | //Fetch the admin singleton 29 | static Admin &fetchInstance(); 30 | 31 | private: 32 | /* Private Class Members */ 33 | //Admin list 34 | vector adminList; 35 | }; 36 | 37 | #endif //_TWITCHADMIN_H 38 | -------------------------------------------------------------------------------- /TwitchIRC/TwitchCommandLimit.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchCommandLimit.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "TwitchCommandLimit.h" 8 | 9 | /** 10 | TwitchCommandLimit Struct 11 | **/ 12 | 13 | TwitchCommandLimit::TwitchCommandLimit() : aSock(NULL), currentSendCount(0), isModOrOp(false), channel(), debugMode(false) { 14 | Time::utcTime(curTVal); 15 | Time::utcTime(curFVal); 16 | } 17 | 18 | void TwitchCommandLimit::Init(Socket *sO, string cName) { 19 | if(!sO) { 20 | cout << "Cannot init the TCL without a socket" << endl; 21 | return; 22 | } 23 | aSock = sO; 24 | channel = cName; 25 | updateThread = new thread(&TwitchCommandLimit::Update, this); 26 | } 27 | 28 | void TwitchCommandLimit::ProcessUserState(const string command) { 29 | if(!aSock) { 30 | cout << "No socket object" << endl; 31 | return; 32 | } 33 | //We only need to know if we have op/mod status, look for it... 34 | if(command.find("mod=1") != string::npos) { 35 | if(!isModOrOp) { 36 | cout << "BOT: Gained Op/Mod Status..." << endl; 37 | } 38 | isModOrOp = true; 39 | } 40 | else { 41 | if(isModOrOp) { 42 | cout << "BOT: Lost Op/Mod Status..." << endl; 43 | } 44 | isModOrOp = false; 45 | } 46 | } 47 | 48 | void TwitchCommandLimit::AddCommand(const string command) { 49 | normalCommands.push(command); 50 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Added command '" + Lib::formatForPrint(command) + "'."); 51 | cout << "BOT: Command '" << Lib::formatForPrint(command).c_str() << "' added to queue..." << endl; 52 | } 53 | 54 | void TwitchCommandLimit::PushCommand(const string command) { 55 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Attempting to push command '" + Lib::formatForPrint(command) + "'."); 56 | cout << "BOT: Command '" << command.c_str() << "' added to queue under FORCE priority." << endl; 57 | forcedCommands.push(command); 58 | } 59 | 60 | void TwitchCommandLimit::Update() { 61 | while (aSock->isValidSocket()) { 62 | TimeVars cur; 63 | Time::utcTime(cur); 64 | string nextCmd; 65 | //Check the time 66 | if ((Time::makeGMTime(cur) - Time::makeGMTime(curFVal)) >= 30) { 67 | //All good! 68 | currentSendCount = 0; 69 | } 70 | //Do we have anything to process? 71 | while (!forcedCommands.empty()) { 72 | //Check how many commands we've sent 73 | if ((isModOrOp && currentSendCount >= COMMAND_LIMIT_OPMOD - 1) 74 | || (!isModOrOp && currentSendCount >= COMMAND_LIMIT_NORMAL - 1)) { 75 | //We're currently over the command limit we're allocated.. 76 | // Stop here. 77 | break; 78 | } 79 | //We're ok to process commands, grab the nextCmd command and pop it from the list 80 | nextCmd = forcedCommands.front(); 81 | forcedCommands.pop(); 82 | //Add one to our counter, set the timer if zero 83 | if (currentSendCount == 0) { 84 | Time::utcTime(curTVal); 85 | } 86 | //Send it! 87 | SendCommand(nextCmd); 88 | } 89 | while (!normalCommands.empty()) { 90 | //Check how many commands we've sent 91 | if ((isModOrOp && currentSendCount >= COMMAND_LIMIT_OPMOD - 1) 92 | || (!isModOrOp && currentSendCount >= COMMAND_LIMIT_NORMAL - 1)) { 93 | //We're currently over the command limit we're allocated.. 94 | // Stop here. 95 | break; 96 | } 97 | //We're ok to process commands, grab the nextCmd command and pop it from the list 98 | nextCmd = normalCommands.front(); 99 | normalCommands.pop(); 100 | //Add one to our counter, set the timer if zero 101 | if (currentSendCount == 0) { 102 | Time::utcTime(curTVal); 103 | } 104 | //Send it! 105 | SendCommand(nextCmd); 106 | } 107 | this_thread::sleep_for(chrono::milliseconds(100)); 108 | } 109 | updateThread->join(); 110 | } 111 | 112 | const string TwitchCommandLimit::Channel() const { 113 | return channel; 114 | } 115 | 116 | void TwitchCommandLimit::SendCommand(const string command) { 117 | if(!aSock) { 118 | cout << "No socket object" << endl; 119 | return; 120 | } 121 | cout << "BOT: Attempting to send command '" << Lib::formatForPrint(command).c_str() << "' to server." << endl; 122 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Attempting to send command '" + Lib::formatForPrint(command) + "'."); 123 | if(!aSock->send(command.c_str())) { 124 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Command delivery failed."); 125 | cout << "BOT: Command '" << Lib::formatForPrint(command).c_str() << "' failed to send." << endl; 126 | } 127 | else { 128 | currentSendCount++; 129 | cout << "BOT: Command '" << Lib::formatForPrint(command).c_str() << "' successfully sent." << endl; 130 | } 131 | } 132 | 133 | const bool TwitchCommandLimit::DebugMode() const { 134 | return debugMode; 135 | } 136 | 137 | const bool TwitchCommandLimit::AdminOnlyMode() const { 138 | return adminOnlyMode; 139 | } 140 | 141 | TwitchCommandLimit &TwitchCommandLimit::fetchInstance() { 142 | if(managedSingleton::instance() == NULL) { 143 | managedSingleton::createInstance(); 144 | } 145 | return *(managedSingleton::instance()); 146 | } 147 | -------------------------------------------------------------------------------- /TwitchIRC/TwitchCommandLimit.h: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchIRC.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _TWITCHCOMMANDLIMIT_H 8 | #define _TWITCHCOMMANDLIMIT_H 9 | 10 | #include "../include.h" 11 | #include "../Lib/lib.h" 12 | #include "Admin.h" 13 | 14 | /* 15 | TwitchCommandLimit Struct 16 | Class instance used to protect the bot from going over the command limit 17 | */ 18 | struct TwitchCommandLimit { 19 | /* Class Enumerations */ 20 | enum TwitchLimits { 21 | //How many commands can we send in a 30 second period 22 | COMMAND_LIMIT_NORMAL = 20, 23 | //If we're a op/mod, how many? 24 | COMMAND_LIMIT_OPMOD = 100, 25 | }; 26 | 27 | /* Methods */ 28 | //Constructor 29 | TwitchCommandLimit(); 30 | //Init Instance 31 | void Init(Socket *sO, string cName); 32 | //Process the USERSTATE message 33 | void ProcessUserState(const string command); 34 | //Add a command to the process queue 35 | void AddCommand(const string command); 36 | //Push a command through the pipe. 37 | void PushCommand(const string command); 38 | //Update, called by TwitchIRC on it's update cycle 39 | void Update(); 40 | //Return the name of the active channel 41 | const string Channel() const; 42 | //Wrapper to send with error check 43 | void SendCommand(const string command); 44 | //Debug mode on? 45 | const bool DebugMode() const; 46 | //Admin Only mode? 47 | const bool AdminOnlyMode() const; 48 | //Set Debug Mode 49 | void setDebugMode(bool flag = false) { debugMode = flag; } 50 | //Set Admin Mode 51 | void setAdminMode(bool flag = false) { adminOnlyMode = flag; } 52 | //Fetch singleton instance 53 | static TwitchCommandLimit &fetchInstance(); 54 | 55 | private: 56 | /* Members */ 57 | //The update thread 58 | thread *updateThread; 59 | //The name of the channel 60 | string channel; 61 | //The queue of force commands 62 | queue forcedCommands; 63 | //The Queue of commands that will be processed 64 | queue normalCommands; 65 | //How many commands have we sent from the last 30s update? 66 | S32 currentSendCount; 67 | //Do we have mod status? 68 | bool isModOrOp; 69 | //The timeval at the time of the currentSendCount initiation 70 | TimeVars curTVal; 71 | //The timeval at the time of the forcedSendCount initiation 72 | TimeVars curFVal; 73 | //Attached socket instance 74 | Socket *aSock; 75 | //DEBUG MODE FLAG 76 | bool debugMode; 77 | //Admin only mode 78 | bool adminOnlyMode; 79 | }; 80 | 81 | #endif //_TWITCHCOMMANDLIMIT_H 82 | -------------------------------------------------------------------------------- /TwitchIRC/TwitchIRC.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchIRC.cpp 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #include "TwitchIRC.h" 8 | 9 | /** 10 | TwitchIRC Class 11 | **/ 12 | 13 | TwitchIRC::TwitchIRC(UFC32 nick, UFC32 usr, UFC32 pass, UFC32 addr, U32 port, UFC32 channel) : 14 | _connectedChannel(channel), serverAddr(), serverPort(0), _socketObj(NULL), autoping_thread(NULL) { 15 | cout << "IRCClient: Establishing" << endl; 16 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Establishing TwitchIRC Instance"); 17 | //Create the buffer 18 | buffer = new ACHAR[_MAXRECV]; 19 | //Create the socket 20 | _socketObj = Lib::createSocketAndConnect(addr, port); 21 | if(!_socketObj) { 22 | cout << "Failed to establish connection to " << addr << endl; 23 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Failed to connect to " + string(addr)+ "."); 24 | return; 25 | } 26 | serverAddr = addr; 27 | serverPort = port; 28 | cout << "Initializing Command Instances" << endl; 29 | //Init the command limit instance... 30 | TwitchCommandLimit::fetchInstance().Init(_socketObj, channel); 31 | ChatCommandDecs::fetchInstance().init(); 32 | cout << "Creating Command Interfaces..." << endl; 33 | //Construct the login token 34 | cout << "IRCClient: Establishing login token" << endl; 35 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Establishing TwitchIRC Login Token"); 36 | const string pS = string("PASS " + string(pass) + "\r\n"); 37 | const string nS = string("NICK " + string(nick) + "\r\n"); 38 | const string uS = string("USER " + string(usr) + "\r\n"); 39 | //Password must be sent first, then our information 40 | if(pS.size()) { 41 | TwitchCommandLimit::fetchInstance().SendCommand(pS); 42 | } 43 | TwitchCommandLimit::fetchInstance().SendCommand(nS); 44 | TwitchCommandLimit::fetchInstance().SendCommand(uS); 45 | //Wait for the welcome reply... 46 | fetchServerMessage(); 47 | if(response.find("Welcome") == string::npos) { 48 | //We failed... 49 | cout << "BOT: Failed to connect to TwitchIRC" << endl << endl << response.c_str() << endl << endl; 50 | Lib::writeToLog("PhantomBotLog.txt", "{Twitch} We did not recieve a welcome message"); 51 | CloseSocket(); 52 | } 53 | else { 54 | //Set the socket to non-blocking mode to allow the program to continue execution 55 | _socketObj->setNonBlocking(); 56 | //Enable advanced commnads 57 | const string aCS1 = string("CAP REQ :twitch.tv/commands\r\n"); 58 | const string aCS2 = string("CAP REQ :twitch.tv/membership\r\n"); 59 | const string aCS3 = string("CAP REQ :twitch.tv/tags\r\n"); 60 | TwitchCommandLimit::fetchInstance().SendCommand(aCS1); 61 | TwitchCommandLimit::fetchInstance().SendCommand(aCS2); 62 | TwitchCommandLimit::fetchInstance().SendCommand(aCS3); 63 | Lib::writeToLog("PhantomBotLog.txt", "{Twitch} Connected to TwitchIRC, connecting to channel '#" + string(channel) + "'."); 64 | //And finally... connect to the channel 65 | const string cS = string("JOIN " + string(channel) + "\r\n"); 66 | TwitchCommandLimit::fetchInstance().SendCommand(cS); 67 | //Send a intro message to init stuff... 68 | SendChatMessage("PhantomBot Now Connected to channel..."); 69 | //Set up AutoPing command 70 | autoping_thread = new thread(&TwitchIRC::AutoPing, this); 71 | } 72 | } 73 | 74 | TwitchIRC::~TwitchIRC() { 75 | Lib::writeToLog("PhantomBotLog.txt", "{C++} Calling ~TwitchIRC(), closing program...\n\n"); 76 | CloseSocket(); 77 | } 78 | 79 | void TwitchIRC::Update() { 80 | fetchServerMessage(); 81 | //cout << "Server: " << response << endl; 82 | if(response.size()) { 83 | //Process messages based on the content 84 | TwitchCommand *cmd; 85 | if(response.find("PRIVMSG") != string::npos) { 86 | TwitchPrivMsg::fetchInstance().Process(response); 87 | } 88 | else if(response.find("PING") != string::npos) { 89 | TwitchPing::fetchInstance().Process(response); 90 | } 91 | else if(response.find("PONG") != string::npos) { 92 | cout << "BOT: Server connection confirmed..." << endl; 93 | } 94 | else if(response.find("USERSTATE") != string::npos) { 95 | TwitchUserState::fetchInstance().Process(response); 96 | } 97 | else { 98 | //cout << "Got unknown response: " << response << endl; 99 | //Lib::writeToLog("PhantomBotLog.txt", "{Twitch} UIID Response '" + response + "'."); 100 | } 101 | } 102 | } 103 | 104 | void TwitchIRC::CloseSocket() { 105 | if(_socketObj) { 106 | _socketObj->close(); 107 | } 108 | } 109 | 110 | bool TwitchIRC::SocketActive() { 111 | if(_socketObj) { 112 | return _socketObj->isValidSocket(); 113 | } 114 | return false; 115 | } 116 | 117 | void TwitchIRC::AutoPing() { 118 | cout << "BOT: Begin AutoPing Routine" << endl; 119 | while(SocketActive()) { 120 | cout << "BOT: Running AutoPing routine to persist connection..." << endl; 121 | const string command = "PING :tmi.twitch.tv\r\n"; 122 | TwitchCommandLimit::fetchInstance().PushCommand(command); 123 | this_thread::sleep_for(chrono::milliseconds(PING_INTERVAL)); 124 | } 125 | autoping_thread->join(); 126 | } 127 | 128 | bool TwitchIRC::SendChatMessage(UFC32 message) { 129 | if(!SocketActive()) { 130 | return false; 131 | } 132 | cout << "BOT: Sending Message: " << Lib::formatForPrint(message).c_str() << "..." << endl; 133 | Lib::writeToLog("PhantomBotLog.txt", "{Twitch} Sending Message " + Lib::formatForPrint(message) + "..."); 134 | const string format = "PRIVMSG " + _connectedChannel + " :" + message + "\r\n"; 135 | //Add it to the queue 136 | TwitchCommandLimit::fetchInstance().AddCommand(format); 137 | return true; 138 | } 139 | 140 | bool TwitchIRC::ProcessConsoleCommand(UFC32 input) { 141 | //Big Time To-Do Here :P 142 | return true; 143 | } 144 | 145 | bool TwitchIRC::fetchServerMessage() { 146 | response = ""; 147 | while (SocketActive()) { 148 | //Get some data... 149 | S32 bytesRead; 150 | SocketCode rc = _socketObj->receive(buffer, _MAXRECV, &bytesRead); 151 | if (rc == SocketCode::Disconnected) { 152 | cout << "Server has closed socket connection, abort..." << endl; 153 | return false; 154 | } 155 | else if (rc == SocketCode::RecieveError) { 156 | //Socket classes are programmed to automatically push the error, no need to repeat here. 157 | return false; 158 | } 159 | else if(rc == SocketCode::NoError) { 160 | if (bytesRead > 0) { 161 | response += string((ACHAR *)buffer); 162 | fill_n(buffer, sizeof(buffer), NULL); 163 | //Check if the server sent it all in one go 164 | if (response.size() > 1 && response[response.size() - 2] == '\r' && response[response.size() - 1] == '\n') { 165 | return true; 166 | } 167 | } 168 | else { 169 | if (bytesRead < 0) { 170 | //What have you done???? 171 | cout << "You have caused some kind of voo-doo magic (" << errno << ") to occur, please stop..." << endl; 172 | response = ""; 173 | return false; 174 | } 175 | //End of the buffer! 176 | return true; 177 | } 178 | } 179 | } 180 | return false; 181 | } 182 | -------------------------------------------------------------------------------- /TwitchIRC/TwitchIRC.h: -------------------------------------------------------------------------------- 1 | /** 2 | TwitchIRC.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _TWITCHIRC_H 8 | #define _TWITCHIRC_H 9 | 10 | #include "../Lib/lib.h" 11 | #include "TwitchCommandLimit.h" 12 | #include "Admin.h" 13 | #include "../TwitchCommands/TwitchCommandProcess.h" 14 | #include "../CustomCommands/CustomCommands.h" 15 | #include "../chatCommandDefinitions.h" 16 | 17 | //Interval in which to ping the server to keep the connection alive, by default it's 4 minutes (240000MS) 18 | #define PING_INTERVAL 240000 19 | 20 | /* 21 | TwitchIRC Class 22 | Wrapper instance for connection and persistence 23 | */ 24 | class TwitchIRC { 25 | 26 | public: 27 | /* Default Stuff */ 28 | //Constructor 29 | TwitchIRC(UFC32 nick, UFC32 usr, UFC32 pass, UFC32 addr, U32 port, UFC32 channel); 30 | //Destructor 31 | ~TwitchIRC(); 32 | 33 | /* Class Commands */ 34 | //Update function, called once per frame to handle updates 35 | void Update(); 36 | //Close the Socket connection 37 | void CloseSocket(); 38 | //Check the status of the Socket 39 | bool SocketActive(); 40 | //Send ping to server, ran once every 5 minutes to prevent premature disconnect 41 | void AutoPing(); 42 | 43 | /* IRC Commands, Handled by the TwitchCommandLimit class */ 44 | //Send a chat message 45 | bool SendChatMessage(UFC32 message); 46 | //Process a console command 47 | bool ProcessConsoleCommand(UFC32 input); 48 | 49 | private: 50 | /* Private Class Methods */ 51 | //Function called during the update to check for new messages 52 | bool fetchServerMessage(); 53 | 54 | /* Private Class Members */ 55 | //Thread instance which runs the AutoPing command 56 | thread *autoping_thread; 57 | //The address of the IRC Server 58 | string serverAddr; 59 | //The port 60 | U32 serverPort; 61 | //Name of channel connected to 62 | string _connectedChannel; 63 | //Socket object 64 | Socket *_socketObj; 65 | //Data buffer 66 | ACHAR *buffer; 67 | 68 | string response; 69 | }; 70 | 71 | #endif //_TWITCHIRC_H 72 | -------------------------------------------------------------------------------- /adminusers.txt: -------------------------------------------------------------------------------- 1 | Phantom13927 2 | -------------------------------------------------------------------------------- /biicode.cmake: -------------------------------------------------------------------------------- 1 | # If we're on GCC, set the c++11 compilation flag. 2 | IF (CMAKE_COMPILER_IS_GNUCXX) 3 | SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 4 | ENDIF (CMAKE_COMPILER_IS_GNUCXX) 5 | 6 | ADD_BII_TARGETS() 7 | -------------------------------------------------------------------------------- /botconfig.txt: -------------------------------------------------------------------------------- 1 | [USERNAME] 2 | irc.chat.twitch.tv 3 | 6667 4 | #[CHANNEL NAME] 5 | [OAUTH PASSWORD] 6 | -------------------------------------------------------------------------------- /chatCommandDefinitions.h: -------------------------------------------------------------------------------- 1 | /** 2 | chatCommandDefinitions.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | #ifndef _CCMDDEC_H 8 | #define _CCMDDEC_H 9 | 10 | //Generic Headers (Don't Touch) 11 | #include "include.h" 12 | #include "CustomCommands/CustomCommands.h" 13 | //Headers to all of your custom commands (Insert the path(s) to your custom command files here) 14 | #include "CustomCommands/ccmd_time.h" 15 | #include "CustomCommands/ccmd_isadmin.h" 16 | #include "CustomCommands/ccmd_adminonly.h" 17 | 18 | /* 19 | ChatCommandDecs Class 20 | Handle class which contains and initalizes all of the custom chat command definitions 21 | */ 22 | class ChatCommandDecs { 23 | public: 24 | /* Public Class Methods */ 25 | //Constructor 26 | ChatCommandDecs() : initialized(false) { } 27 | //Destructor 28 | ~ChatCommandDecs() { } 29 | //Fetch the instance 30 | static ChatCommandDecs &fetchInstance() { 31 | if(managedSingleton::instance() == NULL) { 32 | managedSingleton::createInstance(); 33 | } 34 | return *(managedSingleton::instance()); 35 | } 36 | //Initalize the instance 37 | void init() { 38 | if(initialized == true) { 39 | return; 40 | } 41 | initialized = true; 42 | CustomCommandManager::fetchInstance().AddCommand("!time", new Command_Time()); 43 | CustomCommandManager::fetchInstance().AddCommand("!isadmin", new Command_IsAdmin()); 44 | CustomCommandManager::fetchInstance().AddCommand("!adminonly", new Command_AdminOnly()); 45 | } 46 | 47 | private: 48 | bool initialized; 49 | }; 50 | 51 | #endif //_CCMDDEC_H 52 | -------------------------------------------------------------------------------- /include.h: -------------------------------------------------------------------------------- 1 | /** 2 | include.h 3 | PhantomBot Project 4 | By: Robert F. (Phantom139) 5 | **/ 6 | 7 | //Platform Independant Headers 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "Platform/platform.h" 30 | 31 | #include "Lib/exception.h" 32 | #include "Lib/mSingleton.h" 33 | 34 | using namespace std; 35 | --------------------------------------------------------------------------------