Наверняка многие помнят, как в начале весны чуть ли не 9 из 10 арбитражных пабликов почти одновременно сменили аву на рекламу одной конфы. Нужно признать — прикольное решение. Вот только ходят слухи, что с тех пор у многих SMMщиков так и не прошло ПТСР. А если серьезно — зачем делать руками то, что можно автоматизировать? Ведь одно дело — сменить аву в 2–3 каналах и другое — в сетке из нескольких десятков. А поможет в этом бот для отложенной замены аватарок в Telegram-каналах, процесс создания которого мы и рассмотрим сегодня.


Правда, прежде, чем начать создавать бот, небольшое уточнение: бот должен быть админом канала, в котором вы собираетесь менять аву. Ваш КЭП.
Какие задачи выполняет бот для отложенной замены аватарок
Как очевидно из названия — заменяет авы в указанное время. Это может быть нужно для решения следующих задач:
Массовая замена в одно и то же время — например, если это часть РК.
Замена авы в неудобное время — например, если вы спите.
Минимизация рисков — реклам все равно, почему у вас пропал интернет в оговоренное время. Бот позволяет нивелировать подобные риски.
Каруселинг — добавив в текущий код простой цикл, можно создать из аватарок динамическую карусель.
Безопасность — если вы не хотите использовать боты для администрирования и вам нужен функционал только для замены аватарок.
В целом задачи для этого бота идентичны тем, для которых в принципе используются аватарки. Просто теперь их можно менять не руками. Ну и еще появляется отложка для ав.
Принцип работы бота для отложенной замены аватарок
Алгоритмически данный бот очень простой. Суть его функционала сводится к следующему:
- Прослушивание эфира и реакция на «не подходящие по формату сообщения» в виде приветствия и описания своего функционала.
- Инициация процесса создания задания при получении изображения.
- Запоминание изображения, @usernames каналов и времени замены авы в задачу и присвоение ей ID.
- Остановка создания задания и вывод просьбы предоставить данные в нужном формате, если @usernames или время указаны некорректно.
- Вывод запланированных задач при получении команды /show.
- Удаление задачи по ID при получении команды /delete ID (просто номер — без ID).
- Создание бэкапа при создании/изменении/удалении задач.
- Создание бэкапа для бэкапа перед созданием бэкапа — на случай, если вы «везунчик» и ваш сервер вдруг упадет именно в момент создания резервной копии.
- Восстановление бэкапа или бэкапа для бэкапа при получении команды /restore или /restore_old — соответственно.
- Автоматическое изменение аватарок согласно списку запланированных задач.
Как видим, ничего сложного.
Пошаговая инструкция, как сделать бот для отложенной замены аватарок
Так как данный бот реализован сугубо на bot_api без использований user_api, то его развертывание пройдет несколько проще, чем в последних наших ботах. Тем не менее ему все равно нужен сервер, где он будет «жить», а потому с него и начнем. Здесь есть два стула: создать свой Python-сервер или же арендовать существующий. Для тестов рекомендуем свой, для полноценной работы — арендный. Затем сервер нужно настроить, введя в консоль:
pip install pyTelegramBotAPI
pip install apscheduler
pip install requests
После загрузки необходимых модулей создаем сам бот, для этого:
1. Находим профиль BotFather — это официальный бот Телеги для создания токенов ботов, пишем ему, следуем его инструкции, получаем токен и сохраняем его.
2. Создаем в корне сервера файла bot.py.
3. Открываем его текстовым редактором и вставляем:
import telebot
import requests
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime
import json
import time
import shutil
BOT_TOKEN = 'ТОКЕН СЮДА'
bot = telebot.TeleBot(BOT_TOKEN)
scheduler = BackgroundScheduler()
scheduler.start()
pending_tasks = {}
user_state = {}
id_counter = 1
BACKUP_FILE = 'tasks_backup.json'
OLD_BACKUP_FILE = 'tasks_backup_old.json'
def save_backup():
try:
shutil.copy(BACKUP_FILE, OLD_BACKUP_FILE)
print("Старый бэкап сохранен.")
except FileNotFoundError:
pass
with open(BACKUP_FILE, 'w') as f:
json.dump(pending_tasks, f, indent=4)
print("Бэкап сохранен.")
def clear_all_jobs():
for job in scheduler.get_jobs():
scheduler.remove_job(job.id)
def restore_backup():
global pending_tasks
clear_all_jobs()
try:
with open(BACKUP_FILE, 'r') as f:
pending_tasks = json.load(f)
print("Бэкап восстановлен.")
except FileNotFoundError:
print("Бэкап не найден.")
return
for task_id, task in pending_tasks.items():
scheduler.add_job(
change_photo,
'date',
run_date=datetime.strptime(task['time'], "%Y-%m-%d %H:%M"),
args=[task['chat_ids'], task['photo_url'], task['user_id'], task_id],
id=f"task_{task_id}"
)
def restore_old_backup():
global pending_tasks
clear_all_jobs()
try:
with open(OLD_BACKUP_FILE, 'r') as f:
pending_tasks = json.load(f)
print("Старый бэкап восстановлен.")
except FileNotFoundError:
print("Старый бэкап не найден.")
return
for task_id, task in pending_tasks.items():
scheduler.add_job(
change_photo,
'date',
run_date=datetime.strptime(task['time'], "%Y-%m-%d %H:%M"),
args=[task['chat_ids'], task['photo_url'], task['user_id'], task_id],
id=f"task_{task_id}"
)
@bot.message_handler(commands=['start'])
def send_welcome(message):
bot.reply_to(message, "Привет! Отправь мне фото и список каналов, для которых нужно изменить аватарку.")
@bot.message_handler(commands=['show'])
def show_scheduled_tasks(message):
print("Содержимое scheduled_tasks:")
print(pending_tasks)
if not pending_tasks:
bot.reply_to(message, "Нет запланированных задач.")
return
response = "Запланированные задачи:\n"
for task_id, task in pending_tasks.items():
response += f"ID: {task_id}\nКаналы: {', '.join(task['chat_ids'])}\nВремя: {task['time']}\n\n"
bot.reply_to(message, response)
@bot.message_handler(content_types=['photo'])
def handle_photo(message):
file_info = bot.get_file(message.photo[-1].file_id)
file_url = f'https://api.telegram.org/file/bot{BOT_TOKEN}/{file_info.file_path}'
user_state[message.from_user.id] = {'photo_url': file_url, 'step': 'channels'}
bot.reply_to(message, "Отправь мне ID каналов или супергрупп (через пробел), для которых нужно установить аватарку (например, @your_channel).")
@bot.message_handler(func=lambda message: user_state.get(message.from_user.id, {}).get('step') == 'channels')
def get_channels(message):
chat_ids = message.text.split()
if not chat_ids:
bot.reply_to(message, "Пожалуйста, отправь хотя бы один канал. Пример: @your_channel")
return
user_state[message.from_user.id]['chat_ids'] = chat_ids
user_state[message.from_user.id]['step'] = 'datetime'
bot.reply_to(message, "Теперь отправь мне дату и время в формате YYYY-MM-DD HH:MM, когда нужно сменить аватарку.")
@bot.message_handler(func=lambda message: user_state.get(message.from_user.id, {}).get('step') == 'datetime')
def get_datetime(message):
try:
target_time = datetime.strptime(message.text, "%Y-%m-%d %H:%M")
if target_time < datetime.now():
bot.reply_to(message, "Время не может быть в прошлом. Пожалуйста, отправь будущее время.")
return
task = user_state[message.from_user.id]
task['time'] = target_time.strftime("%Y-%m-%d %H:%M")
global id_counter
task_id = id_counter
id_counter += 1
task['id'] = task_id
task['user_id'] = message.from_user.id
scheduler_task_id = f"task_{task_id}"
scheduler.add_job(
change_photo,
'date',
run_date=target_time,
args=[task['chat_ids'], task['photo_url'], message.chat.id, task_id],
id=scheduler_task_id
)
pending_tasks[task_id] = task
save_backup()
del user_state[message.from_user.id]
bot.reply_to(message, f"Задача запланирована на {target_time.strftime('%Y-%m-%d %H:%M')} с ID {task_id}.")
except ValueError:
bot.reply_to(message, "Неверный формат даты и времени. Пожалуйста, используйте формат YYYY-MM-DD HH:MM.")
def change_photo(chat_ids, file_url, user_id, task_id):
photo = requests.get(file_url).content
url = f'https://api.telegram.org/bot{BOT_TOKEN}/setChatPhoto'
success_count = 0
for chat_id in chat_ids:
response = requests.post(url, data={'chat_id': chat_id}, files={'photo': photo})
if response.status_code == 200:
success_count += 1
if success_count > 0:
bot.send_message(user_id, f"Фото успешно обновлено для {success_count} каналов!")
else:
bot.send_message(user_id, "Произошла ошибка при изменении аватарки. Проверьте ID каналов и попробуйте снова.")
if task_id in pending_tasks:
del pending_tasks[task_id]
scheduler.remove_job(f"task_{task_id}")
save_backup()
@bot.message_handler(commands=['delete'])
def delete_task(message):
try:
task_id = int(message.text.split()[1])
if task_id in pending_tasks:
scheduler.remove_job(f"task_{task_id}")
del pending_tasks[task_id]
save_backup()
bot.reply_to(message, f"Задача с ID {task_id} была удалена.")
else:
bot.reply_to(message, f"Задача с ID {task_id} не найдена.")
except (IndexError, ValueError):
bot.reply_to(message, "Пожалуйста, укажите корректный ID задачи.")
@bot.message_handler(commands=['restore'])
def restore_task(message):
restore_backup()
bot.reply_to(message, "Бэкап успешно восстановлен.")
@bot.message_handler(commands=['restore_old'])
def restore_old(message):
restore_old_backup()
bot.reply_to(message, "Старый бэкап успешно восстановлен.")
bot.polling()
4. Вместо «ТОКЕН СЮДА» вставляем токен, который мы ранее сохраняли в шаге 1.
5. Сохраняем bot.py.
6. Запускаем бот, вводя в консоль:
python bot.py
7. Тестируем работоспособность.
Демонстрация работы
Подводя итоги
Чтобы автоматически сменить аватарку во всей сетке пабликов, вовсе не обязательно кошмарить SMM-отдел или использовать непонятные админ-боты — ведь можно сделать свой. И теперь вы знаете как.