diff options
author | Coleman McFarland <coleman@de.limited> | 2024-04-14 14:32:02 -0400 |
---|---|---|
committer | Coleman McFarland <coleman@de.limited> | 2024-04-14 19:14:15 -0400 |
commit | 2a41ec04bd665bedcd2ff289ca91071c819406bb (patch) | |
tree | b189fc35cefd86e096649dc1c4105d6d3c81089e |
First commit of XMPP bot in C/C++
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | bot.cpp | 14 | ||||
-rw-r--r-- | bot.h | 20 | ||||
-rw-r--r-- | main.cpp | 258 | ||||
-rw-r--r-- | meson.build | 12 |
5 files changed, 308 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f32893 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +builddir +buildDir + @@ -0,0 +1,14 @@ +// +// Created by coleman on 4/13/24. +// + +#include "bot.h" +#include <strophe.h> + + +void *bot::connect() { + xmpp_ctx_t *ctx; + + xmpp_conn_new(ctx); + return nullptr; +} @@ -0,0 +1,20 @@ +// +// Created by coleman on 4/13/24. +// + +#ifndef CHATOPS_BOT_H +#define CHATOPS_BOT_H + +#include <strophe.h> + +class bot { + +public: + void *connect(); + +private: + +}; + + +#endif //CHATOPS_BOT_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..ea4a3ef --- /dev/null +++ b/main.cpp @@ -0,0 +1,258 @@ +#include <cstdio> +#include <cstdlib> +#include <strophe.h> +#include <cstring> +#include <string> +#include <map> + +static int reconnect; +int handle_message(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata) { + printf("Received message!\n"); + return 1; +} + +enum StanzaType { + UNKNOWN, + GROUP_CHAT, + DIRECT_MESSAGE +}; + +std::map<std::string, StanzaType> stanzaTypeMap = { + {"groupchat", GROUP_CHAT}, + {"chat", DIRECT_MESSAGE}, +}; + +StanzaType stanza_type_from_str(const char *s) { + std::string type(s); + auto it = stanzaTypeMap.find(type); + if (it != stanzaTypeMap.end()) { + return it->second; + } + // Unknown type; caller should log + return UNKNOWN; +} + +int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +{ + xmpp_stanza_t *reply, *query, *name, *version, *text; + const char *ns; + xmpp_ctx_t *ctx; + ctx = (xmpp_ctx_t *) userdata; + + printf("Received version request from %s\n", xmpp_stanza_get_from(stanza)); + + reply = xmpp_stanza_reply(stanza); + xmpp_stanza_set_type(reply, "result"); + + query = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(query, "query"); + ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)); + if (ns) { + xmpp_stanza_set_ns(query, ns); + } + + name = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(name, "name"); + xmpp_stanza_add_child(query, name); + xmpp_stanza_release(name); + + text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(text, "libstrophe example bot"); + xmpp_stanza_add_child(name, text); + xmpp_stanza_release(text); + + version = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(version, "version"); + xmpp_stanza_add_child(query, version); + xmpp_stanza_release(version); + + text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(text, "1.0"); + xmpp_stanza_add_child(version, text); + xmpp_stanza_release(text); + + xmpp_stanza_add_child(reply, query); + xmpp_stanza_release(query); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + return 1; +} + +static int _quit_handler(xmpp_conn_t *conn, void *userdata) +{ + (void)userdata; + xmpp_disconnect(conn); + return 0; +} + +int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +{ + xmpp_ctx_t *ctx; + ctx = (xmpp_ctx_t *) userdata; + xmpp_stanza_t *body, *reply; + const char *type; + char *intext, *replytext; + + body = xmpp_stanza_get_child_by_name(stanza, "body"); + if (body == nullptr) + return 1; + type = xmpp_stanza_get_type(stanza); + if (type != nullptr && strcmp(type, "error") == 0) + return 1; + // type: groupchat + intext = xmpp_stanza_get_text(body); + // contents of text in intext + printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza), + intext); + + // Prevent infinite loop: ignore messages from self! + const char *fromtext = xmpp_stanza_get_from(stanza); + if (strcmp(fromtext, "system-monitor@rooms.de.limited/devil") == 0) { + // nothing to free... + return 1; + } + + reply = xmpp_stanza_reply(stanza); + switch (stanza_type_from_str(type)) { + case UNKNOWN: + break; + case GROUP_CHAT: + xmpp_stanza_set_type(reply, type); + xmpp_stanza_set_attribute(reply, "to", "system-monitor@rooms.de.limited"); + break; + case DIRECT_MESSAGE: + xmpp_stanza_set_type(reply, type); + break; + default: + // should never happen + break; + } + + // Read text and reply + if (strcmp(intext, "quit") == 0) { + replytext = strdup("bye!"); + xmpp_timed_handler_add(conn, _quit_handler, 500, nullptr); + } else if (strcmp(intext, "reconnect") == 0) { + replytext = strdup("alright, let's see what happens!"); + reconnect = 1; + xmpp_timed_handler_add(conn, _quit_handler, 500, nullptr); + } else { + replytext = (char *)malloc(strlen(" to you too!") + strlen(intext) + 1); + strcpy(replytext, intext); + strcat(replytext, " to you too!"); + } + xmpp_free(ctx, intext); + xmpp_message_set_body(reply, replytext); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + free(replytext); + + return 1; +} + +void join_muc(xmpp_conn_t *conn, const char *room_jid, const char *nick) { + xmpp_stanza_t *presence, *x, *history; + + // Build presence stanza + presence = xmpp_stanza_new(xmpp_conn_get_context(conn)); + xmpp_stanza_set_name(presence, "presence"); + xmpp_stanza_set_attribute(presence, "to", room_jid); + + // Add x element + x = xmpp_stanza_new(xmpp_conn_get_context(conn)); + xmpp_stanza_set_name(x, "x"); + xmpp_stanza_set_ns(x, "http://jabber.org/protocol/muc"); + + // Add history element to limit chat history + history = xmpp_stanza_new(xmpp_conn_get_context(conn)); + xmpp_stanza_set_name(history, "history"); + xmpp_stanza_set_attribute(history, "maxchars", "0"); + xmpp_stanza_add_child(x, history); + xmpp_stanza_add_child(presence, x); + + xmpp_send(conn, presence); + + xmpp_stanza_release(presence); +} + +void conn_handler(xmpp_conn_t *conn, + xmpp_conn_event_t status, + int error, + xmpp_stream_error_t *stream_error, + void *userdata) +{ + xmpp_ctx_t *ctx; + ctx = (xmpp_ctx_t *) userdata; + + (void)error; + (void)stream_error; + + if (status == XMPP_CONN_CONNECT) { + xmpp_stanza_t *pres; + fprintf(stderr, "DEBUG: connected\n"); + + // Join MUC + join_muc(conn, "system-monitor@rooms.de.limited/devil", "devil"); + + xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", nullptr, + ctx); + xmpp_handler_add(conn, message_handler, nullptr, "message", nullptr, ctx); + + /* Send initial <presence/> so that we appear online to contacts */ + pres = xmpp_presence_new(ctx); + xmpp_send(conn, pres); + xmpp_stanza_release(pres); + } else { + fprintf(stderr, "DEBUG: client -------------- disconnected\n"); + xmpp_stop(ctx); + } +} + +int main(int argc, char **argv) { + xmpp_ctx_t *ctx; + xmpp_conn_t *conn; + xmpp_log_t *log; + + // Initialize library + xmpp_initialize(); + + // Create a context + log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); + ctx = xmpp_ctx_new(nullptr, log); + + // Create a connection + conn = xmpp_conn_new(ctx); + xmpp_conn_set_flags(conn, XMPP_CONN_FLAG_ENABLE_COMPRESSION); + + char *pass; + if ((pass = getenv("CHATOPS_PASSWORD")) == nullptr) { + printf("must set CHATOPS_PASSWORD\n"); + exit(2); + } + char *jid; + if ((jid = getenv("CHATOPS_JID")) == nullptr) { + jid = (char *)"devil@de.limited"; + } + xmpp_conn_set_jid(conn, jid); + xmpp_conn_set_pass(conn, pass); + + // Add handlers +// xmpp_handler_add(conn, handle_message, nullptr, "message", nullptr, ctx); + + // Connect and authenticate + if (xmpp_connect_client(conn, nullptr, 0, conn_handler, ctx) == XMPP_EOK) { + + + // Start the event loop + xmpp_run(ctx); + } + + // Release resources + xmpp_conn_release(conn); + xmpp_ctx_free(ctx); + xmpp_shutdown(); + + return 0; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..23ecd05 --- /dev/null +++ b/meson.build @@ -0,0 +1,12 @@ +project('chatops', 'cpp', + version : '1.0.0', + default_options : ['warning_level=3', 'cpp_std=c++2a']) + +libstrophe = dependency('libstrophe') + +deps = [libstrophe] +srcs = ['main.cpp', 'bot.cpp'] + +chatops = executable('chatops', srcs, install : true, dependencies : deps) + +test('test', chatops) |