Monitoring met Zabbix, notificatie via Telegram (en gebruik emoticons)

Ingediend door Dirk Hornstra op 14-feb-2023 22:17

Zabbix is een geweldig pakket. Je kunt je eigen (en andere) websites monitoren en bij problemen (of als je wilt weten of er iets verandert) een trigger laten zorgen dat je een notificatie krijgt. Overdag zie je die meldingen in de verschillende Slack-kanalen binnen komen en kun je actie ondernemen. Maar goed, dan is het 17.00 uur, je klapt je laptop dicht en "je ziet niets meer". Als er dan iets gebeurt waar je een notificatie van wilt krijgen, dan heb je wel wat opties. Je kunt namelijk acties uitvoeren, een eigen script of eigen acties (zoals een webhook). En je zou ook zelf een website jouw Zabbix kunnen laten uitlezen die de problemen toont. En je kunt ook "gewoon" een mailtje naar jezelf sturen. Die laatste optie, als je op je mobiel de mail ingesteld hebt krijg je die mail wel binnen en ook een notificatie. Maar dan heb je zo'n mail die blijft slingeren, het "pingt" tussen de andere mails door, dus dat is nog steeds niet optimaal.

Ik heb Whatsapp, maar dat is de gewone "gratis" consumentenvariant, om hiervoor een duurdere zakelijke versie te gaan kopen, dat was niet mijn insteek. Dus ik ben eerst eens gaan kijken naar Signal. Want dat pakket kun je volgens mij ook zelf hosten. Helaas, de dev-documentatie, daar zag ik zaken over encoding (als ik mij niet vergis) en ik kon niet echt snel iets vinden om notificaties te versturen, dat viel af. Vervolgens naar Telegram. Had ik in het verleden ooit al geïnstalleerd, maar nog nooit wat mee gedaan. Nou, dat was de jackpot! Kwestie van een private channel aanmaken, instellen dat berichten na 24 uur automatisch verwijderd worden, via @BotFather een botje aanmaken, het botje toevoegen aan dat private channel en vervolgens inrichten. Hier en hier te vinden op de site van Telegram en ook deze externe partij heeft het in een duidelijke actielijst staan: link.


-- start een chat met @BotFather

/start

/newbot

"naam zoals die getoond wordt"

"naam-van-de-bot_bot"

@BotFather: gefeliciteerd, succesvol aangemaakt. Directe link + informatie + token voor HTTP API -- die heb je dus nodig.

-- voeg de bot toe aan jouw private kanaal
-- ga in je browser naar https://api.telegram.org/bot[API token van @BotFather]/getUpdates
-- plaats een berichtje in het kanaal
-- herlaad je browser. dit bericht moet daar nu in staan en ook een channel-id. Die heb je straks nodig.

Het meeste werk zat toen vervolgens in het "goed krijgen" van het bericht wat de bot naar het kanaal verstuurt. Ik zag dat via type "HTML" verzonden kan worden. Ik begon al met een <div>-tag om zaken op te maken, maar dat was iets te voorbarig, er worden een paar tags ondersteund, zoals <b>..</b>, maar niet zulke "layout-tags". Als je een line-break wilt doen plaats je dus een \r\n in je tekst en geen <br/>.

Maar goed, waar moet het bericht mee beginnen? Met een emoticon. Je wilt namelijk zien of er een probleem is (de :rotating_light:) of dat het probleem opgelost is (de :white_check_mark:). Ik ben dus begonnen met de pagina's waar de emoticons op staan: hier en hier.

En als je via C# onderstaande code uitvoert, dan is het direct goed. Dat komt omdat in C# met die \U[code] het karakter direct al juist .



try
{
    var emojiCode = "\U0001F6A8";
    var recoveryEmoji = "\U00002705";
    var postContent = new StringContent("{\"chat_id\": [id-chatkanaal],\"text\": \"" + recoveryEmoji + "<b>No problem here!</b>\",\"parse_mode\": \"HTML\"}", Encoding.UTF8);
    postContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
    var uri = @"https://api.telegram.org/bot[API token van @BotFather]/sendMessage";
    using (var client = new HttpClient())
    {
        var result = await client.PostAsync(uri, postContent);
        var line = await result.Content.ReadAsStringAsync();
        Console.WriteLine(line);
    }
}
catch (Exception x)
{
    Console.WriteLine(x.Message);
}
Console.ReadKey();

