Возможно, это не совсем профильная статья для Инфостарта, но для меня это более родной ресурс, чем, допустим, Хабр, а потребность решить задачу возникла именно в связи с разработкой вокруг 1С, так что размещу здесь.
Итак, уведомления. Многие из вас наверняка пользовались уведомлениями при помощи телеграм-ботов. Например, о проблемах на каких-то серверах. Или, допустим, при парсинге журнала регистрации можно рассылать уведомления о том, что у пользователей возникли ошибки. У нас все это было. И накрылось медным тазом с блокировкой телеграма. Да, телеграмом все еще можно пользоваться через прокси, но с ботами так не выходит. И когда стало ясно, что api телеграма блокируют, а через mtproxy это не поднимешь, стали искать решение.
Да, можно поднимать бота на своем сервере. Но все это сложно, сразу получить ключ не вышло (возможно, опять виноваты блокировки), ну, и все это может оказаться пустой тратой средств если блокировки усилятся. Ну или вечной войной меча и щита, которую я готов проводить в личных целях, но вот постоянно поддерживать работоспособность на работе не очень хочется, чтобы из-за очередной блокировки не потерять важное уведомление.
Можно пойти в Макс. Но я, например, не хочу. Бесит меня он. Ну и там какие-то сложности ограничением числа ботов, необходимостью их регистрировать официально, так что хотелось альтернативы, и чтобы сильно не страдать.
И тут мы вспомнили про недавно развернутый в тестовых целях jabber-сервер. Олды оценят, мы тут недавно даже Миранду ставили. Правда, выяснилось, что в современное шифрование она не может. В общем, чтобы не копипастить инструкции по установке, сходите по ссылке на хабр и почитайте статью. Если кратко, то берете свою VPS в России, разворачиваете сниккет-сервер за десять минут (это не шутка), регистрируете всех. Все, вот оно общение через jabber, и звонки тоже есть. Ставили его как потенциальную замену телеге для рабочего общения, но пока сидим через прокси. Там, конечно, есть свои недостатки по сравнению с телеграмом, например, нет удобного персонального ответа в группе через лягушку, чтобы человеку пришло уведомление, даже если он группу заглушил. Но жить можно. Но пока сидим в телеге все-таки.
По установке - чтобы было яснее, вам потребуется домен, на VPS вы открываете нужные порты, ставите Snikket-сервер. Готовое решение для развертывания, ставится через Docker. Заводите приглашение для первого админа, дальше им можете войти в браузере и пользоваться. Создаете приглашения, по которым пользователи могут зарегистрироваться. Вроде бы можно регистрироваться в браузере, но стабильнее работает в приложении Snikket, которое есть во всяких Google Play и прочих магазинах. Если со ссылки переходите на приложение, оно пускает зарегистрироваться с нужными правами.
На сайте создаете "круги", можно для "круга" сразу указать группу для общения, а в приглашении сразу указать круг, и тогда все зарегистрировавшиеся по этой ссылке, попадут в группу и смогут сразу общаться, на зная идентификаторы jabber. У группы появляется свой jabber-идентификатор (можно посмотреть в свойствах), который нам пригодится для отправки уведомлений.
По поводу Миранды - современный клиент джаббера предлагает Omemo в качестве шифрования. Миранда так не может. Можно и PgP ставить, но это усложняет развертывание для простых пользователей. Впрочем, не обязательно и пользоваться шифрованием - оно даже может что-то усложнять, поскольку если вы потом подключитесь с другого устройства, старые зашифрованные сообщения вы не прочитаете.
Блокировать это не должны. Это локальный трафик, по России. Протокол с телегой и другими распространенными мессенджерами не связан. В общем, я не вижу никакой угрозы.
Но дальше нужно было решить задачу автоматизированной отправки сообщений, что и стало основной темой этой статьи. Собственно, решением и хочу поделиться. Прямо вот готового адреса дернуть через API не было. Зато имеется библиотека slixmpp для Питона, позволяющая писать ботов. Если хотите самостоятельно ставить, вам потребуется питон и сама библиотека, тут варианты или apt install python3-slixmpp для Ubuntu или pip install slixmpp. Версии могут не биться, так что если что-то не работает, пробуйте другие варианты.
На сайте библиотеки были готовые примеры, но не совсем совпадающие с задачей. Индивидуальная отправка или бот, который постоянно сидит в группе и на что-то отвечает. Повозившись, удалось дойти до рабочего скрипта. Хотел его приложить, но тут выставилось 1 стартмани, а бесплатно не вышло, поэтому пусть будет в тексте.
#!/usr/bin/env python3
# Slixmpp: The Slick XMPP Library
# Copyright (C) 2010 Nathanael C. Fritz
# This file is part of Slixmpp.
# See the file LICENSE for copying permission.
import logging
from getpass import getpass
from argparse import ArgumentParser
import asyncio
import slixmpp
class MUCBot(slixmpp.ClientXMPP):
"""
A simple Slixmpp bot that will greets those
who enter the room, and acknowledge any messages
that mentions the bot's nickname.
"""
def __init__(self, jid, password, room, nick, msg):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.room = room
self.nick = nick
self.msg = msg
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
async def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
await self.get_roster()
self.send_presence()
await self.plugin['xep_0045'].join_muc(self.room,
self.nick,
# If a room password is needed, use:
# password=the_room_password,
)
self.send_message(mto=self.room,
mbody=self.msg,
mtype='groupchat')
await self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
parser.add_argument("-r", "--room", dest="room",
help="MUC room to join")
parser.add_argument("-n", "--nick", dest="nick",
help="MUC nickname")
parser.add_argument("-m", "--msg", dest="msg",
help="message")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.room is None:
args.room = input("MUC room: ")
if args.nick is None:
args.nick = input("MUC nickname: ")
# Setup the MUCBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = MUCBot(args.jid, args.password, args.room, args.nick, args.msg)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat
xmpp.register_plugin('xep_0199') # XMPP Ping
xmpp.register_plugin('xep_0203')
xmpp['feature_mechanisms'].disable_tls_cid = True
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
asyncio.get_event_loop().run_until_complete(xmpp.disconnected)
Запускать через python3 xmpp-send-group.py -j user -p password -n nick -r group -m message. Здесь user - jabber-идентификатор пользователя (выглядит как адрес почты), group - jabber-идентификатор группы.
А дальше возник вопрос, как это поднять на сервере 1с. У нас там Астра-Линукс, и поехали всякие несовместимости по версиям Питона. Поэтому было решено упаковать штуку в докер. Соответственно, на сервере нужен только докер (можно старый). Примитивный набор сборки это Dockerfile:
FROM python:3.12-slim
WORKDIR /app
# Установка зависимостей
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Копируем код
COPY xmpp-send-group.py .
# Запуск
ENTRYPOINT ["python3", "xmpp-send-group.py"]
и файл с требованиями requirements.txt, содержащий slixmpp
Собственно, все это можно сбилдить через docker build и запускать через docker run.
Ну и разместил все на докерхабе, так что можете использовать через
docker run --rm asmirnov80/xmpp-sender -j user -p password -n nick -r group -m message
Остается вопрос, как это вызвать из 1с. Рецепт простой - помещаете пользователя сервера 1с в группу докер, перезапускаете сервер (иначе не считаются права), а дальше можно на сервере вызывать через ЗапуститьПриложение примерно вот так (не забывайте экранировать кавычки в сообщениях)
СтрокаКоманды = СтрШаблон("docker run --rm asmirnov80/xmpp-sender -j %1 -p %2 -n ""%3"" -r %4 -m ""%5""",
"user",
Константы.ПарольБотаОповещений.Получить(),
"1c bot",
"group_address",
Сообщение);
// BSLLS:ExternalAppStarting-off
// Запуск в докере отправки уведомлений, безопасно
ЗапуститьПриложение(СтрокаКоманды);
// BSLLS:ExternalAppStarting-on
Уведомления приходят моментально. На мобильном приложение Snikket, на компьютере Gajim. В текущей реализации они приходят в незашифрованном виде, но есть такая библиотека slixmpp-omemo, но пока не дошли руки разобраться с ней.
В общем, рад был поделиться, чтобы не для одного себя делать решение. В конце концов, проблема уведомлений общая, и хорошо бы, чтобы все знали про альтернативу. Если есть какие-то вопросы и идеи по улучшению, рад буду обсудить. Может быть, тогда допилю докер-решение до более универсального. Да, и дисклеймер, я старовер, при написании статьи ни одна нейросеть не пострадала. При написании скрипта на Питоне страдала, но упорно выдавала нерабочий код. Пришлось почитать маны и поправить самостоятельно.
Вступайте в нашу телеграмм-группу Инфостарт