Jump to content

Существует приложение для андроида для уведомления алертов с zabbix вместо телеграмм?


Recommended Posts

Posted

чаты slack, rocketchat, mattermost (последние два self-hosted).

у коллег туда летят оповещения

мне не нравятся - слишком тяжелые приложения сами по себе, хоть для андроида, хоть для винды.

но как вариант в периоды, когда на безрыбье и карась раком станет - вполне.

  • 4 weeks later...
Posted

Я к Максу прикрутил.

 

Собственно оповещалка (скопирована с Телеграма, изменен код скрипта):

var Max = {
    token: null,
    to: null,
    message: null,
    proxy: null,
    format: null,

    send: function() {
        var params = {
            text: Max.message,
            notify: false
        },
        data,
        response,
        request = new HttpRequest(),
        url = 'https://platform-api.max.ru/messages?disable_link_preview=false&chat_id=' + Max.to;

        if (Max.format !== null) {
            params['format'] = Max.format;
        }

        if (Max.proxy) {
            request.setProxy(Max.proxy);
        }

        request.addHeader('Authorization: ' + Max.token);
        request.addHeader('Content-Type: application/json');
        data = JSON.stringify(params);

        // Remove replace() function if you want to see the exposed token in the log file.
        Zabbix.log(4, '[Max Webhook] URL: ' + url.replace(Max.token, '<TOKEN>'));
        Zabbix.log(4, '[Max Webhook] params: ' + data);
        response = request.post(url, data);
        Zabbix.log(4, '[Max Webhook] HTTP code: ' + request.getStatus());

        try {
            response = JSON.parse(response);
        }
        catch (error) {
            response = null;
        }

        if (request.getStatus() !== 200) {
            if (typeof response.message === 'string') {
                throw response.message;
            }
            else {
                throw 'Unknown error. Check debug log for more information.'
            }
        }
    }
}

try {
    var params = JSON.parse(value);

    if (typeof params.Token === 'undefined') {
        throw 'Incorrect value is given for parameter "Token": parameter is missing';
    }

    Max.token = params.Token;

    if (params.HTTPProxy) {
        Max.proxy = params.HTTPProxy;
    } 

    if (['Markdown', 'HTML'].indexOf(params.Format) !== -1) {
        Max.format = params.Format;
    }

    Max.to = params.To;
    Max.message = params.Subject + '\n' + params.Message;
    Max.send();

    return 'OK';
}
catch (error) {
    Zabbix.log(4, '[Max Webhook] notification failed: ' + error);
    throw 'Sending failed: ' + error + '.';
}

Токен прописывается прямо в параметрах оповещалки (параметр Token), в остальных параметрах прописаны макросы ALERT, в конечном итоге они задаются в параметрах пользователя Zabbix (раздел Оповещения).

 

Кроме этого в оповещения добавлен скрипт для сводной информации (добавляется в "Способы оповещения" как PHP-скрипт):

#!/usr/bin/php
<?php // Дополнительная обработка событий Zabbix
ini_set('display_errors', 1);
error_reporting(E_ALL);
require(".../header.php");
require(".../utf_overload.php");

$cli = (boolean)(php_sapi_name()=="cli");
if (!$cli)
{
        $log->prn("Invalid usage! For CLI mode only!");
        exit;
}
$script = basename(__FILE__, '.php');
$params = [];
$params['self'] = array_shift($argv);
$params['subj'] = array_shift($argv);
$params['rcpt'] = array_shift($argv);
$params['event'] = array_shift($argv);

print "Подключение к NMS Zabbix\n";
if (!$zb = new Zabbix (null)) {
        print "! Сбой инициализации NMS Zabbix\n";
        exit;
} elseif ($zb->error()) {
        print "! Сбой подключения NMS Zabbix: " . $zb->error() . "\n";
        exit;
}

if (file_exists("/tmp/{$script}_buffer.tmp") && ((time() - filemtime("/tmp/{$script}_buffer.tmp")) > 60)) unlink("/tmp/{$script}_buffer.tmp");
file_put_contents("/tmp/{$script}_buffer.tmp", getmypid() . "\n", FILE_APPEND);

