rubenlagus / TelegramBots

Java library to create bots using Telegram Bots API
https://telegram.me/JavaBotsApi
MIT License
4.77k stars 1.22k forks source link

How to use the webhook? #872

Closed facundopadilla closed 3 years ago

facundopadilla commented 3 years ago

Hello everyone, how are you, I need your help, I do not understand the documentation, I am working from IntelliJ Idea. Basically my bot already works and everything, but now to assign a webhook is complicated, and the examples do not work. I leave images of my code better.

image

I'm working with 5.0.1.1 version

TimothyGillespie commented 3 years ago

I didn't try this myself yet, but tell me if this works:

import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.api.methods.updates.SetWebhook;
import org.telegram.telegrambots.meta.api.objects.InputFile;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
import java.io.File;

...

            File certificateFile = new File("path/to/your/certificate"); // Edit this
            InputFile inputFile = new InputFile();
            inputFile.setMedia(certificateFile);

            SetWebhook setWebhook = new SetWebhook();
            setWebhook.setUrl("https://your-webhook-location.com"); // Edit this
            setWebhook.setCertificate(inputFile);

            TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
            botsApi.registerBot(new MyWebhookBot(), setWebhook); // Of course change this to your bot class

...

It corresponds to this part of the API (the following link will also link some resources to set up your certificate etc.): https://core.telegram.org/bots/api#setwebhook

Setting up a webhook is a little more difficult because Telegram requires an https connection (which makes total sense). The other options are optional. The certificate is not needed if you do not self-sign afaik. So it might be easier to use nginx as a reverse proxy and let let's encrypt / certbot manage it for you.

aNNiMON commented 3 years ago

Heroku

Heroku starts on a random port, you need to get a value from the environment property PORT:

final String port = System.getenv("PORT");

final var webhook = new DefaultWebhook();
webhook.setInternalUrl("http://0.0.0.0:" + port);

final var setwebhook = SetWebhook.builder()
        .url("https://yourappname.herokuapp.com")
        .build();

final var telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, webhook);
telegramBotsApi.registerBot(new MyWebhookBot(), setwebhook);

Self-hosted on port 8443 with self-signed certificate

Use certgen.sh to generate a certificate (replace SERVERIPADDRESS with the server's IP address or domain):

JKS=keystore.jks
CERT=public_cert.pem

openssl req -newkey rsa:2048 -sha256 -nodes \
    -keyout private.key -x509 \
    -days 365 \
    -out $CERT \
    -subj "/C=US/ST=Utah/L=Location/O=Organization/CN=SERVERIPADDRESS"

openssl pkcs12 -export \
    -in $CERT \
    -inkey private.key \
    -certfile $CERT \
    -out keystore.p12

keytool -importkeystore \
    -srckeystore keystore.p12 \
    -srcstoretype pkcs12 \
    -sigalg SHA1withRSA \
    -destkeystore $JKS \
    -deststoretype pkcs12

rm keystore.p12 private.key

Export your keystore password (see Setting Environment Variables Wiki section):

export KEYSTORE_PASSWORD=mysupersecretpasswordis123456
final var webhook = new DefaultWebhook();
webhook.setInternalUrl("http://0.0.0.0:8443");
webhook.setKeyStore("keystore.jks", System.getenv("KEYSTORE_PASSWORD"));

final var setwebhook = SetWebhook.builder()
        .url("https://SERVERIPADDRESS:8443")
        .certificate(new InputFile(new File("public_cert.pem")))
        // Uncomment if you want to bypass DNS resolving for IP address
        //.ipAddress("SERVERIPADDRESS")
        .build();

final var telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, webhook);
telegramBotsApi.registerBot(new MyWebhookBot(), setwebhook);
facundopadilla commented 3 years ago

Heroku

Heroku starts on a random port, you need to get a value from the environment property PORT:

final String port = System.getenv("PORT");

final var webhook = new DefaultWebhook();
webhook.setInternalUrl("http://0.0.0.0:" + port);

final var setwebhook = SetWebhook.builder()
        .url("https://yourappname.herokuapp.com")
        .build();

final var telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, webhook);
telegramBotsApi.registerBot(new MyWebhookBot(), setwebhook);

Self-hosted on port 8443 with self-signed certificate

Use certgen.sh to generate a certificate (replace SERVERIPADDRESS with the server's IP address or domain):

JKS=keystore.jks
CERT=public_cert.pem

openssl req -newkey rsa:2048 -sha256 -nodes \
    -keyout private.key -x509 \
    -days 365 \
    -out $CERT \
    -subj "/C=US/ST=Utah/L=Location/O=Organization/CN=SERVERIPADDRESS"

openssl pkcs12 -export \
    -in $CERT \
    -inkey private.key \
    -certfile $CERT \
    -out keystore.p12

keytool -importkeystore \
    -srckeystore keystore.p12 \
    -srcstoretype pkcs12 \
    -sigalg SHA1withRSA \
    -destkeystore $JKS \
    -deststoretype pkcs12

rm keystore.p12 private.key

Export your keystore password (see Setting Environment Variables Wiki section):

export KEYSTORE_PASSWORD=mysupersecretpasswordis123456
final var webhook = new DefaultWebhook();
webhook.setInternalUrl("http://0.0.0.0:8443");
webhook.setKeyStore("keystore.jks", System.getenv("KEYSTORE_PASSWORD"));

final var setwebhook = SetWebhook.builder()
        .url("https://SERVERIPADDRESS:8443")
        .certificate(new InputFile(new File("public_cert.pem")))
        // Uncomment if you want to bypass DNS resolving for IP address
        //.ipAddress("SERVERIPADDRESS")
        .build();

final var telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, webhook);
telegramBotsApi.registerBot(new MyWebhookBot(), setwebhook);

Good morning @aNNiMON , thank you very much for your input and reply, in the end I ended up using "Ngrok" to make the webhook work, and it worked, but I do not understand how to capture just the TelegramWebhook messages, it has an onUpdate method, but when from Telegram servers send the messages received by the bot, it sends them correctly to my endpoint but the bot instance does nothing.

aNNiMON commented 3 years ago

@facundopadilla make sure you set the path for your bot:

@Override
public String getBotPath() {
    return getBotUsername();
}
facundopadilla commented 3 years ago

@facundopadilla make sure you set the path for your bot:

@Override
public String getBotPath() {
    return getBotUsername();
}

I will try it, thank you very much

facundopadilla commented 3 years ago

In the end nothing worked for me, I did the serializers and deserializers by hand, everything worked fine, I used Spring directly to create the endpoint for the webhook and with Jackson I could map the JSON from Telegram and use cURL for sending, I close the issue.

antongrizli commented 3 years ago

Hi,

I did a little investigation and with the latest update the lib is doing the following: final var webhook = new DefaultWebhook(); webhook.setInternalUrl("http://0.0.0.0:" + port); this webhook is used for listening for incoming updates from Telegram.

final var setwebhook = SetWebhook.builder() .url("https://yourappname.herokuapp.com") .build(); the setWebhook you can specify one of the ports 443, 80, 88, or 8443 for URL.

then TelegramBotsApi.registerBot(WebhookBot bot, SetWebhook setWebhook) registers your bot and calling setWebhook to set callback URL for your bot via TelegramAPI, however internally callback URL resolved in the next way, the method WebhookUtils.getBotUrl(String externalUrl, String botPath) returns SetWebhook.url + Constants.WEBHOOK_URL_PATH + WebhookBot.getBotPath(), so as the result you have: https://yourappname.herokuapp.com/callback/<BOT_PATH>

Alternative way would be to manually set callback URL: curl -F "url=https://<YOURDOMAIN.EXAMPLE>/<WEBHOOKLOCATION>" https://api.telegram.org/bot<YOURTOKEN>/setWebhook