In Zabbix is het een ander verhaal. Het script waarmee je de tekst post naar de API van Telegram, dat is JavaScript. En daar moet je trucjes mee doen. Ik had daar ook al die \u[code] geprobeerd, op bepaalde sites werd gezegd dat je de code expliciet tussen dubbele quotes moest plaatsen, dus var message = '"\u[code]" website down';, maar ook dat werkt niet (blijkt voor PHP te zijn, niet voor Javascript). Uiteindelijk is het (natuurlijk) StackOverflow die met het juiste antwoord kwam: link. Ik heb WSL draaien op mijn Windows machine, dus daar in bash een apt-get install jq en vervolgens de stappen uitgevoerd:

s=$'\360\237\224\224'
jq -anM --arg s "$s" '$s'

Toen ben ik nog even handmatig met zaken bezig geweest. Het voorbeeld is van een bel, maar ik wilde dus dat zwaailicht en het vinkje. Je ziet in de kolom Bytes (UTF-8) dat dit \xF0\x9F\x9A\xA8 en \xE2\x9C\x85 is.

Je start op Windows je rekenmachine (run: calc), je zet m op "Programmeur", je klikt "HEX" aan en dan voer je stuk voor stuk je hexadecimale codes in en noteer je de octale weergave (OCT). Die gebruik je dan in dat bovenstaande script en zo krijg je het juiste resultaat.

 

Afronden.

En hoe is nu de code geworden?
Hier de uitleg, misschien kun jij er ook nog wat mee. Ik heb de boel redelijk "uitgekleed", dus je kunt veel meer parameters toevoegen, zodat je nog meer informatie kunt delen.

Zabbix:
- Administration
- Media types
Name: Telegram, Type: Webhook

Parameters:
alert_message: {ALERT.MESSAGE}
alert_subject: {ALERT.SUBJECT}
bot_token: [token wat @BotFather je gegeven heeft]
channel_id: ID van het private kanaal waarheen gepost moet worden
event_date:       {EVENT.DATE}
event_opdata:  {EVENT.OPDATA}
event_source:   {EVENT.SOURCE}
event_severity: {EVENT.SEVERITY}
event_tags:        {EVENT.TAGS}
event_time:       {EVENT.TIME}
event_value:      {EVENT.VALUE}
event_update_status:   {EVENT.UPDATE.STATUS}
host_name:       {HOST.HOST}
trigger_description:        {TRIGGER.DESCRIPTION}

Script:


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 isEventResolve(params) {
    return params.event_value == 0;
}
try {
    var params = JSON.parse(value);
    var fields = {
        chat_id: params.channel_id,
        parse_mode: 'HTML',
        text : ''
    };
    if (isEventProblem(params))
    {
        fields.text = '\ud83d\udea8';
    }
    else if (isEventResolve(params)) {
        fields.text = '\u2705';
    }
    fields.text += '\r\n<b>'+params.alert_subject+'</b>\r\n';
    fields.text += 'Host: <b>'+params.host_name+'</b>\r\n';
    fields.text += 'Event time: <b>'+params.event_date+' '+params.event_time+'</b>\r\n';
    var req = new CurlHttpRequest(),
    result = {tags: {}};
    if (typeof params.HTTPProxy === 'string' && params.HTTPProxy.trim() !== '') {
        req.SetProxy(params.HTTPProxy);
    }
    req.AddHeader('Content-Type: application/json; charset=utf-8');
    var telegram_endpoint = 'https://api.telegram.org/bot'+params.bot_token+'/sendMessage';
    var resp = JSON.parse(req.Post(telegram_endpoint, JSON.stringify(fields)));
    if (req.Status() != 200 || !resp.ok || resp.ok === 'false') {
        throw resp.error;
    }
}
catch (error) {
    Zabbix.Log(4, '[ Telegram Webhook ] Telegram notification failed : ' + error);
    throw 'Telegram notification failed : ' + error;
}


Timeout: 30s
Process tags: vinkje
Invluce event menu entry: vinkje
Menu entry name: Open in Telegram https://api.telegram.org/{EVENT.TAGS.__channel_id}/getUpdates
Menu entry URL: {EVENT.TAGS.__message_link}