rubenlagus / TelegramBots

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

creating WebHookBot using SpringBoot #934

Closed LegendaryZer0 closed 3 years ago

LegendaryZer0 commented 3 years ago

Hi! I am using telegrambots-spring-boot-starter v 5.2.0 and trying register my webHookBot Here's my bot config:

bot.url=https://74e437885ee9.ngrok.io

bot.path=adam

    @Slf4j
@Configuration
public class BotConfig {
  @Value("${bot.url}")
  private String BOT_URL;

  @Bean
  public SetWebhook setWebhookInstance() {
    return SetWebhook.builder().url(BOT_URL).build();
  }
  // Create it as
  @Bean
  public AdamSmithBot adamSmithBot(SetWebhook setWebhookInstance) throws TelegramApiException {
    AdamSmithBot adamSmithBot = new AdamSmithBot(setWebhookInstance);
    //        DefaultWebhook defaultWebhook = new DefaultWebhook();
    //        defaultWebhook.setInternalUrl(BOT_URL);
    //        defaultWebhook.registerWebhook(adamSmithBot);

    TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class);
    log.info("SetWebHook from AdamSmith bot {}", adamSmithBot.getSetWebhook());
    telegramBotsApi.registerBot(adamSmithBot, adamSmithBot.getSetWebhook());
    return adamSmithBot;
  }
}

But it dont working, but when i send this request, it working perfectly and updates recieve to me https://api.telegram.org/MY_TOKEN_HERE/setWebhook?url=https://74e437885ee9.ngrok.io

I think my mistake in BotConfig,but i also publush my other clases bot and controller:

    public class AdamSmithBot extends SpringWebhookBot {
  @Value("${bot.token}")
  private String TOKEN;

  @Value("${bot.name}")
  private String BOT_USERNAME;

  @Value("${bot.path}")
  private String BOT_PATH;

  public AdamSmithBot(SetWebhook setWebhook) {

    super(setWebhook);
  }

  public AdamSmithBot(DefaultBotOptions options, SetWebhook setWebhook) {
    super(options, setWebhook);
  }

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

  @Override
  public String getBotToken() {
    return TOKEN;
  }

  @Override
  public BotApiMethod<?> onWebhookUpdateReceived(Update update) {

    if (update.getMessage() != null && update.getMessage().hasText()) {
      Long chatId = update.getMessage().getChatId();
      try {
        execute(new SendMessage(chatId.toString(), "HI HANDSOME " + update.getMessage().getText()));

      } catch (TelegramApiException e) {
        throw new IllegalStateException(e);
      }
    }
    return null;
  }

  @Override
  public String getBotPath() {
    return "adam";
  }
}

Controller:

    @Slf4j
@RestController
public class WebHookBotRecieveController {
    @Autowired
    private AdamSmithBot adamSmithBot;
    @PostMapping("/")
    public void getUpdate(@RequestBody Update update){
        log.info("some update recieved {}",update.toString());
        adamSmithBot.onWebhookUpdateReceived(update);

    }
    @PostMapping("/callback/adam")
    public void getUpdateWithDifferentUrl(@RequestBody Update update){
        log.info("some update recieved {}",update.toString());
        adamSmithBot.onWebhookUpdateReceived(update);

    }
}

NOTE: I seemd some info here: https://github.com/rubenlagus/TelegramBots/wiki/How-To-Update

On the fifth step you do this: TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, defaultWebhookInstance); but what is it defaultWebhookInstance? When i trying put here DefaultWebhook it produce NullPointerException

But when i trying put DefaultWebhook class instead it produce NullPointerException What i made wrong?

LegendaryZer0 commented 3 years ago

EDIT : I refactored some code @Value("${bot.url}") private String BOT_URL; - where was null value (fixed),reloaded library, but now i have that exception:

Caused by: javax.ws.rs.ProcessingException: Failed to start Grizzly HTTP server: Cannot assign requested address: bind
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:270) ~[jersey-container-grizzly2-http-2.33.jar:na]
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:93) ~[jersey-container-grizzly2-http-2.33.jar:na]
    at org.telegram.telegrambots.updatesreceivers.DefaultWebhook.startServer(DefaultWebhook.java:64) ~[telegrambots-5.2.0.jar:na]
    at org.telegram.telegrambots.meta.TelegramBotsApi.<init>(TelegramBotsApi.java:50) ~[telegrambots-meta-5.2.0.jar:na]
    at ru.website.selenium.bot.telegram.config.BotConfig.adamSmithBot(BotConfig.java:44) ~[classes/:na]
    at ru.website.selenium.bot.telegram.config.BotConfig$$EnhancerBySpringCGLIB$$4eb8259d.CGLIB$adamSmithBot$0(<generated>) ~[classes/:na]
    at ru.website.selenium.bot.telegram.config.BotConfig$$EnhancerBySpringCGLIB$$4eb8259d$$FastClassBySpringCGLIB$$1e185cfd.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.3.8.jar:5.3.8]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.3.8.jar:5.3.8]
    at ru.website.selenium.bot.telegram.config.BotConfig$$EnhancerBySpringCGLIB$$4eb8259d.adamSmithBot(<generated>) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.8.jar:5.3.8]
    ... 39 common frames omitted
Caused by: java.net.BindException: Cannot assign requested address: bind
    at java.base/sun.nio.ch.Net.bind0(Native Method) ~[na:na]
    at java.base/sun.nio.ch.Net.bind(Net.java:461) ~[na:na]
    at java.base/sun.nio.ch.Net.bind(Net.java:453) ~[na:na]
    at java.base/sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:227) ~[na:na]
    at java.base/sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:80) ~[na:na]
    at org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler.bindToChannelAndAddress(TCPNIOBindingHandler.java:107) ~[grizzly-framework-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.nio.transport.TCPNIOBindingHandler.bind(TCPNIOBindingHandler.java:64) ~[grizzly-framework-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:215) ~[grizzly-framework-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:195) ~[grizzly-framework-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.bind(TCPNIOTransport.java:186) ~[grizzly-framework-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.http.server.NetworkListener.start(NetworkListener.java:711) ~[grizzly-http-server-2.4.4.jar:2.4.4]
    at org.glassfish.grizzly.http.server.HttpServer.start(HttpServer.java:256) ~[grizzly-http-server-2.4.4.jar:2.4.4]
    at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:267) ~[jersey-container-grizzly2-http-2.33.jar:na]
    ... 53 common frames omitted

Process finished with exit code 0
LegendaryZer0 commented 3 years ago

Okay, I get it, I didn't know that TelegramBotApi runs a grizzly server underneath and that was the reason for my mistakes For those who ever come here with the same problem, I will describe the solution : Let's start with the fact that for the test on localhost we need a public address , e.g. ngrok , then we have to do the following, start grizzli on , let's say port 80 (localhost), specify the correct ngrok url , and then run spring boot application on , let's say port 8080, examples in the code

For ex. ngrok forwarding: Forwarding http://b44ecce72666.ngrok.io -> http://localhost:80 Forwarding https://b44ecce72666.ngrok.io -> http://localhost:80

@Slf4j
@Configuration
public class BotConfig {
//  @Value("${bot.url}")
//  @NotNull
//  @NotEmpty
//  private String BOT_URL;

  @Value("${bot.token}")
  @NotNull
  @NotEmpty
  private String TOKEN;

  @Value("${bot.name}")
  @NotNull
  @NotEmpty
  private String BOT_USERNAME;

  @Bean
  public SetWebhook setWebhookInstance() {
    return SetWebhook.builder().url("https://b44ecce72666.ngrok.io").build();
  } // public address, now it is ngrok, in the future it will (i think) be the server address
  // Create it as
  @Bean
  public AdamSmithBot adamSmithBot(SetWebhook setWebhookInstance) throws TelegramApiException {

    AdamSmithBot adamSmithBot = new AdamSmithBot(setWebhookInstance);
    adamSmithBot.setBOT_USERNAME(BOT_USERNAME);
    adamSmithBot.setTOKEN(TOKEN);
    adamSmithBot.setBOT_PATH("adam");
    DefaultWebhook defaultWebhook = new DefaultWebhook();

    defaultWebhook.setInternalUrl(
        "http://localhost:80"); // the port to start the server, on the localhost computer, on the server it
                                // be the server address
    //   defaultWebhook.registerWebhook(adamSmithBot);

    TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class, defaultWebhook);

    log.info("SetWebHook from AdamSmith bot {}", setWebhookInstance);
    adamSmithBot.getBotUsername();
    telegramBotsApi.registerBot(adamSmithBot, setWebhookInstance);
    return adamSmithBot;

  }
}

Next is code of webhook bot:

@Slf4j
@Setter
public class AdamSmithBot extends SpringWebhookBot {
  @Value("${bot.token}")
  private String TOKEN;

  @Value("${bot.name}")
  private String BOT_USERNAME;

  @Value("${bot.path}")
  private String BOT_PATH;
  @Autowired
  private MainService mainService;

  public AdamSmithBot(SetWebhook setWebhook) {

    super(setWebhook);
  }

  public AdamSmithBot(DefaultBotOptions options, SetWebhook setWebhook) {
    super(options, setWebhook);
  }

  @Override
  public String getBotUsername() {

    log.info("BOT_USERNAME FROM ADAM BOT {}",BOT_USERNAME);
    return BOT_USERNAME;
  }

  @Override
  public String getBotToken() {
    return TOKEN;
  }

  @Override
  public BotApiMethod<?> onWebhookUpdateReceived(Update update) { //All messages coming from the grizzly server will trigger this method
    log.info("Message teext {}",update.toString());
    if (update.getMessage() != null && update.getMessage().hasText()) {
      Long chatId = update.getMessage().getChatId();

      List<PartialBotApiMethod<Message>> listOfCommands=  mainService.receiveUpdate(update);
        listOfCommands.forEach(x->{
          try {
          if(x instanceof SendMessage){

              execute((SendMessage)x);

          }
          if(x instanceof SendPhoto){
            execute((SendPhoto) x);
          }
          } catch (TelegramApiException e) {
            e.printStackTrace();
          }
        });

    }
    return null;
  }

  @Override
  public String getBotPath() {
    return "adam"; //any other path here
  }
}

My code is excessive in some places, and too fancy, but it's not hard to figure out. If you have any suggestions on how to make it better, I'd love to hear them.

EDIT: in that example, if you start spring boot server on 8080 port ngrok urls must be:

Forwarding http://b44ecce72666.ngrok.io -> http://localhost:8080 Forwarding https://b44ecce72666.ngrok.io -> http://localhost:8080

One might wonder, why do we need ngrok? - The point is that if we just specify localhost, Telegram will not understand where to send messages. if you already have a server with a public ip address, use it.

ichromanrd commented 3 years ago

Hi @LegendaryZer0 , I am following your codes here. But I still didn't know, what's the contents of MainService here? Could you please give me some lead on this?

ichromanrd commented 3 years ago

Ah sorry, just realized MainService is just a parser for the update object. Means we can directly process whatever update object contains. Your codes save my day. thanks! ✌️