Python + Telegram = bot
Решил немного погрузиться в изучение Python, в процессе поставил задачу отойти от стандартного Hello World и попытаться написать сразу простого бота для Telegram. В итоге, бот может отвечать на заданные фразы, присылать внешний ip адрес и делать снимок с камеры. Скажу сразу, код не идеальный, но в процессе отладки ни один котик не пострадал.
Для начала сразу оговорюсь писать будем на Python версии 3 и будем использовать модули для облегчения жизни нашего проекта.
1 2 |
python3 -V Python 3.7.5 |
Пишем мы на Ubunru 19.10, поэтому надо учитывать, что есть две версии установленного Python.
Установим PIP и необходимые пакеты:
1 2 3 |
apt install python3-pip pip3 install pytelegrambotapi pip3 install PySocks |
PIP — система управления пакетами, которая используется для установки и управления программными пакетами.
pytelegrambotapi — нужен для работы с API Telegram
PySocks — прокси сервер, иначе бот не будет работать.
Как было указано выше, пишем мы на Ubuntu, поэтому в директории пользователя /home/users-name/ создаем файлы bot-file.py и config.py
1 2 |
touch bot-file.py touch config.py |
Файл bot-file содержит код бота, а файл config необходим для конфигурации бота. В конфигурации необходимо будет указать список пользователей, которым разрешено взаимодействие с ботом.
Начнем с файла конфигурации, там все просто, необходимо просто указать id пользователя Telegram.
1 |
users = ['id-user1','id-user2'] |
Переходим к файлу бота.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
import config import telebot from telebot import apihelper from telebot import types import datetime import os import logging logger = logging.getLogger('log') logger.setLevel(logging.INFO) fh = logging.FileHandler('someTestBot.log') fh.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s | %(levelname)-7s | %(message)s") fh.setFormatter(formatter) logger.addHandler(fh) ### Прокси сервер apihelper.proxy = {'https':'socks5h://login:password@ip-host:port'} ### Token telegram bot bot = telebot.TeleBot('Token-bot', threaded=True) ### Функция проверки авторизации def autor(chatid): strid = str(chatid) for item in config.users: if item == strid: return True return False ### Клавиатура keyboard1 = telebot.types.ReplyKeyboardMarkup() keyboard1.row('Привет', 'Пока', '/ip','/camera') ### Прием документов @bot.message_handler(content_types=['document']) def handle_docs_photo(message): try: chat_id = message.chat.id file_info = bot.get_file(message.document.file_id) downloaded_file = bot.download_file(file_info.file_path) src = '/home/users-name/received/' + message.document.file_name; with open(src, 'wb') as new_file: new_file.write(downloaded_file) bot.reply_to(message, "Пожалуй, я сохраню это") except Exception as e: bot.reply_to(message, e) ### Прием фото @bot.message_handler(content_types=['photo']) def handle_docs_photo(message): try: chat_id = message.chat.id file_info = bot.get_file(message.photo[len(message.photo) - 1].file_id) downloaded_file = bot.download_file(file_info.file_path) src = '/home/users-name/received/' + file_info.file_path; with open(src, 'wb') as new_file: new_file.write(downloaded_file) bot.reply_to(message, "Фото добавлено") except Exception as e: bot.reply_to(message, e) @bot.message_handler(commands=['start']) def start_message(message): if autor(message.chat.id): cid = message.chat.id message_text = message.text user_id = message.from_user.id user_name = message.from_user.first_name bot.send_message(message.chat.id, 'Привет, ' + user_name + ' Что ты хочешь от меня, собака сутулая!', reply_markup=keyboard1) bot.send_sticker(message.chat.id, 'CAADAgAD6CQAAp7OCwABx40TskPHi3MWBA') else: bot.send_message(message.chat.id, 'Тебе сюда нельзя. Твой ID: ' + str(message.chat.id)) bot.send_sticker(message.chat.id, 'CAADAgADcQMAAkmH9Av0tmQ7QhjxLRYE') @bot.message_handler(commands=['camera']) def camera_message(message): if autor(message.chat.id): bot.send_message(message.chat.id, 'Фото с камеры') link = 'http://admin:admin@192.168.0.80/ISAPI/Streaming/channels/101/picture/' os.system('wget %s -O /tmp/photo.jpg'% link) imageFile = '/tmp/photo.jpg' img = open(imageFile, 'rb') bot.send_photo(message.chat.id, img, caption='Фото с камеры', reply_markup=keyboard1) else: bot.send_message(message.chat.id, 'Тебе сюда нельзя. Твой ID: ' + str(message.chat.id)) bot.send_sticker(message.chat.id, 'CAADAgADcQMAAkmH9Av0tmQ7QhjxLRYE') @bot.message_handler(commands=['ip']) def prim_message(message): if autor(message.chat.id): link = 'https://flammlin.com/pi' os.system('wget %s -O /tmp/ip.txt'% link) docum = open('/tmp/ip.txt', 'rb') bot.send_message(message.chat.id, docum, reply_markup=keyboard1) bot.send_sticker(message.chat.id, 'CAADAgADcQMAAkmH9Av0tmQ7QhjxLRYE') else: bot.send_message(message.chat.id, 'Тебе сюда нельзя. Твой ID: ' + str(message.chat.id)) bot.send_sticker(message.chat.id, 'CAADAgADcQMAAkmH9Av0tmQ7QhjxLRYE') bot.polling() |
Разберем некоторые интересные моменты, на которые следует обратить внимание.
Прокси
В данном примере используется Socks 5 прокси:
1 2 |
### Прокси сервер apihelper.proxy = {'https':'socks5h://login:password@ip-host:port'} |
Достаточно указать свои данные и запустить бот.
Token ID
В начале пытался брать Token из файла конфигурации, но бот так и не запустился. Пришлось оставить в коде, поэтому Token бота вписывать придется в сам код.
1 2 |
### Token telegram bot bot = telebot.TeleBot('Token-bot', threaded=True) |
Авторизация
1 2 3 4 5 6 7 |
### Функция проверки авторизации def autor(chatid): strid = str(chatid) for item in config.users: if item == strid: return True return False |
Для проверки пользователя используется функция проверки id пользователя из файла конфигурации. В случае успеха бот присылает сообщение и стикер, тоже самое в случае отказа.
Клавиатура
Красивые кнопочки любят все. 🙂
1 2 3 |
### Клавиатура keyboard1 = telebot.types.ReplyKeyboardMarkup() keyboard1.row('Привет', 'Пока', '/ip','/camera') |
Второй горизонтальный ряд добавляется легко, просто пишем снова — keyboard1.row.
Прием документов и фотографий
Бот умеет сохранять документы и фотографии, которые скачиваются в папку received. Папку предварительно необходимо будет создать в профиле пользователя, где создавали файлы бота и файла конфигурации.
Wget
С вопросом получения картинки с камеры и внешнего ip адреса пришлось повозить подольше. Но в итоге через папку temp и вызов через bash wget все получилось.
Планировщик crontab
Бот запускается через планировщик задач, каждые пять минут происходит проверка запущенного процесса. Так как бот использует прокси и может произойти обрыв канала интернет, то повторный запуск бота не сможет произойти.
Для начала необходимо точно определить где и какая версия Python установлена.
1 2 |
which python3 python3 -V |
В моем случае Python версии 3.7, поэтому в файле я буду указывать именно его.
1 2 |
touch crontab.sh nano crontab.sh |
Создаем bash скрипт для crontab
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/bin/bash if [ "pgrep -f bot-file.py" != 0 ] then { sleep 1 #delay /usr/bin/python3.7 /home/users-name/bot-file.py exit 1 } else { echo "Exit! Python bot is already running!" exit 1 } fi; |
Не забываем проверить пути до бота и Python.
Делаем файл исполняемым:
1 |
chmod u+x /home/users-name/crontab.sh |
Открываем планировщик и вносим задачу:
1 |
crontab -e |
Делал под пользователем, не под root.
1 |
*/5 * * * * /home/users-name/crontab.sh |
Проверить запустился ли бот, можно командой:
1 |
pgrep -f bot-file.py |
Должен будет выдать номер запущенного процесса, если строка пустая, то бот не запущен.
Заключение
В итоге получили бота, который в случае чего может и документ принять и фоточку с камеры наблюдения прислать. В интернете полно статей на данную тематику, есть и powershell боты и другие. Но мне было интересно решить именно такую задачу. В процессе эксплуатации буду наращивать потенциал бота.
Файлы примера бота можно скачать тут: python-bot
Если есть замечания по коду и его оптимизации, то буду рад вашим комментариям. Так как тема для меня слишком новая и код скорее всего далек от идеала!
8 комментариев
Виктор
Фрагмент кода бота
owm = pyowm.OWM(config.tokenOWM,language = «RU») # Передаём токен из файла config.py
bot = telebot.TeleBot(config.token) # Передаём токен из файла config.py
apihelper.proxy = {‘https’:config.proxy} # Передаём Proxy из файла config.py
фрагмент файла config.py:
token = ’72xxxxx14:AAEX_qCfxsmZxxxxxxxxxxxxxNM64ScjLPw’
tokenOWM = ‘c86xxxxxxxxxxxxxxxxxxxxxxa9be9’
proxy = ‘socks5h://gggggg:sssssssss@212.xx.xx.xxx:7xxx7’
—
Расположение (порядок) не играет роли
Виктор
Здравствуйте!
При использовании вашего bash скрипта обнаружил множественный запуск процессов с ботом. Задал этот вопрос на qna.habr.com (ссылка: https://qna.habr.com/q/709483) и мне подсказали что скрипт в данном случае должен быть таким:
#!/bin/bash
if pgrep -f «/usr/bin/python3.6 /home/usertest/bot.py» >&/dev/null
then {
echo «Exit! Python bot is already running!»
exit 1
}
else
{
sleep 1 #delay
/usr/bin/python3.6 /home/usertest/bot.py
exit 0
}
fi;
Но я немного по другому его использую, а именно без указания «/usr/bin/python3.6» :
#!/bin/bash
if pgrep -f «/home/usertest/bot.py» >&/dev/null
then {
echo «Exit! Python bot is already running!»
exit 1
}
else
{
sleep 1 #delay
/usr/bin/python3.6 /home/usertest/bot.py
exit 0
}
fi;
kanitelka
У меня с этим кодом 2 иногда экз проскакивают.
kanitelka
К вопросу логирования:
https://flammlin.com/blog/2020/03/10/python-log-bot-telegram-in-file/
Давид
Здравствуйте, не могли бы вы подсказать, как настроить callback_data с apihelper?
Запускаю программу подобную вашей, но с кнопками дающими обратную связь и появляется ошибка:
(__init__.py:420 MainThread) ERROR — TeleBot: «A request to the Telegram API was unsuccessful. The server returned HTTP 400 Bad Request. Response body:
[b'{«ok»:false,»error_code»:400,»description»:»Bad Request: can\’t parse reply keyboard markup JSON object»}’]»
kanitelka
Предположу, что проблема с прокси сервером. Но без кода сложно сказать, что у вас происходит.
Давид
Прокси использую два — один платный socks5, другой тор, на ошибку не влияет. С VPN при этом всё работает
Тут код:
https://github.com/ketorg0z/pyBot
kanitelka
А какую версию python используете?