def rm_show_descr_images(log, logic, client, room, user):
    log.info("rm_show_descr_images()")
    rm_need_zazemlenie = mbl.get_env(user, "rm_need_zazemlenie")
    if rm_need_zazemlenie == None:
        log.debug("error get_env rm_need_zazemlenie - return to main menu")
        mbl.bot_fault(log, client, room)
        mbl.go_to_main_menu(log, logic, client, room, user)
        return False
    rm_need_ograzhdenie = mbl.get_env(user, "rm_need_ograzhdenie")
    if rm_need_ograzhdenie == None:
        log.debug("error get_env rm_need_ograzhdenie - return to main menu")
        mbl.bot_fault(log, client, room)
        mbl.go_to_main_menu(log, logic, client, room, user)
        return False
    if rm_need_zazemlenie == "need":
        if mba.send_html(
                log, client, room,
                u"Данная заявка требует установку <strong>заземлений</strong>. Отправьте мне фотографии <strong>заземлений</strong>:"
        ) == False:
            log.error("send_html() to user")
        mbl.set_env(user, "rm_need_zazemlenie", "wait_images")
        mbl.set_env(user, "rm_current_need_photo_type", "rm_need_zazemlenie")
        return True
    elif rm_need_ograzhdenie == "need":
        if mba.send_html(
                log, client, room,
                u"Данная заявка требует установку <strong>ограждений</strong>. Отправьте мне фотографии <strong>ограждений</strong>:"
        ) == False:
            log.error("send_html() to user")
        mbl.set_env(user, "rm_need_ograzhdenie", "wait_images")
        mbl.set_env(user, "rm_current_need_photo_type", "rm_need_ograzhdenie")
        return True
    else:
        mbl.set_env(user, "rm_current_need_photo_type", "end")
        log.info("rm_show_descr_images() end photo receive")
        return True
    return True
