mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
1850 lines
48 KiB
Plaintext
1850 lines
48 KiB
Plaintext
// SPDX-FileCopyrightText: 2003-2026 Sébastien Helleu <flashcode@flashtux.org>
|
||
//
|
||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||
|
||
= Relai API WeeChat
|
||
:author: Sébastien Helleu
|
||
:email: flashcode@flashtux.org
|
||
:lang: fr
|
||
include::includes/attributes-fr.adoc[]
|
||
|
||
[[introduction]]
|
||
== Introduction
|
||
|
||
Ce document est une spécification du protocole relay _api_ : le protocole
|
||
utilisé pour relayer les données de WeeChat aux clients avec une API REST HTTP.
|
||
|
||
[[terminology]]
|
||
=== Terminologie
|
||
|
||
Les termes suivants sont utilisés dans ce document :
|
||
|
||
* _relay_ : il s'agit de l'extension "relay" de WeeChat, qui agit comme un
|
||
"serveur" et autorise les _clients_ à se connecter
|
||
* _client_ : il s'agit d'un logiciel connecté au _relay_ via une connexion réseau
|
||
(WeeChat lui-même ou une interface distante).
|
||
|
||
[[network_diagram]]
|
||
=== Diagramme réseau
|
||
|
||
Les _clients_ sont connectés au _relay_ comme dans le diagramme ci-dessous :
|
||
|
||
include::includes/relay.fr.adoc[tag=diagram]
|
||
|
||
[NOTE]
|
||
Tous les clients ici utilisent le protocole _api_ dans l'extension _relay_. +
|
||
L'extension _relay_ autorise aussi les protocoles _irc_ et _weechat_
|
||
(non décrits dans ce document).
|
||
|
||
[[protocol_generalities]]
|
||
== Généralités sur le protocole
|
||
|
||
* Les connexions du _client_ vers _relay_ sont faites avec des sockets TCP sur
|
||
l'IP/port utilisé par _relay_ pour écouter les nouvelles connexions.
|
||
* Le nombre de clients est limité par l'option _relay.network.max_clients_.
|
||
* Chaque _client_ est indépendant des autres clients.
|
||
* Le relai _api_ est une API REST HTTP utilisant le format JSON pour les
|
||
entrées/sorties.
|
||
* Les messages sont automatiquement compressés (deflate, gzip, zstd et permessage-deflate
|
||
pour le protocole websocket).
|
||
* WeeChat peut être utilisé comme client de ce relai.
|
||
|
||
[[api_versioning]]
|
||
=== Version de l'API
|
||
|
||
La version de l'API suit une https://semver.org/lang/fr/[version sémantique ^↗^^]
|
||
« pragmatique », comme WeeChat, sur 3 chiffres `X.Y.Z`, où :
|
||
|
||
* `X` est la version majeure
|
||
* `Y` est la version mineure
|
||
* `Z` est la version de patch.
|
||
|
||
Exemple : la version `2.0.0` apporte des changements incompatibles avec la version
|
||
`1.2.3`.
|
||
|
||
La version de l'API est retournée par la ressource <<resource_version,version>>.
|
||
|
||
[[api_schema]]
|
||
=== Schéma d'API
|
||
|
||
Vous pouvez parcourir et tester l'API en ligne : https://weechat.org/api/[API relay WeeChat ^↗^^].
|
||
|
||
[[response_codes]]
|
||
=== Codes réponse
|
||
|
||
Les codes réponse HTTP suivants peuvent être retournés au client :
|
||
|
||
* `200 OK` : réponse OK avec un corps (JSON)
|
||
* `204 No Content` : réponse OK sans corps
|
||
* `400 Bad Request` : requête invalide reçue
|
||
* `401 Unauthorized` : informations d'identification manquantes ou invalides
|
||
* `403 Forbidden` : permissions insuffisantes
|
||
* `404 Not Found` : ressource non trouvée
|
||
* `500 Internal Server Error` : erreur interne du serveur
|
||
* `503 Service Unavailable` : service non disponible
|
||
|
||
Lors d'une connexion via le protocole websocket, un code de réponse supplémentaire
|
||
est envoyé quand WeeChat pousse des données vers le client sur des évènements :
|
||
|
||
* `0 Event` : évènement poussé au client, si la synchronisation est activée par la
|
||
ressource <<resource_sync,sync>>.
|
||
|
||
[[date_format]]
|
||
=== Format de date
|
||
|
||
Le format de date est https://en.wikipedia.org/wiki/ISO_8601[ISO 8601 ^↗^^],
|
||
en utilisant le fuseau horaire UTC (cela est donc différent de l'affichage par
|
||
WeeChat qui utilise le fuseau horaire local). +
|
||
Les dates sont retournées avec le maximum de précision : jusqu'aux microsecondes
|
||
si cela est possible ou bien les millisecondes, ou juste secondes.
|
||
|
||
Exemples :
|
||
|
||
----
|
||
2023-12-05T19:46:03.847625Z
|
||
2023-12-05T19:46:03.847Z
|
||
2023-12-05T19:46:03Z
|
||
----
|
||
|
||
[[authentication]]
|
||
== Authentification
|
||
|
||
Le mot de passe doit être envoyé dans l'en-tête `Authorization` avec le schéma
|
||
d'authentification `Basic` ou l'en-tête `Sec-WebSocket-Protocol` (voir les
|
||
détails ci-dessous).
|
||
|
||
Le mot de passe peut être envoyé en clair ou haché, avec l'un des formats
|
||
suivants pour l'utilisateur et le mot de passe :
|
||
|
||
* `plain:<mot_de_passe>`
|
||
* `hash:sha256:<horodatage>:<hash>`
|
||
* `hash:sha512:<horodatage>:<hash>`
|
||
* `hash:pbkdf2+sha256:<horodatage>:<itérations>:<hash>`
|
||
* `hash:pbkdf2+sha512:<horodatage>:<itérations>:<hash>`
|
||
|
||
Où :
|
||
|
||
* `<mot_de_passe>` est le mot de passe en clair
|
||
* `<horodatage>` est l'horodatage courant sous forme d'entier (nombre de secondes
|
||
depuis l'époque Unix) ; il est utilisé pour prévenir les attaques par rejeu
|
||
* `<itérations>` est le nombre d'itérations (pour l'algorithme PBKDF2 seulement)
|
||
* `<hash>` est la valeur hachée de l'horodatage + mot de passe (en hexadécimal)
|
||
|
||
[NOTE]
|
||
Le nombre maximal de secondes autorisé avant et après l'heure reçue (lorsque le
|
||
mot de passe est haché) est configurable avec l'option _relay.network.time_window_.
|
||
|
||
Exemple :
|
||
|
||
* l'horodatage courant est `1706431066`
|
||
* le mot de passe est `secret_password`
|
||
* l'algorithme de hachage est `sha256`
|
||
* le résultat du "hash" est le SHA256 de la chaîne `1706431066secret_password`
|
||
qui est en hexadécimal : `dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6`
|
||
* l'en-tête `Authorization` est la valeur encodée en base64 de la chaîne
|
||
`hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6` :
|
||
`aGFzaDpzaGEyNTY6MTcwNjQzMTA2NjpkZmExZGIzZjZiYjY0NDVkMThkOWVjNzQyN2MxMGY2NDIxMjc0ZTNhNDc1MWU2YzFmZmM3ZGQyOGM5NGVhZGY2`.
|
||
|
||
Les en-têtes `Authorization` et `Sec-WebSocket-Protocol` sont autorisés dans
|
||
la première requête avec le protocole websocket ou toute requête HTTP dans
|
||
les autres cas.
|
||
|
||
Exemple de requête avec un mot de passe en clair :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/version'
|
||
----
|
||
|
||
Exemple de requête avec un mot de passe haché (SHA256) :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6' 'https://localhost:9000/api/version'
|
||
----
|
||
|
||
Si TOTP (« Time-based One-Time Password » : mot de passe à usage unique basé sur le
|
||
temps) est activé dans WeeChat/relay (option `relay.network.totp_secret` définie),
|
||
vous devez envoyer la valeur TOTP dans l'en-tête `x-weechat-totp` comme ceci :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6' -H "x-weechat-totp: 123456" 'https://localhost:9000/api/version'
|
||
----
|
||
|
||
En cas d'erreur, une réponse `401 Unauthorized` est retournée avec un champ
|
||
`error` dans les données JSON qui décrit l'erreur.
|
||
|
||
Réponse : mot de passe manquant :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Missing password"
|
||
}
|
||
----
|
||
|
||
Réponse : mot de passe invalide :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Invalid password"
|
||
}
|
||
----
|
||
|
||
Réponse : algorithme de hachage invalide :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Invalid hash algorithm (not found or not supported)"
|
||
}
|
||
----
|
||
|
||
Réponse : horodatage invalide :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Invalid timestamp"
|
||
}
|
||
----
|
||
|
||
Réponse : nombre d'itérations invalide :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Invalid number of iterations"
|
||
}
|
||
----
|
||
|
||
Réponse : TOTP manquant :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Missing TOTP"
|
||
}
|
||
----
|
||
|
||
Réponse : TOTP invalide :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 401 Unauthorized
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Invalid TOTP"
|
||
}
|
||
----
|
||
|
||
[[authentication_sec_websocket_protocol]]
|
||
=== Sec-WebSocket-Protocol
|
||
|
||
L'API WebSocket de JavaScript utilisée dans les navigateurs actuels ne supporte pas
|
||
l'utilisation de l'en-tête `Authorization`. Il est donc aussi possible d'envoyer
|
||
le mot de passe dans l'en-tête `Sec-WebSocket-Protocol`, qui est le seul en-tête
|
||
utilisable avec cette API.
|
||
|
||
Pour utiliser cet en-tête, vous devez spécifier les sous-protocoles `api.weechat`
|
||
et `base64url.bearer.authorization.weechat.<auth>` où `<auth>` est la chaîne
|
||
encodée en base64url avec le mot de passe, dans le même format que ci-dessus.
|
||
|
||
Exemple avec un mot de passe `secret_password` en clair. Cela produit une chaîne
|
||
encodée en base64url avec le contenu `plain:secret_password` qui est
|
||
`cGxhaW46c2VjcmV0X3Bhc3N3b3Jk`.
|
||
|
||
----
|
||
Sec-WebSocket-Protocol: api.weechat, base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk
|
||
----
|
||
|
||
Cela peut être défini avec l'API WebSocket de JavaScript comme ceci :
|
||
|
||
[source,javascript]
|
||
----
|
||
const ws = new WebSocket("wss://localhost:9000/api", [
|
||
"api.weechat",
|
||
"base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk",
|
||
])
|
||
----
|
||
|
||
[[compression]]
|
||
== Compression
|
||
|
||
La compression du corps de la réponse est automatique et basée sur l'en-tête
|
||
`Accept-Encoding` envoyé par le client.
|
||
|
||
Les formats de compression supportés sont :
|
||
|
||
* `deflate` (zlib)
|
||
* `gzip`
|
||
* `zstd`
|
||
|
||
Exemple de requête :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -H "Accept-Encoding: gzip" 'https://localhost:9000/api/version'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
Content-Type: application/json; charset=utf-8
|
||
Content-Encoding: gzip
|
||
Content-Length: 77
|
||
----
|
||
|
||
----
|
||
[77 octets de données]
|
||
----
|
||
|
||
[NOTE]
|
||
Avec le protocole websocket, l'extension "permessage-deflate" autorise
|
||
la compression des messages avec zlib.
|
||
|
||
[[resources]]
|
||
== Ressources
|
||
|
||
[[resource_preflight]]
|
||
=== Requête « preflight »
|
||
|
||
La requête « preflight » avec la méthode HTTP `OPTIONS` est utilisée par les
|
||
navigateurs web pour vérifier que le serveur (WeeChat) autorisera la requête
|
||
proprement dite.
|
||
|
||
Exemple de requête : vérifier que la requête `GET /api/version` est autorisée :
|
||
|
||
[source,http]
|
||
----
|
||
OPTIONS /api/version HTTP/1.1
|
||
Host: localhost:9000
|
||
Connection: keep-alive
|
||
Accept: */*
|
||
Access-Control-Request-Method: GET
|
||
Access-Control-Request-Headers: authorization
|
||
Origin: https://localhost
|
||
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
|
||
Sec-Fetch-Mode: cors
|
||
Sec-Fetch-Site: same-site
|
||
Sec-Fetch-Dest: empty
|
||
Referer: https://localhost/
|
||
Accept-Encoding: gzip, deflate, br, zstd
|
||
Accept-Language: en-US,en;q=0.9,fr;q=0.8
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 204 No Content
|
||
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
|
||
Access-Control-Allow-Headers: origin, content-type, accept, authorization
|
||
Access-Control-Allow-Origin: *
|
||
Content-Type: application/json; charset=utf-8
|
||
Content-Length: 0
|
||
----
|
||
|
||
[[resource_handshake]]
|
||
=== Poignée de main
|
||
|
||
Effectuer une poignée de main entre le client et WeeChat.
|
||
|
||
Cette ressource est accessible sans authentification.
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
POST /api/handshake
|
||
----
|
||
|
||
Paramètres du corps :
|
||
|
||
* `password_hash_algo` (tableau de chaînes, facultatif) : liste des algorithmes
|
||
de hachage supportés par le client, chaque chaîne peut être :
|
||
** `plain` : mot de passe en clair (pas de hachage)
|
||
** `sha256` : hash SHA256
|
||
** `sha512` : hash SHA512
|
||
** `pbkdf2+sha256` : hash PBKDF2 avec SHA256
|
||
** `pbkdf2+sha512` : hash PBKDF2 avec SHA512
|
||
|
||
La réponse a les champs suivants :
|
||
|
||
* `password_hash_algo` (chaîne) : l'algorithme de hachage à utiliser
|
||
(`null` si aucun algorithme n'est compatible)
|
||
* `password_hash_iterations` (entier) : le nombre d'itérations à utiliser si le
|
||
hachage PBKDF2 est utilisé
|
||
* `totp` (booléen) : `true` si le TOTP est activé dans WeeChat (le client doit
|
||
alors envoyer le TOTP dans un en-tête spécifique), `false` sinon
|
||
|
||
Exemple de requête :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -X POST -d '{"password_hash_algo": ["plain", "sha256", "sha512"]}' 'https://localhost:9000/api/handshake'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"password_hash_algo": "sha512",
|
||
"password_hash_iterations": 100000,
|
||
"totp": false
|
||
}
|
||
----
|
||
|
||
[[resource_version]]
|
||
=== Version
|
||
|
||
Retourner la version de WeeChat et du relai API.
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
GET /api/version
|
||
----
|
||
|
||
Exemple de requête :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/version'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"weechat_version": "4.2.0-dev",
|
||
"weechat_version_git": "v4.1.0-143-g0b1cda1c4",
|
||
"weechat_version_number": 67239936,
|
||
"relay_api_version": "0.0.1",
|
||
"relay_api_version_number": 1
|
||
}
|
||
----
|
||
|
||
[[resource_buffers]]
|
||
=== Tampons
|
||
|
||
Retourner les tampons, lignes et pseudos.
|
||
|
||
Points de terminaison :
|
||
|
||
----
|
||
GET /api/buffers
|
||
GET /api/buffers/{buffer_id}
|
||
GET /api/buffers/{buffer_name}
|
||
----
|
||
|
||
Paramètres du chemin :
|
||
|
||
* `buffer_id` (entier, facultatif) : identifiant unique du tampon (à ne pas
|
||
confondre avec le numéro du tampon, qui est différent)
|
||
* `buffer_name` (chaîne, facultatif) : nom de tampon
|
||
|
||
Paramètres de la requête :
|
||
|
||
* `lines` (entier, facultatif, par défaut : `0`) : nombre de lignes à retourner
|
||
dans les tampons avec contenu formaté :
|
||
** nombre négatif : retourner N lignes depuis la fin du tampon
|
||
(lignes les plus récentes)
|
||
** `0` : ne retourner aucune ligne
|
||
** nombre positif : retourner N lignes depuis le début du tampon
|
||
(lignes les plus anciennes)
|
||
* `lines_free` (entier, facultatif, par défaut : `0` si `lines` est `0`, sinon
|
||
toutes les lignes) : nombre de lignes à retourner avec un contenu libre :
|
||
** nombre négatif : retourner N lignes depuis la fin du tampon
|
||
** `0` : ne retourner aucune ligne
|
||
** nombre positif : retourner N lignes depuis le début du tampon
|
||
* `nicks` (booléen, facultatif, par défaut : `false`) : retourner les pseudos
|
||
du tampon
|
||
* `colors` (chaîne, facultatif, par défaut : `ansi`) : comment les chaînes avec
|
||
des couleurs sont retournées :
|
||
** `ansi` : retourner les codes couleur ANSI
|
||
** `weechat` : retourner les codes couleur internes WeeChat
|
||
** `strip` : supprimer les couleurs
|
||
|
||
Exemple de requête : obtenir tous les tampons sans les lignes :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
[
|
||
{
|
||
"id": 1709932823238637,
|
||
"name": "core.weechat",
|
||
"short_name": "weechat",
|
||
"number": 1,
|
||
"type": "formatted",
|
||
"title": "WeeChat 4.2.0-dev (C) 2003-2023 - https://weechat.org/",
|
||
"modes": "",
|
||
"input_prompt": "",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": false,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": true,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "core",
|
||
"name": "weechat"
|
||
},
|
||
"keys": []
|
||
},
|
||
{
|
||
"id": 1709932823423765,
|
||
"name": "irc.server.libera",
|
||
"short_name": "libera",
|
||
"number": 2,
|
||
"type": "formatted",
|
||
"title": "IRC: irc.libera.chat/6697 (2001:4b7a:a008::6667)",
|
||
"modes": "",
|
||
"input_prompt": "",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": false,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": true,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "irc",
|
||
"name": "server.libera",
|
||
"type": "server",
|
||
"server": "libera",
|
||
"channel": "libera",
|
||
"charset_modifier": "irc.libera",
|
||
"nick": "alice",
|
||
"tls_version": "TLS1.3",
|
||
"host": "~alice@example.com"
|
||
},
|
||
"keys": []
|
||
},
|
||
{
|
||
"id": 1709932823649069,
|
||
"name": "irc.libera.#weechat",
|
||
"short_name": "#weechat",
|
||
"number": 3,
|
||
"type": "formatted",
|
||
"title": "Welcome to the WeeChat official support channel",
|
||
"modes": "+nt",
|
||
"input_prompt": "\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": true,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": false,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "irc",
|
||
"name": "libera.#weechat",
|
||
"type": "channel",
|
||
"server": "libera",
|
||
"channel": "#weechat",
|
||
"nick": "alice",
|
||
"host": "~alice@example.com"
|
||
},
|
||
"keys": []
|
||
}
|
||
]
|
||
----
|
||
|
||
Exemple de requête : obtenir le tampon WeeChat "core" avec la dernière ligne
|
||
seulement et aucun code couleur :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/core.weechat?lines=-1&colors=strip'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"id": 1709932823238637,
|
||
"name": "core.weechat",
|
||
"short_name": "weechat",
|
||
"number": 1,
|
||
"type": "formatted",
|
||
"title": "WeeChat 4.2.0-dev (C) 2003-2023 - https://weechat.org/",
|
||
"modes": "",
|
||
"input_prompt": "",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": false,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": true,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "core",
|
||
"name": "weechat"
|
||
},
|
||
"keys": [],
|
||
"lines": [
|
||
{
|
||
"id": 10,
|
||
"y": -1,
|
||
"date": "2023-12-24T08:17:20.786538Z",
|
||
"date_printed": "2023-12-24T08:17:20.786538Z",
|
||
"displayed": true,
|
||
"highlight": false,
|
||
"notify_level": 0,
|
||
"prefix": "",
|
||
"message": "Plugins loaded: alias, buflist, charset, exec, fifo, fset, guile, irc, javascript, logger, lua, perl, php, python, relay, ruby, script, spell, tcl, trigger, typing, xfer",
|
||
"tags": []
|
||
}
|
||
]
|
||
}
|
||
----
|
||
|
||
Exemple de requête : obtenir un tampon de canal IRC avec les pseudos :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat?nicks=true'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"id": 1709932823649069,
|
||
"name": "irc.libera.#weechat",
|
||
"short_name": "#weechat",
|
||
"number": 3,
|
||
"type": "formatted",
|
||
"title": "Welcome to the WeeChat official support channel",
|
||
"modes": "+nt",
|
||
"input_prompt": "\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": true,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": false,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "irc",
|
||
"name": "libera.#weechat",
|
||
"type": "channel",
|
||
"server": "libera",
|
||
"channel": "#weechat",
|
||
"nick": "alice",
|
||
"host": "~alice@example.com"
|
||
},
|
||
"keys": [],
|
||
"nicklist_root": {
|
||
"id": 0,
|
||
"parent_group_id": -1,
|
||
"name": "root",
|
||
"color_name": "",
|
||
"color": "",
|
||
"visible": false,
|
||
"groups": [
|
||
{
|
||
"id": 1709932823649181,
|
||
"parent_group_id": 0,
|
||
"name": "000|o",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": [
|
||
{
|
||
"id": 1709932823649184,
|
||
"parent_group_id": 1709932823649181,
|
||
"prefix": "@",
|
||
"prefix_color_name": "lightgreen",
|
||
"prefix_color": "\u001b[92m",
|
||
"name": "alice",
|
||
"color_name": "bar_fg",
|
||
"color": "",
|
||
"visible": true
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": 1709932823649189,
|
||
"parent_group_id": 0,
|
||
"name": "001|h",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
},
|
||
{
|
||
"id": 1709932823649203,
|
||
"parent_group_id": 0,
|
||
"name": "002|v",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
},
|
||
{
|
||
"id": 1709932823649210,
|
||
"parent_group_id": 0,
|
||
"name": "999|...",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
}
|
||
],
|
||
"nicks": []
|
||
}
|
||
}
|
||
----
|
||
|
||
Exemple de requête : obtenir le tampon fset :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/fset.fset'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"id": 1709932823897200,
|
||
"name": "fset.fset",
|
||
"short_name": "",
|
||
"number": 4,
|
||
"type": "free",
|
||
"title": "\u001b[96m1/\u001b[36m3565 | Filter: \u001b[93m* | Sort: \u001b[97m~name | Key(input): alt+space=toggle boolean, alt+'-'(-)=subtract 1 or set, alt+'+'(+)=add 1 or append, alt+f,alt+r(r)=reset, alt+f,alt+u(u)=unset, alt+enter(s)=set, alt+f,alt+n(n)=set new value, alt+f,alt+a(a)=append, alt+','=mark/unmark, shift+down=mark and move down, shift+up=move up and mark, ($)=refresh, ($$)=unmark/refresh, (m)=mark matching options, (u)=unmark matching options, alt+p(p)=toggle plugins desc, alt+v(v)=toggle help bar, ctrl+x(x)=switch format, (q)=close buffer",
|
||
"modes": "",
|
||
"input_prompt": "",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": false,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": true,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "fset",
|
||
"name": "fset",
|
||
"type": "option",
|
||
"filter": "*"
|
||
},
|
||
"keys": [
|
||
{
|
||
"key": "ctrl-l",
|
||
"command": "/fset -refresh"
|
||
},
|
||
{
|
||
"key": "ctrl-n",
|
||
"command": "/eval ${if:${weechat.bar.buflist.hidden}?/fset -down:/buffer +1}"
|
||
},
|
||
{
|
||
"key": "ctrl-x",
|
||
"command": "/fset -format"
|
||
},
|
||
{
|
||
"key": "down",
|
||
"command": "/fset -down"
|
||
},
|
||
{
|
||
"key": "f11",
|
||
"command": "/fset -left"
|
||
},
|
||
{
|
||
"key": "f12",
|
||
"command": "/fset -right"
|
||
},
|
||
{
|
||
"key": "meta-+",
|
||
"command": "/fset -add 1"
|
||
},
|
||
{
|
||
"key": "meta--",
|
||
"command": "/fset -add -1"
|
||
},
|
||
{
|
||
"key": "meta-comma",
|
||
"command": "/fset -mark"
|
||
},
|
||
{
|
||
"key": "meta-end",
|
||
"command": "/fset -go end"
|
||
},
|
||
{
|
||
"key": "meta-f,meta-a",
|
||
"command": "/fset -append"
|
||
},
|
||
{
|
||
"key": "meta-f,meta-n",
|
||
"command": "/fset -setnew"
|
||
},
|
||
{
|
||
"key": "meta-f,meta-r",
|
||
"command": "/fset -reset"
|
||
},
|
||
{
|
||
"key": "meta-f,meta-u",
|
||
"command": "/fset -unset"
|
||
},
|
||
{
|
||
"key": "meta-home",
|
||
"command": "/fset -go 0"
|
||
},
|
||
{
|
||
"key": "meta-p",
|
||
"command": "/mute /set fset.look.show_plugins_desc toggle"
|
||
},
|
||
{
|
||
"key": "meta-q",
|
||
"command": "/input insert meta-q fset ${property}"
|
||
},
|
||
{
|
||
"key": "meta-return",
|
||
"command": "/fset -set"
|
||
},
|
||
{
|
||
"key": "meta-space",
|
||
"command": "/fset -toggle"
|
||
},
|
||
{
|
||
"key": "meta-v",
|
||
"command": "/bar toggle fset"
|
||
},
|
||
{
|
||
"key": "shift-down",
|
||
"command": "/fset -mark; /fset -down"
|
||
},
|
||
{
|
||
"key": "shift-up",
|
||
"command": "/fset -up; /fset -mark"
|
||
},
|
||
{
|
||
"key": "up",
|
||
"command": "/fset -up"
|
||
}
|
||
]
|
||
}
|
||
----
|
||
|
||
[[resource_buffers_lines]]
|
||
==== Lignes
|
||
|
||
Retourner les lignes d'un tampon.
|
||
|
||
Points de terminaison :
|
||
|
||
----
|
||
GET /api/buffers/{buffer_id}/lines
|
||
GET /api/buffers/{buffer_id}/lines/{line_id}
|
||
GET /api/buffers/{buffer_name}/lines
|
||
GET /api/buffers/{buffer_name}/lines/{line_id}
|
||
----
|
||
|
||
Paramètres de chemin :
|
||
|
||
* `buffer_id` (entier, **obligatoire**) : identifiant unique de tampon (à ne pas
|
||
confondre avec le numéro du tampon, qui est différent)
|
||
* `buffer_name` (chaîne, **obligatoire**) : nom du tampon
|
||
* `line_id` (entier, facultatif) : retourner une seule ligne avec cet identifiant
|
||
|
||
Paramètres de requête :
|
||
|
||
* `lines` (entier, facultatif, par défaut : toutes les lignes) : nombre de lignes
|
||
à retourner :
|
||
** nombre négatif : retourner N lignes depuis la fin du tampon
|
||
(lignes les plus récentes)
|
||
** `0` : ne retourner aucune ligne (autorisé mais ne fait pas vraiment de sens
|
||
avec cette ressource)
|
||
** nombre positif : retourner N lignes depuis le début du tampon
|
||
(lignes les plus anciennes)
|
||
* `colors` (chaîne, facultatif, par défaut : `ansi`) : comment les chaînes avec
|
||
des couleurs sont retournées :
|
||
** `ansi` : retourner les codes couleur ANSI
|
||
** `weechat` : retourner les codes couleur internes WeeChat
|
||
** `strip` : supprimer les couleurs
|
||
|
||
Exemple de requête : obtenir les 1000 dernières lignes d'un tampon, sans codes
|
||
couleur :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat/lines?lines=-1000&colors=strip'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
[
|
||
{
|
||
"id": 0,
|
||
"y": -1,
|
||
"date": "2023-12-05T19:46:03.847625Z",
|
||
"date_printed": "2023-12-05T19:46:03.847625Z",
|
||
"displayed": true,
|
||
"highlight": false,
|
||
"notify_level": 0,
|
||
"prefix": "-->",
|
||
"message": "alice (~alice@example.com) has joined #test",
|
||
"tags": [
|
||
"irc_join",
|
||
"irc_tag_account=alice",
|
||
"irc_tag_time=2023-12-05T19:46:03.847Z",
|
||
"nick_alice",
|
||
"host_~alice@example.com",
|
||
"log4"
|
||
]
|
||
},
|
||
{
|
||
"id": 1,
|
||
"y": -1,
|
||
"date": "2023-12-05T19:46:03.986543Z",
|
||
"date_printed": "2023-12-05T19:46:03.986543Z",
|
||
"displayed": true,
|
||
"highlight": false,
|
||
"notify_level": 0,
|
||
"prefix": "--",
|
||
"message": "Mode #test [+Cnst] by zirconium.libera.chat",
|
||
"tags": [
|
||
"irc_mode",
|
||
"irc_tag_time=2023-12-05T19:46:03.986Z",
|
||
"nick_zirconium.libera.chat",
|
||
"log3"
|
||
]
|
||
},
|
||
{
|
||
"id": 2,
|
||
"y": -1,
|
||
"date": "2023-12-05T19:46:04.287546Z",
|
||
"date_printed": "2023-12-05T19:46:04.287546Z",
|
||
"displayed": true,
|
||
"highlight": false,
|
||
"notify_level": 0,
|
||
"prefix": "--",
|
||
"message": "Channel #test: 1 nick (1 op, 0 voiced, 0 regular)",
|
||
"tags": [
|
||
"irc_366",
|
||
"irc_numeric",
|
||
"irc_tag_time=2023-12-05T19:46:04.287Z",
|
||
"nick_zirconium.libera.chat",
|
||
"log3"
|
||
]
|
||
}
|
||
]
|
||
----
|
||
|
||
[[resources_buffers_nicks]]
|
||
==== Pseudos
|
||
|
||
Retourner les pseudos d'un tampon.
|
||
|
||
Points de terminaison :
|
||
|
||
----
|
||
GET /api/buffers/{buffer_id}/nicks
|
||
GET /api/buffers/{buffer_name}/nicks
|
||
----
|
||
|
||
Paramètres de chemin :
|
||
|
||
* `buffer_id` (entier, **obligatoire**) : identifiant unique de tampon (à ne pas
|
||
confondre avec le numéro du tampon, qui est différent)
|
||
* `buffer_name` (chaîne, **obligatoire**) : nom du tampon
|
||
|
||
Exemple de requête : obtenir les pseudos d'un tampon :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat/nicks'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"id": 0,
|
||
"parent_group_id": -1,
|
||
"name": "root",
|
||
"color_name": "",
|
||
"color": "",
|
||
"visible": false,
|
||
"groups": [
|
||
{
|
||
"id": 1709932823649181,
|
||
"parent_group_id": 0,
|
||
"name": "000|o",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": [
|
||
{
|
||
"id": 1709932823649184,
|
||
"parent_group_id": 1709932823649181,
|
||
"prefix": "@",
|
||
"prefix_color_name": "lightgreen",
|
||
"prefix_color": "\u001b[92m",
|
||
"name": "alice",
|
||
"color_name": "bar_fg",
|
||
"color": "",
|
||
"visible": true
|
||
}
|
||
]
|
||
},
|
||
{
|
||
"id": 1709932823649189,
|
||
"parent_group_id": 0,
|
||
"name": "001|h",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
},
|
||
{
|
||
"id": 1709932823649203,
|
||
"parent_group_id": 0,
|
||
"name": "002|v",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
},
|
||
{
|
||
"id": 1709932823649210,
|
||
"parent_group_id": 0,
|
||
"name": "999|...",
|
||
"color_name": "weechat.color.nicklist_group",
|
||
"color": "\u001b[32m",
|
||
"visible": true,
|
||
"groups": [],
|
||
"nicks": []
|
||
}
|
||
],
|
||
"nicks": []
|
||
}
|
||
----
|
||
|
||
[[resource_hotlist]]
|
||
=== Hotlist
|
||
|
||
Retourner la "hotlist" (notification d'activité sur les tampons).
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
GET /api/hotlist
|
||
----
|
||
|
||
Exemple de requête :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/hotlist'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
[
|
||
{
|
||
"priority": 0,
|
||
"date": "2024-03-17T16:38:51.572834Z",
|
||
"buffer_id": 1710693531508204,
|
||
"count": [
|
||
44,
|
||
0,
|
||
0,
|
||
0
|
||
]
|
||
},
|
||
{
|
||
"priority": 0,
|
||
"date": "2024-03-17T16:38:51.573028Z",
|
||
"buffer_id": 1710693530395959,
|
||
"count": [
|
||
14,
|
||
0,
|
||
0,
|
||
0
|
||
]
|
||
},
|
||
{
|
||
"priority": 0,
|
||
"date": "2024-03-17T16:38:51.611617Z",
|
||
"buffer_id": 1710693531529248,
|
||
"count": [
|
||
4,
|
||
0,
|
||
0,
|
||
0
|
||
]
|
||
}
|
||
]
|
||
----
|
||
|
||
[[resource_input]]
|
||
=== Entrée
|
||
|
||
Envoyer une commande ou du texte à un tampon.
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
POST /api/input
|
||
----
|
||
|
||
Paramètres du corps :
|
||
|
||
* `buffer_id` (entier, facultatif) : identifiant unique du tampon (à ne pas
|
||
confondre avec le numéro du tampon, qui est différent)
|
||
* `buffer_name` (chaîne, facultatif, par défaut : `core.weechat`) : nom de tampon
|
||
* `command` (chaîne, **obligatoire**) : commande ou texte à envoyer au tampon
|
||
|
||
Exemple de requête : dire "hello!" sur le canal #weechat :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST \
|
||
-d '{"buffer_name": "irc.libera.#weechat", "command": "hello!"}' \
|
||
'https://localhost:9000/api/input'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 204 No content
|
||
----
|
||
|
||
Exemple de requête : quitter et fermer le canal #weechat (commande exécutée
|
||
sur le tampon "core" WeeChat) :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST \
|
||
-d '{"command": "/buffer close irc.libera.#weechat"}' \
|
||
'https://localhost:9000/api/input'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 204 No content
|
||
----
|
||
|
||
[[resource_completion]]
|
||
=== Complétion
|
||
|
||
Compléter une commande ou du texte de l'utilisateur sur un tampon.
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
POST /api/completion
|
||
----
|
||
|
||
Paramètres du corps :
|
||
|
||
* `buffer_id` (entier, facultatif) : identifiant unique du tampon (à ne pas
|
||
confondre avec le numéro du tampon, qui est différent)
|
||
* `buffer_name` (chaîne, facultatif, par défaut : `core.weechat`) : nom de tampon
|
||
* `command` (chaîne, **obligatoire**) : commande ou texte à compléter
|
||
* `position` (entier, facultatif, par défaut : fin de la chaîne) : position
|
||
dans la commande (la première position est 0)
|
||
|
||
Exemple de requête : compléter la commande `/qu` sur le canal #weechat :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST \
|
||
-d '{"buffer_name": "irc.libera.#weechat", "command": "/qu"}' \
|
||
'https://localhost:9000/api/completion'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"context": "command",
|
||
"base_word": "qu",
|
||
"position_replace": 1,
|
||
"add_space": true,
|
||
"list": [
|
||
"query",
|
||
"quiet",
|
||
"quit",
|
||
"quote"
|
||
]
|
||
}
|
||
----
|
||
|
||
[[resource_ping]]
|
||
=== Ping
|
||
|
||
Envoyer une requête "ping".
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
POST /api/ping
|
||
----
|
||
|
||
Paramètres du corps :
|
||
|
||
* `data` (chaîne, facultatif) : chaîne qui est retournée dans la réponse
|
||
|
||
Exemple de requête : pas de corps :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST 'https://localhost:9000/api/ping'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 204 No content
|
||
----
|
||
|
||
Exemple de requête : avec des données :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST \
|
||
-d '{"data": "1702835741"}' \
|
||
'https://localhost:9000/api/ping'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 200 OK
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"data": "1702835741"
|
||
}
|
||
----
|
||
|
||
[[resource_sync]]
|
||
=== Sync
|
||
|
||
Démarrer ou arrêter la synchronisation des données avec WeeChat.
|
||
|
||
Cette ressource ne peut être utilisée que si le client est connecté avec le protocole
|
||
websocket, pour que WeeChat puisse pousser des messages au client à tout moment.
|
||
|
||
Si cette ressource est utilisée sans connexion websocket, une erreur 403 (Forbidden)
|
||
est retournée.
|
||
|
||
Point de terminaison :
|
||
|
||
----
|
||
POST /api/sync
|
||
----
|
||
|
||
Paramètres du corps :
|
||
|
||
* `sync` (booléen, facultatif, par défaut : `true`) : `true` pour activer la
|
||
synchronisation avec WeeChat
|
||
* `nicks` (booléen, facultatif, par défaut : `true`) : `true` pour recevoir
|
||
les mises à jour de pseudos dans les tampons (utilisé seulement si `sync`
|
||
vaut `true`)
|
||
* `input` (booléen, facultatif, par défaut : `true`) : `true` pour synchroniser
|
||
l'entrée de tampon depuis le relai distant vers le client local (utilisé
|
||
seulement si `sync` vaut `true`)
|
||
* `colors` (chaîne, facultatif, par défaut : `ansi`) : comment les chaînes avec
|
||
des couleurs sont retournées :
|
||
** `ansi` : retourner les codes couleur ANSI
|
||
** `weechat` : retourner les codes couleur internes WeeChat
|
||
** `strip` : supprimer les couleurs
|
||
|
||
Exemple de requête avec le protocole websocket :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"request": "POST /api/sync",
|
||
"body": {
|
||
"nicks": false
|
||
}
|
||
}
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 204,
|
||
"message": "No Content",
|
||
"request": "POST /api/sync",
|
||
"request_body": {
|
||
"nicks": false
|
||
},
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|
||
|
||
Exemple de requête dans le protocole websocket (non autorisé) :
|
||
|
||
[source,shell]
|
||
----
|
||
curl -L -u 'plain:secret_password' -X POST \
|
||
-d '{"nicks": false}' \
|
||
'https://localhost:9000/api/sync'
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 403 Forbidden
|
||
----
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"error": "Sync resource is available only with a websocket connection"
|
||
}
|
||
----
|
||
|
||
[[websocket]]
|
||
== Websocket
|
||
|
||
Le protocole websocket est utilisé pour effectuer une connexion persistante entre
|
||
le client et WeeChat et recevoir les évènements en temps réel si la synchronisation
|
||
est activée avec la ressource <<resource_sync,sync>>.
|
||
|
||
L'authentification doit être effectuée seulement une fois lorsque le protocole
|
||
websocket est utilisé (voir <<authentication,authentification>>).
|
||
|
||
[[websocket_handshake]]
|
||
=== Poignée de main websocket
|
||
|
||
Pour établir la connexion, une poignée de main est effectuée sur le point de
|
||
terminaison `/api` et ressemble à ceci :
|
||
|
||
[source,http]
|
||
----
|
||
GET /api HTTP/1.1
|
||
Host: localhost:9000
|
||
Connection: Upgrade
|
||
Pragma: no-cache
|
||
Cache-Control: no-cache
|
||
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
|
||
Upgrade: websocket
|
||
Origin: https://example.com
|
||
Sec-WebSocket-Version: 13
|
||
Accept-Encoding: gzip, deflate, br
|
||
Accept-Language: en-US,en;q=0.9,fr;q=0.8
|
||
Sec-WebSocket-Key: 2XE8VAJktqi3Tpw5QnfxVQ==
|
||
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
|
||
----
|
||
|
||
WeeChat retourne sa réponse à la poignée de main pour confirmer que le protocole
|
||
websocket est bien supporté (et que l'authentification est réussie) :
|
||
|
||
[source,http]
|
||
----
|
||
HTTP/1.1 101 Switching Protocols
|
||
Upgrade: websocket
|
||
Connection: Upgrade
|
||
Sec-WebSocket-Accept: PaY9vRflWeOKuD0/F7e5gD9At9U=
|
||
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
|
||
----
|
||
|
||
[NOTE]
|
||
La valeur `Sec-WebSocket-Accept` retournée est le hachage SHA-1 de la valeur
|
||
reçue, concaténée au GUID `258EAFA5-E914-47DA-95CA-C5AB0DC85B11`
|
||
(le hachage SHA-1 est encodé en base64). +
|
||
Dans l'exemple ci-dessus, le hachage SHA-1 de
|
||
`2XE8VAJktqi3Tpw5QnfxVQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11` est
|
||
`PaY9vRflWeOKuD0/F7e5gD9At9U=` (en base64).
|
||
|
||
[[websocket_frames]]
|
||
=== Frames
|
||
|
||
Lorsque le client est connecté via le protocole websocket :
|
||
|
||
* les requêtes et réponses sont en JSON, à l'intérieur des "frames" websocket
|
||
* si la synchronisation est activée avec la ressource <<resource_sync,sync>>,
|
||
WeeChat peut envoyer des "frames" au client à tout moment.
|
||
|
||
Les requêtes envoyées vers WeeChat sont faites avec un objet JSON qui contient
|
||
les champs suivants :
|
||
|
||
* `request` (chaîne) : la méthode HTTP et le chemin
|
||
(exemple : `GET /api/buffers?lines=-100`)
|
||
* `body` (objet ou tableau) : le corps (facultatif, pour les méthodes `POST` et `PUT`)
|
||
* `request_id` (chaîne) : identifiant renvoyé dans la réponse
|
||
|
||
Plusieurs requêtes peuvent être envoyées simultanément avec un tableau d'objets,
|
||
chaque objet étant une requête séparée. +
|
||
Les requêtes sont exécutées dans l'ordre reçu (voir l'exemple ci-dessous).
|
||
|
||
Les réponses vers le client sont faites avec un objet JSON qui contient
|
||
les champs suivants :
|
||
|
||
* `code` (entier) : code réponse HTTP (exemple : `200`)
|
||
* `message` (chaîne) : message pour le code (exemple : `OK`)
|
||
* `request` (chaîne) : la requête (exemple : `GET /api/buffers?lines=-100`)
|
||
* `request_body` (objet) : le corps de la requête, ou `null` si la requête
|
||
n'avait pas de corps
|
||
* `request_id` (chaîne) : l'identifiant de la requête, ou `null` si la requête
|
||
n'avait pas d'identifiant
|
||
* `body_type` (chaîne) : type des objets retournés dans le corps (voir ci-dessous),
|
||
ou `null` si la réponse n'a pas de corps
|
||
* `body` (objet ou tableau) : le corps retourné, ou `null` si la réponse n'a pas
|
||
de corps
|
||
|
||
Les types de corps qui peuvent être retournés :
|
||
|
||
* `handshake` (objet)
|
||
* `version` (objet)
|
||
* `buffers` (tableau)
|
||
* `buffer` (objet)
|
||
* `lines` (tableau)
|
||
* `line` (objet)
|
||
* `nick_group` (objet)
|
||
* `nick` (objet)
|
||
* `hotlist` (objet)
|
||
* `ping` (objet)
|
||
|
||
[TIP]
|
||
Vous pouvez parcourir les schémas en ligne : https://weechat.org/api/[API relay WeeChat ^↗^^].
|
||
|
||
Exemple de requête : obtenir la version :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"request": "GET /api/version",
|
||
"request_id": "get_version"
|
||
}
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 200,
|
||
"message": "OK",
|
||
"request": "GET /api/version",
|
||
"request_body": null,
|
||
"request_id": "get_version",
|
||
"body_type": "version",
|
||
"body": {
|
||
"weechat_version": "4.2.0-dev",
|
||
"weechat_version_git": "v4.1.0-143-g0b1cda1c4",
|
||
"weechat_version_number": 67239936,
|
||
"relay_api_version": "0.0.1",
|
||
"relay_api_version_number": 1
|
||
}
|
||
}
|
||
----
|
||
|
||
Exemple de requête : dire "hello!" sur le canal #weechat :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"request": "POST /api/input",
|
||
"body": {
|
||
"buffer_name": "irc.libera.#weechat",
|
||
"command": "hello!"
|
||
}
|
||
}
|
||
----
|
||
|
||
Réponse :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 204,
|
||
"message": "No Content",
|
||
"request": "POST /api/input",
|
||
"request_body": {
|
||
"buffer_name": "irc.libera.#weechat",
|
||
"command": "hello!"
|
||
},
|
||
"request_id": null,
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|
||
|
||
Exemple de requêtes : envoyer deux requêtes en même temps : obtenir la liste des
|
||
tampons avec les lignes et les pseudos, puis se synchroniser avec le relay distant :
|
||
|
||
[source,json]
|
||
----
|
||
[
|
||
{
|
||
"request": "GET /api/buffers?lines=-1000&nicks=true&colors=weechat",
|
||
"request_id": "initial_sync"
|
||
},
|
||
{
|
||
"request": "POST /api/sync",
|
||
"body": {
|
||
"colors": "weechat"
|
||
}
|
||
}
|
||
]
|
||
----
|
||
|
||
[NOTE]
|
||
Il est recommandé d'envoyer la requête de synchronisation en même temps que la
|
||
première requête qui récupère les données, afin qu'aucun évènement ne soit manqué.
|
||
|
||
Première réponse (le "body" avec les tampons est tronqué pour la lisibilité) :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 200,
|
||
"message": "OK",
|
||
"request": "GET /api/buffers?lines=-1000&nicks=true&colors=weechat",
|
||
"request_body": null,
|
||
"request_id": "initial_sync",
|
||
"body_type": "buffers",
|
||
"body": [
|
||
{
|
||
"id": 1709932823238637,
|
||
"name": "core.weechat",
|
||
"short_name": "weechat",
|
||
"number": 1,
|
||
"type": "formatted"
|
||
}
|
||
]
|
||
}
|
||
----
|
||
|
||
Seconde réponse :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 204,
|
||
"message": "No Content",
|
||
"request": "POST /api/sync",
|
||
"request_body": {
|
||
"colors": "weechat"
|
||
},
|
||
"request_id": null,
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|
||
|
||
WeeChat pousse des données au client à tout moment sur des évènements : lorsque
|
||
des lignes sont affichées, des tampons ajoutés/supprimés/changés, des pseudos
|
||
ajoutés/supprimés/changés, etc.
|
||
|
||
Les messages envoyés au client ont les champs suivants :
|
||
|
||
* `code` : `0`
|
||
* `message` : `Event`
|
||
* `event_name` (chaîne) : le nom de l'évènement (nom du signal ou hsignal)
|
||
* `buffer_id` (entier) : l'identifiant unique du tampon, défini seulement pour
|
||
les sous-objets, -1 dans les autres cas
|
||
|
||
Les évènements suivants sont envoyés au client, selon les options de synchronisation :
|
||
|
||
[width="100%",cols="5,3,3,5",options="header"]
|
||
|===
|
||
| Nom d'évènement | Id tampon | Type de corps | Corps
|
||
| `buffer_opened` | id tampon | `buffer` | tampon avec les lignes et pseudos
|
||
| `buffer_type_changed` | id tampon | `buffer` | tampon
|
||
| `buffer_moved` | id tampon | `buffer` | tampon
|
||
| `buffer_merged` | id tampon | `buffer` | tampon
|
||
| `buffer_unmerged` | id tampon | `buffer` | tampon
|
||
| `buffer_hidden` | id tampon | `buffer` | tampon
|
||
| `buffer_unhidden` | id tampon | `buffer` | tampon
|
||
| `buffer_renamed` | id tampon | `buffer` | tampon
|
||
| `buffer_title_changed` | id tampon | `buffer` | tampon
|
||
| `buffer_time_for_each_line_changed` | id tampon | `buffer` | tampon
|
||
| `buffer_localvar_added` | id tampon | `buffer` | tampon
|
||
| `buffer_localvar_changed` | id tampon | `buffer` | tampon
|
||
| `buffer_localvar_removed` | id tampon | `buffer` | tampon
|
||
| `buffer_cleared` | id tampon | `buffer` | tampon
|
||
| `buffer_closing` | id tampon | `buffer` | tampon
|
||
| `buffer_closed` | id tampon | null | null
|
||
| `buffer_line_added` | id tampon | `line` | ligne de tampon
|
||
| `buffer_line_data_changed` | id tampon | `line` | ligne de tampon
|
||
| `input_text_changed` | id tampon | `buffer` | tampon
|
||
| `input_text_cursor_moved` | id tampon | `buffer` | tampon
|
||
| `nicklist_group_changed` | id tampon | `nick_group` | groupe de pseudos
|
||
| `nicklist_group_added` | id tampon | `nick_group` | groupe de pseudos
|
||
| `nicklist_group_removing` | id tampon | `nick_group` | groupe de pseudos
|
||
| `nicklist_nick_added` | id tampon | `nick` | pseudo
|
||
| `nicklist_nick_removing` | id tampon | `nick` | pseudo
|
||
| `nicklist_nick_changed` | id tampon | `nick` | pseudo
|
||
| `upgrade` ^(1)^ | -1 | null | null
|
||
| `upgrade_ended` ^(1)^ | -1 | null | null
|
||
| `quit` | -1 | null | null
|
||
|===
|
||
|
||
[NOTE]
|
||
^(1)^ Les évènements `upgrade` et `upgrade_ended` sont envoyés seulement si
|
||
le client est connecté sans chiffrement (pas de TLS), car avec TLS le client est
|
||
déconnecté avant que la mise à jour soit faire (la mise à jour des connexions TLS
|
||
n'est pas supportée).
|
||
|
||
Exemple : nouveau tampon : le canal `#weechat` a été rejoint :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "buffer_opened",
|
||
"buffer_id": 1709932823649069,
|
||
"body_type": "buffer",
|
||
"body": {
|
||
"id": 1709932823649069,
|
||
"name": "irc.libera.#test",
|
||
"short_name": "",
|
||
"number": 4,
|
||
"type": "formatted",
|
||
"title": "",
|
||
"modes": "+nt",
|
||
"input_prompt": "\u001b[92m@\u001b[96malice\u001b[48;5;22m(\u001b[39mi\u001b[48;5;22m)",
|
||
"input": "",
|
||
"input_position": 0,
|
||
"input_multiline": false,
|
||
"nicklist": true,
|
||
"nicklist_case_sensitive": false,
|
||
"nicklist_display_groups": false,
|
||
"time_displayed": true,
|
||
"local_variables": {
|
||
"plugin": "irc",
|
||
"name": "libera.#test",
|
||
"type": "channel",
|
||
"nick": "alice",
|
||
"host": "~alice@example.com",
|
||
"server": "libera",
|
||
"channel": "#test"
|
||
},
|
||
"keys": [],
|
||
"lines": []
|
||
}
|
||
}
|
||
----
|
||
|
||
Exemple : nouvelle ligne affichée sur le canal `#weechat` :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "buffer_line_added",
|
||
"buffer_id": 1709932823649069,
|
||
"body_type": "line",
|
||
"body": {
|
||
"id": 5,
|
||
"index": -1,
|
||
"date": "2024-01-07T08:54:00.179483Z",
|
||
"date_printed": "2024-01-07T08:54:00.179483Z",
|
||
"displayed": true,
|
||
"highlight": false,
|
||
"notify_level": 0,
|
||
"prefix": "alice",
|
||
"message": "hello!",
|
||
"tags": [
|
||
"irc_privmsg",
|
||
"self_msg",
|
||
"notify_none",
|
||
"no_highlight",
|
||
"prefix_nick_white",
|
||
"nick_alice",
|
||
"log1"
|
||
]
|
||
}
|
||
}
|
||
----
|
||
|
||
Exemple : le pseudo `bob` est ajouté avec le statut opérateur sur le canal `#weechat` :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "nicklist_nick_added",
|
||
"buffer_id": 1709932823649069,
|
||
"body_type": "nick",
|
||
"body": {
|
||
"id": 1709932823649902,
|
||
"parent_group_id": 1709932823649181,
|
||
"prefix": "@",
|
||
"prefix_color_name": "lightgreen",
|
||
"prefix_color": "\u001b[92m",
|
||
"name": "bob",
|
||
"color_name": "bar_fg",
|
||
"color": "",
|
||
"visible": true
|
||
}
|
||
}
|
||
----
|
||
|
||
Exemple : le tampon du canal `#weechat` a été fermé :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "buffer_closed",
|
||
"buffer_id": 1709932823649069,
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|
||
|
||
Exemple : WeeChat est en cours de mise à jour :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "upgrade",
|
||
"buffer_id": -1,
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|
||
|
||
Exemple : la mise à jour de WeeChat est terminée :
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"code": 0,
|
||
"message": "Event",
|
||
"event_name": "upgrade_ended",
|
||
"buffer_id": -1,
|
||
"body_type": null,
|
||
"body": null
|
||
}
|
||
----
|