keshanchik | Телеграм-бот с нуля и капелька литкода

  • Гость, поучаствуй в создании лаунчера! Напиши те скрипты, которые считаешь базой для удобной игры. Принять участие!
  • МАЙСКАЯ ЛОТЕРЕЯ
    С 1 по 11 мая проходит майская лотерея: первое место — 5000 рублей или кошелёк Steam, второе и третье — Telegram Premium на год и полгода соответственно.
    За каждый отыгранный час игрок получает один билет, увеличивая шансы на победу и гарантированный доступ к альфа и бета-тестам лаунчера.

keshanchik

Новичок
Автор темы
17
689
Привет всем. Частично в олимпиадном движении с 14 лет (C++), тактично сбежал от ООП плюсов на JS, пока не жалею и жалеть никогда не буду. Мне 16, в этом топике буду публиковать этапы развития и в целом код моего мини-проекта, который создаётся исключительно для опыта - бот-органазер в телеграме, подробнее будет вторым постом Ну и будет моё развитие по пути создания проекта, иногда буду закидывать посты с понравившимися задачками с литкода, которые я смогу решить.
Можно задавать вопросы.
 

keshanchik

Новичок
Автор темы
17
689
Сам бот не особо сложен в своём функционале. Пользователю будет предлагаться сделать заметку, либо сделать напоминания, уведомления о каких-либо событиях, сделать свой график дня, о котором бот будет напоминать ежечасно/через заданное время, в зависимости от поставленного графика дня пользователя.
 

keshanchik

Новичок
Автор темы
17
689
Сегодня провёл все первые обязательные и начальные действия для телеграм-бота, подключил библиотеку и ввёл токен, также написал в нескольких строках приём сообщений от пользователей. Уже пару месяцев назад разбирал процесс создания бота и самые банальные способы работы с ним. Научился *тут*


1691586254224.png

Именно в таком формате приходит информация о пользователе и его сообщение боту для дальнейшей работы. Соответственно, пример работы:

JavaScript:
bot.on('message', msg => {
    console.log(msg)
    const text = msg.text
    const chatId = msg.chat.id
    const nameFirst = msg.chat.first_name
    const username = msg.username
    if (text === '/start') {
        bot.sendMessage(chatId, 'Привет,' + nameFirst)
    }
})
/*
Бот принимает сообщение и информацию о пользователе в объект 'msg', вторая строчка используется только
для первоначального разбора объекта и его параметров, дальше идёт присваивание параметров
к константам, через которые мы и будем работать.
На седьмой мы проверяем, равна ли константа, имеющая на себе текст пользователя и нужная нам команда.
Теперь если пользователь вводит '/start', бот выводит ему "Привет," и его имя, указанное в телеграме.
*/
В дальнейшем мы добавим возможность использовать кнопки для вызова команд и напишем информативное первое сообщение для пользователя, дабы объяснить саму работу бота.
 

keshanchik

Новичок
Автор темы
17
689
Сегодня ничего не будет, пока разбираюсь в нужном планировщике заданий на английском, на котором и будет держаться весь код.
 
  • Нравится
Реакции: мой дом роддом

keshanchik

Новичок
Автор темы
17
689
Спустя около двух часов суммарно на гитхабе, путём проб и экспериментов я разобрался, как работает эта херня с телеграмм ботом. Я решил пропустить всю визуальную часть и сначала сделать логику бота.

JavaScript:
bot.onText(/\/add_task(.+)/, (msg, match) => {
const chatId = msg.chat.id
    const taskText = match[1]
        const job = schedule.scheduleJob('5 * * * *', function() {
            bot.sendMessage(chatId, `Уведомление о задаче: \n\n Вы задавали задачу: ${taskText}`)
        })
     bot.sendMessage(chatId, 'Задача добавлена.')
    })
/*
Здесь мы принимаем команду /add_task более лёгким способом, добавляем регулярное выражение (.+).
match в этом случае - массив, который принимает текст задачи, который достаётся из регулярного выражения.
Выше я добавил 'node-schedule', планировщик заданий. Дальше идёт то, что я не объясню, но попытаюсь.
Job - EventEmitter объект, после которого идёт некое условия от времени. Это значит, что благодаря Job
можно при выполнении этого условия запустить код из любой его точки. В скобках после 'scheduleJob' должно идти
шесть звёдочек, каждая из которых обозначает секунда-минута-час-день-месяц-год в соответствующей последовательности.
Лучше посмотреть на гитхабе таблицу.
В моём коде я сделал так, чтобы каждые пять минут каждого часа выполнялась функция.
В нашем случае - это вывод сообщения с тем заданием, который задаёт пользователь в массив.
Текст задачи мы передаём именно в match[1](а не в match[0] или match), чтобы регулятор выражения брал текст задачи
после команды.
Поправьте, пожалуйста, если что-то из вышесказанного я назвал неправильно.
*/

1691825252510.png
Код работает так, когда я задал время '26 * * * *'
Не знаю, правильно ли я делаю, что кидаю сюда код, но я сомневаюсь, что тут кто-то этим будет пользоваться, а кому интересно - пригодится.

