...

Пример кейса: реализация интерактивной задачи из Elma в Telegram с помощью бота

Тема в разделе "Примеры сценариев", создана пользователем AnastasiyaIV, 10 июн 2024.

  1. AnastasiyaIV

    AnastasiyaIV Новичок

    Цель - организовать работу внутри процесса так, чтобы при назначении задачи в Elma сотруднику приходило уведомление в Telegram с кнопками задачи. При нажатии одной из кнопок запрос должен уходить обратно в Элму и отрабатывать по сценарию, соответствующему полученным данным.

    Для ознакомления была взята статья (https://community.elma365.com/ru/threads/2198/)
    В реализации запросов к API Telegram очень полезна дока телеги и особенно: https://core.telegram.org/bots/api#getupdates

    Однако в процессе уточнения ТЗ стало ясно, что требуется более узконаправленная реализация - не на модулях системы, с регистрацией веб-хука, а скриптом внутри процесса (подобный способ не описан ни в комьюнити в телеге, ни в темах комьюнити на сайте Элмы).

    Разберёмся по шагам:
    1. Создаём Telegram-бота с помощью BotFather (https://t.me/BotFather) и копируем сгенерированный токен бота - он пригодится для отправки запросов к API Телеграмма.

    [​IMG]
    2. Создаём необходимый по ТЗ бизнес-процесс. Я для теста работоспособности функционала пустила в параллельный шлюз задачу сотруднику и таймер с последующей проверкой наличия открытых задач у этого сотрудника.
    Если до истечения таймера сотрудник в системе отметит задачу как выполненную либо отклонит её, запрос в Телеграмм уже не уйдет - открытых задач нет.

    [​IMG]
    3. Собственно, не имеет большого смысла показывать скрипт поиска открытых задач, как и скрипт их прерывания в процессе, поэтому сразу покажу самое "вкусное" - шаблон скрипта запроса в телегу. На месте переменной ${yourBotToken} должен стоять токен созданного бота, который вам отдал БатяБот. Вместо ${method} функция принимает тип запроса к API. ${userChatId} - id пользователя в тг, которому будут приходить уведомления. Должен определяться динамически, но для теста оставим статику - свой.
    HTML:
    interface Params {
      message_id?:string;
      text: string;
      callback_data?: string,
      reply_markup?: {
        inline_keyboard: { text: string;
        callback_data: string }[][];
      };
    }
    
    async function invoke_bot(data: Params, method: string): Promise<void> {
        let resp = await
            fetch(`https://api.telegram.org/bot${yourBotToken}/${method}?chat_id=${userChatId}`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
            body: JSON.stringify(data),
        })
       .then(async (data) => {
            await data.json();
        })
    }
    4. Функция, которую вызываем в блоке Отправка запроса на схеме:
    HTML:
    async function botInvoke(): Promise<void> {
        const method = 'sendMessage';
            await invoke_bot({
                text: `${fio_str}, у вас есть задача!`,
                reply_markup: {
                    inline_keyboard: [
                        [{ text: `${Context.data.first_btn}`, callback_data: 'get_task' }],
                        [{ text: `${Context.data.second_btn}`, callback_data: 'reject_task' }],
                    ]
                }
        }, method)
    }
    5. Самой большой сложностью оказалось догадаться, как вытянуть ответ из телеги и обработать callback_data из JSON, поскольку в БП в Элме console.log() не отрабатывает и не видно, в каком виде приходят данные (и приходят ли вообще).
    Причиной оказался зарегистрированный ранее веб-хук из модуля, который создавал конфликт.
    Не будьте как я - прежде чем писать подобный кейс на скриптах, удаляйте привязанные к боту веб-хуки! )))

    HTML:
    async function getUpds(): Promise<void> {
        let delete_hook = await fetch(
            `https://api.telegram.org/bot${yourBotToken}/deleteWebhook`
        );
    
      const method = 'getUpdates';
      let get_btn_call = await fetch(
        `https://api.telegram.org/bot${yourBotToken}/${method}?chat_id=${userChatId}`
      );
      const updates = await get_btn_call.json();
      Context.data.str = updates.toString();
    
      Context.data.upds_len = updates.result.length;
      Context.data.upds_call_query = updates.result[updates.result.length - 1].callback_query.data;
    
        const m = 'sendMessage';
    
      if (updates.result.length === 0) {
        await invoke_bot({
            text: `${fio_str}, у вас есть задача!`,
            reply_markup: {
                inline_keyboard: [
                    [{ text: `${Context.data.first_btn}`, callback_data: 'get_task' }],
                    [{ text: `${Context.data.second_btn}`, callback_data: 'reject_task' }],
                ]
            }
        }, m)
      } else if (updates.result[updates.result.length - 1].callback_query.data == 'reject_task') {   
            await invoke_bot({
            text: `Задача отклонена`,
            callback_data: 'get_finish',
        }, m)
    
            await Application.processes._searchTasks()
            .where((f, g) => g.and(
                g.or(
                    f.state.like(ProcessTaskState.inProgress),
                    f.state.like(ProcessInstanceState.wait),
                    f.state.like(ProcessInstanceState.exec),
                ),
            ))
            .size(10000)
            .all()
            .then(async (allTasks: ProcessTaskItem<ProcessContext>[]) => {
                const tasks = allTasks.filter(task => task.data.instance?.__id ===  Context.data.process_id);
                await submitTasks(tasks, reason);
            })
       
        } else if (updates.result[updates.result.length - 1].callback_query.data == 'get_task') {
            await invoke_bot({
            text: `Вы выполнили задачу`,
            callback_data: 'get_finish',
        }, m)
    
            await Application.processes._searchTasks()
            .where((f, g) => g.and(
                g.or(
                    f.state.like(ProcessTaskState.inProgress),
                    f.state.like(ProcessInstanceState.wait),
                    f.state.like(ProcessInstanceState.exec),
                ),
            ))
            .size(10000)
            .all()
            .then(async (allTasks: ProcessTaskItem<ProcessContext>[]) => {
                const tasks = allTasks.filter(task => task.data.instance?.__id ===  Context.data.process_id);
                await submitTasks(tasks, reason);
            })
        }
    }
    В общем-то, за сим всё. После того, как была выявлена причина, и воскрешён бот, всё прекрасно заработало - при нажатии кнопки задача отмечается как выполненная, и система даёт соответствующий отклик. При нажатии второй кнопки в Элму приходит отказ от задачи.
    А дальше можно развлекаться, как вам угодно.

    [​IMG]

    Вложения: