Эта статья не претендует на что-то серьезное, это просто мой опыт без каких-либо знаний языка Python в создании простого Телеграм бота для проверки необходимых мне узлов/ресурсов сети интернет. Я понимаю, что всё, что будет написано ниже может выполнять вполне успешно Zabbix, но из-за нехватки времени мне оказалось проще реализовать какие-то нужные мне вещи за счет Телеграм бота. Прежде чем начать поясню, бота создавал и настраивал под пользователем user, который имеет обычные права в системе.
И так приступим!
И так для телеграмм бота я использовал ту же виртуальную машину от Scaleway (см. статью Разворачиваем свой SOCKS5 сервер на Scaleway).
Как развернуть среду питон на убунте можно найти в интернете или же на сайте моего друга и коллеги: https://flammlin.com/blog/2020/01/19/python-telegram-bot/ – статья подробно описывает схожую тематику по созданию телеграмм бота.
Принцип работы бота.
На создание бота у меня ушло наверно полных 3-4 дня + тестирование и обкатка. Причем делал в свободное от работы время, так что времени считаю ушло не много. Сначала это был просто бот, потом я вынес все настройки в txt файлы и считаю, что так удобнее. Хотелось конечно еще научить бота создавать файлы настроек, но руки так и не дошли.
И так принцип работы – изначально при запуске бот считывает все настройки из конфигурационного файла config.py в котором у меня указаны следующие параметры:
1 2 3 4 |
users = ['111111111','2222222222'] token = '33333333:AA32423423423sdfsfsfsdfScjLPw' tokenOWM = 'c86ddfsfwdfsdfasddfsfsfsdfsdbe9' proxy = 'socks5h://user:password@111.11.111.11:11111' |
users – список id пользователей которым разрешен доступ к боту
token – токен самого бота
tokenOWM – вы можете его не использовать, но мне уж очень хотелось знать температуру у себя в деревне, где находится дача ну и в своем городе, еще лежа в кровати.
proxy – параметр для подключения бота через прокси. Решил его здесь указать, потому как в большинстве примеров в интернете socks5h пишется так: socks5 – что вызывает ошибку в подключении прокси.
Файл config.py создайте заранее и впишите туда ваши данные. Этот файл должен лежать в той же папке, где у вас будет находиться и сам бот.
Далее бот проверяет есть ли txt файлы настроек, содержащий ip адреса либо доменные имена узлов сети, которые надо проверять. Для личного удобства я разделил настройки пользователя на 3 файла:
1 2 3 |
devices.txt rdp.txt site.txt |
devices.txt – файл содержит узлы, работу которых я регулярно проверяю:
1 2 3 4 |
Офис1:8.8.8.8:666 Офис2:8.8.4.4:21 VPN сервер:8.23.8.8:22 Мой прокси:111.11.111.11:11111 |
параметры указанные в файле вымышленные, но принцип заполнения очень прост:
название:ip-адрес:номер_порта
Количество строк в файле не ограничено! Но помните, если файл будет слишком большой, проверка очень сильно затянется. По этому важно (возможно в будущих версиях сделаю) разделить все проверки на отдельные потоки. А пока в файле 4-10 строк, можно поработать и так.
rdp.txt – файл, в который я вынес все свои RDS (RDP) сервера (порт может быть стандартный и нет):
1 2 3 4 |
Вася:7.2.20.75:3333 Петя:8.3.20.10:3389 Ваня:5.16.22.4:7777 Олег:5.13.12.5:9833 |
Формат заполнения файла rdp.txt такой же как и у devices.txt: название:ip-адрес:номер_порта – это дает возможность указать не стандартные порты RDP.
site.txt – это файл с сайтами. В нем указываются url сайтов (не просто название сайта а url по которому можно будет проверить, если доменное имя отключат за не уплату и регистратор повесит заглушку). Забегая в перед могу сказать следующее – я изучал коды ответов при http запросов и в принципе этого было бы достаточно, чтобы понять сайт доступен или нет, но например Ру-Центр, при отключении домена вешает свою заглушку и http запрос не содержит ошибки. Я сначала этого не знал и мой код содержал ошибку. И по этому было принято решение чуть доработать (и в тоже время упростить) функцию проверки наличия сайта путем добавления после доменного имени адреса какого-либо файла либо уникальной страницы, которая есть на сайте. Ниже функция из телеграм бота, в том виде, в котором она работала раньше:
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 |
### функция проверки HTTP/HTTPS соединений def listSite(status): if preLoad(): # проверяем, а есть ли файл настроек? Далее если он есть: with open('site.txt','r') as f: # открываем файл для чтения listHTTP = f.read().splitlines() # заносим в справочник считывая построчно данные в listHTTP urlStatus = [] # создаем пустой справочник for x, site in enumerate(listHTTP, 1): # цикл обработки переменной site присватается каждый элемент listHTTP по очереди try: # используем 2 типа зпросов GET и HEADER response = requests.get(site) responsePHP = urllib.request.urlopen(site) response.raise_for_status() except HTTPError as http_err: urlStatus.append(site+' - ошибка') errorLogHttp.append(site+' - ошибка') except Exception as err: urlStatus.append(site+' - ошибка') errorLogHttp.append(site+' - ошибка') else: # бывает что сайт возвращает заголовок, но непонятно может это заглушка об неуплате хостинга # поэтому я проверяю наличие выдачи в headers значения X-Powered-By и Content-Length if responsePHP.headers['X-Powered-By'] is None and responsePHP.headers['Content-Length'] is None: urlStatus.append(site+' - внимание!') # сайт возможно не оплачен errorLogHttp.append(site+' - ошибка') else: urlStatus.append(site+' - ok') # сайт наместе, даже написанный на html, но с Content-Length return '\n'.join(urlStatus) #print(errorLog) else: return ''.join('нет файлов настроек') #print(errorLog) status = '' |
И в принципе, на этом всё: бот считывает свои собственные настройки с файла config.py, далее проверяет наличие txt файлов настроек пользователя и после этого стартуя сообщая всем users из файла config.py, что бот готов к работе:
1 2 3 4 5 6 7 8 9 10 11 12 |
# Функция массвой рассылки уведомлений def sendall(text): if len(config.users) > 0: for user in config.users: try: bot.send_message(user, text) except: print() ... ... ... sendall('Бот запущен и готов к работе!') |
Для чего нужно это сообщение? Чтобы в принципе понимать, что бот у вас работает. Если по какой-то причине бот “упал” или ушел в перезагрузку ваш сервер, бот автоматически запустится (для этого мы укажем в конце статьи расписание запуска cron) со следующим содержимым файла:
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash PIDFILE=/home/user/bot-file.pid if pgrep --pidfile $PIDFILE &>/dev/null; then echo "Exit! Python bot is already running!" exit 1 else nohup /usr/bin/python3.6 /home/user/bot-file.py &>/home/user/bot-file.log & echo $!>$PIDFILE fi |
И так рассмотрим код!
Код бота.
Еще раз хотелось напомнить, что данный бот был собран на коленке без знаний питона. Просто интернет и поиск дал мне какие-то ответы на мои запросы и я буду искренне рад, если в комментариях будут указаны мои ошибки или на оборот будет указан более оптимальный код.
Структура бота.
Бот можно представить в виде следующей структуры:
- импорт подключаемых библиотек/функций
- получение необходимых токенов и параметров прокси
- описание функции проверки списка доступа пользователей к боту
- описание функции массовой рассылки о статусе бота
- описание функции проверки наличия файлов настроек
- создание клавиатуры с нашими командами
- создаем пустые справочники ошибок
- описание функции проверки http/https соединений с вызовом функции проверки на наличие файлов настроек
- описание функции проверки rdp соединений с вызовом функции проверки на наличие файлов настроек
- описание функции проверки ssh/ftp и других соединений с вызовом функции проверки на наличие файлов настроек
- работаем с командами которые посылаем с клавиатуры, вызывая при этом соответствующие функции
- добавляем возможность выключения бота (объяснять не буду, но мне так удобно: выключил бота, с телефона/планшета поправил код на сервере, сохранил, подождал пока он через cron запустится)
- подключаем стикеры для разнообразия
- вызываем функцию массовой рассылки о том, что бот запущен
- запускаем бота в цикл прослушки команд функцией bot.polling()
Код бота с комментариями.
Теперь давайте рассмотрим код бота с комментариями. Комментарии вставлены в сам код, плюс на некоторых моментах я остановлюсь чуть подробнее.
Код идет последовательно, разрываясь на небольшие мои пояснения:
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 |
import config # Импорт config.py import telebot import pyowm # Работа с погодой from telebot import apihelper # Нужно для работы Proxy import urllib.request # request нужен для загрузки файлов от пользователя import types import datetime import glob,os import logging import socket import requests from requests.exceptions import HTTPError import sys # используем для sys.exit(0) - прерывание работы программы import time ########## - ОБЛАСТЬ ТЕСТИРОВАНИЯ КОДА ########## # # # owm = pyowm.OWM(config.tokenOWM,language = "RU") # Для погоды: Передаем токен из файла config.py и задаем язык bot = telebot.TeleBot(config.token) # Передаём токен из файла config.py apihelper.proxy = {'https':config.proxy} # Передаём Proxy из файла config.py ### Проверка на список доступа def autor(chatid): strid = str(chatid) for item in config.users: if item == strid: return True return False # Функция массвой рассылки уведомлений def sendall(text): if len(config.users) > 0: for user in config.users: try: bot.send_message(user, text) except: print() ## Проверка наличия файлов настроек, чтобы избежать исключительной ситуации! def preLoad(): fileSetting = glob.glob('./*.txt') # берем txt файлы настроек из каталога бота if not fileSetting: #если файлов нет print('нет файлов настроек!') return False else: #если файлы есть return True print('файлы настроек есть!') # наша функция будет возвращать истину или ложь перед запуском любой команды ### клавитура и команды: у нас 4 строки. keyGlobal = telebot.types.ReplyKeyboardMarkup() keyGlobal.row('/Status') keyGlobal.row('/Сайты','/RDP','/Оборудование') keyGlobal.row('/Дом','/Деревня') keyGlobal.row('/установки') |
Команда Status вызывает проверку всех сайтов и узлов сети указанных во всех файлах настройках пользователя, команды: Сайты, RDP, Оборудование – вызывают уже отдельные функции, которые отвечают за проверку наличия сайтов, серверов удаленного рабочего стола и сетевого оборудования либо сервисов.
1 2 3 4 5 6 |
errorLogHttp = [] errorLogHttpTemp = [] errorLogRdp = [] errorLogRdpTemp = [] errorLogSsh = [] errorLogSshTemp = [] |
создаем пустые справочники для лога ошибок во время запроса пользователя. Идем далее:
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 |
### функция проверки HTTP/HTTPS соединений def listSite(status): if preLoad(): # проверяем, а есть ли файл настроек? Далее если он есть: with open('site.txt','r') as f: # открываем файл для чтения listHTTP = f.read().splitlines() # заносим в справочник считывая построчно данные в listHTTP urlStatus = [] # создаем пустой справочник for x, site in enumerate(listHTTP, 1): # цикл обработки переменной site присватается каждый элемент listHTTP по очереди try: # используем 2 типа зпросов GET и HEADER response = requests.get(site) responsePHP = urllib.request.urlopen(site) response.raise_for_status() except HTTPError as http_err: urlStatus.append(site+' - ошибка') errorLogHttp.append(site+' - ошибка') except Exception as err: urlStatus.append(site+' - ошибка') errorLogHttp.append(site+' - ошибка') else: urlStatus.append(site+' - ok') # сайт на месте return '\n'.join(urlStatus) #print(errorLog) else: return ''.join('нет файлов настроек') #print(errorLog) status = '' |
Здесь в коде есть закомментированные строки: #print(errorLog) – я их оставил для наглядности, очень удобно во время выполнения функции (если вы делаете всё в блокноте и загружаете потом на сервер) вывод результатов работы функции видеть в консоле. Для этого правда необходимо будет запускать бота не по cron, а руками:
1 |
python3 bot-file.py |
Далее идет функция проверки RDP соединений:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### функция проверки RDP соединений на стандартном порту 3389 и других def listRDPClient(status): f = open('rdp.txt', 'r') # открываем файл для чтения rows = [] # создаем пустой список в который будем заносить результаты проверки for line in f: # перебираем строки row = line.split(':') # находим и разделяем строку на значения разделенными двоеточиями sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((row[1],int(row[2]))) # используем 1 и 2 значения разделенной строки (так как в 0 у нас хранится название) if result == 0: rows.append(f'{row[0]} - ОК') # всё хорошо, присваиваем к названию статус ОК else: rows.append(f'{row[0]} - Ошибка') # соответственно тут присваиваем статут с ошибкой errorLogRdp.append(f'{row[0]} - Ошибка') sock.close() return '\n'.join(rows) status = '' |
Теперь проверяем наличие сервисов или служб или же оборудование с любым открытым портом (о котором вы разумеется знаете):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
### функция проверки SSH соединений на стандартном порту 22 либо из файла def listSSHClient(status): f = open('devices.txt', 'r') # открываем файл для чтения rows = [] # создаем пустой список в который будем заносить результаты проверки for line in f: # перебираем строки row = line.split(':') # находим и разделяем строку на значения разделенными двоеточиями sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((row[1],int(row[2]))) if result == 0: # далее так же как и и в функции listRDPClient rows.append(f'{row[0]}:{row[1]} - ОК') else: rows.append(f'{row[0]}:{row[1]} - Ошибка') errorLogSsh.append(f'{row[0]}:{row[1]} - Ошибка') sock.close() return '\n'.join(rows) status = '' |
Далее у нас идет большой кусок кода, который отвечает за команды с клавиатуры, которую мы объявляли выше:
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 |
### Начинаем работу с командой Status @bot.message_handler(commands=['Status']) def welcome_status(message): if autor(message.chat.id): if preLoad(): # проверяем, а есть ли файлы настроек? Далее если они есть: # выводим статус всех наших устройств. Идет обращение к функциям, что описаны ниже # добавляем параметр disable_web_page_preview=True для вывода сайтов без превью. Если нужно превью проверяемых сайтов просто удалите этот параметр bot.send_message(message.chat.id, 'RDP клиенты: \n' + listRDPClient(status) + '\nОборудование: \n' + listSSHClient(status) + '\nСайты: \n' + listSite(status), disable_web_page_preview=True, reply_markup=keyGlobal) else: # файлов настроек нет! Выводим сообщение! bot.send_message(message.chat.id, 'нет файлов настроек!') ### Начинаем работу с командой /Сайты @bot.message_handler(commands=['Сайты']) def welcome_status(message): if autor(message.chat.id): # выводим статус listRDPClient, параметр disable_web_page_preview=True параметр отвечает за отключение превью сайтов, чтобы у нас результат был красивым списком bot.send_message(message.chat.id, 'Сайты: \n' + listSite(status), disable_web_page_preview=True, reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') ### Начинаем работу с командой /RDP @bot.message_handler(commands=['RDP']) def welcome_status(message): if autor(message.chat.id): # выводим статус listRDPClient bot.send_message(message.chat.id, 'RDP клиенты: \n' + listRDPClient(status), reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') ### Начинаем работу с командой /Оборудование @bot.message_handler(commands=['Оборудование']) def welcome_status(message): if autor(message.chat.id): # выводим статус listRDPClient bot.send_message(message.chat.id, 'Оборудование: \n' + listSSHClient(status), reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') ### температура @bot.message_handler(commands=['Дом']) def welcome_temp(message): if autor(message.chat.id): place1 = 'Москва' observation = owm.weather_at_place(place1) w1 = observation.get_weather() temp = w1.get_temperature('celsius')['temp'] user_name = message.from_user.first_name bot.send_message(message.chat.id, user_name+', сейчас в г.' + place1 + ' температура ' + str(temp) + '°C, ' + w1.get_detailed_status(), reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') @bot.message_handler(commands=['Деревня']) def welcome_temp(message): if autor(message.chat.id): place1 = 'Барвиха' observation = owm.weather_at_place(place1) w1 = observation.get_weather() temp = w1.get_temperature('celsius')['temp'] user_name = message.from_user.first_name bot.send_message(message.chat.id, user_name+', сейчас в г.' + place1 + ' температура ' + str(temp) + '°C, ' + w1.get_detailed_status(), reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') ### установки @bot.message_handler(commands=['установки']) def welcome_temp(message): test=[] if autor(message.chat.id): bot.send_message(message.chat.id,'Файлы настроек:') fileSetting = glob.glob('./*.txt') # берем все txt файлы настроек из каталога бота if not fileSetting: #если файлов нет bot.send_message(message.chat.id,'Файлы настроек не найдены!') # далее здесь будет объявлена глобальная переменная # которая будет блокировать выполнение команд если не заданы настройки else: #если файлы есть for colTXT in fileSetting: # запускаем цикл перебора файлов для последующего чтения содержимого #print(colTXT) # тут можно раскоментировать чтобы понимать, что содержит переменная with open(colTXT,'r') as f: # открываем файл (по очереди) для чтения print(colTXT) # выводим имя файла чтобы ниже отобразить его содержимое test.append(f'========= {colTXT} =========\n') listSetting = f.read().splitlines() # заносим в справочник считывая построчно содержимое файла #print(listSetting) # тут можно раскоментировать чтобы понимать, что содержит переменная for x, settingRow in enumerate(listSetting, 1): test.append(f'{settingRow}\n') print(settingRow) # выводим построчно все настройки из файла bot.send_message(message.chat.id, ' '.join(test), disable_web_page_preview=True, reply_markup=keyGlobal) |
Обращаю внимание на несколько моментов:
Если в блоке вызова функции проверки сайтов не использовать параметр disable_web_page_preview=True, то у вас в ответе бота будут еще и заголовки сайта с предпросмотром, что не всегда удобно если список сайтов большой.
Переменная colTXT в функции показа установок пользователя содержит имя файла установок.
Далее у нас идет функция работающая с командой /Start, когда вы подключаетесь к боту:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Тут работаем с командой start @bot.message_handler(commands=['start']) def welcome_start(message): if autor(message.chat.id): user_name = message.from_user.first_name bot.send_message(message.chat.id, 'Привет, '+user_name+'!') # Приветствие от бота, берется ваше имя указанное в профиле bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIe2l4xZVJC1WR6c-xfCZ9OfGfgNxy5AAJ2AAOIhOYNgpBsfWpQCrYYBA') place = 'Электросталь' # для удобства мне сразу показывается температура в городе за окном) observation = owm.weather_at_place(place) w = observation.get_weather() temp = w.get_temperature('celsius')['temp'] bot.send_message(message.chat.id, 'Температура в г. ' + place + ': ' + str(temp) + '°C, ' + w.get_detailed_status()+'.', reply_markup=keyGlobal) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') |
Далее команда выключения бота (вводится руками с клавиатуры: /пока):
1 2 3 4 5 6 7 8 9 10 11 |
# Тут мы выключаем бота чтобы не лезть на сервер и не прибивать процесс) @bot.message_handler(commands=['пока']) def welcome_start(message): if autor(message.chat.id): user_name = message.from_user.first_name bot.send_message(message.chat.id, 'До скорого, '+user_name+'!') # Фраза бота, берется ваше имя указанное в профиле bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIe2F4xZSuUmkGmJn93xLX80Qvf7PzyAAIjAAOIhOYN0t7a_xChAgkYBA') os._exit(0) else: # если кто-то посторонний пытается попасть к нашему боту - посылаем сообщение и id стикера можно взять у бота @idstickerbot bot.send_message(message.chat.id, 'Извини, но тебе доступ запрещен (ID: ' + str(message.chat.id) +')') bot.send_sticker(message.chat.id, 'CAACAgIAAxkBAAIc3V4u9QABpiLBXqhud9AaiZIzx4kj0gACegADiITmDQMsg4ViuzWFGAQ') |
Далее стикеры, рассылка сообщения и запуск бота:
1 2 3 4 5 6 |
@bot.message_handler(content_types=['sticker']) def sticker_id(message): print(message) sendall('Бот запущен и готов к работе!') bot.polling() # запускаем бота |
На этом код бота завершен.
crontab.sh
Теперь нам необходимо создать bash скрипт для запуска бота при перезагрузке машины, либо при какой-либо другой непредвиденной ситуации. Код файла crontab.sh я писал выше, но тут еще раз его представлю. Сначала в каталоге бота создаем файл crontab.sh и открываем его на редактирование:
1 2 |
touch crontab.sh nano crontab.sh |
Далее вставляем скрипт:
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash PIDFILE=/home/user/bot-file.pid if pgrep --pidfile $PIDFILE &>/dev/null; then echo "Exit! Python bot is already running!" exit 1 else nohup /usr/bin/python3.6 /home/user/bot-file.py &>/home/user/bot-file.log & echo $!>$PIDFILE fi |
Обязательно проверьте какая у вас версия питона! У меня в файле указана версия 3.6 и путь, по которому он установлен. Если вы не знаете, то прежде чем создавать файл скрипта crontab.sh выполните эти команды:
1 2 |
which python3 python3 -V |
Далее делаем файл исполняемым:
1 |
chmod u+x /home/user/crontab.sh |
и добавляем задачу в cron:
1 |
crontab -e |
добавив эту строку:
1 |
*/5 * * * * /home/user/crontab.sh |
Всё. В течении 5 минут ваш бот запустится!