Skip to content

Instantly share code, notes, and snippets.

@n00rm
Last active September 13, 2024 12:45
Show Gist options
  • Save n00rm/32f1334b1dd2efc40122fee36551ef17 to your computer and use it in GitHub Desktop.
Save n00rm/32f1334b1dd2efc40122fee36551ef17 to your computer and use it in GitHub Desktop.
Discord Checkmk Notification Script
#!/usr/bin/env python3
# Notify via Discord
import os
import requests
import sys
USERNAME = "check_mk"
AVATARURL = "https://checkmk.com/favicon-96x96.png"
COLORS = {
"CRITICAL": "15597568",
"DOWN": "15597568",
"WARNING": "16768256",
"OK": "52224",
"UP": "52224",
"UNKNOWN": "13421772",
"UNREACHABLE": "13421772",
}
def discord_msg(context):
"""Build the message for discord"""
facts = []
if context.get('WHAT', None) == "SERVICE":
state = context["SERVICESTATE"]
color = COLORS.get(state)
subtitle = "Service Notification"
facts.append({"name": "Service:", "value": context["SERVICEDESC"]})
output = context["SERVICEOUTPUT"] if context["SERVICEOUTPUT"] else ""
else:
state = context["HOSTSTATE"]
color = COLORS.get(state)
subtitle = "Host Notification"
output = context["HOSTOUTPUT"] if context["HOSTOUTPUT"] else ""
facts.extend([
{
"name": "Host:",
"value": context["HOSTNAME"]
},
{
"name": "State:",
"value": state
}
])
return {
"username": USERNAME,
"avatar_url": AVATARURL,
"embeds": [
{
"title": subtitle,
"color": color,
"fields": facts,
"footer": {
"text": output
}
}
]
}
def collect_context():
return {
var[7:]: value
for (var, value) in os.environ.items()
if var.startswith("NOTIFY_")
}
def post_request(message_constructor, success_code=204):
context = collect_context()
url = context.get("PARAMETERS")
r = requests.post(url=url, json=message_constructor(context))
if r.status_code == success_code:
sys.exit(0)
else:
sys.stderr.write(
"Failed to send notification. Status: %i, Response: %s\n" % (r.status_code, r.text))
sys.exit(2)
if __name__ == "__main__":
post_request(discord_msg)
@Krankenkasse
Copy link

Perfekt, dankeschön!

@Hornochs
Copy link

Heyho :)
Danke dir erstmal für dein Skript! Bringt mich weiter :) Jedoch hab ich gemerkt, dass das System die selbe Notification zwei mal abschickt. Hast du eine Idee was es sein könnte?

LG
Horstie

@n00rm
Copy link
Author

n00rm commented Jun 15, 2021

Hi,
der Skript darf nur mit einem Benutzer aufgerufen werden. Bitte benutze den automation user dafür. Das sollte dein Problem lösen. 😉

@Hornochs
Copy link

Hehe, wie bereits im CheckMK Forum geschrieben, hast vollkommen recht 😄 Hier war ein Noob mal wieder am Werk

@Hornochs
Copy link

Hey there,

Sure it does. You must setup on CheckMK when he should notify snd over which way :)

@livi6191
Copy link

livi6191 commented Jan 24, 2022

Hey Guys,

Edit, managed to get it working but getting the following error now:

