Documentation

parent 6a223a9b
# Copyright (C) 2015 Mattia Basaglia
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 2.6)
project(melanobot)
project(MelanoBot)
string(TOLOWER MelanoBot EXECUTABLE_NAME)
set (VERSION_MAJOR 2)
set (VERSION_MINOR 0)
set (MAINTAINER "Mattia Basaglia <mattia.basaglia@gmail.com>")
set(ALL_SOURCES "")
add_subdirectory(src)
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT doc)
foreach(source ${ALL_SOURCES})
set(DOXYGEN_INPUT "${DOXYGEN_INPUT} ${source}")
endforeach()
configure_file(${CMAKE_SOURCE_DIR}/Doxyfile.in ${PROJECT_BINARY_DIR}/Doxyfile)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxyfile
DEPENDS ${PROJECT_BINARY_DIR}/Doxyfile ${ALL_SOURCES}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
COMMENT "Generating Doxygen Documentation" VERBATIM
)
add_custom_target(doc-view
xdg-open ${DOXYGEN_OUTPUT}/html/index.html
COMMENT "Showing Doxygen documentation"
)
endif(DOXYGEN_FOUND)
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
Melanobot v2
============
A not so simple IRC bot.
Contacts
--------
Mattia Basaglia <mattia.basaglia@gmail.com>
License
-------
AGPLv3 or later, see COPYING.
Sources
-------
Up to date sources are available at https://github.com/mbasaglia/Melanobot_v2
Installing
==========
Dependencies
------------
* [C++14 Compiler](http://en.cppreference.com/w/cpp/compiler_support)
* [CMake](http://www.cmake.org/)
* [Boost](http://www.boost.org/)
* asio
* chrono
* property_tree
* [Cpp-Netlib](http://cpp-netlib.org/)
Building
--------
mkdir build && cd build && cmake .. && make
Running
-------
src/melanobot
Configuration
-------------
TODO
Installation
------------
TODO
\ No newline at end of file
# Copyright (C) 2015 Mattia Basaglia
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE HEADERS *.hpp)
set(EXECUTABLE melanobot)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -g")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14")
set(ALL_SOURCES ${ALL_SOURCES} ${SOURCES} ${HEADERS} PARENT_SCOPE)
# Flags
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -pedantic")
include_directories("${CMAKE_SOURCE_DIR}/src")
# Check C++14
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCXX)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(--std=c++14 STD_CXX11)
if(STD_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++14")
else()
message(SEND_ERROR "Requires C++14 or better")
endif()
else()
message(WARNING "Unrecognized compile, make sure it supports C++14")
endif()
# Enable Debug by default, can be changed with -D CMAKE_BUILD_TYPE=Release
if(CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE Debug)
set(DEBUG 1)
else()
set(DEBUG 0)
endif()
add_executable(${EXECUTABLE} ${SOURCES})
install(TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin)
add_executable(${EXECUTABLE_NAME} ${SOURCES})
install(TARGETS ${EXECUTABLE_NAME} RUNTIME DESTINATION bin)
# Boost
find_package (Boost COMPONENTS system chrono thread REQUIRED)
target_link_libraries(${EXECUTABLE} ${Boost_LIBRARIES})
target_link_libraries(${EXECUTABLE_NAME} ${Boost_LIBRARIES})
include_directories (${Boost_INCLUDE_DIRS})
# Cpp-netlib
find_package ( cppnetlib 0.11.0 REQUIRED )
include_directories ( ${CPPNETLIB_INCLUDE_DIRS} )
target_link_libraries ( ${EXECUTABLE} ${CPPNETLIB_LIBRARIES} )
target_link_libraries ( ${EXECUTABLE_NAME} ${CPPNETLIB_LIBRARIES} )
......@@ -23,7 +23,9 @@
#include <cstdint>
#include <cstdlib>
#include <string>
/**
* \brief Namespace for color operations
*/
namespace color {
/**
* \brief 12 bit color
......@@ -66,11 +68,12 @@ public:
b = component_from_hex(s[2]);
}
}
/**
* \brief Creates a color from a DP color string ^. or ^x...
*/
static Color12 from_dp(const std::string& color);
/**
* \brief Creates a color from an IRC color string \3..
*/
......
......@@ -21,32 +21,49 @@
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <string>
#include <unordered_map>
//#include <boost/chrono.hpp>
#include <boost/chrono/io/time_point_io.hpp>
//#include <boost/chrono/io/timezone.hpp>
#include "color.hpp"
/**
* \brief Singleton class handling logs
* \see Log for a nicer interface
* \todo read settings
*/
class Logger
{
public:
static const int colors = 0x1;
static const int timestamp = 0x2;
static Logger& singleton()
/**
* \brief Singleton instance
*/
static Logger& instance()
{
static Logger instance;
return instance;
static Logger singleton;
return singleton;
}
/**
* \brief Register a log "direction"
*
* A direction is a simple identifier showing what kind of message
* has been logged
*/
void register_direction(char name, color::Color12 color)
{
log_directions[name] = color;
}
/**
* \brief Register a log type
*
* A log type is the name of the component which generates the log.
* Better to keep it short, 3 letters should do.
* The default verbosity is 2
*/
void register_log_type(const std::string& name, color::Color12 color)
{
if ( log_type_length < name.size() )
......@@ -54,11 +71,19 @@ public:
log_types[name] = {color,2};
}
/**
* \brief Change verbosity level for a given log type
*
* Messages of that type with higher verbisity will be discarded
*/
void set_log_verbosity(const std::string& name, int level)
{
log_types[name].verbosity = level;
}
/**
* \brief Log a message
*/
void log (const std::string& type, char direction, const std::string& message, int verbosity)
{
/// \todo lock mutex for log_destination
......@@ -66,11 +91,11 @@ public:
if ( type_it != log_types.end() && type_it->second.verbosity < verbosity )
return;
if ( log_flags & timestamp )
if ( !timestamp.empty() )
{
put_color(color::yellow);
log_destination << '['
<< boost::chrono::time_fmt(boost::chrono::timezone::local, "%Y-%m-%d %T")
<< boost::chrono::time_fmt(boost::chrono::timezone::local, timestamp)
<< boost::chrono::system_clock::now()
<< ']';
put_color(color::nocolor);
......@@ -86,9 +111,13 @@ public:
log_destination << message << std::endl;
}
int flags() const { return log_flags; }
bool colors = true; ///< Whether colors are enabled
std::string timestamp = "%Y-%m-%d %T"; ///< Timestamp format
private:
/**
* \brief Data associated with a log type
*/
struct LogType
{
color::Color12 color;
......@@ -98,13 +127,15 @@ private:
Logger() {}
Logger(const Logger&) = delete;
/**
* \brief Put a color code only if colors are enabled
*/
void put_color( const color::Color12& color )
{
if ( log_flags & colors )
if ( colors )
log_destination << color.to_ansi();
}
int log_flags = colors|timestamp;
std::ostream log_destination {std::cout.rdbuf()};
std::unordered_map<std::string, LogType> log_types;
std::unordered_map<char, color::Color12> log_directions;
......@@ -134,7 +165,7 @@ public:
{
if ( color )
stream << color::nocolor.to_ansi();
Logger::singleton().log(type, direction, stream.str(), verbosity);
Logger::instance().log(type, direction, stream.str(), verbosity);
}
template<class T>
......@@ -146,7 +177,7 @@ public:
const Log& operator<< ( const color::Color12& t ) const
{
if ( Logger::singleton().flags() & Logger::colors )
if ( Logger::instance().flags() & Logger::colors )
{
color = true;
stream << t.to_ansi();
......@@ -162,4 +193,18 @@ public:
mutable bool color = false;
};
/**
* \brief Throws an exception with a standardized format
*/
void error [[noreturn]] (const std::string& file, int line,
const std::string& function, const std::string& msg )
{
throw std::logic_error(function+"@"+file+":"+std::to_string(line)+": error: "+msg);
}
/**
* \brief Throws an exception pointing to the call line
*/
#define CRITICAL_ERROR(msg) \
error(__FILE__,__LINE__,__func__,msg)
#endif // LOGGER_HPP
......@@ -7,17 +7,18 @@ int main(int argc, char **argv)
{
Melanobot bot;
Logger::singleton().register_direction('<',color::dark_green);
Logger::singleton().register_direction('>',color::dark_yellow);
Logger::singleton().register_direction('!',color::dark_blue);
Logger::instance().register_direction('<',color::dark_green);
Logger::instance().register_direction('>',color::dark_yellow);
Logger::instance().register_direction('!',color::dark_blue);
/// \todo register these in the proper classes
Logger::singleton().register_log_type("irc",color::dark_magenta);
Logger::singleton().register_log_type("dp",color::dark_cyan);
Logger::singleton().register_log_type("std",color::white);
Logger::singleton().register_log_type("web",color::dark_blue);
Logger::instance().register_log_type("irc",color::dark_magenta);
Logger::instance().register_log_type("dp",color::dark_cyan);
Logger::instance().register_log_type("std",color::white);
Logger::instance().register_log_type("web",color::dark_blue);
Logger::instance().register_log_type("sys",color::red);
Logger::singleton().set_log_verbosity("irc",100);
Logger::instance().set_log_verbosity("irc",100);
network::irc::IrcConnection irc(&bot,network::Server{"irc.quakenet.org",6667});
irc.run();
......
......@@ -32,7 +32,6 @@
#include "../logger.hpp"
namespace network {
namespace http {
......
......@@ -5,7 +5,7 @@
*
* \section License
*
* Copyright (C) Mattia Basaglia
* Copyright (C) 2015 Mattia Basaglia
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -28,6 +28,11 @@
#include <string>
#include <map>
/**
* \brief Namespace for network operations
*
* This includes asyncronous calls and network protocol operations
*/
namespace network {
/**
......@@ -49,6 +54,9 @@ struct Response
std::string contents; ///< Message contents
};
/**
* \brief Callback used by asyncronous calls
*/
typedef std::function<void(const Response&)> AsyncCallback;
/**
......@@ -69,12 +77,18 @@ public:
virtual Response query (const Request& request) = 0;
protected:
/**
* \brief Quick way to create a successful response
*/
Response ok(const std::string& contents)
{
Response r;
r.contents = contents;
return r;
}
/**
* \brief Quick way to create a failure response
*/
Response error(const std::string& error_message)
{
Response r;
......@@ -88,21 +102,20 @@ protected:
*/
namespace http {
/**
* \brief Request parameters
*/
typedef std::map<std::string,std::string> Parameters;
/**
* \brief Encode a string to make it fit to be used in a url
* \todo urldecode (?)
* \see http://www.faqs.org/rfcs/rfc3986.html
*/
std::string urlencode ( const std::string& text );
/**
* \todo (?)
*/
std::string urldecode ( const std::string& url );
/**
* \brief Build a query string from the given parameters
* \brief Creates a query string from the given parameters
*/
std::string build_query ( const Parameters& params );
......@@ -111,10 +124,13 @@ std::string build_query ( const Parameters& params );
*/
Request get(const std::string& url, const Parameters& params = Parameters());
/**
* \brief Creates a POST request
* \brief Creates a simple POST request
*/
Request post(const std::string& url, const Parameters& params = Parameters());
/**
* \brief HTTP client
*/
class Client : public AsyncService
{
public:
......
......@@ -33,6 +33,7 @@
#include "connection.hpp"
#include "../melanobot.hpp"
#include "../settings.hpp"
#include "../logger.hpp"
namespace network {
namespace irc {
......@@ -72,6 +73,9 @@ inline std::string strtoupper ( std::string string )
return string;
}
/**
* \brief A collection of servers
*/
class Network
{
public:
......@@ -83,7 +87,7 @@ public:
: servers(servers)
{
if ( servers.empty() )
throw std::logic_error("Empty network");
CRITICAL_ERROR("Empty network");
}
Network ( const Network& other ) : Network(other.servers) {}
......@@ -93,11 +97,18 @@ public:
*/
Network& operator= ( const Network& other ) = delete;
/**
* \brief Current server
*/
const Server& current() const
{
return servers[current_server];
}
/**
* \brief Advances to the next server in the list and returns it
* \note Acts as a circular buffer, will always return a valid server
*/
const Server& next()
{
++current_server;
......@@ -106,6 +117,10 @@ public:
return current();
}
/**
* \brief Number of servers
* \note Always >= 0
*/
int size() const
{
return servers.size();
......@@ -113,11 +128,19 @@ public:
private:
ServerList servers;
/**
* \todo needs atomic?
*/
std::atomic<unsigned> current_server { 0 };
};
class IrcConnection;
/**
* \brief IRC buffer
*
* Contains the network connection and performs flood checking
*/
class Buffer
{
public:
......@@ -125,43 +148,56 @@ public:
/**
* \brief Run the async process
* \thread irc \lock none
*/
void run();
/**
* \brief Inserts a command to the buffer
* \thread external \lock buffer
*/
void insert(const Command& cmd);
/**
* \brief Check if the buffer is empty
* \thread ? \lock buffer
*/
bool empty() const;
/**
* \brief Process the top command (if any)
* \thread queue \lock buffer
*/
void process();
/**
* \brief Clear all commands
* \thread ? \lock buffer
*/
void clear();
/**
* \brief Outputs directly a line from the command
* \thread irc queue \lock none
*/
void write(const Command& cmd);
/**
* \brief Connect to the given server
* \thread irc, queue \lock none
*/
void connect(const Server& server);
/**
* \brief Disconnect from the server
* \thread irc, queue \lock none
*/
void disconnect();
/**
* \brief Checks if the connection is active
* \thread irc, queue \lock none
*/
bool connected() const;
private:
......@@ -204,22 +240,28 @@ private:
/**
* \brief Schedules an asynchronous line read
* \thread irc, async_read \lock none (async)
*/
void schedule_read();
/**
* \brief Writes a line to the socket
* \thread irc, queue \lock none TODO?
*/
void write_line ( std::string line );
/**
* \brief Async hook on network input
* \thread async_read \lock none
*/
void on_read_line(const boost::system::error_code &error);
};
/**
* \brief IRC connection
*/
class IrcConnection : public Connection
{
public:
......@@ -228,44 +270,87 @@ public:
*/
static void error(const std::string& message);
/**
* \thread main \lock none
*/
IrcConnection(Melanobot* bot, const Network& network, const Settings& settings = {});
/**
* \thread main \lock none
*/
IrcConnection(Melanobot* bot, const Server& server, const Settings& settings = {});
/**
* \thread main \lock none
*/
~IrcConnection() override;
/**
* \thread irc \lock none
*/
void run() override;
/**
* \thread ? \lock buffer
*/
const Server& server() const override;
/**
* \thread irc, external, async_read \lock data(sometimes) buffer(call)
*/
void command ( const Command& cmd ) override;
/**
* \thread external \lock data(sometimes) buffer(call)
*/
void say ( const std::string& channel,
const std::string& message,
int priority = 0,
const Time& timeout = Time::max() ) override;
/**
* \thread external \lock data(sometimes) buffer(call)
*/
void say_as ( const std::string& channel,
const std::string& name,
const std::string& message,
int priority = 0,
const Time& timeout = Time::max() ) override;
/**
* \thread ? \lock none
*/
Status status() const override;
/**
* \thread ? \lock none
*/
std::string protocol() const override;
/**
* \thread ? \lock none
*/
std::string connection_name() const override;
/**
* \thread main, external \lock buffer(indirect) data(indirect)
*/
void connect() override;
/**
* \thread main, external, irc, async_read \lock buffer(indirect)
*/
void disconnect() override;
/**
* \brief Quit and connect
* \thread async_read \lock buffer(indirect) data(indirect)
*/
void reconnect();
/**
* \brief Bot's current nick
* \thread ? \lock data
*/
std::string nick() const;
std::string normalized_nick() const;
private:
......@@ -273,16 +358,19 @@ private:
/**
* \brief Handle a IRC message
* \thread async_read \lock data(sometimes) buffer(indirect, sometimes)
*/
void handle_message(const Message& line);
/**
* \brief Extablish connection to the IRC server
* \thread async_read \lock data buffer(indirect)
*/
void login();
/**
* \brief AUTH to the server
* \thread async_read \lock data buffer(indirect)
*/
void auth();
......
......@@ -21,6 +21,9 @@
#include <boost/property_tree/ptree.hpp>
/**
* \brief Class containing hierarchical settings
*/
typedef boost::property_tree::ptree Settings;
#endif // SETTINGS_HPP
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment