JavaRush /Blog Java /Random-MS /Bot Telegram - peringatan melalui webHook di Java atau ka...

Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google! Bahagian 2

Diterbitkan dalam kumpulan
Bahagian kedua projek - di sini adalah pautan ke yang pertama: Dan seterusnya kelas BotState : Agar bot kami memahami perkara yang diharapkan daripadanya pada masa tertentu, contohnya, memadam peringatan, kami perlu entah bagaimana, beritahu bot kami bahawa nombor yang dimasukkan dan dihantar sekarang harus dianggap sebagai ID peringatan daripada senarai dan harus dipadamkan. Oleh itu, selepas mengklik pada butang "Padam" , bot masuk ke keadaan BotState.ENTERNUMBEREVENT , ini ialah kelas Enum yang dicipta khas dengan keadaan 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
}
Dan kini kami dijangka memasukkan nombor - " Masukkan nombor peringatan daripada senarai ." Selepas memasukkan yang mereka akan pergi ke kaedah yang dikehendaki untuk pemprosesan. Berikut ialah suis keadaan kami:
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 dengan ID pengguna dan statusnya. Medan int adminId adalah untuk saya) Seterusnya, logik kaedah handleUpdate akan menyemak jenis mesej ini? Pertanyaan panggilan balik atau hanya teks? Jika ini adalah teks biasa, maka kami pergi ke kaedah handleInputMessage , di mana kami memproses butang menu utama, dan jika ia diklik, maka kami menetapkan keadaan yang dikehendaki, tetapi jika ia tidak diklik dan ini adalah teks yang tidak dikenali, maka kami tetapkan keadaan dari cache, jika ia tidak ada, maka kami tetapkan keadaan permulaan. Kemudian teks itu akan memproses kaedah pemegang dengan keadaan yang kita perlukan. Sekarang kami membentangkan logik kelas MessageHandler , yang bertanggungjawab untuk memproses mesej bergantung pada keadaan 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);
        }
    }
}
dalam kaedah pemegang, kami menyemak status mesej yang kami terima dan menghantarnya kepada pengendali acara - kelas EventHandler. Di sini kami mempunyai dua kelas baharu, MenuService dan EventCash . MenuService – di sini kami mencipta semua menu kami. EventCash - sama seperti BotStateCash, ia akan menyimpan bahagian acara kami selepas input dan apabila input selesai, kami akan menyimpan acara dalam pangkalan data.
@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,>
Nah, begitulah. apabila kami mencipta acara, objek Acara baharu dicipta dalam cache -eventCash.saveEventCash(userId, new Event()); Kemudian kami memasukkan perihalan acara dan menambahkannya pada cache:
Event event = eventCash.getEventMap().get(userId);
event.setDescription(description);
//save to cache
eventCash.saveEventCash(userId, event);
Kemudian masukkan nombor:
Event event = eventCash.getEventMap().get(userId);
event.setDate(date);
//save data to cache
eventCash.saveEventCash(userId, event);
Kelas CallbackQueryHandler adalah serupa dengan MessageHandler , hanya kami memproses mesej panggilan balik di sana. Tidak masuk akal untuk menganalisis sepenuhnya logik bekerja dengan acara - EventHandler , sudah terlalu banyak huruf, jelas dari nama kaedah dan komen dalam kod. Dan saya tidak nampak maksud meletakkannya sepenuhnya dalam teks, terdapat lebih daripada 300 baris. Berikut ialah pautan ke kelas di Github . Perkara yang sama berlaku untuk kelas MenuService , di mana kami mencipta menu kami. Anda boleh membaca tentang mereka secara terperinci di laman web pengeluar perpustakaan telegram - https://github.com/rubenlagus/TelegramBots/blob/master/TelegramBots.wiki/FAQ.md Atau dalam buku rujukan Telegram - https:// tlgrm.ru/docs/bots /api Sekarang kita tinggal dengan bahagian yang paling lazat. Ini ialah kelas untuk mengendalikan mesej 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 – dayakan kerja berjadual dalam Spring. @Scheduled(cron = "0 0 0 * * *") – kami mengkonfigurasi kaedah untuk dijalankan pada 0:00 setiap hari calendar.setTime(new Date()); - tetapkan masa pelayan. Kami mendapat senarai peringatan untuk hari ini, melalui keajaiban aliran dan lambda. Kami pergi melalui senarai yang diterima, menetapkan kalendar masa penghantaran yang betulUserTime dan... Di sinilah saya memutuskan untuk mengelak dan melancarkan proses yang tertunda dalam masa. Kelas Masa di java bertanggungjawab untuk ini . new Timer().schedule(new SimpleTask(sendEvent), calendarUserTime.getTime()); Untuk itu kita perlu membuat utas:
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);
    }
}
dan pelaksanaan TimerTask
public class SimpleTask extends TimerTask {
    private final SendEvent sendEvent;

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

    @Override
    public void run() {
        sendEvent.start();
    }
}
Ya, saya faham dengan baik bahawa anda boleh melalui pangkalan data setiap 20 minit dan menghantar mesej, tetapi saya menulis segala-galanya tentang perkara ini pada awal-awal lagi)) Di sini kita juga menghadapi kesengsaraan Heroku No. 1. Pada pelan percuma, anda diberi kira-kira 550 dino, iaitu kira-kira jam operasi aplikasi anda setiap bulan. Ini tidak mencukupi untuk sebulan penuh operasi aplikasi, tetapi jika anda memautkan kad, anda diberi 450 dino lagi, yang cukup untuk mata anda. Jika anda bimbang tentang kad itu, anda boleh memautkan kad yang kosong, tetapi pastikan ia mengandungi $0.6... Ini adalah jumlah pengesahan, ia hanya perlu berada dalam akaun. Tiada caj tersembunyi melainkan anda menukar sendiri tarif. Pada pelan percuma, terdapat satu lagi masalah kecil, mari kita panggil No. 1a.. Mereka sentiasa but semula pelayan, atau hanya menghantar arahan untuk memulakan semula aplikasi, secara umum ia but semula setiap hari di suatu tempat pada tengah malam waktu Moscow, dan kadang-kadang di lain masa. Daripada ini, semua proses kami dalam ingatan dipadamkan. Untuk menyelesaikan masalah ini, saya menghasilkan jadual EventCash. Sebelum menghantar, acara disimpan dalam jadual berasingan:
EventCashEntity eventCashEntity = EventCashEntity.eventTo(calendarUserTime.getTime(), event.getDescription(), event.getUser().getId());
eventCashDAO.save(eventCashEntity);
Dan selepas menghantar, perkara berikut dipadamkan:
@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 ialah kelas khas untuk mendapatkan konteks dengan cepat:
@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;
    }
}
Untuk menyemak acara yang tidak diproses, saya membuat perkhidmatan khas yang mempunyai kaedah bertanda @PostConstruct - ia berjalan selepas setiap aplikasi bermula. Ia mengambil semua peristiwa yang tidak diproses daripada pangkalan data dan mengembalikannya ke ingatan. Inilah Heroku yang jahat untuk anda!
@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>
Permohonan kami sudah sedia, dan sudah tiba masanya untuk kami mendapatkan alamat Heroku untuk aplikasi dan pangkalan data. Permohonan anda mesti diterbitkan di Github!!! Pergi ke Heroku.com Klik Cipta Apl Baharu , masukkan nama aplikasi anda, pilih Eropah, buat apl . Itu sahaja, tempat permohonan sudah siap. Jika anda mengklik Buka Apl, penyemak imbas akan mengubah hala anda ke alamat aplikasi anda, ini adalah alamat webhook anda - https://your_name.herokuapp.com/ Daftarkannya dalam telegram, dan dalam aplikasi.property s tetapan tukar telegrambot. webHookPath=https: //telegrambotsimpl.herokuapp.com/ ke server.port=5000 anda boleh dipadamkan atau dikomentari. Sekarang mari kita sambungkan pangkalan data. Pergi ke tab Sumber pada Heroku, klik: Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google!  Bahagian 2: - 1 Cari Heroku Postgres di sana , klik pasang : Anda akan dialihkan ke halaman akaun pangkalan data anda. Cari di sana dalam Tetapan/ Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google!  Bahagian 2: - 2 Akan ada semua data yang diperlukan daripada pangkalan data anda. Dalam application.properties semuanya kini sepatutnya seperti ini:
#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
Gantikan data daripada akaun anda dengan anda: Dalam medan jdbc:postgresql:ec2-54-247-158-179.eu-west-1.compute.amazonaws.com:5432/d2um26le5notq?ssl=true&sslmode=require&sslfactory=org. postgresql.ssl .NonValidatingFactory perlu diganti dalam huruf tebal dengan data yang sepadan daripada akaun (Host, Pangkalan Data). Nama pengguna, medan kata laluan tidak sukar untuk diteka. Sekarang kita perlu membuat jadual dalam pangkalan data, saya melakukan ini dari IDEA. Skrip kami akan berguna untuk mencipta pangkalan data. Kami menambah pangkalan data seperti yang ditulis di atas: Kami mengambil Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google!  Bahagian 2: - 3 medan Hos, Pengguna, Kata Laluan, Pangkalan Data daripada akaun. Medan URl ialah medan spring.datasource.url kami sehingga tanda soal. Kami pergi ke tab Lanjutan , ia sepatutnya seperti ini: Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google!  Bahagian 2: - 4 Jika anda melakukan semuanya dengan betul, maka selepas mengklik pada ujian, akan ada tanda semak hijau. Klik OK. Klik kanan pada pangkalan data kami dan pilih Jump to query console . Salin skrip kami di sana dan klik pada laksana . Pangkalan data harus dibuat. 10,000 talian tersedia untuk anda secara percuma! Semuanya sudah sedia untuk Deploy. Pergi ke aplikasi kami pada Heroku dalam bahagian Deploy. Pilih bahagian Github di sana: Bot Telegram - peringatan melalui webHook di Java atau katakan tidak kepada kalendar Google!  Bahagian 2: - 5 Pautkan repositori anda ke Heroku. Sekarang cawangan anda akan kelihatan. Jangan lupa untuk menolak perubahan terbaharu anda ke .properties. Di bawah, pilih cawangan yang akan dimuat turun dan klik Deploy branch . Jika semuanya dilakukan dengan betul, anda akan dimaklumkan bahawa aplikasi telah berjaya digunakan. Jangan lupa untuk mendayakan Automatic deploys from .. Supaya aplikasi anda bermula secara automatik. Dengan cara ini, apabila anda menolak perubahan pada GitHub, Heroku akan memulakan semula aplikasi secara automatik. Berhati-hati tentang perkara ini, buat benang berasingan untuk buli, dan gunakan yang utama hanya untuk aplikasi yang berfungsi. Kini Murah #2! Ini adalah kelemahan terkenal dari pelan percuma untuk Heroku. Jika tiada mesej masuk, aplikasi masuk ke mod siap sedia, dan selepas menerima mesej ia akan mengambil masa yang agak lama untuk bermula, yang tidak menyenangkan. Terdapat penyelesaian mudah untuk ini - https://uptimerobot.com/ Dan tidak, alat ping Google tidak akan membantu, saya tidak tahu dari mana maklumat ini datang, saya telah Google soalan ini, dan selama kira-kira 10 tahun sekarang topik ini tidak berfungsi dengan pasti, jika ia berfungsi sama sekali. Aplikasi ini akan menghantar permintaan HEAD ke alamat yang anda tentukan untuk masa yang anda tetapkan dan, jika ia tidak membalas, hantar mesej melalui e-mel. Ia tidak akan sukar untuk anda memikirkannya, tidak ada butang yang cukup untuk mengelirukan)) Tahniah!! Jika saya tidak terlupa apa-apa dan anda prihatin, maka anda mempunyai aplikasi anda sendiri yang berfungsi secara percuma dan tidak pernah ranap. Peluang untuk buli dan percubaan terbuka di hadapan anda. Walau apa pun, saya sedia menjawab soalan dan menerima sebarang kritikan! Kod: https://github.com/papoff8295/webHookBotForHabr Bahan yang digunakan: https://tlgrm.ru/docs/bots/api - tentang bot. https://en.wikibooks.org/wiki/Java_Persistence - tentang perhubungan dalam pangkalan data. https://stackoverflow.com/questions/11432498/how-to-call-a-thread-to-run-on-specific-time-in-java - Kelas masa dan TimerTask https://www.youtube.com/ tonton?v=CUDgSbaYGx4 – cara menghantar kod pada Github https://github.com/rubenlagus/TelegramBots - perpustakaan telegram dan banyak maklumat berguna mengenainya.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION