JavaRush /Java блогы /Random-KK /Telegram боты – Java тіліндегі webHook арқылы еске салу н...
Vladimir Popov
Деңгей

Telegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз! 2-бөлім

Топта жарияланған
Жобаның екінші бөлігі – біріншісіне сілтеме: Сонымен BotState класы : Біздің бот белгілі бір уақытта одан не күтілетінін түсінуі үшін, мысалы, еске салғышты жою, біз Қандай да бір жолмен біздің ботқа нөмірлер енді енгізілген және жіберілген тізімнен еске салғыш идентификаторы ретінде қарастырылуы керек және жойылуы керек екенін білуге ​​​​мүмкіндік беріңіз. Сондықтан, «Жою» түймесін басқаннан кейін бот BotState.ENTERNUMBEREVENT күйіне өтеді , бұл бот күйлері бар арнайы жасалған Enum сыныбы.
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
}
Енді біз сандарды енгізуді күтеміз - « Тізімнен еске салғыш нөмірін енгізіңіз ». Енгізгеннен кейін олар өңдеудің қажетті әдісіне өтеді. Міне, біздің күй ауыстырғышымыз:
public class BotStateCash {
    private final Map<long, botstate=""> botStateMap = new HashMap<>();

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

</long,>
Пайдаланушы идентификаторы және оның күйі бар кәдімгі карта. int adminId өрісі маған арналған) Әрі қарай, handleUpdate әдісінің логикасы бұл қандай хабарлама екенін тексереді? Кері қоңырау немесе жай мәтін бе? Егер бұл кәдімгі мәтін болса, онда біз handleInputMessage әдісіне өтеміз, онда біз негізгі мәзір түймелерін өңдейміз және егер олар басылса, біз қажетті күйді орнатамыз, бірақ егер олар басылмаса және бұл бейтаныс мәтін болса, онда біз күйді кэштен орнатыңыз, егер ол жоқ болса, біз бастапқы күйді орнатамыз. Содан кейін мәтін бізге қажетті күймен дескриптор әдісін өңдеуге кіріседі. Енді біз бот күйіне байланысты хабарламаларды өңдеуге жауап беретін MessageHandler класының логикасын ұсынамыз :
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);
        }
    }
}
дескриптор әдісінде біз алған хабардың күйін тексереміз және оны оқиға өңдеушісіне - EventHandler класына жібереміз. Мұнда бізде екі жаңа сынып бар, MenuService және EventCash . MenuService – мұнда біз барлық мәзірлерімізді жасаймыз. EventCash - BotStateCash сияқты , ол енгізілгеннен кейін оқиғаның бөліктерін сақтайды және енгізу аяқталған кезде оқиғаны дерекқорда сақтаймыз.
@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,>
Ал, бұл. оқиғаны жасағанда, кэште жаңа Оқиға нысаны жасалады -eventCash.saveEventCash(userId, new Event()); Содан кейін оқиғаның сипаттамасын енгізіп, оны кэшке қосамыз:
Event event = eventCash.getEventMap().get(userId);
event.setDescription(description);
//save to cache
eventCash.saveEventCash(userId, event);
Содан кейін нөмірді енгізіңіз:
Event event = eventCash.getEventMap().get(userId);
event.setDate(date);
//save data to cache
eventCash.saveEventCash(userId, event);
CallbackQueryHandler сыныбы MessageHandler сыныбына ұқсас , тек біз сол жерде кері шақыру туралы хабарларды өңдейміз. Оқиғалармен жұмыс істеу логикасын толығымен талдаудың мағынасы жоқ - EventHandler , қазірдің өзінде тым көп әріптер бар, бұл codeтағы әдістер мен түсініктемелердің атауларынан анық. Мен оны толық мәтінде көрсетудің мәнін көрмеймін, 300-ден астам жол бар. Мұнда Github сайтындағы сыныпқа сілтеме берілген . Дәл солай біз мәзірлерімізді жасайтын MenuService сыныбына да қатысты . Сіз олар туралы егжей-тегжейлі телеграм кітапханасы өндірушісінің веб-сайтында оқи аласыз - https://github.com/rubenlagus/TelegramBots/blob/master/TelegramBots.wiki/FAQ.md Немесе Telegram анықтамалығында - https:// tlgrm.ru/docs/bots /api Енді бізде ең дәмді бөлік қалды. Бұл 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 – көктемде жоспарланған жұмысты қосыңыз. @Scheduled(cron = "0 0 0 * * *") – біз әдісті күн сайын 0:00-де іске қосу үшін конфигурациялаймыз calendar.setTime(жаңа Күн()); - server уақытын орнату. Біз ағындар мен лямбда сиқыры арқылы бүгінгі күнге арналған еске салғыштар тізімін аламыз. Біз алынған тізімді қарап шығамыз, дұрыс жіберу уақытының күнтізбесін орнатамызUserTime және... Міне, мен уақытында кешіктірілген процестерді айналып өтуді және іске қосуды шештім. Бұған java-дағы Time класы жауап береді . new Timer().schedule(жаңа SimpleTask(sendEvent), calendarUserTime.getTime()); Ол үшін бізге жіп жасау керек:
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);
    }
}
және TimerTask орындалуы
public class SimpleTask extends TimerTask {
    private final SendEvent sendEvent;

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

    @Override
    public void run() {
        sendEvent.start();
    }
}
Иә, әр 20 minutes сайын базадан өтіп, хабарлама жіберуге болатынын жақсы түсінемін, бірақ мен бұл туралы бәрін басында жаздым)) Бұл жерде де No1 Герокудың қасіретін кездестіреміз. Tagін жоспарда сізге шамамен 550 дино беріледі, бұл сіздің өтінішіңіздің айына жұмыс істейтін сағаттары сияқты. Бұл қосымшаның толық айы жұмыс істеуі үшін жеткіліксіз, бірақ егер сіз картаны байланыстырсаңыз, сізге тағы 450 дино беріледі, бұл сіздің көзіңізге жеткілікті. Егер сіз картаға алаңдасаңыз, бос картаны байланыстыра аласыз, бірақ оның құрамында $0,6 бар екеніне көз жеткізіңіз... Бұл растау сомасы, ол тек шотта болуы керек. Тарифті өзіңіз өзгертпесеңіз, жасырын төлемдер болмайды. Tagін жоспарда тағы бір кішігірім мәселе бар, оны №1а деп атаймыз.. Олар үнемі serverлерді қайта жүктейді немесе жай ғана қолданбаны қайта іске қосу пәрменін жібереді, жалпы ол күн сайын Мәскеу уақытымен түн ортасында бір жерде қайта жүктеледі, ал кейде басқа уақытта. Осыдан біздің жадтағы барлық процестер жойылады. Бұл мәселені шешу үшін мен EventCash кестесін ойлап таптым. Жіберу алдында оқиғалар бөлек кестеге сақталады:
EventCashEntity eventCashEntity = EventCashEntity.eventTo(calendarUserTime.getTime(), event.getDescription(), event.getUser().getId());
eventCashDAO.save(eventCashEntity);
Жібергеннен кейін мыналар жойылады:
@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 контекстті жылдам алуға арналған арнайы сынып:
@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;
    }
}
Өңделмеген оқиғаларды тексеру үшін мен @PostConstruct деп белгіленген әдісі бар арнайы қызметті жасадым - ол әрбір қолданба іске қосылғаннан кейін жұмыс істейді. Ол барлық өңделмеген оқиғаларды дерекқордан алып, оларды жадқа қайтарады. Міне, сіз үшін жағымсыз Героку!
@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>
Біздің қосымшамыз дайын және бізге қосымша мен дерекқорға арналған Heroku мекенжайын алу уақыты келді. Сіздің өтініміңіз Github сайтында жариялануы керек!!! Heroku.com сайтына өтіңіз Жаңа қолданба жасау түймесін басыңыз , қолданба атын енгізіңіз, Еуропаны таңдаңыз, қолданба жасаңыз . Болды, өтініш беретін жер дайын. «Қолданбаны ашу» түймесін бассаңыз , браузер сізді қолданбаңыздың мекенжайына қайта бағыттайды, бұл сіздің вебхук мекенжайыңыз - https://your_name.herokuapp.com/ Оны telegram-да тіркеңіз және application.propertie параметрлерінде telegrambot-ты өзгертіңіз . webHookPath=https: //telegrambotsimpl.herokuapp.com/ server.port =5000 файлын жоюға немесе түсініктеме беруге болады. Енді мәліметтер базасын байланыстырайық. Heroku сайтындағы Ресурстар қойындысына өтіңіз , басыңыз: Онда Heroku PostgresTelegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз!  2-бөлім: - 1 табыңыз , орнату түймесін басыңыз : Сіз дерекқор тіркелгісінің бетіне қайта бағытталасыз. Оны Параметрлерден табыңыз/ Дерекқордан барлық қажетті деректер болады. application.properties ішінде бәрі енді келесідей болуы керек: Telegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз!  2-бөлім: - 2
#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
Тіркелгіңіздегі деректерді өзіңіздікімен ауыстырыңыз: jdbc:postgresql:ec2-54-247-158-179.eu-west-1.compute.amazonaws.com:5432/d2um26le5notq?ssl=true&sslmode=requitory&sslf өрісінде. postgresql.ssl .NonValidatingFactory қалын шрифтпен тіркелгіден (Хост, Дерекқор) сәйкес деректермен ауыстырылуы керек.Пайдаланушы аты, құпия сөз өрістерін болжау қиын емес. Енді мәліметтер базасында кестелер жасау керек, мен мұны IDEA-дан жасадым. Біздің сценарий дерекқорды құру үшін пайдалы болады. Дерекқорды жоғарыда жазылғандай қосамыз: Тіркелгіден Telegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз!  2-бөлім: - 3 Хост , Пайдаланушы, Құпия сөз, Деректер қоры өрісін аламыз. URl өрісі сұрақ белгісіне дейінгі біздің spring.datasource.url өрісіміз болып табылады. Біз «Қосымша» қойындысына барамыз , ол келесідей болуы керек: Telegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз!  2-бөлім: - 4 Егер сіз бәрін дұрыс жасасаңыз, сынақты басқаннан кейін жасыл құсбелгі пайда болады. OK түймесін басыңыз. Біздің дерекқорды тінтуірдің оң жақ түймешігімен басып, Сұрау консоліне өту пәрменін таңдаңыз . Біздің сценарийді сол жерге көшіріп, орындау түймесін басыңыз . Деректер базасы құрылуы керек. 10 000 жол сізге тегін! Барлығы орналастыруға дайын. Орналастыру бөліміндегі Heroku қолданбасына өтіңіз. Онда Github бөлімін таңдаңыз: Telegram боты – Java тіліндегі webHook арқылы еске салу немесе Google күнтізбесіне «жоқ» деңіз!  2-бөлім: - 5 Репозиторийді Heroku-ге байланыстырыңыз. Енді сіздің фorалдарыңыз көрінетін болады. Соңғы өзгертулерді .properties жүйесіне енгізуді ұмытпаңыз. Төменде жүктелетін фorалды таңдап, Бөлімшені орналастыру түймесін басыңыз . Егер бәрі дұрыс орындалса, қолданбаның сәтті орналастырылғаны туралы хабарланады. Қолданбаңыз автоматты түрде іске қосылуы үшін .. ішінен Автоматты орналастыруды қосуды ұмытпаңыз . Айтпақшы, GitHub-қа өзгертулерді басқанда, Heroku қолданбаны автоматты түрде қайта іске қосады. Бұл туралы абай болыңыз, қорқыту үшін бөлек ағын жасаңыз және негізгісін тек жұмыс қолданбасы үшін пайдаланыңыз. Енді арзан №2! Бұл Heroku үшін тегін жоспардың белгілі кемшілігі. Кіріс хабарламалар болмаса, қолданба күту режиміне өтеді және хабарлама алғаннан кейін оны бастау үшін біршама уақыт қажет, бұл жағымды емес. Бұл үшін қарапайым шешім бар - https://uptimerobot.com/ Жоқ, Google пинг гаджеттері көмектеспейді, мен бұл ақпараттың қайдан келгенін де білмеймін, мен бұл сұрақты Google-да іздедім және шамамен 10 жыл бойы бұл тақырып мүлдем жұмыс істемейді. Бұл қолданба сіз орнатқан уақыт үшін сіз көрсеткен мекенжайға HEAD сұрауларын жібереді және ол жауап бермесе, электрондық пошта арқылы хабарлама жібереді. Түсіну сізге қиын болмайды, шатастыру үшін түймелер жеткіліксіз)) Құттықтаймыз!! Егер мен ештеңені ұмытпаған болсам және сіз мұқият болсаңыз, онда сізде тегін жұмыс істейтін және ешқашан бұзылмайтын жеке қолданбаңыз бар. Сіздің алдыңызда қорқыту мен эксперимент жасау мүмкіндігі ашылады. Қалай болғанда да сұрақтарға жауап беруге және кез келген сынды қабылдауға дайынмын! Код: https://github.com/papoff8295/webHookBotForHabr Пайдаланылған материалдар: https://tlgrm.ru/docs/bots/api - боттар туралы. https://en.wikibooks.org/wiki/Java_Persistence - дерекқорлардағы қатынастар туралы. https://stackoverflow.com/questions/11432498/how-to-call-a-thread-to-run-on-specific-time-in-java - Уақыт сыныбы және TimerTask https://www.youtube.com/ watch?v=CUDgSbaYGx4 – Github сайтында codeты қалай орналастыруға болады https://github.com/rubenlagus/TelegramBots - телеграм кітапханасы және ол туралы көптеген пайдалы ақпарат.
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION