JavaRush /Blog Jawa /Random-JV /Telegram bot - pangeling liwat webHook ing Jawa utawa ngo...

Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google! Bagean 2

Diterbitake ing grup
Bagean kapindho proyek - iki link menyang pisanan: Lan kelas BotState : Supaya bot kita ngerti apa sing dikarepake ing wektu tartamtu, contone, mbusak pangeling, kita kudu piye wae supaya bot kita ngerti yen nomer sing dilebokake lan dikirim saiki kudu dianggep minangka ID pangeling saka dhaptar lan kudu dibusak. Mulane, sawise ngeklik tombol "Busak" , bot kasebut menyang negara BotState.ENTERNUMBEREVENT , iki minangka kelas Enum sing digawe khusus karo negara bot.
public enum BotState {
    ENTERDESCRIPTION,//the bot will wait for the description to be entered.
    START,
    MYEVENTS, //the bot show to user list events.
    ENTERNUMBEREVENT,//the bot will wait for the number of event to be entered.
    ENTERDATE, //the bot will wait for the date to be entered
    CREATE, //the bot run created event
    ENTERNUMBERFOREDIT, //the bot will wait for the number of event to be entered
    EDITDATE, //the bot will wait for the date to be entered
    EDITDESCRIPTION,//the bot will wait for the description to be entered
    EDITFREQ,//the bot will wait callbackquery
    ALLUSERS, // show all users
    ALLEVENTS, //show all events
    ENTERNUMBERUSER,//the bot will wait for the number of user to be entered.
    ENTERTIME,//the bot will wait for the hour to be entered.
    ONEVENT // state toggle
}
Lan saiki kita ngarepake ngetik nomer - " Ketik nomer pangeling saka dhaptar ." Sawise ngetik sing bakal pindhah menyang cara sing dikarepake kanggo diproses. Punika saklar negara kita:
public class BotStateCash {
    private final Map<long, botstate=""> botStateMap = new HashMap<>();

    public void saveBotState(long userId, BotState botState) {
        botStateMap.put(userId, botState);
    }
}

</long,>
Peta biasa kanthi ID pangguna lan statuse. Kolom int adminId kanggo kula) Sabanjure, logika metode handleUpdate bakal mriksa pesen apa iki? Callbackquery utawa mung teks? Yen iki minangka teks biasa, banjur pindhah menyang cara handleInputMessage , ing ngendi kita ngolah tombol menu utama, lan yen diklik, banjur kita nyetel negara sing dikarepake, nanging yen ora diklik lan iki teks sing ora pati ngerti, banjur kita nyetel negara saka cache, yen ora ana, banjur kita nyetel negara wiwitan. Banjur teks dadi ngolah cara nangani karo negara sing kita butuhake. Saiki kita nampilake logika kelas MessageHandler , sing tanggung jawab kanggo ngolah pesen gumantung saka kahanan bot:
public class MessageHandler {

    private final UserDAO userDAO;
    private final MenuService menuService;
    private final EventHandler eventHandler;
    private final BotStateCash botStateCash;
    private final EventCash eventCash;

    public MessageHandler(UserDAO userDAO, MenuService menuService, EventHandler eventHandler, BotStateCash botStateCash, EventCash eventCash) {
        this.userDAO = userDAO;
        this.menuService = menuService;
        this.eventHandler = eventHandler;
        this.botStateCash = botStateCash;
        this.eventCash = eventCash;
    }

    public BotApiMethod<!--?--> handle(Message message, BotState botState) {
        long userId = message.getFrom().getId();
        long chatId = message.getChatId();
        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(String.valueOf(chatId));
        //if new user
        if (!userDAO.isExist(userId)) {
            return eventHandler.saveNewUser(message, userId, sendMessage);
        }
        //save state in to cache
        botStateCash.saveBotState(userId, botState);
        //if state =...
        switch (botState.name()) {
            case ("START"):
                return menuService.getMainMenuMessage(message.getChatId(),
                        "Воспользуйтесь главным меню", userId);
            case ("ENTERTIME"):
                //set time zone user. for correct sent event
                return eventHandler.enterLocalTimeUser(message);
            case ("MYEVENTS"):
                //list events of user
                return eventHandler.myEventHandler(userId);
            case ("ENTERNUMBEREVENT"):
                //remove event
                return eventHandler.removeEventHandler(message, userId);
            case ("ENTERDESCRIPTION"):
                //enter description for create event
                return eventHandler.enterDescriptionHandler(message, userId);
            case ("ENTERDATE"):
                //enter date for create event
                return eventHandler.enterDateHandler(message, userId);
            case ("CREATE"):
                //start create event, set state to next step
                botStateCash.saveBotState(userId, BotState.ENTERDESCRIPTION);
                //set new event to cache
                eventCash.saveEventCash(userId, new Event());
                sendMessage.setText("Введите описание события");
                return sendMessage;
            case ("ENTERNUMBERFOREDIT"):
                //show to user selected event
                return eventHandler.editHandler(message, userId);
            case ("EDITDESCRIPTION"):
                //save new description in database
                return eventHandler.editDescription(message);
            case ("EDITDATE"):
                //save new date in database
                return eventHandler.editDate(message);
            case ("ALLEVENTS"):
                //only admin
                return eventHandler.allEvents(userId);
            case ("ALLUSERS"):
                //only admin
                return eventHandler.allUsers(userId);
            case ("ONEVENT"):
                // on/off notification
                return eventHandler.onEvent(message);
            case ("ENTERNUMBERUSER"):
                //only admin
                return eventHandler.removeUserHandler(message, userId);
            default:
                throw new IllegalStateException("Unexpected value: " + botState);
        }
    }
}
ing cara nangani, kita mriksa status pesen sing ditampa lan ngirim menyang handler acara - kelas EventHandler. Ing kene kita duwe rong kelas anyar, MenuService lan EventCash . MenuService - kene kita nggawe kabeh menu kita. EventCash - padha karo BotStateCash, bakal nyimpen bagean acara sawise input lan nalika input rampung, kita bakal nyimpen acara ing database.
@Service
@Setter
@Getter
// used to save entered event data per session
public class EventCash {

    private final Map<long, event=""> eventMap = new HashMap<>();

    public void saveEventCash(long userId, Event event) {
        eventMap.put(userId, event);
    }
}
</long,>
Inggih, punika. nalika kita nggawe acara, obyek Event anyar digawe ing cache -eventCash.saveEventCash (userId, Event anyar ()); Banjur kita ketik katrangan acara lan ditambahake menyang cache:
Event event = eventCash.getEventMap().get(userId);
event.setDescription(description);
//save to cache
eventCash.saveEventCash(userId, event);
Banjur ketik nomer:
Event event = eventCash.getEventMap().get(userId);
event.setDate(date);
//save data to cache
eventCash.saveEventCash(userId, event);
Kelas CallbackQueryHandler padha karo MessageHandler , mung kita ngolah pesen callbackquery ing kana. Ora ana gunane kanggo nganalisa logika kerja karo acara - EventHandler , wis akeh banget huruf, jelas saka jeneng metode lan komentar ing kode kasebut. Lan aku ora weruh titik laying metu rampung ing teks, ana luwih saka 300 baris. Punika link menyang kelas ing Github . Padha dadi kanggo kelas MenuService , ngendi kita nggawe menu kita. Sampeyan bisa maca babagan kasebut kanthi rinci ing situs web produsen perpustakaan telegram - https://github.com/rubenlagus/TelegramBots/blob/master/TelegramBots.wiki/FAQ.md Utawa ing buku referensi Telegram - https:// tlgrm.ru/docs/bots /api Saiki kita isih duwe bagean sing paling enak. Iki kelas kanggo nangani pesen EventService :
@EnableScheduling
@Service
public class EventService {
    private final EventDAO eventDAO;
    private final EventCashDAO eventCashDAO;

    @Autowired
    public EventService(EventDAO eventDAO, EventCashDAO eventCashDAO) {
        this.eventDAO = eventDAO;
        this.eventCashDAO = eventCashDAO;
    }

    //start service in 0:00 every day
    @Scheduled(cron = "0 0 0 * * *")
    // @Scheduled(fixedRateString = "${eventservice.period}")
    private void eventService() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int month = calendar.get(Calendar.MONTH);
        int year = calendar.get(Calendar.YEAR);

        //get event list is now date
        List<event> list = eventDAO.findAllEvent().stream().filter(event -> {
            if (event.getUser().isOn()) {
                EventFreq eventFreq = event.getFreq();

                //set user event time
                Calendar calendarUserTime = getDateUserTimeZone(event);

                int day1 = calendarUserTime.get(Calendar.DAY_OF_MONTH);
                int month1 = calendarUserTime.get(Calendar.MONTH);
                int year1 = calendarUserTime.get(Calendar.YEAR);
                switch (eventFreq.name()) {
                    case "TIME": //if one time - remove event
                        if (day == day1 && month == month1 && year == year1) {
                            eventDAO.remove(event);
                            return true;
                        }
                    case "EVERYDAY":
                        return true;
                    case "MONTH":
                        if (day == day1) return true;
                    case "YEAR":
                        if (day == day1 && month == month1) return true;
                    default: return false;
                }
            } else return false;
        }).collect(Collectors.toList());

