I'm currently making my first steps into the territory of network programming, using the SFML Network library to simplify things.
Later on, my bot shall post new pull requests and changes concerning issues to an IRC channel.
I've got the bot to connect to a server (irc.euirc.net
is hardcoded for now) and to a channel, leaving a "Hello!" message.
I'm currently unhappy with the realization of the receive
and waitForConnect
functions, which are basically doing the same. Could someone give me a hint how to combine both methods into one while retaining the current functionality?
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <regex>
#include <algorithm>
#include <SFML/Network.hpp>
#define ARRAY_LEN(x) (sizeof(x)/sizeof(*x))
enum class ServerMessage {Ping, ConnectionEstablished};
std::vector<std::string> split(const std::string& str, const std::string& delimiter) {
std::vector<std::string> splitted;
std::string s{ str };
size_t pos{ 0 };
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
splitted.push_back(token);
s.erase(0, pos + delimiter.length());
}
return splitted;
}
bool send(const std::string& data, sf::TcpSocket* sck)
{
std::cout << "Sending data: " + data << std::endl;
if (sck->send(data.c_str(), data.length()) != sf::Socket::Done)
{
std::cout << "Error on sending " << data << std::endl;
return false;
}
return true;
}
ServerMessage handle(const std::string& msg, sf::TcpSocket* sck)
{
std::cout << msg << std::endl;
if (msg.substr(0, 4) == "PING")
{
auto payload = msg.substr(6, msg.length() - 6);
std::cout << payload << std::endl;
send("PONG :" + payload + "\r\n", sck);
return ServerMessage::Ping;
}
else
{
std::regex connectedMessage{ "^:.* 001 NimBot" };
if(std::regex_search(msg, connectedMessage)) {
return ServerMessage::ConnectionEstablished;
}
}
}
void receive(sf::TcpSocket* sck)
{
char rcvData[255];
memset(rcvData, 0, ARRAY_LEN(rcvData));
std::size_t received;
if (sck->receive(rcvData, ARRAY_LEN(rcvData), received) != sf::Socket::Done)
{
std::cout << "oops" << std::endl;
}
std::string rcvString(rcvData);
auto parts = split(rcvData, "\r\n");
std::for_each(parts.begin(), parts.end(), [sck](std::string& s) { handle(s, sck); });
}
void waitForConnect(sf::TcpSocket* sck)
{
bool connected = false;
char rcvData[255];
while (!connected) {
memset(rcvData, 0, ARRAY_LEN(rcvData));
std::size_t received;
if (sck->receive(rcvData, ARRAY_LEN(rcvData), received) != sf::Socket::Done)
{
std::cout << "oops" << std::endl;
}
std::string rcvString{ rcvData };
auto parts = split(rcvData, "\r\n");
std::for_each(
parts.begin(),
parts.end(),
[sck, &connected](std::string& s) { if(handle(s, sck) == ServerMessage::ConnectionEstablished) connected = true; });
}
std::cout << "CONNECTED!" << std::endl;
}
bool establishConnection(sf::TcpSocket* sck)
{
sf::Socket::Status status = sck->connect("irc.euirc.net", 6667, sf::seconds(5.0f));
if (status != sf::Socket::Done)
{
std::cout << "Error on connect!" << std::endl;
return false;
}
std::cout << "Connect was successful!" << std::endl;
return true;
}
bool loginOnIRC(sf::TcpSocket* sck)
{
send("NICK NimBot\r\n", sck);
send("USER NimBot * * :Nimelrians Bot\r\n", sck);
return true;
}
int main()
{
const std::string channel{ "#nimbottest" };
sf::TcpSocket sck{};
if (!establishConnection(&sck))
{
return 1;
}
if (!loginOnIRC(&sck))
{
return 1;
}
waitForConnect(&sck);
send("JOIN " + channel + "\r\n", &sck);
send("PRIVMSG " + channel + " :Hello!\r\n", &sck);
while (true)
{
receive(&sck);
}
return 0;
}