[telegram] Add support for custom keyboards#18013
[telegram] Add support for custom keyboards#18013Skinah wants to merge 1 commit intoopenhab:mainfrom
Conversation
Signed-off-by: Matthew Skinner <matt@pcmus.com>
There was a problem hiding this comment.
Pull Request Overview
This PR adds support for custom keyboards in the Telegram binding by implementing new functionality to send and remove custom keyboards with configurable button layouts.
- Adds new keyboard action methods for sending single-row and multi-row button keyboards
- Updates the Telegram bot API dependency from version 7.1.0 to 7.11.0
- Updates documentation to include the new keyboard functionality
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| TelegramActions.java | Implements new keyboard sending methods and imports required classes |
| pom.xml | Updates java-telegram-bot-api dependency version |
| README.md | Documents the new keyboard action methods |
| private boolean removeKeyboard(@ActionInput(name = "isSelective") boolean isSelective) { | ||
| Keyboard replyKeyboardRemove = new ReplyKeyboardRemove(isSelective); | ||
| sendReplyMarkup("324", 34354675676576546L, "foo", replyKeyboardRemove); | ||
| return true; |
There was a problem hiding this comment.
The removeKeyboard method contains hardcoded test values instead of using the actual parameters. This method should accept chatId and message parameters to function correctly.
| private boolean removeKeyboard(@ActionInput(name = "isSelective") boolean isSelective) { | |
| Keyboard replyKeyboardRemove = new ReplyKeyboardRemove(isSelective); | |
| sendReplyMarkup("324", 34354675676576546L, "foo", replyKeyboardRemove); | |
| return true; | |
| private boolean removeKeyboard(@ActionInput(name = "chatId") @Nullable Long chatId, | |
| @ActionInput(name = "message") @Nullable String message, | |
| @ActionInput(name = "isSelective") boolean isSelective) { | |
| Keyboard replyKeyboardRemove = new ReplyKeyboardRemove(isSelective); | |
| if (chatId == null || message == null) { | |
| logger.warn("chatId or message not defined; action skipped."); | |
| return false; | |
| } | |
| return sendReplyMarkup(null, chatId, message, replyKeyboardRemove); |
| if (replyId != null) { | ||
| if (!replyId.contains(" ")) { | ||
| sendMessage.replyMarkup(keyboard); | ||
| } | ||
| } |
There was a problem hiding this comment.
The keyboard is only applied when replyId is not null and doesn't contain spaces. This logic is incorrect - the keyboard should be applied regardless of the replyId condition, as keyboard functionality is independent of reply tracking.
| if (replyId != null) { | |
| if (!replyId.contains(" ")) { | |
| sendMessage.replyMarkup(keyboard); | |
| } | |
| } | |
| sendMessage.replyMarkup(keyboard); |
| if (replyId != null) { | ||
| if (!replyId.contains(" ")) { | ||
| if (buttons.length > 0) { | ||
| KeyboardButton[][] keyboard2D = new KeyboardButton[buttons.length][]; | ||
| for (int i = 0; i < buttons.length; i++) { | ||
| KeyboardButton[] rowOfButtons = new KeyboardButton[buttons[i].length]; | ||
| for (int x = 0; x < buttons[i].length; x++) { | ||
| rowOfButtons[x] = new KeyboardButton(buttons[i][x]); | ||
| } | ||
| keyboard2D[i] = rowOfButtons; | ||
| } | ||
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | ||
| sendMessage.replyMarkup(keyBoardMarkup); | ||
| } else { | ||
| logger.warn( | ||
| "The replyId {} for message {} is given, but no buttons are defined. ReplyKeyboardMarkup will be ignored.", | ||
| replyId, message); | ||
| } | ||
| } else { | ||
| logger.warn("replyId {} must not contain spaces. ReplyKeyboardMarkup will be ignored.", replyId); | ||
| } | ||
| } |
There was a problem hiding this comment.
The keyboard is only applied when replyId is not null and doesn't contain spaces. This logic is incorrect - the keyboard should be applied regardless of the replyId condition, as keyboard functionality is independent of reply tracking.
| if (replyId != null) { | |
| if (!replyId.contains(" ")) { | |
| if (buttons.length > 0) { | |
| KeyboardButton[][] keyboard2D = new KeyboardButton[buttons.length][]; | |
| for (int i = 0; i < buttons.length; i++) { | |
| KeyboardButton[] rowOfButtons = new KeyboardButton[buttons[i].length]; | |
| for (int x = 0; x < buttons[i].length; x++) { | |
| rowOfButtons[x] = new KeyboardButton(buttons[i][x]); | |
| } | |
| keyboard2D[i] = rowOfButtons; | |
| } | |
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | |
| sendMessage.replyMarkup(keyBoardMarkup); | |
| } else { | |
| logger.warn( | |
| "The replyId {} for message {} is given, but no buttons are defined. ReplyKeyboardMarkup will be ignored.", | |
| replyId, message); | |
| } | |
| } else { | |
| logger.warn("replyId {} must not contain spaces. ReplyKeyboardMarkup will be ignored.", replyId); | |
| } | |
| } | |
| // Apply keyboard markup if buttons are provided, regardless of replyId | |
| if (buttons.length > 0) { | |
| KeyboardButton[][] keyboard2D = new KeyboardButton[buttons.length][]; | |
| for (int i = 0; i < buttons.length; i++) { | |
| KeyboardButton[] rowOfButtons = new KeyboardButton[buttons[i].length]; | |
| for (int x = 0; x < buttons[i].length; x++) { | |
| rowOfButtons[x] = new KeyboardButton(buttons[i][x]); | |
| } | |
| keyboard2D[i] = rowOfButtons; | |
| } | |
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | |
| sendMessage.replyMarkup(keyBoardMarkup); | |
| } | |
| // Warn if replyId is present but contains spaces (for reply tracking only) | |
| if (replyId != null && replyId.contains(" ")) { | |
| logger.warn("replyId {} must not contain spaces. Reply tracking will be ignored.", replyId); | |
| } | |
| // Warn if replyId is present but no buttons are defined (optional, can be removed) | |
| if (replyId != null && buttons.length == 0) { | |
| logger.warn( | |
| "The replyId {} for message {} is given, but no buttons are defined.", replyId, message); | |
| } |
| if (replyId != null) { | ||
| if (!replyId.contains(" ")) { | ||
| if (buttons.length > 0) { | ||
| KeyboardButton[][] keyboard2D = new KeyboardButton[1][]; | ||
| KeyboardButton[] keyboard = new KeyboardButton[buttons.length]; | ||
| keyboard2D[0] = keyboard; | ||
| for (int i = 0; i < buttons.length; i++) { | ||
| keyboard[i] = new KeyboardButton(buttons[i]); | ||
| } | ||
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | ||
| sendMessage.replyMarkup(keyBoardMarkup); | ||
| } else { | ||
| logger.warn( | ||
| "The replyId {} for message {} is given, but no buttons are defined. ReplyKeyboardMarkup will be ignored.", | ||
| replyId, message); | ||
| } | ||
| } else { | ||
| logger.warn("replyId {} must not contain spaces. ReplyKeyboardMarkup will be ignored.", replyId); | ||
| } | ||
| } |
There was a problem hiding this comment.
The keyboard is only applied when replyId is not null and doesn't contain spaces. This logic is incorrect - the keyboard should be applied regardless of the replyId condition, as keyboard functionality is independent of reply tracking.
| if (replyId != null) { | |
| if (!replyId.contains(" ")) { | |
| if (buttons.length > 0) { | |
| KeyboardButton[][] keyboard2D = new KeyboardButton[1][]; | |
| KeyboardButton[] keyboard = new KeyboardButton[buttons.length]; | |
| keyboard2D[0] = keyboard; | |
| for (int i = 0; i < buttons.length; i++) { | |
| keyboard[i] = new KeyboardButton(buttons[i]); | |
| } | |
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | |
| sendMessage.replyMarkup(keyBoardMarkup); | |
| } else { | |
| logger.warn( | |
| "The replyId {} for message {} is given, but no buttons are defined. ReplyKeyboardMarkup will be ignored.", | |
| replyId, message); | |
| } | |
| } else { | |
| logger.warn("replyId {} must not contain spaces. ReplyKeyboardMarkup will be ignored.", replyId); | |
| } | |
| } | |
| if (buttons.length > 0) { | |
| KeyboardButton[][] keyboard2D = new KeyboardButton[1][]; | |
| KeyboardButton[] keyboard = new KeyboardButton[buttons.length]; | |
| keyboard2D[0] = keyboard; | |
| for (int i = 0; i < buttons.length; i++) { | |
| keyboard[i] = new KeyboardButton(buttons[i]); | |
| } | |
| ReplyKeyboardMarkup keyBoardMarkup = new ReplyKeyboardMarkup(keyboard2D); | |
| sendMessage.replyMarkup(keyBoardMarkup); | |
| } | |
| if (replyId != null && replyId.contains(" ")) { | |
| logger.warn("replyId {} must not contain spaces. Reply tracking will be ignored.", replyId); | |
| } |
| try { | ||
| retMessage = localHandler.execute(sendMessage); | ||
| } catch (Exception e) { | ||
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); |
There was a problem hiding this comment.
Spelling error: 'occured' should be 'occurred'.
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); | |
| logger.warn("Exception occurred whilst sending message:{}", e.getMessage()); |
| try { | ||
| retMessage = localHandler.execute(sendMessage); | ||
| } catch (Exception e) { | ||
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); |
There was a problem hiding this comment.
Spelling error: 'occured' should be 'occurred'.
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); | |
| logger.warn("Exception occurred whilst sending message:{}", e.getMessage()); |
| try { | ||
| retMessage = localHandler.execute(sendMessage); | ||
| } catch (Exception e) { | ||
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); |
There was a problem hiding this comment.
Spelling error: 'occured' should be 'occurred'.
| logger.warn("Exception occured whilst sending message:{}", e.getMessage()); | |
| logger.warn("Exception occurred whilst sending message:{}", e.getMessage()); |
| return true; | ||
| } | ||
|
|
||
| private boolean sendKeyboard(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message, |
There was a problem hiding this comment.
There is significant code duplication between the two sendKeyboard methods. Consider extracting the common logic into a shared helper method to improve maintainability.
| } | ||
|
|
||
| private boolean sendKeyboard(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message, | ||
| @Nullable String replyId, boolean oneTimeKeyboard, @Nullable String... buttons) { |
There was a problem hiding this comment.
The oneTimeKeyboard parameter is never used in the method implementation. It should be applied to the ReplyKeyboardMarkup to control whether the keyboard disappears after one use.
| } | ||
|
|
||
| private boolean sendKeyboard(@ActionInput(name = "chatId") @Nullable Long chatId, @Nullable String message, | ||
| @Nullable String replyId, boolean oneTimeKeyboard, @Nullable String[]... buttons) { |
There was a problem hiding this comment.
The oneTimeKeyboard parameter is never used in the method implementation. It should be applied to the ReplyKeyboardMarkup to control whether the keyboard disappears after one use.
Adds the following new functionality, just waiting on a milestone build to test the jar under java21.
closes #7279
| sendKeyboard(Long chatId, String message, String replyId, boolean oneTimeKeyboard, String[]... buttons) | Send a custom keyboard with multiple rows of buttons. |
| sendKeyboard(Long chatId, String message, String replyId, boolean oneTimeKeyboard, String... buttons)| Send a custom keyboard with a single row of buttons. |
| removeKeyboard(boolean isSelective) | Remove a custom keyboard. |