        for (Event event : list) {
            //set user event time
            Calendar calendarUserTime = getDateUserTimeZone(event);
            int hour1 = calendarUserTime.get(Calendar.HOUR_OF_DAY);
            calendarUserTime.set(year, month, day, hour1, 0, 0);

            String description = event.getDescription();
            String userId = String.valueOf(event.getUser().getId());

            //save the event to the database in case the server reboots.
            EventCashEntity eventCashEntity = EventCashEntity.eventTo(calendarUserTime.getTime(), event.getDescription(), event.getUser().getId());
            eventCashDAO.save(eventCashEntity);

            //create a thread for the upcoming event with the launch at a specific time
            SendEvent sendEvent = new SendEvent();
            sendEvent.setSendMessage(new SendMessage(userId, description));
            sendEvent.setEventCashId(eventCashEntity.getId());

            new Timer().schedule(new SimpleTask(sendEvent), calendarUserTime.getTime());
        }
    }

    private Calendar getDateUserTimeZone(Event event) {
        Calendar calendarUserTime = Calendar.getInstance();
        calendarUserTime.setTime(event.getDate());
        int timeZone = event.getUser().getTimeZone();

        //set correct event time with user timezone
        calendarUserTime.add(Calendar.HOUR_OF_DAY, -timeZone);
        return calendarUserTime;
    }
}

</event>
@EnableScheduling - ngaktifake karya sing dijadwalake ing Spring. @Scheduled (cron = "0 0 0 * * *") - kita ngatur cara kanggo mbukak ing 0:00 saben dina calendar.setTime (Tanggal anyar ()); - nyetel wektu server. Kita entuk dhaptar pangeling kanggo dina iki, liwat sihir stream lan lambda. We mbukak liwat dhaftar ditampa, nyetel wektu ngirim bener calendarUserTime lan ... Iki ngendi aku mutusaké kanggo Dodge lan miwiti pangolahan telat ing wektu. Kelas Wektu ing jawa tanggung jawab kanggo iki . Timer anyar () .jadwal (SimpleTask anyar (sendEvent), calendarUserTime.getTime ()); Kanggo iki, kita kudu nggawe thread:
public class SendEvent extends Thread {


    private long eventCashId;
    private SendMessage sendMessage;

    public SendEvent() {
    }

    @SneakyThrows
    @Override
    public void run() {
        TelegramBot telegramBot = ApplicationContextProvider.getApplicationContext().getBean(TelegramBot.class);
        EventCashDAO eventCashDAO = ApplicationContextProvider.getApplicationContext().getBean(EventCashDAO.class);
        telegramBot.execute(sendMessage);
        //if event it worked, need to remove it from the database of unresolved events
        eventCashDAO.delete(eventCashId);
    }
}
lan implementasi TimerTask
public class SimpleTask extends TimerTask {
    private final SendEvent sendEvent;

    public SimpleTask(SendEvent sendEvent) {
        this.sendEvent = sendEvent;
    }

    @Override
    public void run() {
        sendEvent.start();
    }
}
Ya, aku ngerti banget yen sampeyan bisa mbukak database saben 20 menit lan ngirim pesen, nanging aku nulis kabeh babagan iki ing wiwitan)) Ing kene kita uga nemoni kasangsaran Heroku No. Ing rencana gratis, sampeyan diwenehi udakara 550 dino, kaya jam operasi aplikasi sampeyan saben wulan. Iki ora cukup kanggo sasi lengkap operasi aplikasi, nanging yen sampeyan nyambung kertu, sampeyan bakal diwenehi liyane 450 dino, kang cukup kanggo mripat. Yen sampeyan kuwatir babagan kertu, sampeyan bisa ngubungake sing kosong, nanging priksa manawa ana $ 0,6 ... Iki minangka jumlah verifikasi, mung kudu ana ing akun kasebut. Ora ana biaya sing didhelikake kajaba sampeyan ngganti tarif dhewe. Ing rencana gratis, ana masalah cilik liyane, ayo diarani No. ing wektu liyane. Saka iki, kabeh proses ing memori bakal dibusak. Kanggo ngatasi masalah iki, aku teka karo Tabel EventCash. Sadurunge dikirim, acara disimpen ing tabel sing kapisah:
EventCashEntity eventCashEntity = EventCashEntity.eventTo(calendarUserTime.getTime(), event.getDescription(), event.getUser().getId());
eventCashDAO.save(eventCashEntity);
Lan sawise ngirim, ing ngisor iki bakal dibusak:
@Override
public void run() {
    TelegramBot telegramBot = ApplicationContextProvider.getApplicationContext().getBean(TelegramBot.class);
    EventCashDAO eventCashDAO = ApplicationContextProvider.getApplicationContext().getBean(EventCashDAO.class);
    telegramBot.execute(sendMessage);
    //if event it worked, need to remove it from the database of unresolved events
    eventCashDAO.delete(eventCashId);
}
ApplicationContextProvider minangka kelas khusus kanggo njupuk konteks kanthi cepet:
@Component
//wrapper to receive Beans
public class ApplicationContextProvider implements ApplicationContextAware {

    private static ApplicationContext context;

    public static ApplicationContext getApplicationContext() {
        return context;
    }

    @Override
    public void setApplicationContext(ApplicationContext ac)
            throws BeansException {
        context = ac;
    }
}
Kanggo mriksa acara sing durung diproses, aku nggawe layanan khusus sing duwe metode sing ditandhani @PostConstruct - mlaku sawise saben aplikasi diwiwiti. Iku njupuk munggah kabeh acara unprocessed saka database lan bali menyang memori. Punika Heroku awon kanggo sampeyan!
@Component
public class SendEventFromCache {

    private final EventCashDAO eventCashDAO;
    private final TelegramBot telegramBot;

    @Value("${telegrambot.adminId}")
    private int admin_id;

    @Autowired
    public SendEventFromCache(EventCashDAO eventCashDAO, TelegramBot telegramBot) {
        this.eventCashDAO = eventCashDAO;
        this.telegramBot = telegramBot;
    }

    @PostConstruct
    @SneakyThrows
    //after every restart app  - check unspent events
    private void afterStart() {
        List<eventcashentity> list = eventCashDAO.findAllEventCash();

        SendMessage sendMessage = new SendMessage();
        sendMessage.setChatId(String.valueOf(admin_id));
        sendMessage.setText("Произошла перезагрузка!");
        telegramBot.execute(sendMessage);

        if (!list.isEmpty()) {
            for (EventCashEntity eventCashEntity : list) {
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(eventCashEntity.getDate());
                SendEvent sendEvent = new SendEvent();
                sendEvent.setSendMessage(new SendMessage(String.valueOf(eventCashEntity.getUserId()), eventCashEntity.getDescription()));
                sendEvent.setEventCashId(eventCashEntity.getId());
                new Timer().schedule(new SimpleTask(sendEvent), calendar.getTime());
            }
        }
    }
}
</eventcashentity>
Aplikasi kita wis siyap, lan wektune kanggo njaluk alamat Heroku kanggo aplikasi lan database. Aplikasi sampeyan kudu diterbitake ing Github !!! Pindhah menyang Heroku.com Klik Gawe Aplikasi Anyar , ketik jeneng aplikasi, pilih Eropa, gawe aplikasi . Sing, panggonan kanggo aplikasi wis siyap. Yen sampeyan ngeklik mbukak App, browser bakal ngarahake sampeyan menyang alamat aplikasi sampeyan, iki alamat webhook sampeyan - https://your_name.herokuapp.com/ Ndhaptar ing telegram, lan ing aplikasi.property s setelan ngganti telegrambot. webHookPath = https: //telegrambotsimpl.herokuapp.com/ menyang server.port = 5000 sampeyan bisa dibusak utawa dikomentari. Saiki ayo nyambungake database. Pindhah menyang tab Sumber Daya ing Heroku, klik: Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google!  Bagean 2: - 1 Temokake Heroku Postgres ing kana , klik instal : Sampeyan bakal dialihake menyang kaca akun database. Temokake ana ing Setelan / Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google!  Bagean 2: - 2 Bakal ana kabeh data sing dibutuhake saka database sampeyan. Ing application.properties kabeh saiki kudu kaya iki:
#server.port=5000

telegrambot.userName=@calendar_event_bot
telegrambot.botToken=1731265488:AAFDjUSk3vu5SFfgdfh556gOOFmuml7SqEjwrmnEF5Ak
telegrambot.webHookPath=https://telegrambotsimpl.herokuapp.com/
#telegrambot.webHookPath=https://f5d6beeb7b93.ngrok.io


telegrambot.adminId=39376213

eventservice.period =600000

#spring.datasource.driver-class-name=org.postgresql.Driver
#spring.datasource.url=jdbc:postgresql://localhost:5432/telegramUsers
#spring.datasource.username=postgres
#spring.datasource.password=password

spring.datasource.url=jdbc:postgresql:ec2-54-247-158-179.eu-west-1.compute.amazonaws.com:5432/d2um26le5notq?ssl=true&sslmode=require&sslfactory=org.postgresql.ssl.NonValidatingFactory
spring.datasource.username=ulmbeymwyvsxa
spring.datasource.password=4c7646c69dbgeacbk98fa96e8daa6d9b1bl4894e67f3f3ddd6a27fe7b0537fd
Ganti data saka akun sampeyan karo sampeyan: Ing lapangan jdbc:postgresql:ec2-54-247-158-179.eu-west-1.compute.amazonaws.com:5432/d2um26le5notq?ssl=true&sslmode=require&sslfactory=org. postgresql.ssl .NonValidatingFactory kudu diganti kanthi kandel karo data sing cocog saka akun (Host, Database). Jeneng pangguna, kolom sandhi ora angel ditebak. Saiki kita kudu nggawe tabel ing database, aku nindakake iki saka IDEA. Skrip kita bakal migunani kanggo nggawe database. Kita nambahake database kaya sing ditulis ing ndhuwur: Kita njupuk Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google!  Bagean 2: - 3 kolom Host, User, Password, Database saka akun kasebut. Kolom URL minangka kolom spring.datasource.url nganti tandha pitakon. Kita pindhah menyang tab Advanced , kudu kaya iki: Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google!  Bagean 2: - 4 Yen sampeyan nindakake kabeh kanthi bener, banjur sawise ngeklik test, bakal ana tandha centhang ijo. Klik OK. Klik-tengen ing database kita lan pilih Langsung menyang konsol query . Salin skrip kita ing kana lan klik eksekusi . Database kudu digawe. 10.000 garis kasedhiya kanggo sampeyan kanthi gratis! Kabeh wis siyap kanggo Deploy. Pindhah menyang aplikasi kita ing Heroku ing bagean Deploy. Pilih bagean Github ana: Telegram bot - pangeling liwat webHook ing Jawa utawa ngomong ora menyang tanggalan Google!  Bagean 2: - 5 Sambungake repositori sampeyan menyang Heroku. Saiki cabang sampeyan bakal katon. Aja lali push owah-owahan paling anyar kanggo .properties. Ing ngisor iki, pilih cabang sing bakal diundhuh banjur klik Deploy branch . Yen kabeh wis rampung kanthi bener, sampeyan bakal diwenehi kabar yen aplikasi kasebut wis sukses. Aja lali ngaktifake penyebaran otomatis saka .. Supaya aplikasi sampeyan diwiwiti kanthi otomatis. Miturut cara, nalika sampeyan push owah-owahan menyang GitHub, Heroku bakal kanthi otomatis miwiti maneh aplikasi. Ati-ati babagan iki, nggawe utas sing kapisah kanggo bullying, lan gunakake sing utama mung kanggo aplikasi sing digunakake. Saiki Murah #2! Iki minangka kerugian sing kondhang saka rencana gratis kanggo Heroku. Yen ora ana pesen sing mlebu, aplikasi dadi mode siyaga, lan sawise nampa pesen bakal njupuk wektu sing cukup suwe kanggo miwiti, sing ora nyenengake. Ana solusi prasaja kanggo iki - https://uptimerobot.com/ Lan ora, gadget ping Google ora bakal mbantu, aku malah ora ngerti saka ngendi info iki teka, aku nggoleki pitakonan iki ing Google, lan kira-kira 10 taun saiki topik iki ora bisa digunakake, yen bisa digunakake. Aplikasi iki bakal ngirim panjalukan HEAD menyang alamat sing sampeyan nemtokake kanggo wektu sing wis disetel lan, yen ora nanggapi, ngirim pesen liwat email. Ora bakal angel kanggo sampeyan ngerteni, ora ana tombol sing cukup kanggo bingung)) Sugeng !! Yen aku ora lali apa-apa lan sampeyan enten, sampeyan duwe aplikasi dhewe sing bisa digunakake kanthi gratis lan ora nate nabrak. Kesempatan kanggo bullying lan eksperimen mbukak sadurunge sampeyan. Ing kasus apa wae, aku siap mangsuli pitakon lan nampa kritik! Kode: https://github.com/papoff8295/webHookBotForHabr Bahan sing digunakake: https://tlgrm.ru/docs/bots/api - babagan bot. https://en.wikibooks.org/wiki/Java_Persistence - babagan hubungan ing basis data. https://stackoverflow.com/questions/11432498/how-to-call-a-thread-to-run-on-specific-time-in-java - Kelas wektu lan TimerTask https://www.youtube.com/ nonton?v = CUDgSbaYGx4 - carane ngirim kode ing Github https://github.com/rubenlagus/TelegramBots - perpustakaan telegram lan akeh informasi migunani babagan.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION