spring-tg
Create Telegram Bot commands within Spring in a SpringMVC manner. This project uses rubenlagus' Java implementation of the Telegram Bot API
Examples
You can find practical examples in the "spring-tg-examples" module
Spring Boot
Enable spring-tg
It is necessary to add "bot.token" and "bot.name" to a properties file (application.properties)
@SpringBootApplication
@EnableTgBot // Just add this annotation to some configuration bean
public class SpringBootTelegramBotApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootTelegramBotApplication.class, args);
}
}
Commands/Actions
Ping pong example:
@TgController(name = "PingPong", description = "Sends back Pong", regex = "\\/ping") // describe an action
public class PingPong {
@Autowired
private TgMessageService messageService;
@TgRequest // marks an action handler for a specific order of arguments
public void pong(String command) {
messageService.sendMessage("Pong");
}
}
Another Example with more than one argument:
@TgController(name = "Add", description = "Add two numbers together", regex = "\\/add")
public class AddAction {
@Autowired
private TgMessageService messageService;
@TgRequest
public void add(String command, String numberOne, String numberTwo) {
double valueOne = Double.parseDouble(numberOne);
double valueTwo = Double.parseDouble(numberTwo);
double answer = valueOne + valueTwo;
messageService.sendMessage("Answer: " + answer);
}
}
You may also intercept between each argument:
@TgController(name = "Add", description = "Add two numbers together", regex = "\\/add")
public class AddAction {
@Autowired
private TgMessageService messageService;
@TgRequest
public void add(String command) {
messageService.sendMessage("Please enter your first number.");
}
@TgRequest
public void add(String command, String numberOne) {
messageService.sendMessage("Your first number was: " + numberOne);
messageService.sendMessage("Please enter your second number.");
}
@TgRequest
public void add(String command, String numberOne, String numberTwo) {
messageService.sendMessage("Your second number was: " + numberTwo);
double valueOne = Double.parseDouble(numberOne);
double valueTwo = Double.parseDouble(numberTwo);
double answer = valueOne + valueTwo;
messageService.sendMessage("Answer: " + answer);
}
}
Telegram User Scope: @Scope("tg")
@TgController(name = "Concatenate", description = "Concatenate a string infinitely", regex = "\\/concat")
@Scope("tg") // scoped only to one user
public class AddAction {
@Autowired
private TgMessageService messageService;
private String userText = ""; // this userText exists for every Telegram user, since this bean is scoped by the user
@TgRequest
public void concat(String command) {
messageService.sendMessage("Please write any text. Enter 'DONE' when you want to see the results");
}
@TgRequest
public void concat(String command, String text) {
if(text.equals("DONE")) {
messageService.sendMessage(userText);
userText = "";
} else {
userText += text;
messageService.sendMessage("You added: " + text);
}
}
}
More Control
@TgController(name = "Guess", description = "Guess a random number between 1-10", regex = "\\/guess")
@Scope("tg")
public class PingPong {
private static final Random RANDOM = new Random();
private static final int MAX_RETRIES = 5;
@Autowired
private TgMessageService messageService;
private int randomNumber;
private int retryCounter;
@TgRequest
public void guess(String command) {
messageService.sendMessage("Make your guess...");
retryCounter = 0;
randomNumber = RANDOM.nextInt(10) + 1;
}
@TgRequest
public TgRequestResult guess(String command, String guessString) {
int guess = Integer.parseInt(guessString);
if(guess == randomNumber) {
messageService.sendMessage("You got it!");
return TgRequestResult.OK;
} else {
messageService.sendMessage("Wrong, try again.");
retryCounter++;
return TgRequestResult.RETRY;
}
if(retryCounter >= MAX_RETRIES) {
messageService.sendMessage("Too many tries!");
return TgRequestResult.ABORT;
}
}
@TgRequest
public void guess(String command, String guessString, String wish) {
messageService.sendMessage("I am sorry but I can't make your wish '" + wish + "' come true :(");
}
}
Any Telegram message data type
@TgController(name = "Play Music", description = "Plays the given song", regex = "\\/play_music")
public class PingPong {
@Autowired
private TgMessageService messageService;
@TgRequest
public void playMusic(String command) {
messageService.sendMessage("Please send a song");
}
@TgRequest
public void playMusic(String command, org.telegram.telegrambots.api.objects.Audio audio) {
play(audio);
}
private void play(org.telegram.telegrambots.api.objects.Audio audio) {
// play
}
}
Resolving message data types
@TgController(name = "Play Music", description = "Plays the given song", regex = "\\/play_music")
public class PingPong {
@Autowired
private TgMessageService messageService;
@TgRequest
public void playMusic(String command) {
messageService.sendMessage("Please send a song");
}
/**
* Gets invoked when an {@link org.telegram.telegrambots.api.objects.Audio} is received as a second argument
*/
@TgRequest
public void playMusic(String command, org.telegram.telegrambots.api.objects.Audio audio) {
playAudio(audio);
}
/**
* Gets invoked when an {@link org.telegram.telegrambots.api.objects.Voice} is received as a second argument
*/
@TgRequest
public void playMusic(String command, org.telegram.telegrambots.api.objects.Voice voice) {
playVoice(voice);
}
/**
* Gets invoked when any other message is received as a second argument
*/
@TgRequest
public void playMusic(String command, org.telegram.telegrambots.api.objects.Message message) {
messageService.sendMessage("Invalid message. Please send Audio or Voice.");
}
private void playAudio(org.telegram.telegrambots.api.objects.Audio audio) {
// play audio
}
private void playVoice(org.telegram.telegrambots.api.objects.Voice voice) {
// play voice
}
}
Custom logic for command validation (instead of regex)
// set a class for commandValidator to use a custom CommandValidator
@TgController(name = "Save Image", description = "Saves an image to the disk", commandValidator = SaveImageCommandValidator.class )
public class SaveImage {
@Autowired
private TgMessageService messageService;
@TgRequest
public void pong(TgPhotos photos) {
messageService.sendMessage("Saving photo...");
savePhoto(photos.get( photos.size() - 1 ));
}
private void savePhoto(org.telegram.telegrambots.api.objects.PhotoSize photo) {
// save photo
}
}
@Component
public class SaveImageCommandValidator implements CommandValidator {
public boolean validate(Message message) {
return message.hasPhoto();
}
}
Pre and Post action methods
@TgController(name = "PingPong", description = "Sends back Pong", regex = "\\/ping")
public class PingPong {
@Autowired
private TgMessageService messageService;
@TgPreAction
public void pre(/*Params must be empty*/) {
messageService.sendMessage("Hello User, please prepare to be ponged!");
}
@TgRequest
public void pong(String command) {
messageService.sendMessage("Pong");
}
@TgPostAction
public void post(/*Params must be empty*/) {
messageService.sendMessage("Thanks for being ponged, user <3");
}
}
Spring Security
Restrict methods
@TgController(name = "Admin Action", description = "This action can only be used by admins", regex = "\\/admin")
@RequiredArgsConstructor
public class AdminAction {
private final TgMessageService messageService;
@TgRequest
@PreAuthorize("hasRole('ROLE_ADMIN')") // Use spring method security
public void admin(String command) {
messageService.sendMessage("You are an admin!");
}
}
Define Granted Authorities Provider
@Component
@RequiredArgsConstructor
public class MyTgGrantedAuthoritiesProvider implements TgGrantedAuthoritiesProvider {
/**
* Very simple example. User with the username "ThisIsTheAdminUserName"
* gets the admin role. Is a user.
*/
@Override
public Collection<GrantedAuthority> getAuthoritiesOf(TgContext tgContext) {
List<GrantedAuthority> authorities = new ArrayList<>();
if(tgContext.getUser().getUserName().equals("ThisIsTheAdminUserName")) {
authorities.add(getAuthorityInstance("ADMIN"));
}
authorities.add(getAuthorityInstance("USER"));
return authorities;
}
private static GrantedAuthority getAuthorityInstance(String role) {
return new SimpleGrantedAuthority(String.format("ROLE_%s", role));
}
}