cat notify.log
2022-01-24 02:00:59,353 [20] [cmk.base.notify] ----------------------------------------------------------------------
2022-01-24 02:00:59,353 [20] [cmk.base.notify] Got raw notification (UNKNOWN) context with 0 variables
2022-01-24 02:00:59,353 [20] [cmk.base.events] Error on completing raw context: 'HOSTNAME'
2022-01-24 02:00:59,354 [40] [cmk.base.notify] ERROR:
Traceback (most recent call last):
File "/omd/sites/ad/lib/python3/cmk/base/notify.py", line 355, in locally_deliver_raw_context
return notify_rulebased(raw_context, analyse=analyse)
File "/omd/sites/ad/lib/python3/cmk/base/notify.py", line 474, in notify_rulebased
why_not = rbn_match_rule(rule, raw_context)
File "/omd/sites/ad/lib/python3/cmk/base/notify.py", line 804, in rbn_match_rule
return events.apply_matchers([
File "/omd/sites/ad/lib/python3/cmk/base/events.py", line 414, in apply_matchers
result = matcher(rule, context)
File "/omd/sites/ad/lib/python3/cmk/base/events.py", line 421, in event_match_rule
return apply_matchers([
File "/omd/sites/ad/lib/python3/cmk/base/events.py", line 414, in apply_matchers
result = matcher(rule, context)
File "/omd/sites/ad/lib/python3/cmk/base/events.py", line 675, in event_match_exclude_hosts
if context["HOSTNAME"] in rule.get("match_exclude_hosts", []):
KeyError: 'HOSTNAME'

Thanks

@n00rm
Copy link
Author

n00rm commented Jan 24, 2022

Hi,

I answered your forum post to you problem.
Have a look and let me know if it helps you -> https://forum.checkmk.com/t/check-mk-discord-notification/29311/4?u=norm

Kind Regards
Norm

@qhgh
Copy link

qhgh commented May 9, 2023

Hi Norm,

Is there any way to have this script use the webhook with the 'role mention role id' so it can @ tag people?

I assume maybe somewhere here?

def post_request(message_constructor, success_code=204):
    context = collect_context()

    url = context.get("PARAMETERS")
    r = requests.post(url=url, json=message_constructor(context))

Thanks,
C

@n00rm
Copy link
Author

n00rm commented May 12, 2023

Hi @qhgh

yes it should be pretty easy to add that function.
You can use the Enviroment Variables to get that info to the script.
https://docs.checkmk.com/latest/en/notifications.html#_environment_variables

You could use the Pager ID as a mention Role ID or create multiple notification rule with the mention id as a parameter and use the NOTIFY_PARAMETER_2 variable.


#!/usr/bin/env python3
# Notify via Discord

import os
import requests
import sys

USERNAME = "check_mk"
AVATARURL = "https://checkmk.com/favicon-96x96.png"

COLORS = {
    "CRITICAL": "15597568",
    "DOWN": "15597568",
    "WARNING": "16768256",
    "OK": "52224",
    "UP": "52224",
    "UNKNOWN": "13421772",
    "UNREACHABLE": "13421772",
}

# Add the role mention ID here
# you could also use Variables from here https://docs.checkmk.com/latest/en/notifications.html#_environment_variables
ROLE_MENTION_ID = "your_role_id"

def discord_msg(context):
    """Build the message for discord"""
    facts = []
    if context.get('WHAT', None) == "SERVICE":
        state = context["SERVICESTATE"]
        color = COLORS.get(state)
        subtitle = "Service Notification"
        facts.append({"name": "Service:", "value": context["SERVICEDESC"]})
        output = context["SERVICEOUTPUT"] if context["SERVICEOUTPUT"] else ""
    else:
        state = context["HOSTSTATE"]
        color = COLORS.get(state)
        subtitle = "Host Notification"
        output = context["HOSTOUTPUT"] if context["HOSTOUTPUT"] else ""

    facts.extend([
        {
            "name": "Host:",
            "value": context["HOSTNAME"]
        },
        {
            "name": "State:",
            "value": state
        }
    ])

    mention = f"<@&{ROLE_MENTION_ID}>"
    
    return {
        "username": USERNAME,
        "avatar_url": AVATARURL,
        "content": mention,  # Add the role mention here
        "embeds": [
        {
            "title": subtitle,
            "color": color,
            "fields": facts,
            "footer": {
               "text": output
           }
        }
        ]
    }

# Rest of the code...


Hope this helps.

@qhgh
Copy link

qhgh commented May 12, 2023

Thanks very much n00m!

I gave it a try but it errors:

2023-05-12 14:19:11   * notifying discord via xnewdiscord.py, parameters: https://discord.com/api/webhooks/example, bulk: no
2023-05-12 14:19:11      executing /omd/sites/dc1/local/share/check_mk/notifications/xnewdiscord.py
2023-05-12 14:19:11      Output:   File "/omd/sites/dc1/local/share/check_mk/notifications/xnewdiscord.py", line 92
2023-05-12 14:19:11      Output:     post_request(discord_msg).
2023-05-12 14:19:11      Output:                              ^
2023-05-12 14:19:11      Output: SyntaxError: invalid syntax
2023-05-12 14:19:11      Plugin exited with code 1

I have the file like so:

#!/usr/bin/env python3
# n00m Notify via Discord

import os
import requests
import sys

USERNAME = "check_mk"
AVATARURL = "https://checkmk.com/favicon-96x96.png"

COLORS = {
    "CRITICAL": "15597568",
    "DOWN": "15597568",
    "WARNING": "16768256",
    "OK": "52224",
    "UP": "52224",
    "UNKNOWN": "13421772",
    "UNREACHABLE": "13421772",
}

# Add the role mention ID here
# you could also use Variables from here https://docs.checkmk.com/latest/en/notifications.html#_environment_variables
ROLE_MENTION_ID = "123example123"

def discord_msg(context):
    """Build the message for discord"""
    facts = []
    if context.get('WHAT', None) == "SERVICE":
        state = context["SERVICESTATE"]
        color = COLORS.get(state)
        subtitle = "Service Notification"
        facts.append({"name": "Service:", "value": context["SERVICEDESC"]})
        output = context["SERVICEOUTPUT"] if context["SERVICEOUTPUT"] else ""
    else:
        state = context["HOSTSTATE"]
        color = COLORS.get(state)
        subtitle = "Host Notification"
        output = context["HOSTOUTPUT"] if context["HOSTOUTPUT"] else ""

    facts.extend([
        {
            "name": "Host:",
            "value": context["HOSTNAME"]
        },
        {
            "name": "State:",
            "value": state
        }
    ])

    mention = f"<@&{ROLE_MENTION_ID}>"

    return {
        "username": USERNAME,
        "avatar_url": AVATARURL,
        "content": mention,  # Add the role mention here
        "embeds": [
        {
            "title": subtitle,
            "color": color,
            "fields": facts,
            "footer": {
               "text": output
           }
        }
        ]
    }

# Rest of the code..

def collect_context():
    return {
        var[7:]: value
        for (var, value) in os.environ.items()
        if var.startswith("NOTIFY_")
    }

def post_request(message_constructor, success_code=204):
    context = collect_context()

    url = context.get("PARAMETERS")
    r = requests.post(url=url, json=message_constructor(context))

    if r.status_code == success_code:
        sys.exit(0)
    else:
        sys.stderr.write(
            "Failed to send notification. Status: %i, Response: %s\n" % (r.status_code, r.text))
        sys.exit(2)

if __name__ == "__main__":
    post_request(discord_msg).

Did I get that right?
Thanks!
C

@muzza-sys
Copy link

Thanks very much n00m!

I gave it a try but it errors:

2023-05-12 14:19:11   * notifying discord via xnewdiscord.py, parameters: https://discord.com/api/webhooks/example, bulk: no
2023-05-12 14:19:11      executing /omd/sites/dc1/local/share/check_mk/notifications/xnewdiscord.py
2023-05-12 14:19:11      Output:   File "/omd/sites/dc1/local/share/check_mk/notifications/xnewdiscord.py", line 92
2023-05-12 14:19:11      Output:     post_request(discord_msg).
2023-05-12 14:19:11      Output:                              ^
2023-05-12 14:19:11      Output: SyntaxError: invalid syntax
2023-05-12 14:19:11      Plugin exited with code 1

I have the file like so:

#!/usr/bin/env python3
# n00m Notify via Discord

import os
import requests
import sys

USERNAME = "check_mk"
AVATARURL = "https://checkmk.com/favicon-96x96.png"

COLORS = {
    "CRITICAL": "15597568",
    "DOWN": "15597568",
    "WARNING": "16768256",
    "OK": "52224",
    "UP": "52224",
    "UNKNOWN": "13421772",
    "UNREACHABLE": "13421772",
}

# Add the role mention ID here
# you could also use Variables from here https://docs.checkmk.com/latest/en/notifications.html#_environment_variables
ROLE_MENTION_ID = "123example123"

def discord_msg(context):
    """Build the message for discord"""
    facts = []
    if context.get('WHAT', None) == "SERVICE":
        state = context["SERVICESTATE"]
        color = COLORS.get(state)
        subtitle = "Service Notification"
        facts.append({"name": "Service:", "value": context["SERVICEDESC"]})
        output = context["SERVICEOUTPUT"] if context["SERVICEOUTPUT"] else ""
    else:
        state = context["HOSTSTATE"]
        color = COLORS.get(state)
        subtitle = "Host Notification"
        output = context["HOSTOUTPUT"] if context["HOSTOUTPUT"] else ""

    facts.extend([
        {
            "name": "Host:",
            "value": context["HOSTNAME"]
        },
        {
            "name": "State:",
            "value": state
        }
    ])

    mention = f"<@&{ROLE_MENTION_ID}>"

    return {
        "username": USERNAME,
        "avatar_url": AVATARURL,
        "content": mention,  # Add the role mention here
        "embeds": [
        {
            "title": subtitle,
            "color": color,
            "fields": facts,
            "footer": {
               "text": output
           }
        }
        ]
    }

# Rest of the code..

def collect_context():
    return {
        var[7:]: value
        for (var, value) in os.environ.items()
        if var.startswith("NOTIFY_")
    }

def post_request(message_constructor, success_code=204):
    context = collect_context()

    url = context.get("PARAMETERS")
    r = requests.post(url=url, json=message_constructor(context))

    if r.status_code == success_code:
        sys.exit(0)
    else:
        sys.stderr.write(
            "Failed to send notification. Status: %i, Response: %s\n" % (r.status_code, r.text))
        sys.exit(2)

if __name__ == "__main__":
    post_request(discord_msg).

Did I get that right? Thanks! C

You have a full stop at the end of the code, please remove

@qhgh
Copy link

qhgh commented May 12, 2023

Doh!

@Glowsome
Copy link

Glowsome commented Aug 30, 2023

Would it be possible to make a switch so notifications become more condensed ?
Meaning the discord message becomes more lean and displays as following:
Service Notification: (carriage return here) Service: service name (carriage return here) Host: Hostname (carriage return here) State: State_here (carriage return here) Detail: detail msg here (carriage return here)

Why : when you have a large amount of hosts it is preferable to see the messages in a more condensed way (saves scrolling).

Hope to hear back from you :)

@Glowsome
Copy link

Also one thing i did change in the script was the value of AVATARURL.
For some reason it never took hold, and got the default 'Wumpus' avatar witth the notifications.

After some digging i changed it to : "https://checkmk.com/android-chrome-192x192.png" - which works and shows the logo of CMK as avatar.

@fschlag
Copy link

fschlag commented Feb 2, 2024

Inspired by your plugin I also extended it a lot for my homelab an pushed all changes to a dedicated repo in case someone else wants to use it: https://github.com/fschlag/cmk_discord

@n00rm
Copy link
Author

n00rm commented Feb 5, 2024

Inspired by your plugin I also extended it a lot for my homelab an pushed all changes to a dedicated repo in case someone else wants to use it: https://github.com/fschlag/cmk_discord

Thanks @fschlag! Looks good and future development should and will be done in your project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment