argoproj / argo-cd

Declarative Continuous Deployment for Kubernetes
https://argo-cd.readthedocs.io
Apache License 2.0
17.33k stars 5.26k forks source link

Support Topics/Threads for Telegram Notifications / Remove 3rd Party Telegram Library #17142

Open ForbiddenEra opened 7 months ago

ForbiddenEra commented 7 months ago

Summary

Would like to be able to direct Telegram notifications to a specific Topic/Thread in a channel.

Motivation

For a while now, you can create separate topics and threads inside Telegram channels. We use this (among other use cases) to have a Notifications topic in our team chat, this allows us to keep team discussions organized and keep notifications separate from any discussions.

It would be great to have this ability supported.

I took a quick look at the code to see if it was possible and saw that the go-telegram-bot-api library is used, so I looked at it's code quickly searching for message_thread_id (the property that needs to be passed with the API request to direct a message to a topic) and wasn't able to find it and also noticed the latest release of that library was in 2021 which I think may predate the feature.

Now, considering that, one would first think something along the lines of "Well, the library would have to support it first." but that brings me to my second point/suggestion/proposal on the subject.

While the library provides a fair bit of other functionality for interacting with telegram, none of that is needed. The library itself is not even needed and, IMHO, using/including it is probably overkill here.

I get the desire to want to use a library for something like this since often it's easier to implement a library in a language you're familiar with than figuring out how some 3rd party service works.

In this case, however, it's only a HTTP POST request with a simple JSON payload. I don't see the logic in including a dependency with thousands of lines of code for sending a rather simple HTTP request. If other features were being used, like, say I could use my bot with Argo to do like /sync my-app-name in chat, sure. But this is just pushing notifications, not receiving anything back from Telegram or any other integrations.

In fact; I just realized that this request is simple enough that I might even be able to achieve my desired results using the webhook notification type!

And; in that case, perhaps the best way forward might be to even remove telegram as a bespoke service and simply provide an example for implementing it as a webhook.

Proposal

Remove the go-telegram-bot-api library and replace it with a simple HTTP request.

One only needs to make a POST request to https://api.telegram.com/bot${token}/sendMessage (replacing ${token} with the user provided token of course) with Content-Type: json and a JSON payload like:

{
  "chat_id": -1000000000000,
  "message_thread_id": 1234,
  "parse_mode": "MarkdownV2",
  "text": "Notification Message Content"
}

message_thread_id being optional and only included if the user specified it.

parse_mode is also optional; you can set it to MarkdownV2 to be able to use markdown in the message, but if you do you have to escape some characters:

characters '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!' must be escaped with the preceding character '\'.

But, you can just leave it out and send plaintext without worrying about the escaping; I'm sure currently the notifications are just being sent as plaintext anyway, although perhaps users might like the option and perhaps it could be implemented with it's own templates (which get escaped internally by argo before posting) like slack gets it's own.

The only thing otherwise to figure out would be how to implement it in the configmaps. Perhaps one could have a list of channels that have an optional thread id, sort of like the Teams configuration and then one can reference the defined channel name.

Maybe like:

apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
data:
  service.telegram: |
    token: $telegram-token
    channels:
      prodNotifications:
        chatId: -1000000000000
        threadId: 1234

with an example subscription like:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    notifications.argoproj.io/subscribe.on-sync-succeeded.telegram: prodNotifications
ForbiddenEra commented 7 months ago

So, as I suspected it's definitely possible with webhooks (which, IMHO reinforces even more the lack of need of the 3rd party module).

I think one could even get fancy and use MarkdownV2 since strings.replaceAll is an available function for templates.

Perhaps at an absolute minimum (assuming those who decide don't agree with my proposal), this could be mentioned on the Telegram page in the docs and provided as an example on the webhook page? I did notice the webhook page had an example for Slack which is supported without webhooks otherwise?

Example implementation:

apiVersion: v1
data:
  context: |
    argocdUrl: https://argo.example.com
  service.webhook.telegramwebhook: |
    url: https://api.telegram.org/bot$telegram-token
    headers:
     - name: Content-Type
       value: application/json
  subscriptions: |
    - recipients:
      - telegramwebhook
      triggers:
      - on-deployed
  template.app-deployed: |
    webhook:
      telegramwebhook:
        method: POST
        path: /sendMessage
        body: |
          {
            "chat_id":-1000000000000,
            "message_thread_id":1234,
            "text":"New version of an application {{.app.metadata.name}} is up and running."
          }