Bash Days | Linux | DevOps
23.7K subscribers
225 photos
26 videos
776 links
Авторский блог от действующего девопса

Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.

Автор: Роман Шубин
Реклама: @maxgrue

MAX: https://max.ru/bashdays

Курс: @tormozilla_bot
Блог: https://bashdays.ru
Download Telegram
Такс, S3 кластер на Garage в предыдущем посте мы с тобой сообразили, самое время накрутить обвесов и сделать всё по взрослому. Как ты любишь — без хуйни.

Нежных сразу — нахуй, остальным велком.


Для начала установим балансировщик. То есть входная точка у кластер всегда будет одна, а дальше балансировщик будет раскидывать запросы по 3м нашим серверам в кластере. Если одна из нод пойдет по пизде, балансировщик это прозрачно разрулит и отдаст тебе файл в любом случае, даже без правки конфигов.

Проверяем наш кластер:

docker exec -it garage-garage-1 /garage status


Работает. Дальше берем еще один сервер, который будет выступать балансировщиком. Для теста я создам в Selectel нищую виртуалочку и впихарю на нее haproxy.

Устанавливаем haproxy:

apt install haproxy


Домен для кластера я буду использовать s3.linuxfactory.ru, соответственно во всяких rclone нужно будет прописать его в параметре endpoint.

Правим конфиг /etc/haproxy/haproxy.cfg:

global
log /dev/log local0
maxconn 4096

defaults
mode tcp
timeout connect 5s
timeout client 1m
timeout server 1m
option http-server-close
timeout http-request 10s
timeout queue 1m

frontend garage_front
bind *:443 ssl crt /etc/ssl/garage.pem
option http-buffer-request
default_backend garage_back

backend garage_back
balance roundrobin

server node1 95.123.123.116:3900 check
server node2 152.44.71.205:3900 check
server node3 145.83.116.20:3900 check


Проверяем валидность конфиги:

haproxy -c -f /etc/haproxy/haproxy.cfg


Приучи себя всегда это делать, будь то nginx либо что-то другое. Однажды это спасет твою жопу от пенетрации.


Ага, хуй там плавал. Давай создадим SSL сертификат. Я конечно предпочитаю angie, чтобы он сам это сделал, но у нас haproxy, поэтому придется немного пострадать...

Читать продолжение: https://two.su/aaahd

🛠 #devops #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
26
Относительная нумерация

Частенько народ интересуется, а почему у меня в виме такая странная нумерация. Да, этим вопросом и я в своё время задавался. Называется это «Относительная нумерация».

Главная идея здесь — быстрое перемещение по коду с клавиатуры.

Например, курсор стоит у меня в 27 строке. Если я сейчас выполню комбинацию: 6j, то курсор у меня встанет на строку: Не забудь подставить своё мыло. Такая нумерация показывает расстояние до строки от курсора, а не её абсолютный номер.

Аналогично работает и в других направлениях. В редакторе ZED это включается через конфиг:

relative_line_numbers": "enabled"


В виме же по классике в конфиге:

set number  
set relativenumber


Честно, если ты пользуешься мышкой, это бесполезная настройка. Для новичков это совсем неочевидная фича и возможно даже вредная. Короче у этой фичи целая философия:

➡️ Мышление расстояниями. В заурядной жизни, ты будешь думать — ага, мне бы на строку 15 попасть. А если ты адепт вима, твоё мышление такое — мне бы на 6 строчек ниже.

➡️ Ускорение всех count-команд. Тут не только клавиши hjkl задействованы, ты можешь выполнять ряд других операций. Например:

5dd — удалить 5 строк
3yy — скопировать 3 строки
4> — сдвинуть 4 строки вправо
2} — прыгнуть на 2 абзаца


Ты сразу видишь нужное число, сразу прожимаешь нужную комбинацию.

➡️ Сильно уменьшает мысленную математику. Без — так… я на 120 строке, мне нужно на 133… это +13. С ней — вижу 13 → 13j.

Мозги по сути вообще ничего не считают. Это плюс. Думаешь не головой, а жопой.

➡️ Работает лучше с большими файлами. Когда у тебя файл на 1000+ строк, абсолютные номера создают шум, а относительные прям в тему. Ты ориентируешься вокруг курсора, а не во всём файле.

Минусы? Да конечно!

➡️ Поначалу выламывает тебя, перестраивает, больно
➡️ Плохо подходит для дебага с конкретными строками

В общем относительная нумерация, это не про цифры, она про то, чтобы перестать искать строки и моментально перепрыгивать в нужное место.

Если есть чё добавить, камон в комменты, будет интересно почитать.

🛠 #devops #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
44
Hugo за 30 секунд. Мой пайплайн на Gitea Actions

Наконец-то дошли руки до оптимизации пайпа для сборки Hugo в Gitea, да, есть куда еще стремиться, но там уже с жирными файлами нужно поработать.

Пост больше для энтузиастов и для тех кто хочет посмотреть, как всё происходит под капотом. Можешь взять мою болванку и под свои проекты переделать.

Читать: https://two.su/942is

🛠 #devops #dev

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
24
Docker кеш без боли

Давай поговорим про docker и кеширование, тема вроде поверхностная, но как оказалось многие в ней плавают и особо не понимают почему их сборка каждый раз занимает 100500 минут.

А занимает она столько минут, по одной причине — копировать исходники нужно в последнем слое. Механизм тут простой, если какой-то слой изменился, то следующие слои будут пересобраны. Логично, что если ты копируешь исходники так:

FROM node:22
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
CMD ["node", "dist/index.js"]


Тебе пезда! Всё будет пересобираться каждый раз с нуля, никаким кешированием тут и не пахнет. Так делают многие, ведь оно работает и пофигу что долго.

В примере выше, команда COPY копирует весь проект. Представь, что ты изменил одну букву в конфиге и приехали. Кеш инвалидировался, сборка пошла по новой и RUN npm install доставит много удовольствия.

Ключевой момент тут исходники, которые меняются.

Получаем:

- заново устанавливает npm зависимости
- заново качает пакеты
- заново rebuild native modules
- тратит минуты в CI

Хотя зависимости вообще не менялись. Мрак! Фиксим:

FROM node:22

WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["node", "dist/index.js"]


Теперь если что-то изменить в коде проекта, пересобирается только:

COPY . .  
RUN npm run build


А RUN npm ci возьмется из кеша. Аналогично и в питончике и гошке:

COPY . .  
RUN pip install -r requirements.txt

COPY . .
RUN go mod download


Бест-практика простая: От самого стабильного → к самому изменяемому.

- base image
- системные пакеты
- lock-файлы зависимостей
- install dependencies
- исходники
- build
- startup

Короче думай паттерном, а не жопой — «Какие файлы меняются редко?» и выносить их максимально вверх Dockerfile.

Каждый RUN, COPY, ADD это новый слой. Docker пытается переиспользовать уже существующие слои из кеша. Если инструкция и все предыдущие слои не изменились — слой не пересобирается. Docker сбрасывает кеш начиная с измененного слоя.

Вот и вся наука.

🛠 #devops #docker

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
67
Когда начинаешь вкатываться в Ansible, кажется что loop нужен вот прям всегда и везде. Но это не так.

Многие пишут:

- name: install packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
update_cache: yes
loop: "{{ packages }}"


А переменная packages содержит список пакетов на установку.

И вроде тут всё ок, НО apt вызывается для каждого пакета отдельно, а update_cache может дёргаться несколько раз. Это избыточно и ломает заложенные паттерны.

Поэтому правильнее будет сделать так:

- name: install packages
ansible.builtin.apt:
name: "{{ packages }}"
state: present
update_cache: yes


Здесь apt получает сразу список пакетов, кеш обновляется один раз, установка идёт одной транзакцией. Получается меньше оверхеда, всё происходит быстрее и логичнее.

В первом случае это «хак», ты делаешь loop из одного элемента, внутри которого уже лежит список.

Главная мысль тут — если модуль умеет принимать список — loop нахуй не нужен.

Loop нужен там, где логика на каждый элемент отдельная:

- разные параметры
- условия
- шаблоны
- создание пользователей
- файлы
- сложные таски

Короче не заLOOPайся и будет тебе счастье.

Ну а если хочешь стать гуру, я всегда жду тебя здесь.


🛠 #devops #linuxfactory #ansible

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
284
Всем привет. Недавно прилетела неожиданная задача: Организовать скриптом sftp-обмен по со старых версий винды с аутентификацией по ключам.

🔤🔤🔤🔤🔤🔤🔤

С 10+ все понятно. Там и ssh и sftp из коробки есть настройка от linux вообще не отличается. А вот со старыми - проблема.

Почему до сих пор используются старые версии? Да потому что работают. А выставлять их наружу никто не собирается. Так что пока работает будет работать. Ну, я отвлекся.

Итак sftp. Я нашел две программы psftp (из комплекта putty) и winscp.

Попробовал обе и psftp пришлось забраковать. Оказалось, она не хочет работать, если не видит файлов в каталоге. (права xw (3)). А winscp.com (не сайт, программа для работы в скриптах из комплекта winscp) работает.

Вот ее и будем настраивать.

1. Генерим ключ без парольной фразы с помощью puttygen (из комплекта putty). Желательно выбрать EdDSA(Ed25519(255 bits). Он маленький, и с ним удобно работать. Сохраняем публичный ключ в формате *.ppk .

2. Для того, чтобы публичный ключ нормально читался его нужно привести к однострочному виду:

ssh-ed25519 AAAAC3....8cJiPqx0VAOmEI+yy user@host


Я редактировал прямо в vim.


3. После этоого вставляем ключ в authorized_keys на серваке.

4. Запускаем winscp.exe и в настройках → хранилище меняем реестр на ini файл. Это нужно сделать, чтобы скрипт работал и от другого пользователя.

5. В WinSCP.exe создать подключение например «servername» указав ключ. В подключении можно и начальный каталог указать сразу.

6. Обязательно протестировать соединение приняв fingerprint.

7. Создать скрипт обмена script.sftp типа:

   open servername
put filename
get filename1
bye


8. Запуск скрипта обмена:

C:\prog\WinSCP\winscp.com /ini=C:\prog\WinSCP\WinSCP.ini /logsize=2*100K /script=C:\prog\WinSCP\script.sftp /log="C:\prog\WinSCP\script.sftp.log" /console


9. Иногда, Скрипт, при запуске из какой-нибудь программы типа 1c не срабатывает. Тогда строку пункта 8 проще записать в какой-нибудь servername.bat и выполнять его из программы через cmd:

c:\windows\...\cmd.exe /c C:\prog\WinSCP\servername.bat


Вот тебе и винда, такая простая задача, и столько гемора при реализации. Да, кстати, на десятке тоже работает. Проверил.

Всем работы без багов.

🛠 #devops #windows

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
37
Управление Docker контейнерами без Portainer и Dockge

Долгое время я сидел на Dockge, оно хостится у меня на малинке. А на самой малинке я гоняю: jellifyn, qbittorrent, grafana, prometheus, uptime kuma, watchyourlan, technitium dns.

Для таких целей Portainer крайне избыточен, мне в хер не уперся космический корабль, да и многое в Portainer сделано через жопу, хрен чё найдешь.

Поэтому Dockge покрывает все мои хотелки. Но рано или поздно хочется нового.

И новое это — DockHand. Причем этот проект не просто пилит какой-то энтузиаст, проект с закосом на интерпрайз. Но полностью opensource.

Люблю opensource, за то, что если есть закос на интерпрайз, то можно посмотреть исходники и реверснуть лицензию. С DocHand я пока особо глубоко не ковырялся, но чуть позже обязательно пропатчу, как собственно провернул это с mattermost, gitlab и т.п.


Да, по функционалу DockHand ничем не уступает Portainer и даже превосходит его. Для меня важно, чтобы всё было очевидно и понятно, без - хуй чё найдешь. И тут с этим всё прекрасно.

Ладно, чем бы дитя не тешилось. Вкорячиваем...

Читать продолжение: https://two.su/2517q

🛠 #devops #selfhosted

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
29
Сегодня будем устанавливать в свой периметр крутую штуку. И называется она — Termix. Выбрал я её по причине — решил все 100500 серверов свести в одно место, возможно станет поудобнее. Да и поддержка VNC из коробки, а мне как раз надо кластер из mac mini m4 для ИИ админить. Ну и корпоративные винды всякие по RDP. В общем пока звучит очень вкусно.

Termix — это платформа для управления серверами с открытым исходным кодом, полностью бесплатная. Она предоставляет единое кроссплатформенное решение для управления серверами и инфраструктурой через один удобный интерфейс. Termix является альтернативой Termius и доступен на всех популярных платформах.


Termix предлагает:

- доступ к терминалу по SSH;
- управление удалённым рабочим столом (RDP, VNC, Telnet);
- создание SSH-туннелей;
- удалённое управление файлами;
- и множество других инструментов.

Короче эта штука позволяет подключаться по SSH и т.п. к серверам, прям из браузера. Довольно удобно собрать всё в кучу и забыть про ручную рутину. Хотя ручная рутина порой приятнее, чем все эти «новогодние ёлки» и клики мышкой.

Читать продолжение: https://two.su/ckebu

🛠 #devops #selfhosted

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
231
Безопасность Docker контейнеров

Я тут написал огромный пост про безопасность Docker контейнеров.

Разобрал, как нормально прикручивать security_opt, cap_drop, read_only, pids_limit, лимиты CPU/RAM, запуск не от root, seccomp, network_mode и почему privileged:true это не «чтобы заработало», а потенциальная дыра в жопе.

Без абстрактной душнины, зато с примерами, командами для проверки и объяснением, что именно ломается или защищается каждым параметром.

Если работаешь с Docker или хотя бы иногда пишешь композники, рекомендую почитать и ознакомиться. Там дохуя моментов, после которых захочется пойти и проверить свои контейнеры. Ну или по крайней мере будет запал на будущее, чтобы сделать всё в лучших практиках.

Читать тута: https://two.su/vnelv

🛠 #devops #security

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
266
В прошлом посте мы с тобой перелопалити приоритеты чтения sshd_config, сегодня добъем нюансы. Меня вчера опередили в комментариях с Match, но не страшно.

Сюда по классике не влезло, много буков, поэтому всё в блоге.

Читать продолжение: https://two.su/atcbh

🛠 #linux #devops

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
23
Тут ёбнутая тема пошла, чтобы вы могли ходить на нашу апиху, нужно установить сертификаты от Минцифры.

Я долго это игнорил, пока всё к херам не сломалось.

Ошибка: cURL: SSL certificate problem: self-signed certificate in certificate chain


Да, в голову сразу пришло решение — ёбнуть SSL Ignore. Но это костыль и безопасники в жопу выебут.

Хули, пришлось разбираться. Как оказалось Angie и nginx трогать не надо и всё решается на уровне дистрибутива. У меня убунты старенькие торчат в продах, поэтому затачивал под них. Но смысл не меняется и можешь подкрутить под свои дистрибутивы.

Чтобы 100500 команд не вводить, сделал всё одной командой, запускаешь из консольки и всё сразу работает, без костылей и нападок на твой задний проход со стороны ИБешкников. Даже в скрипт сохранять не нужно.

Установка РФ-сертификатов:

(
set -e

cert_tmp="$(mktemp -d)"
trap 'rm -rf "$cert_tmp"' EXIT

curl -fsSL \
https://gu-st.ru/content/Other/doc/russian_trusted_root_ca.cer \
-o "$cert_tmp/root.crt"

curl -fsSL \
https://gu-st.ru/content/Other/doc/russian_trusted_sub_ca.cer \
-o "$cert_tmp/sub.crt"

openssl x509 -in "$cert_tmp/root.crt" -noout -subject |
grep -Fq "Russian Trusted Root CA"

openssl x509 -in "$cert_tmp/sub.crt" -noout -subject |
grep -Fq "Russian Trusted Sub CA"

root_fingerprint="$(
openssl x509 -in "$cert_tmp/root.crt" -noout -fingerprint -sha1 |
cut -d= -f2 |
tr -d ':'
)"

test "$root_fingerprint" = "8FF915CCAB7BC16F8C5C8099D53E0E115B3AEC2F"

openssl verify \
-CAfile "$cert_tmp/root.crt" \
"$cert_tmp/sub.crt"

install -m 0644 "$cert_tmp/root.crt" \
/usr/local/share/ca-certificates/russian_trusted_root_ca.crt

install -m 0644 "$cert_tmp/sub.crt" \
/usr/local/share/ca-certificates/russian_trusted_sub_ca.crt

update-ca-certificates

curl -fsSvo /dev/null https://business.t-bank-app.ru/
)


Ну и всё, если в куче мусора увидел — SSL certificate verify ok, значит всё прошло успешно. Перезапускаешь свои пыхи или чё там у тебя и можно дальше не париться.

Такие дела. Изучай.

🛠 #devops #security #bash

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
388
Всем привет. Вчера решал задачу фильтрации txt-файла и нашёл конструкцию на awk, от которой испытал эстетический оргазм. Решил с вами ею поделиться.

Итак, задача: есть _data.txt_ с данными и _filter.txt_ со списком фильтрации.

Строки data.txt обрабатываются, если есть совпадение в filter.txt. Это классическая задача фильтрации (например, обработка белых и чёрных списков).

И да, я знаю про программы comm и join, с помощью которых можно решать подобные вещи, но мне нравится awk, потому что делает это гибче благодаря наличию языка программирования и не требует предсортировки.

👍 Итак, сама конструкция:


awk 'NR == FNR { filter[$0] = 1; next } $0 in filter { print $0, "in filter" }' filter.txt data.txt


Обратите внимание: здесь два блока обработки строк, каждый со своим условием

1. NR == FNR { filter[$0] = 1; next }
2. $0 in filter { print $0, "in filter" }

Обычно выполняются оба блока: сначала первый, потом второй, но здесь это не так. Первый блок только считывает данные filter.txt в массив filter.

Второй обрабатывает только те строки, которые есть в массиве.

NR — встроенная переменная, номер записи ДЛЯ ВСЕХ файлов.
FNR — аналогична предыдущей, но начинается с 1 для каждого нового файла.
filter[$0] = 1 — сохраняем всю строку первого файла как ключ массива.
next — сразу переходит к обработке следующей строки.

Таким образом, связка условия NR == FNR и next будет работать только для первого файла (filter.txt).

Для второго файла условие NR == FNR уже не соблюдается, поэтому первый блок не выполняется, а второй выполняется, если строка данных присутствует в массиве filter.

Ну и пример из реальной жизни — filter.txt:

RU
BY


data.txt

RU 127.0.0.1
BY 127.0.0.2
US 127.0.0.3
KZ 127.0.0.4


awk 'NR == FNR { filter[$1] = 1; next } $1 in filter { print $2, "in filter" }' filter.txt data.txt

127.0.0.1 in filter
127.0.0.2 in filter


Обратите внимание: на выходе только нужные строки и только нужный столбец. Как же это круто!

Маленькое ограничение: содержимое filter.txt, загруженное в массив filter, должно помещаться в оперативную память. Иначе система может начать активно использовать swap, а процесс — завершиться из-за нехватки памяти.

man awk
man comm
man join


Всем работы без багов.

© Tagd Tagd

🛠 #devops #bash #linux

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
67
Kubernetes на минималках

Поднимаем K3s локально и не ноем про инфраструктуру


Кто хочет — ищет способ, кто не хочет — ищет причину.

В четвёртом сезоне Linux Factory мы собираем нормальный отказоустойчивый кубер: три control-ноды, три worker-ноды, HAProxy, Keepalived, VIP, отдельный DNS и хранилище.

Это правильно для обучения инфраструктуре, но даже в минимальной конфигурации получается шесть виртуалок по 2 CPU и 2 ГБ RAM. Не у каждого ноутбук способен переварить такой зоопарк.

Показываю, как поднять настоящий однонодовый кластер K3s на одной VM с 2 vCPU и 2–4 ГБ памяти, подключить kubectl и запустить первое приложение.

👉 https://two.su/epn8r

🛠 #k8s #devops

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
141
Всем привет, с вами TagdTagd!

Я тут вынужденно перешёл на MAX. Просто Telegram перестал работать хорошо. Пришлось разбираться с новым API, переделывать уведомления с Telegram на MAX. Решил вот с вами поделиться опытом — может, кому-то пригодится.

За это время пришлось решить несколько проблем:

1. У MAX под Linux не работал звук. Была проблема с прослушиванием аудио/видео и звонками.

2. MAX постоянно допиливают, поэтому меняются адреса для работы с API.

3. Регистрация бота — небольшой квест, но вполне решаемый. И всё это бесплатно.


Большинство проблем решается с помощью поиска или ИИ. Скрипты брал оттуда же, но пришлось немного адаптировать для себя. Но за неделю разобрался. Всё заработало. Если кому-то понадобится рабочий shell-скрипт для отправки в сообщении текста, файла или текста + файла — пишите, приведу в следующем сообщении.

Первый вопрос

Пропустим: там много разных проблем может быть, но моя решилась после запуска в консоли:

/usr/share/max/bin/max-service/bin/max-service


Нужно было просто доставить библиотеки и создать пару линков.

Второй

Есть несколько адресов для общения с MAX по API:

- https://platform-api.max.ru
- https://platform-api2.max.ru

На данный момент работают оба, но рекомендуют второй. Но у него тоже своя фишка. Он не работает без сертификатов Минцифры.

Третий — регистрация бота

В отличие от Telegram зарегистрировать бота могут только ИП и юрлица.

Делается это всё при наличии квалифицированной электронной подписи. Юрлицо может зарегистрировать до 5 ботов, причём требуется указать их назначение. Каждый бот утверждается модератором. На это уходит до 24 часов, но у меня прошло быстро, часов за 6.

Ну, в общем, как-то так. С учётом того, что в ближайший месяц проблемы со связью будут нарастать, рекомендую подсуетиться заранее.

🛠 #devops

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
21
Всем привет, с вами TagdTagd. И это вторая часть. Практическая.

С первой частью можешь ознакомиться здесь


Скрипт писался с привлечением ИИ, но мной отлажен, и точно работает. Требует jq и curl.
В отличие от телеграма, в максе заявлено, что бот может писать сообщения пользователю тольков течение суток, после последнего сообщения пользователя боту.

Поэтому для нормальной работы оповещателя желательно создать группу, добавить туда бота, и от его имени писать.

С определением номеров пользователей и групп разбирался по статье Романа. Там очень хорошо описано.

#!/bin/bash
# Скрипт отправляет текстовое сообщение пользователю или в чат
# $1 "user" для отправки пользователю, "chat" для отправки в чат
# $2 ID пользователя или чата, соответственно
# $3 "Текст сообщения в кавычках"
# $4 "Имя файла в кавычках"
# если файл есть, а текста нет - вместо текста ""
# v 20260812

declare -x PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

set -euo pipefail
exec 1>&2
declare BASE_URL="https://platform-api2.max.ru" # база API + нужны сert минцифры
declare BOT_TOKEN="AAAAAAAAAAAAAAAAAAA_BBBBBBBB_CCCCCCCCCCCCCCCC"

# === Параметры скрипта ===
case "$#" in
0|1|2)
clear
head -6 "$0"
exit 1 ;;
3)
declare FILE="" ;;
*)
declare FILE=$4
if [[ -f "$FILE" ]];then
clear
echo -e "Файл '$FILE' не найден"
exit 1
fi ;;
esac

if [[ -z "$3" && -z "$4" ]]; then
clear
echo -e "Ошибка. Нет текста или файла."
exit 1
fi

declare -l RECEPIENT=$1
declare ID=$2
declare TEXT=$3

case $RECEPIENT in
user | chat )
declare TO_URL="$BASE_URL/messages?${RECEPIENT}_id=$ID" ;;
*)
clear
echo -e "ошибка параметра 1" $RECEPIENT\\n
head -6 "$0"
exit 1 ;;
esac

#declare -p RECEPIENT ID TEXT FILE TO_URL

# === 1. Получить URL для загрузки файла ===
if [[ -n "$FILE" ]]; then
UPLOAD_META=$(curl -s -X POST \
"$BASE_URL/uploads?type=file" \
-H "Authorization: $BOT_TOKEN")

UPLOAD_URL=$(echo "$UPLOAD_META" | jq -r '.url')
if [[ -z "$UPLOAD_URL" || "$UPLOAD_URL" == "null" ]]; then
echo "Не удалось получить URL для загрузки"
echo "Ответ API: $UPLOAD_META"
exit 2
fi

# === 2. Загрузить файл ===
UPLOAD_RESULT=$(curl -s -X POST \
"$UPLOAD_URL" \
-F "data=@$FILE")

# Сохраняем результат загрузки в переменную для вложения
ATTACHMENT_PAYLOAD="$UPLOAD_RESULT"
fi

# === 3. Формируем JSON сообщения ===
# Если есть файл, добавляем attachment
if [[ -n "$FILE" ]]; then
MESSAGE_JSON=$(jq -n \
--arg text "$TEXT" \
--argjson attachment "$ATTACHMENT_PAYLOAD" \
'{
text: $text,
attachments: [
{
type: "file",
payload: $attachment
}
]
}')
else
MESSAGE_JSON=$(jq -n --arg text "$TEXT" '{text: $text}')
fi
sleep 3 # для антифлуда
# === 4. Отправляем сообщение ===

curl -s -X POST \
"$TO_URL" \
-H "Authorization: $BOT_TOKEN" \
-H "Content-Type: application/json" \
-d "$MESSAGE_JSON" | jq .


Сохраняем send_max.sh

chmod +x ./send_max.sh  
/send_max.sh "chat" 123456 "text" "/path/to/file"


🛠 #devops

💬 Bashdays 📲 MAX 🌐 LF 🔵 Blog
Please open Telegram to view this post
VIEW IN TELEGRAM
18