print "Запрос действующих проблем из Zabbix...\n";
$tmp = $zb->request("problem.get", ["output"=>"extend", "recent"=>false, "severities"=>[4,5], "groupids"=>[19,20,21,22], "sortfield"=>["eventid"], "sortorder"=>"DESC"]);
if (!$tmp)
{
        print "! Ошибка получения списка проблем из Zabbix\n";
        exit;
}
if (isset($tmp['error']))
{
        print "! Ошибка получения списка проблем из Zabbix: {$response['error']}\n";
        exit;
}
$tmp = $tmp['result'];
print "Запрос списка проблем из Zabbix выполнен, строк: " . count($tmp) . "\n";
$lst = [];
foreach ($tmp as $rec) $lst[$rec['eventid']] = ['id'=>$rec['eventid'], 'clock'=>$rec['clock'], 'problem'=>$rec['name'], 'level'=>$rec['severity']];

print "Запрос деталей проблем из Zabbix...\n";
$tmp = $zb->request("event.get", ["output"=>"extend", "eventids"=>array_keys($lst), "selectHosts"=>"extend"]);
if (!$tmp)
{
        print "! Ошибка получения деталей проблем из Zabbix\n";
        exit;
}
if (isset($tmp['error']))
{
        print "! Ошибка получения деталей проблем из Zabbix: {$response['error']}\n";
        exit;
}
$tmp = $tmp['result'];
foreach ($tmp as $rec)
{
        if (!isset($lst[$rec['eventid']])) continue;
        if (!$rec['value']) continue;
        if (isset($rec['hosts'], $rec['hosts'][0]))
        {
                if ($rec['hosts'][0]['status']) continue;
                $lst[$rec['eventid']]['host'] = $rec['hosts'][0]['host'];
                $lst[$rec['eventid']]['title'] = $rec['hosts'][0]['name'];
                $lst[$rec['eventid']]['date'] = date("Y-m-d H:i", $rec['clock']);
                $lst[$rec['eventid']]['time'] = time() -  $rec['clock'];
        }
}
$lst = array_filter($lst, function($rec) { return isset($rec['host']);});
$ids = array_keys($lst);
sort($ids);
$ids = implode(",", $ids);

$ref = null; $old = ""; $first = null; $mv = null;
if (file_exists("/tmp/{$script}_max.tmp")) {
        $tmp = file_get_contents("/tmp/{$script}_max.tmp");
        if (($tmp !== false) && $tmp)
        {
                $ref = trim(strtok($tmp, ":"));
                $old = trim(strtok(":"));
                $tmp = trim(strtok(":"));
                if ($tmp) $first = (int)$tmp;
                $tmp = trim(strtok(":"));
                if ($tmp) $mv = (int)$tmp;
        }
} else {
        $first = time();
        $mv = 0;
}

print "Буфферизация отправки команд...\n";
sleep(2);
if (!file_exists("/tmp/{$script}_buffer.tmp")) exit;
$tmp = file_get_contents("/tmp/{$script}_buffer.tmp");
if (!$tmp) exit;
$tmp = explode("\n", trim($tmp));
$tmp = end($tmp);
if ($tmp != getmypid()) exit;
unlink("/tmp/{$script}_buffer.tmp");

$max = new Max();
$rcpt = $params['rcpt'];

if ($ids) {
        print "# Есть аварии\n";
        if ($ids == $old) {
                print "# Состав аварий не изменился, ничего не делать\n";
                null;
        } else {
                print "# Состав аварий изменился\n";
                $cnt = count($lst);
                if (isset($mv) && ($cnt > $mv)) $mv = $cnt;
                $max->build();
                $max->build("Список действующих аварий ({$cnt}):\n");
                foreach ($lst as $rec)
                {
                        if ($cnt > 100) {
                                $max->build("- {$rec['host']}\n");
                        } elseif ($cnt > 60) {
                                $max->build("- {$rec['title']}\n");
                        } else {
                                $max->build("- {$rec['title']} (с " . date("Y-m-d H:i", $rec['clock']) . ")\n");
                        }
                }
                $max->build("\nИнформация обновлена: " . date("Y-m-d H:i:s"));
                if ($ref) {
                        print "# Если есть закрепленное сообщение, то обновить его\n";
                        $max->messageUpdate($ref, null);
                        file_put_contents("/tmp/{$script}_max.tmp", "{$ref}: {$ids}" . ($first ? ": {$first}" . ($mv ? ": {$mv}" : "") : ""));
                } else {
                        print "# Если закрепленного сообщения нет, то создать его и сохранить кеш\n";
                        if ($tmp = $max->messageSend($rcpt, null)) {
                                $ref = $tmp['message']['body']['mid'];
                                $max->messagePin($rcpt, $ref);
                                file_put_contents("/tmp/{$script}_max.tmp", "{$ref}: {$ids}" . ($first ? ": {$first}" . ($mv ? ": {$mv}" : "") : ""));
                        }
                }
        }
} else {
        print "# Нет аварий\n";
        if ($ref) {
                print "# Если есть закрепленное сообщение, то закрыть его и удалить кеш\n";
                $max->build();
                $max->build("Активные аварии отсутствуют.\n");
                if ($first)
                {
                        $tmp = (time() - $first);
                        if ($tmp > 3600) {
                                $tmp = "" . (int)($tmp / 3600) . "ч " . (int)(($tmp % 3600) / 60) . "м " . (int)($tmp % 60) . "с";
                        } elseif ($tmp > 60) {
                                $tmp = "" . (int)($tmp / 60) . "м " . (int)($tmp % 60) . "с";
                        } else {
                                $tmp = "" . (int)($tmp) . "с";
                        }
                        $tmp .= " (с " . date("Y-m-d H:i", $first) . ")";
                        $max->build("Общее время: {$tmp}\n");
                }
                if ($mv)
                {
                        $max->build("Максимум: {$mv}\n");
                }
                $max->build("\nИнформация обновлена: " . date("Y-m-d H:i:s"));
                $max->messageUpdate($ref, null);
                $max->messageUnpin($rcpt, $ref);
                unlink("/tmp/{$script}_max.tmp");
        }
}

Классы Zabbix и Max не прикладываю, потому что там много зависимостей с другими библиотеками и переписывать их мне лень. Но по названиям методов вполне понятно, что они должны делать и их реализация несложна. Если не заморачиваться универсальностью и обработкой ошибок, то эти классы можно в полсотни строк вместить.

 

При регистрации аварии скрипт добавляет сообщение со сводкой, где перечисляются недоступные хосты, это сообщение закрепляется в чате.

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

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

При массовых авариях (когда становится недоступным одновременно большое число хостов) скрипт буфферизует данные за 2-секундный интервал (чтобы сократить rps).

 

Результат выглядит примерно так:

example.thumb.jpg.18ac3b0e93292f740f1b70da57dbcc87.jpg

Posted
16 hours ago, alibek said:

Классы Zabbix и Max не прикладываю, потому что там много зависимостей с другими библиотеками и переписывать их мне лень.

Не понял, это твои личные наработки, которыми ты не хочешь делиться?

Posted

Личные наработки. Не жалко, но выкладывать как есть не могу — много приватной информации, завязанной на работу фирмы. А рефакторить долго.

Проще заново написать эти классы, реализовав только необходимые методы.

Posted

Для оповещалки — не нужны.

Для второго скрипта, который дополняет оповещения сводкой — нужны, потому что там создаются объекты Zabbix и Max, методы которых далее используются. Но для этого файлы не нужны, их проще прямо в тело скрипта встроить (добавить class Zabbix с методом request и class Max с методами messageSend/messageUpdate/messagePin/messageUnpin).

 

Примерно так (не проверял, могут быть небольшие ошибки):

#!/usr/bin/php
<?php // Дополнительная обработка событий Zabbix
ini_set('display_errors', 1);
error_reporting(E_ALL);

$cli = (boolean)(php_sapi_name()=="cli");
if (!$cli)
{
	$log->prn("Invalid usage! For CLI mode only!");
	exit;
}
$script = basename(__FILE__, '.php');
$rcpt = '-идентификатор-группы-max';

class Zabbix
{
	private $curl;

	public function __construct()
	{
		$this->store = [];
		$this->store['base'] = *zabbix-api*;
		$this->store['counter'] = 0;
		$this->curl = curl_init();
		$this->store['error'] = null;
		$token = $this->auth(*zabbix-user*, *zabbix-password*);
		$this->store['auth'] = $token;
	}

	private function _error($error=null)
	{
		if ($error === false) $error = null;
		if ($error === true)
		{
			if (isset($this->curl)) {
				if (curl_errno($this->curl)) {
					$error = ['code'=>curl_errno($this->curl), 'message'=>curl_error($this->curl)];
				} else {
					$error = null;
				}
			} else {
				$error = ['code'=>-1, 'message'=>'No cURL connection'];
			}
		}
		$this->store['error'] = $error;
	}
	public function error($mode=null)
	{
		return $this->store['error'];
	}

	public function request($method, $params=null, $id=null, $auth=null)
	{
		if (!isset($id)) $id = ++$this->store['counter'];
		if ((func_num_args() < 4) && isset($this->store['auth'])) $auth = $this->store['auth'];
		$req = [];
		$req['jsonrpc'] = "2.0";
		$req['method'] = $method;
		$req['params'] = ($params ?: []);
		$req['id'] = $id;
		if (isset($auth)) $req['auth'] = $auth;
		$this->_error();
		curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($this->curl, CURLOPT_HTTPHEADER, ["Content-Type: application/json-rpc"]);
		$data = json_encode($req, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
		curl_setopt($this->curl, CURLOPT_POST, true);
		if (isset($data)) curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data);
		curl_setopt($this->curl, CURLOPT_URL, $this->store['base']);
		$res = curl_exec($this->curl);
		$ret = curl_getinfo($this->curl);
		if (empty($res) || ($res===false))
		{
			$this->_error(true);
			return false;
		}
		if (is_array($ret)) list($code,$mime) = array($ret['http_code'],$ret['content_type']);
		if ($code >= 500) return "#error $code";
		if (!empty($mime)) $mime = trim(explode(';',$mime)[0]);
		switch ($mime)
		{
			case "application/json":
				$data = json_decode($res, true);
				if (json_last_error() != JSON_ERROR_NONE) $data = false;
				break;
			default:
				$data = $res;
			break;
		}
		return $data;
	}

	private function result($response, &$err=null)
	{
		$err = null;
		if (isset($response) && is_array($response))
		{
			if (isset($response['error'])) {
				$err = $response['error'];
				return null;
			} else {
				return $response['result'];
			}
		}
	}

	public function auth($username, $password)
	{
		$api = 'user.login';
		$res = $this->result($this->request($api, ['user'=>$username, 'password'=>$password]), $err);
		if (isset($err)) $this->_error($err);
		return $res;
	}

}

class Max
{

	protected $store;
	private $curl;
	const API_URL = 'https://platform-api.max.ru/';

	public function __construct()
	{
		$this->store = [];
		$this->store['botname'] = *bot*;
		$this->store['base'] = self::API_URL;
		$this->store['token'] = *token*;
		$this->store['error'] = null;
		$this->curl = curl_init();
	}

	private function _error($error=null)
	{
		if ($error === false) $error = null;
		if ($error === true)
		{
			if (isset($this->curl)) {
				if (curl_errno($this->curl)) {
					$error = ['code'=>curl_errno($this->curl), 'message'=>curl_error($this->curl)];
				} else {
					$error = null;
				}
			} else {
				$error = ['code'=>-1, 'message'=>'No cURL connection'];
			}
		}
		$this->store['error'] = $error;
	}
	public function error($mode=null)
	{
		return $this->store['error'];
	}

	private function http($url, $data=null, $method='POST')
	{
		$this->_error();
		curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($this->curl, CURLOPT_HTTPHEADER, ["Authorization: {$this->store['token']}", "Content-Type: application/json; charset=utf-8"]);
		curl_setopt($this->curl, CURLOPT_POST, true);
		if (isset($data) && is_array($data)) $data = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES );
		if (isset($data)) curl_setopt($this->curl, CURLOPT_POSTFIELDS, $data);
		curl_setopt($this->curl, CURLOPT_URL, $this->store['base'] . $url);
		$res = curl_exec($this->curl);
		$ret = curl_getinfo($this->curl);
		if (empty($res) || ($res===false))
		{
			$this->_error(true);
			return false;
		}
		if (is_array($ret)) list($code,$mime) = array($ret['http_code'],$ret['content_type']);
		if ($code >= 500)
		{
			return "#error $code";
		}
		if (!empty($mime)) $mime = trim(explode(';',$mime)[0]);
		$data = json_decode($res, true);
		if (json_last_error() != JSON_ERROR_NONE) $data = false;
		if ($code >= 400)
		{
			$this->_error($data);
			return "#error $code";
		}
		return $data;
	}

	private function result($response)
	{
		if (isset($response) && is_array($response))
		{
			if (isset($response['success'])) {
				if (!$response['success']) $this->_error(['message'=>$response['message']]);
				return !!$response['success'];
			} else {
				return $response;
			}
		}
	}

	final public function messageSend($rcpt, $body, $format=null, $notify=null, $preview=null)
	{
		$api = 'messages';
		if (is_string($rcpt) && substr($rcpt,0,1)=="-") {
			$api .= "?chat_id={$rcpt}";
		} else {
			$api .= "?user_id={$rcpt}";
		}
		if (isset($preview)) $api .= "&disable_link_preview=".($preview?"false":"true");
		$params = [];
		$params['text'] = $body;
		if (isset($format)) $params['format'] = $format;
		if (isset($notify)) $params['notify'] = $notify;
		$res = $this->result($this->http($api, $params, 'POST'));
		return $res;
	}

	final public function messageUpdate($mid, $body, $format=null, $notify=null)
	{
		$api = 'messages';
		$api .= "?message_id={$mid}";
		$params = [];
		$params['text'] = $body;
		if (isset($format)) $params['format'] = $format;
		if (isset($notify)) $params['notify'] = $notify;
		$res = $this->result($this->http($api, $params, 'PUT'));
		return $res;
	}

	final public function messagePin($chat, $mid, $notify=null)
	{
		$api = "chats/{$chat}/pin";
		$params = ['message_id'=>$mid];
		if (isset($notify)) $params['notify'] = $notify;
		$res = $this->result($this->http($api, $params, 'PUT'));
		return $res;
	}

	final public function messageUnpin($chat, $mid)
	{
		$api = "chats/{$chat}/pin";
		$params = ['message_id'=>$mid];
		if (isset($notify)) $params['notify'] = $notify;
		$res = $this->result($this->http($api, $params, 'DELETE'));
		return $res;
	}

}

print "Подключение к NMS Zabbix\n";
if (!$zb = new Zabbix) {
	print "! Сбой инициализации NMS Zabbix\n";
	exit;
}

if (file_exists("/tmp/{$script}_buffer.tmp") && ((time() - filemtime("/tmp/{$script}_buffer.tmp")) > 60)) unlink("/tmp/{$script}_buffer.tmp");
file_put_contents("/tmp/{$script}_buffer.tmp", getmypid() . "\n", FILE_APPEND);

print "Запрос действующих проблем из Zabbix...\n";
$tmp = $zb->request("problem.get", ["output"=>"extend", "recent"=>false, "severities"=>[4,5], "groupids"=>[19,20,21,22], "sortfield"=>["eventid"], "sortorder"=>"DESC"]);
if (!$tmp)
{
	print "! Ошибка получения списка проблем из Zabbix\n";
	exit;
}
$tmp = $tmp['result'];
print "Запрос списка проблем из Zabbix выполнен, строк: " . count($tmp) . "\n";
$lst = [];
foreach ($tmp as $rec) $lst[$rec['eventid']] = ['id'=>$rec['eventid'], 'clock'=>$rec['clock'], 'problem'=>$rec['name'], 'level'=>$rec['severity']];

print "Запрос деталей проблем из Zabbix...\n";
$tmp = $zb->request("event.get", ["output"=>"extend", "eventids"=>array_keys($lst), "selectHosts"=>"extend"]);
if (!$tmp)
{
	print "! Ошибка получения деталей проблем из Zabbix\n";
	exit;
}
$tmp = $tmp['result'];
foreach ($tmp as $rec)
{
	if (!isset($lst[$rec['eventid']])) continue;
	if (!$rec['value']) continue;
	if (isset($rec['hosts'], $rec['hosts'][0]))
	{
		if ($rec['hosts'][0]['status']) continue;
		$lst[$rec['eventid']]['host'] = $rec['hosts'][0]['host'];
		$lst[$rec['eventid']]['title'] = $rec['hosts'][0]['name'];
		$lst[$rec['eventid']]['date'] = date("Y-m-d H:i", $rec['clock']);
		$lst[$rec['eventid']]['time'] = time() -  $rec['clock'];
	}
}
$lst = array_filter($lst, function($rec) { return isset($rec['host']);});
$ids = array_keys($lst);
sort($ids);
$ids = implode(",", $ids);

$ref = null; $old = ""; $first = null; $mv = null;
if (file_exists("/tmp/{$script}_max.tmp")) {
	$tmp = file_get_contents("/tmp/{$script}_max.tmp");
	if (($tmp !== false) && $tmp)
	{
		$ref = trim(strtok($tmp, ":"));
		$old = trim(strtok(":"));
		$tmp = trim(strtok(":"));
		if ($tmp) $first = (int)$tmp;
		$tmp = trim(strtok(":"));
		if ($tmp) $mv = (int)$tmp;
	}
} else {
	$first = time();
	$mv = 0;
}

print "Буфферизация отправки команд...\n";
sleep(2);
if (!file_exists("/tmp/{$script}_buffer.tmp")) exit;
$tmp = file_get_contents("/tmp/{$script}_buffer.tmp");
if (!$tmp) exit;
$tmp = explode("\n", trim($tmp));
$tmp = end($tmp);
if ($tmp != getmypid()) exit;
unlink("/tmp/{$script}_buffer.tmp");

$max = new Max;

if ($ids) {
	print "# Есть аварии\n";
	if ($ids == $old) {
		print "# Состав аварий не изменился, ничего не делать\n";
		null;
	} else {
		print "# Состав аварий изменился\n";
		$cnt = count($lst);
		if (isset($mv) && ($cnt > $mv)) $mv = $cnt;
		$msg = "";
		$msg .= "Список действующих аварий ({$cnt}):\n";
		foreach ($lst as $rec)
		{
			if ($cnt > 100) {
				$msg .= "- {$rec['host']}\n";
			} elseif ($cnt > 60) {
				$msg .= "- {$rec['title']}\n";
			} else {
				$msg .= "- {$rec['title']} (с " . date("Y-m-d H:i", $rec['clock']) . ")\n";
			}
		}
		$msg .= "\nИнформация обновлена: " . date("Y-m-d H:i:s");
		if ($ref) {
			print "# Если есть закрепленное сообщение, то обновить его\n";
			$max->messageUpdate($ref, $msg);
			file_put_contents("/tmp/{$script}_max.tmp", "{$ref}: {$ids}" . ($first ? ": {$first}" . ($mv ? ": {$mv}" : "") : ""));
		} else {
			print "# Если закрепленного сообщения нет, то создать его и сохранить кеш\n";
			if ($tmp = $max->messageSend($rcpt, $msg)) {
				$ref = $tmp['message']['body']['mid'];
				$max->messagePin($rcpt, $ref);
				file_put_contents("/tmp/{$script}_max.tmp", "{$ref}: {$ids}" . ($first ? ": {$first}" . ($mv ? ": {$mv}" : "") : ""));
			}
		}
	}
} else {
	print "# Нет аварий\n";
	if ($ref) {
		print "# Если есть закрепленное сообщение, то закрыть его и удалить кеш\n";
		$msg = "";
		$msg .= "Активные аварии отсутствуют.\n";
		if ($first)
		{
			$tmp = (time() - $first);
			if ($tmp > 3600) {
				$tmp = "" . (int)($tmp / 3600) . "ч " . (int)(($tmp % 3600) / 60) . "м " . (int)($tmp % 60) . "с";
			} elseif ($tmp > 60) {
				$tmp = "" . (int)($tmp / 60) . "м " . (int)($tmp % 60) . "с";
			} else {
				$tmp = "" . (int)($tmp) . "с";
			}
			$tmp .= " (с " . date("Y-m-d H:i", $first) . ")";
			$msg .= "Общее время: {$tmp}\n";
		}
		if ($mv)
		{
			$msg .= "Максимум: {$mv}\n";
		}
		$msg .= "\nИнформация обновлена: " . date("Y-m-d H:i:s");
		$max->messageUpdate($ref, $msg);
		$max->messageUnpin($rcpt, $ref);
		unlink("/tmp/{$script}_max.tmp");
	}
}

 

  • 1 month later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...
На сайте используются файлы cookie и сервисы аналитики для корректной работы форума и улучшения качества обслуживания. Продолжая использовать сайт, вы соглашаетесь с использованием файлов cookie и с Политикой конфиденциальности.