Hi there guys! We are using Zabbix for our monitoring and we want to have an automated message to our MatterMost instance. We can't get this to work: it constantly gives an Mattermost notification failed: SyntaxError: invalid json (at offset 1).
I've been searchin'everywhere, including here, to find a solution. I'm using the MatterMost integration shipped within Zabbix. I can see on my MatterMost server that the identification goes correctly (to say: if I'm using a wrong bot_token it gives a warning in my MatterMost log, if I'm using the correct bot_token it doesn't say anything).
This is the configuration shipped within Zabbix. Anyone? Cause I've been trying to debug this for hours and hours and it's driving me crazy. I do have a working connection with Slack, so I can connect Zabbix to Slack and use Matterbridge to connect Slack to Mattermost, but that seems a bit... bloated.
I've been searchin'everywhere, including here, to find a solution. I'm using the MatterMost integration shipped within Zabbix. I can see on my MatterMost server that the identification goes correctly (to say: if I'm using a wrong bot_token it gives a warning in my MatterMost log, if I'm using the correct bot_token it doesn't say anything).
This is the configuration shipped within Zabbix. Anyone? Cause I've been trying to debug this for hours and hours and it's driving me crazy. I do have a working connection with Slack, so I can connect Zabbix to Slack and use Matterbridge to connect Slack to Mattermost, but that seems a bit... bloated.
Code:
var SEVERITY_COLORS = [
'#97AAB3', '#7499FF', '#FFC859',
'#FFA059', '#E97659', '#E45959'
];
var RESOLVE_COLOR = '#009900';
var SEND_MODE_HANDLERS = {
alarm: handlerAlarm,
event: handlerEvent
};
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(\d+)}/g, function(match, number) {
return number in args
? args[number]
: match
;
});
};
}
function isEventProblem(params) {
return params.event_value == 1
&& params.event_update_status == 0
;
}
function isEventUpdate(params) {
return params.event_value == 1
&& params.event_update_status == 1
;
}
function isEventResolve(params) {
return params.event_value == 0;
}
function getPermalink(mattermost_url, team_name, postid) {
return '{0}/{1}/pl/{2}'.format(
mattermost_url.replace(/\/+$/, ''),
team_name,
postid
);
}
function getChannel(send_to) {
switch (true) {
case /.+\/#.+/.test(send_to):
return getChannelByName(send_to);
case /@.+/.test(send_to):
return getDirectChannel(send_to);
default:
return getChannelByID(send_to);
}
}
function getChannelByName(send_to) {
var team_chan = send_to
.trim()
.split('/#');
var resp = JSON.parse(req.Get(
Mattermost.channel_byname.format(team_chan[0], team_chan[1]),
JSON.stringify(fields)
)
);
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
return resp;
}
function getDirectChannel(send_to) {
Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
arguments.callee.name,
JSON.stringify(arguments)
));
var teamUser = send_to
.trim()
.split('/@'),
bot = getBotUser(),
user = getUserByName(teamUser[1]);
var resp = JSON.parse(req.Post(
Mattermost.direct_channel,
JSON.stringify([bot.id, user.id])
)
);
Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
arguments.callee.name,
JSON.stringify(resp)
));
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
resp.team_name = teamUser[0];
return resp;
}
function getChannelByID(channelID) {
Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
arguments.callee.name,
JSON.stringify(arguments)
));
var resp = JSON.parse(req.Get(
Mattermost.get_channel.format(channelID),
JSON.stringify(fields)
)
);
Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
arguments.callee.name,
JSON.stringify(resp)
));
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
return resp;
}
function getBotUser() {
Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
arguments.callee.name,
JSON.stringify(arguments)
));
var resp = JSON.parse(req.Get(
Mattermost.bot_user,
JSON.stringify(fields)
)
);
Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
arguments.callee.name,
JSON.stringify(resp)
));
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
return resp;
}
function getUserByName(userName) {
Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
arguments.callee.name,
JSON.stringify(arguments)
));
var resp = JSON.parse(req.Get(
Mattermost.user_byname.format(userName),
JSON.stringify(fields)
)
);
Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
arguments.callee.name,
JSON.stringify(resp)
));
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
return resp;
}
function getTeamByID(teamID) {
Zabbix.Log(5, '[ Mattermost Webhook ] Call {0}({1})'.format(
arguments.callee.name,
JSON.stringify(arguments)
));
var resp = JSON.parse(req.Get(
Mattermost.get_team.format(teamID),
JSON.stringify(fields)
)
);
Zabbix.Log(5, '[ Mattermost Webhook ] Result {0}: {1}'.format(
arguments.callee.name,
JSON.stringify(resp)
));
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
return resp;
}
function createProblemURL(zabbix_url, triggerid, eventid, event_source) {
var problem_url = '';
if (event_source === '0') {
problem_url = '{0}/tr_events.php?triggerid={1}&eventid={2}'
.format(
zabbix_url,
triggerid,
eventid
);
}
else {
problem_url = zabbix_url;
}
return problem_url;
}
function getTagValue(event_tags, key) {
var pattern = new RegExp('(' + key + ':.+)');
var tagValue = event_tags
.split(',')
.filter(function (v) {
return v.match(pattern);
})
.map(function (v) {
return v.split(':')[1];
})[0]
|| 0;
return tagValue;
}
function handlerAlarm(req, params) {
var channel = getChannel(params.send_to);
var fields = {
channel_id: channel.id,
props: {}
};
if (isEventProblem(params)) {
var team_name = channel.team_name
? channel.team_name
: getTeamByID(channel.team_id).name;
fields.props.attachments = [
createMessage(
SEVERITY_COLORS[params.event_nseverity] || 0,
params.event_date,
params.event_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
)
];
var resp = JSON.parse(req.Post(
Mattermost.post_message,
JSON.stringify(fields)
)
);
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
result.tags.__mattermost_post_id = resp.id;
result.tags.__mattermost_channel_id = channel.id;
result.tags.__mattermost_channel_name = channel.name;
result.tags.__mattermost_message_link = getPermalink(
params.mattermost_url,
team_name,
resp.id
);
}
else if (isEventUpdate(params)) {
fields.root_id = getTagValue(params.event_tags, 'mattermost_post_id');
if (params.event_source === '0') {}
fields.props.attachments = [
createMessage(
SEVERITY_COLORS[params.event_nseverity] || 0,
params.event_update_date,
params.event_update_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source),
true
)
];
resp = JSON.parse(req.Post(
Mattermost.post_message, JSON.stringify(fields)
)
);
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
}
else if (isEventResolve(params)) {
fields.channel_id = getTagValue(params.event_tags, 'mattermost_channel_id');
fields.id = getTagValue(params.event_tags, 'mattermost_post_id');
fields.props.attachments = [
createMessage(
RESOLVE_COLOR,
params.event_date,
params.event_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
)
];
var post_id = getTagValue(params.event_tags, 'mattermost_post_id');
resp = JSON.parse(req.Put(
Mattermost.chat_update.format(post_id),
JSON.stringify(fields)
)
);
if (req.Status() != 200) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
}
}
function handlerEvent(req, params) {
var channel = getChannel(params.send_to);
var fields = {
channel_id: channel.id,
props: {}
};
if (isEventProblem(params)) {
var team_name = channel.team_name
? channel.team_name
: getTeamByID(channel.team_id).name;
fields.props.attachments = [
createMessage(
SEVERITY_COLORS[params.event_nseverity] || 0,
params.event_date,
params.event_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
)
];
var resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
result.tags.__mattermost_channel_name = channel.name;
result.tags.__mattermost_message_link = getPermalink(
params.mattermost_url,
team_name,
resp.id
);
}
else if (isEventUpdate(params)) {
fields.props.attachments = [
createMessage(
SEVERITY_COLORS[params.event_nseverity] || 0,
params.event_update_date,
params.event_update_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source),
false
)
];
resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
}
else if (isEventResolve(params)) {
fields.props.attachments = [
createMessage(
RESOLVE_COLOR,
params.event_recovery_date,
params.event_recovery_time,
createProblemURL(params.zabbix_url, params.trigger_id, params.event_id, params.event_source)
)
];
resp = JSON.parse(req.Post(Mattermost.post_message, JSON.stringify(fields)));
if (req.Status() != 201) {
throw '[{0}] {1}'.format(resp.status_code, resp.message);
}
}
}
function createMessage(
event_severity_color,
event_date,
event_time,
problem_url,
isShort
) {
var message = {
fallbac: params.alert_subject,
title: params.alert_subject,
color: event_severity_color,
title_link: problem_url,
footer: problem_url,
fields: [
{
title: 'Host',
value: '{0} [{1}]'.format(params.host_name, params.host_ip),
short: true
},
{
title: 'Event time',
value: '{0} {1}'.format(event_date, event_time),
short: true
}
],
};
if (params.event_source === '0') {
message.fields.push(
{
title: 'Severity',
value: params.event_severity,
short: true
},
{
title: 'Opdata',
value: params.event_opdata,
short: true
}
);
}
if (!isShort && params.event_source === '0') {
message.fields.push(
{
title: 'Event tags',
value: '`{0}`'.format(params.event_tags.replace(/__.+?:(.+?,|.+)/g, '') || 'None'),
short: true
},
{
title: 'Trigger description',
value: params.trigger_description,
short: true
}
);
}
if (params.event_source !== '0' || params.event_update_status === '1') {
message.fields.push(
{
title: 'Details',
value: params.alert_message,
short: false
}
);
}
return message;
}
function validateParams(params) {
if (typeof params.bot_token !== 'string' || params.bot_token.trim() === '') {
throw 'Field "bot_token" cannot be empty';
}
if (isNaN(params.event_id)) {
throw 'Field "event_id" is not a number';
}
if ([0, 1, 2, 3].indexOf(parseInt(params.event_source)) === -1) {
throw 'Incorrect "event_source" parameter given: "' + params.event_source + '".\nMust be 0-3.';
}
if (params.event_source !== '0') {
params.event_nseverity = '0';
params.event_severity = 'Not classified';
params.event_update_status = '0';
params.send_mode = 'event';
}
if (params.event_source === '1' || params.event_source === '2') {
params.event_value = '1';
}
if (params.event_source === '1') {
params.host_name = params.discovery_host_dns;
params.host_ip = params.discovery_host_ip;
}
if ([0, 1, 2, 3, 4, 5].indexOf(parseInt(params.event_nseverity)) === -1) {
throw 'Incorrect "event_nseverity" parameter given: ' + params.event_nseverity + '\nMust be 0-5.';
}
if (typeof params.event_severity !== 'string' || params.event_severity.trim() === '') {
throw 'Field "event_severity" cannot be empty';
}
if (params.event_update_status !== '0' && params.event_update_status !== '1') {
throw 'Incorrect "event_update_status" parameter given: ' + params.event_update_status + '\nMust be 0 or 1.';
}
if (params.event_value !== '0' && params.event_value !== '1') {
throw 'Incorrect "event_value" parameter given: ' + params.event_value + '\nMust be 0 or 1.';
}
if (typeof params.host_ip !== 'string' || params.host_ip.trim() === '') {
throw 'Field "host_ip" cannot be empty';
}
if (typeof params.host_name !== 'string' || params.host_name.trim() === '') {
throw 'Field "host_name" cannot be empty';
}
if (typeof params.mattermost_url !== 'string' || params.mattermost_url.trim() === '') {
throw 'Field "mattermost_url" cannot be empty';
}
if (!/^(http|https):\/\/.+/.test(params.mattermost_url)) {
throw 'Field "mattermost_url" must contain a schema';
}
if (['alarm', 'event'].indexOf(params.send_mode) === -1) {
throw 'Incorrect "send_mode" parameter given: ' + params.send_mode + '\nMust be "alarm" or "event".';
}
if (typeof params.send_to !== 'string' || params.send_to.trim() === '') {
throw 'Field "send_to" cannot be empty';
}
if (isNaN(params.trigger_id) && params.event_source === '0') {
throw 'field "trigger_id" is not a number';
}
if (typeof params.zabbix_url !== 'string' || params.zabbix_url.trim() === '') {
throw 'Field "zabbix_url" cannot be empty';
}
if (!/^(http|https):\/\/.+/.test(params.zabbix_url)) {
throw 'Field "zabbix_url" must contain a schema';
}
}
try {
var params = JSON.parse(value);
validateParams(params);
var req = new CurlHttpRequest(),
fields = {},
result = {tags: {}};
if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') {
req.SetProxy(params.HTTPProxy);
}
req.AddHeader('Content-Type: application/json; charset=utf-8');
req.AddHeader('Authorization: Bearer ' + params.bot_token);
params.mattermost_url = params.mattermost_url.replace(/\/+$/, '');
params.zabbix_url = params.zabbix_url.replace(/\/+$/, '');
var APIEndpoint = params.mattermost_url + '/api/v4/';
var Mattermost = {
post_message: APIEndpoint + 'posts',
get_channel: APIEndpoint + 'channels/{0}',
get_team: APIEndpoint + 'teams/{0}',
chat_update: APIEndpoint + 'posts/{0}',
direct_channel: APIEndpoint + 'channels/direct',
channel_byname: APIEndpoint + 'teams/name/{0}/channels/name/{1}',
user_byname: APIEndpoint + 'users/username/{0}',
bot_user: APIEndpoint + 'users/me'
};
params.send_mode = params.send_mode.toLowerCase();
params.send_mode = params.send_mode in SEND_MODE_HANDLERS
? params.send_mode
: 'alarm';
SEND_MODE_HANDLERS[params.send_mode](req, params);
if (params.event_source === '0') {
return JSON.stringify(result);
}
else {
return 'OK';
}
}
catch (error) {
Zabbix.Log(4, '[ Mattermost Webhook ] Mattermost notification failed: ' + error);
throw 'Mattermost notification failed: ' + error;
}
Comment