Janus textroom plugin - how to get messages history

Hi @all! We use janus successfully since years for our own workflow management tool for webRTC communication. Now we will extend some functionality by using the data channel - and in addition partly the textroom plugin using websockets. We got it working basically - but for specific purposes we want to make the history of messages available. As it is configurable in janus.plugin.textroom.jcfg, we can’t find any documentation how to get the mesages “back to the participant” entering the room (again). Did we miss something specific (perhaps finding the concrete documentation)? Thaks in advane!

There’s no specific message: if when you create a room you put something like

history = 10

then new users joining will get the last 10 messages that were sent to the room as soon as they get in. The plugin doesn’t store any message otherwise, and there’s no dedicated message to request all or part of the history.

Notice that the plugin supports forwarding messages sent to the room to an external web backend, where you can handle them any way you want (e.g., for history purposes). That would allow you to create your own history-on-demand mechanism out of band, if you wanted.

Hi Lorenco, i got it. In fact the backend usage will fullfill our needs perfectly. Thanks a lot for your help!

Hi Lorenzo!

I stumbeld over a few issues with the textroom plugin, as it was not working as expected. Changed a bit in your code in janus-gateway-master/src/plugins/janus_textroom.c to get these issues fixed:

  1. history was only performed on messages without receiving participant(s) address (to / tos).
  2. Post to http_backend not working. I added CURLOPT_POSTFIELDSIZE and CURLOPT_TIMEOUT.
  3. Missed the message reciever(s) in msg array for history and backend use
--- /data/projects/janus/janus-gateway-master_org/src/plugins/janus_textroom.c	2025-11-05 06:26:34.000000000 +0000
+++ /data/projects/janus/janus-gateway-master/src/plugins/janus_textroom.c	2025-11-18 11:13:29.050041281 +0000
@@ -1069,11 +1069,15 @@
 			textroom->destroyed = 0;
 			janus_mutex_init(&textroom->mutex);
 			janus_refcount_init(&textroom->ref, janus_textroom_room_free);
-			JANUS_LOG(LOG_VERB, "Created TextRoom: %s (%s, %s, secret: %s, pin: %s, history: %"SCNu16" messages)\n",
+			/* added http_backend information */
+			JANUS_LOG(LOG_VERB, "Created TextRoom: %s (%s, %s, secret: %s, pin: %s, history: %"SCNu16" messages, post: %s)\n",
 				textroom->room_id_str, textroom->room_name,
 				textroom->is_private ? "private" : "public",
 				textroom->room_secret ? textroom->room_secret : "no secret",
-				textroom->room_pin ? textroom->room_pin : "no pin", textroom->history_size);
+				textroom->room_pin ? textroom->room_pin : "no pin",
+				textroom->history_size,
+				textroom->http_backend ? textroom->http_backend : "no http backend"
+			);
 			g_hash_table_insert(rooms,
 				string_ids ? (gpointer)g_strdup(textroom->room_id_str) : (gpointer)janus_uint64_dup(textroom->room_id),
 				textroom);
@@ -1601,6 +1605,16 @@
 		json_object_set_new(msg, "text", json_string(message));
 		if(username || usernames)
 			json_object_set_new(msg, "whisper", json_true());
+		/* add message reciever username to msg object if set */
+		if(username){
+			const char *str_to = json_string_value(username);
+			json_object_set_new(msg, "to", json_string(str_to));
+		}
+		/* add message recievers usernames to msg object if set */
+		if(usernames){
+			const char *str_tos = json_string_value(usernames);
+			json_object_set_new(msg, "tos", json_string(str_tos));
+		}
 		char *msg_text = json_dumps(msg, json_format);
 		if(msg_text == NULL) {
 			json_decref(msg);
@@ -1675,44 +1689,50 @@
 					janus_refcount_decrease(&top->ref);
 				}
 			}
-			if(textroom->history && history_text) {
-				/* Store in the history */
-				g_queue_push_tail(textroom->history, history_text);
-				if(g_queue_get_length(textroom->history) > textroom->history_size) {
-					char *text = (char *)g_queue_pop_head(textroom->history);
-					g_free(text);
-				}
+			/* removed the textroom->history handling out of this condition */
+		}
+		/* inserted the textroom->history handling to make it work */
+		if(textroom->history && history_text) {
+			/* Store in the history */
+			g_queue_push_tail(textroom->history, history_text);
+			if(g_queue_get_length(textroom->history) > textroom->history_size) {
+				char *text = (char *)g_queue_pop_head(textroom->history);
+				g_free(text);
 			}
+		}
 #ifdef HAVE_LIBCURL
