aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kusatz <max@trialserver.de>2020-08-22 14:08:46 +0200
committerMax Kusatz <max@trialserver.de>2020-08-22 14:08:46 +0200
commit598218e9e1d6e97b7793b0cff2c0d50cab879fa4 (patch)
treeb0c80ca9c038195f4f473a34cb99d0c01479676a
parent537a243882f49cd95b304ecf283de950440fd66a (diff)
downloadn_core-598218e9e1d6e97b7793b0cff2c0d50cab879fa4.tar.gz
n_core-598218e9e1d6e97b7793b0cff2c0d50cab879fa4.zip
Mensa working sort of with JSON
-rw-r--r--CMakeLists.txt36
-rw-r--r--README.md61
-rw-r--r--src/N-Commands/KlingerHandler.cpp7
-rw-r--r--src/N-Commands/KlingerHandler.hpp (renamed from src/N-Commands/KlingerHandler.h)3
-rw-r--r--src/N-Commands/RelationshipHandler.cpp10
-rw-r--r--src/N-Commands/RelationshipHandler.h10
-rw-r--r--src/N-Commands/RelationshipHandler.hpp10
-rw-r--r--src/Utilities/GetEssen.cpp250
-rw-r--r--src/Utilities/GetEssen.hpp38
-rw-r--r--src/Utilities/Logger.cpp48
-rw-r--r--src/Utilities/Logger.hpp38
-rw-r--r--src/Utilities/Utilities.hpp142
-rw-r--r--src/Utilities/sqdb.cpp506
-rw-r--r--src/Utilities/sqdb.hpp207
-rw-r--r--src/main.cpp32
15 files changed, 1368 insertions, 30 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b486d78..239e2af 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,40 @@ project(DORgodBotBackend)
set(CMAKE_CXX_STANDARD 20)
+
+find_package(Threads REQUIRED)
+find_package(CURL)
+find_package(JSONCPP)
+
+find_package(Boost COMPONENTS system REQUIRED)
+find_package(Boost COMPONENTS filesystem REQUIRED)
+find_package(Boost COMPONENTS date_time REQUIRED)
+
+include_directories(/usr/local/include ${OPENSSL_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ./src)
+
+add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
+add_definitions(-DBOOST_SYSTEM_NO_DEPRECATED)
+add_definitions(-DHAS_JSONCPP=ON)
+
+
+if (CURL_FOUND)
+ include_directories(${CURL_INCLUDE_DIRS})
+ add_definitions(-DHAVE_CURL)
+endif()
+
link_libraries(pthread)
+link_libraries(curl)
link_libraries(mongoose)
-add_executable(DORgodBotBackend src/main.cpp src/N-Commands/KlingerHandler.cpp src/N-Commands/KlingerHandler.h src/N-Commands/RelationshipHandler.cpp src/N-Commands/RelationshipHandler.h) \ No newline at end of file
+add_executable(DORgodBotBackend src/main.cpp
+ src/N-Commands/KlingerHandler.cpp
+ src/N-Commands/KlingerHandler.hpp
+ src/N-Commands/RelationshipHandler.cpp
+ src/N-Commands/RelationshipHandler.hpp
+ src/Utilities/Logger.cpp
+ src/Utilities/sqdb.cpp
+ src/Utilities/Logger.hpp
+ src/Utilities/sqdb.hpp
+ src/Utilities/Utilities.hpp
+ src/Utilities/GetEssen.cpp
+ src/Utilities/GetEssen.hpp)
+target_link_libraries(DORgodBotBackend -lsqlite3 -ljsoncpp -lboost_date_time -lboost_system ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${CURL_LIBRARIES}) \ No newline at end of file
diff --git a/README.md b/README.md
index cf03635..6f416ec 100644
--- a/README.md
+++ b/README.md
@@ -5,4 +5,63 @@ Hier kommt noch Kram
Benötigte Libraries:
Mongoose https://github.com/Gregwar/mongoose-cpp
-Der Server läuft lokal auf Port 8080 \ No newline at end of file
+Der Server läuft lokal auf Port 8080
+
+
+Niklas Halle
+## N protocol
+### request
+Each message sent to the N core server must be valid JSON and have a `command` field. It contains the command for N to handle, sanitizied (i.e. no leading '/' or trailing username etc).
+The arguments field contains the remaining text just as was send to the bridge.
+All messages may optionally include an `session` field. When you authorize against N core or switch to a menu, you will be returned (see next section) a session id which you can then send with this field to follow up on the inital command.
+
+| Field | Type | Required? | Description |
+|-------|------|-----------|-------------|
+| `command` | string | yes | The command N core should handle |
+| `arguments` | string | no | The arguments to the command (i.e. all remaining text in the message |
+| `session` | string | no | The session id, defaults to `null` |
+
+### answer
+The answer consists of an array of `reply` objects, an `success`-flag and a `session`-id provided by N core.
+The reply objects enable N core to request certain formatting from the bridges, which they should try to implement as close as the underlying platform supports it. In any case, the plain text stringed together from all replies `text` field should be enough to get the message to the user, annotations should be fully optional.
+The session id can be ignored by bridges, but this will opt the bridge out of more complex (context specific) commands.
+JSON object overview:
+
+| Field | Type | Required? | Description |
+|-------|------|-----------|-------------|
+| `reply` | array of `reply`-objects | yes | The reply from N core |
+| `success` | bool | yes | Whether the command was successful |
+| `session` | string | yes | A session id, defaults to `null` |
+
+#### `reply`
+The `reply` object:
+
+| Field | Type | Required? | Description |
+|-------|------|-----------|-------------|
+| `text` | string | yes | A part of the answer |
+| `annotations` | yes | array of `annotation`-objects | The annotations to this answer part |
+
+#### `annotation`
+The `annotation` object:
+
+| Field | Type | Required? | Description |
+|-------|------|-----------|-------------|
+| `type` | string | yes | A part of the answer |
+| `extra` | string | no | Extra content specific to the annotation type |
+
+The currently specified annotation types are enumerated below:
+
+##### `bold`
+The text should be printed as bold. No extra content.
+
+##### `italic`
+The text should be printed as italic. No extra content.
+
+##### `strikethrough`
+The text should be printed as strikethrough. No extra content.
+
+##### `underline`
+The text should be printed as underline. No extra content.
+
+##### `link`
+The text should be formatted as a link. Extra content: the uri to link. \ No newline at end of file
diff --git a/src/N-Commands/KlingerHandler.cpp b/src/N-Commands/KlingerHandler.cpp
index 448532c..c596952 100644
--- a/src/N-Commands/KlingerHandler.cpp
+++ b/src/N-Commands/KlingerHandler.cpp
@@ -2,9 +2,10 @@
// Created by max on 11.08.20.
//
-#include "KlingerHandler.h"
+#include "KlingerHandler.hpp"
+
+void KlingerHandler::onCall(const Mongoose::Request& request, Mongoose::JsonResponse& response) {
-void KlingerHandler::onCall(const Mongoose::Request& request, Mongoose::StreamResponse& response) {
std::cout << "Klinger was called\n";
- response << "Bonjour!";
+ response["text"] = "Bonjour!";
}
diff --git a/src/N-Commands/KlingerHandler.h b/src/N-Commands/KlingerHandler.hpp
index 2fc5267..c078303 100644
--- a/src/N-Commands/KlingerHandler.h
+++ b/src/N-Commands/KlingerHandler.hpp
@@ -2,6 +2,7 @@
// Created by max on 11.08.20.
//
#include <mongoose/Server.h>
+#include <mongoose/JsonResponse.h>
#include <string>
#ifndef DORGODBOTBACKEND_KLINGERHANDLER_H
#define DORGODBOTBACKEND_KLINGERHANDLER_H
@@ -9,7 +10,7 @@
class KlingerHandler {
public:
- void onCall(const Mongoose::Request& request, Mongoose::StreamResponse& response) ;
+ void onCall(const Mongoose::Request& request, Mongoose::JsonResponse& response) ;
};
diff --git a/src/N-Commands/RelationshipHandler.cpp b/src/N-Commands/RelationshipHandler.cpp
index 808e17c..46991a3 100644
--- a/src/N-Commands/RelationshipHandler.cpp
+++ b/src/N-Commands/RelationshipHandler.cpp
@@ -1,4 +1,4 @@
-#include "RelationshipHandler.h"
+#include "RelationshipHandler.hpp"
#include<string>
#include<vector>
#include <algorithm>
@@ -111,12 +111,14 @@ string rsStart(vector<string> names) {
return (names.at(0) + " und " + names.at(1) + " passen nach Angaben von N zu " + to_string(result) + "% zusammen. Gratuliere!\n");
}
-void RelationshipHandler::onCall(Mongoose::Request& request, Mongoose::StreamResponse& response) {
+void RelationshipHandler::onCall(Mongoose::Request& request, Mongoose::JsonResponse& response) {
- // only react when command was issued after this boot
std::cout << "/relation was called\n";
- response << rsStart(vector<string>{request.get("name1", "Lukas"), request.get("name2", "cpp")});
+ response["text"] = rsStart(vector<string>{request.get("name1", "Lukas"), request.get("name2", "cpp")});
+
+ response["success"] = "1";
+ response["session"] = "NULL";
/*if (messagePtr->date > telegram->getBootDate()) {
log(messagePtr->from->username + ": "+messagePtr->text);
diff --git a/src/N-Commands/RelationshipHandler.h b/src/N-Commands/RelationshipHandler.h
deleted file mode 100644
index 251cabf..0000000
--- a/src/N-Commands/RelationshipHandler.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-#include <mongoose.h>
-#include <mongoose/Request.h>
-#include <mongoose/StreamResponse.h>
-
-class RelationshipHandler {
-public:
- void onCall(Mongoose::Request& request, Mongoose::StreamResponse& response);
-
-};
diff --git a/src/N-Commands/RelationshipHandler.hpp b/src/N-Commands/RelationshipHandler.hpp
new file mode 100644
index 0000000..fe9c0ea
--- /dev/null
+++ b/src/N-Commands/RelationshipHandler.hpp
@@ -0,0 +1,10 @@
+#pragma once
+#include <mongoose.h>
+#include <mongoose/Request.h>
+#include <mongoose/JsonResponse.h>
+
+class RelationshipHandler {
+public:
+ void onCall(Mongoose::Request& request, Mongoose::JsonResponse& response);
+
+};
diff --git a/src/Utilities/GetEssen.cpp b/src/Utilities/GetEssen.cpp
new file mode 100644
index 0000000..56742cc
--- /dev/null
+++ b/src/Utilities/GetEssen.cpp
@@ -0,0 +1,250 @@
+//
+// Created by max on 18.12.18.
+//
+
+#include "Utilities/GetEssen.hpp"
+
+
+using namespace boost::posix_time;
+
+void returnEssen(Mongoose::Request &request, Mongoose::JsonResponse &response){
+ std::cout << "mensa was called\n";
+ mensa Mensa_ID;
+ std::string mensa_name = request.get("argument1", "adlershof");
+ std::string offset_request = request.get("argument2", "0");
+
+
+ if(mensa_name == "adlershof"){
+ Mensa_ID = mensa::Adlershof;
+ } else if(mensa_name == "nord"){
+ Mensa_ID = mensa::Nord;
+ }else if (mensa_name == "sued" || mensa_name == "süd"){
+ Mensa_ID = mensa::Sued;
+ }
+
+ int offset = std::stoi(offset_request);
+
+
+ response["success"] = "1";
+ response["session"] = "NULL";
+ response["text"] = getEssen(Mensa_ID, offset);
+
+
+}
+
+bool db_update_mensa_message(const int& mensaID, const ptime& mensaDatum, const string& message){
+ sqdb::Db db("../main.sqlite");
+ string newTime = to_simple_string(ptime(second_clock::local_time()));
+ string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + to_string(mensaDatum.date().day().as_number());
+ sqdb::QueryStr str;
+ db.Query(str.Format(SQDB_MAKE_TEXT("update mensa set Message = '%s', LastModified = '%s' where ID = %i and Datum = '%s';"), message.c_str(), newTime.c_str(), mensaID, mensaDatumString.c_str())).Next();
+ return true;
+}
+
+bool db_insert_mensa_message(const int& mensaID, const ptime& mensaDatum, const string& message){
+ sqdb::Db db("../main.sqlite");
+ string newTime = to_simple_string(ptime(second_clock::local_time()));
+ string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + to_string(mensaDatum.date().day().as_number());
+ sqdb::QueryStr str;
+ db.Query(str.Format(SQDB_MAKE_TEXT("insert into mensa values(%i, '%s', '%s', '%s');"), mensaID, message.c_str(), mensaDatumString.c_str(), newTime.c_str())).Next();
+ return true;
+}
+
+Mensa_Essen db_get_mensa_message(const int& mensaID, const ptime& mensaDatum) {
+ Mensa_Essen out;
+ sqdb::Db db("../main.sqlite");
+
+ sqdb::QueryStr str;
+ string mensaDatumString = to_string(mensaDatum.date().year()) + "-" + mensaDatum.date().month().as_short_string() + "-" + to_string(mensaDatum.date().day().as_number());
+ sqdb::Statement s = db.Query(str.Format(SQDB_MAKE_TEXT("SELECT * from mensa WHERE Datum = '%s' AND ID = %i;"), mensaDatumString.c_str(), mensaID ));
+
+ try {
+ if(s.Next()) {
+ int ID = s.GetField(0);
+ string Message = s.GetField(1);
+ string Datum = s.GetField(2);
+ string LastModified = s.GetField(3);
+
+
+ ptime LMDate(time_from_string(LastModified));
+
+ out.ID = ID;
+ out.Message = Message;
+ out.Datum = mensaDatum;
+ out.LastModified = LMDate;
+ return out;
+ }
+ } catch (sqdb::Exception &exception){
+ log("Error code: " + to_string(exception.GetErrorCode()) + " Error Message: " + exception.GetErrorMsg() + "\n");
+ }
+ out.ID = -2;
+ return out;
+
+}
+
+string getEssen(const int mensa_ID, const int offset) {
+ Mensa_Essen Essen;
+ if (offset >= 0) {
+
+ if (offset > 28) {
+ return "Ich fühle mich geschmeichelt, dass du denkst, dass ich so weit in die Zukunft gucken kann (0 bis 28 Tage)";
+ }
+
+ ptime datumNow = second_clock::local_time();
+ ptime datumEssen = datumNow + (boost::gregorian::days(offset));
+
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+ if (Essen.ID == -2) { // SQL got nothing back :/
+ //log("sql got nothing back!");
+ if (downloadMensaFile(mensa_ID, datumEssen) != CURLE_OK){
+ log("Error downloading file");
+ return "Error downloading file!";
+ }
+ db_insert_mensa_message(mensa_ID, datumEssen, read_mensa_message_from_file(mensa_ID));
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+
+ string out;
+ switch (Essen.ID) {
+ case mensa::Adlershof :
+ out = "<b>Mensa Adlershof</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa ::Nord:
+ out = "<b>Mensa Nord</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa::Sued :
+ out = "<b>Mensa Sued</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ default:
+ out = "Mensa Unbekannt am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+
+ }
+ out += Essen.Message;
+ out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n";
+
+ return out;
+ }
+
+ if (Essen.LastModified + hours(2) < datumNow) { //LastModified is more than 2 hours old
+ if (downloadMensaFile(mensa_ID, datumEssen) != CURLE_OK) {
+ return "Error downloading file!";
+ }
+
+ db_update_mensa_message(mensa_ID, datumEssen, read_mensa_message_from_file(mensa_ID));
+ Essen = db_get_mensa_message(mensa_ID, datumEssen);
+ }
+ string out;
+ switch (Essen.ID) {
+ case mensa::Adlershof :
+ out = "<b>Mensa Adlershof</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa::Nord :
+ out = "<b>Mensa Nord</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ case mensa::Sued :
+ out = "<b>Mensa Sued</b> am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+ default:
+ out = "Mensa Unbekannt am " + to_string(Essen.Datum.date().day().as_number()) + "." + to_string(Essen.Datum.date().month().as_number()) + "." + to_string(Essen.Datum.date().year()) + ":";
+ break;
+
+ }
+ out += Essen.Message;
+ out += "\n (Stand: " + to_simple_string(Essen.LastModified) + ")\n";
+ return out;
+ } else return "Wer denkst du bin ich? Ein Zeitreisender?\nIch kann zwar in die Zukunft gucken, aber nur 28 Tage";
+
+}
+
+string read_mensa_message_from_file(const int& mensa_id_file) {
+ string s;
+ string out;
+ bool serious = false;
+
+ std::ifstream inFile(std::to_string(mensa_id_file));
+ while (std::getline(inFile, s)) {
+ //out += mensa;
+ if (s.find("15.png") != string::npos && serious) {
+ string vegan;
+
+ int vegan_byte[4] = {0xF0, 0x9F, 0x8C, 0xB1};
+ for (int i : vegan_byte) {
+ vegan += (char) i;
+ }
+
+ out += vegan;
+ }
+
+ if (s.find("1.png") != string::npos && serious) {
+ string vegetarian;
+
+ int vegetarian_byte[4] = {0xF0, 0x9F, 0x8C, 0xBD};
+ for (int i : vegetarian_byte) {
+ vegetarian += (char) i;
+ }
+
+ out += vegetarian;
+ }
+ if (s.find("splIcon") != string::npos && serious && s.find("1.png") == string::npos &&
+ s.find("15.png") == string::npos) {
+ string emptyEmoji;
+ int emptyEmoji_byte[6] = {0xE2, 0x98, 0xA3, 0xEF, 0xB8, 0x8F};
+ for (int i : emptyEmoji_byte) {
+ emptyEmoji += (char) i;
+ }
+
+ out += emptyEmoji;
+ }
+
+ if (s.find("splGroup\"") != string::npos) { //Getting the Category of the Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == '>') {
+ j = i + 1;
+ }
+ if (s.at(i) == '<' && j != 0) {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+
+ }
+ serious = (s == "Aktionen" || s == "Essen" || s == "Beilagen");
+ if (serious) {
+ out += "\n<b>" + s + "</b>\n";
+ }
+ }
+ if (s.find("class=\"bold\"") != string::npos && serious) { //Getting the Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == '>') {
+ j = i + 1;
+ //logStatus("Zeichen > gefunden in Position" + std::to_string(i));
+ }
+ if (s.at(i) == '<' && j != 0) {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+
+ }
+
+ out += "\t" + s;
+ }
+
+ if (s.find("euro;") != string::npos && serious) { //geting the price of each Meal
+ for (int i = 0, j = 0; (unsigned) i < s.length(); ++i) {
+ if (s.at(i) == ';') {
+ j = i + 1;
+ }
+ if (s.at(i) == '/') {
+ s = s.substr(j, i - j);
+ j = 0;
+ }
+ }
+ out += "\t<b>" + s + "</b>€\n";
+ }
+ }
+
+ if (out.empty()) {
+ return "Heute kein Essen";
+ }
+ return out;
+
+}
diff --git a/src/Utilities/GetEssen.hpp b/src/Utilities/GetEssen.hpp
new file mode 100644
index 0000000..813d115
--- /dev/null
+++ b/src/Utilities/GetEssen.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "sqdb.hpp"
+#include "Utilities/Utilities.hpp"
+#include "Utilities/Logger.hpp"
+//#include <tgbot/tgbot.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/date_time/gregorian/gregorian.hpp>
+#include <boost/date_time/date_formatting.hpp>
+#include <curl/curl.h>
+#include <iostream>
+#include <sys/stat.h>
+#include <iostream>
+#include <string>
+#include <mongoose/JsonResponse.h>
+#include <mongoose/Server.h>
+
+using namespace std;
+using namespace boost::posix_time;
+
+void returnEssen(Mongoose::Request &request, Mongoose::JsonResponse &response);
+
+string getEssen(int mensa_ID, int offset);
+
+string read_mensa_message_from_file(const int& mensa_id_file);
+
+struct Mensa_Essen{
+ int ID{191};
+ string Message{"nothing got back! There was an error!!"};
+ boost::posix_time::ptime Datum;
+ boost::posix_time::ptime LastModified;
+};
+enum mensa {
+ Adlershof = 191, Nord = 147, Sued = 367
+};
+
+
+
diff --git a/src/Utilities/Logger.cpp b/src/Utilities/Logger.cpp
new file mode 100644
index 0000000..333ae16
--- /dev/null
+++ b/src/Utilities/Logger.cpp
@@ -0,0 +1,48 @@
+#include "Logger.hpp"
+
+/*void Logger::Log(const TelegramMessage& message, LogLevel logLevel) {
+ Log("[" + message->from->username + "] " + message->text, logLevel);
+}*/
+
+void Logger::Log(const string& inMsg, LogLevel logLevel) {
+ string outMsg;
+
+#ifdef NDEBUG
+ if (logLevel == LogLevel::lDEBUG) {
+ // don't log debug messages in release mode
+ return;
+ }
+#endif
+
+ // prefix message with time
+ outMsg = "[" + NOW_STRING + "]";
+
+ // TODO: let at least some (ERROR, WARN) levels go to stderr, not stdout
+ // prefix log level - TODO: allow filtering based on globally set level?
+ switch (logLevel) {
+ case LogLevel::lSTATUS:
+ outMsg += " [STATUS] " + inMsg;
+ break;
+ case LogLevel::lDEBUG:
+ outMsg += " [DEBUG] " + inMsg;
+ break;
+ case LogLevel::lINFO:
+ outMsg += " [INFO] " + inMsg;
+ break;
+ case LogLevel::lWARN:
+ outMsg += " [WARN] " + inMsg;
+ break;
+ case LogLevel::lERROR:
+ outMsg += " [ERROR] " + inMsg;
+ break;
+ }
+
+ Print(outMsg);
+}
+
+void Logger::Print(const string& msg) {
+ // console output
+ cout << msg << endl;
+
+ // file output | TODO: Logger needs state for that - or at least needs to pretend to have one
+} \ No newline at end of file
diff --git a/src/Utilities/Logger.hpp b/src/Utilities/Logger.hpp
new file mode 100644
index 0000000..f319d93
--- /dev/null
+++ b/src/Utilities/Logger.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <string>
+#include <iostream>
+
+
+#include "Utilities.hpp"
+
+using std::string;
+using std::cout;
+using std::endl;
+
+#define logWarn(x) Logger::Log(x, Logger::LogLevel::lWARN)
+#define logDebug(x) Logger::Log(x, Logger::LogLevel::lDEBUG)
+#define logError(x) Logger::Log(x, Logger::LogLevel::lERROR)
+
+#define logStatus(x) Logger::Log(x, Logger::LogLevel::lSTATUS)
+
+#define log(x) logI(x)
+#define logI(x) Logger::Log(x, Logger::LogLevel::lINFO)
+
+class Logger {
+public:
+ enum class LogLevel {
+ lSTATUS, // general bot status messages, everything it logs o it's own
+ lDEBUG, // messages logged for development purposes
+ lINFO, // messages logged on certain events (eg. command executed)
+ lWARN,
+ lERROR
+ };
+
+ static void Log(const string& msg, LogLevel logLevel);
+ //static void Log(const TelegramMessage& message, LogLevel logLevel);
+
+private:
+ static void Print(const string& msg);
+};
+
diff --git a/src/Utilities/Utilities.hpp b/src/Utilities/Utilities.hpp
new file mode 100644
index 0000000..0c9f435
--- /dev/null
+++ b/src/Utilities/Utilities.hpp
@@ -0,0 +1,142 @@
+#pragma once
+
+#include <ctime>
+#include <string>
+#include <curl/curl.h>
+#include <boost/date_time.hpp>
+
+#define NOW std::time(0)
+#define LOG_TIME_FORMAT "%y-%m-%d %H:%M:%S"
+#define HR_TIME_FORMAT "%d.%m.%y, %H:%M:%S UHR"
+#define NOW_STRING TimeStampToHReadble(NOW, false)
+
+static std::string TimeStampToHReadble(const time_t rawTime, bool HR_READABLE) {
+ struct tm *dt;
+ char buffer[30];
+ dt = localtime(&rawTime);
+ if(HR_READABLE) {
+ strftime(buffer, sizeof(buffer), HR_TIME_FORMAT, dt);
+ } else {
+ strftime(buffer, sizeof(buffer), LOG_TIME_FORMAT, dt);
+ }
+ return std::string(buffer);
+}
+
+#include <fstream>
+static std::string getApiTokenFromFile(const std::string& filePath) {
+ std::string token = "NO-TOKEN";
+
+ std::ifstream inFile(filePath);
+
+ if (inFile) {
+ // TODO: validate, at least a little - currently we just assume the file contains the correct data
+ std::getline(inFile, token);
+ } else {
+ return "";
+ }
+
+ return token;
+}
+
+#ifdef NDEBUG
+
+static std::string getProductionApiToken() {
+ return getApiTokenFromFile("../release.api");
+}
+
+#else
+
+static std::string getDebugApiToken() {
+ return getApiTokenFromFile("../debug.api");
+}
+
+#endif
+
+static CURLcode downloadFile(const std::string& url, const std::string& fileName) {
+
+ CURL *curl;
+ FILE *fp;
+ CURLcode res;
+ curl = curl_easy_init();
+
+ if (curl) {
+ fp = fopen(fileName.c_str(), "wb");
+
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl, CURLOPT_USERAGENT,
+ "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36"); //so apparently the Website stw.berlin blocks the IPwhen accessing it very often with the bot...
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK){
+ //TODO: return int as status instead of boolean - maybe even this curl error?
+ return CURLE_COULDNT_CONNECT;
+ }
+ curl_easy_cleanup(curl);
+ fclose(fp);
+ return CURLE_OK;
+ } else {
+ return CURLE_COULDNT_CONNECT;
+ }
+}
+
+static CURLcode downloadMensaFile(const int& ID, const boost::posix_time::ptime& datum) {
+
+ std::string datumString = std::to_string(datum.date().year()) + "-" + std::to_string(datum.date().month().as_number()) + "-" + std::to_string(datum.date().day());
+
+ std::string postfield = "resources_id=" + std::to_string(ID) + "&date=" + datumString;
+
+ CURL *curl;
+ FILE *fp;
+ CURLcode res;
+ curl = curl_easy_init();
+
+ if (curl) {
+ fp = fopen(std::to_string(ID).c_str(), "wb");
+
+ struct curl_slist *slist1;
+
+ slist1 = nullptr;
+ slist1 = curl_slist_append(slist1, "Cookie: seitenbereich=mensen; _ga=GA1.2.1719763776.1545837849; filterkennz=[]; PHPSESSID=9fa00137a2ad065fe595196ddb34e0ba; _gid=GA1.2.226483670.1549982576; loadDataForResource=191; storedPathname=/mensen.html; _gat=1");
+ slist1 = curl_slist_append(slist1, "Origin: https://www.stw.berlin");
+ slist1 = curl_slist_append(slist1, "Accept-Encoding: gzip, deflate, br");
+ slist1 = curl_slist_append(slist1, "Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7");
+ slist1 = curl_slist_append(slist1, "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36");
+ slist1 = curl_slist_append(slist1, "Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
+ slist1 = curl_slist_append(slist1, "Accept: */*");
+ slist1 = curl_slist_append(slist1, "Referer: https://www.stw.berlin/mensen.html");
+ slist1 = curl_slist_append(slist1, "X-Requested-With: XMLHttpRequest");
+ slist1 = curl_slist_append(slist1, "Connection: keep-alive");
+ slist1 = curl_slist_append(slist1, "DNT: 1");
+
+ curl = curl_easy_init();
+ curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 102400L);
+ curl_easy_setopt(curl, CURLOPT_URL, "https://www.stw.berlin/xhr/speiseplan-wochentag.html");
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfield.c_str());
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)32);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist1);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.63.0");
+ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 50L);
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
+ curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
+ curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/home/max/.ssh/known_hosts");
+ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
+
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+ res = curl_easy_perform(curl);
+ if (res != CURLE_OK){
+ //TODO: return int as status instead of boolean - maybe even this curl error?
+ return res;
+ }
+ curl_easy_cleanup(curl);
+ curl = nullptr;
+ curl_slist_free_all(slist1);
+ slist1 = nullptr;
+ fclose(fp);
+ return CURLE_OK;
+ } else {
+ return CURLE_FAILED_INIT;
+ }
+}
diff --git a/src/Utilities/sqdb.cpp b/src/Utilities/sqdb.cpp
new file mode 100644
index 0000000..0b88139
--- /dev/null
+++ b/src/Utilities/sqdb.cpp
@@ -0,0 +1,506 @@
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+
+#include <cstdlib>
+
+#include "sqdb.hpp"
+
+#include <unistd.h>
+
+using namespace sqdb;
+
+Exception::Exception(sqlite3* db)
+{
+ m_errorCode = sqlite3_errcode(db);
+ auto* c = (SQDB_CHAR*)
+#ifdef SQDB_UTF8
+ sqlite3_errmsg
+#else
+ sqlite3_errmsg16
+#endif
+ (db);
+ m_errorMsg = SQDB_STRDUP(c);
+}
+
+Exception::Exception(sqlite3* db, int errorCode)
+ : m_errorCode(errorCode)
+{
+ auto* c = (SQDB_CHAR*)
+#ifdef SQDB_UTF8
+ sqlite3_errmsg
+#else
+ sqlite3_errmsg16
+#endif
+ (db);
+ m_errorMsg = SQDB_STRDUP(c);
+}
+
+Exception::Exception(const SQDB_CHAR* errorMsg)
+ : m_errorCode(-1)
+{
+ m_errorMsg = SQDB_STRDUP(errorMsg);
+}
+
+Exception::~Exception()
+{
+ free(m_errorMsg);
+}
+
+int Exception::GetErrorCode() const
+{
+ return m_errorCode;
+}
+
+const SQDB_CHAR* Exception::GetErrorMsg() const
+{
+ return m_errorMsg;
+}
+
+RefCount::RefCount()
+ : m_refCount(nullptr)
+{
+}
+
+RefCount::RefCount(const RefCount& x)
+ : m_refCount(x.m_refCount)
+{
+}
+
+RefCount& RefCount::operator=(const RefCount& x)
+{
+ if ( this != &x )
+ {
+ m_refCount = x.m_refCount;
+ }
+ return *this;
+}
+
+void RefCount::IncRef()
+{
+ if ( !m_refCount )
+ {
+ m_refCount = new unsigned;
+ *m_refCount = 0;
+ }
+ ++*m_refCount;
+}
+
+unsigned RefCount::DecRef()
+{
+ assert(m_refCount);
+ unsigned value = --*m_refCount;
+ if ( value == 0 )
+ {
+ delete m_refCount;
+ m_refCount = nullptr;
+ }
+ return value;
+}
+
+Blob::Blob(const void* data, int size)
+ : m_size(size)
+{
+ m_data = new char[size];
+ std::uninitialized_copy((char*)data, (char*)data + size, m_data);
+ IncRef();
+}
+
+Blob::Blob(const Blob& x)
+ : RefCount(x), m_data(x.m_data), m_size(x.m_size)
+{
+ IncRef();
+}
+
+Blob& Blob::operator=(const Blob& x)
+{
+ if ( this != &x )
+ {
+ RefCount::operator=(x);
+ IncRef();
+ m_data = x.m_data;
+ m_size = x.m_size;
+ }
+ return *this;
+}
+
+int Blob::GetSize() const
+{
+ return m_size;
+}
+
+const char* Blob::GetData() const
+{
+ return m_data;
+}
+
+Blob::~Blob()
+{
+ if ( DecRef() == 0 )
+ {
+ delete[] m_data;
+ }
+}
+
+Convertor::Convertor(sqlite3* db, sqlite3_stmt* stmt, int field)
+ : m_db(db), m_stmt(stmt), m_field(field)
+{
+}
+
+Convertor::operator int() const
+{
+ return GetInt();
+}
+
+Convertor::operator unsigned long() const
+{
+ return GetUnsignedLong();
+}
+
+Convertor::operator long long() const
+{
+ return GetLongLong();
+}
+
+Convertor::operator double() const
+{
+ return GetDouble();
+}
+
+Convertor::operator SQDB_STD_STRING() const
+{
+ return GetString();
+}
+
+Convertor::operator const SQDB_CHAR*() const
+{
+ return GetText();
+}
+
+Convertor::operator Blob() const
+{
+ return GetBlob();
+}
+
+int Convertor::GetInt() const
+{
+ assert(m_stmt);
+ return sqlite3_column_int(m_stmt, m_field);
+}
+
+unsigned long Convertor::GetUnsignedLong() const
+{
+ assert(m_stmt);
+ const char* data = (char*) sqlite3_column_blob(m_stmt, m_field);
+ return strtoul(data, nullptr, 10);
+}
+
+long long Convertor::GetLongLong() const
+{
+ assert(m_stmt);
+ return sqlite3_column_int64(m_stmt, m_field);
+}
+
+double Convertor::GetDouble() const
+{
+ assert(m_stmt);
+ return sqlite3_column_double(m_stmt, m_field);
+}
+
+SQDB_STD_STRING Convertor::GetString() const
+{
+ assert(m_stmt);
+ const auto* result = (const SQDB_CHAR*)
+#ifdef SQDB_UTF8
+ sqlite3_column_text
+#else
+ sqlite3_column_text16
+#endif
+ (m_stmt, m_field);
+ return result;
+}
+
+const SQDB_CHAR* Convertor::GetText() const
+{
+ assert(m_stmt);
+ const auto* result = (const SQDB_CHAR*)
+#ifdef SQDB_UTF8
+ sqlite3_column_text
+#else
+ sqlite3_column_text16
+#endif
+ (m_stmt, m_field);
+ return result;
+}
+
+Blob Convertor::GetBlob() const
+{
+ assert(m_stmt);
+ const void* data = sqlite3_column_blob(m_stmt, m_field);
+ int size = sqlite3_column_bytes(m_stmt, m_field);
+ return Blob(data, size);
+}
+
+Statement::Statement(sqlite3* db, sqlite3_stmt* stmt)
+ : RefCount(), m_db(db), m_stmt(stmt), m_needReset(false)
+{
+ IncRef();
+}
+
+Statement::Statement(const Statement& x)
+ : RefCount(x), m_db(x.m_db), m_stmt(x.m_stmt), m_needReset(false)
+{
+ IncRef();
+}
+
+Statement& Statement::operator=(const Statement& x)
+{
+ if ( this != &x )
+ {
+ RefCount::operator=(x);
+ IncRef();
+ m_db = x.m_db;
+ m_stmt = x.m_stmt;
+ m_needReset = x.m_needReset;
+ }
+ return *this;
+}
+
+bool Statement::Next() {
+ assert(m_stmt);
+ int ret = SQLITE_BUSY;
+
+ while (ret == SQLITE_BUSY) {
+ ret = sqlite3_step(m_stmt);
+ m_needReset = true;
+ if ( ret == SQLITE_DONE ) {
+ return false;
+ } else if ( ret == SQLITE_ROW ) {
+ return true;
+ } else if ( ret == SQLITE_BUSY ) {
+ sleep(1);
+ continue;
+ } else {
+ throw Exception(m_db, ret);
+ }
+ }
+ return false;
+}
+
+Convertor Statement::GetField(int field) const
+{
+ return {m_db, m_stmt, field};
+}
+
+Statement::~Statement()
+{
+ if ( DecRef() == 0 )
+ {
+ sqlite3_finalize(m_stmt);
+ }
+}
+
+void Statement::BindBlob(int i, const void* value, int n)
+{
+ if ( m_needReset )
+ Reset();
+ DoBind(i, value, n);
+}
+
+void Statement::BindNull(int i)
+{
+ if ( m_needReset )
+ Reset();
+ DoBind(i);
+}
+
+void Statement::DoBind(int i, int value)
+{
+ const int ret = sqlite3_bind_int(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, long long value)
+{
+ const int ret = sqlite3_bind_int64(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, double value)
+{
+ const int ret = sqlite3_bind_double(m_stmt, i, value);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const SQDB_STD_STRING& value)
+{
+ const int ret =
+#ifdef SQDB_UTF8
+ sqlite3_bind_text
+#else
+ sqlite3_bind_text16
+#endif
+ (m_stmt, i, value.c_str(), value.size() * sizeof(SQDB_STD_STRING::value_type), SQLITE_TRANSIENT);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const SQDB_CHAR* value)
+{
+ const int len = SQDB_STRLEN(value);
+
+ const int ret =
+#ifdef SQDB_UTF8
+ sqlite3_bind_text
+#else
+ sqlite3_bind_text16
+#endif
+ (m_stmt, i, value, len * sizeof(SQDB_CHAR), SQLITE_TRANSIENT);
+
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i, const void* value, int n)
+{
+ const int ret = sqlite3_bind_blob(m_stmt, i, value, n, SQLITE_TRANSIENT);
+ CHECK(m_db, ret);
+}
+
+void Statement::DoBind(int i)
+{
+ const int ret = sqlite3_bind_null(m_stmt, i);
+ CHECK(m_db, ret);
+}
+
+void Statement::Reset()
+{
+ assert(m_needReset);
+ assert(m_stmt);
+
+ sqlite3_reset(m_stmt);
+ m_needReset = false;
+}
+
+QueryStr::QueryStr()
+ : m_buf(nullptr)
+{
+}
+
+const SQDB_CHAR* QueryStr::Format(const SQDB_CHAR* fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+#ifdef SQDB_UTF8
+ sqlite3_free(m_buf);
+ m_buf = sqlite3_vmprintf(fmt, va);
+#else
+ free(m_buf);
+ int len = _vscwprintf(fmt, va) + 1;
+ m_buf = (SQDB_CHAR*)malloc(len * sizeof(SQDB_CHAR));
+ vswprintf(m_buf, len, fmt, va);
+#endif
+
+ va_end(va);
+
+ return m_buf;
+}
+
+const SQDB_CHAR* QueryStr::Get() const
+{
+ return m_buf;
+}
+
+QueryStr::~QueryStr()
+{
+#ifdef SQDB_UTF8
+ sqlite3_free(m_buf);
+#else
+ free(m_buf);
+#endif
+}
+
+Db::Db(const SQDB_CHAR* fileName)
+ : RefCount()
+{
+#ifdef SQDB_UTF8
+ const int ret = sqlite3_open(fileName, &m_db);
+#else
+ const int ret = sqlite3_open16(fileName, &m_db);
+#endif
+ CHECK(m_db, ret);
+
+ IncRef();
+}
+
+void Db::BeginTransaction()
+{
+ Query(SQDB_MAKE_TEXT("BEGIN;")).Next();
+}
+
+void Db::CommitTransaction()
+{
+ Query(SQDB_MAKE_TEXT("COMMIT;")).Next();
+}
+
+void Db::RollbackTransaction()
+{
+ Query(SQDB_MAKE_TEXT("ROLLBACK;")).Next();
+}
+
+bool Db::TableExists(const SQDB_CHAR* tableName)
+{
+ QueryStr str;
+ Statement s =
+ Query(
+ str.Format(SQDB_MAKE_TEXT("select count(*) from sqlite_master where type='table' and name='%s';"), tableName));
+ s.Next();
+ const int count = s.GetField(0);
+ return count > 0;
+}
+
+Statement Db::Query(const SQDB_CHAR* queryStr)
+{
+ sqlite3_stmt* stmt = nullptr;
+#ifdef SQDB_UTF8
+ const int ret = sqlite3_prepare(m_db, queryStr, -1, &stmt, nullptr);
+#else
+ const int ret = sqlite3_prepare16(m_db, queryStr, -1, &stmt, NULL);
+#endif
+ CHECK(m_db, ret);
+
+ return Statement(m_db, stmt);
+}
+
+long long Db::LastId()
+{
+ long long ret = sqlite3_last_insert_rowid(m_db);
+ return ret;
+}
+
+Db::Db(const Db& x)
+ : RefCount(x),
+ m_db(x.m_db)
+{
+ IncRef();
+}
+
+Db& Db::operator=(const Db& x)
+{
+ if ( this != &x )
+ {
+ RefCount::operator=(x);
+
+ IncRef();
+ m_db = x.m_db;
+ }
+ return *this;
+}
+
+Db::~Db()
+{
+ if ( DecRef() == 0 )
+ {
+ sqlite3_close(m_db);
+ }
+}
+
diff --git a/src/Utilities/sqdb.hpp b/src/Utilities/sqdb.hpp
new file mode 100644
index 0000000..ea38787
--- /dev/null
+++ b/src/Utilities/sqdb.hpp
@@ -0,0 +1,207 @@
+#pragma once
+
+#include <string>
+
+#include "sqlite3.h"
+
+#ifdef _WIN32
+# include <tchar.h>
+# define SQDB_MAKE_TEXT(x) _TEXT(x)
+# define SQDB_STRLEN _tcslen
+# define SQDB_STRDUP _tcsdup
+#else
+# define SQDB_MAKE_TEXT(x) (x)
+# define SQDB_STRLEN strlen
+# define SQDB_STRDUP strdup
+#endif
+
+#if !defined(SQDB_UTF16) && !defined(SQDB_UTF8)
+# ifdef _WIN32
+# if defined(UNICODE) || defined(_UNICODE)
+# define SQDB_UTF16
+# else
+# define SQDB_UTF8
+# endif
+# else
+# define SQDB_UTF8
+# endif
+#endif
+
+#ifdef SQDB_UTF8
+# define SQDB_CHAR char
+# define SQDB_STD_STRING std::string
+#endif
+
+#ifdef SQDB_UTF16
+# define SQDB_CHAR TCHAR
+# define SQDB_STD_STRING std::wstring
+#endif
+
+namespace sqdb
+{
+
+ class Exception
+ {
+ public:
+ Exception(sqlite3* db);
+
+ Exception(sqlite3* db, int errorCode);
+
+ Exception(const SQDB_CHAR* errorMsg);
+
+ ~Exception();
+
+ int GetErrorCode() const;
+
+ const SQDB_CHAR* GetErrorMsg() const;
+ private:
+ int m_errorCode;
+ SQDB_CHAR* m_errorMsg;
+ };
+
+#define CHECK(db, returnCode) \
+ if ( (returnCode) != SQLITE_OK ) throw Exception(db, returnCode)
+
+ class RefCount
+ {
+ protected:
+ RefCount();
+
+ RefCount(const RefCount& x);
+ RefCount& operator=(const RefCount& x);
+
+ void IncRef();
+ unsigned DecRef();
+
+ private:
+ unsigned* m_refCount;
+ };
+
+ class Blob : public RefCount
+ {
+ public:
+ Blob(const void* data, int size);
+
+ Blob(const Blob& x);
+ Blob& operator=(const Blob& x);
+
+ int GetSize() const;
+ const char* GetData() const;
+
+ ~Blob();
+
+ private:
+ char* m_data;
+ int m_size;
+ };
+
+ class Convertor
+ {
+ public:
+ Convertor(sqlite3* db, sqlite3_stmt* stmt, int field);
+
+ operator int() const;
+ operator unsigned long() const;
+ operator long long() const;
+ operator double() const;
+ operator SQDB_STD_STRING() const;
+ operator const SQDB_CHAR*() const;
+ operator Blob() const;
+
+ int GetInt() const;
+ unsigned long GetUnsignedLong() const;
+ long long GetLongLong() const;
+ double GetDouble() const;
+ SQDB_STD_STRING GetString() const;
+ const SQDB_CHAR* GetText() const;
+ Blob GetBlob() const;
+
+ private:
+ sqlite3* m_db;
+ sqlite3_stmt* m_stmt;
+ int m_field;
+ };
+
+ class Statement : public RefCount
+ {
+ public:
+ Statement(sqlite3* db, sqlite3_stmt* stmt);
+
+ Statement(const Statement& x);
+ Statement& operator=(const Statement& x);
+
+ bool Next();
+ Convertor GetField(int field) const;
+
+ template<class T>
+ void Bind(int i, const T& value)
+ {
+ if ( m_needReset )
+ Reset();
+ DoBind(i, value);
+ }
+
+ void BindBlob(int i, const void* value, int n);
+ void BindNull(int i);
+
+ ~Statement();
+
+ private:
+ void DoBind(int i, int value);
+ void DoBind(int i, long long value);
+ void DoBind(int i, double value);
+ void DoBind(int i, const SQDB_STD_STRING& value);
+ void DoBind(int i, const SQDB_CHAR* value);
+
+ // Bind blob.
+ void DoBind(int i, const void* value, int n);
+
+ // Bind null.
+ void DoBind(int i);
+
+ // Reset binders so that new values can be bound.
+ void Reset();
+
+ sqlite3* m_db;
+ sqlite3_stmt* m_stmt;
+ bool m_needReset;
+ };
+
+ class QueryStr
+ {
+ public:
+ QueryStr();
+
+ const SQDB_CHAR* Format(const SQDB_CHAR* fmt, ...);
+
+ const SQDB_CHAR* Get() const;
+
+ ~QueryStr();
+
+ private:
+ SQDB_CHAR* m_buf;
+ };
+
+ class Db : public RefCount
+ {
+ public:
+ Db(const SQDB_CHAR* fileName);
+
+ void BeginTransaction();
+ void CommitTransaction();
+ void RollbackTransaction();
+
+ bool TableExists(const SQDB_CHAR* tableName);
+ Statement Query(const SQDB_CHAR* queryStr);
+ long long LastId();
+
+ Db(const Db& x);
+ Db& operator=(const Db& x);
+
+ ~Db();
+
+ private:
+ sqlite3* m_db;
+ };
+
+}
diff --git a/src/main.cpp b/src/main.cpp
index b11a8e7..8a08b4a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,33 +1,45 @@
-#include "N-Commands/KlingerHandler.h"
-#include "N-Commands/RelationshipHandler.h"
+#include "N-Commands/KlingerHandler.hpp"
+#include "N-Commands/RelationshipHandler.hpp"
+#include "Utilities/GetEssen.hpp"
+#include "Utilities/Logger.hpp"
+#include "Utilities/Utilities.hpp"
+#include "Utilities/sqdb.hpp"
#include <mongoose/Server.h>
#include <mongoose/WebController.h>
+#include <mongoose/JsonController.h>
using namespace std;
using namespace Mongoose;
-class MyController : public WebController
+class MyController : public JsonController
{
public:
- void hello(Request &request, StreamResponse &response)
+ void hello(Request &request, JsonResponse &response)
{
- response << "Hello " << htmlEntities(request.get("name", "... what's your name ?")) << endl;
+ response["text"] = "Hello " + htmlEntities(request.get("name", "... what's your name ?")) + "\n";
+ response["success"] = "1";
+ response["session"] = "NULL";
}
- void klinger(Request &request, StreamResponse &response){
+ void klinger(Request &request, JsonResponse &response){
KlingerHandler klinger;
klinger.onCall(request, response);
}
- void relation(Request &request, StreamResponse &response){
+ void relation(Request &request, JsonResponse &response){
RelationshipHandler relation;
relation.onCall(request, response);
}
+ void mensa(Request &request, JsonResponse &response){
+ returnEssen(request, response);
+ }
void setup()
{
- addRoute("GET", "/hello", MyController, hello);
- addRoute("GET", "/klinger", MyController, klinger);
- addRoute("GET", "/relation", MyController, relation);
+ setPrefix("/api/v1");
+ addRouteResponse("GET", "/hello", MyController, hello, JsonResponse);
+ addRouteResponse("GET", "/klinger", MyController, klinger, JsonResponse);
+ addRouteResponse("GET", "/relation", MyController, relation, JsonResponse);
+ addRouteResponse("GET", "/mensa", MyController, mensa, JsonResponse);
}
};