Документация BILLmanager 6

Создание модулей уведомлений

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

По умолчанию BILLmanager поддерживает три типа уведомлений:

  • SMS сообщения (ntsms) — короткие сообщения, отправляемые на номера телефонов указанные в профилях пользователей:
    • реализована только отправка сообщений;
    • для отправки используются шлюзы;
  • Email сообщения (ntemail) — сообщения на email адреса пользователей (часть уведомлений может быть отправлена только этим способом, так как реализуют специфичный для email уведомлений функционал):
    • реализована отправка и получение уведомлений;
    • для отправки и получения уведомлений используются шлюзы;
  • Сообщения в меню уведомлений (ntinternal) — сообщения, отображаемые в личном кабинете в разделе "Уведомления", могут быть сформированы на основе отправляемого email сообщения:
    • реализована только отправка сообщений;
    • шлюзы не поддерживаются.

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

Механизм работы уведомлений

Работа уведомлений в BILLmanager состоит из следующих этапов:

  1. Проверка подписки клиента на тип уведомления.
  2. Генерация XML-уведомления нужного типа.
  3. Передача XML, содержащего в себе:
    • шаблон уведомления;
    • XML самого уведомления;
    • данные о провайдере, связанном с уведомлением и пользователе, которому отправляется уведомление, модулю соответствующему типу уведомления.

Внутри модуля выполняются следующие действия:

  1. Определяется тип шаблона уведомления на основе его структуры, после чего:
    • для XSLT шаблонов происходит наложение шаблона на XML-уведомления;
    • для EJS шаблонов XML переводится в JSON и передается EJS шаблонизатору вместе с шаблоном.
  2. При необходимости в результирующем тексте производится замена макросов на необходимые значения.
  3. При отсутствии поддержки шлюзов уведомлений производится отправка уведомления непосредственно средствами модуля.
  4. При наличии поддержки шлюзов, на основе переданного идентификатора провайдера, а также типа модуля уведомлений выбирается шлюз для отправки уведомления.
  5. Параметры шлюзы, необходимые контактные данные, текст сообщения и его заголовок передаются модулю шлюза для отправки.

Чтобы в BILLmanager для типа уведомления отображался шаблон:

  1. Поместите в директорию etc/notice/<тип_уведомления>/ XML-файл, содержащий имя шаблона, которое будет отображаться в интерфейса BILLmanager.

    Пример XML-файла
    <doc> 
    	<template notice="имя_файла_ejs">
    		<locale>notice_name</locale> 
    		<locale_ru>Имя шаблона</locale_ru>
    	</template>
    </doc>
    Пояснения

    Имя XML-файла должно быть составлено по шаблону: billmgr_mod_<имя_модуля>_notice

  2. Поместите в директорию etc/notice/<тип_уведомления>/ EJS файл, содержащий шаблон уведомления.
    Имя EJS файла должно быть составлено по шаблону: <имя_модуля>_<код_страны>. Например, example_ru.ejs.
    Не указывайте код страны, чтобы платформа считала шаблон английским.

    Пример EJS файла

Архитектура модулей

Для работы с BILLmanager каждый модуль уведомления или шлюза должен уметь обрабатывать определённый набор команд, описанный ниже. Если команда не поддерживается, или данные выводятся в не поддерживаемом формате, BILLmanager не сможет взаимодействовать с модулем.

Архитектура модуля уведомлений

Модуль устанавливается в каталог /usr/local/mgr5/notify/ и должен уметь обрабатывать следующие команды:

  • --command process — обработка очереди уведомлений на отправку. Очередь уведомлений хранится в таблице notifytask со следующей структурой:
    • id — идентификатор уведомления. Генерируется автоматически;
    • modulename — имя модуля типа уведомлений. Стандартные значения ntemailntsmsntinternal
    • filename — имя файла с данными уведомления;
    • priority — приоритет отправки уведомления. При обработке очереди уведомлений рекомендуется выбирать из базы данных уведомления небольшими порциями отсортированными по убыванию приоритета. В этом случае, при проведении объёмных рассылок важные уведомления будут доставлены в срок;
    • error_count — количество попыток отправки уведомления, завершившихся ошибкой;
    • forcedonothing — флаг отправки уведомления игнорируя файл billmgr.DoNothing, создаваемый во время переноса данных из другого биллинга;
    • err_info — текст сообщения об ошибке отправки уведомления;
    • createdate — дата постановки уведомления в очередь;
  • --command getmessage --gate gate_id, где gate_id — код шлюза, для которого производится обработка получения сообщения. В качестве параметра --gate, может быть передано значение all . В этом случае необходимо обработать получения сообщений всеми шлюзами типа уведомления;
  • --command features — запрос параметров модуля уведомлений. В ответ модуль должен вывести в стандартный поток вывода XML-описание поддерживаемого функционала. Формат XML-документа следующий:
<doc>
  <features>
    <feature name="html"/> 
    <feature name="sms"/> 
    <feature name="call"/> 
  </features>https://docs.ispsystem.net/pages/editpage.action?pageId=211032476#
  <contact_type>тип контакта</contact_type> 
</doc>

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

  • непрерывная работа модуля с периодической проверкой появления новых сообщений в очереди;
  • добавление входящих сообщений в запросы клиентов;
  • управление услугами через входящие сообщения;
  • запрос и вывод информация управляющими командами.

Реализация модуля с использованием заголовочных файлов BILLmanager предполагает обязательную реализацию методов:

  • virtual mgr_xml::Xml Features() const = 0; — возврат XML-описания поддерживаемых возможностей. Вывод данных в поток вывода будет произведён автоматически;
  • virtual bool UserNotify(const string& filename) const = 0; — отправка пользователю уведомления, описанного в файле с именем, переданном в параметре;
  • virtual void GetMessage(string gate_id = 0) const = 0; — обработка получения входящих сообщения для шлюза с кодом gate_id

Дополнительно может быть переопределен метод:

  • virtual int ProcessQueue() const; — обработка очереди уведомлений, которые необходимо отправить пользователям. UserNotify вызывает при работе этого метода класса и может быть определён пустым в случае реализации всей необходимой логики в ProcessQueue

Архитектура модуля шлюза

Модуль устанавливается в каталог /usr/local/mgr5/gate/ и должен уметь обрабатывать следующие команды:

  • --command features — запрос параметров модуля шлюза. В ответ модуль должен вывести в стандартный поток вывода XML-описание поддерживаемого функционала. Формат XML-документа следующий:
<doc>
  <features>
    <feature name="outgoing"/> 
    <feature name="ingoing"/> 
    <feature name="formtune"/> 
    <feature name="check_connection"/> 
  </features>
  <notify_module>тип модуля уведомлений</notify_module> 
</doc>
  • --command formtune — модификация формы настроек параметров шлюза. На вход модулю передается XML-описание формы параметров шлюза, на выход модуль должен вернуть модифицированную XML-описания формы настроек;
  • --command check_connection — проверка подключения к шлюзу с указанными параметрами. На вход модулю передается XML-описание формы параметров шлюза, с добавлением введенных на форме данных. На выход модуль должен вернуть XML-описание формы настроек (XML может быть изменена при необходимости);
  • --command outgoing и --command ingoing — не вызываются BILLmanager напрямую в общем случае, исключением является отправка СМС сообщений. В этом случае реализация --command outgoing обязательна в определённом формате. В остальном случае могут быть описаны любые другие команды, которые будут вызываться модулями уведомлений. Ниже описана работа стандартным модулей с этими командами:
    • --command outgoing — отправка уведомления. На вход модулю передается XML-описание сообщения следующего вида:
<doc>
  <gateway> - параметры шлюза
    <param>value</param>
    <param>value</param>
    ...
    <param>value</param>
    <xmlparams>Параметры подключения шлюза в виде XML</xmlparams>
  </gateway>
  <message>текст сообщения</message>
  <user> - параметры пользователя, которому отправляется уведомление
    <param>value</param>
    <param>value</param>
    ...
    <param>value</param>
  </user>
  <project> - параметры провайдера
    <param>value</param>
    <param>value</param>
    ...
    <param>value</param>https://docs.ispsystem.net/pages/editpage.action?pageId=211032476#
  </project>
</doc>

На выход модуль должен вернуть пустую XML или XML-описания ошибки

    • --command ingoing (используется для получения почты) — на вход модулю передается XML с параметрами шлюза, как описано выше, на выход нужно вернуть XML со списком полученных сообщений вида:
<doc>
  <messages>
    <message>исходный текст сообщения</message>
    ...
    <message>исходный текст сообщения</message>
  </messages>
</doc>

Реализация модуля с использованием заголовочных файлов BILLmanager предполагает обязательную реализацию методов:

  • virtual mgr_xml::Xml Features() const = 0; — возвращается XML-описание поддерживаемых возможностей;
  • virtual mgr_xml::Xml Ingoing(mgr_xml::Xml& input) const = 0; — на вход получает XML с параметрами шлюза (параметры могут быть так же получены методом GateParam), возвращает список сообщений в описанном формате;
  • virtual void Outgoing(mgr_xml::Xml& input) const = 0; — на вход получает XML с параметрами шлюза и сообщением для отправки (параметры могут быть так же получены методом GateParam).

Примеры модулей

C++ (с использованием библиотек BILLmanager в пакете разработчика)