-			/* Is there a backend waiting for this message too? */
-			if(textroom->http_backend) {
-				/* Prepare the libcurl context */
-				CURLcode res;
-				CURL *curl = curl_easy_init();
-				if(curl == NULL) {
-					JANUS_LOG(LOG_ERR, "Error initializing CURL context\n");
+		/* Is there a backend waiting for this message too? */
+		if(textroom->http_backend) {
+			/* Prepare the libcurl context */
+			CURLcode res;
+			CURL *curl = curl_easy_init();
+			JANUS_LOG(LOG_VERB, "[CURL] init %s \n", textroom->http_backend);
+			if(curl == NULL) {
+				JANUS_LOG(LOG_ERR, "[CURL] Error initializing context\n");
+			} else {
+				curl_easy_setopt(curl, CURLOPT_URL, textroom->http_backend);
+				struct curl_slist *headers = NULL;
+				headers = curl_slist_append(headers, "Accept: application/json");
+				headers = curl_slist_append(headers, "Content-Type: application/json");
+				headers = curl_slist_append(headers, "charsets: utf-8");
+				/* added some lines */
+				curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+				curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg_text);
+				curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(msg_text));
+				curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, janus_textroom_write_data);
+				curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
+				/* Send the request */
+				res = curl_easy_perform(curl);
+				if(res != CURLE_OK) {
+					JANUS_LOG(LOG_ERR, "[CURL] Couldn't relay event to the backend: %s\n", curl_easy_strerror(res));
 				} else {
-					curl_easy_setopt(curl, CURLOPT_URL, textroom->http_backend);
-					struct curl_slist *headers = NULL;
-					headers = curl_slist_append(headers, "Accept: application/json");
-					headers = curl_slist_append(headers, "Content-Type: application/json");
-					headers = curl_slist_append(headers, "charsets: utf-8");
-					curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
-					curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg_text);
-					curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, janus_textroom_write_data);
-					/* Send the request */
-					res = curl_easy_perform(curl);
-					if(res != CURLE_OK) {
-						JANUS_LOG(LOG_ERR, "Couldn't relay event to the backend: %s\n", curl_easy_strerror(res));
-					} else {
-						JANUS_LOG(LOG_DBG, "Event sent!\n");
-					}
-					curl_easy_cleanup(curl);
-					curl_slist_free_all(headers);
+					JANUS_LOG(LOG_VERB, "[CURL] Event sent!\n");
 				}
+				curl_easy_cleanup(curl);
+				curl_slist_free_all(headers);
 			}
-#endif
 		}
+#endif
 		janus_refcount_decrease(&participant->ref);
 		free(msg_text);
 		janus_mutex_unlock(&textroom->mutex);
@@ -1832,6 +1852,7 @@
 		json_t *history = json_object_get(root, "history");
 		gboolean send_history = history ? json_is_true(history) : TRUE;
 		if(send_history) {
+			JANUS_LOG(LOG_VERB, "Sending history data\n");
 			if(textroom->history != NULL && textroom->history->head != NULL) {
 				GList *temp = textroom->history->head;
 				char *text = NULL;
@@ -2457,6 +2478,7 @@
 			/* Prepare the libcurl context */
 			CURLcode res;
 			CURL *curl = curl_easy_init();
+			JANUS_LOG(LOG_VERB, "[CURL] init %s \n", textroom->http_backend);
 			if(curl == NULL) {
 				JANUS_LOG(LOG_ERR, "Error initializing CURL context\n");
 			} else {
@@ -2467,7 +2489,9 @@
 				headers = curl_slist_append(headers, "charsets: utf-8");
 				curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
 				curl_easy_setopt(curl, CURLOPT_POSTFIELDS, msg_text);
+				curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(msg_text));
 				curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, janus_textroom_write_data);
+				curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
 				/* Send the request */
 				res = curl_easy_perform(curl);
 				if(res != CURLE_OK) {

This may be needfull for somebody else.

Not sure what you mean by this?

In general, though, if you write a patch that fixes a broken behaviour, please consider submitting a PR on the repo. We can review it there, and maybe ask you to make some changes, but then if accepted in gets in the main codebase and everyone benefits.

Only a message send to everybody was added to history array. I will submit the PR on the repo…

That’s done on purpose. If you send a message to a specific participant, that’s a private message, and not one meant for the history everyone else will get.

Oh yes - that makes perfect sense of course. I realized it 5min ago while implemnting in our framework. I will have to revert this part definitivly :wink:

Most probably i try to create a modified plugin with optional filter to get only the messages adressed or sended from the joining (formally known) member for our own purposes…

PS: I tested the forwarding of chat messages via HTTP and it works as expected for me, with no change needed.