Автопостинг c ВК в телеграм

Автопостинг c ВК в телеграм




Получаем записи

Получать новые записи будем при помощи VK API, при этом нам не нужно заморачиваться с созданием приложений, т.к. получить записи со стены можно даже без авторизации ВКонтакте (если в настройках сообщества не указано обратно).

Итак, сформируем ссылку, которая будет нам возвращать последние 10 записей от имени сообщества из группы C:\Music: https://api.vk.com/method/wall.get?domain=c.music&count=10&filter=owner&access_token=token

Что здесь что? domain - короткое имя сообщества. Если его нет, то меняем domain=xxx на owner_id=-yyy (обратите внимание на минус перед числом, это важно). count - число выводимых записей. Чем дольше пауза между проверками и чем чаще в сообществе появляются записи, тем большее число нужно выставить, но не более 100. filter=owner просит сервер выводить записи только от имени группы, полезно, если стена открыта, access_token - это токен пользователя, который «дёргает» API от имени одного из приложений.

Давайте теперь создадим файл bot.py, в котором зададим основные константы и импорты:

# -*- coding: utf-8 -*-
import timeimport eventletimport requestsimport loggingimport telebotfrom time import sleep
# Каждый раз получаем по 10 последних записей со стеныURL_VK = 'https://api.vk.com/method/wall.get?domain=c.music&count=10&filter=owner&access_token=Ваш_токен_VK'FILENAME_VK = 'last_known_id.txt'BASE_POST_URL = 'https://vk.com/wall-39270586_'
BOT_TOKEN = 'токен бота, постящего в канал'CHANNEL_NAME = '@канал'
bot = telebot.TeleBot(BOT_TOKEN)

Во-первых, не забудьте сделать нужного бота администратором канала, иначе фокус не удастся.

Во-вторых, обратите внимание, что в импортах появилась библиотека eventlet, она поможет нам избежать проблем при получении записей из ВК.

В-третьих, в указанный txt-файл будем записывать номер верхнего поста на момент проверки, я решил не заморачиваться с созданием key-value хранилищ, ради одного числа-то. В-четвёртых, в качестве параметра BASE_POST_URL указываем ссылку на любой пост из нашей группы без последнего числа.

Иногда ВК начинает дурить и не возвращает список постов за приемлемое время. В этом случае, нам нужно отвалиться по таймауту и пропустить проверку. Можно, конечно, попробовать ещё раз, но мы люди не настойчивые :)

def get_data(): timeout = eventlet.Timeout(10) try: feed = requests.get(URL_VK) return feed.json() except eventlet.timeout.Timeout: logging.warning('Got Timeout while retrieving VK JSON data. Cancelling...') return None finally: timeout.cancel()

Суть простая: получилось - возвращаем объект с постами.

Не получилось - возвращаем None. Теперь перейдем непосредственно к парсингу. Алгоритм будет такой:

1. Открываем файл, хранящий последний известный номер верхнего поста.

2. Если метод get_data() вернул объект с записями, начинаем проходить по нему со второго элемента, т.к. первый - какое-то неизвестное мне рандомное число.

3. Если номер поста меньше или равен последнему известному - завершаем обход.

4. Проверяем наличие закрепленного поста. Если таковой есть, то передаем функции отправки сообщений все записи, кроме закрепленной. Иначе - просто передаем все.

5. У каждой проверяемой записи забираем ID, подставляем рядом с BASE_POST_URL и получаем полный ID записи.

6. Отправляем его в канал.

7. Как только обход завершился, берем номер первой (второй, если первая - закрепленная) записи и записываем в файл поверх старого значения.

8. Засыпаем или завершаемся.

По поводу п.8: дополнительно предусмотрим в нашей программе два режима: в первом режиме скрипт постоянно работает, засыпая после каждой итерации на 4 минуты; во втором режиме скрипт просто завершает работу, что позволяет ставить его в планировщик cron.

В определении режима нам поможет константная переменная SINGLE_RUN, которую надо не забыть указать где-нибудь вверху.

def send_new_posts(items, last_id): for item in items: if item['id'] <= last_id: break link = '{!s}{!s}'.format(BASE_POST_URL, item['id']) bot.send_message(CHANNEL_NAME, link) # Спим секунду, чтобы избежать разного рода ошибок и ограничений (на всякий случай!) time.sleep(1) return

def check_new_posts_vk(): # Пишем текущее время начала logging.info('[VK] Started scanning for new posts') with open(FILENAME_VK, 'rt') as file: last_id = int(file.read()) if last_id is None: logging.error('Could not read from storage. Skipped iteration.') return logging.info('Last ID (VK) = {!s}'.format(last_id)) try: feed = get_data() # Если ранее случился таймаут, пропускаем итерацию. Если всё нормально - парсим посты. if feed is not None: entries = feed['response'][1:] try: # Если пост был закреплен, пропускаем его tmp = entries[0]['is_pinned'] # И запускаем отправку сообщений send_new_posts(entries[1:], last_id) except KeyError: send_new_posts(entries, last_id) # Записываем новый last_id в файл. with open(FILENAME_VK, 'wt') as file: try: tmp = entries[0]['is_pinned'] # Если первый пост - закрепленный, то сохраняем ID второго file.write(str(entries[1]['id'])) logging.info('New last_id (VK) is {!s}'.format((entries[1]['id']))) except KeyError: file.write(str(entries[0]['id'])) logging.info('New last_id (VK) is {!s}'.format((entries[0]['id']))) except Exception as ex: logging.error('Exception of type {!s} in check_new_post(): {!s}'.format(type(ex).__name__, str(ex))) pass logging.info('[VK] Finished scanning') return

Осталось дело за малым - написать логику запуска всего процесса и инициализировать логгер, который будет писать в текстовый файлик обо всех событиях в жизни бота:

if __name__ == '__main__': # Избавляемся от спама в логах от библиотеки requests logging.getLogger('requests').setLevel(logging.CRITICAL) # Настраиваем наш логгер logging.basicConfig(format='[%(asctime)s] %(filename)s:%(lineno)d %(levelname)s - %(message)s', level=logging.INFO, filename='bot_log.log', datefmt='%d.%m.%Y %H:%M:%S') if not SINGLE_RUN: while True: check_new_posts_vk() # Пауза в 4 минуты перед повторной проверкой logging.info('[App] Script went to sleep.') time.sleep(60 * 4) else: check_new_posts_vk() logging.info('[App] Script exited.\n')

Перед запуском бота, создадим вручную файл last_known_id.txt и впишем в него один из последних числовых ID, в моём случае это было чудесное число 1893. После включения бота, в зависимости от значения SINGLE_RUN, он будет либо постоянно работать, проверяя каждые 4 минуты на наличие новых постов, либо завершится после окончания первой проверки. Для себя я выбрал второй вариант, добавив скрипт в cron.

На сегодня всё, теперь вы можете легко и просто настроить автопостинг записей практически из любой группы ВК в свой канал.




Report Page