Как сделать рассылку в телеграмм боте через определенное время?
Есть вопрос с телеграмм ботом на telebot. Нужно, чтобы бот отправлял сообщение (картинку), через определенный интервал времени. Основная проблема заключается в том, что при получении сообщения с новым интервалом бот должен отменить старый цикл и рассылать по новому интервалу, но этого не происходит. У меня не получается сделать это через while и schedule. В моем коде, когда он принимает сообщение с интервалом, то отправляет сообщения, но когда пишу 'Отключить', то не реагирует. В этом и проблема. Помогите, пожалуйста.
import schedule
import time
import random
import telebot
from glob import glob
from telebot import types
key = '...'
bot = telebot.TeleBot(key)
@bot.message_handler(commands=['Начать', 'start', '/start', 'Главная'])
def start(message):
markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
item1 = types.KeyboardButton('Рассылка')
markup.add(item1)
bot.send_message(message.chat.id, 'Используйте меню', reply_markup = markup)
@bot.message_handler(content_types=['text', 'photo'])
def main(message):
lists = glob('images/*')
pic = random.choice(lists)
if message.chat.type == 'private':
if message.text == 'Рассылка':
sub_menu = types.ReplyKeyboardMarkup(resize_keyboard=True)
item8 = types.KeyboardButton('Назад')
item15 = types.KeyboardButton('10 мин')
item9 = types.KeyboardButton('30 мин')
item10 = types.KeyboardButton('1 час')
item11= types.KeyboardButton('10 часов')
item12 = types.KeyboardButton('24 часа')
item16 = types.KeyboardButton('Отключить')
sub_menu.add(item8, item15, item9, item10, item11, item12, item16)
bot.send_message(message.chat.id, 'Здесь можно настроить частоту автоматической',
reply_markup=sub_menu)
elif message.text == '10 мин':
while True:
time.sleep(600)
bot.send_photo(message.chat.id, photo=(open(pic, 'rb')))
pic = random.choice(lists)
if message.text == 'Отключить' or message.text == '30 мин' or message.text == '1 час' or message.text == '10 часов' or message.text == '24 часа':
break
elif message.text == '30 мин':
while True:
time.sleep(1800)
bot.send_photo(message.chat.id, photo=(open(pic, 'rb')))
pic = random.choice(lists)
if message.text == 'Отключить' or message.text == '10 мин' or message.text == '1 час' or message.text == '10 часов' or message.text == '24 часа':
break
elif message.text == 'Отключить':
bot.send_message(message.chat.id, 'Рассылка отключена!')
else:
bot.send_message(message.chat.id, 'Используйте кнопки!')
bot.polling(none_stop=True)
Ответы (2 шт):
Не претендую на адекватность решения, скорее костыль и не для того чтобы ходить.
Создайте класс с переменной "времени", ну, или просто какой-то флаг. Кнопками (или командой боту) вы можете изменить значение этой переменной, она будет глобальной для кода.
Пример кода:
import telebot as tb
import time
token = TOKEN
bot = tb.TeleBot (token)
class Timer():
_time = 10
@bot.message_handler(commands=['start'])
def cm_start(message):
#Какие-то действия при старте
bot.send_message (message.chat.id, text='')
@bot.message_handler(commands=['timer_on'])
def cm_timer_on(message):
Timer._time = 10
#Тот самый таймер и переменная
while Timer._time != 0:
bot.send_message(message.chat.id, text=f'Время {time.time()}')
time.sleep(Timer._time)
print ('oop') #это сообщение будет в консоли после выполнения команды /timer_off
@bot.message_handler(commands=['timer_off'])
def cm_timer_off(message):
#Тут указываем что переменная ноль или любое другое условие выхода из цикла
Timer._time = 0
bot.send_message (message.chat.id, text='Ня пока')
bot.infinity_polling()
Конечно, это пример, думаю что вы способны использовать идею для своей задачи.
Я предлагаю вам использовать apscheduler.
Создайте глобальную переменную где то в начале.
# Фоновый планировщик
from apscheduler.schedulers.background import BackgroundScheduler
# Для указания интервала
from apscheduler.triggers.interval import IntervalTrigger
mailing = BackgroundScheduler()
# Запускаем планировщика. Он будет ждать, когда появится работа.
# Будет работать, даже если не будет работы.
mailing.start()
def sending_newsletter():
# остальной код...
...
# В каком то из функции...
@bot.message(...)
def some_function(message):
# Указываем, что это глобальная переменная
global mailing
# ищем, есть ли работа с айди пользователя
has_job = mailing.get_job(message.from_user.id) is not None
minutes = 0
if message.text == '10 мин':
minutes = 10
elif message.text == '30 мин':
minutes = 30
elif message.text == 'Отключить' and has_job:
# Останавливаем работу
mailing.pause_job(message.from_user.id)
else:
return bot.send_message(
message.from_user.id, 'Используйте кнопки!')
if has_job:
# Работа с таким айди есть,
# значит нужно пересоздать работу с таким же айди
mailing.reschedule_job(
sending_newsletter, trigger=IntervalTrigger(minutes=minutes))
else:
# Создаём работу с айди пользователя
mailing.add_job(
sending_newsletter,
trigger=IntervalTrigger(minutes=minutes),
id=message.from_user.id)
Чтобы передать параметры в функцию, нужно сделать так: mailing.add_job(sending_newsletter, trigger=IntervalTrigger(minutes=minutes), id=message.from_user.id, trigger_args=(1, 2)), также самое и в mailing.reschedule_job
Как для синхр. бота, который не особо популярный - неплохо, но всё же лучше использовать асинхр. версию telebot или использовать aiogram и использовать асинхр. функции.Синхронность блокирует основной поток, но BackgroundScheduler работает в другом потоке и блокировать основу не будет, но когда будет запускать функции, то поток будет блокироваться.Есть асинхр. часть apscheduler, благодаря асинхр. основной поток блокироваться не будет и бот виснуть также не будет.
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
sch = AsyncIOScheduler()
# Там всё абсолютно тоже самое, что и в BackgroundScheduler
Знаю, что вопрос 3х летней давности, но а мало ли, кому то пригодиться?