@Override
public void onUpdateReceived(Update update) {
UpdatesReceiver.handleUpdates(update);
}
men ishlov beruvchiga o'tkazaman (o'z UpdatesReceiver klassi):
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 - bu yangilanish turiga qarab boshqaruvni boshqa ixtisoslashtirilgan ishlov beruvchiga o'tkazadigan markaziy ishlov beruvchi: TextMessageHandler yoki CallbackQueryHandler, men ularning konstruktorlariga zanjir bo'ylab yangilanishni uzataman. Yangilanish bot bilan ishlashda eng muhim narsa va uni yo'qotib bo'lmaydi, chunki yangilanishda saqlangan ma'lumotlar yordamida biz qaysi foydalanuvchi va qaysi chatga javob jo'natish kerakligini bilib olamiz. Foydalanuvchiga javoblar yaratish uchun men alohida sinf yozdim. U oddiy matnli xabar, ichki klaviaturali xabar, rasmli xabar va javob klaviaturasi bilan xabar yuborishi mumkin. Inline klaviatura quyidagicha ko'rinadi: u tugmalarni bosish orqali foydalanuvchi serverga qayta qo'ng'iroqni yuboradigan tugmalarni belgilaydi, ularni oddiy xabarlar bilan deyarli bir xil tarzda qayta ishlash mumkin. Uni "qo'llab-quvvatlash" uchun sizga o'zingizning ishlov beruvchingiz kerak. Biz har bir tugma uchun amalni o'rnatamiz, so'ngra u Update ob'ektiga yoziladi. Bular. "Xarajat" tugmasi uchun biz qayta qo'ng'iroq qilish uchun "/price" tavsifini o'rnatamiz, uni keyinchalik yangilanishdan olishimiz mumkin. Keyinchalik, alohida sinfda men ushbu qayta qo'ng'iroqni qayta ishlashim mumkin:
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;
...
Javob berish klaviaturasi quyidagicha ko'rinadi: Va mohiyatiga ko'ra, u foydalanuvchi yozishni almashtiradi. “Kutubxona” tugmasini bosish tezda botga “Kutubxona” xabarini yuboradi. Klaviaturaning har bir turi uchun men Builder naqshini qo'llagan holda o'z sinfimni yozdim: inline va reply . Natijada, siz o'zingizning talablaringizga qarab, mohiyatan kerakli klaviaturani "chizishingiz" mumkin. Bu juda qulay, chunki klaviaturalar boshqacha bo'lishi mumkin, ammo printsip bir xil bo'lib qoladi. Ichki klaviatura yordamida xabar yuborishning intuitiv usuli:
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());
}
}
Botga qat'iy funksionallik berish uchun slash belgisidan foydalangan holda maxsus buyruqlar ixtiro qilindi: /library, /help, /o'yin va boshqalar. Aks holda, foydalanuvchi yozishi mumkin bo'lgan har qanday axlatni qayta ishlashimiz kerak bo'ladi. Aslida, bu MessageHandler uchun yozilgan:
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());
}
...
Shunday qilib, siz botga qanday buyruq yuborishingizga qarab, ishga maxsus ishlov beruvchi kiritiladi. Keling, uzoqroqqa boraylik va tahlilchi va kutubxonaning ishini ko'rib chiqaylik. Agar siz botga Google Play do'konidagi o'yinga havola yuborsangiz, maxsus ishlov beruvchi avtomatik ravishda ishlaydi . Bunga javoban foydalanuvchi o'yin haqida ma'lumotni quyidagi shaklda oladi: Shu bilan birga, o'yinni bot kutubxonasiga (avval mahalliy xaritaga, keyin -> json fayliga) qo'shishga harakat qiladigan usul chaqiriladi. ). Agar o'yin allaqachon kutubxonada bo'lsa, u holda tekshiruv o'tkaziladi (odatiy xashmapda bo'lgani kabi) va agar maydon ma'lumotlari (masalan, versiya raqami o'zgargan bo'lsa), kutubxonadagi o'yin ustiga yoziladi. Hech qanday o'zgarishlar aniqlanmasa, hech qanday yozuv kiritilmaydi. Agar kutubxonada umuman o'yin bo'lmasa, u avval mahalliy xaritaga ( tyk kabi ob'ekt ) yoziladi va keyin json fayliga yoziladi, chunki agar serverdagi dastur kutilmaganda yopilsa, ma'lumotlar yo'qolgan, lekin uni har doim fayl yordamida o'qish mumkin. Aslida, dastur ishga tushganda kutubxona har doim statik blokdagi fayldan birinchi marta yuklanadi:
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());
}
}
Bu erda siz qo'shimcha ravishda fayldagi ma'lumotlarni vaqtinchalik xaritaga o'qishingiz kerak, so'ngra faylda o'yinni qidirishda katta harflar sezgirligini saqlab qolish uchun to'liq xaritaga "nusxalanadi" (tITan QuEST yozish orqali bot hali ham topadi. kutubxonadagi Titan Quest o'yini). Boshqa yechim topishning iloji bo'lmadi, bu Jekson yordamida deserializatsiya xususiyatlari. Shunday qilib, havola uchun har bir so'rov bilan, agar iloji bo'lsa, o'yin kutubxonaga qo'shiladi va shu bilan kutubxona kengayadi. Muayyan o'yin haqida qo'shimcha ma'lumotni /libraryGame_Name buyrug'i yordamida olish mumkin . Siz bir vaqtning o'zida ma'lum bir parametrni (masalan, joriy versiya) va barcha parametrlarni topishingiz mumkin. Bu avvalroq muhokama qilingan inline klaviatura yordamida amalga oshiriladi. Ish davomida bu yerda olingan ko‘nikmalarni masalalar yechishda ham qo‘lladim. Masalan, kutubxonada joylashgan tasodifiy o'yinlar nomlari ro'yxati (variant /library buyrug'i yordamida mavjud):
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);
}
Bot havolalarni qanday qayta ishlaydi? U ularni Google Play-ga tegishli yoki yo'qligini tekshiradi (xost, protokol, port):
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;
}
}
Agar hamma narsa tartibda bo'lsa, u holda bot Jsoup kutubxonasidan foydalangan holda havola orqali ulanadi, bu sizga sahifaning HTML kodini olish imkonini beradi, bu keyingi tahlil va tahlil qilish kerak. Siz noto'g'ri yoki zararli havola bilan botni alday olmaysiz.
if (GooglePlayCorrectURL.isLinkValid(link)){
if (!link.getPath().contains("apps")){
throw new InvalidGooglePlayLinkException("К сожалению, бот работает исключительно с играми. Введите другую ссылку.");
}
URL = forceToRusLocalization(URL);
document = Jsoup.connect(URL).get();
}
else {
throw new NotGooglePlayLinkException();
}
...
Bu erda biz mintaqaviy sozlamalar bilan bog'liq muammoni hal qilishimiz kerak edi. Bot Google Play do'koniga Evropada joylashgan serverdan ulanadi, shuning uchun Google Play do'konidagi sahifa tegishli tilda ochiladi. Men sahifaning rus tilidagi versiyasiga majburan "yo'naltiruvchi" tayoqchani yozishim kerak edi (loyiha, axir, bizning auditoriyamizga qaratilgan edi). Buni amalga oshirish uchun havolaning oxirida hl parametrini diqqat bilan qo'shishingiz kerak: &hl=ru Google Play serveriga GET so'rovida .
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;
}
Muvaffaqiyatli ulanishdan so'ng, biz tahlil qilish va tahlil qilish uchun tayyor HTML hujjatini olamiz, ammo bu ushbu maqola doirasidan tashqarida. Parser kodi bu yerda . Tahlil qiluvchining o'zi kerakli ma'lumotlarni oladi va o'yin bilan ob'ekt yaratadi, agar kerak bo'lsa, keyinchalik kutubxonaga qo'shiladi. <h2>Xulosa qilish</h2>Bot ma'lum funksiyalarni o'z ichiga olgan bir nechta buyruqlarni qo'llab-quvvatlaydi. U foydalanuvchidan xabarlarni oladi va ularni o'z buyruqlari bilan moslashtiradi. Agar bu havola yoki /game + link buyrug'i bo'lsa, u o'sha havolani Google Play-ga tegishli yoki yo'qligini tekshiradi. Agar havola to'g'ri bo'lsa, u Jsoup orqali ulanadi va HTML hujjatini oladi. Ushbu hujjat yozma parser asosida tahlil qilinadi. Hujjatdan o'yin haqida kerakli ma'lumotlar olinadi, so'ngra o'yinga ega ob'ekt ushbu ma'lumotlar bilan to'ldiriladi. Keyinchalik, o'yinli ob'ekt mahalliy xotiraga joylashtiriladi (agar o'yin hali mavjud bo'lmasa) va ma'lumotlar yo'qolishining oldini olish uchun darhol faylga yoziladi. Kutubxonada yozilgan o'yinni (o'yin nomi xarita uchun kalit, o'yinga ega ob'ekt xarita uchun qiymat) /library Game_name buyrug'i yordamida olinishi mumkin. Agar ko'rsatilgan o'yin bot kutubxonasida topilsa, foydalanuvchiga o'yin haqida ma'lumot olishi mumkin bo'lgan ichki klaviatura qaytariladi. Agar o'yin topilmasa, siz yoki nomning to'g'ri yozilganligiga ishonch hosil qilishingiz kerak (u Google Play do'konidagi o'yin nomiga to'liq mos kelishi kerak, bundan tashqari) yoki botni yuborish orqali o'yinni kutubxonaga qo'shishingiz kerak. o'yinga havola. Men botni heroku-ga joylashtirdim va kelajakda o'z botini yozishni va uni heroku-da bepul joylashtirishni rejalashtirganlar uchun siz duch kelishi mumkin bo'lgan qiyinchiliklarni hal qilish uchun bir nechta tavsiyalar beraman (chunki men ularga o'zim duch kelganman). Afsuski, Heroku-ning tabiatiga ko'ra, bot kutubxonasi doimo har 24 soatda bir marta "qayta o'rnatiladi". Mening rejam Heroku serverlarida fayllarni saqlashni qo'llab-quvvatlamaydi, shuning uchun u mening o'yin faylimni Github'dan tortib oladi. Bir nechta echim bor edi: ma'lumotlar bazasidan foydalaning yoki ushbu faylni o'yin bilan saqlaydigan boshqa serverni qidiring. Men hozircha hech narsa qilmaslikka qaror qildim, chunki aslida bot unchalik foydali emas. Bu menga to'liq tajriba orttirish uchun kerak edi, bu men erishgan narsadir. Shunday qilib, Heroku uchun tavsiyalar:
-
Agar siz Rossiyada yashasangiz, VPN orqali heroku-da ro'yxatdan o'tishingiz kerak bo'ladi.
-
Loyihaning ildiziga Procfile deb nomlangan kengaytmasiz faylni qo'yish kerak. Uning mazmuni shunday bo'lishi kerak: https://github.com/miroha/Telegram-Bot/blob/master/Procfile
-
Pom.xml da, misolga muvofiq quyidagi qatorlarni qo'shing , bu erda mainClass tegida asosiy usulni o'z ichiga olgan sinfga yo'l ko'rsatiladi: bot.BotApplication (agar BotApplication klassi bot papkasida bo'lsa).
-
Mvn paketi buyruqlari va boshqalar yordamida hech qanday loyiha qurmang, Heroku siz uchun hamma narsani yig'adi.
-
Loyihaga gitignore qo'shish tavsiya etiladi, masalan:
# Log file *.log # Compiled resources target # Tests test # IDEA files .idea *.iml
-
Aslida loyihani github-ga yuklang va keyin omborni Heroku-ga ulang (yoki boshqa usullardan foydalaning, adashmasam, ulardan 3 tasi bor).
-
Agar yuklab olish muvaffaqiyatli bo'lsa ("Yaratish muvaffaqiyatli bo'ldi"), Dynos sozlamalariga o'tishni unutmang:
va slayderni o'zgartiring, so'ngra uning ON holatida ekanligiga ishonch hosil qiling (men buni qilmaganim sababli, mening botim ishlamadi va men bir necha kun miyamni sindirib tashladim va juda ko'p keraksiz harakatlar qildim. ).
-
Github-da bot tokenini yashiring. Buning uchun siz muhit o'zgaruvchisidan tokenni olishingiz kerak:
public class Bot extends TelegramLongPollingBot { private static final String BOT_TOKEN = System.getenv("TOKEN"); @Override public String getBotToken() { return BOT_TOKEN; } ... }
Va keyin botni joylashtirgandan so'ng, ushbu o'zgaruvchini Sozlamalar yorlig'idagi Heroku asboblar paneliga o'rnating (TOKENning o'ng tomonida VALUE maydoni bo'ladi, u erda bot tokenini nusxalash):
- Java-da yozilgan to'liq ishlaydigan loyihani oldi;
- uchinchi tomon API (Telegram Bot API) bilan ishlashni o'rgangan;
- amalda men seriallashtirishni chuqurroq o'rgandim, JSON va Jekson kutubxonasi bilan ko'p ishladim (dastlab men GSON dan foydalandim, lekin u bilan bog'liq muammolar bor edi);
- fayllar bilan ishlash ko'nikmalarimni mustahkamladim, Java NIO bilan tanishdim;
- konfiguratsiya .xml fayllari bilan ishlashni o'rgandim va o'zimni jurnalga o'rgandim;
- Rivojlanish muhitida (IDEA) yaxshilangan malaka;
- git bilan ishlashni o'rgandi va gitignore qiymatini o'rgandi;
- veb-sahifalarni tahlil qilish bo'yicha ko'nikmalarga ega bo'ldi (Jsoup kutubxonasi);
- bir nechta dizayn naqshlarini o'rgangan va qo'llagan;
- kodni yaxshilash tuyg'usi va istagi rivojlangan (refaktoring);
- Men Internetda yechim topishni va javob topa olmagan savollarni berishdan uyalmaslikni o'rgandim.
GO TO FULL VERSION