[FR] Contact via Converse

'flemme de rédiger ça en anglais pour l’instant, mais je tâcherai de prendre le courage de le faire dans les jours à venir (d’où le [FR] dans le titre, une version [EN] suivra, j’espère).

C’est un projet dont j’avais eu l’idée il y a un petit moment, et auquel je me suis mis sérieusement avec l’arrivée en début d’année de Converse.js dans la liste des applications facilement installables : le but était de faire en sorte que les gens qui viennent sur mon site puissent me parler par XMPP, sans pour ça avoir besoin de créer un compte quelque part ni d’installer un client dédié.
Et comme ça marche et que les quelques personnes à qui j’en ai parlé jusque là ont trouvé le truc assez fun, je me suis dit que c’était toujours utile de documenter au cas où d’autres gens voudraient faire de même :slight_smile:

Donc, commençons par le début. Pour éviter aux gens d’avoir à créer un compte XMPP, il faut un serveur qui accepte les connexions anonymes. Ça demande ici de mettre un peu les mains dans le cambouis, vu qu’il faut configurer Metronome spécifiquement pour ça.
J’ai donc commencé par ajouter un domaine dédié côté YunoHost (ci-après désigné par « DOMAIN »), ce qui permet de profiter des facilités du type gestion automatique des certificats, et donc éviter pas mal de prises de tête. Ensuite, j’ai modifié la conf’ Metronome pour ce domaine (/etc/metronome/conf.d/DOMAIN.cfg.lua) comme suit :

VirtualHost "DOMAIN"
  enable = true
  ssl = {
        key = "/etc/yunohost/certs/DOMAIN/key.pem";
        certificate = "/etc/yunohost/certs/DOMAIN/crt.pem";
  }
  authentication = "anonymous"
  allow_anonymous_multiresourcing = true
  admins = { "ADMIN" }
  modules_disabled = { "offline" }
  
  disco_items = {
    { "muc.DOMAIN" },
  };

Component "muc.DOMAIN" "muc"
  name = "DOMAIN Chatrooms"
  restrict_room_creation = "local"
  allow_anonymous_creation = true
  room_default_config = {
    persistent = false
  };

(où « ADMIN » est à remplacer par votre adresse XMPP principale). Notez que, ceci fait, le domaine en question est totalement débranché du LDAP : il ne sera pas possible que des utilisateurs de la machine aient des adresses XMPP dessus, il n’y aura que des comptes anonymes.
De mémoire, un redémarrage de Metronome est préférable pour bien prendre en compte la configuration.

Donc, ceci fait, nous avons un domaine sur lequel il est possible de se connecter anonymement, et où les utilisateurs anonymes peuvent créer des salons de discussions. Maintenant, reste à mettre en place le client. Pour ça, comme indiqué dès le titre, Converse est assez pratique et facile à installer, donc profitons-en. À vous de voir où vous voulez l’installer, ça peut être sur le domaine dédié qu’on vient de créer, mais ça marche aussi bien si c’est ailleurs.
La configuration se fait ensuite dans /var/www/converse/index.html. J’ai fait quelques modifs cosmétiques (et donc pas strictement nécessaires), donc je vous montre un fichier assez différent de celui d’origine, mais si vous essayez de comprendre et que vous êtes paumé·e·s, n’hésitez pas à demander, je tâcherai de vous aider à démêler ce qui est réellement utile :

<!doctype html>
<html>
<head>
	<meta charset="utf-8"/>
	<title>Converse</title>
	<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
	<meta name="description" content="Converse XMPP/Jabber Chat"/>
	<meta name="author" content="JC Brand" />
	<meta name="keywords" content="xmpp chat webchat converse.js" />
	<link rel="shortcut icon" type="image/ico" href="dist/favicon.ico"/>
	<link rel="manifest" href="manifest.json">
	<link type="text/css" rel="stylesheet" media="screen" href="dist/converse.min.css" />
	<script src="dist/libsignal-protocol.min.js"></script>
	<script src="dist/emojis.js"></script>
	<script src="dist/icons.js"></script>
	<script src="dist/converse.min.js"></script>
	<script>
		window.onload = function() {
			converse.plugins.add("custom", { initialize: function() {
				function fade(op) {
					document.getElementById("conversejs").style.opacity = op+"%";
				}
				var mut = new MutationObserver(function() {
					if (document.getElementsByName("join").length != 1)
						return; // Waiting for the form.
					mut.disconnect(); // That's done, no more needed.
					elzmsg.firstChild.nodeValue = ""; // Not needed yet.
					for (var i=0; i<=100; i++) setTimeout(fade, i*5, i);
					var button = document.getElementsByName("join")[0];
					button.value = "Lancer la conversation";
					button.onclick = function() {
						if (document.getElementsByName("nick")[0].validity.valid) {
							for (var i=0; i<=100; i++) setTimeout(fade, i*5, 100-i);
							elzmsg.firstChild.nodeValue = "Je réponds dès que possible…";
						}
					}
				});
				var logout = this._converse.api.user.logout;
				var elzmsg = document.getElementById("elzmsg");
				mut.observe(document.body, { childList: true, subtree: true });
				this._converse.api.listen.on("enteredNewRoom", function(room) {
					room.directInvite("ADMIN");
					room.occupants.on("add", function() {
						if (room.occupants.length != 2) return;
						for (var i=0; i<=100; i++) setTimeout(fade, i*5, i);
					});
					room.occupants.on("remove", function() {
						if (room.occupants.length != 1) return;
						elzmsg.firstChild.nodeValue = "Merci, à la prochaine !";
						for (var i=0; i<=100; i++) setTimeout(fade, i*5, 100-i);
						setTimeout(logout, 505); // The conversation is over.
					});
				});
			}});
			converse.initialize({
				message_archiving: "never",
				authentication: 'anonymous',
				jid: 'DOMAIN',
				allow_message_retraction: false,
				allow_message_corrections: false,
				allow_non_roster_messaging: true,
				allow_user_trust_override: false,
				clear_messages_on_reconnection: true,
				auto_join_rooms: [ (Date.now()+Math.random()).toString(16)
				        .replace(".", "-")+'@muc.DOMAIN' ],
				bosh_service_url: window.location+'http-bind',
				whitelisted_plugins: [ "custom" ],
				hide_muc_participants: true,
				view_mode: 'fullscreen',
				auto_reconnect: true,
				auto_login: true,
				auto_focus: true,
				keepalive: false,
				singleton: true,
				i18n: 'fr',
			});
		};
	</script>
	<style>
		#conversejs {
			opacity: 0;
		}
		#conversejs, .chatroom-body, .flyout, .muc-nickname-form {
			background-color: transparent !important;
		}
		.muc-nickname-form fieldset {
			background-color: #FFEBCD88;
			padding: 2em !important;
			margin: 0 !important;
		}
		.muc-nickname-form fieldset:first-child {
			border-top-right-radius: 12px;
			border-top-left-radius: 12px;
		}
		.muc-nickname-form fieldset:last-child {
			border-bottom-right-radius: 12px;
			border-bottom-left-radius: 12px;
		}
		.chat-head-chatroom {
			display: none !important;
		}
		#elzmsg {
			color: #FFEBCD;
			font-family: Romande ADF Script Std;
			font-size: 1.5em;
			position: fixed;
			left: 42%;
		}
		@font-face {
			font-family: Romande ADF Script Std;
			src: local("Romande ADF Script Std"), url(/styles/fonts/RomandeADFScriptStd-Italic.otf) format("opentype");
		}
	</style>
</head>
<body class="converse-fullscreen">
	<noscript>You need to enable JavaScript to run the Converse.js chat app.</noscript>
	<div id="elzmsg">Chargement en cours…</div>
	<div id="conversejs-bg"></div>
</body>
</html>
<!--
    @licstart
    This is free and unencumbered software released into the public domain.

    Anyone is free to copy, modify, publish, use, compile, sell, or
    distribute this software, either in source code form or as a compiled
    binary, for any purpose, commercial or non-commercial, and by any
    means.

    In jurisdictions that recognize copyright laws, the author or authors
    of this software dedicate any and all copyright interest in the
    software to the public domain. We make this dedication for the benefit
    of the public at large and to the detriment of our heirs and
    successors. We intend this dedication to be an overt act of
    relinquishment in perpetuity of all present and future rights to this
    software under copyright law.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

    For more information, please refer to <http://unlicense.org/>
    @licend
-->

J’ai bricolé en ne connaissant pas masse Converse, donc il est possible que certains trucs soient grandement simplifiables, mais ça fonctionne en l’état. Une personne qui ouvrira la page concernée va donc se voir demander de choisir un pseudo, puis, si elle valide, vous recevrez une invitation à rejoindre une conversation donnée. Si vous quittez cette conversation, ça met fin à la discussion et la personne est déconnectée.

Voilà, c’est à peu près tout, n’avez qu’à remplacer DOMAIN et ADMIN là où il faut, et tout marchera :slight_smile:
Après, on peut quand même ajouter quelques remarques. D’abord, sur le choix d’un salon de discussion plutôt qu’une conversation privée. J’avais commencé par essayer de faire ça par une conversation privée, ce qui simplifiait la conf’ côté Metronome (pas besoin d’avoir le domaine muc. supplémentaire), mais présentait un inconvénient assez gênant : la personne qui vous contacte est dans ce cas identifiée par son identifiant Jabber (et un JID anonyme, c’est long, moche et pas très informatif) plutôt que par un joli pseudo (j’ai essayé tout ce à quoi j’ai pensé pour faire apparaître un pseudo, mais ça n’a pas marché).
Donc j’ai préféré passer par des salons de discussions temporaires créés à la demande, ce qui permet au passage d’inviter d’autres gens dans la discussion en cas de besoin (la possibilité de le faire n’est pas accessible côté Converse en l’état, mais vous, depuis votre client XMPP habituel, vous pouvez). D’ailleurs, à ce sujet : il est tout à fait possible d’inviter plusieurs personnes lors de la création du salon, il suffit pour ça de dupliquer la ligne directInvite pour envoyer à plusieurs adresses.
J’avais d’ailleurs pensé au début à faire un truc un peu plus sophistiqué, en rendant le Converse accessible depuis plusieurs adresses sur le serveur, et en ajoutant un petit bout de code côté JavaScript pour que converse invite un compte utilisateur différent selon l’adresse utilisée (à la façon des /~user, chaque utilisateur de la machine joignable à une adresse donnée). Ce n’est a priori pas tellement plus compliqué, mais bon, vu que je suis présentement le seul utilisateur à avoir un compte sur mon serveur, ça n’avait pas grand intérêt ^^

Voilà, je crois que là c’est à peu près tout. Merci à Maranda (dev de Metronome) et à jcbrand (dev de Converse) que j’ai un peu embêté quand je galérais sur les bonnes options à utiliser :slight_smile:
Et si vous voulez voir ce que ça donne chez moi (et donc venir me causer par la même occasion), vous n’avez qu’à vous rendre (pour le petit message qui vous dit si je suis connecté ou pas, c’est un truc fait main totalement ad hoc qui ne passe pas par XMPP, donc je ne pense pas que ce soit utile de développer).

(Dernière note : pour une raison qui m’échappe encore, l’internationalisation semble ne pas fonctionner, et Converse est donc en anglais bien que la config soit censée le forcer au français. Je ne sais pas d’où ça vient, tout coup de main pour investiguer le truc sera le bienvenu, mais bon, ce n’est pas non plus particulièrement gênant en l’état)

2 Likes