Боты в Телеграмме для написания диплома

Привет, меня зовут Илья, я сейчас сдаю вступительные экзамены в магистратуру. Столкнулся при поступлении с проблемой, что результаты экзаменов в рейтинговом списке появляются не сразу, а постоянно его открывать и находить себя на странице — после раза двадцатого надоело. После исследования devtools я захотел написать приложение для отслеживания изменений рейтинга, а уведомления отправлять в телеграм. А Rust был выбран по простой причине — он мне понравился, ну и есть удобные штуки всякие.

Не судите строго, мой первый опыт написания статьи (и бота). Также она не претендует на звание полноценного туториала по разработке телеграм ботов на Rust, но я старался. И тем более это не туториал по самому языку.

Реализация

Для запросов использовал reqwest с включенной фичей json для десериализации, для поддержки async/await — tokio, для работы с SQLite — rusqlite.

Пока что есть возможность смотреть рейтинг только для магистратуры ИТМО

Схема работы

Получаем список программ и сохраняем в базе, далее запускаем цикл, на каждом шаге запрашиваем и обрабатываем обновления с телеграма, и примерно каждые 10 минут обновляем рейтинг с сайта.

ДИПЛОМ ЗА СУТКИ В CHATGPT? ЭТО РЕАЛЬНО!

Получение рейтинга

Начал я с получения данных о рейтинге с abitlk.itmo.ru . Здесь получаем данные, функция find_score ищет в них нужную запись, в случае если не находит, возвращает ошибку, которая будет обработана, и будет отправлено сообщение, что ничего не найдено

