From fcdd308c110762ec7417a1e7a58ad16ef8b60c8c Mon Sep 17 00:00:00 2001 From: Huck <1059231721@qq.com> Date: Tue, 24 Dec 2024 01:46:46 +0800 Subject: [PATCH] Add feishu notify method --- asset/assets_vfsdata.go | 2 +- config/config.go | 31 +++++ config/notifiers.go | 32 +++++ config/receiver/receiver.go | 5 +- notify/feishu/feishu_test.go | 28 +++++ notify/util.go | 14 ++- template/default.tmpl | 219 ----------------------------------- 7 files changed, 107 insertions(+), 224 deletions(-) create mode 100644 notify/feishu/feishu_test.go delete mode 100644 template/default.tmpl diff --git a/asset/assets_vfsdata.go b/asset/assets_vfsdata.go index cab8d4894f..5a4daed1c8 100644 --- a/asset/assets_vfsdata.go +++ b/asset/assets_vfsdata.go @@ -165,7 +165,7 @@ var Assets = func() http.FileSystem { modTime: time.Date(1970, 1, 1, 0, 0, 1, 0, time.UTC), uncompressedSize: 8101, - compressedContent: []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\x59\xcf\x6f\xeb\x36\x0c\xbe\xe7\xaf\x20\xfc\x76\x68\x0e\xf5\x1b\x76\x2c\x50\x0c\x0f\xc3\x7e\x1c\xba\x61\x68\xd1\x5d\x86\x21\x50\x6d\xc6\x55\x2b\x4b\xae\x44\x27\x0d\xd2\xfc\xef\x83\x6c\xc7\x91\x2d\x27\x91\xd3\xec\xb4\xdc\x12\x99\xfc\x48\x7f\x1f\x4d\xca\xf2\x7a\x0d\x29\xce\xb9\x44\x88\x66\x33\x26\x50\x53\xce\x24\xcb\x50\x47\xb0\xd9\x7c\x73\xfe\xaf\xd7\x80\x32\x85\xcd\x66\xb2\xd7\xe5\xf1\xfe\xce\x7a\xad\xd7\x10\xff\xfc\x4e\xa8\x25\x13\x8f\xf7\x77\xb0\xd9\x7c\xfd\xf2\xb5\xb2\x33\x3f\x6a\x4c\x90\x2f\x50\xdf\x5a\xa3\xfb\xe6\x0f\x7c\x40\xa9\xc5\x5b\x89\x7a\x55\xbb\x37\x81\xba\x91\x4c\xf9\xf4\x82\x09\xd9\x08\x7f\x5b\xef\x07\x62\x54\x1a\xf8\x00\x52\x8f\x45\x81\xba\x76\xe5\x73\xc0\xb7\xf6\x62\x34\xe7\x9a\xcb\xcc\xfa\xdc\x58\x9f\xea\x86\x4c\xfc\x4b\xb5\x0a\x1f\x20\x50\xba\x11\xff\x01\x6b\xf4\xab\x56\x65\x71\xc7\x9e\x50\x98\xf8\x41\x69\xc2\xf4\x4f\xc6\xb5\x89\xff\x62\xa2\x44\x1b\xf0\x45\x71\x09\x11\x58\x54\xa8\x43\x66\x04\x57\x16\x2b\xfe\x49\xe5\xb9\x92\xb5\xf3\xb4\x59\x73\xf0\xa6\xb0\xd9\x5c\xad\xd7\xb0\xe4\xf4\xdc\x35\x8e\xef\x31\x57\x0b\xec\x46\xff\x83\xe5\x68\x1a\x46\x87\xa2\xb7\x89\x4f\xdb\x5f\x7b\x64\x4a\xd1\x24\x9a\x17\xc4\x95\x8c\x0e\x70\x4c\xf8\x4e\xb5\xa4\x33\xc1\x0d\x35\xa6\x9a\xc9\x0c\x21\x86\xcd\xa6\xce\xeb\x66\xb2\x5b\xf4\x79\xb2\xac\x5c\x57\x44\xda\xf4\xed\xbf\x5b\x68\x6f\xa0\x49\xac\x0e\xfe\x4d\x4a\x45\xcc\xe6\xd4\x81\x74\x96\x4f\xc3\x7d\x50\xa5\x4e\xf0\xa6\x16\x13\x25\x6a\x46\x4a\xd7\x95\x38\x19\x20\xea\x20\x05\xb3\x9c\xe9\xd7\x54\x2d\xa5\xc7\xc5\x24\x94\x8c\xc0\xac\x27\xe3\xe9\x08\x45\x0e\x22\x64\x32\xcc\x88\x11\x2c\x79\x8d\x53\x9c\xb3\x52\x50\x4c\x9c\x04\x36\x54\x10\xe6\x85\x60\xd4\x7d\x38\xe3\x7d\x35\xd8\xc5\x29\x8d\x6d\x0f\xf9\x10\x54\xb7\x09\x05\xe2\xcd\x99\x10\x4f\x2c\x79\xf5\xf0\x06\xd3\xb7\xa0\xf0\x01\xc7\x0c\x05\x97\xaf\xc1\x19\x24\x4d\x06\x3c\x8d\xc2\x1c\x0a\x8d\xb6\xd6\x02\xad\x9d\x84\x0e\x32\x56\xf5\xe0\xc0\x94\x79\xa2\x24\xe6\xea\x85\x47\xe1\xf6\xa5\x16\xa1\x19\x87\xdf\xdc\x5c\x29\xaa\x27\x8e\x53\x83\xae\x79\x61\x6f\x2d\x2d\x69\xd5\xba\xf8\x0d\x6d\x5c\x39\xfa\x88\x89\xe0\x28\xe9\xf4\x82\xdc\x87\xb8\x9b\x8a\xa7\x69\xe6\xe3\x72\x69\x88\xc9\x04\xcd\x00\xae\xd7\xc1\xe3\xfd\xac\xaa\xc2\x64\x28\x39\xb6\xc0\x39\x1a\xc3\xb2\xd3\x9e\x6f\x0f\xcc\x57\xa8\x19\x78\x7b\x1a\xda\xe0\x84\x9b\xf4\xe6\x6b\x67\x80\x4f\xe1\x7b\xb8\xb6\x8d\xb3\x5a\x84\x7a\xb1\x6a\x9d\x87\x19\xe9\xee\x02\xaa\x20\xd7\xce\x1d\x0d\xc4\xbb\x47\xa3\xc4\x02\xd3\x5e\xc4\xed\x72\x78\xcc\xad\x87\x17\xf5\x3a\x84\x52\x53\xf5\xf1\xf1\xd5\xd4\x51\x7d\x89\xc9\x33\xa3\xb1\x9a\x4f\x2e\xfa\x1d\xd0\xcf\xdd\x28\x3f\x6a\xe1\xe1\x0d\xea\xb3\x47\xf5\x9e\x3e\xa4\x66\x76\x58\xee\xed\xa4\xbe\x79\xc1\x34\xad\x46\xd8\x13\xcb\x42\xad\x59\x86\x92\x66\xfd\x11\xd7\xad\xaf\x05\x4f\x48\x69\x55\x98\x5d\xd9\x12\x23\x9c\x75\x0b\xed\x52\x4b\xe3\x7a\x81\xcf\x2a\x4a\xe2\xb4\x9a\xa5\xdc\x14\x82\xad\x66\x7b\x76\x53\xc7\x1b\xb7\x8f\x9c\x2b\xc9\x49\x59\x42\x66\xa4\x94\x18\x39\x12\x3b\xb3\xab\x34\xcf\x6a\x81\xfa\x0c\xfb\x47\x0f\xea\xbf\xaf\xa7\xf3\x94\x53\x78\x35\x9d\xaf\x98\xfc\x2d\xfd\x21\x26\x77\x7b\xba\x31\x33\xc5\xdd\xcd\x49\xe7\x61\xdf\xbd\xa6\x8f\x7f\x47\x70\x70\x2e\xf2\x8e\x91\xd7\x65\x91\x50\x60\xa6\x59\x3e\x44\xe5\xff\x96\x94\x94\x9b\x44\xe9\x74\xb7\x37\x57\x92\x76\xdb\x7d\xbf\x14\xfb\xf6\xa7\x37\xae\x3e\xd2\x45\x0d\xbb\xad\x78\xc2\xf7\xcb\xa3\xfe\x69\x1e\x73\x43\xc8\x72\xb7\xf9\xe6\x39\xd3\xab\x93\xea\xb4\x8f\x75\x7a\xc5\x7b\x48\xcd\x49\x40\x88\x4c\x5f\x60\x94\x50\xce\xf1\xdc\xa7\x15\x6b\x43\x87\x6a\x36\x10\xfc\x04\xf1\x16\x3f\x9c\x8f\x72\x17\xeb\x42\xfa\x10\xe9\x2f\x5c\xb3\xb3\x3c\x2e\x1d\xa0\xde\x59\xc7\x85\xf3\x49\xf5\x1a\x33\xc8\x55\xa1\xb9\xd2\xdc\xbe\xa1\x5e\x37\x6f\x3b\xdf\x6d\x97\xe0\xe6\x16\xa2\x68\xfb\x12\xb4\x3d\xff\xee\xdc\xad\xf5\x01\x00\xa8\xfc\x0c\x2e\x70\xeb\xc7\x65\x8a\xef\xdb\x23\x78\x88\xb6\x97\xa2\x8e\x07\x9f\xc3\x15\xbe\x39\x8e\x51\xa2\x39\xf1\x84\x89\x68\xda\x1a\xb6\xf0\x6d\x5a\xb7\x10\xfd\xc6\xb3\xe7\x2e\x16\x0a\x83\x15\x20\x93\x69\x1f\x75\xc9\xb4\xe4\x32\x8b\xa6\x70\x25\xd1\x01\xaa\x61\xa6\x47\x62\xfd\x8e\x29\x2f\xf3\xf0\x68\x5c\xce\x95\x0d\x65\x57\x77\xa1\x8e\x86\xb9\x53\xcb\x5e\x0c\x99\xb6\x9a\xb8\xbf\xeb\x6f\x6a\x2e\x74\xc7\xad\xab\x53\x5b\x18\x5e\xec\x51\x6a\x8d\x56\x2c\x40\xb5\xb3\x2b\x17\xa4\xde\xf9\x14\x3c\xae\x62\x5f\xc9\x63\xca\xee\x90\xfa\x57\xdd\x56\xa7\x55\xf2\x8a\xd4\x3d\x36\x3a\x79\x52\x0d\x80\x31\xc1\x99\x39\xfd\xe0\x7d\x5f\x7a\x9f\xfe\x5a\x32\x00\x7c\xf8\x73\xc9\x80\xc3\xb1\x6f\x26\x43\xc9\x7b\x1f\x4e\xfe\x0d\x00\x00\xff\xff\x74\x5d\xc4\xb5\xa5\x1f\x00\x00"), + compressedContent: []byte("\x1f\x8b\x08\x08\x73\x9f\x65\x67\x00\x03\x64\x65\x66\x61\x75\x6c\x74\x2e\x74\x6d\x70\x6c\x00\xed\x59\x4d\x6f\xe3\x36\x10\xbd\xfb\x57\x10\xda\x1e\xe2\x83\xbd\x45\x8f\x01\x82\x62\x51\xf4\xe3\x90\x16\x45\x82\xf4\x52\x14\x02\x23\x8d\x6d\x26\x14\xa9\x90\x94\x1d\xc3\xdd\xff\xde\xa1\x24\xcb\xa2\x28\xd9\x94\xe3\x02\x05\xea\x9c\x22\x7a\xe6\xcd\xf0\xbd\xd1\x90\x14\x77\x3b\x92\xc2\x82\x09\x20\x51\x1c\x53\x0e\xca\x64\x54\xd0\x25\xa8\x88\x7c\xfd\xfa\xa5\xf5\xbc\xdb\x11\x10\x29\x0e\x4e\x76\x43\x2e\x4f\x0f\xf7\xd6\x0b\x7f\x9f\xff\xf8\x6e\x40\x09\xca\x71\x08\x47\x3e\x7f\xfa\x5c\xda\xe9\xef\x15\x24\xc0\xd6\xa0\xee\xac\xd1\x43\xfd\x40\xfe\x26\x85\xe2\x6f\x05\xa8\x6d\xe5\x5e\x07\x72\x23\xe9\xe2\xf9\x05\x12\x63\x23\xfc\x69\xbd\x1f\x0d\x35\x85\x46\x5f\x23\x9f\xf2\x1c\x51\x4a\x57\xb6\x20\xf0\xd6\xfc\x18\x2d\x98\x62\x62\x69\x7d\x6e\xad\x4f\x39\x21\x3d\xff\xa9\x1c\x45\x57\x0e\xa2\x1d\xf1\x2f\x62\x8d\x7e\x56\xb2\xc8\xef\xe9\x33\x70\x3d\x7f\x94\xca\x40\xfa\x3b\x65\x4a\xcf\xff\xa0\xbc\x00\x1b\xf0\x45\x32\x41\x22\x62\x51\x49\x15\x72\x69\xc8\x8d\xc5\x9a\xff\x20\xb3\x4c\x8a\xca\x79\x5a\x8f\xb5\xf0\xa6\xe8\x72\x83\x2e\x1b\x66\x56\xae\x31\x92\x91\xc9\x35\xb8\xd1\x7f\xa3\x19\x06\xac\x18\xed\x8b\xde\x24\x3e\x6d\xfe\x1b\x90\x29\x05\x9d\x28\x96\x1b\x26\x45\x74\x84\x63\x03\xef\xa6\x92\x34\xe6\x4c\x9b\xda\x54\x51\xb1\xc4\xcc\xf0\xa1\xca\xeb\x76\x72\x18\xf4\x79\xb2\xac\xcc\x4a\x22\x6d\xfa\xf6\xe9\x8e\x34\x13\xa8\x13\xab\x82\x7f\x11\x42\xa2\x4e\x98\x93\x03\xd9\x1a\x3e\x0f\xf7\x51\x16\x2a\x81\xdb\x4a\x4c\x10\xa0\xa8\x91\xaa\xaa\xc4\x49\x0f\x51\x47\x29\x88\x33\xaa\x5e\x53\xb9\x11\x1e\x17\x93\x50\x32\x02\xb3\x9e\x8c\xa7\x23\x14\x39\x88\x90\x49\x3f\x23\x9a\xd3\xe4\x75\x8e\x4f\xb4\xe0\x66\x6e\x98\xe1\x50\x53\x61\x20\xcb\x39\x35\xee\xcb\x39\x1f\xaa\x41\x17\xa7\xd0\xb6\x3d\x64\x7d\x50\x6e\x13\x0a\xc4\x5b\x50\xce\x9f\x71\xc0\xc3\xeb\x4d\xdf\x82\xe2\x9b\x74\xca\x90\x33\xf1\x1a\x9c\x41\x52\x67\xc0\xd2\x28\xcc\x21\x57\x60\x6b\x2d\xd0\xba\x95\xd0\x51\xc6\xca\x1e\x1c\x98\x32\x4b\xa4\xc0\xae\xf3\xc2\x02\x73\xb0\xf6\xd8\xa8\x43\x33\x0e\x9f\xdc\x42\x4a\x53\xad\x38\xad\x1a\x6c\x9b\xe7\x76\x6a\x69\x61\xb6\x8d\x8b\xdf\xd0\xc6\x95\xa3\x8f\x98\x70\x06\xc2\x9c\x5f\x90\x43\x88\x87\x55\xf1\x3c\xcd\x7c\x5c\x26\xb4\xa1\x22\x01\xdd\x83\xeb\x75\xf0\xf9\x30\xab\x32\xd7\x4b\x10\x0c\x1a\x60\x5c\x6d\x34\x46\x3b\x8b\x50\x0f\xcc\x57\xa8\x5e\xf0\x06\x1a\x5a\xef\x0a\x37\xe9\xac\xaf\xce\x02\x3e\x25\xdf\x92\x99\x6d\x9c\xe5\x20\xa9\x06\xcb\xd6\x79\x9c\x11\x77\x17\x50\x06\x99\xb5\x66\xd4\x13\xef\x01\xb4\xe4\x6b\x48\x3b\x11\xf7\xc3\xe1\x31\xf7\x1e\x5e\xd4\x59\x08\xa5\xba\xec\xe3\xe3\xab\xc9\x51\x7d\x03\xc9\x8a\x9a\xb1\x9a\x4f\xae\xfa\x1d\xd1\xaf\xbd\x51\x7e\x52\xdc\xc3\xeb\xd5\x67\x40\xf5\x8e\x3e\x46\xc6\x76\xb1\x1c\xec\xa4\xbe\x79\x4e\x95\xd9\x8e\xb0\x37\x74\x19\x6a\x8d\xf9\x0b\x13\x77\x97\x38\xa7\xbc\x16\xc0\xf4\xaa\xb8\x96\xd7\x7f\xb5\xbc\x3a\xfa\xa0\xfe\x56\xe1\xc1\x02\xf0\xcd\xfd\x6a\x74\xf4\x5f\xb3\x04\x37\x97\xd8\xba\x0e\x5d\x0b\xd5\x84\xd8\x2d\x84\xab\xd6\xe3\x96\x02\x9f\x55\x7c\x11\x99\xd9\xc6\x29\xd3\x18\x6a\x1b\x0f\x6c\xa6\x4f\xaf\xdb\x3e\x32\xea\xc2\x70\x08\x09\x89\x8d\x94\x7c\xe4\x8e\xc8\xd9\xba\x14\x7a\x85\x87\x5a\x75\x81\xe3\x83\x07\xf5\xef\xd7\xd3\x65\xca\x29\xbc\x9a\x2e\x57\x4c\xfe\x89\xee\x18\x93\x87\x2d\xfd\x98\x2d\x45\x7b\x33\x2f\x5a\x2f\xfb\xe1\x2b\xcd\xf8\x23\x62\x0b\xe7\x2a\xef\x18\x79\xdb\x2c\x1a\xe0\xb0\x54\x34\xeb\xa3\xf2\x7f\x4b\x0a\xb6\xc9\x44\xaa\xf4\x70\x34\x93\xc2\x1c\x4e\x7b\x7e\x29\x76\xed\xcf\x6f\x5c\x5d\xa4\xab\x1a\x76\x57\xf9\x0c\xef\xd7\x57\xfd\xc3\x3c\x66\xda\x00\xcd\xda\xcd\x37\xcb\xa8\xda\x9e\x55\xa7\x5d\xac\xf3\x2b\xde\x43\xaa\x3f\x04\x85\xc8\xf4\x89\x8c\x12\xaa\xf5\x75\xf6\xc3\x8a\x35\xa1\x43\x35\xeb\x09\x7e\x86\x78\xeb\xef\x2e\x47\x79\x1b\xeb\x4a\x7a\x1f\xe9\x2f\x4c\xd1\x8b\xbc\x2e\x0e\x50\xe7\x53\xd7\x95\xf3\x49\x79\x8c\xe9\xe5\x2a\x57\x0c\xcf\x16\xf6\x03\xc5\xac\x3e\xed\x7c\xb3\x1f\x22\xb7\x77\x24\x8a\xf6\x87\xa0\xfd\xf5\x87\x33\x5b\xeb\x43\xf0\xaf\xf4\xd3\x80\xdb\xd8\xda\x8f\x89\x14\xde\xf7\x37\x30\xb8\x95\xac\x7f\x8a\x1c\x0f\xe4\xe6\x06\xde\x5a\x8e\x11\xca\x66\x58\x42\x79\x34\x6d\x0c\x1b\xf8\x26\x2d\xcc\xea\x17\xb6\x5c\xb9\x58\x18\x06\x4a\x40\x8a\x33\xee\xa0\x6e\xa8\x12\xf6\xda\x71\x4a\x6e\x70\xfe\x07\xa0\x0a\x66\x7a\x22\xd6\xaf\x90\xb2\x22\x0b\x8f\xc6\xc4\x42\xda\x50\x76\xf4\x10\xea\x64\x98\x7b\xb9\xe9\xc4\x40\xe8\xbd\x26\xed\xff\xab\x2b\xd5\x36\xb4\xe3\xe6\xea\xd4\x14\x86\x17\x7b\x94\x5a\xa3\x15\x0b\x50\xed\xe2\xca\x05\xa9\x77\x39\x05\x4f\xab\xd8\x55\xf2\x94\xb2\x07\xa4\xee\xaf\xed\x56\xa7\x64\xf2\x0a\xc6\xfd\x6a\x78\xf6\x4a\xd5\x03\x46\x39\xa3\x7d\x57\x19\x81\xf7\x2e\x43\xe9\x7d\xf8\xb2\xac\x07\xf8\xf8\x6d\x59\x8f\xc3\xa9\x2b\xb3\xbe\xe4\xbd\x7b\xb3\x7f\x00\x14\x17\xe2\x91\xa4\x21\x00\x00"), }, "/templates/email.tmpl": &vfsgen۰CompressedFileInfo{ name: "email.tmpl", diff --git a/config/config.go b/config/config.go index 890592ca90..522505a7c0 100644 --- a/config/config.go +++ b/config/config.go @@ -272,6 +272,9 @@ func resolveFilepaths(baseDir string, cfg *Config) { for _, cfg := range receiver.RocketchatConfigs { cfg.HTTPConfig.SetDirectory(baseDir) } + for _, cfg := range receiver.FeishuConfigs { + cfg.HTTPConfig.SetDirectory(baseDir) + } } } @@ -606,6 +609,29 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { rocketchat.TokenFile = c.Global.RocketchatTokenFile } } + for _, feishu := range rcv.FeishuConfigs { + if feishu.HTTPConfig == nil { + feishu.HTTPConfig = c.Global.HTTPConfig + } + if feishu.APIURL == nil { + if c.Global.FeishuAPIURL == nil { + return errors.New("no global FeishuAPIURL set ") + } + feishu.APIURL = c.Global.FeishuAPIURL + } + if feishu.APPID == "" { + if c.Global.FeishuAPPID == "" { + return errors.New("no global FeishuAPPID set ") + } + feishu.APPID = c.Global.FeishuAPPID + } + if feishu.APPSecret == "" { + if c.Global.FeishuAPPSecret == "" { + return errors.New("no global FeishuAPPSecret set ") + } + feishu.APPSecret = c.Global.FeishuAPPSecret + } + } names[rcv.Name] = struct{}{} } @@ -710,6 +736,7 @@ func DefaultGlobalConfig() GlobalConfig { TelegramAPIUrl: mustParseURL("https://api.telegram.org"), WebexAPIURL: mustParseURL("https://webexapis.com/v1/messages"), RocketchatAPIURL: mustParseURL("https://open.rocket.chat/"), + FeishuAPIURL: mustParseURL("https://open.feishu.cn/open-apis"), } } @@ -831,6 +858,9 @@ type GlobalConfig struct { WeChatAPIURL *URL `yaml:"wechat_api_url,omitempty" json:"wechat_api_url,omitempty"` WeChatAPISecret Secret `yaml:"wechat_api_secret,omitempty" json:"wechat_api_secret,omitempty"` WeChatAPICorpID string `yaml:"wechat_api_corp_id,omitempty" json:"wechat_api_corp_id,omitempty"` + FeishuAPIURL *URL `yaml:"feishu_api_url,omitempty" json:"feishu_api_url,omitempty"` + FeishuAPPID string `yaml:"feishu_app_id,omitempty" json:"feishu_app_id,omitempty"` + FeishuAPPSecret Secret `yaml:"feishu_app_secret,omitempty" json:"feishu_app_secret,omitempty"` VictorOpsAPIURL *URL `yaml:"victorops_api_url,omitempty" json:"victorops_api_url,omitempty"` VictorOpsAPIKey Secret `yaml:"victorops_api_key,omitempty" json:"victorops_api_key,omitempty"` VictorOpsAPIKeyFile string `yaml:"victorops_api_key_file,omitempty" json:"victorops_api_key_file,omitempty"` @@ -988,6 +1018,7 @@ type Receiver struct { MSTeamsV2Configs []*MSTeamsV2Config `yaml:"msteamsv2_configs,omitempty" json:"msteamsv2_configs,omitempty"` JiraConfigs []*JiraConfig `yaml:"jira_configs,omitempty" json:"jira_configs,omitempty"` RocketchatConfigs []*RocketchatConfig `yaml:"rocketchat_configs,omitempty" json:"rocketchat_configs,omitempty"` + FeishuConfigs []*FeishuConfig `yaml:"feishu_configs,omitempty" json:"feishu_configs,omitempty"` } // UnmarshalYAML implements the yaml.Unmarshaler interface for Receiver. diff --git a/config/notifiers.go b/config/notifiers.go index 74c74a92a0..97d62ebc06 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -203,6 +203,15 @@ var ( Description: `{{ template "jira.default.description" . }}`, Priority: `{{ template "jira.default.priority" . }}`, } + + DefaultFeishuConfig = FeishuConfig{ + NotifierConfig: NotifierConfig{ + VSendResolved: true, + }, + ToUser: `{{ template "feishu.default.to_user" . }}`, + ToChat: `{{ template "feishu.default.to_chat" . }}`, + Message: `{{ template "feishu.default.message" . }}`, + } ) // NotifierConfig contains base options common across all notifier configurations. @@ -597,6 +606,29 @@ func (c *WechatConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } +// FeishuConfig configures notifications via Feishu. +type FeishuConfig struct { + NotifierConfig `yaml:",inline" json:",inline"` + + HTTPConfig *commoncfg.HTTPClientConfig `yaml:"http_config,omitempty" json:"http_config,omitempty"` + + APIURL *URL `yaml:"api_url,omitempty" json:"api_url,omitempty"` + APPID string `yaml:"app_id,omitempty" json:"app_id,omitempty"` + APPSecret Secret `yaml:"app_secret,omitempty" json:"app_secret,omitempty"` + ToUser string `yaml:"to_user,omitempty" json:"to_user,omitempty"` + ToChat string `yaml:"to_chat,omitempty" json:"to_chat,omitempty"` + Message string `yaml:"message,omitempty" json:"message,omitempty"` +} + +func (c *FeishuConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = DefaultFeishuConfig + type plain FeishuConfig + if err := unmarshal((*plain)(c)); err != nil { + return err + } + return nil +} + // OpsGenieConfig configures notifications via OpsGenie. type OpsGenieConfig struct { NotifierConfig `yaml:",inline" json:",inline"` diff --git a/config/receiver/receiver.go b/config/receiver/receiver.go index d92a19a4c5..a27fc32ef1 100644 --- a/config/receiver/receiver.go +++ b/config/receiver/receiver.go @@ -23,6 +23,7 @@ import ( "github.com/prometheus/alertmanager/notify" "github.com/prometheus/alertmanager/notify/discord" "github.com/prometheus/alertmanager/notify/email" + "github.com/prometheus/alertmanager/notify/feishu" "github.com/prometheus/alertmanager/notify/jira" "github.com/prometheus/alertmanager/notify/msteams" "github.com/prometheus/alertmanager/notify/msteamsv2" @@ -109,7 +110,9 @@ func BuildReceiverIntegrations(nc config.Receiver, tmpl *template.Template, logg for i, c := range nc.RocketchatConfigs { add("rocketchat", i, c, func(l *slog.Logger) (notify.Notifier, error) { return rocketchat.New(c, tmpl, l, httpOpts...) }) } - + for i, c := range nc.FeishuConfigs { + add("feishu", i, c, func(l *slog.Logger) (notify.Notifier, error) { return feishu.New(c, tmpl, l, httpOpts...) }) + } if errs.Len() > 0 { return nil, &errs } diff --git a/notify/feishu/feishu_test.go b/notify/feishu/feishu_test.go new file mode 100644 index 0000000000..ec70554ab9 --- /dev/null +++ b/notify/feishu/feishu_test.go @@ -0,0 +1,28 @@ +package feishu + +import ( + "github.com/prometheus/alertmanager/config" + "github.com/prometheus/alertmanager/notify/test" + commoncfg "github.com/prometheus/common/config" + "github.com/prometheus/common/promslog" + "github.com/stretchr/testify/require" + "testing" +) + +func TestFeishuInitialAuthentication(t *testing.T) { + _, u, fn := test.GetContextWithCancelingURL() + defer fn() + app_id := "app_id" + app_secret := "app_secret" + _, err := New( + &config.FeishuConfig{ + APIURL: &config.URL{URL: u}, + HTTPConfig: &commoncfg.HTTPClientConfig{}, + APPID: app_id, + APPSecret: config.Secret(app_secret), + }, + test.CreateTmpl(t), + promslog.NewNopLogger(), + ) + require.NoError(t, err) +} diff --git a/notify/util.go b/notify/util.go index 14e3050bdb..176a325447 100644 --- a/notify/util.go +++ b/notify/util.go @@ -48,7 +48,7 @@ func RedactURL(err error) error { // Get sends a GET request to the given URL. func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { - return request(ctx, client, http.MethodGet, url, "", nil) + return request(ctx, client, http.MethodGet, url, "", map[string]string{}, nil) } // PostJSON sends a POST request with JSON payload to the given URL. @@ -61,11 +61,16 @@ func PostText(ctx context.Context, client *http.Client, url string, body io.Read return post(ctx, client, url, "text/plain", body) } +// PostTextAddHeaders sends a POST request with text payload and header to the given URL +func PostTextAddHeaders(ctx context.Context, client *http.Client, url string, headers map[string]string, body io.Reader) (*http.Response, error) { + return request(ctx, client, http.MethodPost, url, "text/plain", headers, body) +} + func post(ctx context.Context, client *http.Client, url, bodyType string, body io.Reader) (*http.Response, error) { - return request(ctx, client, http.MethodPost, url, bodyType, body) + return request(ctx, client, http.MethodPost, url, bodyType, map[string]string{}, body) } -func request(ctx context.Context, client *http.Client, method, url, bodyType string, body io.Reader) (*http.Response, error) { +func request(ctx context.Context, client *http.Client, method, url, bodyType string, headers map[string]string, body io.Reader) (*http.Response, error) { req, err := http.NewRequest(method, url, body) if err != nil { return nil, err @@ -74,6 +79,9 @@ func request(ctx context.Context, client *http.Client, method, url, bodyType str if bodyType != "" { req.Header.Set("Content-Type", bodyType) } + for k, v := range headers { + req.Header.Set(k, v) + } return client.Do(req.WithContext(ctx)) } diff --git a/template/default.tmpl b/template/default.tmpl deleted file mode 100644 index 57e877c0c2..0000000000 --- a/template/default.tmpl +++ /dev/null @@ -1,219 +0,0 @@ -{{ define "__alertmanager" }}Alertmanager{{ end }} -{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts?receiver={{ .Receiver | urlquery }}{{ end }} - -{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }} -{{ define "__description" }}{{ end }} - -{{ define "__text_alert_list" }}{{ range . }}Labels: -{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} -{{ end }}Annotations: -{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} -{{ end }}Source: {{ .GeneratorURL }} -{{ end }}{{ end }} - -{{ define "__text_alert_list_markdown" }}{{ range . }} -Labels: -{{ range .Labels.SortedPairs }} - {{ .Name }} = {{ .Value }} -{{ end }} -Annotations: -{{ range .Annotations.SortedPairs }} - {{ .Name }} = {{ .Value }} -{{ end }} -Source: {{ .GeneratorURL }} -{{ end }} -{{ end }} - -{{ define "slack.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "slack.default.username" }}{{ template "__alertmanager" . }}{{ end }} -{{ define "slack.default.fallback" }}{{ template "slack.default.title" . }} | {{ template "slack.default.titlelink" . }}{{ end }} -{{ define "slack.default.callbackid" }}{{ end }} -{{ define "slack.default.pretext" }}{{ end }} -{{ define "slack.default.titlelink" }}{{ template "__alertmanagerURL" . }}{{ end }} -{{ define "slack.default.iconemoji" }}{{ end }} -{{ define "slack.default.iconurl" }}{{ end }} -{{ define "slack.default.text" }}{{ end }} -{{ define "slack.default.footer" }}{{ end }} - - -{{ define "pagerduty.default.description" }}{{ template "__subject" . }}{{ end }} -{{ define "pagerduty.default.client" }}{{ template "__alertmanager" . }}{{ end }} -{{ define "pagerduty.default.clientURL" }}{{ template "__alertmanagerURL" . }}{{ end }} -{{ define "pagerduty.default.instances" }}{{ template "__text_alert_list" . }}{{ end }} - - -{{ define "opsgenie.default.message" }}{{ template "__subject" . }}{{ end }} -{{ define "opsgenie.default.description" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 -}} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{- end }} -{{ if gt (len .Alerts.Resolved) 0 -}} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{- end }} -{{- end }} -{{ define "opsgenie.default.source" }}{{ template "__alertmanagerURL" . }}{{ end }} - - -{{ define "wechat.default.message" }}{{ template "__subject" . }} -{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 -}} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{- end }} -{{ if gt (len .Alerts.Resolved) 0 -}} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{- end }} -AlertmanagerUrl: -{{ template "__alertmanagerURL" . }} -{{- end }} -{{ define "wechat.default.to_user" }}{{ end }} -{{ define "wechat.default.to_party" }}{{ end }} -{{ define "wechat.default.to_tag" }}{{ end }} -{{ define "wechat.default.agent_id" }}{{ end }} - - - -{{ define "victorops.default.state_message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 -}} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{- end }} -{{ if gt (len .Alerts.Resolved) 0 -}} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{- end }} -{{- end }} -{{ define "victorops.default.entity_display_name" }}{{ template "__subject" . }}{{ end }} -{{ define "victorops.default.monitoring_tool" }}{{ template "__alertmanager" . }}{{ end }} - -{{ define "pushover.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "pushover.default.message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 }} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{ end }} -{{ end }} -{{ define "pushover.default.url" }}{{ template "__alertmanagerURL" . }}{{ end }} - -{{ define "sns.default.subject" }}{{ template "__subject" . }}{{ end }} -{{ define "sns.default.message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 }} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "telegram.default.message" }} -{{ if gt (len .Alerts.Firing) 0 }} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "discord.default.content" }}{{ end }} -{{ define "discord.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "discord.default.message" }} -{{ if gt (len .Alerts.Firing) 0 }} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "webex.default.message" }}{{ .CommonAnnotations.SortedPairs.Values | join " " }} -{{ if gt (len .Alerts.Firing) 0 }} -Alerts Firing: -{{ template "__text_alert_list" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -Alerts Resolved: -{{ template "__text_alert_list" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "msteams.default.summary" }}{{ template "__subject" . }}{{ end }} -{{ define "msteams.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "msteams.default.text" }} -{{ if gt (len .Alerts.Firing) 0 }} -# Alerts Firing: -{{ template "__text_alert_list_markdown" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -# Alerts Resolved: -{{ template "__text_alert_list_markdown" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "msteamsv2.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "msteamsv2.default.text" }} -{{ if gt (len .Alerts.Firing) 0 }} -# Alerts Firing: -{{ template "__text_alert_list_markdown" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -# Alerts Resolved: -{{ template "__text_alert_list_markdown" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{ define "jira.default.summary" }}{{ template "__subject" . }}{{ end }} -{{ define "jira.default.description" }} -{{ if gt (len .Alerts.Firing) 0 }} -# Alerts Firing: -{{ template "__text_alert_list_markdown" .Alerts.Firing }} -{{ end }} -{{ if gt (len .Alerts.Resolved) 0 }} -# Alerts Resolved: -{{ template "__text_alert_list_markdown" .Alerts.Resolved }} -{{ end }} -{{ end }} - -{{- define "jira.default.priority" -}} -{{- $priority := "" }} -{{- range .Alerts.Firing -}} - {{- $severity := index .Labels "severity" -}} - {{- if (eq $severity "critical") -}} - {{- $priority = "High" -}} - {{- else if (and (eq $severity "warning") (ne $priority "High")) -}} - {{- $priority = "Medium" -}} - {{- else if (and (eq $severity "info") (eq $priority "")) -}} - {{- $priority = "Low" -}} - {{- end -}} -{{- end -}} -{{- if eq $priority "" -}} - {{- range .Alerts.Resolved -}} - {{- $severity := index .Labels "severity" -}} - {{- if (eq $severity "critical") -}} - {{- $priority = "High" -}} - {{- else if (and (eq $severity "warning") (ne $priority "High")) -}} - {{- $priority = "Medium" -}} - {{- else if (and (eq $severity "info") (eq $priority "")) -}} - {{- $priority = "Low" -}} - {{- end -}} - {{- end -}} -{{- end -}} -{{- $priority -}} -{{- end -}} - -{{ define "rocketchat.default.title" }}{{ template "__subject" . }}{{ end }} -{{ define "rocketchat.default.alias" }}{{ template "__alertmanager" . }}{{ end }} -{{ define "rocketchat.default.titlelink" }}{{ template "__alertmanagerURL" . }}{{ end }} -{{ define "rocketchat.default.emoji" }}{{ end }} -{{ define "rocketchat.default.iconurl" }}{{ end }} -{{ define "rocketchat.default.text" }}{{ end }}