tg-simple-api
— это Java-библиотека с интеграцией Spring, предназначенная для быстрого и удобного создания Telegram-ботов с помощью аннотаций и встроенного менеджера состояний.
- Добавьте аннотацию
@EnableTgSimpleApi
в ваш основной класс:
@EnableTgSimpleApi
@SpringBootApplication
public class BotApp {
public static void main(String[] args) {
SpringApplication.run(BotApp.class, args);
}
}
- Создайте бин для
BotHandler
и вашего бота:
@Configuration
@EnableConfigurationProperties(BotProperties.class)
public class BotConfig {
private final BotProperties botProperties;
public BotConfig(BotProperties botProperties) {
this.botProperties = botProperties;
}
@Bean
public TelegramBot telegramBot() {
return new TelegramBot(botProperties.token(), botProperties.name(), botProperties.webHookPath());
}
@Bean
public BotHandler botHandler() {
return new DefaultBotHandler();
}
}
- Передавайте
Update
вBotHandler
:
@RestController
public class WebHookController {
private final BotHandler botHandler;
public WebHookController(BotHandler botHandler) {
this.botHandler = botHandler;
}
@PostMapping("/webhook")
public ResponseEntity<?> receiveWebhook(@RequestBody Update update) {
botHandler.handleMessage(update);
return ResponseEntity.ok().build();
}
}
Обрабатывает текстовые сообщения:
@BotText(text = "/start")
public void startCommand(Update update) { }
-
actionName
— ID действия. Это строка, которая позволяет связать методы и состояние пользователя. По умолчанию у метода с@BotText("/start")
установлено значениеstart-command-method
. Это значение служит стартовой точкой и может быть использовано как ссылка в состоянии. Оно не обязательно уникально: если несколько методов используют одинаковоеactionName
, то они будут доступны в менеджере состояний под одним именем. -
text
— текст сообщения (можно использовать regex, еслиisRegex = true
) -
order
— порядок выполнения методов (если методов вызывается несколько) -
isRegex
— использовать ли регулярное выражение
Изменение стартового действия по умолчанию:
По умолчанию вызывается метод с @BotText("/start")
. Чтобы задать другой метод, нужно изменить defaultStartActionName
:
@Service
public class MessageService {
private final StateManager manager;
@PostConstruct
public void init() {
manager.setDefaultStartActionName("new-default-start-action-name");
}
@BotText(actionName = "new-default-start-action-name")
public void handleStart(Update update) { }
}
Стандартное значение нужно для того чтобы указать библиотеке какой метод будет использоваться при первом обращении пользователя к боту.
Обработка callback-запросов:
@BotCallBack(actionName = "create", callbackName = {"^.*ad$", "^read.*"}, isRegex = true)
public void handleCallback(Update update) { }
-
actionName
— ID действия. -
callbackName
— Массив строк, содержащий регулярные выражения (или имена callback'ов) для callback-данных. -
isRegex
— Использовать ли регулярное выражение для сравнения callback. -
order
— Порядок выполнения.
Обработка документов:
@BotMedia(actionName = "doc-wait",fileNameRegex = ".*",fileExtensions = "pdf")
public void handleDocument(Update update) { }
actionName
— ID действия.fileExtensions
— Ожидаемые расширения.minFileSize
— Минимальный размер файла (кб).maxFileSize
— Максимальный размер файла (кб).fileNameRegex
— Regex имя файла.order
— Порядок выполнения.
Обработка голосовых:
@BotMedia(actionName = "voice_wait")
public void handleVoice(Update update) { }
Этот метод предназначен для обработки голосовых сообщений. Он использует аннотацию @BotMedia
и принимает объект Update
в качестве аргумента.
actionName
— ID действия.maxDurationSeconds
— Максимальная длина голосового сообщения.minDurationSeconds
— Минимальная длина голосового сообщения.order
— Порядок выполнения.textInterpreter
— Определяет, нужно ли переводить голосовое сообщение в текст (по умолчанию:false
).languageCode
— Языковая модель для распознавания (по умолчанию:en
).regex
— Фильтр по тексту, полученному из голосового (по умолчанию:.*
).
Если textInterpreter = true
, метод handleVoice
должен принимать дополнительный аргумент String
, содержащий расшифрованный текст голосового сообщения.
@BotMedia(actionName = "voice_wait")
public void handleVoice(Update update, String transcribedText) { }
Таким образом, в зависимости от значения textInterpreter
, обработчик либо работает с Update
, либо также получает текстовую интерпретацию голосового сообщения.
Обработка медиафайлов:
@BotMedia(actionName = "media-action", mediaType = MediaType.PHOTO)
public void handleMedia(Update update) { }
-
actionName
— ID действия. -
mediaType
— Тип медиафайла (например,MediaType.PHOTO
). -
order
— Порядок выполнения.
Более точная обработка изображений с фильтрами:
@BotPhoto(actionName = "photo-action", minWidth = 100, aspectRatio = "1:1")
public void handlePhoto(Update update) { }
-
actionName
— ID действия. -
minFileSize
— Минимальный размер файла (в килобайтах). -
maxFileSize
— Максимальный размер файла (в килобайтах). -
minWidth
— Минимальная ширина изображения. -
minHeight
— Минимальная высота изображения. -
aspectRatio
— Соотношение сторон изображения. -
format
— Формат изображения.
Аннотация @BotScheduled
используется для планирования задач, которые будут выполняться по расписанию, с учетом роли пользователя, для которого они актуальны. Вот как это работает:
@BotScheduled(fixedRate = 10000, zone = "Europe/Minsk", runOnStartup = true, roles = {"admin"})
public void scheduledMessage(Set<Long> selectedUserIds) {
// Логика обработки для пользователей с ролью "admin"
}
-
fixedRate
— Частота, с которой задача будет выполняться в миллисекундах. Например,10000
означает выполнение задачи каждые 10 секунд. -
runOnStartup
— Определяет, будет ли задача выполнена при старте приложения. Если установлено вtrue
, задача будет выполнена один раз сразу после запуска приложения. -
roles
— Список ролей пользователей, для которых задача будет доступна. В этом примере задача будет выполняться только для пользователей с ролью"admin"
. Метод будет вызываться для каждого пользователя, чейuserId
соответствует роли, указанной вroles
. -
cron
— CRON-выражение для задания расписания выполнения задачи (вместоfixedRate
илиfixedDelay
). -
fixedDelay
— Задержка между выполнения задачи, заданная в миллисекундах. В отличие отfixedRate
, задача будет выполнена только после завершения предыдущей. -
zone
— Часовой пояс, в котором будет выполняться задача. Например,"Europe/Minsk"
для Минска.
-
Подбор пользователей с подходящей ролью: Когда задача запланирована с параметром
roles = {"admin"}
, она будет вызываться только для тех пользователей, у которых есть эта роль. Это означает, что метод будет выполняться для каждого пользователя, чейuserId
соответствует роли"admin"
. -
Передача списка пользователей: В метод передается список
selectedUserIds
, который содержитuserId
всех пользователей с ролью"admin"
. Таким образом, метод может обработать информацию для каждого пользователя в списке. -
Выполнение задачи в заданное время: Задача будет выполнена по расписанию (каждые 10 секунд в данном примере), и для каждого пользователя из списка
selectedUserIds
будет выполнена соответствующая логика (например, отправка сообщения пользователю, обновление данных и т.д.). -
Как происходит отбор пользователей: В процессе выполнения задачи система сначала отбирает пользователей, у которых есть указанные роли, затем передает их
id
в метод. Это гарантирует, что задача будет выполнена только для тех пользователей, которые соответствуют указанным в аннотации ролям. В этом примере, если пользователь имеет роль"admin"
, егоuserId
попадет в список и будет обработан.
Ограничивает доступ к обработчику по ролям:
@BotCallBack(actionName = "admin-action", callbackName = "admin-callback")
@RoleBasedAccess(roles = {"admin"})
public void adminOnly(Update update) { }
roles
— Роли, которым доступен данный метод. (все пользователи инициализируются какuser
при первом обращении)
Компонент StateManager
управляет текущим состоянием пользователя и позволяет реализовать многошаговые сценарии взаимодействия.
-
setNextStep(Long userId, UserContext context)
— задает состояние пользователю.UserContext
содержит:-
UserState
— тип ожидаемых взаимодействий (например, текст, фото, callback) -
список допустимых
actionName
— какие действия разрешены в этом состоянии
-
-
addUserRole(Long userId, String role)
— добавляет роль для пользователя (например, "admin") -
removeUserRole(Long userId, String role)
— забирает роль у пользователя -
setDefaultStartActionName(String actionName)
— задает стартовое действие по умолчанию, которое вызывается при первом сообщении пользователя или сбросе состояния
Менеджер состояний также поддерживает добавление и удаление слушателей для изменений ролей и состояний.
-
addRoleChangeListener(RoleChangeListener roleChangeListener)
— добавляет слушателя для изменений роли пользователя. -
removeRoleChangeListener(RoleChangeListener roleChangeListener)
— удаляет слушателя для изменений роли пользователя. -
addStateChangeListener(StateChangeListener stateChangeListener)
— добавляет слушателя для изменений состояния пользователя. -
removeStateChangeListener(StateChangeListener stateChangeListener)
— удаляет слушателя для изменений состояния пользователя.
-
RoleChangeListener
: интерфейс для прослушивания изменений ролей пользователя.public interface RoleChangeListener { void onRoleChange(Long userId, String oldRole, String newRole); }
-
onRoleChange(Long userId, String oldRole, String newRole)
— вызывается при изменении роли пользователя. Параметры:-
userId
— идентификатор пользователя -
oldRole
— старая роль пользователя -
newRole
— новая роль пользователя
-
-
-
StateChangeListener
: интерфейс для прослушивания изменений состояний пользователя.public interface StateChangeListener { void onStateChange(Long userId, UserContext oldState, UserContext newState); }
-
onStateChange(Long userId, UserContext oldState, UserContext newState)
— вызывается при изменении состояния пользователя. Параметры:-
userId
— идентификатор пользователя -
oldState
— предыдущее состояние пользователя (представлено объектомUserContext
) -
newState
— новое состояние пользователя (представлено объектомUserContext
)
-
-
manager.setNextStep(userId, UserContext.builder()
.addState(Set.of(UserState.AWAITING_VOICE,UserState.AWAITING_DOCUMENT))
.addActionData(Set.of("voice_wait","doc_wait"))
.build());
На данный момент библиотека поддерживает только следующие состояния:
- AWAITING_TEXT — Ожидание текстового ввода.
- AWAITING_VOICE — Ожидание голосового сообщения.
- AWAITING_DOCUMENT — Ожидание документа.
- AWAITING_CALLBACK — Ожидание callback-запроса.
- AWAITING_PHOTO — Ожидание фотографии.
- AWAITING_MEDIA — Ожидание других медиафайлов (например, видео, аудио).
По умолчанию все внутренние компоненты библиотеки (например, DefaultStateManager
) можно переопределить. Для этого достаточно определить бин с таким же типом. Библиотека использует @ConditionalOnMissingBean
, чтобы ваши реализации автоматически подменяли дефолтные.
Например, чтобы переопределить StateManager
:
@Bean
public StateManager myCustomStateManager() {
return new MyStateManager();
}
-
Все методы с аннотациями
@BotCallBack, @BotVoice, @BotDocument, @BotMedia, @BotPhoto, @BotText
должны приниматьUpdate
в качестве аргумента. -
Поддерживаются Webhook и LongPolling.
Новые пользователи автоматически получают роль user
. Для изменения роли используется:
manager.addUserRole(userId, "admin");
В библиотеке предусмотрен набор стандартных уведомителей (Notifier
), которые автоматически отправляют сообщения пользователю в различных ситуациях. Все они реализуют отдельные интерфейсы и по умолчанию используют бин Notifier
, через который происходит отправка сообщений.
Каждый уведомитель можно переопределить, создав собственный бин соответствующего интерфейса.
Назначение: уведомляет пользователя об отсутствии прав на выполнение команды. Формат сообщения:
❌ You do not have permission to use this command.
Your current roles: <ROLE_1, ROLE_2>
Интерфейс: AccessDeniedNotifier
Назначение: сообщение об инициализации языковой модели. Формат сообщения:
🤖 Initializing the language model... Please wait a moment
Интерфейс: InitModelMessageNotifier
Назначение: уведомление о том, что запрос обрабатывается (в данный момент используется только во время перевода голоса в текст). Формат сообщения:
⏳ Please wait... Your request is being processed.
Интерфейс: ProcessingMessageNotifier
Назначение: сообщает о том, что не удалось найти подходящую языковую модель. Формат сообщения:
⚠️ An error occurred while trying to find a relevant model. Please try again later.
Интерфейс: ExceptionFoundRelevantModelNotifier
Назначение: уведомляет пользователя о неожиданном действии (не соответствует текущему состоянию). Формат сообщения:
⚠️ I wasn't expecting that action.
Your current state: <STATE_1, STATE_2>
Интерфейс: UnexpectedActionNotifier
Назначение: уведомляет пользователя, если распознанный голосовой текст не соответствует ожидаемому шаблону (regex
).
Формат сообщения:
⚠️ Sorry, I couldn't recognize your voice message as a valid command.
🔊 Your input: "<user's voice text>"
🧪 Expected to match: `<regex>`
Интерфейс: VoiceRegexFailedNotify
Назначение: универсальный отправщик сообщений в Telegram, используется всеми Notifier
-классами.
Интерфейс: Notifier
Метод:
void sendMessage(Long chatId, String text);
💡 Все уведомители используют
@Autowired(required = false)
, поэтому вы можете легко переопределить поведение, создав свой бин нужного интерфейса.
Вы можете настроить распознаватель голоса в приложении, определив бин AudioTranscribe
. Пример конфигурации:
@Bean
public AudioTranscribe audioTranscribe() {
DefaultAudioTranscribe audioTranscribe = new DefaultAudioTranscribe(telegramBot());
audioTranscribe.setToken(botProperties.token());
audioTranscribe.initOnStart(true);
audioTranscribe.addLanguageModel("ru", "C:/Users/karma/Downloads/vosk-model-ru-0.42");
return audioTranscribe;
}
-
audioTranscribe.setToken(String token)
:- Устанавливает токен для вашего бота. Токен необходим для общения с API Telegram.
-
audioTranscribe.setInitOnStart(boolean initOnStart)
:- Если установить в
true
, все модели распознавания голоса инициализируются при старте приложения. - Если установить в
false
, модель будет инициализироваться только при первом запросе на распознавание голоса. - Поведение по умолчанию:
false
(инициализация только при первом запросе).
- Если установить в
-
audioTranscribe.addLanguageModel(String languageCode, String modelPath)
:- Добавляет языковую модель для конкретного языка.
- В примере добавляется модель для русского языка из указанного пути.
-
Не обязательно создавать экземпляр
AudioTranscribe
: Бин будет автоматически создан, но появится ошибка если попробовать использовать функцию распознавания голоса предварительно не настроивDefaultAudioTranscribe
. -
При первом использовании: Если
initOnStart
установлено вfalse
, модель будет инициализирована только при первом запросе на распознавание, и этот процесс может занять некоторое время в зависимости от размера модели.
По умолчанию в библиотеке используется ИИ для распознавания голоса Vosk. Vosk — это система распознавания речи с открытым исходным кодом, которая поддерживает множество языков и работает на различных платформах.
Для корректной работы распознавания голоса необходимо скачать и добавить соответствующую языковую модель. Вы можете найти различные модели для Vosk на официальной странице:
Просто выберите модель для нужного языка и укажите путь к ней в конфигурации, как показано ниже:
audioTranscribe.addLanguageModel("ru", "C:/path/to/vosk-model-ru");
Пример для русского языка:
audioTranscribe.addLanguageModel("ru", "C:/Users/karma/Downloads/vosk-model-ru-0.42");
Vosk поддерживает множество языков, включая, но не ограничиваясь:
- Русский
- Английский
- Испанский
- Французский
- Немецкий
Для других языков можно найти модели на указанном выше сайте или в репозиториях с открытым исходным кодом.