Использование заголовочных файлов BILLmanager для разработки собственных модулей обработчиков доступно с версии BILLmanager 5.58.0. Кроме приведённого примера, можно изучить примеры представленные в пакете разработчика BILLmanager — billmanager-[Редакция BILLmanager]-devel, например:

yum install billmanager-devel
или 
yum install billmanager-corporate-devel
Пояснения

После этого примеры можно найти в директории:

/usr/local/mgr5/src/examples

C++ (с использованием библиотек BILLmanager)

Реализация модуля уведомлений и шлюза для отправки и получения XMPP сообщений представлена по ссылке https://github.com/ISPsystemLLC/jabber

Пример реализован на C++, с использования заголовочных файлов COREmanager и BILLmanager, а так же библиотеки Gloox. Пример составляют:

  • модуль уведомлений ntjabber — основной файл ntjabber.cpp. Отвечает за тип уведомлений, позволяет добавлять шаблоны уведомлений и создавать рассылки нужного типа;
  • модуль шлюза для подключения к Jabber серверу gwjabber — основной файл gwjabber.cpp. Отвечает за отправку уведомлений на Jabber контакт пользователя, а так же обрабатывает входящие сообщения;
  • XML-файлы, описывающие необходимые сообщения и параметры подключения с серверу:
    • billmgr_mod_ntjabber.xml — добавляет на форму редактирования пользователей поле Jabber контакта (отображение и сохранение значения происходит автоматически согласно механизму описанному по ссылке), добавляет описание типа уведомлений;
    • billmgr_mod_gwjabber.xml — описывает параметры подключения к jabber-серверу, а так же описывает наименование модуля подключения;
  • описание поля базы данных jabber — описывает дополнительное поле для таблицы user базы данных BILLmanager;
  • логотип шлюза billmanager-plugin-gwjabber.png — добавляет отображение логотипа XMPP на форму выбора модуля шлюза;
  • файл описания сборки Makefile

Другие языки программирования

XML-модулей

XML-описание модуля сохраняется в файл /usr/local/mgr5/etc/xml/billmgr_mod_ntxxx.xml, для модуля уведомлений и в файл /usr/local/mgr5/etc/xml/billmgr_mod_gwxxx.xml для модулей шлюзов, где xxx — уникальное имя модуля. В данном примере рассматривается только XML для модуля шлюза.  Для XML-файла модуля уведомлений смотрите пример на C++.

Формат файла (на примере интеграции с ePochta SMS)
<mgrdata>
  <plugin name="gwepochta">           <!-- описание плагина для отображения в BILLmanager -->
    <group>gateway</group>            <!-- привязка плагина к разделу шлюзов сообщений -->
    <author>BILLmanager team</author> <!-- автор модуля -->
  </plugin>
  <metadata name="gateway.gwepochta"> <!-- описание настроек модуля -->
    <form>
      <field name="login">
        <input type="text" name="login" required="yes" identifier="yes"/>
      </field>
      <field name="password">
        <input type="password" name="password" required="yes"/>
      </field>
      <field name="sender">
        <input type="text" name="sender" required="yes"/>
      </field>
    </form>
  </metadata>
  <lang name="ru">
    <messages name="plugin">         <!-- сообщения для описания плагина -->
      <msg name="desc_short_gwepochta">ePochta SMS</msg>
      <msg name="desc_full_gwepochta">ePochta SMS</msg>
      <msg name="price_gwepochta">Бесплатно</msg>
    </messages>
    <messages name="gateway.gwepochta"> <!-- сообщения для формы настроек модуля -->
      <msg name="login">Логин</msg>
      <msg name="password">Пароль</msg>
      <msg name="sender">Отправитель</msg>
      <msg name="hint_login">Логин в личный кабинет ePochta SMS</msg>
      <msg name="hint_password">Пароль от личного кабинета</msg>
      <msg name="hint_sender">Подпись отправителя сообщения</msg>
    </messages>
    <messages name="gateway_include"> <!-- наименование модуля для отображения в различных разделах BILLmanager -->
      <msg name="module_gwepochta">Сервер ePochta SMS</msg>
      <msg name="gwepochta">ePochta SMS</msg>
      <msg name="desc_gwepochta">ePochta SMS</msg>
    </messages>
  </lang>
  <lang name="en"> <!-- английская локализация сообщений -->
    <messages name="plugin">
      <msg name="desc_short_gwepochta">ePochta SMS</msg>
      <msg name="desc_full_gwepochta">Server ePochta SMS</msg>
      <msg name="price_gwepochta">Free</msg>
    </messages>
  </lang>
</mgrdata>

Go

package main

import "bytes"
import "log"
import "encoding/xml"
import "flag"
import "fmt"
import "os"
import "io/ioutil"
import "net/http"