pub async fn get_rating_competition( degree: str, case_number: Result, Box> < let rating_response: RatingResponse = reqwest::get(format!( «//rating//budget?program_id=» )) .await? .json() .await?; match find_score(rating_response, case_number) < None =>Err(Box::from(«no matching competition»)), competition => Ok(competition), > >

Поиск записи в данных. Если в поле ok вернулось false , сразу возвращаем None . Иначе достаем массив всех записей из response.result.general_competition , и находим в нем позицию по номеру дела из личного кабинета (у меня вида Мх-хххх-2022 )

fn find_score(response: RatingResponse, case_number: Option < if !response.ok < return None; >response .result .general_competition .iter() .find(|c| -> bool < if let Some(c) = c == case_number >else < false >>) .cloned() >

Структура RatingResponse выглядит так. Тут я задал только нужные поля, остальные, которые придут в ответе, будут проигнорированы при парсинге.

pub struct Response < pub ok: bool, pub message: String, pub result: T, >pub type RatingResponse = Response; pub struct RatingResult < pub general_competition: Vec, > pub struct Competition < pub position: i32, pub priority: i32, pub total_scores: f64, pub case_number: Option, pub exam_scores: Option, >

Еще по теме:  Как синхронизировать контакты Телеграмм с телефоном

Также каждая структура наследует serde::Deserialize для генерации нужных методов, которые будут потом преобразовывать сырые данные (в данном случае json) в структуру:

Rey Way Blog | Как написать диплом/реферат/курсовую с помощью ChatGPT ПО ШАГАМ


#[derive(Deserialize)] pub struct Response <>

Получение команд от пользователей

Для получения сообщений используется эндпойнт https://api.telegram.org/botTOKEN/getUpdates?offset=x для получения обновлений, где есть и новые сообщения. offset необходим для пометки обновлений как прочитанных, чтобы они больше не возвращались. Функция для их получения:

async fn get_updates(offset: i32) -> Result> < let params = [(«offset», let url = reqwest::Url::parse_with_params(/getUpdates»), let response = reqwest::get(url).await?; if !response.status().is_success() < let error: ErrorResponse = response.json().await?; let text = format!( «cannot get updatesnerror: `<>`», error.description.unwrap_or_default() ); send_message( return Err(Box::from(«cannot get updates»)); > Ok(response.json().await?) >

Полученные данные обрабатываем. Здесь во-первых сохраняется максимальный update_id для последующего использования в качестве offset , далее в MessageRequest::from(text) парсится текст сообщения, если это известная команда, она далее обрабатывается, если нет, отправляется сообщение, что это не команда.

pub async fn handle_updates(db: Result> < let data = get_updates(offset).await?; let mut max_update_id = 0; for update in data.result < if update.update_id >max_update_id < max_update_id = update.update_id; >if let Some(message) = update.message < if let Some(text) = message.text < let chat_id = message.from.id.to_string(); match MessageRequest::from(text) < Some(request) =>handle_message_request(db, request, send_message(messages::unknown_message, > > > Ok(max_update_id + 1) >

Парсится так (функция from ). Текст разбивается по пробелам, далее в случае односложных команд просто возвращается значение перечисления, а в случае /watch из значений массива создается объект для их передачи. При неправильной команде возвращается её имя, далее оно будет использовано для отправки синтаксиса этой команды.

impl MessageRequest < pub fn from(text: String) ->Option < let text: Vec= text.split(‘ ‘).map(|w| w.to_string()).collect(); if text.is_empty() < return None; >let command = text[0].as_str(); match command < «/watch» | «/unwatch» => < let incorrect_command = Some(Self::IncorrectCommand(command.to_string())); if text.len() < 5 < if text.len() == 2 text[1] == «all» < return Some(Self::UnwatchAll); >return incorrect_command; > // waiting for let-chain if let Some(watch) = Watch::new(«itmo», text[3], if watch.degree == Degree::Master < return match command < «/watch» =>Some(Self::Watch(watch)), «/unwatch» => Some(Self::Unwatch(watch)), _ => incorrect_command, >; > > incorrect_command > «/about» => Some(Self::About), «/help» => Some(Self::Help), «/start» => Some(Self::Start), _ => None, > > >

Структура MessageRequest и вспомогательные Degree и Watch :

Модели запроса пользователя

pub enum Degree < Bachelor, Master, Postgraduate, >pub struct Watch < pub uni: String, pub degree: Degree, pub program_id: String, pub case_number: String, >pub enum MessageRequest

MessageRequest обрабатывается так. Для простых команд отправляем соответствующие сообщения, команды /watch и /unwatch обрабатываем (сама функция обработки чуть ниже).

async fn handle_message_request( db: str, ) -> Result < match request < MessageRequest::Watch(args) => < let result = handle_competition(db, chat_id, args.case_number, if let Err(_) = result < send_message(messages::rating_not_found, chat_id).await?; >> MessageRequest::Unwatch(args) => < db.delete_competition( args.program_id, send_message(messages::done, chat_id).await?; >MessageRequest::UnwatchAll => < db.delete_competition_by_user(chat_id)?; send_message(messages::done, chat_id).await?; >MessageRequest::IncorrectCommand(command) => < send_incorrect_command_message(MessageRequest::Help => send_message(messages::help, chat_id).await?, MessageRequest::Start => send_message(messages::start, chat_id).await?, MessageRequest::About => send_message(messages::about, chat_id).await?, >; Ok(()) >

Код обработки довольно скучный, но довольно большой. Сначала получается позиция, достается уже ранее запрошенная позиция из базы, если они не совпадают, то отсылается сообщение пользователю, и значение в базе обновляется. Если не равны, то сообщение отсылается только в случае, если эта функция была вызвана после обработки команды от пользователя, а не на этапе регулярной проверки обновлений. За это отвечает параметр is_user_request :

Еще по теме:  Бот которые вскрывают переписку в Телеграмме

pub async fn handle_competition( db: str, degree: str, program_id: Result> < let competition = get_rating_competition(db, degree, program_id, case_number).await?; match db.select_competition(chat_id, case_number, degree, program_id) < Ok(old_competition) => < if let Some(competition) = competition < let program = db.select_program(«itmo», program_id)?; let program_name = if let Some(program) = program < program.title_ru >else < «Названия нет».to_string() >; let mut should_send_message = false; // update if competition is old (competition != old_competition) // insert if is new (when old == None, on first user request) if let Some(old_competition) = old_competition < if competition != old_competition < db.update_competition( should_send_message = true; >> else < db.insert_competition( >// send if it’s user request or record in db was updated if is_user_request || should_send_message < send_competition_message(program_name).await?; >> > Err(e) => < eprintln!(«cannot select competition: «) > >; Ok(()) >

Отправка сообщения выглядит так. Экранируем некоторые символы, отправляем запрос, проверяем результат.

pub async fn send_message(text: str) -> Result> < let text = \-«).replace(‘.’, «\.»); let params = [ («chat_id», chat_id), («text», text), («parse_mode», «MarkdownV2″), ]; let url = reqwest::Url::parse_with_params(/sendMessage»), let response = reqwest::get(url).await?; if !response.status().is_success() < let error: ErrorResponse = response.json().await?; let msg = «Cannot send message request»; if let Some(description) = error.description < eprintln!(«: «); > return Err(Box::from(msg)); > let data: SendMessageResponse = response.json().await?; if !data.ok < eprintln!( «Cannot send message: <>», data.description .unwrap_or_else(|| «error has no description».to_string()) ) > Ok(()) >

Объединяем

В функции main сначала запрашивается список всех программ (для сохранения их названий), далее запускается бесконечный цикл, который каждую секунду проверяет обновления со стороны телеграма, вызывая функцию handle_updates , а также раз в примерно 10 минут проверяет обновления рейтингов и рассылает сообщения об обновлении. #[tokio::main] нужно для старта main сразу как асинхронной функции, без необходимости ручного создания loop-а.

const TEN_MIN_IN_SEC: i32 = 10 * 60; #[tokio::main] async fn main() -> Result> < let db = init_db()?; load_programs( let mut offset = 0; let mut sec_counter = 0; loop < offset = handle_updates( if sec_counter == 0 < check_rating_updates( >sec_counter = (sec_counter + 1) % TEN_MIN_IN_SEC; time::sleep(time::Duration::from_secs(1)).await; > >
check_rating_updates
async fn check_rating_updates(db: Result> < // select registered watchers from ‘results’ for c in db.select_all_competitions()? < if let Some(case_number) = c.competition.case_number < handle_competition(db, c.degree, c.program_id, false) .await?; >> Ok(()) >

Итоги

P.S.

  • Хотелось бы проверить, как приложение справится под нагрузкой (сервер довольно слабенький)
  • Из возможных фич — учитывать, что кто-то из тех, кто находится выше по рейтингу, уже проходят на направление, которое у них выше по приоритету
  • После написания основной части статьи я переписал часть проекта, улучшив передачу ошибок наверх и их обработку
  • Только магистратура потому что для бакалавриата вроде как приходит другая структура данных, а времени особо не было на это
  • По хорошему, ввод данных надо делать не передачей нескольких аргументов в одной строке, а чтобы пользователь мог ввести все по очереди, в идеале — чтобы надо было просто выбирать из предложенных вариантов, например, с помощью кнопок
  • Вдохновился этой статьей https://habr.com/p/679832
  • Команда потестить: /watch itmo master 15850 M1-0979-2022 . Это не я 🙂 Выбрал наугад
Еще по теме:  Join group chat on Telegram что это значит

Источник: habr.com

Писать курсовые и дипломы с помощью ChatGPT теперь можно прямо в Word — благодаря сервису Paperpal

Нейросеть анализирует написанное вами и предложит варианты по перефразированию сложных предложений или даже слов, что значительно уменьшит количество правок любой работы и сделает текст более академическим.

Можно использовать в браузере либо интегрировать как расширение в Microsoft Word. А самое крутое — работает с русским языком.

Зачем нужен Paperpal на основе ChatGPT

  • Специально для исследователей и академического письма
  • Обширные рекомендации по языку
  • Точное обнаружение сложных грамматических ошибок
  • Предложения по перефразированию сбивающих с толку предложений
  • Проверяет наличие структурных и технических несоответствий

Как установить Paperpal for Word

1. Откройте “Добавить Add-ins”

  • Откройте документ
  • Нажмите on Insert tab
  • Нажмите on Get Add-ins

2. Найдите и добавьте Paperpal

  • Найдите Paperpal в Office Add-ins каталоге
  • Нажмите Добавить и затем нажмите Далее

3. Нажмите “Открыть Paperpal”

  • Нажмите Open Paperpal на Home закладеке of Word
  • Paperpal готов помогать Вам!

Источник: onff.ru

Чат-бот
для университета

Мобильный помощник абитуриентам вуза в telegram. В этом кейсе расскажем как справлялись с задачей, почему отказались от некоторых задумок и как в процессе всплыли неожиданные подробности. Но обо всем по-порядку.

За первый месяц работы бота:
Подписчиков присоединилось к чат-боту
Сообщений отправлено
Сообщение получено от пользователей
О компании

Марийский государственный университет (МарГУ) каждый год набирает больше 2400 студентов-первокурсников. В том числе и иностранных студентов. Как и у любого большого вуза, на сайте МарГУ находится огромной объем информации, а найти нужную становится настоящим испытанием для абитуриентов и их родителей.

Телеграм-бот МарГУ должен был рассказывать абитуриентам, как подавать документы, заселяться в общежитие, смотреть рейтинг и когда сдавать вступительные экзамены. Все это происходило внутри мессенджера, а значит не нужно было скачивать дополнительные приложения и ждать прогрузки страницы сайта — ответ студенты получали мгновенно после запроса. Времени на запуск чат-бота у нас было мало — всего 2 недели.

МарГУ (Марийский Государственный Университет) в 2021 году решил задействовать ещё больше каналов для привлечения абитуриентов. Было понятно, что привлечение работает, нужно усилить коммуникацию и заговорить с поступающими на понятном языке, дать ответы на вопросы и подсказки для поступления.

Поэтому решили запускать чат-бот в Telegram. И это логично — телеграм есть в смартфоне практически каждого студента и абитуриента, а значит, можно общаться со своей аудиторией в удобном им формате.

В функционал заложили популярные вопросы — как добраться до корпуса, какие документы нужны для заселения или какие экзамены нужно сдать, чтобы, например, поступить на врача. Классный, масштабный и информативный сценарий — задача ровно такая, как мы любим.

Этапы выполнения

Рейтинг
( Пока оценок нет )
Загрузка ...