From 66a73601f697c99ce2d0554eebe5017e2a0861bd Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Sat, 11 Mar 2023 19:49:56 +0300 Subject: [PATCH] =?utf8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB=20mat?= =?utf8?q?terircd=20=D0=BD=D0=B0=20=D1=81=D0=B0=D0=BC=D0=BE=D0=BF=D0=B8?= =?utf8?q?=D1=81=D0=BD=D1=8B=D0=B9=20mmc?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit http://www.mmc.stargrave.org/ На работе для IM-а используется Mattermost. Это у которого в РФ даже сайте нельзя открыть. И клиентов для него никаких нет толковых для терминала. По сути только один официально есть, но написанный на Haskell. Я честно искал как этот Haskell можно бы было собрать без бинарников из Интернета. Штатно разработчики GHC ничего не предлагают. Штатные клиенты это сплошной GUI или мобильные приложения или нечто запускаемое в броузере. Когда-то MM у меня в jail-е с Firefox-ом отображался. Но после очередного обновления сервера -- перестал полностью. Короче ничего адекватного. Поэтому я использовал единственное что вообще хоть как-то что-то с ним позволяло делать: matterircd, который эмулировал IRC сервер и был неким мостом между IRC клиентом и MM-сервером. Из-за IRC природы, длинные сообщения бьются на части. Нередко отправляется сообщение в котором только одно слово остаётся. Многострочные сообщения не отправить вовсе. В качестве клиента использую irssi, в котором нет ни vi-режима редактирования текста, ни возможности использования внешнего редактора. В общем-то это наверное намекает на то, что это для быстрого общения и коротких сообщений. matterircd непонятно пытается подкачивать сообщения пока мы были в offline: что-то месяцами прилетает каждый раз после входа, а что-то только честно один раз. В общем, решил рискнуть и попробовать написать свой клиент, ведь MM написан на Go и весь его API доступен через библиотечные вызовы. Скептически был настроен: наверняка будет много подвохов, затык в UI обязательно небось случится, да и вообще я сам не знаю какой IM-клиент я бы хотел видеть. Но в итоге, потратив день, удалось в общем и целом довести его до конца. Сейчас у меня по сути нет никаких TODO которые бы я хотел сделать. Была идея сделать мост между XMPP клиентом и MM-сервером. Ибо уже не будет ограничений на размеры сообщений, да и вообще даже можно было бы и файлы передавать через этот протокол. А XMPP клиентов не мало написано. Но когда вновь посмотрел на MCabber, который много лет когда-то использовал, то понял что например кусок кода уже не выделить легко и удобно из-за roster-панельки. Иметь все сообщения сливаемые на одном экране, как это было в GNU Freetalk, (вроде бы) mICQ, Go-шном xmpp-client или как некоторые прадлагают для suckless IRC клиентов сливать все выводы FIFO в один multitail -- мне точно не нравится. Вообще не шибко то много на работе общения происходит, тем более параллельно, но не забуду как ужасно бывает отправить сообщение не в тот чат. Я убеждён что на экране должен быть только один чат и ввод сообщений только в него должен идти. Suckless подход к чатикам, когда для каждого канала создаётся директория с текстовыми файлами информационными и парой in/out FIFO файлов, из которых можно читать приходящие сообщения и отправлять -- мне не нравилась. Конечно же она очень проста и красива для backend-а. Но вот frontend то удобный поверх всех этих FIFO файлов то кто будет писать? Всегда конечно есть вариант написания просто TUI интерфейса используя curses-like библиотеки (для Go есть раздолье). Обнаружил что для Multitail нигде нет документации о его keybinding-ах, которые вообще чуть ли не на каждой клавише что-то делают. Крайне разочаровал он меня этим. Ибо были мысли о попытке его использовать для отображения нужных мне окон. Ещё не понимая как пойдёт написание frontend-а, я реализовал suckless подход. Только кроме FIFO файла для входящих сообщений, я сделал просто append-only файл. Мне в любом случае нужно сохранять сообщения на диске, поэтому зачем заставлять делал некий tee над ним, чтобы просто копию данных лить в файл? Но сообщения из MM могут быть многострочными и имеют метаинформацию (как минимум postId, кроме времени и отправителя). Сохранять его одной строкой, а потом болезненно парсить, не охота. Поэтому делаю .rec-файл, прекрасно для этой задачи подходящий. Приятная бага нашлась в rlwrap утилите. Читать сообщения построчно (и отправлять построчно) -- не вариант, ибо это будет не многострочное сообщение, а много однострочных. Сигналом завершения сообщения можно считать закрытие FIFO файла. В rlwrap есть --one-shot опция, которая, судя по документации, завершает выполнения после чтения первой строчки ввода. Но если ввод был произведён --multi-line-ом, то он успешно съедает весь введённый текст, что логично и мне понятно как программисту. Просто это бага в документации. Бесконечный цикл с rlwrap -o -m cat > chans/foobar/in отлично выполняет задачу. Теперь ввод можно делать и в vi-режиме и вообще вызывать внешний редактор. Для отображения .rec-файлов написал утилитку которая выводит одно сообщение одной строкой (не считая многострочных), без метаинформации. При запуске она парсит весь файл и выводить только n-последних сообщений, эмулируя показ истории. А дальше она не закрывает файл, а ведёт себя как tail -f, отображая появляющиеся новые сообщения. Пока правда просто циклом со sleep-ом и проверкой размера. И выводит с каждым сообщением и bell, чтобы оповестить tmux и терминал. Но а frontend написался шустро сам собой. Управление окнами, keybind-ами, всеми этими оповещениями, быстрым переключением между окон, и ещё кучей полезных фич -- уже имеется в tmux. Сделать так, чтобы он мог открывать окна с двумя половинками (в одной выводятся сообщения, а в другой вводятся) -- плёвое дело без подвохов оказалось. В общем, запускается tmux, внутри mmc, который при запуске у сервера запрашивает и всех пользователей и каналов и у каждого канала ещё запрашивает историю с последнего нам известного postId. Если появляются сообщения, то он дёргает утилиту которая открывает окно в tmux, где и утилита для чтения .rec-а и утилита для записи сообщения сразу же запущены. Отдельный keybinding есть чтобы запустить fzf с выбором пользователя/канала и открыть окно. Прикреплённые файлы показываются в виде "[FILE] FileId (FileName)" сообщения. Скачать можно послав FileId в file/get FIFO и читая его из file/out при этом. Решил засовывать его в pax архив, где заданы поля с FileId и MIME-типом присланным из сервера. Архив приятен тем, что в нём имя файла чётко сохраняется. Чтобы не делать это совсем уж руками, есть cmd/download утилитка, которой надо указать state-директорию и FileId просто навсего. Отправку файлов думал делать через тоже посылку сообщения в FIFO файл соответствующей users/chans директории, но пока остановился на посылке просто сообщения "/FILE /path/to/file" прямо в чатике. Узнать email/имя пользователя можно прочитав users/USER/{email,name}. Узнать его актуальный статус (online/away/offline) прочитав "status" FIFO в нём. Аналогично информация и в каналах записывается. "users" FIFO показывает актуальный (честно делается запрос без кэширования) список пользователей канала. Это всё не интегрировано в tmux или какие-то keybinding-и, ибо крайне редко требуется -- можно и руками. А оповещения о том что пользователь что-то вводит выводятся просто делая display-message в tmux-е, который, как этого и хотелось бы, показывается небольшое время только. Пока более чем доволен результатом. Оно стало значительно удобнее чем было с irssi+matterircd. Плюс ещё и с нормальной machine-readable БД сообщений. И возможностью отправки файлов и более удобного приёма. -- 2.48.1