func request(operation, username, password, phone, message, sender string) (string, string) {
  type SMS struct {
    XMLName    xml.Name  `xml:"SMS"`
    Operation  string    `xml:"operations>operation"`
    Username  string    `xml:"authentification>username"`
    Password  string    `xml:"authentification>password"`
    Message    string    `xml:"message>text"`
    Sender    string    `xml:"message>sender"`
    Number    string    `xml:"numbers>number"`
  }
  
  v := &SMS{
      Operation:   operation,
      Username:  username,
      Password:  password,
      Message:  message,
      Sender:    sender,
      Number:    phone,
  }

  output, err := xml.MarshalIndent(v, "  ", "    ")
  log.Print("REQUEST: " + string(output))
    
  if err != nil {
    return "", ""
  }
  
  resp, err := http.Post("http://api.myatompark.com/members/sms/xml.php", "image/jpeg", bytes.NewBuffer(output))
  
  if err != nil {
    return "", ""
  }
  
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)
  
  log.Print("RESPONSE: " + string(body))
  
  type Response struct {
    XMLName    xml.Name  `xml:"RESPONSE"`
    Status    int      `xml:"status"`
    }
  
  r := Response{Status: 1}
  resperr := xml.Unmarshal(body, &r)
  if resperr != nil {
    log.Printf("error: %v", resperr)
    return "", ""
  }
  
  if r.Status != 0 {
    return string(body), "error"
  }
  
  return string(body), ""
}

func main() {
  f, _ := os.OpenFile("var/gwepochta.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
  defer f.Close()
  
  log.SetOutput(f)
    
  command_ptr := flag.String("command", "features", "gateway command")

  flag.Parse()

  if *command_ptr == "features" {
    type Feature struct {
      XMLName  xml.Name  `xml:"feature"`
      Name  string    `xml:"name,attr"`
    }

    type Features struct {
      XMLName    xml.Name   `xml:"doc"`
      Features   []Feature    `xml:"features>feature"`
      Module    string    `xml:"notify_module"`
    }

    v := &Features{
        Module: "ntsms",
        Features: []Feature {
                {Name: "formtune"},
                {Name: "check_connection"},
                {Name: "outgoing"},
        },
      }

    output, err := xml.MarshalIndent(v, "  ", "    ")
    if err != nil {
      fmt.Println("error: %v\n", err)
    }

    os.Stdout.Write(output)
  } else if *command_ptr == "formtune" {
    bytes, _ := ioutil.ReadAll(os.Stdin)
    os.Stdout.Write(bytes)
  } else if *command_ptr == "check_connection" {
    type Doc struct {
      XMLName   xml.Name   `xml:"doc"`
      XMLparams    string    `xml:"xmlparams"`
      Login    string    `xml:"login"`
      Password  string    `xml:"password"`
    }
    
    bytes, _ := ioutil.ReadAll(os.Stdin)
    
    v := Doc{XMLparams: "none", Login: "none", Password: "none"}
    err := xml.Unmarshal(bytes, &v)
    if err != nil {
      log.Printf("error: %v", err)
      return
    }
    
    paramerr := xml.Unmarshal([]byte(v.XMLparams), &v)
    if paramerr != nil {
      log.Printf("error: %v", paramerr)
      return
    }

    _, error := request("BALANCE", v.Login, v.Password, "", "", "")
    
    if error != "" {
      type Error struct {
        Type  string  `xml:"type,attr"`
      }
      
      type Doc struct {
        XMLName   xml.Name   `xml:"doc"`
        ErrorType    Error    `xml:"error"`
      }
      
      t := &Error {
        Type: error,
      }
      
      v := &Doc{
        ErrorType:   *t,
      }

      output, _ := xml.MarshalIndent(v, "  ", "    ")
      os.Stdout.Write(output)
      return
    }
    os.Stdout.Write(bytes)
  } else if *command_ptr == "outgoing" {
    bytes, _ := ioutil.ReadAll(os.Stdin)
    log.Print(string(bytes))
    
    type Doc struct {
      XMLName   xml.Name   `xml:"doc"`
      XMLparams    string    `xml:"gateway>xmlparams"`
      Login    string    `xml:"login"`
      Password  string    `xml:"password"`
      Sender    string    `xml:"sender"`
      Message    string    `xml:"message"`
      Phone    string    `xml:"user>phone"`
    }
    
    v := Doc{XMLparams: "none", Login: "none", Password: "none", Message: "none", Phone: "none"}
    err := xml.Unmarshal(bytes, &v)
    
    log.Print(v.XMLparams)
    
    if err != nil {
      log.Printf("error: %v", err)
      return
    }
    
    xml.Unmarshal([]byte(v.XMLparams), &v)
    
    request("SEND", v.Login, v.Password, v.Phone, v.Message, v.Sender)
  }
}
Дата последнего обновления статьи: 17.04.2025. Статья подготовлена техническими писателями ISPsystem.