Loadable Modules - Extending the mesibo Platform
mesibo On-Premise's loadable module architecture lets you extend the platform with your own code — intercepting messages, adding custom processing logic, and integrating with any external service or backend. You can build profanity filters, message translators, AI-powered chatbots connected to LLMs or services like Dialogflow, vector database lookups, IoT and robotics control systems, analytics pipelines, and more. All of this runs inside your own infrastructure with full control over your data.
Modules are built as C/C++ shared libraries (.so files) and loaded into mesibo at runtime — no recompilation of mesibo itself is required.
Prerequisites
- Running mesibo On-Premise
- Knowledge of building and deploying C/C++ shared libraries
What is a mesibo Module?
A mesibo module is a message processor that intercepts each message passing through the server and decides what to do with it:
- Pass — forward the message to the recipient unchanged
- Consume — drop the message, preventing it from reaching the recipient
- Process — modify, analyze, or respond to the message before forwarding or consuming it
For example:
- A profanity filter module inspects each message and consumes those containing objectionable content
- A translation module translates each message before forwarding it to the recipient
- A chatbot module sends incoming messages to an AI service and replies to the sender with the response
The logic inside each module is entirely yours — mesibo provides the hooks, your code provides the behavior.
Modules are built as shared libraries (.so files) and loaded by the mesibo On-Premise server at runtime.

How Modules Work
When mesibo receives a message, it invokes the callback functions defined in each loaded module, in the order the modules were loaded. Each module can inspect the message and return a result indicating whether to pass or consume it.

Multiple modules can be loaded simultaneously, each with its own configuration. The same module can also be loaded more than once with different configurations. Load order and configuration for all modules are specified in /etc/mesibo/mesibo.conf.

Anatomy of a mesibo Module
A mesibo module has three components:
- An initialization function called once by mesibo after loading the module
- A set of callback functions defined by the module and called by mesibo on events (incoming messages, calls, logins, etc.)
- A module definition structure that declares the callback functions and module metadata

Module Definition
A module is described by the mesibo_module_t structure:
typedef struct mesibo_module_s {
mesibo_uint_t version; /* mesibo module API version */
mesibo_uint_t flags; /* module flags */
const char *name; /* module name */
const char *description; /* module description */
void *ctx; /* module context */
module_config_t *config; /* module configuration */
mesibo_int_t (*on_cleanup)(mesibo_module_t *mod);
mesibo_int_t (*on_message)(mesibo_module_t *mod, mesibo_message_params_t *params,
char *message, mesibo_uint_t len);
mesibo_int_t (*on_message_status)(mesibo_module_t *mod, mesibo_message_params_t *params,
mesibo_uint_t status);
mesibo_int_t (*on_call)(mesibo_module_t *mod);
mesibo_int_t (*on_call_status)(mesibo_module_t *mod);
mesibo_int_t (*on_login)(mesibo_module_t *mod, mesibo_user_t *user);
mesibo_uint_t signature; /* module signature */
// initialized by mesibo
mesibo_int_t (*invoke)(mesibo_int_t id, mesibo_module_t *mod, ...);
} mesibo_module_t;Setting up the Development Environment
The mesibo Docker image includes a complete development environment — compilers, debuggers, and source code for several ready-to-use example modules (chatbot, translation filter, etc.). No external toolchain setup is required.
Setting up your working directory
Copy the source code from the container to your host machine so that edits persist across container restarts. In this example, the working directory on the host is /home/mesibo/working-dir. Create a bin subdirectory for compiled modules:
$ mkdir -p /home/mesibo/working-dir/bin
Start mesibo with the working directory and module output directory mounted:
$ sudo docker run -v /etc/letsencrypt/archive/mesibo.example.com:/certs \
-v /home/mesibo/working-dir:/mesibo/working \
-v /home/mesibo/working-dir/bin:/usr/lib64/mesibo/ \
-v /etc/mesibo:/etc/mesibo \
--net=host --ipc=host --name mesibo --rm -d mesibo/mesibo <APP_TOKEN>
Entering the development shell
Once the container is running, open a shell inside it:
$ sudo docker exec -it mesibo /bin/bash
You will see a shell prompt:
[root@mesibo /]#
Navigate to your mounted working directory and copy the module source files from the container:
[root@mesibo /]# cd /mesibo/working
[root@mesibo working]# cp -a /mesibo/src .
[root@mesibo working]# cd src
[root@mesibo src]# ls
README.md chatbot filter include make.inc skeleton translate
Compiling modules
To compile any module, run make from its directory:
[root@mesibo src]# cd skeleton
[root@mesibo skeleton]# make
To create a new module, copy the skeleton directory, rename it, and update the MODULE field in its Makefile to match your module name. For a detailed example, refer to the
Building a Chatbot section below.
mesibo Configuration File
Modules are loaded by specifying them in /etc/mesibo/mesibo.conf. To load a module without configuration:
module <module_name>
To load a module with configuration key-value pairs:
module <module_name> {
<config_name> = <config_value>
<config_name> = <config_value>
}
For example:
module skeleton {
file_name = xyz
auth_key = abc
}
To load multiple modules (mesibo processes them in the order listed):
module module_one {
/* configuration for module_one */
}
module module_two {
/* configuration for module_two */
}
Configuration key-value pairs are passed to each module's initialization function as a list of module_config_item_t name-value pairs.
Module Initialization
mesibo initializes each module by calling a function named mesibo_module_<module_name>_init. For example, if your module name is chatbot, the function must be named mesibo_module_chatbot_init. If this function is not defined, mesibo will fail to load the module.
The prototype is defined in module.hopen_in_new:
int mesibo_module_<module_name>_init(mesibo_int_t version, mesibo_module_t *mod, mesibo_uint_t len);Parameters:
version— mesibo module API version. Verify it matchesMESIBO_MODULE_VERSION.mod— pointer to the module structure allocated by mesibo. The module must populate this with callback function pointers and a description.len— size of the module definition structure. Verify it matchessizeof(mesibo_module_t).
Use the
MESIBO_MODULE_SANITY_CHECKmacro as the first line of your initialization function. It performs all the above checks automatically.
Configuration specified in mesibo.conf is available in the mod->config structure as a list of module_config_item_t name-value pairs.
Example initialization for the skeleton module configured with file_name and auth_key:
int mesibo_module_skeleton_init(mesibo_int_t version, mesibo_module_t *m, mesibo_uint_t len) {
MESIBO_MODULE_SANITY_CHECK(m, version, len);
if(m->config) {
mesibo_log(m, 1, "Configuration items passed to the module:\n");
for(int i = 0; i < m->config->count; i++) {
mesibo_log(m, 0, " %s = %s\n",
m->config->items[i].name,
m->config->items[i].value);
}
}
m->flags = 0;
m->description = strdup("Sample Module");
m->on_cleanup = skeleton_on_cleanup;
m->on_message = skeleton_on_message;
m->on_message_status = skeleton_on_message_status;
m->on_login = skeleton_on_login;
return MESIBO_RESULT_OK;
}This will log:
Configuration items passed to the module:
file_name = xyz
auth_key = abc
Module Callback Functions
Callback functions are called by mesibo on specific events. The following events are supported:
- Incoming message from a user
- Status update for a message sent by the module
- Incoming or outgoing call
- User login or logout
- Module unload (for cleanup)
Not all callbacks need to be defined — implement only those relevant to your module. All prototypes are in module.hopen_in_new.
Important: Callback functions must not block. If your processing is time-consuming or involves network calls, copy the data and process it in a separate thread.
on_message
Called when a user sends a message.
mesibo_int_t (*on_message)(mesibo_module_t *mod,
mesibo_message_params_t *params,
char *message, mesibo_uint_t len);Parameters:
mod— pointer to the module structureparams— pointer to message parameters (id,from,to, etc.). See Module Data Structures.message— buffer containing the message byteslen— length of the message in bytes
Returns:
MESIBO_RESULT_PASS— pass the message to the next module or the recipient unchangedMESIBO_RESULT_CONSUMED— consume the message; it will not be forwarded
on_message_status
Called when there is a delivery status update for a message sent by the module.
mesibo_int_t (*on_message_status)(mesibo_module_t *mod,
mesibo_message_params_t *params,
mesibo_uint_t status);Parameters:
mod— pointer to the module structureparams— pointer to message parametersstatus— status code such asMSGSTATUS_SENT,MSGSTATUS_DELIVERED,MSGSTATUS_READ
Returns: MESIBO_RESULT_OK
on_call
Called when a user initiates or receives a call.
mesibo_int_t (*on_call)(mesibo_module_t *mod);Returns: MESIBO_RESULT_OK
on_call_status
Called when there is a status update for a call (ringing, answered, ended, etc.).
mesibo_int_t (*on_call_status)(mesibo_module_t *mod);Returns: MESIBO_RESULT_OK
on_login
Called when a user logs in or logs out.
mesibo_int_t (*on_login)(mesibo_module_t *mod, mesibo_user_t *user);Parameters:
mod— pointer to the module structureuser— pointer to the user structure containing the user's address and online status. See Module Data Structures.
Returns: MESIBO_RESULT_OK
on_cleanup
Called when the module is unloaded. Use this to free memory and release any resources acquired during initialization.
mesibo_int_t (*on_cleanup)(mesibo_module_t *mod);Module Helper Functions
mesibo_message
Sends a message from the module to a user or group.
mesibo_int_t mesibo_message(mesibo_module_t *mod, mesibo_message_params_t *params,
const char *message, mesibo_uint_t len);Parameters:
mod— pointer to the module structureparams— message parameters including sender, recipient, and message IDmessage— buffer containing the message byteslen— length of the message in bytes
Returns: 0 on success, -1 on failure
Example:
mesibo_message_params_t p;
memset(&p, 0, sizeof(p));
p.to = "user_destination";
p.from = "user_source";
p.id = rand();
const char *message = "Hello from Module";
mesibo_message(mod, &p, message, strlen(message));Utility Functions
mesibo provides utility functions for asynchronous operations, HTTP requests, socket I/O, and logging.
HTTP
mesibo_util_http
Makes an asynchronous HTTP request. Useful for integrating with external services such as LLM APIs, Dialogflow, translation services, or any REST endpoint.
mesibo_int_t mesibo_util_http(mesibo_http_t *req, void *cbdata);Parameters:
req— pointer to the HTTP request structure. See HTTP Request Structure.cbdata— pointer to arbitrary user-defined data passed through to the response callbacks.
Returns: 0 on success, -1 on failure
Example:
mesibo_http_t req;
memset(&req, 0, sizeof(req));
req.url = "https://example.com/api";
req.post = "op=query&token=xxxxxxxxx&uid=456";
req.on_data = mesibo_http_on_data_callback;
mesibo_util_http(&req, NULL);Socket
mesibo provides socket I/O functions for connecting to external services over TCP or WebSocket.
mesibo_util_socket_connect
Opens a connection to a socket.
mesibo_int_t mesibo_util_socket_connect(mesibo_socket_t *sock, void *cbdata);Parameters:
sock— pointer to the socket structure. See Socket Structure.cbdata— pointer to arbitrary callback data
Returns: 0 on success, -1 on failure
mesibo_util_socket_write
Writes data to a connected socket.
mesibo_int_t mesibo_util_socket_write(mesibo_int_t sock, const char *data, mesibo_int_t len);Parameters:
sock— socket handledata— raw data byteslen— number of bytes to write
Returns: 0 on success, -1 on failure
mesibo_util_socket_close
Closes the socket connection.
void mesibo_util_socket_close(mesibo_int_t sock);Logging
mesibo_log
Prints output to the mesibo container logs.
mesibo_int_t mesibo_log(mesibo_module_t *mod, mesibo_uint_t level, const char *format, ...);Parameters:
mod— pointer to the module structurelevel— log level. Level0always prints. For level > 0, logging must be enabled in the configuration file.format— printf-style format string followed by arguments
Returns: 0 on success, -1 on failure
Example:
mesibo_log(mod, 0, "%s\n", "Hello from mesibo Module!");mesibo_vlog
Same as mesibo_log but accepts a va_list argument list.
mesibo_int_t mesibo_vlog(mesibo_module_t *mod, mesibo_uint_t level, const char *format, va_list args);mesibo_util_getconfig
Retrieves a configuration value by name from the module configuration passed in mesibo.conf.
char *mesibo_util_getconfig(mesibo_module_t *mod, const char *item_name);Parameters:
mod— pointer to the module structureitem_name— name of the configuration key
Returns: the configuration value as a string
Example — given this configuration:
module skeleton {
file = abc
auth_token = xyz
}
char *item_val = mesibo_util_getconfig(mod, "file");
// item_val contains "abc"
mesibo_util_json_extract
Extracts the value of a key from a JSON string.
char *mesibo_util_json_extract(char *src, const char *key, char **next);Parameters:
src— JSON source stringkey— key to look upnext— set to a pointer to the byte immediately after the matched value, or NULL if not needed
Returns: the value as a string
Example:
char *json = strdup("{ \"name\":\"apple\", \"game\":\"ball\" }");
char *value = mesibo_util_json_extract(json, "name", NULL);
// value contains "apple"
Module Data Structures
Message Parameters Structure
mesibo_message_params_t defines the parameters of an incoming or outgoing message. Used as an argument to on_message, on_message_status, mesibo_message, and related functions.
typedef struct mesibo_message_params_s {
mesibo_uint_t aid;
mesibo_uint_t id;
mesibo_uint_t refid;
mesibo_uint_t groupid;
mesibo_uint_t flags;
mesibo_uint_t type;
mesibo_uint_t expiry;
mesibo_uint_t to_online;
char *to, *from;
} mesibo_message_params_t;aid— Application IDid— Message ID. For outgoing messages, specify this inmesibo_message.refid— Reference message ID; links this message to another (e.g. a reply)groupid— Group ID for group messages;0for one-to-one messagesflags— Message flagstype— Arbitrary user-defined message typeexpiry— Time-to-live for outgoing messages, in secondsto_online— If set, deliver only if the recipient is currently online
User Structure
typedef struct mesibo_user_s {
mesibo_uint_t flags;
char *address;
mesibo_int_t online;
} mesibo_user_t;flags— User flagsaddress— User address (any character sequence that uniquely identifies the user)online— Online status:1if online,0if offline
HTTP Request Structure
Used with mesibo_util_http().
typedef struct _mesibo_http_t {
const char *url;
const char *post;
const char *content_type; // body content type
const char *extra_header;
const char *user_agent;
const char *referrer;
const char *origin;
const char *cookie;
const char *encoding; // gzip, deflate, identity, br
const char *cache_control;
const char *accept;
const char *etag;
mesibo_uint_t ims; // If-Modified-Since, GMT timestamp
mesibo_uint_t conn_timeout, header_timeout, body_timeout, total_timeout;
mesibo_http_ondata_t on_data;
mesibo_http_onstatus_t on_status;
mesibo_http_onclose_t on_close;
} mesibo_http_t;Properties:
url— HTTP or HTTPS URL. Authentication credentials can be embedded:https://user:pass@api.example.compost— Raw POST body string, e.g."authtoken=xyz&user=abc"content_type— Content-Type header, e.g."application/json"extra_header— Additional request headers, e.g."Authorization: Bearer <token>"user_agent— User-Agent string (default:mesibo/x.x)referrer— HTTP Referer headerorigin— HTTP Origin headercookie— HTTP Cookie headerencoding— Content encoding (gzip,deflate,identity,br)cache_control— Cache-Control headeraccept— Accept headeretag— ETag headerims— If-Modified-Since timestampconn_timeout,header_timeout,body_timeout,total_timeout— per-phase timeouts
Callbacks:
on_data— receives the response body, potentially in multiple chunks
typedef mesibo_int_t (*mesibo_http_ondata_t)(void *cbdata, mesibo_int_t state,
mesibo_int_t progress, const char *buffer, mesibo_int_t size);The state parameter reflects the current HTTP phase:
typedef enum {
MODULE_HTTP_STATE_REQUEST,
MODULE_HTTP_STATE_REQBODY,
MODULE_HTTP_STATE_RESPHEADER,
MODULE_HTTP_STATE_RESPBODY,
MODULE_HTTP_STATE_DONE
} module_http_state_t;progress ranges from 0 to 100. A negative value indicates an error.
Example callback:
static int mesibo_http_callback(void *cbdata, mesibo_int_t state,
mesibo_int_t progress, const char *buffer, mesibo_int_t size) {
tMessageContext *b = (tMessageContext *)cbdata;
mesibo_module_t *mod = b->mod;
if (progress < 0) {
mesibo_log(mod, 0, "HTTP error in callback\n");
return -1;
}
if (state != MODULE_HTTP_STATE_RESPBODY) {
return 0;
}
memcpy(b->buffer + b->datalen, buffer, size);
b->datalen += size;
if (progress == 100) {
// response complete — process b->buffer here
}
return 0;
}on_status— called with the HTTP response status code and content type
typedef mesibo_int_t (*mesibo_http_onstatus_t)(void *cbdata, mesibo_int_t status,
const char *response_type);on_close— called when the connection closes.resultisMESIBO_RESULT_FAILif the connection closed with an error.
typedef void (*mesibo_http_onclose_t)(void *cbdata, mesibo_int_t result);Socket Structure
Used with mesibo_util_socket_connect().
typedef struct mesibo_socket_s {
const char *url;
const char *ws_protocol; // WebSocket only — valid only if url is NULL
mesibo_int_t keepalive;
mesibo_int_t verify_host;
void *ssl_ctx; // use if provided; auto-generated if enable_ssl=1
mesibo_socket_onconnect_t on_connect;
mesibo_socket_onwrite_t on_write;
mesibo_socket_ondata_t on_data;
mesibo_socket_onclose_t on_close;
} mesibo_socket_t;Properties:
url— HTTP or HTTPS host URLws_protocol— WebSocket URL (ws://orwss://); used only whenurlis NULLkeepalive— enable persistent keep-alive connectionverify_host— verify host validity; disable on private networksssl_ctx— SSL context for HTTPS/WSS connections
Callbacks:
on_connect— called on successful connection;asockholds the socket handle
typedef void (*mesibo_socket_onconnect_t)(void *cbdata, mesibo_int_t asock, mesibo_int_t fd);on_write— called when a write operation completes
typedef void (*mesibo_socket_onwrite_t)(void *cbdata);on_data— called when data is received
typedef mesibo_int_t (*mesibo_socket_ondata_t)(void *cbdata, const char *data, mesibo_int_t len);on_close— called when the connection closes
typedef void (*mesibo_socket_onclose_t)(void *cbdata, mesibo_int_t type);Module Configuration Structure
Configuration key-value pairs from mesibo.conf are passed to the module as:
typedef struct module_config_item_s {
char *name;
char *value;
} module_config_item_t;
typedef struct module_configs_s {
int count;
module_config_item_t items[0];
} module_config_t;count holds the number of items; items is the array of name-value pairs.
Memory Management
Module callbacks can be invoked millions of times under load. Avoid dynamic memory allocation inside callbacks — allocate and prepare resources during initialization and reuse them throughout the module's lifetime.
- Module definition structure — allocated by mesibo; do not free it.
- Config item structures — allocated by mesibo and can be freed if needed. The key and value strings inside each item are statically allocated — use those pointers freely but do not free them.
Building a Chatbot
The following example builds a working chatbot module that forwards messages to Dialogflow and replies to users with the response. Dialogflow is used here as a concrete REST API integration example — the same pattern applies to any HTTP-based AI or LLM service.

The chatbot works as follows:
- A dedicated user address is configured as the chatbot destination
- When a user sends a message to that address, the
on_messagecallback receives it - The module forwards the message text to Dialogflow via HTTP
- When Dialogflow responds, the module sends the reply back to the original sender using
mesibo_message - Messages addressed to any other destination are passed through unchanged
The complete source is available at github.com/mesibo/onpremise-loadable-modules/tree/master/chatbotopen_in_new.
About Dialogflow
Dialogflow is a Google service for building conversational interfaces. Once trained on your data (FAQs, emails, etc.), it responds to user queries in natural language. It is accessed via a REST API.
For full documentation, refer to Dialogflow Documentationopen_in_new.
Getting Dialogflow API credentials
You will need a GCP project ID and an access token.
Set up a GCP Consoleopen_in_new project:
- Create or select a project and note the project ID
- Create a service account
- Download the service account key as a JSON file
Set the credentials environment variable:
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-file.json"
Print your access token:
echo $(gcloud auth application-default print-access-token)
Save the token for use in the module configuration.
Invoking the Dialogflow API
Send a POST request to:
https://dialogflow.googleapis.com/v2/projects/<PROJECT_ID>/agent/sessions/<SESSION_ID>
SESSION_ID can be any unique identifier — using the mesibo message ID works well.
With headers:
Authorization: Bearer <YOUR_ACCESS_TOKEN>
Content-Type: application/jsonAnd POST body:
{
"queryInput": {
"text": {
"text": "<message text>",
"languageCode": "<language code>"
}
}
}Step 1 - Create the Source File
Create chatbot.c and include the module header:
#include "module.h"Step 2 - Define the Module Configuration
Define the configuration block for the chatbot in /etc/mesibo/mesibo.conf:
module chatbot {
project = <GCP Project ID>
endpoint = https://dialogflow.googleapis.com/v2
access_token = <Service Account access token>
language = en
address = my_chatbot
log = 1
}
Step 3 - Initialize the Module
The initialization function reads configuration, stores it in the module context, and registers the on_message callback:
int mesibo_module_chatbot_init(mesibo_int_t version, mesibo_module_t *m, mesibo_uint_t len) {
MESIBO_MODULE_SANITY_CHECK(m, version, len);
m->flags = 0;
m->description = strdup("Chatbot Module");
m->on_message = chatbot_on_message;
if(m->config) {
chatbot_config_t *cbc = get_config_dialogflow(m);
if(cbc == NULL) {
mesibo_log(m, MODULE_LOG_LEVEL_0VERRIDE, "%s: missing configuration\n", m->name);
return MESIBO_RESULT_FAIL;
}
m->ctx = (void *)cbc;
if(chatbot_init_dialogflow(m) != MESIBO_RESULT_OK) {
mesibo_log(m, MODULE_LOG_LEVEL_0VERRIDE, "%s: invalid configuration\n", m->name);
return MESIBO_RESULT_FAIL;
}
} else {
return MESIBO_RESULT_FAIL;
}
return MESIBO_RESULT_OK;
}The configuration is stored in chatbot_config_t and saved in m->ctx so it is accessible from all callbacks:
typedef struct chatbot_config_s {
/* from mesibo.conf */
const char *project;
const char *endpoint;
const char *access_token;
const char *address;
const char *language;
int log;
/* constructed during init */
char *post_url;
char *auth_bearer;
module_http_option_t *chatbot_http_opt;
} chatbot_config_t;Use mesibo_util_getconfig to read each value:
static chatbot_config_t *get_config_dialogflow(mesibo_module_t *mod) {
chatbot_config_t *cbc = (chatbot_config_t *)calloc(1, sizeof(chatbot_config_t));
cbc->project = mesibo_util_getconfig(mod, "project");
cbc->endpoint = mesibo_util_getconfig(mod, "endpoint");
cbc->access_token = mesibo_util_getconfig(mod, "access_token");
cbc->address = mesibo_util_getconfig(mod, "address");
cbc->language = mesibo_util_getconfig(mod, "language");
cbc->log = atoi(mesibo_util_getconfig(mod, "log"));
return cbc;
}Construct the Dialogflow URL and authorization header once during init rather than on every request:
static int chatbot_init_dialogflow(mesibo_module_t *mod) {
chatbot_config_t *cbc = (chatbot_config_t *)mod->ctx;
asprintf(&cbc->post_url, "%s/projects/%s/agent/sessions",
cbc->endpoint, cbc->project);
asprintf(&cbc->auth_bearer, "Authorization: Bearer %s", cbc->access_token);
cbc->chatbot_http_opt = mesibo_chatbot_get_http_opt(cbc);
return MESIBO_RESULT_OK;
}Step 4 - Handle Incoming Messages
The on_message callback intercepts each message. Messages addressed to the chatbot are forwarded to Dialogflow and consumed; all other messages are passed through unchanged:
static mesibo_int_t chatbot_on_message(mesibo_module_t *mod, mesibo_message_params_t *p,
char *message, mesibo_uint_t len) {
chatbot_config_t *cbc = (chatbot_config_t *)mod->ctx;
if(0 == strcmp(p->to, cbc->address)) {
// Copy params — do not modify the original as other modules may use it
mesibo_message_params_t *np = (mesibo_message_params_t *)calloc(1, sizeof(mesibo_message_params_t));
memcpy(np, p, sizeof(mesibo_message_params_t));
chatbot_process_message(mod, np, message, len);
return MESIBO_RESULT_CONSUMED;
}
return MESIBO_RESULT_PASS;
}Step 5 - Send the Message to Dialogflow
Build the HTTP request and send it asynchronously. The current message context is passed as callback data so it is available when the response arrives:
typedef struct http_context_s {
mesibo_module_t *mod;
mesibo_message_params_t *params;
char *from;
char *to;
char *post_data;
mesibo_int_t status;
char response_type[HTTP_RESPONSE_TYPE_LEN];
char buffer[HTTP_BUFFER_LEN];
int datalen;
} http_context_t;
static mesibo_int_t chatbot_process_message(mesibo_module_t *mod, mesibo_message_params_t *p,
const char *message, mesibo_uint_t len) {
chatbot_config_t *cbc = (chatbot_config_t *)mod->ctx;
char post_url[HTTP_POST_URL_LEN_MAX];
sprintf(post_url, "%s/%lu:detectIntent", cbc->post_url, p->id);
char *raw_post_data;
asprintf(&raw_post_data,
"{\"queryInput\":{\"text\":{\"text\":\"%.*s\",\"languageCode\":\"%s\"}}}",
(int)len, message, cbc->language);
http_context_t *http_context = (http_context_t *)calloc(1, sizeof(http_context_t));
http_context->mod = mod;
http_context->params = p;
http_context->from = strdup(p->from);
http_context->to = strdup(p->to);
http_context->post_data = raw_post_data;
cbc->chatbot_http_req->url = post_url;
cbc->chatbot_http_req->post = raw_post_data;
cbc->chatbot_http_req->on_data = chatbot_http_on_data_callback;
cbc->chatbot_http_req->on_status = chatbot_http_on_status_callback;
cbc->chatbot_http_req->on_close = chatbot_http_on_close_callback;
mesibo_util_http(cbc->chatbot_http_req, (void *)http_context);
return MESIBO_RESULT_OK;
}Step 6 - Handle the Dialogflow Response
The response arrives in chunks via on_data. Accumulate chunks into a buffer until progress == 100, then extract the response text and send it back to the user:
static mesibo_int_t chatbot_http_on_data_callback(void *cbdata, mesibo_int_t state,
mesibo_int_t progress, const char *buffer, mesibo_int_t size) {
http_context_t *b = (http_context_t *)cbdata;
mesibo_module_t *mod = b->mod;
if (progress < 0) {
mesibo_log(mod, MODULE_LOG_LEVEL_0VERRIDE, "HTTP error in chatbot callback\n");
mesibo_chatbot_destroy_http_context(b);
return MESIBO_RESULT_FAIL;
}
if (MODULE_HTTP_STATE_RESPBODY != state) {
return MESIBO_RESULT_OK;
}
if (buffer != NULL && size != 0) {
if (HTTP_BUFFER_LEN < (b->datalen + size)) {
mesibo_log(mod, MODULE_LOG_LEVEL_0VERRIDE, "HTTP callback: buffer overflow\n");
return MESIBO_RESULT_FAIL;
}
memcpy(b->buffer + b->datalen, buffer, size);
b->datalen += size;
}
return MESIBO_RESULT_OK;
}
mesibo_int_t chatbot_http_on_status_callback(void *cbdata, mesibo_int_t status,
const char *response_type) {
http_context_t *b = (http_context_t *)cbdata;
if(!b) return MESIBO_RESULT_FAIL;
mesibo_module_t *mod = b->mod;
if(!mod) return MESIBO_RESULT_FAIL;
chatbot_config_t *cbc = (chatbot_config_t *)mod->ctx;
b->status = status;
if(response_type != NULL) {
memcpy(b->response_type, response_type, strlen(response_type));
mesibo_log(mod, cbc->log, "status: %d, response_type: %s\n", (int)status, response_type);
}
return MESIBO_RESULT_OK;
}
void chatbot_http_on_close_callback(void *cbdata, mesibo_int_t result) {
http_context_t *b = (http_context_t *)cbdata;
mesibo_module_t *mod = b->mod;
if(MESIBO_RESULT_FAIL == result) {
mesibo_log(mod, MODULE_LOG_LEVEL_0VERRIDE, "Invalid HTTP response\n");
return;
}
// Extract the response text from the Dialogflow JSON response
char *extracted_response = mesibo_util_json_extract(b->buffer, "fulfillmentText", NULL);
// Send the response back to the user who sent the original query
mesibo_message_params_t p;
memset(&p, 0, sizeof(mesibo_message_params_t));
p.id = rand();
p.refid = b->params->id; // link response to the original query message
p.aid = b->params->aid;
p.from = b->to;
p.to = b->from;
p.expiry = 3600;
mesibo_message(mod, &p, extracted_response, strlen(extracted_response));
mesibo_chatbot_destroy_http_context(b);
}Step 7 - Compile the Module
Open the sample Makefile and set the MODULE variable to chatbot:
MODULE = chatbot
Then run:
$ sudo make
Verify the output library was created:
/usr/lib64/mesibo/mesibo_mod_chatbot.so
Step 8 - Load the Module
Add the chatbot configuration to /etc/mesibo/mesibo.conf (copy from sample.conf and update the project and access_token values).
Then start mesibo with the module library directory and configuration directory mounted:
$ sudo docker run -v /etc/letsencrypt/archive/mesibo.example.com:/certs \
-v /usr/lib64/mesibo/:/usr/lib64/mesibo/ \
-v /etc/mesibo:/etc/mesibo \
--net=host --ipc=host --name mesibo --rm -d mesibo/mesibo <APP_TOKEN>