def zabbix_show_groups(log, logic, client, room, user, data, source_message,
                       cmd):
    try:
        log.info("zabbix_show_groups()")

        zapi = zabbix_init(log)
        if zapi == None:
            log.error("zabbix_init()")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        groups = get_default_groups(log, client, room, user, zapi)
        if groups == None:
            log.debug("error get_default_groups() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        groups_names = zabbix_get_hosts_groups_names(log, zapi, groups)
        if groups_names == None:
            log.debug(
                "error zabbix_get_hosts_groups_names() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        zabbix_login = mbl.get_env(user, "zabbix_login")
        if zabbix_login == None:
            zabbix_login = "******"
        text = "<p>Текущий пользователь: <strong>%s</strong></p>" % zabbix_login
        text += "<p><strong>Список текущих групп:</strong></p><ol>"
        for name in groups_names:
            text += "<li>%s</li> " % name
        text += "</ol>"
        if mba.send_html(log, client, room, text) == False:
            log.error("send_html() to user %s" % user)
            return False
        # Завершаем текущий этап и переходим в главное меню:
        if mba.send_notice(
                log, client, room,
                u"возвращаюсь в начальное меню. Отправьте 'помощь' или '1' для получения справки."
        ) == False:
            log.error("send_notice() to user %s" % user)
        mbl.set_state(user, logic)
        return True
    except Exception as e:
        log.error(get_exception_traceback_descr(e))
        return False
def show_zayavka_detail(log, memmory, logic, client, room, user, data, message,
                        cmd):
    log.debug("show_zayavka_detail()")
    # сохраняем раздел для возможного повторного вопроса о номере заявки:
    state = mbl.get_state(log, user)
    if state == None:
        log.error("mbl.get_state(log,%s)" % user)
        return False
    log.debug("1: state=")
    log.debug(state)
    log.info("save cur_state to return_to_zayavka_enter")
    mbl.set_env(user, "return_to_zayavka_enter", state)

    # Получаем номер заявки:
    try:
        zayavka_num = int(mbl.get_env(user, "rm_zayavka_num"))
    except:
        log.warning(u"user not enter num zayavka: user send: '%s'" %
                    (mbl.get_env(user, "rm_zayavka_num")))
        if mba.send_message(
                log, client, room,
                u"Не смог распознать номер заявки. Пожалуйста, введите номер заявки числом"
        ) == False:
            log.error("send_message() to user")
        return False
    text = "<strong>Детали заявки с номером %d:</strong>" % zayavka_num
    #TODO
    # TODO получить данные из АПИ:
    # Выставляем переменные:
    mbl.set_env(user, "rm_need_zazemlenie", "need")  # или not_need
    mbl.set_env(user, "rm_need_ograzhdenie", "need")
    text += "<p>FIXME</p>"

    # выводим вопрос:
    text += "<p>Если всё верно - введите <strong>1</strong>,\nесли нет - введите <strong>2</strong>.</p>"

    if mba.send_html(log, client, room, text) == False:
        log.error("send_html() to user %s" % user)
        return False
    mbl.set_state(user, data["answer"])
    return True
def zabbix_show_triggers(log, logic, client, room, user, data, source_message,
                         cmd):
    try:
        log.info("zabbix_show_triggers()")
        zabbix_priority = mbl.get_env(user, "zabbix_priority")
        if zabbix_priority == None:
            log.debug("error get_env zabbix_priority - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        zapi = zabbix_init(log)
        if zapi == None:
            log.error("zabbix_init()")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        groups = get_default_groups(log, client, room, user, zapi)
        if groups == None:
            log.debug("error get_default_groups() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        if mba.send_notice(log, client, room,
                           u"формирую статистику...") == False:
            log.error("send_notice() to user %s" % user)
            return False

        #Get List of problems
        problems = zapi.problem.get(\
            groupids=groups,\
            output=['eventid','objectid','clock','ns','name','severity'],\
            source=0,\
            object=0, # тип проблем - триггеры\
            sortfield=['eventid'], preservekeys=1,limit=100,recent=1,evaltype=0,\
            severities=zabbix_priority,\
            sortorder='DESC',\
            selectSuppressionData=['maintenanceid','suppress_until']\
            )
        if problems == None:
            log.error("error zapi.problems.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        triggerids = []
        for problemid in problems:
            problem = problems[problemid]
            triggerids.append(problem['objectid'])

        #Get List of triggers
        triggers = zapi.trigger.get(\
            output=['priority','expression','recovery_mode','recovery_expression','comments','url'],\
            selectHosts=['hostid'],\
            triggerids=triggerids,\
            monitored=1,skipDependent=1,preservekeys=1,\
            selectItems=['itemid','hostid','name','key_','value_type','units','valuemapid']\
            )
        if triggers == None:
            log.error("error zapi.trigger.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        hostids = []
        for triggerid in triggers:
            trigger = triggers[triggerid]
            for item in trigger['hosts']:
                hostids.append(item['hostid'])

        #Get List of hosts
        hosts = zapi.host.get(hostids=hostids,
                              output=[
                                  'hostid', 'name', 'maintenanceid',
                                  'maintenance_status', 'maintenance_type'
                              ])
        if hosts == None:
            log.error("error zapi.host.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        priority = u"среднего"
        if zabbix_priority == "5":
            priority = u"критического"
        elif zabbix_priority == "4":
            priority = u"важного"

        text = "<p>Список активных триггеров <strong>%s</strong> уровня:</p><ol>" % priority
        for problemid in problems:
            problem = problems[problemid]
            triggerid = problem['objectid']
            if triggerid not in triggers:
                continue
            trigger = triggers[triggerid]
            hostid = int(trigger['hosts'][0]['hostid'])
            host = get_host_by_id(log, hosts, hostid)
            if host == None:
                #log.debug("skip unknown host")
                #        log.debug(hosts)
                continue
            data = get_time_str_from_unix_time(problem['clock'])
            period = get_period_str_from_ns(problem['clock'])
            #  print("номер: %d, дата наступления события: %s (продолжительность: %s), описание: '%s', хост: '%s'"%(index,data,period,problem['name'],host['name']))

            text += "<li>"
            text += "<strong>" + problem['name'] + "</strong>"
            text += ", <em>устройство:</em> <strong>%s</strong>" % host['name']
            text += ", <em>время события:</em> <strong>%s</strong>" % data
            text += ", <em>продолжительность:</em> <strong>%s</strong>" % period
            text += "</li>"
        text += "</ol>"
        if mba.send_html(log, client, room, text) == False:
            log.error("send_html() to user %s" % user)
            return False

        #mbl.go_to_main_menu(log,logic,client,room,user)
        if mba.send_notice(
                log, client, room,
                u"можете выбрать просмотр триггеров иной важности (1 - критические, 2 - важные, 3 - средние) или 0 - для выхода"
        ) == False:
            log.error("send_notice() to user %s" % user)
            return False
        return True
    except Exception as e:
        log.error(get_exception_traceback_descr(e))
        return False
def zabbix_show_stat(log, logic, client, room, user, data, source_message,
                     cmd):
    try:
        log.info("zabbix_show_triggers()")

        zapi = zabbix_init(log)
        if zapi == None:
            log.error("zabbix_init()")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        groups = get_default_groups(log, client, room, user, zapi)
        if groups == None:
            log.debug("error get_default_groups() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False

        if mba.send_notice(log, client, room,
                           u"формирую статистику...") == False:
            log.error("send_notice() to user %s" % user)
            return False

        triggers = zapi.trigger.get(groupids=groups,
                                    only_true="1",
                                    active="1",
                                    min_severity=3,
                                    output="extend",
                                    selectFunctions="extend",
                                    expandDescription="True")
        if triggers == None:
            log.debug("error zapi.trigger.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False
        priority_3_num = len(triggers)

        triggers = zapi.trigger.get(groupids=groups,
                                    only_true="1",
                                    active="1",
                                    min_severity=4,
                                    output="extend",
                                    selectFunctions="extend",
                                    expandDescription="True")
        if triggers == None:
            log.debug("error zapi.trigger.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False
        priority_4_num = len(triggers)

        triggers = zapi.trigger.get(groupids=groups,
                                    only_true="1",
                                    active="1",
                                    min_severity=5,
                                    output="extend",
                                    selectFunctions="extend",
                                    expandDescription="True")
        if triggers == None:
            log.debug("error zapi.trigger.get() - return to main menu")
            mbl.bot_fault(log, client, room)
            mbl.go_to_main_menu(log, logic, client, room, user)
            return False
        priority_5_num = len(triggers)

        sev_5_num = priority_5_num
        sev_4_num = priority_4_num - sev_5_num
        sev_3_num = priority_3_num - priority_4_num

        zabbix_login = mbl.get_env(user, "zabbix_login")
        if zabbix_login == None:
            zabbix_login = "******"
        text = "<p>Текущий пользователь: <strong>%s</strong></p>" % zabbix_login
        text += "<p><strong>Список проблем для выбранных групп, сгруппированных по важности:</strong></p><br><ol>"
        text += "<li>Критических проблем - %d шт.</li> " % sev_5_num
        text += "<li>Важных проблем - %d шт.</li> " % sev_4_num
        text += "<li>Средних проблем - %d шт.</li> " % sev_3_num
        text += "</ol>"
        if mba.send_html(log, client, room, text) == False:
            log.error("send_html() to user %s" % user)
            return False
        # Завершаем текущий этап и ждём ответа от пользователя:
        mbl.set_state(user, data["answer"])
        return True
    except Exception as e:
        log.error(get_exception_traceback_descr(e))
        return False
def process_message(log,
                    client,
                    user,
                    room,
                    message,
                    formated_message=None,
                    format_type=None,
                    reply_to_id=None,
                    file_url=None,
                    file_type=None):
    global logic
    global memmory
    source_message = None
    source_cmd = None

    if reply_to_id != None and format_type == "org.matrix.custom.html" and formated_message != None:
        # разбираем, чтобы получить исходное сообщение и ответ
        source_message = re.sub('<mx-reply><blockquote>.*<\/a><br>', '',
                                formated_message)
        source_message = re.sub('</blockquote></mx-reply>.*', '',
                                source_message)
        source_cmd = re.sub(r'.*</blockquote></mx-reply>', '',
                            formated_message.replace('\n', ''))
        log.debug("source=%s" % source_message)
        log.debug("cmd=%s" % source_cmd)
        message = source_cmd

    # убираем пробелы по бокам:
    message = message.strip()

    # имя бота:
    nick_name = client.api.get_display_name(client.user_id)
    log.debug("nick_name=%s" % nick_name)

    to_us = False
    if re.match(r'^!*%s:* ' % nick_name.lower(), message.lower()) != None:
        to_us = True
        # корректный формат body:
        log.debug("remove prefix from cmd")
        # разделяем только один раз (первое слово), а потом берём "второе слово",
        # которое содержит всю оставшуюся строку:
        #message=message.split(' ',1)[1].strip()
        message = re.sub(r'^!*%s: ' % nick_name.lower(), '', message.lower())

    if re.match(r'^!*"%s" \(https://matrix.to/.*\): ' % nick_name.lower(),
                message.lower()) != None:
        to_us = True
        # некорректный формат body (RitX/Element-android):
        # убираем командный префикс:
        #message=re.sub('^!*%s:* '%nick_name.lower(),'', message)
        log.debug("remove prefix from cmd")
        message = re.sub(
            r'^!*"%s" \(https://matrix.to/[/#_.:@A-Za-z]*\): ' %
            nick_name.lower(), '', message.lower())

    # Проверяем сколько в комнате пользователей. Если более двух - то это не приватный чат и потому не отвечаем на команды, если к нам не обращаются по имени:
    users = client.rooms[room].get_joined_members()
    if users == None:
        log.error("room.get_joined_members()")
        return False
    users_num = len(users)
    log.debug("in room %d users" % users_num)
    if users_num > 2 and to_us == False:
        # публичная комната - не обрабатываем команды:
        log.info(
            "this is public room - skip proccess_commands without our name")
        return True
    else:
        log.debug("this is private chat (2 users) - proccess commands")

    # обработка по логике
    log.debug("get cmd: %s" % message)
    log.debug("user=%s" % user)
    if user == conf.matrix_username or "@%s:" % conf.matrix_username in user:
        log.debug("message from us - skip")
        return True
    state = get_state(log, user)
    if state == None:
        log.error("get_state(log,%s)" % user)
        return False

    for cmd in state:
        if message.lower() == u"отмена" or message.lower(
        ) == "cancel" or message.lower() == "0":
            # Стандартная команда отмены - перехода в начальное меню:
            set_state(user, logic)
            text = "Переход в начало меню. Наберите 'помощь' или 'help' для спрвки по командам"
            if mba.send_message(log, client, room, text) == False:
                log.error("send_message() to user")
                log.error("cur state:")
                log.error(state)
                return False
            return True

        if check_equal_cmd(state, message.lower(), cmd) or cmd == "*":
            data = state[cmd]
            # Шлём стандартное для этого состояния сообщение:
            if "message" in data:
                # поддержка переменных в сообщениях:
                text = replace_env2val(log, user, data["message"])
                if text == None:
                    text = data["message"]
                if "message_type" in data and data["message_type"] == "html":
                    if mba.send_html(log, client, room, text) == False:
                        log.error("send_html() to user %s" % user)
                        log.error("cur state:")
                        log.error(state)
                        return False
                else:
                    if mba.send_message(log, client, room, text) == False:
                        log.error("send_message() to user")
                        log.error("cur state:")
                        log.error(state)
                        return False
            # Устанавливаем переданный пользователем текст, как переменную (если так описано в правиле логики бота):
            if "set_env" in data:
                set_env(user, data["set_env"], message)
            # Устанавливаем статическую переменную (значение может содержать переменную в {}):
            if "set_static_keys" in data:
                for key in data["set_static_keys"]:
                    val = data["set_static_keys"][key]
                    # в цикле заменяем значения переменной
                    val = replace_env2val(log, user, val)
                    if val == None:
                        log.error("replace_env2val()")
                        bot_fault(log, client, room)
                        log.error("cur state:")
                        log.error(state)
                        return False
                    set_env(user, key, val)
            # Проверяем, что должны сделать:
            # Отмена:
            if data["type"] == "sys_cmd_cancel":
                set_state(user, logic)
                return True
            # Обычная команда:
            if data["type"] == "cmd":
                set_state(user, data["answer"])
                return True

            # Отправка файла с данными из url:
            if data["type"] == "url_to_file":
                set_state(user, logic)
                # Подготовка URL:
                if "url" not in data:
                    log.error(
                        "rule file has error logic: 'type':'url_to_file' have no 'url'"
                    )
                    bot_fault(log, client, room)
                    log.error("cur state:")
                    log.error(state)
                    return False
                url = data["url"]
                # Заменяем параметры:
                url = replace_env2val(log, user, url)
                if url == None:
                    log.error("replace_env2val()")
                    bot_fault(log, client, room)
                    log.error("cur state:")
                    log.error(state)
                    return False
                file_name = get_env(user, "file_name")
                if file_name == None:
                    log.error("not set file_name env")
                    bot_fault(log, client, room)
                    log.error("cur state:")
                    log.error(state)
                    return False
                content_type = get_env(user, "content_type")
                if content_type == None:
                    log.error("not set content_type env")
                    bot_fault(log, client, room)
                    log.error("cur state:")
                    log.error(state)
                    return False
                log.debug("send file. Data get from url: '%s'" % url)
                if send_report(log,
                               client,
                               user,
                               room,
                               url,
                               content_type=content_type,
                               file_name=file_name) == False:
                    log.error("send_report(user=%(user)s, url='%(url)')" % {
                        "user": user,
                        "url": url
                    })
                    return False
                # Очищаем все переменные у пользователя
                memmory[user]["env"] = {}
                set_state(user, logic)
                return True

            #=========================== zabbix =====================================
            if data["type"] == "zabbix_check_login":
                log.debug("message=%s" % message)
                log.debug("cmd=%s" % cmd)
                zabbix_user_name = get_env(user, "zabbix_login")
                if zabbix_user_name == None:
                    log.error("get_env('zabbix_login')")
                    if mba.send_message(log, client, room,
                                        "Внутренняя ошибка бота") == False:
                        log.error("send_message() to user")
                        return False
                zabbix_login = mblz.zabbix_check_login(log, zabbix_user_name)
                if zabbix_login == None:
                    if mba.send_message(
                            log, client, room,
                            "Некорректный zabbix_login - попробуйте ещё раз"
                    ) == False:
                        log.error("send_message() to user")
                        return False
                    return True
                else:
                    set_state(user, logic)
                    set_env(user, "zabbix_login", zabbix_login)
                    if mblz.zabbix_update_hosts_groups_of_user(log,
                                                               user) == False:
                        log.error('error save groups of user')
                        if mba.send_message(
                                log, client, room,
                                "error save groups of user") == False:
                            log.error("send_message() to user")
                            return False
                    if mba.send_message(
                            log, client, room,
                            "сохранил zabbix_login '%s' для вас. Теперь вы будет получать статистику из групп, в которые входит этот пользователь\nВернулся в основное меню"
                            % zabbix_login) == False:
                        log.error("send_message() to user")
                        return False
                    return True

            if data["type"] == "zabbix_get_version":
                log.debug("message=%s" % message)
                log.debug("cmd=%s" % cmd)
                return mblz.zabbix_get_version(log, logic, client, room, user,
                                               data, message, cmd)
            if data["type"] == "zabbix_show_groups":
                log.debug("message=%s" % message)
                log.debug("cmd=%s" % cmd)
                return mblz.zabbix_show_groups(log, logic, client, room, user,
                                               data, message, cmd)
            if data["type"] == "zabbix_show_stat":
                log.debug("message=%s" % message)
                log.debug("cmd=%s" % cmd)
                return mblz.zabbix_show_stat(log, logic, client, room, user,
                                             data, message, cmd)
            if data["type"] == "zabbix_show_triggers":
                log.debug("message=%s" % message)
                log.debug("cmd=%s" % cmd)
                return mblz.zabbix_show_triggers(log, logic, client, room,
                                                 user, data, message, cmd)
            #=========================== zabbix  - конец =====================================

    if get_state(log, user) == logic:
        # Пользователь пишет что попало в самом начале диалога:
        if mba.send_message(
                log, client, room,
                "Не понял Вас. Пожалуйста, введите 'помощь' или 'help' для справки по известным мне командам"
        ) == False:
            log.error("send_message() to user")
            return False
    else:
        if mba.send_message(
                log, client, room,
                "Не распознал команду - похоже я её не знаю... Пожалуйста, введите варианты описанные выше или 'отмена' или '0'"
        ) == False:
            log.error("send_message() to user")
            return False
    return True
def send_statistic(log, client, user, room, statistic_about, statistic_days):
    log.debug("send_statistic(%s,%s,%s,%s)" %
              (user, room, statistic_about, statistic_days))
    num = 0
    mba.send_message(log, client, room, u"Формирую статистику...")
    # подключаемся к базе рассылок:
    try:
        con = mdb.connect(conf.send_db_host,
                          conf.send_db_user,
                          conf.send_db_passwd,
                          conf.send_db_name,
                          charset="utf8",
                          use_unicode=True)
        cur = con.cursor()
    except mdb.Error as e:
        log.error("send_statistic(): error connect to db (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    # получаем статистику:
    about_text = "всех пользователей"
    where_about = ""
    if statistic_about == 'current_user':
        about_text = u"Вас"
        mail_address = ""
        telegram_number = ""
        sms_number = ""
        # мало указать только адрес матрицы - запросы на отправку для пользователя могли быть в те времена, когда матрицы ещё не было:
        try:
            sql = u"select mail_address,telegram_number,sms_number from u_users where matrix_uid='%s'" % user
            log.debug("send_statistic(): sql: %s" % sql)
            cur.execute(sql)
            user_data = cur.fetchone()
            mail_address = user_data[0]
            telegram_number = user_data[1]
            sms_number = user_data[2]
        except mdb.Error as e:
            log.error("send_statistic(): sql error (%d: %s)" %
                      (e.args[0], e.args[1]))
            return False
        or_where = ""
        if mail_address != "" and mail_address != None:
            or_where += " or email_address='%s' " % mail_address
        if telegram_number != "" and telegram_number != None:
            or_where += " or address='%s' " % telegram_number
        if or_where == "":
            where_about = " and matrix_address='%s'" % user
        else:
            where_about = " and ( matrix_address='%s' " % user + or_where + ") "
    else:
        # для всех:
        about_text = u"всех пользователей"
        where_about = ""
    # временной лимит:
    time_now = time.time()
    time_offset = time_now - int(statistic_days) * 24 * 3600
    where_days = "time_create > '%s'" % time.strftime(
        '%Y-%m-%d %T', time.localtime(time_offset))

    count_all = 0
    count_sent_by_matrix = 0
    count_readed_by_matrix = 0
    count_success_matrix = 0
    count_success_telegram = 0
    count_success_email = 0
    count_success_with_errors = 0
    count_errors = 0
    count_fault = 0
    # всего задач на отправку:
    try:
        sql = u"select count(*) from telegram where %s %s" % (where_days,
                                                              where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_all = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_all=%d" % count_all)
    # успешно отправлено через MATRIX:
    try:
        sql = u"select count(*) from telegram where status='sent_by_matrix' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_sent_by_matrix = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_sent_by_matrix=%d" % count_sent_by_matrix)
    # успешно прочитано через MATRIX:
    try:
        sql = u"select count(*) from telegram where status='readed_by_matrix' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_readed_by_matrix = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_readed_by_matrix=%d" % count_readed_by_matrix)

    # успешно через MATRIX:
    count_success_matrix = count_sent_by_matrix + count_readed_by_matrix
    log.debug("count_success_matrix=%d" % count_success_matrix)

    # успешно прочитано через Telegram:
    try:
        sql = u"select count(*) from telegram where status='sent_by_telegram' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_success_telegram = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_success_telegram=%d" % count_success_telegram)

    # успешно прочитано через почту:
    try:
        sql = u"select count(*) from telegram where status='sent_by_email' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_success_email = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_success_email=%d" % count_success_email)

    # пока ещё не отправлено (не было попыток отправить):
    try:
        sql = u"select count(*) from telegram where status='new' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_new = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_new=%d" % count_new)

    # отправка не удалась, но попытки продолжаются:
    try:
        sql = u"select count(*) from telegram where status='error' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_errors = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_errors=%d" % count_errors)

    # отправка не удалась, и попытки отправить прекращены:
    try:
        sql = u"select count(*) from telegram where status='fault' and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_fault = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_fault=%d" % count_fault)

    # отправка удалась, но не с первого раза:
    try:
        sql = u"select count(*) from telegram where status like '%%sent%%' and retry_num > 1 and %s %s" % (
            where_days, where_about)
        log.debug("send_statistic(): sql: %s" % sql)
        cur.execute(sql)
        count_success_with_errors = cur.fetchone()[0]
    except mdb.Error as e:
        log.error("send_statistic(): sql error (%d: %s)" %
                  (e.args[0], e.args[1]))
        return False
    log.debug("count_success_with_errors=%d" % count_success_with_errors)

    if con:
        con.close()
    report=u"""<p><strong>Статистика по рассылке для %(about_text)s</strong></p>
<p>Всего за последние %(statistic_days)s дней было %(count_all)d запросов на рассылку для %(about_text)s. Из них:</p>
  <ul>
    <li>Ещё не отправлялись (я ещё не пробовал отправить): <strong>%(count_new)d</strong></li>
    <li>Успешно отправлено через MATRIX: <strong>%(count_success_matrix)d</strong> сообщений, из них прочитано %(count_readed_by_matrix)d сообщений</li>
    <li>Успешно отправлено через Telegram: <strong>%(count_success_telegram)d</strong> сообщений</li>
    <li>Успешно отправлено через почту: <strong>%(count_success_email)d</strong> сообщений</li>
    <li>Отправлено, но не с первого раза (либо не было связи, либо не указаны учётные данные по основным системам): <strong>%(count_success_with_errors)d</strong> сообщений</li>
    <li>Не получилось отправить, но попытки отправить всё ещё продолжаются: <strong>%(count_errors)d</strong> сообщений</li>
    <li>Не получилось отправить совсем (система прекратила попытки отправить): <strong>%(count_fault)d</strong> сообщений</li>
  </ul>
<p><em>Всего хорошего, с уважением служба ИТ.</em></p>
"""%{\
    "statistic_days":statistic_days,\
    "about_text":about_text,\
    "count_new":count_new,\
    "count_all":count_all,\
    "count_success_matrix":count_success_matrix,\
    "count_readed_by_matrix":count_readed_by_matrix,\
    "count_success_telegram":count_success_telegram,\
    "count_success_email":count_success_email,\
    "count_success_with_errors":count_success_with_errors,\
    "count_errors":count_errors,\
    "count_fault":count_fault\
  }
    if mba.send_html(log, client, room, report) == False:
        log.error("send_statistic(): error mba.send_html()")
        return False

    return True
def send_new_notify(log,
                    matrix_client,
                    matrix_room,
                    last_email_timestamp,
                    last_email_message_ids,
                    server,
                    login,
                    passwd,
                    mailbox="inbox",
                    redmine_sender="*****@*****.**"):
    log.debug("start function")
    result = {}
    result["last_email_timestamp"] = last_email_timestamp
    result["last_email_message_ids"] = last_email_message_ids
    try:
        client = init(log, server, login, passwd, mailbox, check_cert=False)
        if client == None:
            log.error("mail init(server=%s, email=%s, mailbox=%s)" %
                      (server, login, mailbox))
            return None

        log.debug("try search today email from %s" % redmine_sender)
        messages = client.search(
            [u'SINCE',
             datetime.datetime.now(), 'FROM', redmine_sender], 'utf8')
        all_check_messages = 0
        skip_last_messages = 0
        skip_old_messages = 0
        new_messages = 0
        for uid, message_data in client.fetch(messages, 'RFC822').items():
            all_check_messages += 1
            #log.debug("proccess email with uid=%d"%uid)
            email_message = email.message_from_bytes(message_data[b'RFC822'])

            subj_encoded = decode_header(email_message.get('Subject'))
            subj = subj_encoded[0][0].decode(subj_encoded[0][1])

            message_id = decode_header(email_message.get('Message-ID'))[0][0]
            date_str = decode_header(email_message.get('Date'))[0][0]
            date_dt = datetime.datetime.strptime(date_str,
                                                 '%a, %d %b %Y %H:%M:%S %z')
            message_unix_time = time.mktime(date_dt.timetuple())

            if message_unix_time == last_email_timestamp and message_id in last_email_message_ids:
                # скорее всего то же самое письмо, что отправляли в прошлый раз последним - пропускаем.
                # эта проверка нужна т.к. теоритически могут быть письма с тем же временем получения,
                # но не отправленные в прошлый раз. Или отправленные, но тогда они отправятся ещё раз :-(
                # но это лучше, чем если бы они не отправились бы вообще. Т.к. вероятность этого мала, то смысла
                # городить (и запоминать) список message_id-ов нет.
                #log.debug("skip previowse last sended email")
                skip_last_messages += 1
                continue
            if message_unix_time < last_email_timestamp:
                # пропускаем более старые сообщения
                #log.debug("skip older emails before previowse last sended email")
                skip_old_messages += 1
                continue

            new_messages += 1
            # обрабатываем более новые сообщения:
            # получаем телос сообщения:
            if email_message.is_multipart():
                for part in email_message.walk():
                    ctype = part.get_content_type()
                    cdispo = str(part.get('Content-Disposition'))

                    # skip any text/plain (txt) attachments
                    if ctype == 'text/plain' and 'attachment' not in cdispo:
                        body = part.get_payload(decode=True)  # decode
                        break
            # not multipart - i.e. plain text, no attachments, keeping fingers crossed
            else:
                body = email_message.get_payload(decode=True)

            decripted_body = body.decode('utf8')
            # отправляем пользователю сообщение:
            # переформатируем почтовое сообщение в нужное сообщение матрицы (оставляем нужную информацию):
            result_matrix_text = email_message_to_matrix(log, decripted_body)
            if mba.send_html(log, matrix_client, matrix_room,
                             result_matrix_text) == False:
                log.error("mba.send_message()")
                return None

            # сохраняем последние данные сообщения:
            if result["last_email_timestamp"] == message_unix_time:
                # время то же, поэтому добавляем message_id:
                result["last_email_message_ids"].append(message_id)
            else:
                result["last_email_timestamp"] = message_unix_time
                result["last_email_message_ids"] = [message_id]

    except Exception as e:
        log.error(get_exception_traceback_descr(e))
        return None
    log.info(
        "proccess emails result: new=%d, last_sended_skip=%d, old=%d, all=%d" %
        (new_messages, skip_last_messages, skip_old_messages,
         all_check_messages))
    return result