Как пропускать и одновременно присутствовать на лекции или «за меня учится Python бот»
Немного вводной
Я имею самый обычный рабочий график: 5/2, 8ч/день. В настоящий момент удаленно учусь в аспирантуре (коронавирус, все дела) и единственный день, когда я могу вдоволь почувствовать себя человеком-соседом и поделать что-то по дому, — это суббота. Как вы понимаете, здесь что-то пошло не так и вместо обещанных будничных пар, которые должны были проходить по вечерам после работы, нам утрамбовали всю субботу. Но дела ведь себя не переделают, поэтому решено было написать на python простого бота-кликера, который мог бы:
Заходить на пару по скормленной извне ссылки;
Стартовать запись экрана со звуком;
Ожидать окончания пары;
Выключать запись и выходить с пары.
Таким образом я и на паре присутствую, и домашними делами занимаюсь. А еще могу просмотреть лекции с удвоенным ускорением и в любое удобное для меня время. Придумано — сделано. Может быть мой опыт поможет кому-то решить аналогичную «проблему».
В ВУЗе мы используем Microsoft Teams и для того, чтобы попасть на лекцию требуется перейти по ссылке, которую за несколько часов до старта этой самой лекции, высылает староста. На мой взгляд, самым простым решением является бот-кликер, наблюдающий за экраном и тыкающий по кнопкам. А также было бы неплохо стартовать все это удаленно и желательно с телефона.
Кликер
Начал я с питонячей библиотеки pyautogui. Если кратенько, она умеет перехватывать управление клавомышью, тыкать кнопки, вводить тексты, делать снимки экрана, искать окна приложений и взаимодействовать с ними и многое-многое другое. Для своего кликера я выстроил следующую логику: я заранее сохраняю картинку нужной кнопки, pyautogui ожидает ее появления на экране, когда он находит кнопку, то тыкает по ней.
Функция ожидания:
def find_element(element):
"""Ожидание появления элемента на экране"""
sleep(1)
r = None
count = 100
while r is None:
count -= 1
r = pyautogui.locateOnScreen(dir_true_files + element, confidence=0.7)
print(count)
if count == 0:
print(element + ' не найден!')
break
else:
if r != None:
print(element + ' нашОлся!')
return r
else:
continue
Здесь pyautogui ищет на экране в пределах счетчика »100» совпадений в 70% с заранее сохраненной картинкой.
Далее функции для взаимодействия с приложением и создание скриншота экрана:
def press_key(key):
"""Нажатие одной клавиши"""
sleep(1)
pyautogui.press(key)
def write_text(text):
"""Ввод текста"""
sleep(1)
pyautogui.write(text, interval=0.2)
def create_screenshot(screens_directory, name):
"""Создание скриншота"""
sleep(1)
im1 = pyautogui.screenshot()
screenshot = im1.save(screens_directory + name)
return screenshot
Здесь можно расширить функционал бота, добавив возможность сравнивать время с временем системы, искать поле для ввода текста, делать его активным и вводить приветствие в зависимости от времени суток — с 09.00 до 12.00 «Доброе утро!», с 12.00 до 17.00 «Добрый день!», с 17.00 до 21.00 «Добрый вечер!». Можно сделать список, из которого раз в n минут бот будет забирать рандомное значение и передавать его функции, отправляющей текст — [«Где экшон?», «Лучше бы в армию пошел», «Отличная мысль!», «Я не расслышал, повторите», «Хочу есть»] и т.д. Но нас на парах отмечали по факту присутствия и нахождению в онлайне — поэтому я не стал с этим заморачиваться.
Для записи экрана по работе я использую заранее настроенный OBS. Здесь его функциональности хватит с головой. Да, я знаю про ffmpeg, но подружиться с ним у меня так и не получилось. В итоге для старта записи я использую поиск окна по заголовку и выношу приложение «на передний план». Эти манипуляции нужны для того, чтобы, имея активное окно OBS, нажать через pyautogui хоткей для старта и окончания записи.
def active_window(title):
"""Найти окно по заголовку и сделать его активным"""
sleep(1)
app = Application().connect(title_re=title, backend='win32')
app.window(title_re=title).set_focus()
sleep(3)
Кнопки сохранены, шаги в MS Teams боту прописаны, все отлажено. Как запускаться?
Чат бот
До этого я много игрался с чат-ботами, поэтому мой выбор пал именно на них. Самым простым решением имхо здесь является Телеграмм с его @BotFather. Создаем бота, используя telebot. Я захотел отрезать от него все, что можно и нельзя, оставив ему один единственный функционал — получать ссылку и заходит на пару.
А как скармливать ему ссылку и открывать ее в MS Teams? Тут я вспомнил про давно задвинутый в дальний ящик чудесный selenium. Расчехлил и инициализировал хромдрайвер с нажатием на кнопку «Open Microsoft Teams»:
def site_desktop(url):
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(executable_path=driver_chrome, options=chrome_options)
driver.set_window_size(1920, 1080)
driver.get(url)
find_and_click('/open_team.png')
sleep(3)
driver.close()
Можно заморочиться с распознаванием сообщений в другом чате, откуда к нам поступаю ссылки на лекции, привязаться ко времени (первая пара в 9.00, вторая в 10.40 и т.д.) и в нужное время автоматом забрать сообщение, начинающееся с «https://», но я предпочитаю контролировать этот процесс и копипастить ссылки лично.
В итоге, когда я отправляю боту url, он делает следующее:
Открывает selenium chrome driver с присланной сслыкой;
Кликает по кнопку «Открыть Microsoft Teams»;
Открывает MS Teams, проверяет статус микрофона — включен/выключен. Если выключен, отключает;
Подключается к паре;
Делает активным окно OBS, нажимает хоткей для записи экрана, отправляет скриншот рабочего стола в Телеграмм;
Ждет 90 минут;
Отправляет сообщение в Телеграмм о том, что пара закончилась;
Вновь делает активным окно OBS, выключает запись, кликает на кнопку для выхода с пары;
Отправляет финальное сообщение о завершении цикла. Ждет новой ссылки.
bot = telebot.TeleBot(bot_token)
@bot.message_handler(content_types=['text'])
def get_text_messages(message):
if message.text:
try:
bot.send_message(message.chat.id, text='Стартую вход на пару')
txt = message.text
site_desktop(txt)
find_and_click('/disable_micro.png')
if find_element('/micro_status_on.png'):
find_and_click('/disable_micro.png')
find_and_click('/apply_study.png')
active_window('OBS')
press_key('f7')
sleep(3)
create_screenshot(dir_to_save_fail_screen, name_fail_screen)
bot.send_photo(message.chat.id, open(dir_to_save_fail_screen + name_fail_screen, 'rb'))
bot.send_message(message.chat.id, text='Зашел на пару, включил запись экрана')
# Длительность пары 90 мин == 5400
sleep(5400)
bot.send_message(message.chat.id, text='Пара подошла к концу, выключаю запись экрана, выхожу')
active_window('OBS')
press_key('f8')
sleep(3)
find_and_click('/exit.png')
bot.send_message(message.chat.id, text='Успешное завершение')
except Exception as e:
bot.send_message(message.chat.id, text='ОШИБКА + ' + str(e))
bot.polling()
Не очень хорошо, что 90 минут пары я обернул в простой time.sleep, но этого, как показала практика, более, чем достаточно. Также в OBS можно настроить запись видео только окна MS Teams в небольшом разрешении с фиксированной частотой кадров и пожатым звуком, дабы уменьшить итоговый размер видоса. И затем слать его себе в Телеграмм.
А вот так это выглядит для меня, который в этот момент вполне может ехать куда-нибудь за стройматериалами (ссылки замазюкал, время сократил):
З.Ы. Я ни в коем случае не призываю пропускать таким образом уроки/пары — ученье свет! Но, увы, отечественное образование частично состоит из архаичных лекций времен советского союза, выхлоп от которых… чуть больше, чем никакой. И такой робот позволит хотя бы частично разгрузиться и переключиться на что-то более полезное и продуктивное.