├── src └── main │ ├── java │ └── org │ │ └── telegram │ │ ├── structure │ │ └── WeatherAlert.java │ │ ├── services │ │ ├── CustomTimerTask.java │ │ ├── TimerExecutor.java │ │ ├── DirectionsService.java │ │ ├── Emoji.java │ │ ├── LocalisationService.java │ │ ├── RaeService.java │ │ └── WeatherService.java │ │ ├── BotConfig.java │ │ ├── commands │ │ ├── StopCommand.java │ │ ├── StartCommand.java │ │ ├── HelloCommand.java │ │ └── HelpCommand.java │ │ ├── BuildVars.java │ │ ├── Commands.java │ │ ├── Main.java │ │ ├── updateshandlers │ │ ├── WebHookExampleHandlers.java │ │ ├── CommandsHandler.java │ │ ├── RaeHandlers.java │ │ ├── ChannelHandlers.java │ │ ├── ElektrollArtFanHandler.java │ │ ├── DirectionsHandlers.java │ │ └── FilesHandlers.java │ │ └── database │ │ ├── CreationStrings.java │ │ ├── ConnectionDB.java │ │ └── DatabaseManager.java │ └── resources │ ├── strings_es.properties │ ├── strings.properties │ ├── strings_pt.properties │ ├── strings_eo.properties │ ├── strings_it.properties │ └── strings_nl.properties ├── .gitignore ├── .travis.yml ├── eclipse configuration.md ├── README.md ├── HOWTO.md └── pom.xml /src/main/java/org/telegram/structure/WeatherAlert.java: -------------------------------------------------------------------------------- 1 | package org.telegram.structure; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author Ruben Bermudez 7 | * @version 1.0 8 | * Weather Alert representation 9 | */ 10 | @Data 11 | public class WeatherAlert { 12 | private int id; 13 | private long userId; 14 | private int cityId; 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Maven template 2 | target/ 3 | pom.xml.tag 4 | pom.xml.releaseBackup 5 | pom.xml.versionsBackup 6 | pom.xml.next 7 | release.properties 8 | dependency-reduced-pom.xml 9 | buildNumber.properties 10 | .mvn/timing.properties 11 | ### Java template 12 | *.class 13 | 14 | # Mobile Tools for Java (J2ME) 15 | .mtj.tmp/ 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.ear 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | ### IDE files 26 | .idea/ 27 | copyright/ 28 | *.iml 29 | 30 | #File System specific files 31 | .DS_STORE 32 | 33 | # logs files 34 | *.log 35 | 36 | # Certs 37 | *.jks 38 | *.cer 39 | *.pem 40 | 41 | # Idea 42 | *.ipr 43 | *.iws 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | script: mvn clean compile package 5 | notifications: 6 | webhooks: 7 | secure: "L4E4xS1JJcnUDG2oQtBrz/rGxNK8dEbKIRjH6nNHZRGEjGUQh0ECWTEMotX3GDNZ4iIERwZuUS2saLqytlISHPTbpKegZX+YaVk8s9Zuhm/LgrcqtBp7GaxJj+jUx/TdqL/jjuwmstMgzfmMmsx5ALaukAHr1kvP2188XxEiEL9HyORb3hHHDFNOQCsu/evLnewNs1NnHd+OOGm8YLW1ztxE6z5tMx8nvk7jqHEfSkf3KfbZ6QI7fNcelW26b5bPAKyM3b+99Fyz0rXGRSpYw8x15EB4n7hXwQREoL+iZyuF6mKjsdsndiyFoN7UMfKYsQY31OQQKPpcU8OkuXtNqq6F2zfMacQyCfuYYd7szDykI3C4RYVY76PMI1Eym4C4kRYTeTsoyQyp8Lp6daPM0akOCKXvzI7xc9Cp3/EXtRPjncXTA5LJkLiuMwi0BOFBetoei4nOnLPlR1fTwxLVqx13siWl13aIXNIk4axD0PAmi4IAVmsJOLRWxCEvSgS5MQ+M0AZibilQOsZlTipbRovmhe+DkMZifU/mjTkF9DhLjMl1HawFAClell3JcU7IceHwfkZDGE036yvtUi3axcvyR4Sr7qMRno9MOkSbhKp3gQqTVCd7arZrKMhdAYf7PIu9IDUs2+2zlZp2pq2OlMc9c5Z+tXgcX+JqzB3GlHc=" 8 | email: false -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/CustomTimerTask.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * @author Ruben Bermudez 8 | * @version 2.0 9 | * Task to be executed periodically 10 | */ 11 | @Setter 12 | @Getter 13 | public abstract class CustomTimerTask { 14 | private String taskName = ""; ///< Task name 15 | private int times = 1; 16 | 17 | /** 18 | * Constructor 19 | * 20 | * @param taskName Name of the task 21 | */ 22 | public CustomTimerTask(String taskName, int times) { 23 | this.taskName = taskName; 24 | this.times = times; 25 | } 26 | 27 | public void reduceTimes() { 28 | if (this.times > 0) { 29 | this.times -= 1; 30 | } 31 | } 32 | 33 | /** 34 | * @abstract Should contain the functionality of the task 35 | */ 36 | public abstract void execute(); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/BotConfig.java: -------------------------------------------------------------------------------- 1 | package org.telegram; 2 | 3 | /** 4 | * @author Ruben Bermudez 5 | * @version 1.0 6 | * @brief Bots configurations 7 | * @date 20 of June of 2015 8 | */ 9 | public class BotConfig { 10 | public static final String WEATHER_TOKEN = ""; 11 | public static final String WEATHER_USER = "weatherbot"; 12 | 13 | public static final String TRANSIFEX_TOKEN = ""; 14 | public static final String TRANSIFEX_USER = "TGlanguagesbot"; 15 | 16 | public static final String FILES_TOKEN = ""; 17 | public static final String FILES_USER = "filesbot"; 18 | 19 | public static final String DIRECTIONS_TOKEN = ""; 20 | public static final String DIRECTIONS_USER = "directionsbot"; 21 | 22 | public static final String CHANNEL_TOKEN = ""; 23 | public static final String CHANNEL_USER = "channelupdatesbot"; 24 | 25 | public static final String RAE_TOKEN = ""; 26 | public static final String RAE_USER = "raebot"; 27 | 28 | public static final String WEBHOOK_TOKEN = ""; 29 | public static final String WEBHOOK_USER = "webhooksamplebot"; 30 | 31 | public static final String COMMANDS_TOKEN = ""; 32 | public static final String COMMANDS_USER = "MyCommandsBot"; 33 | 34 | public static final String ELEKTROLLART_TOKEN = ""; 35 | public static final String ELEKTROLLART_USER = "ElektrollArtFanBot"; 36 | } 37 | -------------------------------------------------------------------------------- /eclipse configuration.md: -------------------------------------------------------------------------------- 1 | # How to use TelegramBots with Eclipse 2 | 3 | 4 | ### Step 1: Install Maven 5 | To get started, you need to install the Maven-Plugin for Eclipse. Click on Help > Eclipse Marketplace. After it finished loading, search for “Maven”. 6 | Now install **“Maven (Java EE) Integration for Eclipse WTP”**. You have to restart, after the installation is completed. 7 | 8 | ### Step 2: Download the Project 9 | Now you need to get the project itself. 10 | Visit [TelegramBots - github.com][1] and choose “Download Zip”, to download the zip-file. [Here] is a direct link if you are lazy. 11 | 12 | ### Step 3: Create folder 13 | Now let’s setup the project-folder. Go to your Eclipse-Workspace and create a folder (name it whatever you want it to be called). I just named it **“TelegramBotApi”**. Afterwards copy everything from the zip-file into that folder. 14 | 15 | ### Step 4: Import project 16 | To import your project into the workspace you have to go to File > Import > (Folder) Maven > Existing Maven Projects > Next. Choose the folder, you have created in Step 3. In my case: **“TelegramBotApi”**. Click on finish and let Maven import the dependencies. 17 | 18 | ### Step 5: Setting the compliance level 19 | In this last step you need to change the Compiler compliance level. To do this right-click on your Project (in my case **“TelegramBotApi”**) > Properties > Java Compiler. Uncheck the “Use compliance from…” if necessary and set it to 1.7 20 | 21 | *Now you are done. Everything should work fine. You need to set your Bot-Token in the BotConfig.java, afterwards you can run Main.java to check.* 22 | 23 | **For a better intstruction sheet, visit [Google Drive]** 24 | 25 | [Google Drive]:https://goo.gl/5jd40w 26 | [here]:https://github.com/rubenlagus/TelegramBots/archive/master.zip 27 | [1]:https://github.com/rubenlagus/TelegramBots/ 28 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/commands/StopCommand.java: -------------------------------------------------------------------------------- 1 | package org.telegram.commands; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.database.DatabaseManager; 5 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; 6 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 7 | import org.telegram.telegrambots.meta.api.objects.Chat; 8 | import org.telegram.telegrambots.meta.api.objects.User; 9 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 10 | import org.telegram.telegrambots.meta.generics.TelegramClient; 11 | 12 | /** 13 | * This commands stops the conversation with the bot. 14 | * Bot won't respond to user until he sends a start command 15 | * 16 | * @author Timo Schulz (Mit0x2) 17 | */ 18 | @Slf4j 19 | public class StopCommand extends BotCommand { 20 | 21 | public static final String LOGTAG = "STOPCOMMAND"; 22 | 23 | /** 24 | * Construct 25 | */ 26 | public StopCommand() { 27 | super("stop", "With this command you can stop the Bot"); 28 | } 29 | 30 | @Override 31 | public void execute(TelegramClient telegramClient, User user, Chat chat, String[] arguments) { 32 | DatabaseManager dbManager = DatabaseManager.getInstance(); 33 | 34 | if (dbManager.getUserStateForCommandsBot(user.getId())) { 35 | dbManager.setUserStateForCommandsBot(user.getId(), false); 36 | String userName = user.getFirstName() + " " + user.getLastName(); 37 | 38 | SendMessage answer = new SendMessage(chat.getId().toString(), "Good bye " + userName + "\n" + "Hope to see you soon!"); 39 | 40 | try { 41 | telegramClient.execute(answer); 42 | } catch (TelegramApiException e) { 43 | log.error("Error", e); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/BuildVars.java: -------------------------------------------------------------------------------- 1 | package org.telegram; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author Ruben Bermudez 8 | * @version 1.0 9 | * @brief Custom build vars FILL EVERYTHING CORRECTLY 10 | * @date 20 of June of 2015 11 | */ 12 | 13 | public class BuildVars { 14 | public static final Boolean debug = true; 15 | public static final Boolean useWebHook = false; 16 | public static final int PORT = 8443; 17 | public static final String EXTERNALWEBHOOKURL = "https://example.changeme.com:" + PORT; // https://(xyz.)externaldomain.tld 18 | public static final String INTERNALWEBHOOKURL = "https://localhost.changeme.com:" + PORT; // https://(xyz.)localip/domain(.tld) 19 | public static final String pathToCertificatePublicKey = "./YOURPEM.pem"; //only for self-signed webhooks 20 | public static final String pathToCertificateStore = "./YOURSTORE.jks"; //self-signed and non-self-signed. 21 | public static final String certificateStorePassword = "yourpass"; //password for your certificate-store 22 | 23 | public static final String OPENWEATHERAPIKEY = ""; 24 | 25 | public static final String DirectionsApiKey = ""; 26 | 27 | public static final String TRANSIFEXUSER = ""; 28 | public static final String TRANSIFEXPASSWORD = ""; 29 | public static final List ADMINS = new ArrayList<>(); 30 | 31 | public static final String pathToLogs = "./"; 32 | 33 | public static final String linkDB = "jdbc:mysql://localhost:3306/YOURDATABSENAME?useUnicode=true&characterEncoding=UTF-8"; 34 | public static final String controllerDB = "com.mysql.cj.jdbc.Driver"; 35 | public static final String userDB = ""; 36 | public static final String password = ""; 37 | 38 | static { 39 | // Add elements to ADMIN array here 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/commands/StartCommand.java: -------------------------------------------------------------------------------- 1 | package org.telegram.commands; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.database.DatabaseManager; 5 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; 6 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 7 | import org.telegram.telegrambots.meta.api.objects.Chat; 8 | import org.telegram.telegrambots.meta.api.objects.User; 9 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 10 | import org.telegram.telegrambots.meta.generics.TelegramClient; 11 | 12 | /** 13 | * This commands starts the conversation with the bot 14 | * 15 | * @author Timo Schulz (Mit0x2) 16 | */ 17 | @Slf4j 18 | public class StartCommand extends BotCommand { 19 | public StartCommand() { 20 | super("start", "With this command you can start the Bot"); 21 | } 22 | 23 | @Override 24 | public void execute(TelegramClient telegramClient, User user, Chat chat, String[] strings) { 25 | DatabaseManager databseManager = DatabaseManager.getInstance(); 26 | StringBuilder messageBuilder = new StringBuilder(); 27 | 28 | String userName = user.getFirstName() + " " + user.getLastName(); 29 | 30 | if (databseManager.getUserStateForCommandsBot(user.getId())) { 31 | messageBuilder.append("Hi ").append(userName).append("\n"); 32 | messageBuilder.append("i think we know each other already!"); 33 | } else { 34 | databseManager.setUserStateForCommandsBot(user.getId(), true); 35 | messageBuilder.append("Welcome ").append(userName).append("\n"); 36 | messageBuilder.append("this bot will demonstrate you the command feature of the Java TelegramBots API!"); 37 | } 38 | 39 | SendMessage answer = new SendMessage(chat.getId().toString(), messageBuilder.toString()); 40 | 41 | try { 42 | telegramClient.execute(answer); 43 | } catch (TelegramApiException e) { 44 | log.error("Error", e); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Telegram Bot Java Library 2 | [![Build Status](https://travis-ci.org/rubenlagus/TelegramBotsExample.svg?branch=master)](https://travis-ci.org/rubenlagus/TelegramBotsExample) 3 | [![Telegram](http://trellobot.doomdns.org/telegrambadge.svg)](https://telegram.me/JavaBotsApi) 4 | 5 | Samples for [TelegramBots](https://github.com/rubenlagus/TelegramBots) library 6 | 7 | ## Translations 8 | Bots are now supporting multilanguage. If you want to add your own, feel free to translate at [transifex](https://www.transifex.com/projects/p/telegrambots/) 9 | 10 | ## Contributions 11 | Feel free to fork this project, work on it and then make a pull request agains **dev** branch. 12 | 13 | Most of the times I will accept them if they add something valuable to the code. 14 | 15 | Please, **DO NOT PUSH ANY TOKEN OR API KEY**, I will never accept a pull request with that content. 16 | 17 | ## Telegram Bot API 18 | This library use [Telegram bot API](https://core.telegram.org/bots), you can find more information following the link. 19 | 20 | ## Questions or Suggestions 21 | Feel free to create issues [here](https://github.com/rubenlagus/TelegramBots/issues) as you need 22 | 23 | ## Usage with eclipse 24 | 25 | Follow the steps created by Rico [here](https://github.com/rubenlagus/TelegramBots/blob/master/eclipse%20configuration.md) 26 | 27 | ## Tutorial 28 | Short tutorial how to make a simple Echo Bot is available [here](HOWTO.md) 29 | 30 | ## License 31 | 32 | This program is free software: you can redistribute it and/or modify 33 | it under the terms of the GNU General Public License as published by 34 | the Free Software Foundation, either version 3 of the License, or 35 | (at your option) any later version. 36 | 37 | This program is distributed in the hope that it will be useful, 38 | but WITHOUT ANY WARRANTY; without even the implied warranty of 39 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 40 | GNU General Public License for more details. 41 | 42 | You should have received a copy of the GNU General Public License 43 | along with this program. If not, see . 44 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/commands/HelloCommand.java: -------------------------------------------------------------------------------- 1 | package org.telegram.commands; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.database.DatabaseManager; 5 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; 6 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 7 | import org.telegram.telegrambots.meta.api.objects.Chat; 8 | import org.telegram.telegrambots.meta.api.objects.User; 9 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 10 | import org.telegram.telegrambots.meta.generics.TelegramClient; 11 | 12 | /** 13 | * This command simply replies with a hello to the users command and 14 | * sends them the 'kind' words back, which they send via command parameters 15 | * 16 | * @author Timo Schulz (Mit0x2) 17 | */ 18 | @Slf4j 19 | public class HelloCommand extends BotCommand { 20 | 21 | private static final String LOGTAG = "HELLOCOMMAND"; 22 | 23 | public HelloCommand() { 24 | super("hello", "Say hallo to this bot"); 25 | } 26 | 27 | @Override 28 | public void execute(TelegramClient telegramClient, User user, Chat chat, String[] arguments) { 29 | 30 | if (!DatabaseManager.getInstance().getUserStateForCommandsBot(user.getId())) { 31 | return; 32 | } 33 | 34 | String userName = chat.getUserName(); 35 | if (userName == null || userName.isEmpty()) { 36 | userName = user.getFirstName() + " " + user.getLastName(); 37 | } 38 | 39 | StringBuilder messageTextBuilder = new StringBuilder("Hello ").append(userName); 40 | if (arguments != null && arguments.length > 0) { 41 | messageTextBuilder.append("\n"); 42 | messageTextBuilder.append("Thank you so much for your kind words:\n"); 43 | messageTextBuilder.append(String.join(" ", arguments)); 44 | } 45 | 46 | SendMessage answer = new SendMessage(chat.getId().toString(), messageTextBuilder.toString()); 47 | 48 | try { 49 | telegramClient.execute(answer); 50 | } catch (TelegramApiException e) { 51 | log.error("Error", e); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/main/java/org/telegram/Commands.java: -------------------------------------------------------------------------------- 1 | package org.telegram; 2 | 3 | /** 4 | * @author Ruben Bermudez 5 | * @version 1.0 6 | * @brief Command for the bots 7 | * @date 20 of June of 2015 8 | */ 9 | public class Commands { 10 | public static final String commandInitChar = "/"; 11 | /// Transifex iOS command 12 | public static final String transifexiOSCommand = commandInitChar + "langios"; 13 | /// Transifex android command 14 | public static final String transifexAndroidCommand = commandInitChar + "langdroid"; 15 | /// Transifex android command 16 | public static final String transifexTDesktop = commandInitChar + "langdesk"; 17 | /// Transifex android command 18 | public static final String transifexWebogram = commandInitChar + "langweb"; 19 | /// Transifex android command 20 | public static final String transifexWP = commandInitChar + "langwp"; 21 | /// Transifex android command 22 | public static final String transifexOSX = commandInitChar + "langosx"; 23 | /// Transifex android support command 24 | public static final String transifexAndroidSupportCommand = commandInitChar + "langtestdroid"; 25 | /// Help command 26 | public static final String help = commandInitChar + "help"; 27 | /// Upload command 28 | public static final String uploadCommand = commandInitChar + "upload"; 29 | /// Report command 30 | public static final String reportCommand = commandInitChar + "report"; 31 | /// Start command 32 | public static final String startCommand = commandInitChar + "start"; 33 | /// Cancel command 34 | public static final String cancelCommand = commandInitChar + "cancel"; 35 | /// Delete command 36 | public static final String deleteCommand = commandInitChar + "delete"; 37 | /// List command 38 | public static final String listCommand = commandInitChar + "list"; 39 | /// Start directions command 40 | public static final String startDirectionCommand = commandInitChar + "directions"; 41 | /// Set Language command 42 | public static final String setLanguageCommand = commandInitChar + "language"; 43 | 44 | public static final String STOPCOMMAND = commandInitChar + "stop"; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/commands/HelpCommand.java: -------------------------------------------------------------------------------- 1 | package org.telegram.commands; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.database.DatabaseManager; 5 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.BotCommand; 6 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.IBotCommand; 7 | import org.telegram.telegrambots.extensions.bots.commandbot.commands.ICommandRegistry; 8 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 9 | import org.telegram.telegrambots.meta.api.objects.Chat; 10 | import org.telegram.telegrambots.meta.api.objects.User; 11 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 12 | import org.telegram.telegrambots.meta.generics.TelegramClient; 13 | 14 | /** 15 | * This command helps the user to find the command they need 16 | * 17 | * @author Timo Schulz (Mit0x2) 18 | */ 19 | @Slf4j 20 | public class HelpCommand extends BotCommand { 21 | 22 | private static final String LOGTAG = "HELPCOMMAND"; 23 | 24 | private final ICommandRegistry commandRegistry; 25 | 26 | public HelpCommand(ICommandRegistry commandRegistry) { 27 | super("help", "Get all the commands this bot provides"); 28 | this.commandRegistry = commandRegistry; 29 | } 30 | 31 | @Override 32 | public void execute(TelegramClient telegramClient, User user, Chat chat, String[] strings) { 33 | 34 | if (!DatabaseManager.getInstance().getUserStateForCommandsBot(user.getId())) { 35 | return; 36 | } 37 | 38 | StringBuilder helpMessageBuilder = new StringBuilder("Help\n"); 39 | helpMessageBuilder.append("These are the registered commands for this Bot:\n\n"); 40 | 41 | for (IBotCommand botCommand : commandRegistry.getRegisteredCommands()) { 42 | helpMessageBuilder.append(botCommand.toString()).append("\n\n"); 43 | } 44 | 45 | SendMessage helpMessage = new SendMessage(chat.getId().toString(), helpMessageBuilder.toString()); 46 | helpMessage.enableHtml(true); 47 | 48 | try { 49 | telegramClient.execute(helpMessage); 50 | } catch (TelegramApiException e) { 51 | log.error("Error", e); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/Main.java: -------------------------------------------------------------------------------- 1 | package org.telegram; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.telegrambots.longpolling.TelegramBotsLongPollingApplication; 5 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 6 | import org.telegram.telegrambots.webhook.TelegramBotsWebhookApplication; 7 | import org.telegram.telegrambots.webhook.WebhookOptions; 8 | import org.telegram.updateshandlers.ChannelHandlers; 9 | import org.telegram.updateshandlers.CommandsHandler; 10 | import org.telegram.updateshandlers.DirectionsHandlers; 11 | import org.telegram.updateshandlers.ElektrollArtFanHandler; 12 | import org.telegram.updateshandlers.FilesHandlers; 13 | import org.telegram.updateshandlers.RaeHandlers; 14 | import org.telegram.updateshandlers.WeatherHandlers; 15 | import org.telegram.updateshandlers.WebHookExampleHandlers; 16 | 17 | /** 18 | * @author Ruben Bermudez 19 | * @version 1.0 20 | * Main class to create all bots 21 | */ 22 | @Slf4j 23 | public class Main { 24 | public static void main(String[] args) { 25 | try (TelegramBotsWebhookApplication webhookApplication = new TelegramBotsWebhookApplication(WebhookOptions.builder().enableRequestLogging(true).build())) { 26 | webhookApplication.registerBot(new WebHookExampleHandlers(BotConfig.WEBHOOK_TOKEN)); 27 | try (TelegramBotsLongPollingApplication botsApplication = new TelegramBotsLongPollingApplication()) { 28 | botsApplication.registerBot(BotConfig.WEATHER_TOKEN, new WeatherHandlers(BotConfig.WEATHER_TOKEN)); 29 | botsApplication.registerBot(BotConfig.CHANNEL_TOKEN, new ChannelHandlers(BotConfig.CHANNEL_TOKEN)); 30 | botsApplication.registerBot(BotConfig.COMMANDS_TOKEN, new CommandsHandler(BotConfig.COMMANDS_TOKEN, BotConfig.COMMANDS_USER)); 31 | botsApplication.registerBot(BotConfig.DIRECTIONS_TOKEN, new DirectionsHandlers(BotConfig.DIRECTIONS_TOKEN)); 32 | botsApplication.registerBot(BotConfig.ELEKTROLLART_TOKEN, new ElektrollArtFanHandler(BotConfig.ELEKTROLLART_TOKEN)); 33 | botsApplication.registerBot(BotConfig.FILES_TOKEN, new FilesHandlers(BotConfig.FILES_TOKEN)); 34 | botsApplication.registerBot(BotConfig.RAE_TOKEN, new RaeHandlers(BotConfig.RAE_TOKEN)); 35 | Thread.currentThread().join(); 36 | } catch (Exception e) { 37 | log.error("Error registering bot", e); 38 | } 39 | } catch (TelegramApiException e) { 40 | log.error("Error registering bot", e); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/WebHookExampleHandlers.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.BotConfig; 5 | import org.telegram.BuildVars; 6 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 7 | import org.telegram.telegrambots.meta.api.methods.botapimethods.BotApiMethod; 8 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 9 | import org.telegram.telegrambots.meta.api.methods.updates.DeleteWebhook; 10 | import org.telegram.telegrambots.meta.api.methods.updates.SetWebhook; 11 | import org.telegram.telegrambots.meta.api.objects.Update; 12 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 13 | import org.telegram.telegrambots.meta.generics.TelegramClient; 14 | import org.telegram.telegrambots.webhook.TelegramWebhookBot; 15 | 16 | /** 17 | * @author pithera 18 | * @version 1.0 19 | * Simple Webhook example 20 | */ 21 | @Slf4j 22 | public class WebHookExampleHandlers implements TelegramWebhookBot { 23 | private final TelegramClient telegramClient; 24 | 25 | public WebHookExampleHandlers(String botToken) { 26 | telegramClient = new OkHttpTelegramClient(botToken); 27 | } 28 | 29 | @Override 30 | public BotApiMethod consumeUpdate(Update update) { 31 | if (update.hasMessage() && update.getMessage().hasText()) { 32 | SendMessage sendMessage = SendMessage 33 | .builder() 34 | .chatId(update.getMessage().getChatId().toString()) 35 | .text("Well, all information looks like noise until you break the code.") 36 | .build(); 37 | return sendMessage; 38 | } 39 | return null; 40 | } 41 | 42 | @Override 43 | public void runDeleteWebhook() { 44 | try { 45 | telegramClient.execute(new DeleteWebhook()); 46 | } catch (TelegramApiException e) { 47 | log.info("Error deleting webhook"); 48 | } 49 | } 50 | 51 | @Override 52 | public void runSetWebhook() { 53 | try { 54 | telegramClient.execute(SetWebhook 55 | .builder() 56 | .url(BuildVars.EXTERNALWEBHOOKURL + getBotPath()) 57 | .build()); 58 | } catch (TelegramApiException e) { 59 | log.info("Error setting webhook"); 60 | } 61 | } 62 | 63 | @Override 64 | public String getBotPath() { 65 | return "/" + BotConfig.WEBHOOK_USER; //arbitrary path to deliver updates on, username is an example. 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/database/CreationStrings.java: -------------------------------------------------------------------------------- 1 | package org.telegram.database; 2 | 3 | /** 4 | * @author Ruben Bermudez 5 | * @version 2.0 6 | * Strings to create database 7 | */ 8 | public class CreationStrings { 9 | public static final int version = 8; 10 | public static final String createVersionTable = "CREATE TABLE IF NOT EXISTS Versions(ID INTEGER PRIMARY KEY AUTO_INCREMENT, Version INTEGER);"; 11 | public static final String insertCurrentVersion = "INSERT IGNORE INTO Versions (Version) VALUES(%d);"; 12 | public static final String createFilesTable = "CREATE TABLE IF NOT EXISTS Files (fileId VARCHAR(100) PRIMARY KEY, userId BIGINT NOT NULL, caption TEXT NOT NULL)"; 13 | public static final String createUsersForFilesTable = "CREATE TABLE IF NOT EXISTS FilesUsers (userId BIGINT PRIMARY KEY, status INTEGER NOT NULL DEFAULT 0)"; 14 | public static final String createRecentWeatherTable = "CREATE TABLE IF NOT EXISTS RecentWeather (ID INTEGER PRIMARY KEY AUTO_INCREMENT, userId BIGINT NOT NULL, " + 15 | "date TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, cityId INTEGER NOT NULL, cityName VARCHAR(60) NOT NULL," + 16 | "CONSTRAINT unique_cistyuser UNIQUE (userId,cityId))"; 17 | public static final String createDirectionsDatabase = "CREATE TABLE IF NOT EXISTS Directions (userId BIGINT PRIMARY KEY, status INTEGER NOT NULL, " + 18 | "messageId INTEGER NOT NULL DEFAULT 0, origin VARCHAR(100));"; 19 | public static final String createLastUpdateDatabase = "CREATE TABLE IF NOT EXISTS LastUpdate (token VARCHAR(125) PRIMARY KEY, updateId INTEGER NOT NULL DEFAULT -1);"; 20 | public static final String createUserLanguageDatabase = "CREATE TABLE IF NOT EXISTS UserLanguage (userId BIGINT PRIMARY KEY, languageCode VARCHAR(10) NOT NULL)"; 21 | public static final String createUserWeatherOptionDatabase = "CREATE TABLE IF NOT EXISTS UserWeatherOptions (userId BIGINT PRIMARY KEY, languageCode VARCHAR(10) NOT NULL DEFAULT 'en', " + 22 | "units VARCHAR(10) NOT NULL DEFAULT 'metric')"; 23 | public static final String createWeatherStateTable = "CREATE TABLE IF NOT EXISTS WeatherState (userId BIGINT NOT NULL, chatId BIGINT NOT NULL, state INTEGER NOT NULL DEFAULT 0, " + 24 | "languageCode VARCHAR(10) NOT NULL DEFAULT 'en', " + 25 | "CONSTRAINT `watherPrimaryKey` PRIMARY KEY(userId,chatId));"; 26 | public static final String createWeatherAlertTable = "CREATE TABLE IF NOT EXISTS WeatherAlert (id INTEGER PRIMARY KEY AUTO_INCREMENT, userId BIGINT NOT NULL, cityId INTEGER NOT NULL, " + 27 | "cityName VARCHAR(60) NOT NULL, time INTEGER NOT NULL DEFAULT -1, CONSTRAINT unique_cityNameAlert UNIQUE (userId, cityName)," + 28 | "CONSTRAINT unique_cityIdAlert UNIQUE (userId, cityId));"; 29 | 30 | public static final String CREATE_COMMANDS_TABLE = "CREATE TABLE IF NOT EXISTS CommandUsers (userId BIGINT PRIMARY KEY, status INTEGER NOT NULL);"; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/CommandsHandler.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.commands.HelloCommand; 5 | import org.telegram.commands.HelpCommand; 6 | import org.telegram.commands.StartCommand; 7 | import org.telegram.commands.StopCommand; 8 | import org.telegram.database.DatabaseManager; 9 | import org.telegram.services.Emoji; 10 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 11 | import org.telegram.telegrambots.extensions.bots.commandbot.CommandLongPollingTelegramBot; 12 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 13 | import org.telegram.telegrambots.meta.api.objects.Update; 14 | import org.telegram.telegrambots.meta.api.objects.message.Message; 15 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 16 | 17 | /** 18 | * This handler mainly works with commands to demonstrate the Commands feature of the API 19 | * 20 | * @author Timo Schulz (Mit0x2) 21 | */ 22 | @Slf4j 23 | public class CommandsHandler extends CommandLongPollingTelegramBot { 24 | /** 25 | * Constructor. 26 | */ 27 | public CommandsHandler(String botToken, String botUsername) { 28 | super(new OkHttpTelegramClient(botToken), true, () -> botUsername); 29 | register(new HelloCommand()); 30 | register(new StartCommand()); 31 | register(new StopCommand()); 32 | HelpCommand helpCommand = new HelpCommand(this); 33 | register(helpCommand); 34 | 35 | registerDefaultAction((telegramClient, message) -> { 36 | SendMessage commandUnknownMessage = new SendMessage(String.valueOf(message.getChatId()), 37 | "The command '" + message.getText() + "' is not known by this bot. Here comes some help " + Emoji.AMBULANCE); 38 | try { 39 | telegramClient.execute(commandUnknownMessage); 40 | } catch (TelegramApiException e) { 41 | log.error("Error sending message in commands bot", e); 42 | } 43 | helpCommand.execute(telegramClient, message.getFrom(), message.getChat(), new String[] {}); 44 | }); 45 | } 46 | 47 | @Override 48 | public void processNonCommandUpdate(Update update) { 49 | if (update.hasMessage()) { 50 | Message message = update.getMessage(); 51 | 52 | if (!DatabaseManager.getInstance().getUserStateForCommandsBot(message.getFrom().getId())) { 53 | return; 54 | } 55 | 56 | if (message.hasText()) { 57 | SendMessage echoMessage = new SendMessage(String.valueOf(message.getChatId()), "Hey heres your message:\n" + message.getText()); 58 | try { 59 | telegramClient.execute(echoMessage); 60 | } catch (TelegramApiException e) { 61 | log.error("Error processing non-command update", e); 62 | } 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/TimerExecutor.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.time.Clock; 6 | import java.time.Duration; 7 | import java.time.LocalDateTime; 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.ScheduledExecutorService; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author Ruben Bermudez 14 | * @version 2.0 15 | * Execute a task periodically 16 | */ 17 | @Slf4j 18 | public class TimerExecutor { 19 | private static volatile TimerExecutor instance; ///< Instance 20 | private static final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); ///< Thread to execute operations 21 | 22 | /** 23 | * Private constructor due to singleton 24 | */ 25 | private TimerExecutor() { 26 | } 27 | 28 | /** 29 | * Singleton pattern 30 | * 31 | * @return Instance of the executor 32 | */ 33 | public static TimerExecutor getInstance() { 34 | final TimerExecutor currentInstance; 35 | if (instance == null) { 36 | synchronized (TimerExecutor.class) { 37 | if (instance == null) { 38 | instance = new TimerExecutor(); 39 | } 40 | currentInstance = instance; 41 | } 42 | } else { 43 | currentInstance = instance; 44 | } 45 | 46 | return currentInstance; 47 | } 48 | 49 | /** 50 | * Add a new CustomTimerTask to be executed 51 | * 52 | * @param task Task to execute 53 | * @param targetHour Hour to execute it 54 | * @param targetMin Minute to execute it 55 | * @param targetSec Second to execute it 56 | */ 57 | public void startExecutionEveryDayAt(CustomTimerTask task, int targetHour, int targetMin, int targetSec) { 58 | log.warn("Posting new task {}", task.getTaskName()); 59 | final Runnable taskWrapper = () -> { 60 | try { 61 | task.execute(); 62 | task.reduceTimes(); 63 | startExecutionEveryDayAt(task, targetHour, targetMin, targetSec); 64 | } catch (Exception e) { 65 | log.error("Bot threw an unexpected exception at TimerExecutor", e); 66 | } 67 | }; 68 | if (task.getTimes() != 0) { 69 | final long delay = computNextDilay(targetHour, targetMin, targetSec); 70 | executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS); 71 | } 72 | } 73 | 74 | /** 75 | * Find out next daily execution 76 | * 77 | * @param targetHour Target hour 78 | * @param targetMin Target minute 79 | * @param targetSec Target second 80 | * @return time in second to wait 81 | */ 82 | private long computNextDilay(int targetHour, int targetMin, int targetSec) { 83 | final LocalDateTime localNow = LocalDateTime.now(Clock.systemUTC()); 84 | LocalDateTime localNextTarget = localNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec); 85 | while (localNow.isAfter(localNextTarget.minusSeconds(1))) { 86 | localNextTarget = localNextTarget.plusDays(1); 87 | } 88 | 89 | final Duration duration = Duration.between(localNow, localNextTarget); 90 | return duration.getSeconds(); 91 | } 92 | 93 | @Override 94 | public void finalize() { 95 | this.stop(); 96 | } 97 | 98 | /** 99 | * Stop the thread 100 | */ 101 | private void stop() { 102 | executorService.shutdown(); 103 | try { 104 | executorService.awaitTermination(1, TimeUnit.DAYS); 105 | } catch (InterruptedException ex) { 106 | log.error("Task interrupted", ex); 107 | } catch (Exception e) { 108 | log.error("Bot threw an unexpected exception at TimerExecutor", e); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /HOWTO.md: -------------------------------------------------------------------------------- 1 | Let us create a simple echo bot 2 | 3 | - follow [these](https://github.com/rubenlagus/TelegramBots/blob/master/eclipse%20configuration.md) steps to work with eclipse 4 | 5 | - got to org.telegram and edit `BuildVars.java` and fill it out 6 | 7 | 8 | ``` 9 | public static final Boolean debug = true; 10 | public static final String pathToLogs = "./"; 11 | public static final String linkDB = "jdbc:mysql://[IP_OF_YOU_MYSQL_SERVER]:3306/[DATABASE]?useUnicode=true&characterEncoding=UTF-8"; 12 | public static final String controllerDB = "com.mysql.jdbc.Driver"; 13 | public static final String userDB = "[YOUR_DB_USERNAME]"; 14 | public static final String password = "[YOUR_SECRET_DB_PASSWORD]"; 15 | ``` 16 | 17 | For our project those settings are enough. 18 | 19 | [DATABASE]: your database. i.e. myProject 20 | 21 | 22 | - next, go to org.telegram and edit `BotConfig.java` 23 | Here we must fill in our login credentials for our bot. 24 | 25 | ``` 26 | public static final String TOKENMYPROJECT = "[YOUR_TOP_SECRET_TOKEN]"; 27 | public static final String USERNAMEMYPROJECT = "myProjectBot"; 28 | ``` 29 | [YOUR_TOP_SECRET_TOKEN]: your token you got from the [BotFather](https://telegram.me/BotFather) 30 | 31 | 32 | - go to org.telegram.updatehandlers and create a new class. This class is responsible for your bot actions. (in our case just return the text back). This class should extending `TelegramLongPollingBot`. 33 | 34 | 35 | It should look similiar like this: 36 | 37 | 38 | 39 | 40 | ``` 41 | package org.telegram.updateshandlers; 42 | 43 | import org.telegram.telegrambots.api.objects.Update; 44 | import org.telegram.telegrambots.bots.TelegramLongPollingBot; 45 | 46 | public class MyProjectHandler extends TelegramLongPollingBot { 47 | 48 | @Override 49 | public String getBotUsername() { 50 | // TODO Auto-generated method stub 51 | return null; 52 | } 53 | 54 | @Override 55 | public void onUpdateReceived(Update arg0) { 56 | // TODO Auto-generated method stub 57 | 58 | } 59 | 60 | @Override 61 | public String getBotToken() { 62 | // TODO Auto-generated method stub 63 | return null; 64 | } 65 | 66 | 67 | 68 | } 69 | ``` 70 | 71 | Then you can program your bot. First edit getBotToken() and getBotUsername(). Simply return your credentials mentioned in BotConfig. So for example `return BotConfig.USERNAMEMYPROJECT;` 72 | 73 | 74 | 75 | The onUpdateReceived() method could look like this: 76 | 77 | ``` 78 | @Override 79 | public void onUpdateReceived(Update update) { 80 | 81 | //check if the update has a message 82 | if(update.hasMessage()){ 83 | Message message = update.getMessage(); 84 | 85 | //check if the message has text. it could also contain for example a location ( message.hasLocation() ) 86 | if(message.hasText()){ 87 | 88 | //create a object that contains the information to send back the message 89 | SendMessage sendMessageRequest = new SendMessage(); 90 | sendMessageRequest.setChatId(message.getChatId().toString()); //who should get the message? the sender from which we got the message... 91 | sendMessageRequest.setText("you said: " + message.getText()); 92 | try { 93 | sendMessage(sendMessageRequest); //at the end, so some magic and send the message ;) 94 | } catch (TelegramApiException e) { 95 | //do some error handling 96 | }//end catch() 97 | }//end if() 98 | }//end if() 99 | 100 | }//end onUpdateReceived() 101 | ``` 102 | - go to the `Main.java` in org.telegram and register your updatehandler 103 | ``` 104 | public static void main(String[] args) { 105 | 106 | TelegramBotsApi telegramBotsApi = new TelegramBotsApi(); 107 | try { 108 | telegramBotsApi.registerBot(new MyProjectHandler()); 109 | } catch (TelegramApiException e) { 110 | BotLogger.error(LOGTAG, e); 111 | }//end catch() 112 | }//end main() 113 | ``` 114 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/database/ConnectionDB.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the source code of Telegram Bot v. 2.0 3 | * It is licensed under GNU GPL v. 3 or later. 4 | * You should have received a copy of the license in this archive (see LICENSE). 5 | * 6 | * Copyright Ruben Bermudez, 3/12/14. 7 | */ 8 | package org.telegram.database; 9 | 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.telegram.BuildVars; 12 | 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.sql.Connection; 15 | import java.sql.DatabaseMetaData; 16 | import java.sql.DriverManager; 17 | import java.sql.PreparedStatement; 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | import java.sql.Statement; 21 | 22 | /** 23 | * @author Ruben Bermudez 24 | * @version 2.0 25 | * Connector to database 26 | */ 27 | @Slf4j 28 | public class ConnectionDB { 29 | private final Connection currentConection; 30 | 31 | public ConnectionDB() { 32 | this.currentConection = openConexion(); 33 | } 34 | 35 | private Connection openConexion() { 36 | Connection connection = null; 37 | try { 38 | Class.forName(BuildVars.controllerDB).getDeclaredConstructor().newInstance(); 39 | connection = DriverManager.getConnection(BuildVars.linkDB, BuildVars.userDB, BuildVars.password); 40 | } catch (SQLException | ClassNotFoundException | IllegalAccessException | InstantiationException | 41 | NoSuchMethodException | InvocationTargetException e) { 42 | log.error("Error opening connection", e); 43 | } 44 | 45 | return connection; 46 | } 47 | 48 | public void closeConexion() { 49 | try { 50 | this.currentConection.close(); 51 | } catch (SQLException e) { 52 | log.error("Error closing the connection", e); 53 | } 54 | 55 | } 56 | 57 | public ResultSet runSqlQuery(String query) throws SQLException { 58 | final Statement statement; 59 | statement = this.currentConection.createStatement(); 60 | return statement.executeQuery(query); 61 | } 62 | 63 | public Boolean executeQuery(String query) throws SQLException { 64 | final Statement statement = this.currentConection.createStatement(); 65 | return statement.execute(query); 66 | } 67 | 68 | public PreparedStatement getPreparedStatement(String query) throws SQLException { 69 | return this.currentConection.prepareStatement(query); 70 | } 71 | 72 | public PreparedStatement getPreparedStatement(String query, int flags) throws SQLException { 73 | return this.currentConection.prepareStatement(query, flags); 74 | } 75 | 76 | public int checkVersion() { 77 | int max = 0; 78 | try { 79 | final DatabaseMetaData metaData = this.currentConection.getMetaData(); 80 | final ResultSet res = metaData.getTables(null, null, "", 81 | new String[]{"TABLE"}); 82 | while (res.next()) { 83 | if (res.getString("TABLE_NAME").compareTo("Versions") == 0) { 84 | try(ResultSet result = runSqlQuery("SELECT Max(Version) FROM Versions")) { 85 | while (result.next()) { 86 | max = Math.max(max, result.getInt(1)); 87 | } 88 | } 89 | } 90 | } 91 | } catch (SQLException e) { 92 | log.error("Error checking version", e); 93 | } 94 | return max; 95 | } 96 | 97 | /** 98 | * Initilize a transaction in database 99 | * @throws SQLException If initialization fails 100 | */ 101 | public void initTransaction() throws SQLException { 102 | this.currentConection.setAutoCommit(false); 103 | } 104 | 105 | /** 106 | * Finish a transaction in database and commit changes 107 | * @throws SQLException If a rollback fails 108 | */ 109 | public void commitTransaction() throws SQLException { 110 | try { 111 | this.currentConection.commit(); 112 | } catch (SQLException e) { 113 | if (this.currentConection != null) { 114 | this.currentConection.rollback(); 115 | } 116 | } finally { 117 | this.currentConection.setAutoCommit(true); 118 | } 119 | } 120 | 121 | public void rollbackTransaction() throws SQLException { 122 | try { 123 | this.currentConection.rollback(); 124 | } catch (SQLException e) { 125 | log.error("Error rolling back the transaction", e); 126 | } finally { 127 | this.currentConection.setAutoCommit(true); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/RaeHandlers.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.services.RaeService; 5 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 6 | import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; 7 | import org.telegram.telegrambots.meta.api.methods.AnswerInlineQuery; 8 | import org.telegram.telegrambots.meta.api.methods.ParseMode; 9 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 10 | import org.telegram.telegrambots.meta.api.objects.Update; 11 | import org.telegram.telegrambots.meta.api.objects.inlinequery.InlineQuery; 12 | import org.telegram.telegrambots.meta.api.objects.inlinequery.inputmessagecontent.InputTextMessageContent; 13 | import org.telegram.telegrambots.meta.api.objects.inlinequery.result.InlineQueryResult; 14 | import org.telegram.telegrambots.meta.api.objects.inlinequery.result.InlineQueryResultArticle; 15 | import org.telegram.telegrambots.meta.api.objects.message.Message; 16 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 17 | import org.telegram.telegrambots.meta.generics.TelegramClient; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * @author Ruben Bermudez 24 | * @version 1.0 25 | * Handler for inline queries in Raebot 26 | */ 27 | @Slf4j 28 | public class RaeHandlers implements LongPollingSingleThreadUpdateConsumer { 29 | private static final Integer CACHETIME = 86400; 30 | private final RaeService raeService = new RaeService(); 31 | private static final String THUMBNAILBLUE = "https://lh5.ggpht.com/-kSFHGvQkFivERzyCNgKPIECtIOELfPNWAQdXqQ7uqv2xztxqll4bVibI0oHJYAuAas=w300"; 32 | private static final String helpMessage = "Este bot puede ayudarte a buscar definiciones de palabras según el diccionario de la RAE.\n\n" + 33 | "Funciona automáticamente, no hay necesidad de añadirlo a ningún sitio.\n" + 34 | "Simplemente abre cualquiera de tus chats y escribe `@raebot loquesea` en la zona de escribir mensajes.\n" + 35 | "Finalmente pulsa sobre un resultado para enviarlo." + 36 | "\n\n" + 37 | "Por ejemplo, intenta escribir `@raebot Punto` aquí."; 38 | 39 | private final TelegramClient telegramClient; 40 | 41 | public RaeHandlers(String botToken) { 42 | telegramClient = new OkHttpTelegramClient(botToken); 43 | } 44 | 45 | @Override 46 | public void consume(Update update) { 47 | try { 48 | if (update.hasInlineQuery()) { 49 | handleIncomingInlineQuery(update.getInlineQuery()); 50 | } else if (update.hasMessage() && update.getMessage().isUserMessage()) { 51 | try { 52 | telegramClient.execute(getHelpMessage(update.getMessage())); 53 | } catch (TelegramApiException e) { 54 | log.error("Error", e); 55 | } 56 | } 57 | } catch (Exception e) { 58 | log.error("Unknown exception", e); 59 | } 60 | } 61 | 62 | /** 63 | * For an InlineQuery, results from RAE dictionariy are fetch and returned 64 | * @param inlineQuery InlineQuery recieved 65 | */ 66 | private void handleIncomingInlineQuery(InlineQuery inlineQuery) { 67 | String query = inlineQuery.getQuery(); 68 | log.debug("Searching: {}", query); 69 | try { 70 | if (!query.isEmpty()) { 71 | List results = raeService.getResults(query); 72 | telegramClient.execute(converteResultsToResponse(inlineQuery, results)); 73 | } else { 74 | telegramClient.execute(converteResultsToResponse(inlineQuery, new ArrayList<>())); 75 | } 76 | } catch (TelegramApiException e) { 77 | log.error("Error handing inline query", e); 78 | } 79 | } 80 | 81 | /** 82 | * Converts resutls from RaeService to an answer to an inline query 83 | * @param inlineQuery Original inline query 84 | * @param results Results from RAE service 85 | * @return AnswerInlineQuery method to answer the query 86 | */ 87 | private static AnswerInlineQuery converteResultsToResponse(InlineQuery inlineQuery, List results) { 88 | return AnswerInlineQuery 89 | .builder() 90 | .inlineQueryId(inlineQuery.getId()) 91 | .cacheTime(CACHETIME) 92 | .results(convertRaeResults(results)) 93 | .build(); 94 | } 95 | 96 | /** 97 | * Converts results from RaeService to a list of InlineQueryResultArticles 98 | * @param raeResults Results from rae service 99 | * @return List of InlineQueryResult 100 | */ 101 | private static List convertRaeResults(List raeResults) { 102 | List results = new ArrayList<>(); 103 | 104 | for (int i = 0; i < raeResults.size(); i++) { 105 | RaeService.RaeResult raeResult = raeResults.get(i); 106 | InlineQueryResultArticle article = new InlineQueryResultArticle( 107 | Integer.toString(i), 108 | raeResult.getTitle(), 109 | InputTextMessageContent 110 | .builder() 111 | .disableWebPagePreview(true) 112 | .parseMode(ParseMode.MARKDOWN) 113 | .messageText(raeResult.getDefinition()) 114 | .build() 115 | ); 116 | article.setDescription(raeResult.getDescription()); 117 | article.setThumbnailUrl(THUMBNAILBLUE); 118 | results.add(article); 119 | } 120 | 121 | return results; 122 | } 123 | 124 | /** 125 | * Create a help message when an user try to send messages directly to the bot 126 | * @param message Received message 127 | * @return SendMessage method 128 | */ 129 | private static SendMessage getHelpMessage(Message message) { 130 | return SendMessage 131 | .builder() 132 | .chatId(message.getChatId()) 133 | .parseMode(ParseMode.MARKDOWN) 134 | .text(helpMessage) 135 | .build(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/resources/strings_es.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= Magia con las palabras es lo que se me da bien, quizás te alegre el día.\n\nObtén los últimos archivos de localización de Telegram en tu idioma\:\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para iOS.\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para Android.\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para Webogram.\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para Tdesktop.\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para OS X.\n|-- %s CÓDIGO_IDIOMA \: Obtén el idioma para Windows Phone.\n\n 2. Obtén un archivo de localización actualizado para tu beta de Android\:\n|-- %s CÓDIGO_IDIOMA\: Obtén el idioma para Android beta. 2 | helpFiles= ¿Quieres compartir un archivo con alguien? Has llegado al puerto adecuado. \n\nComparte archivos usando un enlace personalizado\: \n|-- %s \: ID_ARCHIVO \: Obtener un archivo por id. \n|-- %s \: Iniciar la subida de un archivo. \n|-- %s \: Elige y elimina uno de tus archivos.\n|-- %s \: Muestra una lista de tus archivos compartidos. 3 | helpDirections= Un viaje por delante es una buena ocasión, ¿pero no sería mejor conociendo el camino?\n\nPara obtener la ruta que debes seguir entre dos lugares\:\n|-- %s \: Comienza a obtener la ruta 4 | sendFileToUpload= Por favor, envíame el archivo que quieres compartir. Asegúrate de adjuntarlo como archivo, no como imagen o vídeo. 5 | fileUploaded= Perfecto, ya tengo tu archivo. Envía este link a cualquiera que desees que lo tenga y podrá descargarlo\:\n\n 6 | deleteUploadedFile= Por favor, selecciona un archivo de la lista para eliminarlo\: 7 | fileDeleted= El archivo fue eliminado 8 | wrongFileId= Lo siento, no podemos encontrar el archivo con ese ID. Puede que haya un error o que el archivo haya sido eliminado. 9 | listOfFiles= Esta es la lista actual de tus archivos compartidos\: 10 | noFiles= Aún no has compartido ningún archivo. 11 | processFinished= El proceso actual ha sido cancelado. 12 | uploadedFileURL= https\://telegram.me/filesbot?start\= 13 | initDirections= Por favor, responde a este mensaje con el lugar de salida. 14 | sendDestination= Por favor, responde a este mensaje con el lugar de destino. 15 | youNeedReplyDirections= Lo siento, no puedo ayudarte a menos que respondas al mensaje que te he enviado. 16 | chooselanguage= Por favor, elige un idioma de la lista para cambiar a ese idioma. 17 | errorLanguage= No soportamos ese idioma o ha habido un error. El proceso ha sido cancelado. 18 | directionsInit= %s está a %s de %s y se necesita %s para llegar, siguiendo estas direcciones\:\n\n 19 | directionsNotFound= No hemos encontrado una ruta desde %s hasta %s. 20 | errorFetchingDirections= Ha habido un error obteniendo la ruta 21 | directionsStep= %s durante %s (%s) 22 | languageModified= Tus ajustes de idioma fueron actualizados. 23 | 24 | 25 | helpWeatherMessage= ¿Te interesa el estado del tiempo?\nEnvíame estos comandos y te mantendré informado\:\n\t\t%s Obtén el tiempo actual \n\t\t%s Obtén el pronóstico para 3 días \n\t\t%s Establece alertas\n\t\t%s Elige un idioma\n\t\t%s Cambia las unidades 26 | forecast=%sPronóstico 27 | current=%sTiempo actual 28 | help=%sAyuda 29 | settings=%sAjustes 30 | cancel=%sCancelar 31 | location=%sUbicación 32 | new=%sNueva 33 | languages=%sIdiomas 34 | alerts=%sAlertas 35 | units=%sUnidades 36 | back=%sAtrás 37 | delete=%sEliminar 38 | showList=%sMostrar lista 39 | rateMe=%sPuntúame 40 | metricSystem=Sistema métrico 41 | imperialSystem= Sistema imperial 42 | selectLanguage=Tu idioma actual es %s. Elige un idioma de la lista para usarlo. 43 | languageUpdated=Tus ajustes de idioma fueron actualizados. 44 | errorLanguageNotFound= No soportamos ese idioma o se cometió un error ortográfico. Por favor elige uno de la lista. 45 | selectUnits=Tu sistema de unidades actual es %s. Elige una opción de la lista para cambiarlo. 46 | unitsUpdated=Tus ajustes de unidades fueron actualizados. 47 | errorUnitsNotFound= No soportamos ese sistema de unidades o se cometió un error ortográfico. Por favor elige uno de la lista. 48 | onWeatherNewCommand=Por favor, envíame la ciudad en la que estas interesado, usa este formato\: CIUDAD,PAÍS 49 | onWeatherLocationCommand=Por favor, envíame la ubicación en la que estás interesado. 50 | onCurrentCommandFromHistory=Elige una opción de tu solicitudes recientes, "nueva" para enviar una nueva ciudad o "ubicación" para enviarme una ubicación para obtener el tiempo actual. 51 | onCurrentCommandWithoutHistory=Elige "nueva" para enviar una nueva ciudad o "ubicación" para enviarme una ubicación para obtener el tiempo actual. 52 | onForecastCommandFromHistory=Elige una opción de tus solicitudes recientes, "nueva" para enviar una ciudad o "ubicación" 53 | onForecastCommandWithoutHistory=Elige "nueva" para enviar una nueva ciudad o "ubicación" 54 | weatherCurrent= El tiempo para %s es\:\n\n%s 55 | currentWeatherPartMetric=\t- Tiempo\: %s\n\t- Nubes\: %s\n\t-Temperatura\: %s ºC 56 | currentWeatherPartImperial=\t- Tiempo\: %s\n\t- Nubes\: %s\n\t-Temperatura\: %s ºF 57 | weatherForcast= El tiempo para %s será\:\n\n%s 58 | forecastWeatherPartMetric= %s el %s\n- Pronóstico\: %s\n\t- Temperatura máxima\: %s ºC\n\t- Temperatura mínima\: %s ºC\n 59 | forecastWeatherPartImperial= %s el %s\n- Pronóstico\: %s\n\t- Temperatura máxima\: %s ºF\n\t- Temperatura mínima\: %s ºF\n 60 | weatherAlert= El tiempo para %s será\:\n\n%s 61 | alertWeatherPartMetric=\t- Pronóstico\: %s\n\t- Temperatura máxima\: %s ºC\n\t- Temperatura mínima\: %s ºC\n 62 | alertWeatherPartImperial=\t- Pronóstico\: %s\n\t- Temperatura máxima\: %s ºF\n\t- Temperatura mínima\: %s ºF\n 63 | chooseOption=Por favor, elige una opción del menú. 64 | backToMainMenu=Proceso cancelado, vuelta al menú principal. 65 | onSettingsCommand=Por favor, elige una opción\:\n\t\t%s Idiomas\: elige un idioma\n\t\t%s Unidades\: cambia las unidades\n\t\t%s Alertas\: establece alertas diarias\n\t\t%s Atrás\: vuelve al menú principal 66 | alertsMenuMessage=Aquí puedes administrar tus alertas o añadir nuevas. 67 | chooseNewAlertCity=¿Para qué ciudad quieres establecer una alerta? Elige una opción de tus solicitudes recientes. 68 | newAlertSaved=%s Tu alerta para %s fue añadida correctamente, recibirás actualizaciones del tiempo dos veces al día. 69 | initialAlertList=Tienes %d alertas\:\n\n%s 70 | partialAlertList=%s%s\n 71 | noAlertList=No pude encontrar ninguna alerta. 72 | alertDeleted=La alerta seleccionada fue eliminada. 73 | cityNotFound= Ciudad no encontrada 74 | errorFetchingWeather= Lo sentimos, hubo un error solicitando el tiempo. 75 | rateMeMessage=Si te gusta este bot, por favor, puntúalo en https\://telegram.me/findbot?start\=weatherbot 76 | -------------------------------------------------------------------------------- /src/main/resources/strings.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= Tricks with words is the game that I play, give it a shot, I might make your day.\n\nTo get the latest Telegram localization file for a language: \n|-- %s LANG_CODE : Get the latest iOS language.\n|-- %s LANG_CODE : Get the latest Android language.\n|-- %s LANG_CODE : Get the latest Webogram language.\n|-- %s LANG_CODE : Get the latest Tdesktop language.\n|-- %s LANG_CODE : Get the latest OSX-App language.\n|-- %s LANG_CODE : Get the latest Windows Phone language.\n\n2. To get an updated localization file for your Android beta-app: \n|-- %s LANG_CODE : Get the latest Android-beta language. 2 | helpFiles= Leaving a file for some others to find? Just dock your boat here and a bay comes to mind.\n\nShare files through a custom link: \n|-- %s FILEID : Get a file by id.\n|-- %s : Start your file upload.\n|-- %s : Select one of your files to delete it.\n|-- %s : Show a list of your shared files. 3 | helpDirections= The road ahead, paved with good intentions, the right path ahead however, is what I tend to mention.\n\nTo get directions between two locations: \n|-- %s : Start to get directions 4 | sendFileToUpload= Please send me a file you want to share. Make sure you attach it as file, not as an image or video. 5 | fileUploaded= Great, your file has been uploaded. Send this link to anyone you want and they will be able to download the file:\n\n 6 | deleteUploadedFile= Please select the file you want to delete: 7 | fileDeleted= The file was deleted 8 | wrongFileId= Sorry, we can't find a file with that ID. Either a typo was made or it was deleted already. 9 | listOfFiles= This your currently shared list of files: 10 | noFiles= You haven't shared any files yet. 11 | processFinished= The current process was cancelled. 12 | uploadedFileURL= https://telegram.me/filesbot?start= 13 | initDirections= Please reply to this message with your starting point. 14 | sendDestination= Please reply to this message with your destination. 15 | youNeedReplyDirections= I'm sorry, I can't help you unless you reply to the message I sent you. 16 | chooselanguage= Please, select a language from the list to change to that language. 17 | errorLanguage= We don't support that language or a typo was made. The process has been cancelled. 18 | directionsInit= %s is %s away from %s and it takes %s to arrive there, following these directions:\n\n 19 | directionsNotFound= Directions not found between %s and %s. 20 | errorFetchingDirections= Error fetching directions info 21 | directionsStep= %s during %s (%s) 22 | languageModified= Your language setting has been updated. 23 | movedToLangBot=This bot has been moved to @langbot, enjoy it! 24 | checkLangBot=Check @langbot 25 | 26 | helpWeatherMessage= Curious about the weather?\ 27 | \nJust send me these commands and you'll know a lot better\:\ 28 | \n\t\t%s Get the current weather for a location or city\ 29 | \n\t\t%s Get a 3-day forecast for a location or city\ 30 | \n\t\t%s Set current alerts\ 31 | \n\t\t%s Select a forecast-language\ 32 | \n\t\t%s Change your unit preferences 33 | forecast=%sForecast 34 | current=%sCurrent 35 | help=%sHelp 36 | settings=%sSettings 37 | cancel=%sCancel 38 | location=%sLocation 39 | new=%sNew 40 | languages=%sLanguages 41 | alerts=%sAlerts 42 | units=%sUnits 43 | back=%sBack 44 | delete=%sDelete 45 | showList=%sShow list 46 | rateMe=%sRate me 47 | metricSystem=Metric System 48 | imperialSystem=Imperial System 49 | selectLanguage=Your current language is *%s*. Select a language from the list to change to that language. 50 | languageUpdated=Your language setting has been updated. 51 | errorLanguageNotFound= We don't support that language or a typo was made. Please select one from the list. 52 | selectUnits=Your current unit setting is *%s*. Select an option from the list to change it. 53 | unitsUpdated=Your unit settings have been updated. 54 | errorUnitsNotFound= We don't support that unit system or a typo was made. Please select one from the list. 55 | onWeatherNewCommand=Please send me the city you are interested in using this format: *CITY,COUNTRY* 56 | onWeatherLocationCommand=Please send me the location you are interested in. 57 | onCurrentCommandFromHistory=Select an option from your _recent requests_, *new* to send a new city or *location* to send me a location to get current weather. 58 | onCurrentCommandWithoutHistory=Select *new* to send a new city or *location* to send me a location to get current weather. 59 | onForecastCommandFromHistory=Select an option from your _recent requests_, *new* to send a new city or *location* to send me a location to get _3-days_ weather forecast. 60 | onForecastCommandWithoutHistory=Select *new* to send a new city or *location* to send me a location to get a _3-days_ weather forecast. 61 | weatherCurrent= The weather for *%s* is:\n\n %s 62 | currentWeatherPartMetric=\t- _Weather:_ %s\n\t- _Cloudiness:_ %s\n\t- _Temperature:_ %s ºC\n\n 63 | currentWeatherPartImperial=\t- _Weather:_ %s\n\t- _Cloudiness:_ %s\n\t- _Temperature:_ %s ºF\n\n 64 | weatherForcast= The weather for *%s* will be:\n\n %s 65 | forecastWeatherPartMetric= %s On *%s* \n\t- _Forecast:_ %s\n\t- _High temperature:_ %s ºC\n\t- _Low temperature:_ %s ºC\n\n 66 | forecastWeatherPartImperial= %s On *%s* \n\t- _Forecast:_ %s\n\t- _High temperature:_ %s ºF\n\t- _Low temperature:_ %s ºF\n\n 67 | weatherAlert= The weather for *%s* will be:\n\n %s 68 | alertWeatherPartMetric=\t- _Forecast:_ %s\n\t- _High temperature:_ %s ºC\n\t- _Low temperature:_ %s ºC\n\n 69 | alertWeatherPartImperial=\t- _Forecast:_ %s\n\t- _High temperature:_ %s ºF\n\t- _Low temperature:_ %s ºF\n\n 70 | chooseOption=Please, select an option from the menu. 71 | backToMainMenu=Process cancelled, back to main menu. 72 | onSettingsCommand=Please, select an option:\ 73 | \n\t\t%s *Languages*: Select a _forecast-language_\ 74 | \n\t\t%s *Units*: Select your preferred _units_\ 75 | \n\t\t%s *Alerts*: Set daily _alerts_\ 76 | \n\t\t%s *Back*: Return to main menu 77 | alertsMenuMessage=Here you can manage your alerts or add new ones. 78 | chooseNewAlertCity=Which city do you want to set an alert for? Select an option from your _recent requests_. 79 | newAlertSaved=%s Your alert for *%s* has been correctly created, you will receive the weather update _twice a day_. 80 | initialAlertList=You have *%d* alerts:\n\n%s 81 | partialAlertList=%s *%s*\n 82 | noAlertList=I couldn't find any alert for you. 83 | alertDeleted=The selected alert has been deleted. 84 | cityNotFound= City not found 85 | errorFetchingWeather= We're sorry, there was an error fetching the weather. 86 | rateMeMessage=If you like this bot, please rate it using [@findbot](https://telegram.me/findbot?start=weatherbot) -------------------------------------------------------------------------------- /src/main/resources/strings_pt.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= Para obter a última tradução para os aplicativos do Telegram em um idioma\:\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para iOS.\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para Android.\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para Webogram.\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para TDesktop.\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para OSX.\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para Windows Phone.\n\n2. Para obter a o arquivo de tradução atualizado para o seu aplicativo beta do Android\:\n|-- %s CODIGO_DO_IDIOMA\: Obter a última tradução para Android-beta. 2 | helpFiles= Deseja compartilhar um arquivo com outras pessoas? Você está no lugar certo\!\n\nCompartilhe arquivos através de um link\:\n|-- %s IDDOARQUIVO \: Obter arquivo pelo id.\n|-- %s \: Iniciar seu upload de arquivo.\n|-- %s \: Escolher um de seus arquivos para apagar.\n|-- %s \: Mostrar lista de seus arquivos compartilhados. 3 | helpDirections= A estrada à frente, cheia de boas intenções, o caminho logo à frente no entanto, é o que eu tendo a mencionar.\n\nPara obter localizações entre dois locais\:\n|-- %s \: Começar para receber as direções 4 | sendFileToUpload= Por favor, envie-me o arquivo que deseja compartilhar. Certifique-se de anexá-lo como arquivo, e não como uma imagem ou vídeo. 5 | fileUploaded= Ótimo, seu arquivo foi carregado. Envie esse link para qualquer um que quiser e eles poderão baixar o seu arquivo\:\n\n 6 | deleteUploadedFile= Por favor, selecione o arquivo que deseja apagar\: 7 | fileDeleted= O arquivo foi deletado 8 | wrongFileId= Desculpe, não conseguimos achar o arquivo com esse ID. Houve algum erro ao digitar ou o arquivo já foi deletado. 9 | listOfFiles= Essa é sua lista de arquivos compartilhados atualmente\: 10 | noFiles= Você não compartilhou arquivos ainda. 11 | processFinished= O processo atual foi cancelado. 12 | uploadedFileURL= https\://telegram.me/filesbot?start\= 13 | initDirections= Por favor, responda a essa mensagem com seu ponto de partida. 14 | sendDestination= Por favor, responda a essa mensagem com seu destino. 15 | youNeedReplyDirections= Me desculpe, não consigo ajudá-lo a menos que você responda a mensagem que lhe enviei. 16 | chooselanguage= Por favor, escolha um idioma da lista para alterar para essa linguagem; 17 | errorLanguage= Nós não temos suporte para essa linguagem ou ela foi escrita errada. Esse processo foi cancelado. 18 | directionsInit= %s é %s distante de %s e demora %s para chegar lá, seguindo essas direções\:\n\n 19 | directionsNotFound= Direções não encontradas entre %s e %s 20 | errorFetchingDirections= Erro ao obter informações de direção. 21 | directionsStep= %s durante %s (%s) 22 | languageModified= Sua configuração de idioma foi atualizada. 23 | 24 | 25 | helpWeatherMessage= Curioso sobre o clima?\nBasta me enviar esses comandos e você saberá muito mais.\n\n\t\t%s Obter o clima atual de uma localização ou cidade\n\t\t%s Obter a previsão de 3 dias de uma localização ou cidade\n\t\t%s Definir alertas atuais\n\t\t%s Selecionar um idioma para a previsão\n\t\t%s Alterar suas preferências de unidade 26 | forecast=%sPrevisão 27 | current=%sAtual 28 | help=%sAjuda 29 | settings=%sConfiguração 30 | cancel=%sCancelar 31 | location=%sLocalização 32 | new=%sNovo 33 | languages=%sIdiomas 34 | alerts=%sAlertas 35 | units=%sUnidades 36 | back=%sVoltar 37 | delete=%sApagar 38 | showList=%sMostrar lista 39 | rateMe=%sMe avalie 40 | metricSystem=Sistema Métrico 41 | imperialSystem= Sistema Imperial 42 | selectLanguage=Seu idioma atual é %s. Selecione um idioma da lista para alterar para essa linguagem. 43 | languageUpdated=Sua configuração de idioma foi atualizada. 44 | errorLanguageNotFound= Nós não temos suporte para essa linguagem ou ela foi escrita errada. Por favor, selecione um idioma da lista. 45 | selectUnits=Sua unidade atual é %s. Selecione uma opção da lista para alterar. 46 | unitsUpdated=Sua configuração de unidade foi atualizada. 47 | errorUnitsNotFound= Nós não temos suporte para essa unidade ou ela foi escrita errada. Por favor, selecione um tipo de unidade da lista. 48 | onWeatherNewCommand=Envie-me a cidade e país que você está interessado, use o formato\: CIDADE,PAÍS 49 | onWeatherLocationCommand=Por favor, selecione a localização que você está interessado. 50 | onCurrentCommandFromHistory=Selecione uma opção de suas solicitações recentes, "novo" para enviar uma nova cidade ou "localização" para me enviar seu local e obter o clima atual. 51 | onCurrentCommandWithoutHistory=Selecione "novo" para enviar uma nova cidade ou "localização" para me enviar a localização e obter o clima atual. 52 | onForecastCommandFromHistory=Selecione uma opção de suas solicitações recentes, "novo" para enviar uma nova cidade ou "localização" para me enviar seu local e obter a previsão de 3 dias. 53 | onForecastCommandWithoutHistory=Selecione "novo" para enviar uma nova cidade ou "localização" para me enviar seu local e obter a previsão de 3 dias. 54 | weatherCurrent= O clima para %s é de\:\n\n%s 55 | currentWeatherPartMetric=\t- Clima\: %s\n\t- Nebulosidade\: %s\n\t- Clima\: %s ºC\n\n 56 | currentWeatherPartImperial=\t- Clima\: %s\n\t- Nebulosidade\: %s\n\t- Temperatura\: %s ºF\n\n 57 | weatherForcast= O clima para %s será de\:\n\n%s 58 | forecastWeatherPartMetric= %s Em %s \n\t- Previsão\: %s\n\t- Máxima\: %s ºC\n\t- Mínima\: %s ºC\n\n 59 | forecastWeatherPartImperial= %s Em %s \n\t- Previsão\: %s\n\t- Máxima\: %s ºF\n\t- Mínima\: %s ºF\n\n 60 | weatherAlert= O clima para %s será de\:\n\n%s 61 | alertWeatherPartMetric=\t- Previsão\: %s\n\t- Máxima\: %s ºC\n\t- Mínima\: %s ºC\n\n 62 | alertWeatherPartImperial=\t- Previsão\: %s\n\t- Máxima\: %s ºF\n\t- Mínima\: %s ºF\n\n 63 | chooseOption=Por favor, selecione uma opção do menu. 64 | backToMainMenu=Processo cancelado, de volta ao menu principal. 65 | onSettingsCommand=Por favor, selecione uma opção\:\n\t\t%s Idiomas\: Selecione um idioma para a previsão\n\t\t%s Unidades\: Selecione sua unidade preferida\n\t\t%s Alertas\: Definir alertas diários\n\t\t%s Voltar\: Voltar ao menu principal 66 | alertsMenuMessage=Aqui você pode alterar ou adicionar novos alertas. 67 | chooseNewAlertCity=Para qual cidade você deseja definir um alerta? Selecione uma opção de suas solicitações recentes. 68 | newAlertSaved=%s Seu alerta para %s foi criado corretamente, você irá receber atualizações sobre o clima duas vezes ao dia. 69 | initialAlertList=Você tem %d alertas\:\n\n%s 70 | partialAlertList=%s%s\n 71 | noAlertList=Eu não consegui encontrar nenhum alerta para você. 72 | alertDeleted=O alerta selecionado foi apagado. 73 | cityNotFound= Cidade não encontrada 74 | errorFetchingWeather= Desculpe, houve um erro ao obter o clima. 75 | rateMeMessage=Se você gosta deste bot, por favor nos avalie em https\://telegram.me/findbot?start\=weatherbot 76 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/DirectionsService.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.OkHttpClient; 5 | import okhttp3.Request; 6 | import okhttp3.Response; 7 | import okhttp3.ResponseBody; 8 | import org.json.JSONArray; 9 | import org.json.JSONObject; 10 | import org.jsoup.Jsoup; 11 | import org.telegram.BuildVars; 12 | 13 | import java.io.UnsupportedEncodingException; 14 | import java.net.URLEncoder; 15 | import java.nio.charset.StandardCharsets; 16 | import java.time.format.DateTimeFormatter; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * @author Ruben Bermudez 22 | * @version 1.0 23 | * Weather service 24 | */ 25 | @Slf4j 26 | public class DirectionsService { 27 | private static final String BASEURL = "https://maps.googleapis.com/maps/api/directions/json"; ///< Base url for REST 28 | private static final String APIIDEND = "&key=" + BuildVars.DirectionsApiKey; 29 | private static final String PARAMS = "&language=@language@&units=metric"; 30 | private static final DateTimeFormatter dateFormaterFromDate = DateTimeFormatter.ofPattern("dd/MM/yyyy"); ///< Date to text formater 31 | private static volatile DirectionsService instance; ///< Instance of this class 32 | 33 | /** 34 | * Constructor (private due to singleton pattern) 35 | */ 36 | private DirectionsService() { 37 | } 38 | 39 | /** 40 | * Singleton 41 | * 42 | * @return Return the instance of this class 43 | */ 44 | public static DirectionsService getInstance() { 45 | DirectionsService currentInstance; 46 | if (instance == null) { 47 | synchronized (DirectionsService.class) { 48 | if (instance == null) { 49 | instance = new DirectionsService(); 50 | } 51 | currentInstance = instance; 52 | } 53 | } else { 54 | currentInstance = instance; 55 | } 56 | return currentInstance; 57 | } 58 | 59 | /** 60 | * Fetch the directions 61 | * 62 | * @param origin Origin address 63 | * @param destination Destination address 64 | * @return Destinations 65 | */ 66 | public List getDirections(String origin, String destination, String language) { 67 | final List responseToUser = new ArrayList<>(); 68 | try { 69 | String completURL = BASEURL + "?origin=" + getQuery(origin) + "&destination=" + 70 | getQuery(destination) + PARAMS.replace("@language@", language) + APIIDEND; 71 | OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build(); 72 | Request request = new Request.Builder() 73 | .url(completURL) 74 | .header("charset", StandardCharsets.UTF_8.name()) 75 | .header("content-type", "application/json") 76 | .get() 77 | .build(); 78 | try (Response response = okHttpClient.newCall(request).execute()) { 79 | if (response.isSuccessful()) { 80 | try (ResponseBody body = response.body()) { 81 | if (body != null) { 82 | JSONObject jsonObject = new JSONObject(body.string()); 83 | if (jsonObject.getString("status").equals("OK")) { 84 | JSONObject route = jsonObject.getJSONArray("routes").getJSONObject(0); 85 | String startOfAddress = LocalisationService.getString("directionsInit", language); 86 | String partialResponseToUser = String.format(startOfAddress, 87 | route.getJSONArray("legs").getJSONObject(0).getString("start_address"), 88 | route.getJSONArray("legs").getJSONObject(0).getJSONObject("distance").getString("text"), 89 | route.getJSONArray("legs").getJSONObject(0).getString("end_address"), 90 | route.getJSONArray("legs").getJSONObject(0).getJSONObject("duration").getString("text") 91 | ); 92 | responseToUser.add(partialResponseToUser); 93 | responseToUser.addAll(getDirectionsSteps( 94 | route.getJSONArray("legs").getJSONObject(0).getJSONArray("steps"), language)); 95 | } else { 96 | responseToUser.add(LocalisationService.getString("directionsNotFound", language)); 97 | } 98 | } else { 99 | } 100 | } 101 | } 102 | } 103 | } catch (Exception e) { 104 | log.warn("Error getting directions", e); 105 | responseToUser.add(LocalisationService.getString("errorFetchingDirections", language)); 106 | } 107 | return responseToUser; 108 | } 109 | 110 | private String getQuery(String address) throws UnsupportedEncodingException { 111 | return URLEncoder.encode(address, "UTF-8"); 112 | } 113 | 114 | private List getDirectionsSteps(JSONArray steps, String language) { 115 | List stepsStringify = new ArrayList<>(); 116 | String partialStepsStringify = ""; 117 | for (int i = 0; i < steps.length(); i++) { 118 | String step = getDirectionForStep(steps.getJSONObject(i), language); 119 | if (partialStepsStringify.length() > 1000) { 120 | stepsStringify.add(partialStepsStringify); 121 | partialStepsStringify = ""; 122 | } 123 | partialStepsStringify += i + ".\t" + step + "\n\n"; 124 | } 125 | if (!partialStepsStringify.isEmpty()) { 126 | stepsStringify.add(partialStepsStringify); 127 | } 128 | return stepsStringify; 129 | } 130 | 131 | private String getDirectionForStep(JSONObject jsonObject, String language) { 132 | String direction = LocalisationService.getString("directionsStep", language); 133 | String htmlIntructions = Jsoup.parse(jsonObject.getString("html_instructions")).text(); 134 | String duration = jsonObject.getJSONObject("duration").getString("text"); 135 | String distance = jsonObject.getJSONObject("distance").getString("text"); 136 | 137 | direction = String.format(direction, htmlIntructions, duration, distance); 138 | 139 | return direction; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/resources/strings_eo.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= \u0108u vi estas suferanto de babela eraro? Provu min por Telegram-tradukaro.\n\nPor ricevi la plej lastatempan Telegram-tradukdosieron por lingvo\: \n|-- %s LINGVOKODO \: Ricevi la plej lastatempan iOS-tradukon.\n|-- %s LINGVOKODO \: Ricevi la plej lastatempan Android-tradukon.\n|-- %s LINGVOKODO \: Ricevi la plej lastatempan Webogram-tradukon.\n|-- %s LINGVOKODO \: Ricevi la plej lastatempan Tdesktop-tradukon.\n|-- %s LINGVOKODO \: Ricevi la plej lastatempan OSX-App-tradukon.\n|-- %s LINGVOKODO \: Ricevi la plej lastatempan Windows Phone-tradukon.\n\n2. Por ricevi \u011disdatigitan tradukdosieron por via Android-beta-aplika\u0135o\: \n|-- %s LINGVOKODO \: Ricevi la plej lastatempan Android-beta-tradukon.\n\n(La lingvokodo de Esperanto estas\: eo) 2 | helpFiles= \u0108u vi volas konigi dosieron al piratoj? Bonvolu resti \u0109i tie kun viaj boatoj\!\n\nKonigi dosierojn per ligilo\: \n|-- %s DOSIERIDENTIGILO \: Ricevi dosieron per identigilo.\n|-- %s \: Komenci dosieral\u015duton.\n|-- %s \: Elekti unu de viaj dosieroj por forigi \u011din.\n|-- %s \: Montri liston de viaj al\u015dutitaj dosieroj. 3 | helpDirections= Se mapon mi havus, mi bone voja\u011dus.\n\nPor ricevi veturinstrukcion inter du lokoj\: \n|-- %s \: Komenci ricevi veturinstrukcion 4 | sendFileToUpload= Sendu al mi dosieron kiun vi volas konigi. Certu, ke vi aligos \u011din kiel dosieron, ne kiel bildon a\u016d videon. 5 | fileUploaded= Bone, via dosiero estas al\u015dutita. Sendu tiun ligilon al \u0109iuj ajn, kiujn vi volas kaj ili povos el\u015duti la dosieron\:\n 6 | deleteUploadedFile= Elektu la dosieron, kiun vi volas forigi\: 7 | fileDeleted= La dosiero estis forigita 8 | wrongFileId= Mi beda\u016dras, mi ne povas trovi dosieron per tiu identigilo. A\u016d vi tajperaris a\u016d \u011di estis forigita jam. 9 | listOfFiles= Jen viaj nunaj al\u015dutitaj dosieroj\: 10 | noFiles= Vi ne havas ajnajn al\u015dutitajn dosierojn ankora\u016d. 11 | processFinished= La nuna procezo estas nuligita. 12 | uploadedFileURL= https\://telegram.me/filesbot?start\= 13 | initDirections= Respondu al tiu mesa\u011do per via deirpunkto. 14 | sendDestination= Respondu al tiu mesa\u011do per via destino. 15 | youNeedReplyDirections= Mi beda\u016dras, mi ne povas helpi vin, krom se vi respondas al mia mesa\u011do. 16 | chooselanguage= Elektu lingvon de la listo por \u015dan\u011di \u0109i tiun agordon. 17 | errorLanguage= Mi ne subtenas tiun lingvon a\u016d vi tajperaris. La procezo estas nuligita. 18 | directionsInit= %s trovi\u011das je %s for de %s, kaj necesas %s por alveni tien, sekvante tiun veturinstrukcion\:\n 19 | directionsNotFound= Veturinstrukcio inter %s kaj %s ne estas trovita. 20 | errorFetchingDirections= Mi beda\u016dras, eraro okazis dum ricevado de la veturinstrukcio. 21 | directionsStep= %s dum %s (%s) 22 | languageModified= Via lingvo-agordo estas \u011disdatigita. 23 | 24 | 25 | helpWeatherMessage= Suno a\u016d pluvo, hajlo a\u016d glacio? \u0108u vi volas scii?\nDemandu al mi kaj mi donos la scion al vi\!\n\t\t%s Ricevu la nunan veteron de loko a\u016d urbo\n\t\t%s Ricevu prognozon por 3 tagoj de loko a\u016d urbo\n\t\t%s \u015can\u011du nunajn avertojn\n\t\t%s Elektu lingvon por prognozoj\n\t\t%s Elektu viajn preferitajn unuojn 26 | forecast=%sPrognozo 27 | current=%sNuna 28 | help=%sHelpo 29 | settings=%sAgordoj 30 | cancel=%sNuligi 31 | location=%sLoko 32 | new=%sNova 33 | languages=%sLingvoj 34 | alerts=%sAvertoj 35 | units=%sUnuoj 36 | back=%sReen 37 | delete=%sForigi 38 | showList=%sMontri liston 39 | rateMe=%sKlasifiku min 40 | metricSystem=Metra sistemo 41 | imperialSystem= Imperia sistemo 42 | selectLanguage=Via nuna lingvo estas %s. Elektu lingvon de la listo por \u015dan\u011di \u0109i tiun agordon. 43 | languageUpdated=Via lingvo-agordo estas \u011disdatigita. 44 | errorLanguageNotFound= Mi ne subtenas tiun lingvon a\u016d vi tajperaris. Elektu alian el la listo. 45 | selectUnits=Viaj nunaj unuoj estas %s. Elektu unuojn de la listo por \u015dan\u011di \u0109i tiun agordon. 46 | unitsUpdated=Via unuo-agordo estas \u011disdatigita. 47 | errorUnitsNotFound= Mi ne subtenas tiujn unuojn a\u016d vi tajperaris. Elektu unu de la listo. 48 | onWeatherNewCommand=Sendu al mi la urbon, kiu vin interesas. Uzu la jenan formaton\: URBO,LANDO 49 | onWeatherLocationCommand=Sendu al mi la lokon, kiu vin interesas. 50 | onCurrentCommandFromHistory=Elektu opcion de viaj lastaj petoj, "nova" por sendi novan urbon a\u016d "loko" por sendi al mi lokon por ricevi la nunan veteron. 51 | onCurrentCommandWithoutHistory=Elektu "nova" por sendi novan urbon a\u016d "loko" por sendi al mi lokon por ricevi la nunan veteron. 52 | onForecastCommandFromHistory=Elektu opcion de viaj lastaj petoj, "nova" por sendi novan urbon a\u016d "loko" por sendi al mi lokon por ricevi prognozon por 3 tagoj. 53 | onForecastCommandWithoutHistory=Elektu "nova" por sendi novan urbon a\u016d "loko" por sendi al mi lokon por ricevi prognozon por 3 tagoj. 54 | weatherCurrent= La vetero por %s estas\:\n\n %s 55 | currentWeatherPartMetric=\t- Vetero\: %s\n\t- Nubeco\: %s\n\t- Temperaturo\: %s \u00baC\n 56 | currentWeatherPartImperial=\t- Vetero\: %s\n\t- Nubeco\: %s\n\t- Temperaturo\: %s \u00baF\n 57 | weatherForcast= La vetero por %s estos\:\n\n %s 58 | forecastWeatherPartMetric= %s je %s \n\t- Prognozo\: %s\n\t- Alta temperaturo\: %s \u00baC\n\t- Malalta temperaturo\: %s \u00baC\n 59 | forecastWeatherPartImperial= %s je %s \n\t- Prognozo\: %s\n\t- Alta temperaturo\: %s \u00baF\n\t- Malalta temperaturo\: %s \u00baF\n 60 | weatherAlert= La vetero por %s estos\:\n\n %s 61 | alertWeatherPartMetric=\t- Prognozo\: %s\n\t- Alta temperaturo\: %s \u00baC\n\t- Malalta temperaturo\: %s \u00baC\n 62 | alertWeatherPartImperial=\t- Prognozo\: %s\n\t- Alta temperaturo\: %s \u00baF\n\t- Malalta temperaturo\: %s \u00baF\n 63 | chooseOption=Elektu opcion de la menuo. 64 | backToMainMenu=Procezo estas nuligita, reen al la \u0109efa menuo. 65 | onSettingsCommand=Elektu opcion\:\n\t\t%s Lingvoj\: Elektu lingvon por prognozoj\n\t\t%s Unuoj\: Elektu viajn preferitajn unuojn\n\t\t%s Avertoj\: \u015can\u011du tagajn avertojn\n\t\t%s Reen\: Reen al la \u0109efa menuo 66 | alertsMenuMessage=\u0108i tie vi povas administri viajn avertojn a\u016d aldoni novajn avertojn. 67 | chooseNewAlertCity=Por kiu urbo vi volas agordi averton? Elektu opcion de viaj lastaj petoj. 68 | newAlertSaved=%s Via averto por %s estas kreita \u011duste, vi ricevos la veteron dufoje tage. 69 | initialAlertList=Vi havas %d avertojn\:\n\n%s 70 | partialAlertList=%s%s\n 71 | noAlertList=Mi ne povas trovi ajnan averton por vi. 72 | alertDeleted=La elektita averto estas forigita. 73 | cityNotFound= Urbo ne estas trovita 74 | errorFetchingWeather= Mi beda\u016dras, eraro okazis dum ricevado de la vetero. 75 | rateMeMessage=Se vi \u015datas \u0109i tiun roboton, bonvolu klasifikadi \u011din \u0109e https\://telegram.me/findbot?start\=weatherbot 76 | -------------------------------------------------------------------------------- /src/main/resources/strings_it.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= Faccio giochetti con le parole, fai un tentativo, potrei migliorare la tua giornata.\n\nPer ottenere le ultime traduzioni di Telegram per una lingua\: \n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per iOS.\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per Android.\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per Webogram.\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per Tdesktop.\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per l'app OSX.\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione per Windows Phone.\n\n2. Per ottenere una traduzione aggiornata per l'app beta di Android\:\n|-- %s LANG_CODE \: Ottieni l'ultima traduzione della beta per Android.. 2 | helpFiles= Lasciare un file in modo che altri lo trovino?\n\nCondividi i file tramite un link personalizzato\: \n|-- %s FILEID \: Ottieni il file tramite id.\n|-- %s \: Avvia il caricamento del file.\n|-- %s \: Seleziona una dei tuoi file per eliminarlo.\n|-- %s \: Mostra la lista dei tuoi file condivisi. 3 | helpDirections= La strada davanti, con tutte le buone intenzioni, è quella corretta che cerco di indicarti.\n\nPer ottenere le indicazioni stradali tra due località\: \n|-- %s \: Inizia a ricevere indicazioni 4 | sendFileToUpload= Per favore inviami il file che desideri condividere. Assicurati di inviarlo come file, non come immagine o video. 5 | fileUploaded= Grandioso, il tuo file è stato caricato. Invia questo link a chiunque tu voglia e potrà scaricare il file\: \n\n 6 | deleteUploadedFile= Per favore, seleziona il file che vuoi eliminare\: 7 | fileDeleted= Il file è stato eliminato 8 | wrongFileId= Ci spiace, non siamo riusciti a trovare un file con quell'ID. O è stato fatto un errore o il file è già stato eliminato. 9 | listOfFiles= Questa è la lista dei file che hai condiviso fino ad ora\: 10 | noFiles= Al momento non hai condiviso alcun file. 11 | processFinished= Il processo corrente è stato annullato. 12 | uploadedFileURL= https\://telegram.me/filesbot?start\= 13 | initDirections= Per favore rispondi a questo messaggio con il tuo punto di partenza. 14 | sendDestination= Per favore rispondi a questo messaggio con la tua destinazione. 15 | youNeedReplyDirections= Mi spiace, non posso aiutarti finché non rispondi al messaggio che ti ho mandato. 16 | chooselanguage= Per favore, selezionare una lingua dalla lista per cambiarla. 17 | errorLanguage= Non supportiamo quella lingua o è stato fatto un errore. Il processo è stato annullato. 18 | directionsInit= %s è %s lontano da %s e ci vuole %s per arrivare, seguendo queste direzioni\:\n 19 | directionsNotFound= Indicazioni tra %s e %s non trovate. 20 | errorFetchingDirections= Errore nel caricare le indicazioni 21 | directionsStep= %s durante %s (%s) 22 | languageModified= Le impostazioni della lingua sono state aggiornate 23 | 24 | 25 | helpWeatherMessage= Curiosità sul meteo?\nBasta inviare questo comando per conoscerlo meglio\:\n\t\t%s Ottieni le previsioni attuali per una citta o una posizione\n\t\t%s Ottieni 3 giorni di previsioni per una città o una posizione\n\t\t%s Imposta gli avvisi \n\t\t%s Imposta la lingua delle previsioni\n\t\t%s Cambia l'unità di misura 26 | forecast=%sPrevisioni 27 | current=%sAttuale 28 | help=%sAiuto 29 | settings=%sImpostazioni 30 | cancel=%sAnnulla 31 | location=%sPosizione 32 | new=%sNuovo 33 | languages=%sLingua 34 | alerts=%sAllarmi 35 | units=%sUnità 36 | back=%sIndietro 37 | delete=%sElimina 38 | showList=%sMostra lista 39 | rateMe=%sVotami 40 | metricSystem=Sistema Metrico 41 | imperialSystem= Sistema Imperiale 42 | selectLanguage=La lingua attuale è %s. Seleziona una lingua dalla lista per cambiarla in quella. 43 | languageUpdated=Le impostazioni della lingua sono state aggiornate 44 | errorLanguageNotFound= Non supportiamo quella lingua o c'è stato un errore. Per favore selezionane una dalla lista. 45 | selectUnits=L'unità di misura attuale impostata è %s. Seleziona un'opzione dalla lista per cambiarla. 46 | unitsUpdated=Le tue impostazioni di unità di misura sono state aggiornate. 47 | errorUnitsNotFound= Non supportiamo quell'unità di misura o c'è stato un errore. Per favore selezionane uno dalla lista. 48 | onWeatherNewCommand=Per favore inviami la città che ti interessa, e usa questo formato\: CITTÀ,PAESE 49 | onWeatherLocationCommand=Per favore inviami la posizione che ti interessa. 50 | onCurrentCommandFromHistory=Seleziona un'opzione dalle tue richieste recenti, "nuovo" per inviare una nuova città o "posizione" per inviare una posizione e ricevere le previsioni attuali. 51 | onCurrentCommandWithoutHistory=Seleziona "nuovo" per inviare una nuova città o "posizione" per inviare una posizione e ricevere le previsioni attuali. 52 | onForecastCommandFromHistory=Seleziona un'opzione dalle tue richieste recenti, "nuovo" per inviare una nuova città o "posizione" per inviare una posizione e ricevere le previsioni per 3 giorni. 53 | onForecastCommandWithoutHistory=Seleziona "nuovo" per inviare una nuova città o "posizione" per inviare una posizione e ricevere le previsioni per 3 giorni. 54 | weatherCurrent= Le previsioni per %s sono\:\n\n%s 55 | currentWeatherPartMetric=\t- Meteo\: %s\n\t- Nuvolosità\: %s\n\t- Temperatura\: %s ºC\n\n 56 | currentWeatherPartImperial=\t- Meteo\: %s\n\t- Nuvolosità\: %s\n\t- Temperatura\: %s ºC\n\n 57 | weatherForcast= Le previsioni per %s saranno\:\n\n%s 58 | forecastWeatherPartMetric= %s Il %s \n\t- Previsione\: %s\n\t- Temperatura massima\: %s ºC\n\t- Temperatura minima\: %s ºC\n\n 59 | forecastWeatherPartImperial= %s Il %s \n\t- Previsione\: %s\n\t- Temperatura massima\: %s ºC\n\t- Temperatura minima\: %s ºC\n\n 60 | weatherAlert= Le previsioni per %s saranno\:\n\n%s 61 | alertWeatherPartMetric=\t- Previsione\: %s\n\t- Temperatura massima\: %s ºC\n\t- Temperatura minima\: %s ºC\n\n 62 | alertWeatherPartImperial=\t- Previsione\: %s\n\t- Temperatura massima\: %s ºC\n\t- Temperatura minima\: %s ºC\n\n 63 | chooseOption=Per favore, seleziona un'opzione dal menu. 64 | backToMainMenu=Processo annullato, ritorna nel menu principale. 65 | onSettingsCommand=Per favore, scegli un'opzione\:\n\t\t%s Lingue\: Seleziona la lingua per le previsioni\n\t\t%s Unità\: Seleziona l'unità di misura\n\t\t%s Avvisi\: Seleziona il numero di avvisi giornalieri\n\t\t%s Indietro\: Ritorna al menu principale 66 | alertsMenuMessage=Qui puoi gestire i tuoi allarmi o aggiungerne di nuovi. 67 | chooseNewAlertCity=Per quale città si desidera impostare un avviso? Seleziona un'opzione dalle richieste recenti 68 | newAlertSaved=%s Il tuo avviso per %s è stato creato correttamente, riceverai l'aggiornamento meteo due volte al giorno. 69 | initialAlertList=Hai %d allarmi\:\n\n%s 70 | partialAlertList=%s%s\n 71 | noAlertList=Non sono riuscito a trovare alcun allarme per te. 72 | alertDeleted=L'allarme selezionato è stato eliminato. 73 | cityNotFound= Città non trovata 74 | errorFetchingWeather= Siamo spiacenti, c'è stato un errore nel caricare le previsioni. 75 | rateMeMessage=Se ti piace questo bot, per favore votalo su https\://telegram.me/findbot?start\=weatherbot 76 | -------------------------------------------------------------------------------- /src/main/resources/strings_nl.properties: -------------------------------------------------------------------------------- 1 | helpTransifex= Schieten woorden tekort of heb je last van een babylonische dwaling? Probeer mij dan eens uit voor een Telegram-vertaling.\n\nOm de laatste vertaling voor Telegram te verkrijgen voor een taal\:\n|-- %s TAAL_CODE \: De meest recente iOS-vertaling.\n|-- %s TAAL_CODE \: De meest recente Android-vertaling\n|-- %s TAAL_CODE \: De meest recente Webogram-vertaling.\n|-- %s TAAL_CODE \: De meest recente Tdesktop-vertaling.\n|-- %s TAAL_CODE \: De meest recente OSX-vertaling.\n|-- %s TAAL_CODE \: De meest recente Windows Phone-vertaling.\n\n2. Alleen voor Android beta in je huidige taal\:\n|-- %s TAAL_CODE \: De meest Android-beta vertaling\n\n(TAAL_CODE voorbeeld\: nl) 2 | helpFiles= Het downloadverbod is van kracht want Brein greep de macht, je downloads zijn nu officieel illegaal, maar een bestandje met mij delen, zodat anderen het kunnen vinden, is dat eigenlijk wel zo abnormaal?\n\nBestanden delen via een link\:\n|-- %s BESTANDSID \: Ontvang een bestand met ID.\n|-- %s \: Begin een bestandsupload.\n|-- %s \: Kies een bestand om te verwijderen.\n|-- %s \: Geef een lijst met gedeelde bestanden weer. 3 | helpDirections= Vele wegen leiden naar Rome, maar terug naar Leiden is toch nog een hele klus, bent u het zoeken op de kaart al zat? Vraag het aan mij en ik breng u op het rechte pad\!\n\nOm een routebeschrijving tussen twee locaties te ontvangen\:\n|-- %s \: Start een routebeschrijving. 4 | sendFileToUpload= Stuur mij het bestand dat je wilt delen. Zorg ervoor dat je het stuurt als een bestand en niet als een afbeelding of video. 5 | fileUploaded= Je bestand is geüpload. Stuur deze link naar iedereen die je maar wilt en zij kunnen het bestand ermee downloaden\:\n\n 6 | deleteUploadedFile= Selecteer het bestand dat je wilt verwijderen\: 7 | fileDeleted= Het bestand is verwijderd. 8 | wrongFileId= Sorry, ik kan het bestand met dat ID niet vinden. Mogelijk is er een typefout gemaakt of is het bestand al verwijderd. 9 | listOfFiles= Dit zijn je gedeelde bestanden op het moment\: 10 | noFiles= Je hebt nog geen bestanden gedeeld. 11 | processFinished= Het huidige proces is geannuleerd. 12 | uploadedFileURL= https\://telegram.me/filesbot?start\= 13 | initDirections= Beantwoord dit bericht met je vertrekpunt. 14 | sendDestination= Beantwoord dit bericht met je bestemming. 15 | youNeedReplyDirections= Het spijt me, ik kan je niet helpen tenzij je het bericht dat ik stuurde beantwoord. 16 | chooselanguage= Kies een taal uit de lijst om de taal te wijzigen. 17 | errorLanguage= Deze taal ondersteunen we niet of je hebt een typefout gemaakt. Het proces is geannuleerd. 18 | directionsInit= %s is %s verwijderd van %s en het duurt %s om er te komen met de volgende routebeschrijving\:\n\n 19 | directionsNotFound= Routebeschrijving tussen %s en %s niet gevonden. 20 | errorFetchingDirections= Ophalen van routebeschrijving mislukt 21 | directionsStep= %s voor %s (%s) 22 | languageModified= Je taalinstelling is bijgewerkt. 23 | 24 | 25 | helpWeatherMessage= Zon of regen, hagel of ijs?\nVraag het aan mij en ik maak u weer wijs\!\n\n\t\t%s Om de huidige weersvoorspelling te ontvangen\n\t\t%s Voor een drie-daagse weersvoorspelling\n\t\t%s Waarschuwingen instellen\n\t\t%s Kies een weersvoorspellingstaal\n\t\t%s Kies een eenhedenstelsel 26 | forecast=%sVoorspelling 27 | current=%sHuidig 28 | help=%sHelp 29 | settings=%sInstellingen 30 | cancel=%sAnnuleren 31 | location=%sLocatie 32 | new=%sNieuw 33 | languages=%sTalen 34 | alerts=%sWaarschuwingen 35 | units=%sEenheden 36 | back=%sTerug 37 | delete=%sVerwijderen 38 | showList=%sLijst weergeven 39 | rateMe=%sBeoordeel mij 40 | metricSystem=Metriek stelsel 41 | imperialSystem= Imperiaal stelsel 42 | selectLanguage=Je huidige taal is %s. Kies een taal van de lijst om deze instelling te wijzigen. 43 | languageUpdated=Je taalinstelling is bijgewerkt. 44 | errorLanguageNotFound= Deze taal ondersteunen we niet of je hebt een typefout gemaakt. Kies een taal uit de lijst. 45 | selectUnits=Je huidige eenhedenstelsel is %s. Kies een optie van de lijst om deze instelling te wijzigen. 46 | unitsUpdated=Je gekozen eenheid is bijgewerkt. 47 | errorUnitsNotFound= Dit stelsel ondersteunen we helaas niet, of je hebt een typefout gemaakt. Kies een stelsel van de lijst. 48 | onWeatherNewCommand=Stuur me de stad waarin je geïnteresseerd bent. Gebruik dit formaat\: STAD,LAND 49 | onWeatherLocationCommand=Stuur me de locatie waarin je geïnteresseerd bent. 50 | onCurrentCommandFromHistory=Kies een optie van je recente verzoeken, "nieuw" om een stad te sturen of "locatie" om mij een locatie te sturen en de huidige weersvoorspelling te ontvangen. 51 | onCurrentCommandWithoutHistory=Kies "nieuw" om een stad te sturen of "locatie" om mij een locatie te sturen de huidige weersvoorspelling te ontvangen. 52 | onForecastCommandFromHistory=Kies een optie van je recente verzoeken, "nieuw" om een stad te sturen of "locatie" om mij een locatie te sturen en een drie-daagse weersvoorspelling te ontvangen. 53 | onForecastCommandWithoutHistory=Kies "nieuw" om een stad te sturen of "locatie" om mij een locatie te sturen en een drie-daagse weersvoorspelling te ontvangen. 54 | weatherCurrent= De weersverwachting voor %s is\:\n\n%s 55 | currentWeatherPartMetric=\t- Weer\: %s\n\t- Bewolking\: %s\n\t- Temperatuur\: %s ºC\n 56 | currentWeatherPartImperial=\t- Weer\: %s\n\t- Bewolking\: %s\n\t- Temperatuur\: %s ºF\n 57 | weatherForcast= De weersverwachting voor %s\:\n\n%s 58 | forecastWeatherPartMetric= %s op %s \n\t- Voorspelling\: %s\n\t- Hoogste temperatuur\: %s ºC\n\t- Laagste temperatuur\: %s ºC\n 59 | forecastWeatherPartImperial= %s on %s \n\t- Voorspelling\: %s\n\t- Hoogste temperatuur\: %s ºF\n\t- Laagste temperatuur\: %s ºF\n 60 | weatherAlert= De weersverwachting voor %s\:\n\n%s 61 | alertWeatherPartMetric=\t- Voorspelling\: %s\n\t- Hoogste temperatuur\: %s ºC\n\t- Laagste temperatuur\: %s ºC\n 62 | alertWeatherPartImperial=\t- Voorspelling\: %s\n\t- Hoogste temperatuur\: %s ºF\n\t- Laagste temperatuur\: %s ºF\n 63 | chooseOption=Selecteer een optie uit het menu. 64 | backToMainMenu=Proces geannuleerd, terug naar het hoofdmenu. 65 | onSettingsCommand=Maak een keuze\:\n\t\t%s Taal\: Kies een weersvoorspellings-taal\n\t\t%s Eenheid\: Wijzig je voorkeurseenheid.\n\t\t%s Alerts\: dagelijkse waarschuwingen instellen\n\t\t%s Back\: Terug naar het hoofdmenu 66 | alertsMenuMessage=Hier kan je je waarschuwingen beheren of nieuwe toevoegen. 67 | chooseNewAlertCity=Voor welke stad wil je een waarschuwing instellen? Kies een optie van je recente verzoeken. 68 | newAlertSaved=%s Je waarschuwing voor %s is aangemaakt, je ontvangt twee keer per dag een weersvoorspelling. 69 | initialAlertList=Je hebt %d waarschuwingen\:\n\n%s 70 | partialAlertList=%s%s\n 71 | noAlertList=Geen waarschuwingen gevonden. 72 | alertDeleted=De geselecteerde waarschuwing is verwijderd. 73 | cityNotFound= Stad niet gevonden. 74 | errorFetchingWeather= Sorry, er is iets misgegaan bij het ophalen van het weer. 75 | rateMeMessage=Als je deze bot leuk vindt dan kan je hem hier beoordelen https\://telegram.me/findbot?start\=weatherbot 76 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/ChannelHandlers.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 5 | import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; 6 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 7 | import org.telegram.telegrambots.meta.api.objects.Update; 8 | import org.telegram.telegrambots.meta.api.objects.message.Message; 9 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ForceReplyKeyboard; 10 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; 11 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 12 | import org.telegram.telegrambots.meta.generics.TelegramClient; 13 | 14 | import java.io.InvalidObjectException; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | /** 18 | * @author Ruben Bermudez 19 | * @version 1.0 20 | * Handler for updates to channel updates bot 21 | * This is a use case that will send a message to a channel if it is added as an admin to it. 22 | */ 23 | @Slf4j 24 | public class ChannelHandlers implements LongPollingSingleThreadUpdateConsumer { 25 | private static final int WAITINGCHANNEL = 1; 26 | 27 | private static final String HELP_TEXT = "Send me the channel username where you added me as admin."; 28 | private static final String CANCEL_COMMAND = "/stop"; 29 | private static final String AFTER_CHANNEL_TEXT = "A message to provided channel will be sent if the bot was added to it as admin."; 30 | private static final String WRONG_CHANNEL_TEXT = "Wrong username, please remember to add *@* before the username and send only the username."; 31 | private static final String CHANNEL_MESSAGE_TEXT = "This message was sent by *@updateschannelbot*. Enjoy!"; 32 | private static final String ERROR_MESSAGE_TEXT = "There was an error sending the message to channel *%s*, the error was: ```%s```"; 33 | 34 | private final ConcurrentHashMap userState = new ConcurrentHashMap<>(); 35 | private final TelegramClient telegramClient; 36 | 37 | public ChannelHandlers(String botToken) { 38 | telegramClient = new OkHttpTelegramClient(botToken); 39 | } 40 | 41 | @Override 42 | public void consume(Update update) { 43 | try { 44 | Message message = update.getMessage(); 45 | if (message != null && message.hasText()) { 46 | try { 47 | handleIncomingMessage(message); 48 | } catch (InvalidObjectException e) { 49 | log.error("Channel Handler Error", e); 50 | } 51 | } 52 | } catch (Exception e) { 53 | log.error("Error handling channel message", e); 54 | } 55 | } 56 | 57 | // region Incoming messages handlers 58 | 59 | private void handleIncomingMessage(Message message) throws InvalidObjectException { 60 | int state = userState.getOrDefault(message.getFrom().getId(), 0); 61 | switch(state) { 62 | case WAITINGCHANNEL: 63 | onWaitingChannelMessage(message); 64 | break; 65 | default: 66 | sendHelpMessage(message.getChatId(), message.getMessageId(), null); 67 | userState.put(message.getFrom().getId(), WAITINGCHANNEL); 68 | break; 69 | } 70 | } 71 | 72 | private void onWaitingChannelMessage(Message message) { 73 | try { 74 | if (message.getText().equals(CANCEL_COMMAND)) { 75 | userState.remove(message.getFrom().getId()); 76 | sendHelpMessage(message.getChatId(), message.getMessageId(), null); 77 | } else { 78 | if (message.getText().startsWith("@") && !message.getText().trim().contains(" ")) { 79 | telegramClient.execute(getMessageToChannelSent(message)); 80 | sendMessageToChannel(message.getText(), message); 81 | userState.remove(message.getFrom().getId()); 82 | } else { 83 | telegramClient.execute(getWrongUsernameMessage(message)); 84 | } 85 | } 86 | } catch (TelegramApiException e) { 87 | log.error("Error waiting channel message", e); 88 | } 89 | } 90 | 91 | private void sendMessageToChannel(String username, Message message) { 92 | SendMessage sendMessage = new SendMessage(username.trim(), CHANNEL_MESSAGE_TEXT); 93 | sendMessage.enableMarkdown(true); 94 | 95 | try { 96 | telegramClient.execute(sendMessage); 97 | } catch (TelegramApiException e) { 98 | sendErrorMessage(message, e.getMessage()); 99 | } 100 | } 101 | 102 | private void sendErrorMessage(Message message, String errorText) { 103 | SendMessage sendMessage = new SendMessage(String.valueOf(message.getChatId()), String.format(ERROR_MESSAGE_TEXT, message.getText().trim(), errorText.replace("\"", "\\\""))); 104 | sendMessage.enableMarkdown(true); 105 | sendMessage.setReplyToMessageId(message.getMessageId()); 106 | sendMessage.enableMarkdown(true); 107 | 108 | try { 109 | telegramClient.execute(sendMessage); 110 | } catch (TelegramApiException e) { 111 | log.error("Error sending error message", e); 112 | } 113 | } 114 | 115 | private static SendMessage getWrongUsernameMessage(Message message) { 116 | SendMessage sendMessage = new SendMessage(String.valueOf(message.getChatId()), WRONG_CHANNEL_TEXT); 117 | sendMessage.enableMarkdown(true); 118 | sendMessage.setReplyToMessageId(message.getMessageId()); 119 | 120 | ForceReplyKeyboard forceReplyKeyboard = new ForceReplyKeyboard(); 121 | forceReplyKeyboard.setSelective(true); 122 | sendMessage.setReplyMarkup(forceReplyKeyboard); 123 | 124 | sendMessage.enableMarkdown(true); 125 | return sendMessage; 126 | } 127 | 128 | private static SendMessage getMessageToChannelSent(Message message) { 129 | SendMessage sendMessage = new SendMessage(String.valueOf(message.getChatId()), AFTER_CHANNEL_TEXT); 130 | sendMessage.enableMarkdown(true); 131 | sendMessage.setReplyToMessageId(message.getMessageId()); 132 | return sendMessage; 133 | } 134 | 135 | private void sendHelpMessage(Long chatId, Integer messageId, ReplyKeyboardMarkup replyKeyboardMarkup) { 136 | SendMessage sendMessage = new SendMessage(String.valueOf(chatId), HELP_TEXT); 137 | sendMessage.enableMarkdown(true); 138 | sendMessage.setReplyToMessageId(messageId); 139 | if (replyKeyboardMarkup != null) { 140 | sendMessage.setReplyMarkup(replyKeyboardMarkup); 141 | } 142 | try { 143 | telegramClient.execute(sendMessage); 144 | } catch (TelegramApiException e) { 145 | log.error("Error sending help message", e); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/Emoji.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | /** 4 | * @author Ruben Bermudez 5 | * @version 1.0 6 | * @brief Enumerate of emojis with unicode chars 7 | * @date 02 of July of 2015 8 | */ 9 | public enum Emoji { 10 | // Emoticones group 11 | GRINNING_FACE_WITH_SMILING_EYES('\uD83D', '\uDE01'), 12 | FACE_WITH_TEARS_OF_JOY('\uD83D', '\uDE02'), 13 | SMILING_FACE_WITH_OPEN_MOUTH('\uD83D', '\uDE03'), 14 | SMILING_FACE_WITH_OPEN_MOUTH_AND_SMILING_EYES('\uD83D', '\uDE04'), 15 | SMILING_FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT('\uD83D', '\uDE05'), 16 | SMILING_FACE_WITH_OPEN_MOUTH_AND_TIGHTLY_CLOSED_EYES('\uD83D', '\uDE06'), 17 | WINKING_FACE('\uD83D', '\uDE09'), 18 | SMILING_FACE_WITH_SMILING_EYES('\uD83D', '\uDE0A'), 19 | FACE_SAVOURING_DELICIOUS_FOOD('\uD83D', '\uDE0B'), 20 | RELIEVED_FACE('\uD83D', '\uDE0C'), 21 | SMILING_FACE_WITH_HEART_SHAPED_EYES('\uD83D', '\uDE0D'), 22 | SMIRKING_FACE('\uD83D', '\uDE0F'), 23 | UNAMUSED_FACE('\uD83D', '\uDE12'), 24 | FACE_WITH_COLD_SWEAT('\uD83D', '\uDE13'), 25 | PENSIVE_FACE('\uD83D', '\uDE14'), 26 | CONFOUNDED_FACE('\uD83D', '\uDE16'), 27 | FACE_THROWING_A_KISS('\uD83D', '\uDE18'), 28 | KISSING_FACE_WITH_CLOSED_EYES('\uD83D', '\uDE1A'), 29 | FACE_WITH_STUCK_OUT_TONGUE_AND_WINKING_EYE('\uD83D', '\uDE1C'), 30 | FACE_WITH_STUCK_OUT_TONGUE_AND_TIGHTLY_CLOSED_EYES('\uD83D', '\uDE1D'), 31 | DISAPPOINTED_FACE('\uD83D', '\uDE1E'), 32 | ANGRY_FACE('\uD83D', '\uDE20'), 33 | POUTING_FACE('\uD83D', '\uDE21'), 34 | CRYING_FACE('\uD83D', '\uDE22'), 35 | PERSEVERING_FACE('\uD83D', '\uDE23'), 36 | FACE_WITH_LOOK_OF_TRIUMPH('\uD83D', '\uDE24'), 37 | DISAPPOINTED_BUT_RELIEVED_FACE('\uD83D', '\uDE25'), 38 | FEARFUL_FACE('\uD83D', '\uDE28'), 39 | WEARY_FACE('\uD83D', '\uDE29'), 40 | SLEEPY_FACE('\uD83D', '\uDE2A'), 41 | TIRED_FACE('\uD83D', '\uDE2B'), 42 | LOUDLY_CRYING_FACE('\uD83D', '\uDE2D'), 43 | FACE_WITH_OPEN_MOUTH_AND_COLD_SWEAT('\uD83D', '\uDE30'), 44 | FACE_SCREAMING_IN_FEAR('\uD83D', '\uDE31'), 45 | ASTONISHED_FACE('\uD83D', '\uDE32'), 46 | FLUSHED_FACE('\uD83D', '\uDE33'), 47 | DIZZY_FACE('\uD83D', '\uDE35'), 48 | FACE_WITH_MEDICAL_MASK('\uD83D', '\uDE37'), 49 | GRINNING_CAT_FACE_WITH_SMILING_EYES('\uD83D', '\uDE38'), 50 | CAT_FACE_WITH_TEARS_OF_JOY('\uD83D', '\uDE39'), 51 | SMILING_CAT_FACE_WITH_OPEN_MOUTH('\uD83D', '\uDE3A'), 52 | SMILING_CAT_FACE_WITH_HEART_SHAPED_EYES('\uD83D', '\uDE3B'), 53 | CAT_FACE_WITH_WRY_SMILE('\uD83D', '\uDE3C'), 54 | KISSING_CAT_FACE_WITH_CLOSED_EYES('\uD83D', '\uDE3D'), 55 | POUTING_CAT_FACE('\uD83D', '\uDE3E'), 56 | CRYING_CAT_FACE('\uD83D', '\uDE3F'), 57 | WEARY_CAT_FACE('\uD83D', '\uDE40'), 58 | FACE_WITH_NO_GOOD_GESTURE('\uD83D', '\uDE45'), 59 | FACE_WITH_OK_GESTURE('\uD83D', '\uDE46'), 60 | PERSON_BOWING_DEEPLY('\uD83D', '\uDE47'), 61 | SEE_NO_EVIL_MONKEY('\uD83D', '\uDE48'), 62 | HEAR_NO_EVIL_MONKEY('\uD83D', '\uDE49'), 63 | SPEAK_NO_EVIL_MONKEY('\uD83D', '\uDE4A'), 64 | HAPPY_PERSON_RAISING_ONE_HAND('\uD83D', '\uDE4B'), 65 | PERSON_RAISING_BOTH_HANDS_IN_CELEBRATION('\uD83D', '\uDE4C'), 66 | PERSON_FROWNING('\uD83D', '\uDE4D'), 67 | PERSON_WITH_POUTING_FACE('\uD83D', '\uDE4E'), 68 | PERSON_WITH_FOLDED_HANDS('\uD83D', '\uDE4F'), 69 | 70 | // Dingbats group 71 | BLACK_SCISSORS(null, '\u2702'), 72 | WHITE_HEAVY_CHECK_MARK(null, '\u2705'), 73 | AIRPLANE(null, '\u2708'), 74 | ENVELOPE(null, '\u2709'), 75 | RAISED_FIST(null, '\u270A'), 76 | RAISED_HAND(null, '\u270B'), 77 | VICTORY_HAND(null, '\u270C'), 78 | PENCIL(null, '\u270F'), 79 | BLACK_NIB(null, '\u2712'), 80 | HEAVY_CHECK_MARK(null, '\u2714'), 81 | HEAVY_MULTIPLICATION_X(null, '\u2716'), 82 | SPARKLES(null, '\u2728'), 83 | EIGHT_SPOKED_ASTERISK(null, '\u2733'), 84 | EIGHT_POINTED_BLACK_STAR(null, '\u2734'), 85 | SNOWFLAKE(null, '\u2744'), 86 | SPARKLE(null, '\u2747'), 87 | CROSS_MARK(null, '\u274C'), 88 | NEGATIVE_SQUARED_CROSS_MARK(null, '\u274E'), 89 | BLACK_QUESTION_MARK_ORNAMENT(null, '\u2753'), 90 | WHITE_QUESTION_MARK_ORNAMENT(null, '\u2754'), 91 | WHITE_EXCLAMATION_MARK_ORNAMENT(null, '\u2755'), 92 | HEAVY_EXCLAMATION_MARK_SYMBOL(null, '\u2757'), 93 | HEAVY_BLACK_HEART(null, '\u2764'), 94 | HEAVY_PLUS_SIGN(null, '\u2795'), 95 | HEAVY_MINUS_SIGN(null, '\u2796'), 96 | HEAVY_DIVISION_SIGN(null, '\u2797'), 97 | BLACK_RIGHTWARDS_ARROW(null, '\u27A1'), 98 | CURLY_LOOP(null, '\u27B0'), 99 | 100 | // Transport and map symbols Group 101 | ROCKET('\uD83D', '\uDE80'), 102 | RAILWAY_CAR('\uD83D', '\uDE83'), 103 | HIGH_SPEED_TRAIN('\uD83D', '\uDE84'), 104 | HIGH_SPEED_TRAIN_WITH_BULLET_NOSE('\uD83D', '\uDE85'), 105 | METRO('\uD83D', '\uDE87'), 106 | STATION('\uD83D', '\uDE89'), 107 | BUS('\uD83D', '\uDE8C'), 108 | BUS_STOP('\uD83D', '\uDE8F'), 109 | AMBULANCE('\uD83D', '\uDE91'), 110 | FIRE_ENGINE('\uD83D', '\uDE92'), 111 | POLICE_CAR('\uD83D', '\uDE93'), 112 | TAXI('\uD83D', '\uDE95'), 113 | AUTOMOBILE('\uD83D', '\uDE97'), 114 | RECREATIONAL_VEHICLE('\uD83D', '\uDE99'), 115 | DELIVERY_TRUCK('\uD83D', '\uDE9A'), 116 | SHIP('\uD83D', '\uDEA2'), 117 | SPEEDBOAT('\uD83D', '\uDEA4'), 118 | HORIZONTAL_TRAFFIC_LIGHT('\uD83D', '\uDEA5'), 119 | CONSTRUCTION_SIGN('\uD83D', '\uDEA7'), 120 | POLICE_CARS_REVOLVING_LIGHT('\uD83D', '\uDEA8'), 121 | TRIANGULAR_FLAG_ON_POST('\uD83D', '\uDEA9'), 122 | DOOR('\uD83D', '\uDEAA'), 123 | NO_ENTRY_SIGN('\uD83D', '\uDEAB'), 124 | SMOKING_SYMBOL('\uD83D', '\uDEAC'), 125 | NO_SMOKING_SYMBOL('\uD83D', '\uDEAD'), 126 | BICYCLE('\uD83D', '\uDEB2'), 127 | PEDESTRIAN('\uD83D', '\uDEB6'), 128 | MENS_SYMBOL('\uD83D', '\uDEB9'), 129 | WOMENS_SYMBOL('\uD83D', '\uDEBA'), 130 | RESTROOM('\uD83D', '\uDEBB'), 131 | BABY_SYMBOL('\uD83D', '\uDEBC'), 132 | TOILET('\uD83D', '\uDEBD'), 133 | WATER_CLOSET('\uD83D', '\uDEBE'), 134 | BATH('\uD83D', '\uDEC0'), 135 | 136 | // Weather 137 | UMBRELLA_WITH_RAIN_DROPS(null, '\u2614'), 138 | HIGH_VOLTAGE_SIGN(null, '\u26A1'), 139 | SNOWMAN_WITHOUT_SNOW(null, '\u26C4'), 140 | SUN_BEHIND_CLOUD(null, '\u26C5'), 141 | CLOSED_UMBRELLA('\uD83C', '\uDF02'), 142 | SUN_WITH_FACE('\uD83C', '\uDF1E'), 143 | FOGGY('\uD83C', '\uDF01'), 144 | CLOUD(null, '\u2601'), 145 | 146 | // Others 147 | LEFT_RIGHT_ARROW(null, '\u2194'), 148 | ALARM_CLOCK(null, '\u23F0'), 149 | SOON_WITH_RIGHTWARDS_ARROW_ABOVE('\uD83D', '\uDD1C'), 150 | EARTH_GLOBE_EUROPE_AFRICA('\uD83C', '\uDF0D'), 151 | GLOBE_WITH_MERIDIANS('\uD83C', '\uDF10'), 152 | STRAIGHT_RULER('\uD83D', '\uDCCF'), 153 | INFORMATION_SOURCE(null, '\u2139'), 154 | BLACK_RIGHT_POINTING_DOUBLE_TRIANGLE(null, '\u23E9'), 155 | BLACK_RIGHT_POINTING_TRIANGLE(null, '\u25B6'), 156 | BACK_WITH_LEFTWARDS_ARROW_ABOVE('\uD83D', '\uDD19'), 157 | WRENCH('\uD83D', '\uDD27'), 158 | DIGIT_THREE(null, '\u0033'), 159 | CLIPBOARD('\uD83D', '\uDCCB'), 160 | THUMBS_UP_SIGN('\uD83D', '\uDC4D'), 161 | WHITE_RIGHT_POINTING_BACKHAND_INDEX('\uD83D', '\uDC49'), 162 | TEAR_OFF_CALENDAR('\uD83D', '\uDCC6'), 163 | LARGE_ORANGE_DIAMOND('\uD83D', '\uDD36'), 164 | HUNDRED_POINTS_SYMBOL('\uD83D', '\uDCAF'), 165 | ROUND_PUSHPIN('\uD83D', '\uDCCD'), 166 | WAVING_HAND_SIGN('\uD83D', '\uDC4B'); 167 | 168 | Character firstChar; 169 | Character secondChar; 170 | 171 | Emoji(Character firstChar, Character secondChar) { 172 | this.firstChar = firstChar; 173 | this.secondChar = secondChar; 174 | } 175 | 176 | @Override 177 | public String toString() { 178 | StringBuilder sb = new StringBuilder(); 179 | 180 | if (this.firstChar != null) { 181 | sb.append(this.firstChar); 182 | } 183 | if (this.secondChar != null) { 184 | sb.append(this.secondChar); 185 | } 186 | 187 | return sb.toString(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/LocalisationService.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.InputStreamReader; 6 | import java.net.URL; 7 | import java.net.URLConnection; 8 | import java.util.ArrayList; 9 | import java.util.Enumeration; 10 | import java.util.List; 11 | import java.util.Locale; 12 | import java.util.MissingResourceException; 13 | import java.util.PropertyResourceBundle; 14 | import java.util.ResourceBundle; 15 | 16 | /** 17 | * @author Ruben Bermudez 18 | * @version 1.0 19 | */ 20 | public class LocalisationService { 21 | private static final String STRINGS_FILE = "strings"; 22 | private static final Object lock = new Object(); 23 | 24 | private static final List supportedLanguages = new ArrayList<>(); 25 | private static final Utf8ResourceBundle defaultLanguage; 26 | private static final Utf8ResourceBundle spanish; 27 | private static final Utf8ResourceBundle dutch; 28 | private static final Utf8ResourceBundle italian; 29 | private static final Utf8ResourceBundle portuguese; 30 | private static final Utf8ResourceBundle esperanto; 31 | 32 | static { 33 | synchronized (lock) { 34 | defaultLanguage = new Utf8ResourceBundle(STRINGS_FILE, Locale.ROOT); 35 | supportedLanguages.add(new Language("en", "English")); 36 | spanish = new Utf8ResourceBundle(STRINGS_FILE, new Locale("es", "ES")); 37 | supportedLanguages.add(new Language("es", "Español")); 38 | portuguese = new Utf8ResourceBundle(STRINGS_FILE, new Locale("pt", "PT")); 39 | supportedLanguages.add(new Language("pt", "Português")); 40 | dutch = new Utf8ResourceBundle(STRINGS_FILE, new Locale("nl", "NL")); 41 | supportedLanguages.add(new Language("nl", "Nederlands")); 42 | italian = new Utf8ResourceBundle(STRINGS_FILE, new Locale("it", "IT")); 43 | supportedLanguages.add(new Language("it", "Italiano")); 44 | esperanto = new Utf8ResourceBundle(STRINGS_FILE, new Locale("eo", "EO")); 45 | supportedLanguages.add(new Language("eo", "Esperanto")); 46 | } 47 | } 48 | 49 | /** 50 | * Get a string in default language (en) 51 | * 52 | * @param key key of the resource to fetch 53 | * @return fetched string or error message otherwise 54 | */ 55 | public static String getString(String key) { 56 | String result; 57 | try { 58 | result = defaultLanguage.getString(key); 59 | } catch (MissingResourceException e) { 60 | result = "String not found"; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | /** 67 | * Get a string in default language 68 | * 69 | * @param key key of the resource to fetch 70 | * @return fetched string or error message otherwise 71 | */ 72 | public static String getString(String key, String language) { 73 | String result; 74 | try { 75 | switch (language.toLowerCase()) { 76 | case "es": 77 | result = spanish.getString(key); 78 | break; 79 | case "pt": 80 | result = portuguese.getString(key); 81 | break; 82 | case "nl": 83 | result = dutch.getString(key); 84 | break; 85 | case "it": 86 | result = italian.getString(key); 87 | break; 88 | case "eo": 89 | result = esperanto.getString(key); 90 | break; 91 | default: 92 | result = defaultLanguage.getString(key); 93 | break; 94 | } 95 | } catch (MissingResourceException e) { 96 | result = defaultLanguage.getString(key); 97 | } 98 | 99 | return result; 100 | } 101 | 102 | public static List getSupportedLanguages() { 103 | return supportedLanguages; 104 | } 105 | 106 | public static Language getLanguageByCode(String languageCode) { 107 | return supportedLanguages.stream().filter(x -> x.getCode().equals(languageCode)).findFirst().orElse(null); 108 | } 109 | 110 | public static Language getLanguageByName(String languageName) { 111 | return supportedLanguages.stream().filter(x -> x.getName().equals(languageName)).findFirst().orElse(null); 112 | } 113 | 114 | public static String getLanguageCodeByName(String language) { 115 | return supportedLanguages.stream().filter(x -> x.getName().equals(language)) 116 | .map(Language::getCode).findFirst().orElse(null); 117 | } 118 | 119 | public static class Language { 120 | private String code; 121 | private String name; 122 | private String emoji; 123 | 124 | public Language(String code, String name) { 125 | this.code = code; 126 | this.name = name; 127 | } 128 | 129 | public String getCode() { 130 | return code; 131 | } 132 | 133 | public void setCode(String code) { 134 | this.code = code; 135 | } 136 | 137 | public String getName() { 138 | return name; 139 | } 140 | 141 | public void setName(String name) { 142 | this.name = name; 143 | } 144 | 145 | public String getEmoji() { 146 | return emoji; 147 | } 148 | 149 | public void setEmoji(String emoji) { 150 | this.emoji = emoji; 151 | } 152 | 153 | @Override 154 | public String toString() { 155 | if (emoji == null || emoji.isEmpty()) { 156 | return name; 157 | } else { 158 | return emoji + " " + name; 159 | } 160 | } 161 | } 162 | 163 | private static class Utf8ResourceBundle extends ResourceBundle { 164 | 165 | private static final String BUNDLE_EXTENSION = "properties"; 166 | private static final String CHARSET = "UTF-8"; 167 | private static final ResourceBundle.Control UTF8_CONTROL = new UTF8Control(); 168 | 169 | Utf8ResourceBundle(String bundleName, Locale locale) { 170 | setParent(ResourceBundle.getBundle(bundleName, locale, UTF8_CONTROL)); 171 | } 172 | 173 | @Override 174 | protected Object handleGetObject(String key) { 175 | return parent.getObject(key); 176 | } 177 | 178 | @Override 179 | public Enumeration getKeys() { 180 | return parent.getKeys(); 181 | } 182 | 183 | private static class UTF8Control extends Control { 184 | public ResourceBundle newBundle 185 | (String baseName, Locale locale, String format, ClassLoader loader, boolean reload) 186 | throws IllegalAccessException, InstantiationException, IOException { 187 | String bundleName = toBundleName(baseName, locale); 188 | String resourceName = toResourceName(bundleName, BUNDLE_EXTENSION); 189 | ResourceBundle bundle = null; 190 | InputStream stream = null; 191 | if (reload) { 192 | URL url = loader.getResource(resourceName); 193 | if (url != null) { 194 | URLConnection connection = url.openConnection(); 195 | if (connection != null) { 196 | connection.setUseCaches(false); 197 | stream = connection.getInputStream(); 198 | } 199 | } 200 | } else { 201 | stream = loader.getResourceAsStream(resourceName); 202 | } 203 | if (stream != null) { 204 | try { 205 | bundle = new PropertyResourceBundle(new InputStreamReader(stream, CHARSET)); 206 | } finally { 207 | stream.close(); 208 | } 209 | } 210 | return bundle; 211 | } 212 | } 213 | } 214 | } -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/ElektrollArtFanHandler.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 6 | import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; 7 | import org.telegram.telegrambots.meta.api.methods.AnswerCallbackQuery; 8 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 9 | import org.telegram.telegrambots.meta.api.methods.updatingmessages.EditMessageText; 10 | import org.telegram.telegrambots.meta.api.objects.CallbackQuery; 11 | import org.telegram.telegrambots.meta.api.objects.Update; 12 | import org.telegram.telegrambots.meta.api.objects.message.Message; 13 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.InlineKeyboardMarkup; 14 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardButton; 15 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.InlineKeyboardRow; 16 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 17 | import org.telegram.telegrambots.meta.generics.TelegramClient; 18 | 19 | import java.util.ArrayList; 20 | 21 | 22 | /** 23 | * @author Clevero 24 | * @version 1.0 25 | * Handler for updates to ElektrollArtFanBot 26 | * This bot is an example for using inline buttons, here to make a gallery. 27 | * Bot contains some images from ElektrollArt that are all licensed under creative commons 28 | */ 29 | @Slf4j 30 | public class ElektrollArtFanHandler implements LongPollingSingleThreadUpdateConsumer { 31 | 32 | private final ArrayList urls; 33 | private final String BACK = "⬅️ Back"; 34 | private final String NEXT = "Next ➡️"; 35 | private final String INDEX_OUT_OF_RANGE = "Requested index is out of range!"; 36 | 37 | private final TelegramClient telegramClient; 38 | 39 | public ElektrollArtFanHandler(String botToken) { 40 | telegramClient = new OkHttpTelegramClient(botToken); 41 | this.urls = new ArrayList<>(); 42 | this.addUrls(); 43 | } 44 | 45 | private void addUrls(){ 46 | 47 | /* 48 | * Just some sample links of my fav images from elektrollart.de 49 | */ 50 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=2964", "http://www.elektrollart.de/wp-content/uploads/deer-724x1024.png", "Deer Nature (cc-by)"}); 51 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=2960", "http://www.elektrollart.de/wp-content/uploads/butterfly_wallpaper_by_elektroll-d424m9d-1024x576.png", "Butterfly Wallpaper (cc-by)"}); 52 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=2897", "http://www.elektrollart.de/wp-content/uploads/ilovefs_wallpaper-1024x576.png", "I Love Free Software – Wallpaper (CC0)"}); 53 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=3953", "http://www.elektrollart.de/wp-content/uploads/diaspora_wallpaper_by_elektroll-d4anyj4-1024x576.png", "diaspora Wallpaper (CC-BY-SA)"}); 54 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=549", "http://www.elektrollart.de/wp-content/uploads/diaspora_flower-1024x576.png", "Diaspora Digital Wallpaper (CC-BY-SA)"}); 55 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=534", "http://www.elektrollart.de/wp-content/uploads/debian-butterfly-1024x576.png", "Debian-Butterfly Wallpaper (CC-BY-SA)"}); 56 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=531", "http://www.elektrollart.de/wp-content/uploads/cc-white-1920x1080-1024x576.png", "CC-Wallpaper (CC-BY-NC-SA)"}); 57 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=526", "http://www.elektrollart.de/wp-content/uploads/debian-gal-1920x1080-1024x576.png", "Debian Wallpaper (CC-BY-SA)"}); 58 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=523", "http://www.elektrollart.de/wp-content/uploads/Ubuntusplash-1920x1080-1024x576.png", "Ubuntu Wallpaper (CC-BY-NC-SA)"}); 59 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=559", "http://www.elektrollart.de/wp-content/uploads/skullgirll_a-1024x576.png", "Skullgirl Wallpapers (CC-BY-NC-SA)"}); 60 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=559", "http://www.elektrollart.de/wp-content/uploads/skullgirll_b-1024x576.png", "Skullgirl Wallpapers (CC-BY-NC-SA)"}); 61 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=847", "http://www.elektrollart.de/wp-content/uploads/archlinux_wallpaper-1024x576.png", "ArchLinux (CC0)"}); 62 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=1381", "http://www.elektrollart.de/wp-content/uploads/tuxxi-small-724x1024.png", "Piep (CC-BY)"}); 63 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=4264", "http://www.elektrollart.de/wp-content/uploads/Thngs_left_unsaid-724x1024.jpg", "Things Left Unsaid (CC-BY)"}); 64 | this.urls.add(new String[]{"http://www.elektrollart.de/?p=2334", "http://www.elektrollart.de/wp-content/uploads/redpanda-1024x826.png", "<3 mozilla (CC0)"}); 65 | } 66 | 67 | @Override 68 | public void consume(Update update) { 69 | 70 | //check if the update has a message 71 | if (update.hasMessage()) { 72 | Message message = update.getMessage(); 73 | 74 | //check if the message contains a text 75 | if (message.hasText()) { 76 | String input = message.getText(); 77 | 78 | if (input.equals("/start")) { 79 | /* 80 | * we just add the first link from our array 81 | * 82 | * We use markdown to embedd the image 83 | */ 84 | SendMessage sendMessagerequest = new SendMessage(message.getChatId().toString(), "[​](" + this.urls.get(0)[1] + ")"); 85 | sendMessagerequest.enableMarkdown(true); 86 | 87 | sendMessagerequest.setReplyMarkup(this.getGalleryView(0, -1)); 88 | 89 | 90 | try { 91 | telegramClient.execute(sendMessagerequest); 92 | } catch (TelegramApiException e) { 93 | log.error("Error sending start message", e); 94 | } 95 | } 96 | } 97 | } else if (update.hasCallbackQuery()) { 98 | CallbackQuery callbackquery = update.getCallbackQuery(); 99 | String[] data = callbackquery.getData().split(":"); 100 | int index = Integer.parseInt(data[2]); 101 | 102 | if (data[0].equals("gallery")) { 103 | 104 | InlineKeyboardMarkup markup = null; 105 | 106 | if (data[1].equals("back")) { 107 | markup = this.getGalleryView(Integer.parseInt(data[2]), 1); 108 | if (index > 0) { 109 | index--; 110 | } 111 | } else if (data[1].equals("next")) { 112 | markup = this.getGalleryView(Integer.parseInt(data[2]), 2); 113 | if (index < this.urls.size() - 1) { 114 | index++; 115 | } 116 | } else if (data[1].equals("text")) { 117 | try { 118 | this.sendAnswerCallbackQuery("Please use one of the given actions below, instead.", false, callbackquery); 119 | } catch (TelegramApiException e) { 120 | log.error("Send text response", e); 121 | } 122 | } 123 | 124 | if (markup == null) { 125 | try { 126 | this.sendAnswerCallbackQuery(INDEX_OUT_OF_RANGE, false, callbackquery); 127 | } catch (TelegramApiException e) { 128 | log.error("Send index out of range response", e); 129 | } 130 | } else { 131 | 132 | EditMessageText editMarkup = new EditMessageText("[​](" + this.urls.get(index)[1] + ")"); 133 | editMarkup.setChatId(callbackquery.getMessage().getChatId().toString()); 134 | editMarkup.setInlineMessageId(callbackquery.getInlineMessageId()); 135 | editMarkup.enableMarkdown(true); 136 | editMarkup.setMessageId(callbackquery.getMessage().getMessageId()); 137 | editMarkup.setReplyMarkup(markup); 138 | try { 139 | telegramClient.execute(editMarkup); 140 | } catch (TelegramApiException e) { 141 | log.error("Error updating markup", e); 142 | } 143 | } 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * 150 | * @param text The text that should be shown 151 | * @param alert If the text should be shown as a alert or not 152 | * @param callbackquery 153 | * @throws TelegramApiException 154 | */ 155 | private void sendAnswerCallbackQuery(String text, boolean alert, CallbackQuery callbackquery) throws TelegramApiException{ 156 | AnswerCallbackQuery answerCallbackQuery = new AnswerCallbackQuery(callbackquery.getId()); 157 | answerCallbackQuery.setShowAlert(alert); 158 | answerCallbackQuery.setText(text); 159 | telegramClient.execute(answerCallbackQuery); 160 | } 161 | 162 | /** 163 | * 164 | * @param index Index of the current image 165 | * @param action What button was clicked 166 | * @return 167 | */ 168 | private InlineKeyboardMarkup getGalleryView(int index, int action){ 169 | /* 170 | * action = 1 -> back 171 | * action = 2 -> next 172 | * action = -1 -> nothing 173 | */ 174 | 175 | if(action == 1 && index > 0){ 176 | index--; 177 | } 178 | else if((action == 1 && index == 0)){ 179 | return null; 180 | } 181 | else if(action == 2 && index >= this.urls.size()-1){ 182 | return null; 183 | } 184 | else if(action == 2){ 185 | index++; 186 | } 187 | 188 | return InlineKeyboardMarkup 189 | .builder() 190 | .keyboardRow(new InlineKeyboardRow( 191 | InlineKeyboardButton.builder().text(this.urls.get(index)[2]).callbackData("gallery:text:" + index).build() 192 | )) 193 | .keyboardRow(new InlineKeyboardRow( 194 | InlineKeyboardButton.builder().text(BACK).callbackData("gallery:back:" + index).build(), 195 | InlineKeyboardButton.builder().text(NEXT).callbackData("gallery:next:" + index).build() 196 | )) 197 | .keyboardRow(new InlineKeyboardRow( 198 | InlineKeyboardButton.builder().text("Link").callbackData(this.urls.get(index)[0]).build() 199 | )) 200 | .build(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/RaeService.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.OkHttpClient; 5 | import okhttp3.Request; 6 | import okhttp3.Response; 7 | import okhttp3.ResponseBody; 8 | import org.jsoup.Jsoup; 9 | import org.jsoup.nodes.Document; 10 | import org.jsoup.nodes.Element; 11 | import org.jsoup.select.Elements; 12 | 13 | import java.io.IOException; 14 | import java.net.URLEncoder; 15 | import java.nio.charset.StandardCharsets; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.Objects; 21 | 22 | /** 23 | * @author Ruben Bermudez 24 | * @version 1.0 25 | * Rae service 26 | */ 27 | @Slf4j 28 | public class RaeService { 29 | private static final String BASEURL = "http://dle.rae.es/srv/"; ///< Base url for REST 30 | private static final String SEARCHEXACTURL = "search?m=30&w="; 31 | private static final String SEARCHWORDURL = "search?m=form&w="; 32 | private static final String WORDLINKBYID = "http://dle.rae.es/?id="; 33 | 34 | private final OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build(); 35 | 36 | public List getResults(String query) { 37 | List results = new ArrayList<>(); 38 | 39 | String completeURL; 40 | try { 41 | completeURL = BASEURL + SEARCHEXACTURL + URLEncoder.encode(query, StandardCharsets.UTF_8); 42 | 43 | Request request = new Request.Builder() 44 | .url(completeURL) 45 | .header("charset", StandardCharsets.UTF_8.name()) 46 | .header("content-type", "application/json") 47 | .get() 48 | .build(); 49 | 50 | try (Response response = okHttpClient.newCall(request).execute()) { 51 | if (response.isSuccessful()) { 52 | try (ResponseBody body = response.body()) { 53 | if (body != null) { 54 | Document document = Jsoup.parse(body.string()); 55 | Element article = document.getElementsByTag("article").first(); 56 | String articleId = null; 57 | if (article != null) { 58 | articleId = article.attributes().get("id"); 59 | } 60 | Elements elements = document.select(".j"); 61 | 62 | if (elements.isEmpty()) { 63 | results = getResultsWordSearch(query); 64 | } else { 65 | results = getResultsFromExactMatch(elements, query, articleId); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } catch (IOException e) { 72 | log.error("Error getting RAE results", e); 73 | } 74 | 75 | return results; 76 | } 77 | 78 | private List getResultsWordSearch(String query) { 79 | List results = new ArrayList<>(); 80 | 81 | String completeURL; 82 | try { 83 | completeURL = BASEURL + SEARCHWORDURL + URLEncoder.encode(query, StandardCharsets.UTF_8); 84 | 85 | Request request = new Request.Builder() 86 | .url(completeURL) 87 | .header("charset", StandardCharsets.UTF_8.name()) 88 | .get() 89 | .build(); 90 | 91 | try (Response response = okHttpClient.newCall(request).execute()) { 92 | if (response.isSuccessful()) { 93 | try (ResponseBody body = response.body()) { 94 | if (body != null) { 95 | Document document = Jsoup.parse(body.string()); 96 | Element list = document.select("body div ul").first(); 97 | 98 | if (list != null) { 99 | Elements links = list.getElementsByTag("a"); 100 | if (!links.isEmpty()) { 101 | for (Element link : links) { 102 | List partialResults = fetchWord(URLEncoder.encode(link.attributes().get("href"), StandardCharsets.UTF_8), link.text()); 103 | if (!partialResults.isEmpty()) { 104 | results.addAll(partialResults); 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | } catch (IOException e) { 114 | log.error("Error getting results from search", e); 115 | } 116 | 117 | return results; 118 | } 119 | 120 | private List fetchWord(String link, String word) { 121 | List results = new ArrayList<>(); 122 | 123 | String completeURL; 124 | try { 125 | completeURL = BASEURL + link; 126 | 127 | Request request = new Request.Builder() 128 | .url(completeURL) 129 | .header("charset", StandardCharsets.UTF_8.name()) 130 | .get() 131 | .build(); 132 | 133 | try (Response response = okHttpClient.newCall(request).execute()) { 134 | if (response.isSuccessful()) { 135 | try (ResponseBody body = response.body()) { 136 | if (body != null) { 137 | Document document = Jsoup.parse(body.string()); 138 | Element article = document.getElementsByTag("article").first(); 139 | String articleId = null; 140 | if (article != null) { 141 | articleId = article.attributes().get("id"); 142 | } 143 | Elements elements = document.select(".j"); 144 | 145 | if (!elements.isEmpty()) { 146 | results = getResultsFromExactMatch(elements, word, articleId); 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } catch (IOException e) { 153 | log.error("Fetching words", e); 154 | } 155 | 156 | return results; 157 | } 158 | 159 | private List getResultsFromExactMatch(Elements elements, String word, String link) { 160 | List results = new ArrayList<>(); 161 | for (int i = 0; i < elements.size(); i++) { 162 | Element element = elements.get(i); 163 | RaeResult result = new RaeResult(); 164 | if (link != null && !link.isEmpty()) { 165 | result.link = WORDLINKBYID + link; 166 | } 167 | result.index = i; 168 | result.word = capitalizeFirstLetter(word); 169 | Elements tags = element.getElementsByTag("abbr"); 170 | tags.removeIf(x -> !Objects.equals(x.parent(), element)); 171 | for (Element tag : tags) { 172 | result.tags.put(tag.text(), tag.attributes().get("title")); 173 | } 174 | Elements definition = element.getElementsByTag("mark"); 175 | definition.removeIf(x -> !Objects.equals(x.parent(), element)); 176 | if (definition.isEmpty()) { 177 | results.addAll(findResultsFromRedirect(element, word)); 178 | } else { 179 | StringBuilder definitionBuilder = new StringBuilder(); 180 | definition.forEach(y -> { 181 | String partialText = y.text(); 182 | if (!definitionBuilder.isEmpty()) { 183 | definitionBuilder.append(" "); 184 | partialText = partialText.toLowerCase(); 185 | } 186 | definitionBuilder.append(partialText); 187 | }); 188 | result.definition = capitalizeFirstLetter(definitionBuilder.toString()); 189 | results.add(result); 190 | } 191 | } 192 | 193 | return results; 194 | } 195 | 196 | private List findResultsFromRedirect(Element element, String word) { 197 | List results = new ArrayList<>(); 198 | Element redirect = element.getElementsByTag("a").first(); 199 | if (redirect != null) { 200 | String link = redirect.attributes().get("href"); 201 | results = fetchWord(link, word); 202 | } 203 | 204 | return results; 205 | } 206 | 207 | private static String capitalizeFirstLetter(String original) { 208 | if (original == null || original.isEmpty()) { 209 | return original; 210 | } 211 | return original.substring(0, 1).toUpperCase() + original.substring(1); 212 | } 213 | 214 | public static class RaeResult { 215 | public int index; 216 | public String word; 217 | public Map tags = new HashMap<>(); 218 | public String definition; 219 | public String link; 220 | 221 | public String getDefinition() { 222 | final StringBuilder builder = new StringBuilder(); 223 | if (link != null && !link.isEmpty()) { 224 | builder.append("[").append(word).append("]("); 225 | builder.append(link).append(")\n"); 226 | } 227 | for (Map.Entry tag : tags.entrySet()) { 228 | builder.append("*").append(tag.getKey()).append("*"); 229 | builder.append(" (_").append(tag.getValue()).append("_)\n"); 230 | } 231 | builder.append(definition); 232 | return builder.toString(); 233 | } 234 | 235 | public String getDescription() { 236 | return definition; 237 | } 238 | 239 | public String getTitle() { 240 | final StringBuilder builder = new StringBuilder(); 241 | builder.append(index).append(". ").append(word); 242 | return builder.toString(); 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.telegram 8 | botapi 9 | 7.2.0 10 | 11 | 12 | UTF-8 13 | UTF-8 14 | 17 15 | 17 16 | ${java.version} 17 | ${java.version} 18 | 19 | 4.5.3 20 | 7.2.0 21 | 20240303 22 | 1.17.2 23 | 8.3.0 24 | 2.15.1 25 | 1.18.30 26 | 4.12.0 27 | 2.0.12 28 | 29 | 30 | 31 | 32 | 33 | org.jetbrains.kotlin 34 | kotlin-stdlib-jdk8 35 | 1.9.22 36 | 37 | 38 | org.jetbrains 39 | annotations 40 | 24.1.0 41 | 42 | 43 | org.eclipse.jetty 44 | jetty-util 45 | 11.0.19 46 | 47 | 48 | org.slf4j 49 | slf4j-api 50 | ${slf4j.version} 51 | 52 | 53 | 54 | 55 | 56 | 57 | org.telegram 58 | telegrambots-longpolling 59 | ${telegrambots.version} 60 | 61 | 62 | org.telegram 63 | telegrambots-webhook 64 | ${telegrambots.version} 65 | 66 | 67 | org.telegram 68 | telegrambots-client 69 | ${telegrambots.version} 70 | 71 | 72 | org.telegram 73 | telegrambots-extensions 74 | ${telegrambots.version} 75 | 76 | 77 | com.squareup.okhttp3 78 | okhttp 79 | ${okhttp.version} 80 | 81 | 82 | org.projectlombok 83 | lombok 84 | ${lombok.version} 85 | provided 86 | 87 | 88 | org.json 89 | json 90 | ${json.version} 91 | 92 | 93 | commons-io 94 | commons-io 95 | ${commonsio.version} 96 | 97 | 98 | com.mysql 99 | mysql-connector-j 100 | ${mysql.version} 101 | 102 | 103 | org.jsoup 104 | jsoup 105 | ${jsoup.version} 106 | 107 | 108 | 109 | 110 | ${project.basedir}/target 111 | ${project.build.directory}/classes 112 | ${project.artifactId}-${project.version} 113 | ${project.build.directory}/test-classes 114 | ${project.basedir}/src/main/java 115 | 116 | 117 | maven-clean-plugin 118 | 3.3.2 119 | 120 | 121 | clean-project 122 | clean 123 | 124 | clean 125 | 126 | 127 | 128 | 129 | 130 | maven-assembly-plugin 131 | 3.6.0 132 | 133 | 134 | 135 | org.telegram.Main 136 | 137 | 138 | 139 | jar-with-dependencies 140 | 141 | 142 | 143 | 144 | make-assembly 145 | package 146 | 147 | single 148 | 149 | 150 | 151 | 152 | 153 | org.apache.maven.plugins 154 | maven-source-plugin 155 | 3.3.0 156 | 157 | 158 | attach-sources 159 | verify 160 | 161 | jar-no-fork 162 | 163 | 164 | 165 | 166 | 167 | org.apache.maven.plugins 168 | maven-jar-plugin 169 | 3.3.0 170 | 171 | 172 | 173 | true 174 | org.telegram.Main 175 | 176 | 177 | 178 | 179 | 180 | org.apache.maven.plugins 181 | maven-javadoc-plugin 182 | 3.6.3 183 | 184 | 185 | attach-javadocs 186 | site 187 | 188 | javadoc-no-fork 189 | 190 | 191 | 192 | 193 | 194 | org.apache.maven.plugins 195 | maven-enforcer-plugin 196 | 3.4.1 197 | 198 | 199 | enforce 200 | 201 | enforce 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | org.apache.maven.plugins 216 | maven-dependency-plugin 217 | 3.6.1 218 | 219 | 220 | copy-dependencies 221 | package 222 | 223 | copy-dependencies 224 | 225 | 226 | ${project.build.directory}/lib 227 | false 228 | false 229 | true 230 | 231 | 232 | 233 | 234 | 235 | org.apache.maven.plugins 236 | maven-compiler-plugin 237 | 3.12.1 238 | 239 | ${java.version} 240 | ${java.version} 241 | UTF-8 242 | 243 | 244 | org.projectlombok 245 | lombok 246 | ${lombok.version} 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/DirectionsHandlers.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.Commands; 5 | import org.telegram.database.DatabaseManager; 6 | import org.telegram.services.DirectionsService; 7 | import org.telegram.services.LocalisationService; 8 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 9 | import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; 10 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 11 | import org.telegram.telegrambots.meta.api.objects.Update; 12 | import org.telegram.telegrambots.meta.api.objects.message.Message; 13 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ForceReplyKeyboard; 14 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; 15 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardRemove; 16 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; 17 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 18 | import org.telegram.telegrambots.meta.generics.TelegramClient; 19 | 20 | import java.io.InvalidObjectException; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.concurrent.ConcurrentLinkedQueue; 24 | 25 | /** 26 | * @author Ruben Bermudez 27 | * @version 1.0 28 | * Handler for updates to Directions Bot 29 | */ 30 | @Slf4j 31 | public class DirectionsHandlers implements LongPollingSingleThreadUpdateConsumer { 32 | private static final int WATING_ORIGIN_STATUS = 0; 33 | private static final int WATING_DESTINY_STATUS = 1; 34 | private final ConcurrentLinkedQueue languageMessages = new ConcurrentLinkedQueue<>(); 35 | private final TelegramClient telegramClient; 36 | 37 | public DirectionsHandlers(String botToken) { 38 | telegramClient = new OkHttpTelegramClient(botToken); 39 | } 40 | 41 | @Override 42 | public void consume(Update update) { 43 | try { 44 | handleDirections(update); 45 | } catch (Exception e) { 46 | log.error("Error processing update in directions bot", e); 47 | } 48 | } 49 | 50 | private void handleDirections(Update update) throws InvalidObjectException { 51 | Message message = update.getMessage(); 52 | if (message != null && message.hasText()) { 53 | if (languageMessages.contains(message.getFrom().getId())) { 54 | onLanguageSelected(message); 55 | } else { 56 | String language = DatabaseManager.getInstance().getUserLanguage(update.getMessage().getFrom().getId()); 57 | if (message.getText().startsWith(Commands.setLanguageCommand)) { 58 | onSetLanguageCommand(message, language); 59 | } else if (message.getText().startsWith(Commands.startDirectionCommand)) { 60 | onStartdirectionsCommand(message, language); 61 | } else if ((message.getText().startsWith(Commands.help) || 62 | (message.getText().startsWith(Commands.startCommand) || !message.isGroupMessage())) && 63 | DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == -1) { 64 | sendHelpMessage(message, language); 65 | } else if (!message.getText().startsWith("/")) { 66 | if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == WATING_ORIGIN_STATUS && 67 | message.isReply() && 68 | DatabaseManager.getInstance().getUserDestinationMessageId(message.getFrom().getId()) == message.getReplyToMessage().getMessageId()) { 69 | onOriginReceived(message, language); 70 | 71 | } else if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == WATING_DESTINY_STATUS && 72 | message.isReply() && 73 | DatabaseManager.getInstance().getUserDestinationMessageId(message.getFrom().getId()) == message.getReplyToMessage().getMessageId()) { 74 | onDestinationReceived(message, language); 75 | } else if (!message.isReply()) { 76 | if (DatabaseManager.getInstance().getUserDestinationStatus(message.getFrom().getId()) == -1) { 77 | sendHelpMessage(message, language); 78 | } else { 79 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("youNeedReplyDirections", language)); 80 | try { 81 | telegramClient.execute(sendMessageRequest); 82 | } catch (TelegramApiException e) { 83 | log.error("Error handling directions", e); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | private void onDestinationReceived(Message message, String language) { 93 | String origin = DatabaseManager.getInstance().getUserOrigin(message.getFrom().getId()); 94 | String destiny = message.getText(); 95 | List directions = DirectionsService.getInstance().getDirections(origin, destiny, language); 96 | SendMessage.SendMessageBuilder sendMessageRequestBuilder = SendMessage.builder(); 97 | sendMessageRequestBuilder.chatId(message.getChatId()); 98 | ReplyKeyboardRemove replyKeyboardRemove = ReplyKeyboardRemove.builder().selective(true).build(); 99 | sendMessageRequestBuilder.replyMarkup(replyKeyboardRemove); 100 | sendMessageRequestBuilder.replyToMessageId(message.getMessageId()); 101 | for (String direction : directions) { 102 | sendMessageRequestBuilder.text(direction); 103 | try { 104 | telegramClient.executeAsync(sendMessageRequestBuilder.build()).thenAccept(sentMessage -> { 105 | if (sentMessage != null) { 106 | DatabaseManager.getInstance().deleteUserForDirections(message.getFrom().getId()); 107 | } 108 | }).thenAccept(unused -> log.info("Sent destination received")); 109 | } catch (TelegramApiException e) { 110 | log.error("Error on destination received", e); 111 | } 112 | } 113 | 114 | } 115 | 116 | private void onOriginReceived(Message message, String language) { 117 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("sendDestination", language)); 118 | sendMessageRequest.setReplyToMessageId(message.getMessageId()); 119 | ForceReplyKeyboard forceReplyKeyboard = new ForceReplyKeyboard(); 120 | forceReplyKeyboard.setSelective(true); 121 | sendMessageRequest.setReplyMarkup(forceReplyKeyboard); 122 | 123 | try { 124 | telegramClient.executeAsync(sendMessageRequest).thenAccept(sentMessage -> { 125 | if (sentMessage != null) { 126 | DatabaseManager.getInstance().addUserForDirection(message.getFrom().getId(), WATING_DESTINY_STATUS, 127 | sentMessage.getMessageId(), message.getText()); 128 | } 129 | }).thenAccept(unused -> log.info("Sent origin received")); 130 | } catch (TelegramApiException e) { 131 | log.error("Error on origin received", e); 132 | } 133 | } 134 | 135 | private void sendHelpMessage(Message message, String language) throws InvalidObjectException { 136 | String helpDirectionsFormated = String.format( 137 | LocalisationService.getString("helpDirections", language), 138 | Commands.startDirectionCommand); 139 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), helpDirectionsFormated); 140 | try { 141 | telegramClient.execute(sendMessageRequest); 142 | } catch (TelegramApiException e) { 143 | log.error("Error sending help", e); 144 | } 145 | } 146 | 147 | private void onStartdirectionsCommand(Message message, String language) { 148 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("initDirections", language)); 149 | sendMessageRequest.setReplyToMessageId(message.getMessageId()); 150 | ForceReplyKeyboard forceReplyKeyboard = new ForceReplyKeyboard(); 151 | forceReplyKeyboard.setSelective(true); 152 | sendMessageRequest.setReplyMarkup(forceReplyKeyboard); 153 | try { 154 | telegramClient.executeAsync(sendMessageRequest).thenAccept(sentMessage -> { 155 | if (sentMessage != null) { 156 | DatabaseManager.getInstance().addUserForDirection(message.getFrom().getId(), WATING_ORIGIN_STATUS, 157 | sentMessage.getMessageId(), null); 158 | } 159 | }).thenAccept(unused -> log.info("Sent start directions")); 160 | } catch (TelegramApiException e) { 161 | log.error("Error on start directions", e); 162 | } 163 | } 164 | 165 | private void onSetLanguageCommand(Message message, String language) throws InvalidObjectException { 166 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("chooselanguage", language)); 167 | ReplyKeyboardMarkup.ReplyKeyboardMarkupBuilder replyKeyboardMarkupBuilder = ReplyKeyboardMarkup.builder(); 168 | List languages = LocalisationService.getSupportedLanguages(); 169 | List commands = new ArrayList<>(); 170 | for (LocalisationService.Language languageItem : languages) { 171 | KeyboardRow commandRow = new KeyboardRow(); 172 | commandRow.add(languageItem.getCode() + " --> " + languageItem.getName()); 173 | commands.add(commandRow); 174 | } 175 | replyKeyboardMarkupBuilder.resizeKeyboard(true); 176 | replyKeyboardMarkupBuilder.oneTimeKeyboard(true); 177 | replyKeyboardMarkupBuilder.keyboard(commands); 178 | replyKeyboardMarkupBuilder.selective(true); 179 | sendMessageRequest.setReplyMarkup(replyKeyboardMarkupBuilder.build()); 180 | try { 181 | telegramClient.execute(sendMessageRequest); 182 | languageMessages.add(message.getFrom().getId()); 183 | } catch (TelegramApiException e) { 184 | log.error("Error setting language", e); 185 | } 186 | } 187 | 188 | private void onLanguageSelected(Message message) throws InvalidObjectException { 189 | String[] parts = message.getText().split("-->", 2); 190 | SendMessage.SendMessageBuilder sendMessageRequestBuilder = SendMessage.builder(); 191 | sendMessageRequestBuilder.chatId(message.getChatId()); 192 | if (LocalisationService.getLanguageByCode(parts[0].trim()) != null) { 193 | DatabaseManager.getInstance().putUserLanguage(message.getFrom().getId(), parts[0].trim()); 194 | sendMessageRequestBuilder.text(LocalisationService.getString("languageModified", parts[0].trim())); 195 | } else { 196 | sendMessageRequestBuilder.text(LocalisationService.getString("errorLanguage")); 197 | } 198 | sendMessageRequestBuilder.replyToMessageId(message.getMessageId()); 199 | ReplyKeyboardRemove replyKeyboardRemove = ReplyKeyboardRemove.builder().selective(true).build(); 200 | sendMessageRequestBuilder.replyMarkup(replyKeyboardRemove); 201 | try { 202 | telegramClient.execute(sendMessageRequestBuilder.build()); 203 | languageMessages.remove(message.getFrom().getId()); 204 | } catch (TelegramApiException e) { 205 | log.error("Error on lanaguage selected", e); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/updateshandlers/FilesHandlers.java: -------------------------------------------------------------------------------- 1 | package org.telegram.updateshandlers; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.telegram.Commands; 5 | import org.telegram.database.DatabaseManager; 6 | import org.telegram.services.Emoji; 7 | import org.telegram.services.LocalisationService; 8 | import org.telegram.telegrambots.client.okhttp.OkHttpTelegramClient; 9 | import org.telegram.telegrambots.longpolling.util.LongPollingSingleThreadUpdateConsumer; 10 | import org.telegram.telegrambots.meta.api.methods.send.SendDocument; 11 | import org.telegram.telegrambots.meta.api.methods.send.SendMessage; 12 | import org.telegram.telegrambots.meta.api.objects.InputFile; 13 | import org.telegram.telegrambots.meta.api.objects.Update; 14 | import org.telegram.telegrambots.meta.api.objects.message.Message; 15 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardMarkup; 16 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.ReplyKeyboardRemove; 17 | import org.telegram.telegrambots.meta.api.objects.replykeyboard.buttons.KeyboardRow; 18 | import org.telegram.telegrambots.meta.exceptions.TelegramApiException; 19 | import org.telegram.telegrambots.meta.exceptions.TelegramApiRequestException; 20 | import org.telegram.telegrambots.meta.generics.TelegramClient; 21 | 22 | import java.io.InvalidObjectException; 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | import java.util.concurrent.ConcurrentLinkedQueue; 28 | 29 | /** 30 | * @author Ruben Bermudez 31 | * @version 1.0 32 | * Handler for updates to Files Bot 33 | * This bot is an example for the use of sendMessage asynchronously 34 | */ 35 | @Slf4j 36 | public class FilesHandlers implements LongPollingSingleThreadUpdateConsumer { 37 | private static final int INITIAL_UPLOAD_STATUS = 0; 38 | private static final int DELETE_UPLOADED_STATUS = 1; 39 | private final ConcurrentLinkedQueue languageMessages = new ConcurrentLinkedQueue<>(); 40 | private final TelegramClient telegramClient; 41 | 42 | public FilesHandlers(String botToken) { 43 | telegramClient = new OkHttpTelegramClient(botToken); 44 | } 45 | 46 | @Override 47 | public void consume(Update update) { 48 | try { 49 | if (update.hasMessage()) { 50 | try { 51 | handleFileUpdate(update); 52 | } catch (TelegramApiRequestException e) { 53 | if (e.getApiResponse().contains("Bot was blocked by the user")) { 54 | if (update.getMessage().getFrom() != null) { 55 | DatabaseManager.getInstance().deleteUserForFile(update.getMessage().getFrom().getId()); 56 | } 57 | } 58 | } catch (Exception e) { 59 | log.error("Error handling file update", e); 60 | } 61 | } 62 | } catch (Exception e) { 63 | log.error("Unknown exception", e); 64 | } 65 | } 66 | 67 | private void handleFileUpdate(Update update) throws InvalidObjectException, TelegramApiException { 68 | Message message = update.getMessage(); 69 | if (message != null && message.hasText()) { 70 | if (languageMessages.contains(message.getFrom().getId())) { 71 | onLanguageReceived(message); 72 | } else { 73 | String language = DatabaseManager.getInstance().getUserLanguage(update.getMessage().getFrom().getId()); 74 | if (message.getText().startsWith(Commands.setLanguageCommand)) { 75 | onSetLanguageCommand(message, language); 76 | } else { 77 | String[] parts = message.getText().split(" ", 2); 78 | if (parts[0].startsWith(Commands.startCommand)) { 79 | if (parts.length == 2) { 80 | onStartWithParameters(message, language, parts[1]); 81 | } else { 82 | sendHelpMessage(message, language); 83 | } 84 | } else if (!message.isGroupMessage()) { 85 | if (parts[0].startsWith(Commands.uploadCommand)) { // Open upload for user 86 | onUploadCommand(message, language); 87 | } else if (parts[0].startsWith(Commands.cancelCommand)) { 88 | onCancelCommand(message, language); 89 | } else if (parts[0].startsWith(Commands.deleteCommand)) { 90 | onDeleteCommand(message, language, parts); 91 | } else if (parts[0].startsWith(Commands.listCommand)) { 92 | onListCommand(message, language); 93 | } else { 94 | sendHelpMessage(message, language); 95 | } 96 | } 97 | } 98 | } 99 | } else if (message != null && message.hasDocument() 100 | && DatabaseManager.getInstance().getUserStatusForFile(message.getFrom().getId()) == INITIAL_UPLOAD_STATUS) { 101 | String language = DatabaseManager.getInstance().getUserLanguage(update.getMessage().getFrom().getId()); 102 | DatabaseManager.getInstance().addFile(message.getDocument().getFileId(), message.getFrom().getId(), message.getDocument().getFileName()); 103 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("fileUploaded", language) + 104 | LocalisationService.getString("uploadedFileURL", language) + message.getDocument().getFileId()); 105 | telegramClient.execute(sendMessageRequest); 106 | } 107 | } 108 | 109 | private void onListCommand(Message message, String language) throws InvalidObjectException, TelegramApiException { 110 | HashMap files = DatabaseManager.getInstance().getFilesByUser(message.getFrom().getId()); 111 | SendMessage.SendMessageBuilder sendMessageRequestBuilder = SendMessage.builder(); 112 | if (!files.isEmpty()) { 113 | StringBuilder text = new StringBuilder(LocalisationService.getString("listOfFiles", language) + ":\n\n"); 114 | for (Map.Entry entry : files.entrySet()) { 115 | text.append(LocalisationService.getString("uploadedFileURL", language)).append(entry.getKey()).append(" ").append(Emoji.LEFT_RIGHT_ARROW).append(" ").append(entry.getValue()).append("\n"); 116 | } 117 | sendMessageRequestBuilder.text(text.toString()); 118 | } else { 119 | sendMessageRequestBuilder.text(LocalisationService.getString("noFiles", language)); 120 | } 121 | sendMessageRequestBuilder.chatId(message.getChatId()); 122 | sendMessageRequestBuilder.replyMarkup(ReplyKeyboardRemove.builder().build()); 123 | telegramClient.execute(sendMessageRequestBuilder.build()); 124 | } 125 | 126 | private void onDeleteCommand(Message message, String language, String[] parts) throws InvalidObjectException, TelegramApiException { 127 | if (DatabaseManager.getInstance().getUserStatusForFile(message.getFrom().getId()) == DELETE_UPLOADED_STATUS && 128 | parts.length == 2) { 129 | onDeleteCommandWithParameters(message, language, parts[1]); 130 | } else { 131 | onDeleteCommandWithoutParameters(message, language); 132 | } 133 | } 134 | 135 | private void onDeleteCommandWithoutParameters(Message message, String language) throws TelegramApiException { 136 | DatabaseManager.getInstance().addUserForFile(message.getFrom().getId(), DELETE_UPLOADED_STATUS); 137 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("deleteUploadedFile", language)); 138 | HashMap files = DatabaseManager.getInstance().getFilesByUser(message.getFrom().getId()); 139 | ReplyKeyboardMarkup replyKeyboardMarkup = null; 140 | if (!files.isEmpty()) { 141 | List commands = new ArrayList<>(); 142 | for (Map.Entry entry : files.entrySet()) { 143 | KeyboardRow commandRow = new KeyboardRow(); 144 | commandRow.add(Commands.deleteCommand + " " + entry.getKey() + " " + Emoji.LEFT_RIGHT_ARROW 145 | + " " + entry.getValue()); 146 | commands.add(commandRow); 147 | } 148 | replyKeyboardMarkup = new ReplyKeyboardMarkup(commands); 149 | replyKeyboardMarkup.setResizeKeyboard(true); 150 | replyKeyboardMarkup.setOneTimeKeyboard(true); 151 | } 152 | sendMessageRequest.setReplyMarkup(replyKeyboardMarkup); 153 | telegramClient.execute(sendMessageRequest); 154 | } 155 | 156 | private void onDeleteCommandWithParameters(Message message, String language, String part) throws TelegramApiException { 157 | String[] innerParts = part.split(Emoji.LEFT_RIGHT_ARROW.toString(), 2); 158 | boolean removed = DatabaseManager.getInstance().deleteFile(innerParts[0].trim()); 159 | SendMessage.SendMessageBuilder sendMessageRequestBuilder = SendMessage.builder(); 160 | if (removed) { 161 | sendMessageRequestBuilder.text(LocalisationService.getString("fileDeleted", language)); 162 | } else { 163 | sendMessageRequestBuilder.text(LocalisationService.getString("wrongFileId", language)); 164 | } 165 | sendMessageRequestBuilder.chatId(message.getChatId()); 166 | 167 | telegramClient.execute(sendMessageRequestBuilder.build()); 168 | DatabaseManager.getInstance().deleteUserForFile(message.getFrom().getId()); 169 | 170 | } 171 | 172 | private void onCancelCommand(Message message, String language) throws TelegramApiException { 173 | DatabaseManager.getInstance().deleteUserForFile(message.getFrom().getId()); 174 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("processFinished", language)); 175 | telegramClient.execute(sendMessageRequest); 176 | } 177 | 178 | private void onUploadCommand(Message message, String language) throws TelegramApiException { 179 | DatabaseManager.getInstance().addUserForFile(message.getFrom().getId(), INITIAL_UPLOAD_STATUS); 180 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("sendFileToUpload", language)); 181 | telegramClient.execute(sendMessageRequest); 182 | } 183 | 184 | private void sendHelpMessage(Message message, String language) throws TelegramApiException { 185 | 186 | String formatedString = String.format( 187 | LocalisationService.getString("helpFiles", language), 188 | Commands.startCommand, Commands.uploadCommand, Commands.deleteCommand, 189 | Commands.listCommand); 190 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), formatedString); 191 | telegramClient.execute(sendMessageRequest); 192 | } 193 | 194 | private void onStartWithParameters(Message message, String language, String part) throws TelegramApiException { 195 | if (DatabaseManager.getInstance().doesFileExists(part.trim())) { 196 | SendDocument sendDocumentRequest = new SendDocument(String.valueOf(message.getChatId()), new InputFile(part.trim())); 197 | telegramClient.execute(sendDocumentRequest); 198 | } else { 199 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("wrongFileId", language)); 200 | telegramClient.execute(sendMessageRequest); 201 | } 202 | } 203 | 204 | private void onSetLanguageCommand(Message message, String language) throws TelegramApiException { 205 | SendMessage sendMessageRequest = new SendMessage(String.valueOf(message.getChatId()), LocalisationService.getString("chooselanguage", language)); 206 | List languages = LocalisationService.getSupportedLanguages(); 207 | List commands = new ArrayList<>(); 208 | for (LocalisationService.Language languageItem : languages) { 209 | KeyboardRow commandRow = new KeyboardRow(); 210 | commandRow.add(languageItem.getCode() + " " + Emoji.LEFT_RIGHT_ARROW + " " + languageItem.getName()); 211 | commands.add(commandRow); 212 | } 213 | ReplyKeyboardMarkup replyKeyboardMarkup = new ReplyKeyboardMarkup(commands); 214 | replyKeyboardMarkup.setResizeKeyboard(true); 215 | replyKeyboardMarkup.setOneTimeKeyboard(true); 216 | replyKeyboardMarkup.setSelective(true); 217 | sendMessageRequest.setReplyMarkup(replyKeyboardMarkup); 218 | telegramClient.execute(sendMessageRequest); 219 | languageMessages.add(message.getFrom().getId()); 220 | } 221 | 222 | private void onLanguageReceived(Message message) throws TelegramApiException { 223 | String[] parts = message.getText().split(Emoji.LEFT_RIGHT_ARROW.toString(), 2); 224 | SendMessage.SendMessageBuilder sendMessageRequestBuilder = SendMessage.builder(); 225 | sendMessageRequestBuilder.chatId(message.getChatId()); 226 | if (LocalisationService.getLanguageByCode(parts[0].trim()) != null) { 227 | DatabaseManager.getInstance().putUserLanguage(message.getFrom().getId(), parts[0].trim()); 228 | sendMessageRequestBuilder.text(LocalisationService.getString("languageModified", parts[0].trim())); 229 | } else { 230 | sendMessageRequestBuilder.text(LocalisationService.getString("errorLanguage")); 231 | } 232 | sendMessageRequestBuilder.replyToMessageId(message.getMessageId()); 233 | ReplyKeyboardRemove replyKeyboardRemove = ReplyKeyboardRemove.builder().selective(true).build(); 234 | sendMessageRequestBuilder.replyMarkup(replyKeyboardRemove); 235 | telegramClient.execute(sendMessageRequestBuilder.build()); 236 | languageMessages.remove(message.getFrom().getId()); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/services/WeatherService.java: -------------------------------------------------------------------------------- 1 | package org.telegram.services; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import okhttp3.OkHttpClient; 5 | import okhttp3.Request; 6 | import okhttp3.Response; 7 | import okhttp3.ResponseBody; 8 | import org.json.JSONObject; 9 | import org.telegram.BuildVars; 10 | import org.telegram.database.DatabaseManager; 11 | 12 | import java.io.UnsupportedEncodingException; 13 | import java.net.URLEncoder; 14 | import java.nio.charset.StandardCharsets; 15 | import java.time.Instant; 16 | import java.time.LocalDate; 17 | import java.time.ZoneId; 18 | import java.time.format.DateTimeFormatter; 19 | 20 | /** 21 | * @author Ruben Bermudez 22 | * @version 1.0 23 | * Weather service 24 | */ 25 | @Slf4j 26 | public class WeatherService { 27 | public static final String METRICSYSTEM = "metric"; 28 | public static final String IMPERIALSYSTEM = "imperial"; 29 | 30 | private static final String BASEURL = "http://api.openweathermap.org/data/2.5/"; ///< Base url for REST 31 | private static final String FORECASTPATH = "forecast/daily"; 32 | private static final String CURRENTPATH = "weather"; 33 | private static final String APIIDEND = "&APPID=" + BuildVars.OPENWEATHERAPIKEY; 34 | private static final String FORECASTPARAMS = "&cnt=3&units=@units@&lang=@language@"; 35 | private static final String ALERTPARAMS = "&cnt=1&units=@units@&lang=@language@"; 36 | private static final String CURRENTPARAMS = "&cnt=1&units=@units@&lang=@language@"; 37 | private static final DateTimeFormatter dateFormaterFromDate = DateTimeFormatter.ofPattern("dd/MM/yyyy"); ///< Date to text formater 38 | private static volatile WeatherService instance; ///< Instance of this class 39 | 40 | private final OkHttpClient okHttpClient = new OkHttpClient().newBuilder().build(); 41 | 42 | /** 43 | * Constructor (private due to singleton pattern) 44 | */ 45 | private WeatherService() { 46 | } 47 | 48 | /** 49 | * Singleton 50 | * 51 | * @return Return the instance of this class 52 | */ 53 | public static WeatherService getInstance() { 54 | WeatherService currentInstance; 55 | if (instance == null) { 56 | synchronized (WeatherService.class) { 57 | if (instance == null) { 58 | instance = new WeatherService(); 59 | } 60 | currentInstance = instance; 61 | } 62 | } else { 63 | currentInstance = instance; 64 | } 65 | return currentInstance; 66 | } 67 | 68 | /** 69 | * Fetch the weather of a city for one day 70 | * 71 | * @param cityId City to get the weather 72 | * @return userHash to be send to use 73 | * @apiNote Forecast for the day 74 | */ 75 | public String fetchWeatherAlert(int cityId, long userId, String language, String units) { 76 | String cityFound; 77 | String responseToUser; 78 | try { 79 | String completeURL = BASEURL + FORECASTPATH + "?" + getCityQuery(cityId + "") + 80 | ALERTPARAMS.replace("@language@", language).replace("@units@", units) + APIIDEND; 81 | Request request = new Request.Builder() 82 | .url(completeURL) 83 | .header("charset", StandardCharsets.UTF_8.name()) 84 | .get() 85 | .build(); 86 | 87 | try (Response response = okHttpClient.newCall(request).execute()) { 88 | if (response.isSuccessful()) { 89 | try (ResponseBody body = response.body()) { 90 | if (body != null) { 91 | JSONObject jsonObject = new JSONObject(body.string()); 92 | log.info("Alert fetched: {}", jsonObject); 93 | if (jsonObject.getInt("cod") == 200) { 94 | cityFound = jsonObject.getJSONObject("city").getString("name") + " (" + 95 | jsonObject.getJSONObject("city").getString("country") + ")"; 96 | saveRecentWeather(userId, cityFound, jsonObject.getJSONObject("city").getInt("id")); 97 | responseToUser = String.format(LocalisationService.getString("weatherAlert", language), 98 | cityFound, convertListOfForecastToString(jsonObject, language, units, false)); 99 | } else { 100 | log.warn("Unable to read alerts fetched {}", jsonObject); 101 | responseToUser = LocalisationService.getString("cityNotFound", language); 102 | } 103 | } else { 104 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 105 | } 106 | } 107 | } else { 108 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 109 | } 110 | } 111 | } catch (Exception e) { 112 | log.error("Error fetching alerts", e); 113 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 114 | } 115 | return responseToUser; 116 | } 117 | 118 | /** 119 | * Fetch the weather of a city 120 | * 121 | * @param city City to get the weather 122 | * @return userHash to be send to use 123 | * @note Forecast for the following 3 days 124 | */ 125 | public String fetchWeatherForecast(String city, Long userId, String language, String units) { 126 | String cityFound; 127 | String responseToUser; 128 | try { 129 | String completeURL = BASEURL + FORECASTPATH + "?" + getCityQuery(city) + 130 | FORECASTPARAMS.replace("@language@", language).replace("@units@", units) + APIIDEND; 131 | Request request = new Request.Builder() 132 | .url(completeURL) 133 | .header("charset", StandardCharsets.UTF_8.name()) 134 | .get() 135 | .build(); 136 | 137 | try (Response response = okHttpClient.newCall(request).execute()) { 138 | if (response.isSuccessful()) { 139 | try (ResponseBody body = response.body()) { 140 | if (body != null) { 141 | JSONObject jsonObject = new JSONObject(body.string()); 142 | log.info("Fetched weather forecast {}", jsonObject); 143 | if (jsonObject.getInt("cod") == 200) { 144 | cityFound = jsonObject.getJSONObject("city").getString("name") + " (" + 145 | jsonObject.getJSONObject("city").getString("country") + ")"; 146 | saveRecentWeather(userId, cityFound, jsonObject.getJSONObject("city").getInt("id")); 147 | responseToUser = String.format(LocalisationService.getString("weatherForcast", language), 148 | cityFound, convertListOfForecastToString(jsonObject, language, units, true)); 149 | } else { 150 | log.warn("City forecast not found {}", jsonObject); 151 | responseToUser = LocalisationService.getString("cityNotFound", language); 152 | } 153 | } else { 154 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 155 | } 156 | } 157 | } else { 158 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 159 | } 160 | } 161 | } catch (Exception e) { 162 | log.error("Error fetching city forecast", e); 163 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 164 | } 165 | return responseToUser; 166 | } 167 | 168 | /** 169 | * Fetch the weather of a city 170 | * 171 | * @return userHash to be send to use 172 | * @note Forecast for the following 3 days 173 | */ 174 | public String fetchWeatherForecastByLocation(Double longitude, Double latitude, Long userId, String language, String units) { 175 | String cityFound; 176 | String responseToUser; 177 | try { 178 | String completeURL = BASEURL + FORECASTPATH + "?lat=" + URLEncoder.encode(latitude + "", StandardCharsets.UTF_8) + "&lon=" 179 | + URLEncoder.encode(longitude + "", StandardCharsets.UTF_8) + 180 | FORECASTPARAMS.replace("@language@", language).replace("@units@", units) + APIIDEND; 181 | Request request = new Request.Builder() 182 | .url(completeURL) 183 | .header("charset", StandardCharsets.UTF_8.name()) 184 | .get() 185 | .build(); 186 | 187 | try (Response response = okHttpClient.newCall(request).execute()) { 188 | if (response.isSuccessful()) { 189 | try (ResponseBody body = response.body()) { 190 | if (body != null) { 191 | JSONObject jsonObject = new JSONObject(body.string()); 192 | if (jsonObject.getInt("cod") == 200) { 193 | cityFound = jsonObject.getJSONObject("city").getString("name") + " (" + 194 | jsonObject.getJSONObject("city").getString("country") + ")"; 195 | saveRecentWeather(userId, cityFound, jsonObject.getJSONObject("city").getInt("id")); 196 | responseToUser = String.format(LocalisationService.getString("weatherForcast", language), 197 | cityFound, convertListOfForecastToString(jsonObject, language, units, true)); 198 | } else { 199 | log.warn("No forecast for location found {}", jsonObject); 200 | responseToUser = LocalisationService.getString("cityNotFound", language); 201 | } 202 | } else { 203 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 204 | } 205 | } 206 | } else { 207 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 208 | } 209 | } 210 | } catch (Exception e) { 211 | log.error("Error fetching location forecast", e); 212 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 213 | } 214 | return responseToUser; 215 | } 216 | 217 | /** 218 | * Fetch the weather of a city 219 | * 220 | * @param city City to get the weather 221 | * @return userHash to be send to use 222 | * @apiNote Forecast for the following 3 days 223 | */ 224 | public String fetchWeatherCurrent(String city, Long userId, String language, String units) { 225 | String cityFound; 226 | String responseToUser; 227 | Emoji emoji = null; 228 | try { 229 | String completeURL = BASEURL + CURRENTPATH + "?" + getCityQuery(city) + 230 | CURRENTPARAMS.replace("@language@", language).replace("@units@", units) + APIIDEND; 231 | Request request = new Request.Builder() 232 | .url(completeURL) 233 | .header("charset", StandardCharsets.UTF_8.name()) 234 | .get() 235 | .build(); 236 | 237 | try (Response response = okHttpClient.newCall(request).execute()) { 238 | if (response.isSuccessful()) { 239 | try (ResponseBody body = response.body()) { 240 | if (body != null) { 241 | JSONObject jsonObject = new JSONObject(body.string()); 242 | if (jsonObject.getInt("cod") == 200) { 243 | cityFound = jsonObject.getString("name") + " (" + 244 | jsonObject.getJSONObject("sys").getString("country") + ")"; 245 | saveRecentWeather(userId, cityFound, jsonObject.getInt("id")); 246 | emoji = getEmojiForWeather(jsonObject.getJSONArray("weather").getJSONObject(0)); 247 | responseToUser = String.format(LocalisationService.getString("weatherCurrent", language), 248 | cityFound, convertCurrentWeatherToString(jsonObject, language, units, emoji)); 249 | } else { 250 | log.warn("No current weather found {}", jsonObject); 251 | responseToUser = LocalisationService.getString("cityNotFound", language); 252 | } 253 | } else { 254 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 255 | } 256 | } 257 | } else { 258 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 259 | } 260 | } 261 | } catch (Exception e) { 262 | log.error("Error fetching current weather", e); 263 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 264 | } 265 | return responseToUser; 266 | } 267 | 268 | /** 269 | * Fetch the weather of a city 270 | * 271 | * @return userHash to be send to use 272 | * @note Forecast for the following 3 days 273 | */ 274 | public String fetchWeatherCurrentByLocation(Double longitude, Double latitude, Long userId, String language, String units) { 275 | String cityFound; 276 | String responseToUser; 277 | try { 278 | String completeURL = BASEURL + CURRENTPATH + "?lat=" + URLEncoder.encode(latitude + "", StandardCharsets.UTF_8) + "&lon=" 279 | + URLEncoder.encode(longitude + "", StandardCharsets.UTF_8) + 280 | CURRENTPARAMS.replace("@language@", language).replace("@units@", units) + APIIDEND; 281 | Request request = new Request.Builder() 282 | .url(completeURL) 283 | .header("charset", StandardCharsets.UTF_8.name()) 284 | .get() 285 | .build(); 286 | 287 | try (Response response = okHttpClient.newCall(request).execute()) { 288 | if (response.isSuccessful()) { 289 | try (ResponseBody body = response.body()) { 290 | if (body != null) { 291 | JSONObject jsonObject = new JSONObject(body.string()); 292 | if (jsonObject.getInt("cod") == 200) { 293 | cityFound = jsonObject.getString("name") + " (" + 294 | jsonObject.getJSONObject("sys").getString("country") + ")"; 295 | saveRecentWeather(userId, cityFound, jsonObject.getInt("id")); 296 | responseToUser = String.format(LocalisationService.getString("weatherCurrent", language), 297 | cityFound, convertCurrentWeatherToString(jsonObject, language, units, null)); 298 | } else { 299 | log.warn("No weather found for location {}", jsonObject); 300 | responseToUser = LocalisationService.getString("cityNotFound", language); 301 | } 302 | } else { 303 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 304 | } 305 | } 306 | } else { 307 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 308 | } 309 | } 310 | } catch (Exception e) { 311 | log.error("Error fetching weather for location", e); 312 | responseToUser = LocalisationService.getString("errorFetchingWeather", language); 313 | } 314 | return responseToUser; 315 | } 316 | 317 | private String convertCurrentWeatherToString(JSONObject jsonObject, String language, String units, Emoji emoji) { 318 | String temp = ((int)jsonObject.getJSONObject("main").getDouble("temp"))+""; 319 | String cloudiness = jsonObject.getJSONObject("clouds").getInt("all") + "%"; 320 | String weatherDesc = jsonObject.getJSONArray("weather").getJSONObject(0).getString("description"); 321 | 322 | String responseToUser; 323 | if (units.equals(METRICSYSTEM)) { 324 | responseToUser = LocalisationService.getString("currentWeatherPartMetric", language); 325 | } else { 326 | responseToUser = LocalisationService.getString("currentWeatherPartImperial", language); 327 | } 328 | responseToUser = String.format(responseToUser, emoji == null ? weatherDesc : emoji.toString(), cloudiness, temp); 329 | 330 | return responseToUser; 331 | } 332 | 333 | /** 334 | * Convert a list of weather forcast to a list of strings to be sent 335 | * 336 | * @param jsonObject JSONObject contining the list 337 | * @return String to be sent to the user 338 | */ 339 | private String convertListOfForecastToString(JSONObject jsonObject, String language, String units, boolean addDate) { 340 | String responseToUser = ""; 341 | for (int i = 0; i < jsonObject.getJSONArray("list").length(); i++) { 342 | JSONObject internalJSON = jsonObject.getJSONArray("list").getJSONObject(i); 343 | responseToUser += convertInternalInformationToString(internalJSON, language, units, addDate); 344 | } 345 | return responseToUser; 346 | } 347 | 348 | /** 349 | * Convert internal part of then answer to string 350 | * 351 | * @param internalJSON JSONObject containing the part to convert 352 | * @return String to be sent to the user 353 | */ 354 | private String convertInternalInformationToString(JSONObject internalJSON, String language, String units, boolean addDate) { 355 | String responseToUser = ""; 356 | LocalDate date; 357 | String tempMax; 358 | String tempMin; 359 | String weatherDesc; 360 | date = Instant.ofEpochSecond(internalJSON.getLong("dt")).atZone(ZoneId.systemDefault()).toLocalDate(); 361 | tempMax = ((int)internalJSON.getJSONObject("temp").getDouble("max")) + ""; 362 | tempMin = ((int)internalJSON.getJSONObject("temp").getDouble("min")) + ""; 363 | JSONObject weatherObject = internalJSON.getJSONArray("weather").getJSONObject(0); 364 | Emoji emoji = getEmojiForWeather(internalJSON.getJSONArray("weather").getJSONObject(0)); 365 | weatherDesc = weatherObject.getString("description"); 366 | 367 | if (units.equals(METRICSYSTEM)) { 368 | if (addDate) { 369 | responseToUser = LocalisationService.getString("forecastWeatherPartMetric", language); 370 | } else { 371 | responseToUser = LocalisationService.getString("alertWeatherPartMetric", language); 372 | } 373 | } else { 374 | if (addDate) { 375 | responseToUser = LocalisationService.getString("forecastWeatherPartImperial", language); 376 | } else { 377 | responseToUser = LocalisationService.getString("alertWeatherPartImperial", language); 378 | } 379 | } 380 | if (addDate) { 381 | responseToUser = String.format(responseToUser, Emoji.LARGE_ORANGE_DIAMOND.toString(), 382 | dateFormaterFromDate.format(date), emoji == null ? weatherDesc : emoji.toString(), tempMax, tempMin); 383 | } else { 384 | responseToUser = String.format(responseToUser, emoji == null ? weatherDesc : emoji.toString(), 385 | tempMax, tempMin); 386 | } 387 | 388 | return responseToUser; 389 | } 390 | 391 | private void saveRecentWeather(Long userId, String cityName, int cityId) { 392 | DatabaseManager.getInstance().addRecentWeather(userId, cityId, cityName); 393 | } 394 | 395 | private String getCityQuery(String city) throws UnsupportedEncodingException { 396 | String cityQuery = ""; 397 | try { 398 | cityQuery += "id=" + URLEncoder.encode(Integer.parseInt(city)+"", StandardCharsets.UTF_8); 399 | } catch(NumberFormatException | NullPointerException e) { 400 | cityQuery += "q=" + URLEncoder.encode(city, StandardCharsets.UTF_8); 401 | } 402 | return cityQuery; 403 | } 404 | 405 | private Emoji getEmojiForWeather(JSONObject weather) { 406 | Emoji emoji; 407 | 408 | switch(weather.getString("icon")) { 409 | case "01n": 410 | case "01d": 411 | emoji = Emoji.SUN_WITH_FACE; 412 | break; 413 | case "02n": 414 | case "02d": 415 | emoji = Emoji.SUN_BEHIND_CLOUD; 416 | break; 417 | case "03n": 418 | case "03d": 419 | case "04n": 420 | case "04d": 421 | emoji = Emoji.CLOUD; 422 | break; 423 | case "09n": 424 | case "09d": 425 | case "10n": 426 | case "10d": 427 | emoji = Emoji.UMBRELLA_WITH_RAIN_DROPS; 428 | break; 429 | case "11n": 430 | case "11d": 431 | emoji = Emoji.HIGH_VOLTAGE_SIGN; 432 | break; 433 | case "13n": 434 | case "13d": 435 | emoji = Emoji.SNOWFLAKE; 436 | break; 437 | case "50n": 438 | case "50d": 439 | emoji = Emoji.FOGGY; 440 | break; 441 | default: 442 | emoji = null; 443 | } 444 | 445 | return emoji; 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /src/main/java/org/telegram/database/DatabaseManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This is the source code of Telegram Bot v. 2.0 3 | * It is licensed under GNU GPL v. 3 or later. 4 | * You should have received a copy of the license in this archive (see LICENSE). 5 | * 6 | * Copyright Ruben Bermudez, 3/12/14. 7 | */ 8 | package org.telegram.database; 9 | 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.telegram.structure.WeatherAlert; 12 | 13 | import java.sql.PreparedStatement; 14 | import java.sql.ResultSet; 15 | import java.sql.SQLException; 16 | import java.sql.Types; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Ruben Bermudez 23 | * @version 2.0 24 | * Database Manager to perform database operations 25 | */ 26 | @Slf4j 27 | public class DatabaseManager { 28 | private static volatile DatabaseManager instance; 29 | private static volatile ConnectionDB connetion; 30 | 31 | /** 32 | * Private constructor (due to Singleton) 33 | */ 34 | private DatabaseManager() { 35 | connetion = new ConnectionDB(); 36 | final int currentVersion = connetion.checkVersion(); 37 | log.info("Current db version: " + currentVersion); 38 | if (currentVersion < CreationStrings.version) { 39 | recreateTable(currentVersion); 40 | } 41 | } 42 | 43 | /** 44 | * Get Singleton instance 45 | * 46 | * @return instance of the class 47 | */ 48 | public static DatabaseManager getInstance() { 49 | final DatabaseManager currentInstance; 50 | if (instance == null) { 51 | synchronized (DatabaseManager.class) { 52 | if (instance == null) { 53 | instance = new DatabaseManager(); 54 | } 55 | currentInstance = instance; 56 | } 57 | } else { 58 | currentInstance = instance; 59 | } 60 | return currentInstance; 61 | } 62 | 63 | /** 64 | * Recreates the DB 65 | */ 66 | private void recreateTable(int currentVersion) { 67 | try { 68 | connetion.initTransaction(); 69 | if (currentVersion == 0) { 70 | currentVersion = createNewTables(); 71 | } 72 | if (currentVersion == 1) { 73 | currentVersion = updateToVersion2(); 74 | } 75 | if (currentVersion == 2) { 76 | currentVersion = updateToVersion3(); 77 | } 78 | if (currentVersion == 3) { 79 | currentVersion = updateToVersion4(); 80 | } 81 | if (currentVersion == 4) { 82 | currentVersion = updateToVersion5(); 83 | } 84 | if (currentVersion == 5) { 85 | currentVersion = updateToVersion6(); 86 | } 87 | if (currentVersion == 6) { 88 | currentVersion = updateToVersion7(); 89 | } 90 | if (currentVersion == 7) { 91 | currentVersion = updateToVersion8(); 92 | } 93 | if (currentVersion == 8) { 94 | currentVersion = updateToVersion8(); 95 | } 96 | connetion.commitTransaction(); 97 | } catch (SQLException e) { 98 | log.error("Error updating DB", e); 99 | try { 100 | connetion.rollbackTransaction(); 101 | } catch (SQLException ex) { 102 | log.error("Error rollingback the transaction", ex); 103 | } 104 | } 105 | } 106 | 107 | private int updateToVersion2() throws SQLException { 108 | connetion.executeQuery(CreationStrings.createRecentWeatherTable); 109 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 2)); 110 | return 2; 111 | } 112 | 113 | private int updateToVersion3() throws SQLException { 114 | connetion.executeQuery(CreationStrings.createDirectionsDatabase); 115 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 3)); 116 | return 3; 117 | } 118 | 119 | private int updateToVersion4() throws SQLException { 120 | connetion.executeQuery(CreationStrings.createLastUpdateDatabase); 121 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 4)); 122 | return 4; 123 | } 124 | 125 | private int updateToVersion5() throws SQLException { 126 | connetion.executeQuery(CreationStrings.createUserLanguageDatabase); 127 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 5)); 128 | return 5; 129 | } 130 | 131 | private int updateToVersion6() throws SQLException { 132 | connetion.executeQuery(CreationStrings.createWeatherStateTable); 133 | connetion.executeQuery(CreationStrings.createUserWeatherOptionDatabase); 134 | connetion.executeQuery(CreationStrings.createWeatherAlertTable); 135 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 6)); 136 | return 6; 137 | } 138 | 139 | private int updateToVersion7() throws SQLException { 140 | connetion.executeQuery("ALTER TABLE WeatherState MODIFY chatId BIGINT NOT NULL"); 141 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 7)); 142 | return 7; 143 | } 144 | 145 | private int updateToVersion8() throws SQLException { 146 | connetion.executeQuery(CreationStrings.CREATE_COMMANDS_TABLE); 147 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 8)); 148 | return 8; 149 | } 150 | 151 | private int updateToVersion9() throws SQLException { 152 | connetion.executeQuery("ALTER TABLE Files MODIFY COLUMN userId BIGINT;"); 153 | connetion.executeQuery("ALTER TABLE FilesUsers MODIFY COLUMN userId BIGINT;"); 154 | connetion.executeQuery("ALTER TABLE RecentWeather MODIFY COLUMN userId BIGINT;"); 155 | connetion.executeQuery("ALTER TABLE Directions MODIFY COLUMN userId BIGINT;"); 156 | connetion.executeQuery("ALTER TABLE UserLanguage MODIFY COLUMN userId BIGINT;"); 157 | connetion.executeQuery("ALTER TABLE UserWeatherOptions MODIFY COLUMN userId BIGINT;"); 158 | connetion.executeQuery("ALTER TABLE WeatherState MODIFY COLUMN userId BIGINT;"); 159 | connetion.executeQuery("ALTER TABLE WeatherAlert MODIFY COLUMN userId BIGINT;"); 160 | connetion.executeQuery("ALTER TABLE CommandUsers MODIFY COLUMN userId BIGINT;"); 161 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, 9)); 162 | return 8; 163 | } 164 | 165 | private int createNewTables() throws SQLException { 166 | connetion.executeQuery(CreationStrings.createVersionTable); 167 | connetion.executeQuery(CreationStrings.createFilesTable); 168 | connetion.executeQuery(String.format(CreationStrings.insertCurrentVersion, CreationStrings.version)); 169 | connetion.executeQuery(CreationStrings.createUsersForFilesTable); 170 | connetion.executeQuery(CreationStrings.createRecentWeatherTable); 171 | connetion.executeQuery(CreationStrings.createDirectionsDatabase); 172 | connetion.executeQuery(CreationStrings.createUserLanguageDatabase); 173 | connetion.executeQuery(CreationStrings.createWeatherStateTable); 174 | connetion.executeQuery(CreationStrings.createUserWeatherOptionDatabase); 175 | connetion.executeQuery(CreationStrings.createWeatherAlertTable); 176 | connetion.executeQuery(CreationStrings.CREATE_COMMANDS_TABLE); 177 | return CreationStrings.version; 178 | } 179 | 180 | public boolean setUserStateForCommandsBot(Long userId, boolean active) { 181 | int updatedRows = 0; 182 | try { 183 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("INSERT INTO CommandUsers (userId, status) VALUES(?, ?) ON DUPLICATE KEY UPDATE status=?"); 184 | preparedStatement.setLong(1, userId); 185 | preparedStatement.setInt(2, active ? 1 : 0); 186 | preparedStatement.setInt(3, active ? 1 : 0); 187 | 188 | updatedRows = preparedStatement.executeUpdate(); 189 | } catch (SQLException e) { 190 | log.error("Error setting user state for commands bot", e); 191 | } 192 | return updatedRows > 0; 193 | } 194 | 195 | public boolean getUserStateForCommandsBot(Long userId) { 196 | int status = -1; 197 | try { 198 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("Select status FROM CommandUsers WHERE userId=?"); 199 | preparedStatement.setLong(1, userId); 200 | final ResultSet result = preparedStatement.executeQuery(); 201 | if (result.next()) { 202 | status = result.getInt("status"); 203 | } 204 | } catch (SQLException e) { 205 | log.error("Error getting user state for command bot", e); 206 | } 207 | return status == 1; 208 | } 209 | 210 | public boolean addFile(String fileId, Long userId, String caption) { 211 | int updatedRows = 0; 212 | try { 213 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO Files (fileId, userId, caption) VALUES(?, ?, ?)"); 214 | preparedStatement.setString(1, fileId); 215 | preparedStatement.setLong(2, userId); 216 | preparedStatement.setString(3, caption); 217 | 218 | updatedRows = preparedStatement.executeUpdate(); 219 | } catch (SQLException e) { 220 | log.error("Error getting file", e); 221 | } 222 | return updatedRows > 0; 223 | } 224 | 225 | public HashMap getFilesByUser(Long userId) { 226 | HashMap files = new HashMap<>(); 227 | try { 228 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT * FROM Files WHERE userId = ?"); 229 | preparedStatement.setLong(1, userId); 230 | final ResultSet result = preparedStatement.executeQuery(); 231 | while (result.next()) { 232 | files.put(result.getString("fileId"), result.getString("caption")); 233 | } 234 | result.close(); 235 | } catch (SQLException e) { 236 | log.error("Error getting files for user", e); 237 | } 238 | return files; 239 | } 240 | 241 | public boolean addUserForFile(Long userId, int status) { 242 | int updatedRows = 0; 243 | try { 244 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO FilesUsers (userId, status) VALUES(?, ?)"); 245 | preparedStatement.setLong(1, userId); 246 | preparedStatement.setInt(2, status); 247 | updatedRows = preparedStatement.executeUpdate(); 248 | } catch (SQLException e) { 249 | log.error("Error getting user for file", e); 250 | } 251 | return updatedRows > 0; 252 | } 253 | 254 | public boolean deleteUserForFile(Long userId) { 255 | int updatedRows = 0; 256 | try { 257 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM FilesUsers WHERE userId=?;"); 258 | preparedStatement.setLong(1, userId); 259 | 260 | updatedRows = preparedStatement.executeUpdate(); 261 | } catch (SQLException e) { 262 | log.error("Error deleting user file", e); 263 | } 264 | return updatedRows > 0; 265 | } 266 | 267 | public int getUserStatusForFile(Long userId) { 268 | int status = -1; 269 | try { 270 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("Select status FROM FilesUsers WHERE userId=?"); 271 | preparedStatement.setLong(1, userId); 272 | final ResultSet result = preparedStatement.executeQuery(); 273 | if (result.next()) { 274 | status = result.getInt("status"); 275 | } 276 | } catch (SQLException e) { 277 | log.error("Error getting user status", e); 278 | } 279 | return status; 280 | } 281 | 282 | public boolean doesFileExists(String fileId) { 283 | boolean exists = false; 284 | try { 285 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("Select fileID FROM Files WHERE fileId=?"); 286 | preparedStatement.setString(1, fileId); 287 | final ResultSet result = preparedStatement.executeQuery(); 288 | exists = result.next(); 289 | } catch (SQLException e) { 290 | log.error("Error checking file", e); 291 | } 292 | return exists; 293 | } 294 | 295 | public boolean deleteFile(String fileId) { 296 | int updatedRows = 0; 297 | try { 298 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM Files WHERE fileId=?;"); 299 | preparedStatement.setString(1, fileId); 300 | 301 | updatedRows = preparedStatement.executeUpdate(); 302 | } catch (SQLException e) { 303 | log.error("Error deleting files", e); 304 | } 305 | return updatedRows > 0; 306 | } 307 | 308 | public boolean addRecentWeather(Long userId, Integer cityId, String cityName) { 309 | int updatedRows = 0; 310 | try { 311 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO RecentWeather (userId, cityId, cityName) VALUES(?, ?, ?)"); 312 | preparedStatement.setLong(1, userId); 313 | preparedStatement.setInt(2, cityId); 314 | preparedStatement.setString(3, cityName); 315 | updatedRows = preparedStatement.executeUpdate(); 316 | } catch (SQLException e) { 317 | log.error("Error adding recent weather", e); 318 | } 319 | cleanUpRecent(userId); 320 | return updatedRows > 0; 321 | } 322 | 323 | public List getRecentWeather(Long userId) { 324 | List recentWeather = new ArrayList<>(); 325 | try { 326 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("select * FROM RecentWeather WHERE userId=? order by date desc"); 327 | preparedStatement.setLong(1, userId); 328 | final ResultSet result = preparedStatement.executeQuery(); 329 | while (result.next()) { 330 | recentWeather.add(result.getString("cityName")); 331 | } 332 | } catch (SQLException e) { 333 | log.error("Error getting recent weather", e); 334 | } 335 | 336 | return recentWeather; 337 | } 338 | 339 | private void cleanUpRecent(Long userId) { 340 | try { 341 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM RecentWeather WHERE userid = ? AND ID <= (SELECT ID FROM (SELECT id From RecentWeather where userId = ? ORDER BY id DESC LIMIT 1 OFFSET 4 ) AS T1 )"); 342 | preparedStatement.setLong(1, userId); 343 | preparedStatement.setLong(2, userId); 344 | preparedStatement.executeUpdate(); 345 | } catch (SQLException e) { 346 | log.error("Error cleaning up recent user", e); 347 | } 348 | } 349 | 350 | public boolean addUserForDirection(Long userId, int status, int messageId, String origin) { 351 | int updatedRows = 0; 352 | try { 353 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO Directions (userId, status, messageId, origin) VALUES(?, ?, ?, ?)"); 354 | preparedStatement.setLong(1, userId); 355 | preparedStatement.setInt(2, status); 356 | preparedStatement.setInt(3, messageId); 357 | if (origin == null || origin.isEmpty()) { 358 | preparedStatement.setNull(4, Types.VARCHAR); 359 | } else { 360 | preparedStatement.setString(4, origin); 361 | } 362 | updatedRows = preparedStatement.executeUpdate(); 363 | } catch (SQLException e) { 364 | log.error("Error adding user for direction", e); 365 | } 366 | return updatedRows > 0; 367 | } 368 | 369 | public int getUserDestinationStatus(Long userId) { 370 | int status = -1; 371 | try { 372 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT status FROM Directions WHERE userId = ?"); 373 | preparedStatement.setLong(1, userId); 374 | final ResultSet result = preparedStatement.executeQuery(); 375 | if (result.next()) { 376 | status = result.getInt("status"); 377 | } 378 | } catch (SQLException e) { 379 | log.error("Error getting user destination status", e); 380 | } 381 | return status; 382 | } 383 | 384 | public int getUserDestinationMessageId(Long userId) { 385 | int messageId = 0; 386 | try { 387 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT messageId FROM Directions WHERE userId = ?"); 388 | preparedStatement.setLong(1, userId); 389 | final ResultSet result = preparedStatement.executeQuery(); 390 | if (result.next()) { 391 | messageId = result.getInt("messageId"); 392 | } 393 | } catch (SQLException e) { 394 | log.error("Error getting user destination message id", e); 395 | } 396 | return messageId; 397 | } 398 | 399 | public String getUserOrigin(Long userId) { 400 | String origin = ""; 401 | try { 402 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT origin FROM Directions WHERE userId = ?"); 403 | preparedStatement.setLong(1, userId); 404 | final ResultSet result = preparedStatement.executeQuery(); 405 | if (result.next()) { 406 | origin = result.getString("origin"); 407 | } 408 | } catch (SQLException e) { 409 | log.error("Error get user origin", e); 410 | } 411 | return origin; 412 | } 413 | 414 | public boolean deleteUserForDirections(Long userId) { 415 | int updatedRows = 0; 416 | try { 417 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM Directions WHERE userId=?;"); 418 | preparedStatement.setLong(1, userId); 419 | 420 | updatedRows = preparedStatement.executeUpdate(); 421 | } catch (SQLException e) { 422 | log.error("Error deleting user directions", e); 423 | } 424 | return updatedRows > 0; 425 | } 426 | 427 | public boolean putLastUpdate(String token, Integer updateId) { 428 | int updatedRows = 0; 429 | try { 430 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO LastUpdate (token, updateId) VALUES(?, ?)"); 431 | preparedStatement.setString(1, token); 432 | preparedStatement.setInt(2, updateId); 433 | updatedRows = preparedStatement.executeUpdate(); 434 | } catch (SQLException e) { 435 | log.error("Error adding last update", e); 436 | } 437 | return updatedRows > 0; 438 | } 439 | 440 | public Integer getLastUpdate(String token) { 441 | Integer updateId = -1; 442 | try { 443 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT updateId FROM LastUpdate WHERE token = ?"); 444 | preparedStatement.setString(1, token); 445 | final ResultSet result = preparedStatement.executeQuery(); 446 | if (result.next()) { 447 | updateId = result.getInt("updateId"); 448 | } 449 | } catch (SQLException e) { 450 | log.error("Error getting last update", e); 451 | } 452 | return updateId; 453 | } 454 | 455 | public String getUserLanguage(Long userId) { 456 | String languageCode = "en"; 457 | try { 458 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT languageCode FROM UserLanguage WHERE userId = ?"); 459 | preparedStatement.setLong(1, userId); 460 | final ResultSet result = preparedStatement.executeQuery(); 461 | if (result.next()) { 462 | languageCode = result.getString("languageCode"); 463 | } 464 | } catch (SQLException e) { 465 | log.error("Error getting user language", e); 466 | } 467 | return languageCode; 468 | } 469 | 470 | public boolean putUserLanguage(Long userId, String language) { 471 | int updatedRows = 0; 472 | try { 473 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO UserLanguage (userId, languageCode) VALUES(?, ?)"); 474 | preparedStatement.setLong(1, userId); 475 | preparedStatement.setString(2, language); 476 | updatedRows = preparedStatement.executeUpdate(); 477 | } catch (SQLException e) { 478 | log.error("Error updating user language", e); 479 | } 480 | return updatedRows > 0; 481 | } 482 | 483 | public int getWeatherState(Long userId, Long chatId) { 484 | int state = 0; 485 | try { 486 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT state FROM WeatherState WHERE userId = ? AND chatId = ?"); 487 | preparedStatement.setLong(1, userId); 488 | preparedStatement.setLong(2, chatId); 489 | final ResultSet result = preparedStatement.executeQuery(); 490 | if (result.next()) { 491 | state = result.getInt("state"); 492 | } 493 | } catch (SQLException e) { 494 | log.error("Error getting weather state", e); 495 | } 496 | return state; 497 | } 498 | 499 | public boolean insertWeatherState(Long userId, Long chatId, int state) { 500 | int updatedRows = 0; 501 | try { 502 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("REPLACE INTO WeatherState (userId, chatId, state) VALUES (?, ?, ?)"); 503 | preparedStatement.setLong(1, userId); 504 | preparedStatement.setLong(2, chatId); 505 | preparedStatement.setInt(3, state); 506 | updatedRows = preparedStatement.executeUpdate(); 507 | } catch (SQLException e) { 508 | log.error("Error insert weather state", e); 509 | } 510 | return updatedRows > 0; 511 | } 512 | 513 | public Integer getRecentWeatherIdByCity(Long userId, String city) { 514 | Integer cityId = null; 515 | try { 516 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("select cityId FROM RecentWeather WHERE userId=? AND cityName=?"); 517 | preparedStatement.setLong(1, userId); 518 | preparedStatement.setString(2, city); 519 | final ResultSet result = preparedStatement.executeQuery(); 520 | if (result.next()) { 521 | cityId = result.getInt("cityId"); 522 | } 523 | } catch (SQLException e) { 524 | log.error("Error getting recent weather by city", e); 525 | } 526 | 527 | return cityId; 528 | } 529 | 530 | public String[] getUserWeatherOptions(Long userId) { 531 | String[] options = new String[] {"en", "metric"}; 532 | try { 533 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("SELECT * FROM UserWeatherOptions WHERE userId = ?"); 534 | preparedStatement.setLong(1, userId); 535 | final ResultSet result = preparedStatement.executeQuery(); 536 | if (result.next()) { 537 | options[0] = result.getString("languageCode"); 538 | options[1] = result.getString("units"); 539 | } else { 540 | addNewUserWeatherOptions(userId); 541 | } 542 | } catch (SQLException e) { 543 | log.error("Error getting wether options", e); 544 | } 545 | return options; 546 | } 547 | 548 | private boolean addNewUserWeatherOptions(Long userId) { 549 | int updatedRows = 0; 550 | try { 551 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("INSERT INTO UserWeatherOptions (userId) VALUES (?)"); 552 | preparedStatement.setLong(1, userId); 553 | updatedRows = preparedStatement.executeUpdate(); 554 | } catch (SQLException e) { 555 | log.error("Error adding new user weather options", e); 556 | } 557 | return updatedRows > 0; 558 | } 559 | 560 | public boolean putUserWeatherLanguageOption(Long userId, String language) { 561 | int updatedRows = 0; 562 | try { 563 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("UPDATE UserWeatherOptions SET languageCode = ? WHERE userId = ?"); 564 | preparedStatement.setString(1, language); 565 | preparedStatement.setLong(2, userId); 566 | updatedRows = preparedStatement.executeUpdate(); 567 | } catch (SQLException e) { 568 | log.error("Error updating weather language options", e); 569 | } 570 | return updatedRows > 0; 571 | } 572 | 573 | public boolean putUserWeatherUnitsOption(Long userId, String units) { 574 | int updatedRows = 0; 575 | try { 576 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("UPDATE UserWeatherOptions SET units = ? WHERE userId = ?"); 577 | preparedStatement.setString(1, units); 578 | preparedStatement.setLong(2, userId); 579 | updatedRows = preparedStatement.executeUpdate(); 580 | } catch (SQLException e) { 581 | log.error("Error adding weather unit option", e); 582 | } 583 | return updatedRows > 0; 584 | } 585 | 586 | public boolean createNewWeatherAlert(long userId, Integer cityId, String cityName) { 587 | int updatedRows = 0; 588 | try { 589 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("INSERT INTO WeatherAlert (userId, cityId, cityName) VALUES (?,?,?)"); 590 | preparedStatement.setLong(1, userId); 591 | preparedStatement.setInt(2, cityId); 592 | preparedStatement.setString(3, cityName); 593 | updatedRows = preparedStatement.executeUpdate(); 594 | } catch (SQLException e) { 595 | log.error("Error creating weather alert", e); 596 | } 597 | return updatedRows > 0; 598 | } 599 | 600 | public List getAlertCitiesNameByUser(long userId) { 601 | List alertCitiesNames = new ArrayList<>(); 602 | try { 603 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("select cityName FROM WeatherAlert WHERE userId=?"); 604 | preparedStatement.setLong(1, userId); 605 | final ResultSet result = preparedStatement.executeQuery(); 606 | while (result.next()) { 607 | alertCitiesNames.add(result.getString("cityName")); 608 | } 609 | } catch (SQLException e) { 610 | log.error("Error getting alerts by user", e); 611 | } 612 | 613 | return alertCitiesNames; 614 | } 615 | 616 | public boolean deleteAlertCity(Long userId, String cityName) { 617 | int updatedRows = 0; 618 | try { 619 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM WeatherAlert WHERE userId=? AND cityName=?;"); 620 | preparedStatement.setLong(1, userId); 621 | preparedStatement.setString(2, cityName); 622 | updatedRows = preparedStatement.executeUpdate(); 623 | } catch (SQLException e) { 624 | log.error("Error delete city alert", e); 625 | } 626 | return updatedRows > 0; 627 | } 628 | 629 | public boolean deleteAlertsForUser(Long userId) { 630 | int updatedRows = 0; 631 | try { 632 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("DELETE FROM WeatherAlert WHERE userId=?"); 633 | preparedStatement.setLong(1, userId); 634 | updatedRows = preparedStatement.executeUpdate(); 635 | } catch (SQLException e) { 636 | log.error("Error deleting alerts for user", e); 637 | } 638 | return updatedRows > 0; 639 | } 640 | 641 | public List getAllAlerts() { 642 | List allAlerts = new ArrayList<>(); 643 | 644 | try { 645 | final PreparedStatement preparedStatement = connetion.getPreparedStatement("select * FROM WeatherAlert"); 646 | final ResultSet result = preparedStatement.executeQuery(); 647 | while (result.next()) { 648 | WeatherAlert weatherAlert = new WeatherAlert(); 649 | weatherAlert.setId(result.getInt("id")); 650 | weatherAlert.setUserId(result.getInt("userId")); 651 | weatherAlert.setCityId(result.getInt("cityId")); 652 | allAlerts.add(weatherAlert); 653 | } 654 | } catch (SQLException e) { 655 | log.error("Error getting all alerts", e); 656 | } 657 | 658 | return allAlerts; 659 | } 660 | } 661 | --------------------------------------------------------------------------------