GENWiki

Premier IT Outsourcing and Support Services within the UK

User Tools

Site Tools


opensource:rocketmsg

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <curl/curl.h>
  5. #include <json-c/json.h>
  6.  
  7. const char *VERSION = "1.002";
  8.  
  9. struct MemoryStruct
  10. {
  11. char *memory;
  12. size_t size;
  13. };
  14.  
  15. static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
  16. {
  17. size_t realsize = size * nmemb;
  18. struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  19.  
  20. char *ptr = realloc(mem->memory, mem->size + realsize + 1);
  21. if (ptr == NULL)
  22. {
  23. printf("Not enough memory (realloc returned NULL)\n");
  24. return 0;
  25. }
  26.  
  27. mem->memory = ptr;
  28. memcpy(&(mem->memory[mem->size]), contents, realsize);
  29. mem->size += realsize;
  30. mem->memory[mem->size] = 0;
  31.  
  32. return realsize;
  33. }
  34. char *read_message_from_stdin()
  35. {
  36. char *message = NULL;
  37. size_t size = 0;
  38. size_t capacity = 0;
  39. char buffer[4096];
  40.  
  41. while (fgets(buffer, sizeof(buffer), stdin) != NULL)
  42. {
  43. size_t len = strlen(buffer);
  44. if (size + len + 1 > capacity)
  45. {
  46. capacity = (size + len + 1) * 2;
  47. char *new_message = realloc(message, capacity);
  48. if (new_message == NULL)
  49. {
  50. free(message);
  51. return NULL;
  52. }
  53. message = new_message;
  54. }
  55. memcpy(message + size, buffer, len);
  56. size += len;
  57. }
  58.  
  59. if (message != NULL)
  60. {
  61. message[size] = '\0';
  62. }
  63.  
  64. return message;
  65. }
  66.  
  67. char *json_escape_string(const char *str)
  68. {
  69. if (str == NULL)
  70. return NULL;
  71.  
  72. size_t len = strlen(str);
  73. size_t escaped_len = 0;
  74. for (size_t i = 0; i < len; i++)
  75. {
  76. switch (str[i])
  77. {
  78. case '\\':
  79. case '"':
  80. escaped_len += 2;
  81. break;
  82. default:
  83. escaped_len++;
  84. break;
  85. }
  86. }
  87.  
  88. char *escaped_str = malloc(escaped_len + 1);
  89. if (escaped_str == NULL)
  90. return NULL;
  91.  
  92. size_t j = 0;
  93. for (size_t i = 0; i < len; i++)
  94. {
  95. switch (str[i])
  96. {
  97. case '\\':
  98. escaped_str[j++] = '\\';
  99. escaped_str[j++] = '\\';
  100. break;
  101. case '"':
  102. escaped_str[j++] = '\\';
  103. escaped_str[j++] = '"';
  104. break;
  105. default:
  106. escaped_str[j++] = str[i];
  107. break;
  108. }
  109. }
  110. escaped_str[j] = '\0';
  111.  
  112. return escaped_str;
  113. }
  114.  
  115. int main(int argc, char *argv[])
  116. {
  117. fprintf(stderr, "RocketMSG Version %s\n", VERSION);
  118. if (argc < 2)
  119. {
  120. fprintf(stderr, "Usage: %s <room> [message]\n", argv[0]);
  121. return 1;
  122. }
  123.  
  124. const char *username = "some username";
  125. const char *password = "usernames password";
  126. const char *room = argv[1];
  127. const char *message = NULL;
  128.  
  129. if (argc < 3)
  130. {
  131. message = read_message_from_stdin();
  132. if (message == NULL)
  133. {
  134. fprintf(stderr, "Failed to read message from stdin\n");
  135. return 1;
  136. }
  137. }
  138. else
  139. {
  140. message = argv[2];
  141. }
  142.  
  143. const char *SERVER_URL = "http://10.1.2.3:3001";
  144.  
  145. CURL *curl;
  146. CURLcode res;
  147.  
  148. curl_global_init(CURL_GLOBAL_DEFAULT);
  149. curl = curl_easy_init();
  150. if (curl)
  151. {
  152. struct MemoryStruct chunk;
  153. chunk.memory = malloc(1);
  154. chunk.size = 0;
  155.  
  156. char authUrl[256];
  157. snprintf(authUrl, sizeof(authUrl), "%s/api/v1/login", SERVER_URL);
  158.  
  159. char authPayload[256];
  160. snprintf(authPayload, sizeof(authPayload), "{\"username\":\"%s\",\"password\":\"%s\"}", username, password);
  161.  
  162. struct curl_slist *authHeaders = NULL;
  163. authHeaders = curl_slist_append(authHeaders, "Content-Type: application/json");
  164.  
  165. curl_easy_setopt(curl, CURLOPT_URL, authUrl);
  166. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, authPayload);
  167. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, authHeaders);
  168. curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  169. curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
  170.  
  171. res = curl_easy_perform(curl);
  172. if (res != CURLE_OK)
  173. {
  174. fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
  175. }
  176. else
  177. {
  178. // Parse the response to extract the user ID and token
  179. json_object *jsonResponse = json_tokener_parse(chunk.memory);
  180. if (jsonResponse == NULL)
  181. {
  182. fprintf(stderr, "Failed to parse JSON response.\n");
  183. }
  184. else
  185. {
  186. json_object *jsonData = json_object_object_get(jsonResponse, "data");
  187. if (jsonData == NULL)
  188. {
  189. fprintf(stderr, "Failed to get 'data' object from JSON response.\n");
  190. }
  191. else
  192. {
  193. const char *userId = json_object_get_string(json_object_object_get(jsonData, "userId"));
  194. const char *authToken = json_object_get_string(json_object_object_get(jsonData, "authToken"));
  195.  
  196. if (userId && authToken)
  197. {
  198.  
  199. free(chunk.memory);
  200. chunk.memory = malloc(1);
  201. chunk.size = 0;
  202.  
  203. char url[256];
  204. snprintf(url, sizeof(url), "%s/api/v1/chat.postMessage", SERVER_URL);
  205.  
  206. char *escaped_message = json_escape_string(message);
  207.  
  208. json_object *payload = json_object_new_object();
  209. json_object_object_add(payload, "channel", json_object_new_string(room));
  210. json_object_object_add(payload, "text", json_object_new_string(escaped_message));
  211. const char *json_payload = json_object_to_json_string(payload);
  212.  
  213. struct curl_slist *headers = NULL;
  214. headers = curl_slist_append(headers, "Content-Type: application/json");
  215.  
  216. char authTokenHeader[256];
  217. snprintf(authTokenHeader, sizeof(authTokenHeader), "X-Auth-Token: %s", authToken);
  218. headers = curl_slist_append(headers, authTokenHeader);
  219.  
  220. char userIdHeader[256];
  221. snprintf(userIdHeader, sizeof(userIdHeader), "X-User-Id: %s", userId);
  222. headers = curl_slist_append(headers, userIdHeader);
  223.  
  224. curl_easy_setopt(curl, CURLOPT_URL, url);
  225. curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_payload);
  226. curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
  227.  
  228. res = curl_easy_perform(curl);
  229. if (res != CURLE_OK)
  230. {
  231. fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
  232. }
  233. else
  234. {
  235. // Parse the response to check if the message was posted successfully
  236. json_object *postResponse = json_tokener_parse(chunk.memory);
  237. if (postResponse == NULL)
  238. {
  239. fprintf(stderr, "Failed to parse JSON response.\n");
  240. }
  241. else
  242. {
  243. json_object *success = json_object_object_get(postResponse, "success");
  244. if (success == NULL || !json_object_get_boolean(success))
  245. {
  246. json_object *error = json_object_object_get(postResponse, "error");
  247. if (error != NULL)
  248. {
  249. const char *errorMessage = json_object_get_string(error);
  250. fprintf(stderr, "Failed to post message. Error: %s\n", errorMessage);
  251. }
  252. else
  253. {
  254. fprintf(stderr, "Failed to post message. Unknown error.\n");
  255. fprintf(stderr, "Response: %s\n", chunk.memory);
  256. }
  257. }
  258. else
  259. {
  260. printf("Message posted successfully.\n");
  261. }
  262.  
  263. json_object_put(postResponse);
  264. }
  265. }
  266.  
  267. curl_slist_free_all(headers);
  268. json_object_put(payload);
  269. free(escaped_message);
  270. }
  271. else
  272. {
  273. fprintf(stderr, "Failed to extract user ID and auth token from the response.\n");
  274. }
  275. }
  276.  
  277. json_object_put(jsonResponse);
  278. }
  279. }
  280.  
  281. free(chunk.memory);
  282. curl_slist_free_all(authHeaders);
  283. }
  284.  
  285. curl_easy_cleanup(curl);
  286. curl_global_cleanup();
  287. // free(message);
  288.  
  289. return 0;
  290. }

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:

  1. rocketmsg "@Fred" "You are a muppet"

Send a message to the Room #General:

  1. 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:

  1. rocketmsg "SomeTeam" < auto.log
/home/gen.uk/domains/wiki.gen.uk/public_html/data/pages/opensource/rocketmsg.txt · Last modified: 2024/03/15 13:13 by genadmin

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki