@Override
public void onUpdateReceived(Update update) {
UpdatesReceiver.handleUpdates(update);
}
Мен өңдеушіге беремін (өз UpdatesReceiver сыныбы):
public static void handleUpdates(Update update) {
...
if (update.hasMessage() && update.getMessage().hasText()){
log.info("[Update (id {}) типа \"Текстовое сообщение\"]", update.getUpdateId());
new TextMessageHandler(update, replyGenerator).handleTextMessage();
}
else if (update.hasCallbackQuery()) {
//логгирование
new CallbackQueryHandler(update, replyGenerator).handleCallBackQuery();
}
else {
//логгирование
replyGenerator.sendTextMessage(update.getMessage().getChatId(), "Я могу принимать только текстовые messages!");
}
}
UpdatesReceiver – жаңарту түріне байланысты басқаруды басқа мамандандырылған өңдегішке тасымалдайтын орталық өңдеуші: TextMessageHandler немесе CallbackQueryHandler, мен конструкторларына жаңартуды тізбек бойынша әрі қарай жіберемін. Жаңарту - ботпен жұмыс істеу кезінде ең маңызды нәрсе және оны жоғалтуға болмайды, өйткені жаңартуда сақталған ақпараттың көмегімен біз қай пайдаланушыға және қай чатқа жауап жіберу керектігін анықтаймыз. Пайдаланушыға жауаптар жасау үшін мен бөлек сынып жаздым. Ол кәдімгі мәтіндік хабарламаны, кірістірілген пернетақтамен хабарламаны, суреті бар хабарламаны және жауап пернетақтасымен хабарламаны жібере алады. Кірістірілген пернетақта келесідей көрінеді: ол түймелерді басу арқылы пайдаланушы serverге кері қоңырау жіберетін, оны әдеттегі хабарламалар сияқты дерлік өңдеуге болатынын анықтайды. Оны «күту» үшін сізге өзіңіздің өңдеуші қажет. Әрбір түйме үшін әрекетті орнатамыз, содан кейін ол Жаңарту нысанына жазылады. Анау. «Шығын» түймесі үшін кері қоңырау үшін «/баға» сипаттамасын орнатамыз, оны кейінірек жаңартудан аламыз. Әрі қарай, бөлек сыныпта мен бұл кері қоңырауды өңдей аламын:
public void handleCallBackQuery() {
String call_data = update.getCallbackQuery().getData();
long message_id = update.getCallbackQuery().getMessage().getMessageId();
long chat_id = update.getCallbackQuery().getMessage().getChatId();
switch (call_date){
case "/price" :
//тут что-то сделать
break;
...
Жауап беру пернетақтасы келесідей көрінеді: Ол мәні бойынша пайдаланушының теруін ауыстырады. «Кітапхана» түймесін басу ботқа «Кітапхана» хабарламасын жылдам жібереді. Пернетақтаның әрбір түрі үшін мен Builder үлгісін енгізе отырып, өз класымды жаздым: inline және reply . Нәтижесінде сіз өзіңіздің талаптарыңызға байланысты қажетті пернетақтаны «сызуға» болады. Бұл өте ыңғайлы, өйткені пернетақталар әртүрлі болуы мүмкін, бірақ принцип өзгеріссіз қалады. Мұнда кірістірілген пернетақта арқылы хабарлама жіберудің интуитивті әдісі берілген:
public synchronized void sendInlineKeyboardMessage(long chat_id, String gameTitle) {
SendMessage keyboard = InlineKeyboardMarkupBuilder.create(chat_id)
.setText("Вы может узнать следующую информацию об игре " + gameTitle)
.row()
.button("Стоимость " + "\uD83D\uDCB0", "/price " + gameTitle)
.button("Обновлено " + "\uD83D\uDDD3", "/updated " + gameTitle)
.button("Версия " + "\uD83D\uDEE0", "/version " + gameTitle)
.endRow()
.row()
.button("Требования " + "\uD83D\uDCF5", "/requirements " + gameTitle)
.button("Покупки " + "\uD83D\uDED2", "/iap " + gameTitle)
.button("Размер " + "\uD83D\uDD0E", "/size " + gameTitle)
.endRow()
.row()
.button("Получить всю информацию об игре" + "\uD83D\uDD79", "/all " + gameTitle)
.endRow()
.row()
.button("Скрыть клавиатуру", "close")
.endRow()
.build();
try {
execute(keyboard);
} catch (TelegramApiException e) {
log.error("[Не удалось отправить сообщение с -inline- клавиатурой]: {}", e.getMessage());
}
}
Ботқа қатаң функционалдылық беру үшін қиғаш сызықты қолданатын арнайы пәрмендер ойлап табылды: /library, / help, / game, т.б. Әйтпесе, пайдаланушы жазуы мүмкін кез келген қоқысты өңдеуге тура келеді. Шын мәнінде, бұл MessageHandler үшін жазылған:
if (message.equals(ChatCommands.START.getDescription())) {
replyGenerator.sendTextMessage(chat_id, new StartMessageHandler().reply());
replyGenerator.sendReplyKeyboardMessage(chat_id);
}
else if (message.equals(ChatCommands.HELP.getDescription())
|| message.equalsIgnoreCase("Помощь")) {
replyGenerator.sendTextMessage(chat_id, new HelpMessageHandler().reply());
}
...
Осылайша, сіз ботқа қандай пәрмен жіберетініңізге байланысты жұмысқа арнайы өңдеуші қосылады. Одан әрі қарай талдаушы мен кітапхананың жұмысын қарастырайық. Егер сіз ботқа Google Play дүкеніндегі ойынға сілтеме жіберсеңіз, арнайы өңдеуші автоматты түрде жұмыс істейді . Жауап ретінде пайдаланушы ойын туралы ақпаратты келесі пішінде алады: Сонымен бірге, ойынды бот кітапханасына қосуға тырысатын әдіс шақырылады (алдымен жергілікті картаға, содан кейін -> json файлына. ). Егер ойын кітапханада бұрыннан бар болса, тексеру жүргізіледі (кәдімгі хэшмаптағыдай), ал өріс деректері (мысалы, нұсқа нөмірі өзгерген болса), кітапханадағы ойын қайта жазылады. Ешқандай өзгерістер анықталмаса, ешқандай жазбалар жасалмайды. Кітапханада ойын мүлде болмаса, ол алдымен жергілікті картаға жазылады ( tyk сияқты нысан ), содан кейін json файлына жазылады, өйткені serverдегі қолданба күтпеген жерден жабылса, деректер жоғалған, бірақ оны әрқашан файл арқылы оқуға болады. Іс жүзінде, бағдарлама іске қосылған кезде кітапхана әрқашан статикалық блоктағы файлдан бірінші рет жүктеледі:
static {
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(ConcurrentSkipListMap.class, String.class, GooglePlayGame.class);
try {
Path path = Paths.get(LIBRARY_PATH);
if (!Files.exists(path)) {
Files.createDirectories(path.getParent());
Files.createFile(path);
log.info("[Файл библиотеки создан]");
}
else {
ConcurrentMap<string, googleplaygame=""> temporary = mapper.readValue(new File(LIBRARY_PATH), mapType);
games.putAll(temporary);
log.info("[Количество игр в загруженной библиотеке] = " + games.size());
}
}
catch (IOException e) {
log.error("[Ошибка при чтении/записи file] {}", e.getMessage());
}
}
Мұнда қосымша файлдағы деректерді уақытша картаға оқуға тура келеді, содан кейін файлдағы ойынды іздеу кезінде регистрді сездірмеу үшін толық картаға «көшіріледі» (tITan QuEST жазу арқылы бот әлі де табады. кітапханадағы Titan Quest ойыны). Басқа шешім табу мүмкін болмады, бұл Джексонның көмегімен сериядан шығарудың ерекшеліктері. Осылайша, әрбір сілтеме сұрауымен ойын мүмкіндігінше кітапханаға қосылады және кітапхана осылайша кеңейеді. Белгілі бір ойын туралы қосымша ақпаратты /libraryGame_Name командасы арқылы алуға болады . Белгілі бір параметрді (мысалы, ағымдағы нұсқаны) және барлық параметрлерді бірден біле аласыз. Бұл бұрын талқыланған кірістірілген пернетақта арқылы жүзеге асырылады. Жұмыс барысында осында алған дағдыларымды есептер шығару барысында да қолдандым. Мысалы, кітапханада орналасқан кездейсоқ ойындар атауларының тізімі (опция /library пәрмені арқылы қол жетімді):
private String getRandomTitles(){
if (LibraryService.getLibrary().size() < 10){
return String.join("\n", LibraryService.getLibrary().keySet());
}
List<string> keys = new ArrayList<>(LibraryService.getLibrary().keySet());
Collections.shuffle(keys);
List<string> randomKeys = keys.subList(0, 10);
return String.join("\n", randomKeys);
}
Бот сілтемелерді қалай өңдейді? Ол олардың Google Play (хост, протокол, порт) тиесілілігін тексеру үшін тексереді:
private static class GooglePlayCorrectURL {
private static final String VALID_HOST = "play.google.com";
private static final String VALID_PROTOCOL = "https";
private static final int VALID_PORT = -1;
private static boolean isLinkValid(URI link) {
return (isHostExist(link) && isProtocolExist(link) && link.getPort() == VALID_PORT);
}
private static boolean isProtocolExist(URI link) {
if (link.getScheme() != null) {
return link.getScheme().equals(VALID_PROTOCOL);
}
else {
return false;
}
}
private static boolean isHostExist(URI link) {
if (link.getHost() != null) {
return link.getHost().equals(VALID_HOST);
}
else {
return false;
}
}
Егер бәрі реттелген болса, онда бот Jsoup кітапханасының көмегімен сілтеме арқылы қосылады, ол әрі қарай талдауға және талдауға жататын беттің HTML codeын алуға мүмкіндік береді. Сіз ботты қате немесе зиянды сілтеме арқылы алдай алмайсыз.
if (GooglePlayCorrectURL.isLinkValid(link)){
if (!link.getPath().contains("apps")){
throw new InvalidGooglePlayLinkException("К сожалению, бот работает исключительно с играми. Введите другую ссылку.");
}
URL = forceToRusLocalization(URL);
document = Jsoup.connect(URL).get();
}
else {
throw new NotGooglePlayLinkException();
}
...
Бұл жерде бізге аймақтық параметрлермен байланысты мәселені шешуге тура келді. Бот Google Play дүкеніне Еуропада орналасқан serverден қосылады, сондықтан Google Play дүкеніндегі бет сәйкес тілде ашылады. Маған беттің орысша нұсқасына күштеп «қайта бағыттайтын» балдақ жазуға тура келді (жоба, сайып келгенде, біздің аудиторияға бағытталған). Мұны істеу үшін сілтеменің соңында Google Play serverіне GET сұрауында hl: &hl=ru параметрін мұқият қосу керек .
private String forceToRusLocalization(String URL) {
if (URL.endsWith("&hl=ru")){
return URL;
}
else {
if (URL.contains("&hl=")){
URL = URL.replace(
URL.substring(URL.length()-"&hl=ru".length()), "&hl=ru");
}
else {
URL += "&hl=ru";
}
}
return URL;
}
Сәтті қосылымнан кейін біз талдауға және талдауға дайын HTML құжатын аламыз, бірақ бұл осы мақаланың ауқымынан тыс. Талдау codeы осында . Талдаушының өзі қажетті ақпаратты шығарып алады және ойынмен бірге an object жасайды, ол кейінірек қажет болған жағдайда кітапханаға қосылады. <h2>Қорытындылау</h2>Бот белгілі бір функционалдылықты қамтитын бірнеше пәрмендерді қолдайды. Ол пайдаланушыдан хабарламаларды алады және оларды өз командаларымен сәйкестендіреді. Егер бұл сілтеме немесе /game + сілтеме пәрмені болса, ол сол сілтемені Google Play жүйесіне тиесілі екенін тексеру үшін тексереді. Егер сілтеме дұрыс болса, ол Jsoup арқылы қосылып, HTML құжатын алады. Бұл құжат жазбаша талдаушы негізінде талданады. Құжаттан ойын туралы қажетті ақпарат алынады, содан кейін ойыны бар нысан осы деректермен толтырылады. Содан кейін ойыны бар нысан жергілікті жадқа орналастырылады (егер ойын әлі жоқ болса) және деректердің жоғалуын болдырмау үшін дереу файлға жазылады. Кітапханада жазылған ойынды (ойынның аты картаның кілті, ойыны бар нысан картаның мәні) /library Game_name командасы арқылы алуға болады. Көрсетілген ойын бот кітапханасында табылса, пайдаланушыға ойын туралы ақпаратты ала алатын кірістірілген пернетақта қайтарылады. Ойын табылмаса, атау дұрыс жазылғанына көз жеткізу керек (ол Google Play дүкеніндегі ойын атауымен толық сәйкес келуі керек, жағдайды қоспағанда) немесе бот жіберу арқылы ойынды кітапханаға қосу керек. ойынға сілтеме. Мен ботты герокуда орналастырдым және болашақта өз ботын жазып, оны герокуда тегін орналастыруды жоспарлағандар үшін мен сізде кездесетін қиындықтарды шешуге арналған бірнеше кеңес беремін (өйткені мен олармен өзім кездескенмін). Өкінішке орай, Heroku сипатына байланысты бот кітапханасы 24 сағат сайын бір рет «қалпына келтіріледі». Менің жоспарым Heroku serverлерінде файлдарды сақтауға қолдау көрсетпейді, сондықтан ол менің ойын файлымды Github-тан алады. Бірнеше шешім болды: дерекқорды пайдаланыңыз немесе осы файлды ойынмен бірге сақтайтын басқа serverді іздеңіз. Мен әзірге ештеңе жасамауды шештім, өйткені шын мәнінде бот соншалықты пайдалы емес. Бұл маған толық тәжірибе алу үшін қажет болды, бұл мен негізінен қол жеткіздім. Сонымен, Heroku үшін ұсыныстар:
-
Егер сіз Ресейде тұрсаңыз, VPN арқылы герокуда тіркелуіңіз керек.
-
Жобаның түбіне Procfile деп аталатын кеңейтімі жоқ файлды қою керек. Оның мазмұны келесідей болуы керек: https://github.com/miroha/Telegram-Bot/blob/master/Procfile
-
pom.xml ішінде келесі жолдарды мысалға сәйкес қосыңыз , мұнда mainClass тегінде негізгі әдісті қамтитын сыныпқа жол көрсетіледі: bot.BotApplication (егер BotApplication сыныбы бот қалтасында болса).
-
Mvn бумасының пәрмендерін және т.б. пайдаланып ешбір жобаны құрмаңыз, Heroku сіз үшін бәрін жинайды.
-
Жобаға gitignore қосу ұсынылады, мысалы:
# Log file *.log # Compiled resources target # Tests test # IDEA files .idea *.iml
-
Іс жүзінде жобаны github-қа жүктеңіз, содан кейін репозиторийді Heroku-ға қосыңыз (немесе басқа әдістерді қолданыңыз, қателеспесем, олардың 3-і бар).
-
Жүктеп алу сәтті болса («Құрастыру сәтті»), Dynos конфигурациясына өтіңіз:
және жүгірткіні ауыстырыңыз, содан кейін оның ҚОСУЛЫ күйінде екеніне көз жеткізіңіз (мен мұны істемегендіктен, менің ботым жұмыс істемеді және мен бірнеше күн бойы миымды шайқадым және көптеген қажетсіз қозғалыстар жасадым. ).
-
Github жүйесінде бот белгісін жасырыңыз. Ол үшін орта айнымалысынан таңбалауышты алу керек:
public class Bot extends TelegramLongPollingBot { private static final String BOT_TOKEN = System.getenv("TOKEN"); @Override public String getBotToken() { return BOT_TOKEN; } ... }
Содан кейін ботты орналастырғаннан кейін, Параметрлер қойындысындағы Heroku бақылау тақтасында осы айнымалы мәнді орнатыңыз (TOKEN оң жағында VALUE өрісі болады, боттың таңбалауышын сол жерге көшіріңіз):
- Java тілінде жазылған толық жұмыс істейтін жобаны алды;
- үшінші тарап API интерфейсімен (Telegram Bot API) жұмыс істеуді үйренді;
- тәжірибеде мен сериализацияға тереңірек үңілдім, JSON және Джексон кітапханасымен көп жұмыс істедім (бастапқыда мен GSON қолдандым, бірақ онымен проблемалар болды);
- файлдармен жұмыс істеу дағдыларын нығайтты, Java NIO-мен танысты;
- конфигурациялы .xml файлдарымен жұмыс істеуді үйрендім және журнал жүргізуге үйрендім;
- әзірлеу ортасындағы (IDEA) біліктілігін арттыру;
- git-пен жұмыс істеуді үйренді және gitignore құндылығын білді;
- веб-беттерді талдау дағдыларын алды (Jsoup кітапханасы);
- бірнеше дизайн үлгілерін үйренді және пайдаланды;
- codeты жақсарту сезімі мен ұмтылысы (рефакторинг) дамыды;
- Жауап таба алмаған сұрақтарды қоюдан ұялмай, шешімдерді онлайн табуды үйрендім.
GO TO FULL VERSION