1. Увод
Овај документ је спецификација api релеј протокола: протокола који се користи за прослеђивање WeeChat података клијентима употребом HTTP REST API.
1.1. Терминологија
У документу се користе следећи појмови:
-
релеј: то је програм WeeChat за релеј додатком који се понаша као „сервер” омогућава клијентима да се успоставе везу са њим
-
клијент: то је софтвер повезан са релејем преком мрежне везе (сам WeeChat или удаљени интерфејс).
1.2. Мрежни дијаграм
клијенти су повезани са релејем као што је приказано на следећем дијаграму:
┌───────────┐ Радна станица
┌────────┐ ┌───┤ клијент 1 │ (Linux, Windows,
│ irc │◄──┐ ╔═══════════╤═══════╗ │ └───────────┘ BSD, macOS, …)
└────────┘ └──╢ │ ║◄───┘ ┌───────────┐
...... ║ WeeChat │ Релеј ║◄───────┤ клијент 2 │ Мобилни уређај
┌────────┐ ┌──╢ │ ║◄───┐ └───────────┘ (Android, iPhone, …)
│ jabber │◄──┘ ╚═══════════╧═══════╝ │ ......
└────────┘ │ ┌───────────┐
...... └───┤ клијент N │ Остали уређаји
└───────────┘
└────────────┘ └───────────────────┘╘══════╛└────────────────────────────────┘
мрежни сервери ncurses интерфејс релеј удаљени интерфејси
|
Белешка
|
Сви клијенти овде су клијенти који користе api протокол у релеј додатку. релеј додатак такође подржава irc и weechat протоколе (који нису описани у овом документу). |
2. Уопштено о протоколу
-
Везе од клијента ка релеју се успостављају преко TCP сокета на IP/порту који користи релеј додатак за ослушкивање нових веза.
-
Број клијената је ограничен опцијом relay.network.max_clients.
-
Сваки клијент је независан у односу на остале клијенте.
-
api релеј је HTTP REST API који користи JSON формат за улаз/излаз.
-
Поруке се аутоматски компресују (deflate, gzip, zstd и permessage-deflate за websocket протокол).
-
WeeChat може да се користи као клијент за овај релеј.
2.1. API верзије
API верзије се додељују користећи „практично” семантичко Верзирање ↗,
као и WeeChat, на три цифре X.Y.Z, при чему је:
-
Xглавна верзија -
Yмала верзија -
Zверзија закрпе.
Пример: верзија 2.0.0 уводи измене које нису компатибилне са верзијом 1.2.3.
API верзију враћа version ресурс.
2.2. API схема
API можете да прегледате и тестирате на мрежи: WeeChat API релеј ↗.
2.3. Кодови одговора
Клијенту могу да се врате следећи HTTP кодови одговора:
-
200 OK: одговор OK са телом (JSON) -
204 No Content: одговор OK без тела -
400 Bad Request: примљен је неисправан захтев -
401 Unauthorized: подаци за пријаву недостају или нису исправни -
403 Forbidden: нема довољно дозвола -
404 Not Found: није пронађен ресурс -
500 Internal Server Error: интерна грешка сервера -
503 Service Unavailable: сервис није доступан
Када је веза успостављена преко websocket протокола, шаље се још један додатни код одговора када WeeChat гура податке клијенту приликом догађаја:
-
0 Event: догађај је гурнут клијенту, ако је синхронизација укључена sync ресурсом.
2.4. Формат датума
Формат датума је ISO 8601 ↗,
уз коришћење UTC временске зоне (ово се разликује у односу на приказ у програму
WeeChat који користи локалну временску зону).
Датуми се враћају са максималном прецизношћу: до микросекунде, ако је то могуће,
или милисекунде, или само секунде.
Примери:
2023-12-05T19:46:03.847625Z 2023-12-05T19:46:03.847Z 2023-12-05T19:46:03Z
3. Потврда идентитета
Лозинка мора да се пошаље у Authorization заглављу са Basic схемом
потврде идентитета или у заглављу Sec-WebSocket-Protocol (погледајте
детаље испод).
Лозинка може да се пошаље као прости текст или хеширана, користећи један од следећих формата за корисничко име и лозинку:
-
plain:<лозинка> -
hash:sha256:<временска_ознака>:<хеш> -
hash:sha512:<временска_ознака>:<хеш> -
hash:pbkdf2+sha256:<временска_ознака>:<итерација>:<хеш> -
hash:pbkdf2+sha512:<временска_ознака>:<итерација>:<хеш>
Где је:
-
<лозинка>лозинка као прости текст -
<временска_ознака>је текућа временска ознака као цео број (број секунди протекао од Unix Епохе); користи се за спречавање replay напада -
<итерација>број итерација (само за PBKDF2 алгоритам) -
<хеш>је хеширана вредност временска_ознака + лозинка (као хексадецимални број)
|
Белешка
|
Максимални број секунди који се дозвољава пре и након примљеног времена (када се лозинка шаље хеширана) може да се подеси опцијом relay.network.time_window. |
Пример:
-
тренутна временска ознака је
1706431066 -
лозинка је
secret_password -
хеш алгоритам је
sha256 -
добијени хеш је SHA256 стринга
1706431066secret_passwordи он је хексадецимални број:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6 -
Authorizationзаглавље је base64 кодирани стрингhash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6:aGFzaDpzaGEyNTY6MTcwNjQzMTA2NjpkZmExZGIzZjZiYjY0NDVkMThkOWVjNzQyN2MxMGY2NDIxMjc0ZTNhNDc1MWU2YzFmZmM3ZGQyOGM5NGVhZGY2.
Заглавља Authorization и Sec-WebSocket-Protocol се дозвољавају у првом захтеву
код websocket протокола, или у било ком HTTP захтеву у осталим случајевима.
Пример захтева са лозинком у простом тексту:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/version'
Пример захтева са хешираном лозинком (SHA256):
curl -L -u 'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6' 'https://localhost:9000/api/version'
Ако је на WeeChat/релеј страни укључен TOTP (Time-based One-Time Password)
(постављена је опција relay.network.totp_secret), у x-weechat-totp заглављу морате
да пошаљете TOTP вредност на следећи начин:
curl -L -u 'hash:sha256:1706431066:dfa1db3f6bb6445d18d9ec7427c10f6421274e3a4751e6c1ffc7dd28c94eadf6' -H "x-weechat-totp: 123456" 'https://localhost:9000/api/version'
У случају грешке, враћа се одговор 401 Unauthorized са пољем error у
JSON подацима које описује грешку.
Одговор: недостаје лозинка:
HTTP/1.1 401 Unauthorized
{
"error": "Missing password"
}
Одговор: неисправна лозинка:
HTTP/1.1 401 Unauthorized
{
"error": "Invalid password"
}
Одговор: неисправан хеш алгоритам:
HTTP/1.1 401 Unauthorized
{
"error": "Invalid hash algorithm (not found or not supported)"
}
Одговор: неисправна временска ознака:
HTTP/1.1 401 Unauthorized
{
"error": "Invalid timestamp"
}
Одговор: неисправан број итерација:
HTTP/1.1 401 Unauthorized
{
"error": "Invalid number of iterations"
}
Одговор: недостаје TOTP:
HTTP/1.1 401 Unauthorized
{
"error": "Missing TOTP"
}
Одговор: неисправан TOTP:
HTTP/1.1 401 Unauthorized
{
"error": "Invalid TOTP"
}
3.1. Sec-WebSocket-Protocol
JavaScript WebSocket API који се користи у текућим веб прегледачима не подржава
навођење Authorization заглавља. Због тога се такође подржава и слање
лозинке у Sec-WebSocket-Protocol заглављу и то је једино заглавље које
може да се постави овим API.
Да бисте користили ово заглавље, морате да наведете под-протоколе api.weechat и
base64url.bearer.authorization.weechat.<аут> где је <аут> base64url
кодирани стринг лозинке у истом формату који је објашњен изнад.
Пример са лозинком secret_password кодираном у простом тексту. То значи да
base64url треба да кодира стринг plain:secret_password, а то је
cGxhaW46c2VjcmV0X3Bhc3N3b3Jk.
Sec-WebSocket-Protocol: api.weechat, base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk
Ово може да се постави са JavaScript WebSocket API на следећи начин:
const ws = new WebSocket("wss://localhost:9000/api", [
"api.weechat",
"base64url.bearer.authorization.weechat.cGxhaW46c2VjcmV0X3Bhc3N3b3Jk",
])
4. Компресија
Компресија тела одговора је аутоматска и заснива се на заглављу Accept-Encoding
које пошаље клијент.
Подржавају се следећи формати компресије:
-
deflate(zlib) -
gzip -
zstd
Пример захтева:
curl -L -u 'plain:secret_password' -H "Accept-Encoding: gzip" 'https://localhost:9000/api/version'
Одговор:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Content-Length: 77
[77 bytes data]
Напомена: са websocket протоколом, проширење „permessage-deflate” омогућава да се поруке компресују са zlib.
5. Ресурси
5.1. Пробни захтев
Веб прегледачи користе пробне захтеве са HTTP методом OPTIONS да провере да ли ће сервер
(WeeChat) дозволити стварни захтев.
Пример захтева: провера да ли је захтев GET /api/version одобрен:
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
Одговор:
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
5.2. Handshake
Обавља усаглашавање клијента и WeeChat.
Приступ овом ресурсу је дозвољен и без провере идентитета.
Крајња тачка:
POST /api/handshake
Параметри тела:
-
password_hash_algo(низ стрингова, није обавезан): листа хеш алгоритама које подржава клијент, сваки стринг може да буде:-
plain: лозинка у чистом тексту (нема хеширања) -
sha256: хеш SHA256 -
sha512: хеш SHA512 -
pbkdf2+sha256: хеш PBKDF2 са SHA256 -
pbkdf2+sha512: хеш PBKDF2 са SHA512
-
Одговор има следећа поља:
-
password_hash_algo(стринг): хеш алгоритам који треба да се користи (nullако ниједан алгоритам није компатибилан) -
password_hash_iterations(цео број): број итерација који треба да се примени ако се користи хеш PBKDF2 -
totp(логичка):trueако је у WeeChat укључен TOTP (онда клијент мора да пошаље TOTP у одређеном заглављу),falseу супротном
Пример захтева:
curl -L -X POST -d '{"password_hash_algo": ["plain", "sha256", "sha512"]}' 'https://localhost:9000/api/handshake'
Одговор:
HTTP/1.1 200 OK
{
"password_hash_algo": "sha512",
"password_hash_iterations": 100000,
"totp": false
}
5.3. Version
Враћа верзије програма WeeChat и релеј API-ја.
Крајња тачка:
GET /api/version
Пример захтева:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/version'
Одговор:
HTTP/1.1 200 OK
{
"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
}
5.4. Buffers
Враћа бафере, линије и надимке.
Крајње тачке:
GET /api/buffers
GET /api/buffers/{id_бафера}
GET /api/buffers/{име_бафера}
Path parameters:
-
id_бафера(цео број, није обавезно): јединствени идентификатор бафера (не треба да се помеша са бројем бафера, то је нешто друго) -
име_бафера(стринг, није обавезно): име бафера
Параметри упита:
-
lines(цео број, није обавезно, подразумевано:0): број линија које се враћају у баферима са форматираним садржајем:-
негативни број: враћа N линија од краја бафера (најновијих линија)
-
0: не враћа ниједну линију -
позитивни број: враћа N линија од почетка бафера (најстаријих линија)
-
-
lines_free(цео број, није обавезно, подразумевано:0ако јеlines0, у супротном, све линије): број линија који се враћа у баверима са слободним садржајем:-
негативни број: враћа N линија од краја бафера
-
0: не враћа ниједну линију -
позитивни број: враћа N линија од почетка бафера
-
-
nicks(логичка, није обавезно, подразумевано:false): враћа надимке у баферу -
colors(стринг, није обавезно, подразумевано:ansi): како се враћају стрингови са кодовима боје:-
ansi: враћају се ANSI кодови боје -
weechat: враћају се WeeChat интерни кодови боје -
strip: уклањају се боје
-
Пример захтева: врати све бафере без линија:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers'
Одговор:
HTTP/1.1 200 OK
[
{
"id": 1709932823238637,
"name": "core.weechat",
"short_name": "weechat",
"number": 1,
"type": "formatted",
"hidden": false,
"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": [],
"last_read_line_id": -1
},
{
"id": 1709932823423765,
"name": "irc.server.libera",
"short_name": "libera",
"number": 2,
"type": "formatted",
"hidden": false,
"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": [],
"last_read_line_id": -1
},
{
"id": 1709932823649069,
"name": "irc.libera.#weechat",
"short_name": "#weechat",
"number": 3,
"type": "formatted",
"hidden": false,
"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": [],
"last_read_line_id": -1
}
]
Пример захтева: врати WeeChat основни бафер само са последњом линијом и без кодова боје:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/core.weechat?lines=-1&colors=strip'
Одговор:
HTTP/1.1 200 OK
{
"id": 1709932823238637,
"name": "core.weechat",
"short_name": "weechat",
"number": 1,
"type": "formatted",
"hidden": false,
"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": "Учитани додаци: alias, buflist, charset, exec, fifo, fset, guile, irc, javascript, logger, lua, perl, php, python, relay, ruby, script, spell, tcl, trigger, typing, xfer",
"tags": []
}
],
"last_read_line_id": -1
}
Пример захтева: врати бафере IRC канала са надимцима:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat?nicks=true'
Одговор:
HTTP/1.1 200 OK
{
"id": 1709932823649069,
"name": "irc.libera.#weechat",
"short_name": "#weechat",
"number": 3,
"type": "formatted",
"hidden": false,
"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": [],
"last_read_line_id": -1,
"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": []
}
}
Пример захтева: врати fset бафер:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/fset.fset'
Одговор:
HTTP/1.1 200 OK
{
"id": 1709932823897200,
"name": "fset.fset",
"short_name": "",
"number": 4,
"type": "free",
"hidden": false,
"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"
}
],
"last_read_line_id": -1
}
Lines
Враћа линије бафера.
Крајње тачке:
GET /api/buffers/{id_бафера}/lines
GET /api/buffers/{id_бафера}/lines/{id_линије}
GET /api/buffers/{име_бафера}/lines
GET /api/buffers/{име_бафера}/lines/{id_линије}
Параметри путање:
-
id_бафера(цео број, обавезно): јединствени идентификатор бафера (не треба да се помеша са именом бафера, то је нешто друго) -
име_бафера(стринг, обавезно): име бафера -
id_линије(цео број, није обавезно): враћа једну линију са овим идентификатором
Параметри упита:
-
lines(цео број, није обавезно, подразумевано: све линије): број линија који се враћа:-
негативни број: враћа N линија од краја бафера (најновијих линија)
-
0: не враћа се ниједна линија (дозвољено је, али нема смисла са овим ресурсом) -
позитивни број: враћа N линија од почетка бафера (најстаријих линија)
-
-
colors(стринг, није обавезно, подразумевано:ansi): како да се врати стринг са кодовима боје:-
ansi: враћају се ANSI кодови боје -
weechat: враћају се WeeChat интерни кодови боје -
strip: уклањају се боје
-
Пример захтева: врати последњих 1000 линија бафера, без кодова боје:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat/lines?lines=-1000&colors=strip'
Одговор:
HTTP/1.1 200 OK
[
{
"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"
]
}
]
Nicks
Враћа надимке у баферу.
Крајње тачке:
GET /api/buffers/{id_бафера}/nicks
GET /api/buffers/{име_бафера}/nicks
Параметри упита:
-
id_бафера(цео број, обавезно): јединствени идентификатор бафера (не треба да се помеша са бројем бафера, то је нешто друго) -
име_бафера(стринг, обавезно): име бафера
Пример захтева: врати надимке бафера:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/buffers/irc.libera.%23weechat/nicks'
Одговор:
HTTP/1.1 200 OK
{
"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": []
}
5.5. Hotlist
Враћа врућу листу.
Крајња тачка:
GET /api/hotlist
Пример захтева:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/hotlist'
Одговор:
HTTP/1.1 200 OK
[
{
"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
]
}
]
5.6. Scripts
Return loaded scripts (all languages).
Крајња тачка:
GET /api/scripts
Пример захтева:
curl -L -u 'plain:secret_password' 'https://localhost:9000/api/scripts'
Одговор:
HTTP/1.1 200 OK
[
{
"name": "highmon.pl",
"version": "2.7",
"description": "Highlight Monitor",
"author": "KenjiE20",
"license": "GPL3"
},
{
"name": "go.py",
"version": "3.1.1",
"description": "Quick jump to buffers",
"author": "Sébastien Helleu <flashcode@flashtux.org>",
"license": "GPL3"
}
]
5.7. Input
Шаље команду или текст у бафер.
Крајња тачка:
POST /api/input
Параметри тела:
-
buffer_id(цео број, није обавезно): јединствени идентификатор бафера (не треба да се помеша са бројем бафера, то је нешто друго) -
buffer_name(стринг, није обавезно, подразумевано:core.weechat): име бафера -
command(стринг, обавезно): команда или текст који се шаље баферу
Пример захтева: кажи „здраво!” на канал #weechat:
curl -L -u 'plain:secret_password' -X POST \
-d '{"buffer_name": "irc.libera.#weechat", "command": "здраво!"}' \
'https://localhost:9000/api/input'
Одговор:
HTTP/1.1 204 No content
Пример захтева: напусти и затвори канал #weechat (команда се извршава у WeeChat основном баферу):
curl -L -u 'plain:secret_password' -X POST \
-d '{"command": "/buffer close irc.libera.#weechat"}' \
'https://localhost:9000/api/input'
Одговор:
HTTP/1.1 204 No content
5.8. Completion
Довршава команду или текст у баферу.
Крајња тачка:
POST /api/completion
Параметри тела:
-
buffer_id(цео број, није обавезно): јединствени идентификатор (не треба да се помеша са бројем бафера, то је нешто друго) -
buffer_name(стринг, није обавезно, подразумевано:core.weechat): име бафера -
command(стринг, обавезно): команда или текст који се довршава -
position(цео број, није обавезно, подразумевано: крај стринга): позиција у команди (прва позиција је 0)
Пример захтева: доврши команду /qu на каналу #weechat:
curl -L -u 'plain:secret_password' -X POST \
-d '{"buffer_name": "irc.libera.#weechat", "command": "/qu"}' \
'https://localhost:9000/api/completion'
Одговор:
HTTP/1.1 200 OK
{
"context": "command",
"base_word": "qu",
"position_replace": 1,
"add_space": true,
"list": [
"query",
"quiet",
"quit",
"quote"
]
}
5.9. Ping
Шаље „ping” захтев.
Крајња тачка:
POST /api/ping
Параметри тела:
-
data(стринг, није обавезно): стринг који се шаље назад у одговору
Пример захтева: без тела:
curl -L -u 'plain:secret_password' -X POST 'https://localhost:9000/api/ping'
Одговор:
HTTP/1.1 204 No content
Пример захтева: са подацима:
curl -L -u 'plain:secret_password' -X POST \
-d '{"data": "1702835741"}' \
'https://localhost:9000/api/ping'
Одговор:
HTTP/1.1 200 OK
{
"data": "1702835741"
}
5.10. Sync
Почиње или зауставља синхронизацију података са WeeChat.
Овај ресурс може да се користи само онда када је клијент повезан websocket протоколом, јер ће WeeChat гурати поруке клијенту у било које време.
Ако се овај ресурс употреби без websocket везе, враћа се грешка 403 (Forbidden).
Крајња тачка:
POST /api/sync
Параметри тела:
-
sync(логичка, није обавезно, подразумевано:true):trueда се укључи синхронизација са WeeChat -
nicks(логичка, није обавезно, подразумевано:true):trueда се примају ажурирања надимака у баферима (користи се само ако јеsynctrue) -
input(логичка, није обавезно, подразумевано:true):trueда се синхронизује улаз бафера са удаљеног релеја на локални клијент (користи се само ако јеsynctrue) -
colors(стринг, није обавезно, подразумевано:ansi): како се враћају стрингови са кодовома боје (користи се само ако јеsynctrue):-
ansi: враћају се ANSI кодови боје -
weechat: враћају се WeeChat интерни кодови боје -
strip: уклањају се боје
-
Пример захтева са websocket протоколом:
{
"request": "POST /api/sync",
"body": {
"nicks": false
}
}
Одговор:
{
"code": 204,
"message": "No Content",
"request": "POST /api/sync",
"request_body": {
"nicks":false
},
"body_type": null,
"body": null
}
Пример захтева без websocket протокола (без пријаве):
curl -L -u 'plain:secret_password' -X POST \
-d '{"nicks": false}' \
'https://localhost:9000/api/sync'
Одговор:
HTTP/1.1 403 Forbidden
{
"error": "Sync resource is available only with a websocket connection"
}
6. Websocket
Websocket протокол се користи за креирање сталне везе измељу клијента и WeeChat програма, и за пријем догађаја у стварном времену, у случају када је sync ресурсом укључена синхронизација.
Када се користи websocket протокол, потврда идентитета мора да се обави само једном. (погледајте потврду идентитета).
6.1. Websocket усаглашавање
Да би се успоставила веза, врши се усаглашавање на /api крајњој тачки, које
изгледа овако:
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 враћа свој одговор усаглашавања којим потврђује да је websocket протокол исправно подржан (и да је потврда идентитета била успешна):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: PaY9vRflWeOKuD0/F7e5gD9At9U=
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
|
Белешка
|
Враћена Sec-WebSocket-Accept вредност је SHA-1 хеш примљене вредности на коју је надовезан
GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 (SHA-1 се кодира у base64).У горњем примеру, SHA-1 од 2XE8VAJktqi3Tpw5QnfxVQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11 је
PaY9vRflWeOKuD0/F7e5gD9At9U= (у base64).
|
6.2. Оквири
Када је клијент повезан websocket протоколом:
-
захтеви и одговори су у JSON, унутар websocket оквира
-
ако је ресурсом sync укључена синхронизација, WeeChat може клијенту да пошаље JSON оквире у било које време.
Захтеви WeeChat програму се праве JSON објектом који садржи следећа поља:
-
request(стринг): HTTP метода и путања (пример:GET /api/buffers?lines=-100) -
body(објекат или низ): тело (није обавезно, заPOSTиPUTметоде) -
request_id(стринг): идентификатор који се шаље назад у одговору
Употребом низа објеката, више захтева може да се пошаље одједном, тако што је
сваки објекат посебан захтев.
Захтеви се извршавају у редоследу у којем су примљени (погледајте пример испод).
Одговори клијенту се шаљу као JSON објекат који садржи следећа поља:
-
code(цео број): код HTTP одговора (пример:200) -
message(стринг): порука за код (пример:OK) -
request(стринг): захтев (пример:GET /api/buffers?lines=-100) -
request_body(објекат): тело захтева, илиnullако захтев није имао тело -
request_id(стринг): id захтева, илиnullако захтев није имао id -
body_type(стринг): тип објеката који се враћа у телу (погледајте испод), илиnullако одговор нема тело -
body(објекат или низ): враћено тело, илиnullако одговор нема тело
Типови тела који могу да се врате:
-
handshake(објекат) -
version(објекат) -
buffers(низ) -
buffer(објекат) -
lines(низ) -
line(објекат) -
nick_group(објекат) -
nick(објекат) -
hotlist(објекат) -
scripts(низ) -
ping(објекат)
|
Савет
|
Ове схеме можете да прегледате на мрежи: WeeChat Relay API ↗. |
Пример захтева: врати верзију:
{
"request": "GET /api/version",
"request_id": "get_version"
}
Одговор:
{
"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
}
}
Пример захтева: кажи „здраво!” на каналу #weechat:
{
"request": "POST /api/input",
"body": {
"buffer_name": "irc.libera.#weechat",
"command": "здраво!"
}
}
Response:
{
"code": 204,
"message": "No Content",
"request": "POST /api/input",
"request_body": {
"buffer_name": "irc.libera.#weechat",
"command": "здраво!"
},
"request_id": null,
"body_type": null,
"body": null
}
Пример захтева: пошаљи сва захтева одједном: врати листу свих бафера са линијама и надимцима, а затим се синхронизуј са удаљеним:
[
{
"request": "GET /api/buffers?lines=-1000&nicks=true&colors=weechat",
"request_id": "initial_sync"
},
{
"request": "POST /api/sync",
"body": {
"colors": "weechat"
}
}
]
|
Белешка
|
Препоручује се да се захтев за синхронизацијом пошаље заједно са првим захтевом који преузима податке, тако да се не пропусти ниједан догађај. |
Први одговор (тело са баферима је одсечено ради лакшег читања):
{
"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"
}
]
}
Други одговор:
{
"code": 204,
"message": "No Content",
"request": "POST /api/sync",
"request_body": {
"colors": "weechat"
},
"request_id": null,
"body_type": null,
"body": null
}
WeeChat гура податке клијенту у било које време када се десе одређени догађаји: када се линије приказују,бафери додају/уклањају/мењају, надимци nicks додају/уклањају/мењају, итд.
Поруке које се шаљу клијенту имају следећа поља:
-
code:0 -
message:Event -
event_name(стринг): име догађаја (име сигнала or hsignal) -
buffer_id(цео број): јединствени идентификатор бафера, поставља се само за под-објекте, -1 у осталим случајевима
Клијенту се шаљу следећи догађаји, у сагласности са опцијама синхронизације:
| Име догађаја | Id бафера | Тип тела | Тело |
|---|---|---|---|
|
buffer id |
|
бафер са свим линијама и надимцима |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
null |
null |
|
buffer id |
|
бафер линија |
|
buffer id |
|
бафер линија |
|
buffer id |
|
бафер |
|
buffer id |
|
бафер |
|
buffer id |
|
група надимака |
|
buffer id |
|
група надимака |
|
buffer id |
|
група надимака |
|
buffer id |
|
надимак |
|
buffer id |
|
надимак |
|
buffer id |
|
надимак |
|
-1 |
null |
null |
|
-1 |
null |
null |
|
-1 |
null |
null |
|
Белешка
|
(1) Догађаји upgrade и upgrade_ended се шаљу само ако је клијент повезан чистим
текстом (без TLS), јер се у случају TLS веза са клијентом најпре прекида пре
него што се обави ажурирање (ажурирање TLS веза није подржано).
|
Пример: нови бафер: приступљено је каналу #weechat:
{
"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",
"hidden": false,
"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": []
}
}
Пример: приказана је нова линија на каналу #weechat:
{
"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"
]
}
}
Пример: додат је надимак bob са статусом оператора у канал #weechat:
{
"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
}
}
Пример: затворен је бафер канала #weechat:
{
"code": 0,
"message": "Event",
"event_name": "buffer_closed",
"buffer_id": 1709932823649069,
"body_type": null,
"body": null
}
Пример: WeeChat се ажурира:
{
"code": 0,
"message": "Event",
"event_name": "upgrade",
"buffer_id": -1,
"body_type": null,
"body": null
}
Пример: извршено је ажурирање програма WeeChat:
{
"code": 0,
"message": "Event",
"event_name": "upgrade_ended",
"buffer_id": -1,
"body_type": null,
"body": null
}