Table of Contents
RocketMSG
A Linux Commandline tool that can post a message into a room/team/channel/user, which is actually very useful especially in automations.
We run a lot of automation in linux, powered by cron (or anacron) to handle data in, data out, report generation, backup's and the suchlike. We also use Rocket.chat for the company chat system, being privately hosted and secure.
Having a commandline tool that can post a message, from the command line arguments, or even via stdin is a key part of that, keeping people informed of things that are happening, and providing visual verifications.
This is written in c, very minimalist so it can compile on many systems.
This is a very basic implementation. If you're interested in a fully featured commercial version with multiline quoting (fixed spacing), attachments, debug and more then hit us up on the gen.net.uk contact form.
Prerequisites
You will need two libraries, libcurl-devel, and json-c-devel, that's it.
The Code
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <curl/curl.h> #include <json-c/json.h> const char *VERSION = "1.002"; struct MemoryStruct { char *memory; size_t size; }; static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; if (ptr == NULL) { return 0; } mem->memory = ptr; mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } char *read_message_from_stdin() { char *message = NULL; size_t size = 0; size_t capacity = 0; char buffer[4096]; { if (size + len + 1 > capacity) { capacity = (size + len + 1) * 2; if (new_message == NULL) { return NULL; } message = new_message; } size += len; } if (message != NULL) { message[size] = '\0'; } return message; } char *json_escape_string(const char *str) { if (str == NULL) return NULL; size_t escaped_len = 0; for (size_t i = 0; i < len; i++) { switch (str[i]) { case '\\': case '"': escaped_len += 2; break; default: escaped_len++; break; } } if (escaped_str == NULL) return NULL; size_t j = 0; for (size_t i = 0; i < len; i++) { switch (str[i]) { case '\\': escaped_str[j++] = '\\'; escaped_str[j++] = '\\'; break; case '"': escaped_str[j++] = '\\'; escaped_str[j++] = '"'; break; default: escaped_str[j++] = str[i]; break; } } escaped_str[j] = '\0'; return escaped_str; } int main(int argc, char *argv[]) { if (argc < 2) { return 1; } const char *username = "some username"; const char *password = "usernames password"; const char *room = argv[1]; const char *message = NULL; if (argc < 3) { message = read_message_from_stdin(); if (message == NULL) { return 1; } } else { message = argv[2]; } const char *SERVER_URL = "http://10.1.2.3:3001"; CURL *curl; CURLcode res; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { struct MemoryStruct chunk; chunk.size = 0; char authUrl[256]; char authPayload[256]; snprintf(authPayload, sizeof(authPayload), "{\"username\":\"%s\",\"password\":\"%s\"}", username, password); struct curl_slist *authHeaders = NULL; authHeaders = curl_slist_append(authHeaders, "Content-Type: application/json"); curl_easy_setopt(curl, CURLOPT_URL, authUrl); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, authPayload); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, authHeaders); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); res = curl_easy_perform(curl); if (res != CURLE_OK) { } else { // Parse the response to extract the user ID and token json_object *jsonResponse = json_tokener_parse(chunk.memory); if (jsonResponse == NULL) { } else { json_object *jsonData = json_object_object_get(jsonResponse, "data"); if (jsonData == NULL) { } else { const char *userId = json_object_get_string(json_object_object_get(jsonData, "userId")); const char *authToken = json_object_get_string(json_object_object_get(jsonData, "authToken")); if (userId && authToken) { chunk.size = 0; char url[256]; char *escaped_message = json_escape_string(message); json_object *payload = json_object_new_object(); json_object_object_add(payload, "channel", json_object_new_string(room)); json_object_object_add(payload, "text", json_object_new_string(escaped_message)); const char *json_payload = json_object_to_json_string(payload); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); char authTokenHeader[256]; headers = curl_slist_append(headers, authTokenHeader); char userIdHeader[256]; headers = curl_slist_append(headers, userIdHeader); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_payload); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); res = curl_easy_perform(curl); if (res != CURLE_OK) { } else { // Parse the response to check if the message was posted successfully json_object *postResponse = json_tokener_parse(chunk.memory); if (postResponse == NULL) { } else { json_object *success = json_object_object_get(postResponse, "success"); if (success == NULL || !json_object_get_boolean(success)) { json_object *error = json_object_object_get(postResponse, "error"); if (error != NULL) { const char *errorMessage = json_object_get_string(error); } else { } } else { } json_object_put(postResponse); } } curl_slist_free_all(headers); json_object_put(payload); } else { } } json_object_put(jsonResponse); } } curl_slist_free_all(authHeaders); } curl_easy_cleanup(curl); curl_global_cleanup(); // free(message); return 0; }
Configuring
You need to pick a user who will be sending these messages, its best to create a new user, e.g. "automation" and give it a password and permissions with access to everywhere you want to be able to post messages. Then you need to code that user in *username, the user's password in *password, and finally set your rocket.chat server URL in, you guessed it, *SERVER_URL.
Compiling
We're going to use clang here instead of gcc simply because its, well, easier. If you don't have clang, just install it. This assumes you've called the source code rocketmsg.c
clang -o rocketmsg rocketmsg.c -lcurl -ljson-c
If you get any errors relating to either curl or json-c, then make sure the header files are in the PATH, and if not, make it so, OR, copy the headers locally to a subdirectory (e.g. if you're .c file is in /home/smartie, then stick the headers for json-c in /home/smartie/json-c and it'll find them. You must of course make sure you have the lib's installed (see prerequisites).
Once compiled, and this is tested working on MacOS, RHEL and CentOS, simply run it like this..
Usage
Send a message to Fred:
rocketmsg "@Fred" "You are a muppet"
Send a message to the Room #General:
rocketmsg "#General" "I am rocketmsg, and I rule the world, or at least the automation server"
Send the contents of auto.log to the Team SomeTeam:
rocketmsg "SomeTeam" < auto.log