Это самый первый вариант кода, который у меня получился. Пока что время задаю я, но это быстро решается и пока что я хочу разобраться, как сделать так, чтобы пользователь сначала вводил команду, потом вводил задачу, а затем устанавливал своё время. В трёх сообщениях. Но идея есть и чуть позже я буду пробовать. Делаю я это для того, чтобы пользователь мог пользоваться кнопками в боте. По-моему мнению, так гораздо удобнее, чем будет, если пользователь будет прописывать команду, задачу и время в одной строчке. Это будет делаться как минимум для лучшего понимания и меньшей затраты времени на то, чтобы разобраться в функционале, а как максимум - это легче реализовать.

Кнопка пока выглядит так:
1691825475910.png

P.S.: Думаю, что за сегодня-завтра напишу, а послезавтра кину пост.
 

keshanchik

Новичок
Автор темы
17
689
Там всё сложнее, чем я думал и у меня неправильно используется время, заданное пользователем в планировщике задач, поэтому завтра буду переписывать и как получится - скину результат. :(
 
  • Плачу
Реакции: england.

keshanchik

Новичок
Автор темы
17
689
1692026311831.png

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

Кстати, в планировщике можно ставить ещё и "правила". Например, ставить часовой пояс, задавать дни недели, когда должно выдаваться уведомление и т.д. Возможностей куча, обязательно попробую и сделаю всё.
Но, когда я присваивал к правилу минут эту переменную, которая приходит из диалога - уведомления приходили ровно на начало каждого часа, а когда снова записывал туда вручную минуты - всё работало прекрасно.
 

keshanchik

Новичок
Автор темы
17
689
Заработало.

Короче, не без помощи chatGPT я во всём разобрался и теперь по порядку.
JavaScript:
let task = ''
let times = ''
bot.setMyCommands([
    {command: '/add_task', description: 'Добавить задачу' }
])
bot.onText(/\/start/, msg => {
    const chatId = msg.chat.id
    bot.sendMessage(chatId, 'Добро пожаловать! \n\n Данный бот поможет вам в организации дня')

bot.onText(/\/add_task/, (msg) => {
const chatId = msg.chat.id
    bot.sendMessage(chatId, 'Введите задачу')
     bot.onText(/(.+)/,  (msg, match) => {
        const chatId = msg.chat.id;

        const receivedMessage = match[1];
        if (!task) {
            task = receivedMessage;
           bot.sendMessage(chatId, `Задача "${task}" добавлена.`);
        }
bot.sendMessage(chatId, 'Введите время выполнения задачи')
        bot.onText(/(.+)/, (msg, time) =>{
            const chatId = msg.chat.id
            const minutes = time[1]
            if (!times) {
                times = minutes;
                 bot.sendMessage(chatId, `Время выбрано: ${minutes}`);
            }
        })
})
    const job = schedule.scheduleJob(`${parseInt(times)} * * * *`, function() {
        bot.sendMessage(chatId, `Уведомление о задаче: \n\n Вы задавали задачу: ${task}`)
    })

})

Это первый говнокод, который у меня получился и в итоге не работал. В целом, он почти не отличается от моего нынешнего, не считая одну тему, о которой скажу позже. Из говна здесь то, что я пошёл трудным, тупым и при этом нерабочим путём. Чтобы параметры 'times' и 'task' сделать глобальными и использовать их в других функциях я просто пошёл через 'if'. Если кому интересно, как это работает - напишите. Следующей ошибкой было то, что я пытался после перевода 'times' в глобальную переменную (после её ввода в чате бота в телеграме пользователем) сразу пытался вставить это время в планировщик задач, соответственно в параметр, отвечающий за время. "const job = schedule.scheduleJob(`${parseInt(times)} * * * *`". Однако, кроме этого я делал и через new Data. Это ещё один способ указать время, просто он пишется на отдельной строке и в строку, которая выше, вместо тех одинарных скобок вставляется заданная переменная. Пример:
JavaScript:
const data = new Data('2023, 7, 16, 13, 32')
const job = schedule.scheduleJob(data, function() {
    bot.sendMessage(chatId, 'Вы начали писать пост.')
})
В месяце 7, потому что месяца в JavaScript идут от 0 до 11. Я пытался сделать так:
JavaScript:
const data = new Data('2023, 7, 16, ${times}')
const job = schedule.scheduleJob(data, function() {
    bot.sendMessage(chatId, 'Это уведомление отправлено в минуту ${times}')
})
Однако, с этим у меня тоже не сработало. Также я пытался сделать через правила, но там та же история.

Следующей совершенно незначительной ошибкой было то, что я сообщения принимал через bot.onText (задачу и время), хотя это было совершенно необязательно, потому что при вводе /add_task и последующем двойном bot.on, который принимает любые сообщения, программа ждала как раз таки два сообщения от пользователя, откуда можно было брать задачу и время.

Теперь перейдём к коду, который у меня получился в итоге благодаря нейросети.

JavaScript:
const userTasks = {};

bot.onText(/\/add_task/, (msg) => {
    const chatId = msg.chat.id;
    bot.sendMessage(chatId, 'Введите задачу:');
    bot.once('message', (addedMsg) => {
        const task = addedMsg.text;
        bot.sendMessage(chatId, `Задача "${task}" добавлена.`);

        bot.sendMessage(chatId, 'Введите время уведомления (в формате ЧЧ:ММ):');
        bot.once('message', (timeMsg) => {
            const time = timeMsg.text;
            userTasks[chatId] = { task, time };
            scheduleTask(chatId, task, time);
            bot.sendMessage(chatId, `Время уведомления для задачи "${task}" установлено на ${time}.`);
        });
    });
});

function scheduleTask(chatId, task, time) {
    const [hours, minutes] = time.split(':');
    const notificationTime = new Date();
    notificationTime.setHours(hours);
    notificationTime.setMinutes(minutes);

    schedule.scheduleJob(notificationTime, () => {
        bot.sendMessage(chatId, `Время начать задачу "${task}"!`);
        delete userTasks[chatId];
    });
}
Можно не смотреть на то, что все ответные сообщения для пользователя написано более логично, как оно и должно быть. После просьбы разъяснить, как это можно сделать он кинул мне готовый код :/
Здесь самое главное отличие - создание функции scheduleTask, которая:
  1. Создаёт массив, состоящий из заданного часа и заданной минуты пользователем. Пользователь задаёт, к примеру, '13:46' одним сообщение, эта строка передаётся в переменную 'time'. Новое, что я узнал это то, что строку можно разделить, буквально, на две+ части и оно в таком виде передаётся в массив. В нашем случае строка разделяется знаком ':' через оператор 'split'. Однако, туда можно поставить что угодно.
  2. Создаёт дату, как я показывал выше. Однако, вместо 'date' в моём случае - создаётся notificationTime.
  3. С помощью 'setHours' и 'setMinutes' передаёт из массива в первой строке функции час и минуту, заданную пользователем. О них я не знал, кстати. (я про setHours и setMinutes)
  4. С помощью знакомого нам метода мы добавляем планировщик, вставляем вместо даты 'notificationTime'.
  5. В функции мы выводим само уведомление и удаляем задачу. После удаления уведомление не придёт на следующий день в это время.

    А теперь главный для меня вопрос - почему также не работало без функции и 'setHours' 'setMinutes' и т.д.?
    . . .
    А я сам не знаю. Скорее всего, это добавлено специально для таких нужд, как ввод времени пользователем.
Также бот вместо bot.on вставил bot.once. Компилятор сам по себе его нормально не переваривает, подсвечивает предупреждением, но оно всё равно работает. Вот комментарий chatGPT по bot.once прямым текстом.
bot.once используется для создания "одноразового слушателя" в Discord.js. Когда указанное событие происходит впервые, данный слушатель выполнит указанный код и затем удалится. Это полезно, когда нужно выполнить какое-то действие только один раз, например, при запуске бота или при подключении к серверу Discord.
И эта штука реально решила мою проблему с прошлым кодом, которая, как мне казалась, происходила из-за неправильной расставки зон видимости.






















Слева - bot.on, справа - bot.once. Думаю, разница на лицо. 'node-telegram-bot-api' после ввода пользователем сообщения возвращает выполнение функции к первой строке для присваивания переменных и заново выполняет. С bot.once каждая строка выполняется лишь один раз. Скорее всего, я объяснил неправильно, но я так это понимаю.

Ну и, вроде как, последнее отличие - вместо двух переменных task и time в этом коде они заменяются одним массивом.

Результат на сегодняшний день на правом скриншоте.
 
  • Нравится
Реакции: Agent47

keshanchik

Новичок
Автор темы
17
689
Чтобы параметры 'times' и 'task' сделать глобальными и использовать их в других функциях я просто пошёл через 'if'.
Неправильно сказанул. 'times' и 'task' и так являются глобальными переменными, но если они получают какое-либо значение в какой-либо области видимости - в любой другой это значение остаётся тем, которое задано в глобальном области.
 

keshanchik

Новичок
Автор темы
17
689
Временно в тупике. Одна из важнейших фич, которая работала пару дней, перестала работать без каких-либо на то причин. Говорю я про часовой пояс, который выбирался автоматически под пользователя самой программой. Код функции не переписывался, уже работал над кое-чем серьёзным, а когда решил вернуться, потому что то, что должны было работать там, не работало. Хз в чём может быть проблема, пытался решить часа три, но ничего не заработало.

Решил пока оставить эту тему. Работаю над выбором определённого дня и выбором дня недели, в который должно приходить уведомление. Также по команде /cancel теперь можно отменить задачу.
 

keshanchik

Новичок
Автор темы
17
689
Upd: теперь бот работает, при этом с часовым поясом и возможностью выбора дня недели в отдельном режиме органайзера. Как оптимизирую код через классы, буду делать возможность добавления сразу несколько задач одновременно

1693480549559.png
Была создана клавиатура для выбора дня недели.

Кстати можно на это сообщение поставить класс
 
  • Нравится
  • Нравится
Реакции: NERAMIL и KISLOTNAYA