Nikovit <Путешествия и разработка>

Пишем бота пересылки сообщений из VK в Telegram на Python

 
aaf44db7572a2fcafef3ae5ea5c20bb2.png

Очень часто бывает что у вас группа в vk.com и вам бы хотелось завести канал в телеграмм но постить вручную сообщения в два источника не очень удобно. Ниже мы рассмотрим бота для пересылки сообщений из вконтакте в телеграм.



Регистрируем бота в Telegram

Добавляем в список контактов @BotFather

Отправляем ему команду:
/newbot


Придумываем имя боту
Alright, a new bot. How are we going to call it? Please choose a name for your bot.

Придумываем username, должно заканчиваться обязательно на 'bot'
Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.

Все, бот зарегистрирован, самое важное это последние сообщение с токеном бота, ни кому не сообщайте его т.к. зная токен можно полностью управлять ботом.
Done! Congratulations on your new bot. You will find it at t.me/XXXXbot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.


Use this token to access the HTTP API:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


For a description of the Bot API, see this page: https://core.telegram.org/bots/api

Пишем бота Telegram

Создаем в нашем проекте файл settings.ini и добавляем в него настройки подключения нашего будущего бота пересылки сообщений из vk.
[Settings]
last_id = 123
include_link = true
preview_link = false
[VK]
login = login
password = pass
domain = oldlentach
count = 30
[Telegram]
bot_token = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
channel = @test
где:
last_id - последний ID сообщения ленты вконтакте, можно оставить 123
include_link - вставлять ли ссылки
preview_link - использовать ли предпросмотр ссылок

login - ваш логин аккаунта вконтакте
password - ваш пароль аккаунта вконтакте
domain - группа или id сообщества вконтакте
count - количество последних забираемых сообщений сообщество (ограничение API вконтакте, максимум 100 за один запрос)

bot_token - токен бота полученный выше
channel - название канала в телеграмме для публикации ботом, важно помнить что для публикации сообщений в канале ботом, его нужно добавить администратором канала


Нам понадобятся библиотеки:

vk_api
pyTelegramBotAPI

configparser и logging из стандартной библиотеки Python, и конечно сам Python, на момент написания статьи у меня была версия 3.6.2

Устанавливаем библиотеки через pip желательно в virtualenv, в консоли набираем:
pip install vk_api
pip install pyTelegramBotAPI

Создаем файл vk_to_tg.py и начинаем в него писать.

Импортируем модули:
import os
import sys
import vk_api
import telebot
import configparser
import logging
from telebot.types import InputMediaPhoto

Считываем данные из settings.ini
config_path = os.path.join(sys.path[0], 'settings.ini')
config = configparser.ConfigParser()
config.read(config_path)
LOGIN = config.get('VK', 'LOGIN')
PASSWORD = config.get('VK', 'PASSWORD')
DOMAIN = config.get('VK', 'DOMAIN')
COUNT = config.get('VK', 'COUNT')
BOT_TOKEN = config.get('Telegram', 'BOT_TOKEN')
CHANNEL = config.get('Telegram', 'CHANNEL')
INCLUDE_LINK = config.getboolean('Settings', 'INCLUDE_LINK')
PREVIEW_LINK = config.getboolean('Settings', 'PREVIEW_LINK')

Инициализируем телеграмм бота vk телеграмм бота
bot = telebot.TeleBot(BOT_TOKEN)

Получаем данные из vk.com для последующей обработки
# Получаем данные из vk.com
def get_data(domain_vk, count_vk):
    vk_session = vk_api.VkApi(LOGIN, PASSWORD)
    vk_session.auth()
    vk = vk_session.get_api()
    # Используем метод wall.get из документации по API vk.com
    response = vk.wall.get(domain=domain_vk, count=count_vk)
    return response

Проверяем и извлекаем данные по условиям перед отправкой
# Проверяем данные по условиям перед отправкой
def check_posts_vk():
    response = get_data(DOMAIN, COUNT)
    response = reversed(response['items'])

    for post in response:

        # Читаем последний извесный id из файла
        id = config.get('Settings', 'LAST_ID')

        # Сравниваем id, пропускаем уже опубликованные
        if int(post['id']) <= int(id):
            continue

        print('-----------------------------------------')
        print(post)

        # Текст
        text = post['text']

        # Проверяем есть ли что то прикрепленное к посту
        images = []
        links = []
        attachments = []
        if 'attachments' in post:
            attach = post['attachments']
            for add in attach:
                if add['type'] == 'photo':
                    img = add['photo']
                    images.append(img)
                elif add['type'] == 'audio':
                    # Все аудиозаписи заблокированы везде, кроме оффицальных приложений
                    continue
                elif add['type'] == 'video':
                    video = add['video']
                    if 'player' in video:
                        links.append(video['player'])
                else:
                    for (key, value) in add.items():
                        if key != 'type' and 'url' in value:
                            attachments.append(value['url'])

        if INCLUDE_LINK:
            post_url = "https://vk.com/" + DOMAIN + "?w=wall" + \
                str(post['owner_id']) + '_' + str(post['id'])
            links.insert(0, post_url)
        text = '\n'.join([text] + links)
        send_posts_text(text)

        if len(images) > 0:
            image_urls = list(map(lambda img: max(
                img["sizes"], key=lambda size: size["type"])["url"], images))
            print(image_urls)
            bot.send_media_group(CHANNEL, map(
                lambda url: InputMediaPhoto(url), image_urls))

        # Проверяем есть ли репост другой записи
        if 'copy_history' in post:
            copy_history = post['copy_history']
            copy_history = copy_history[0]
            print('--copy_history--')
            print(copy_history)
            text = copy_history['text']
            send_posts_text(text)

            # Проверяем есть ли у репоста прикрепленное сообщение
            if 'attachments' in copy_history:
                copy_add = copy_history['attachments']
                copy_add = copy_add[0]

                # Если это ссылка
                if copy_add['type'] == 'link':
                    link = copy_add['link']
                    text = link['title']
                    send_posts_text(text)
                    img = link['photo']
                    send_posts_img(img)
                    url = link['url']
                    send_posts_text(url)

                # Если это картинки
                if copy_add['type'] == 'photo':
                    attach = copy_history['attachments']
                    for img in attach:
                        image = img['photo']
                        send_posts_img(image)

        # Записываем id в файл
        config.set('Settings', 'LAST_ID', str(post['id']))
        with open(config_path, "w") as config_file:
            config.write(config_file)

Отправляем посты в телеграмм

Если это текст:
# Текст
def send_posts_text(text):
    if text == '':
        print('no text')
    else:
        # В телеграмме есть ограничения на длину одного сообщения в 4091 символ, разбиваем длинные сообщения на части
        for msg in split(text):
            bot.send_message(CHANNEL, msg, disable_web_page_preview=not PREVIEW_LINK)

Если сообщение длинное то разбиваем его на несколько:
def split(text):
    if len(text) >= max_message_length:
        last_index = max(
            map(lambda separator: text.rfind(separator, 0, max_message_length), message_breakers))
        good_part = text[:last_index]
        bad_part = text[last_index + 1:]
        return [good_part] + split(bad_part)
    else:
        return [text]


Если это изображение:
# Изображения
def send_posts_img(img):
    # Находим картинку с максимальным качеством
    url = max(img["sizes"], key=lambda size: size["type"])["url"]
    bot.send_photo(CHANNEL, url)

И в самом конце инициализируем наш скрипт:
if __name__ == '__main__':
    check_posts_vk()

Репозиторий бота на github.com

Все, удачного Вам написания собственных Telegram ботов на Python


0
Артур
07.10.2018 05:36:05
Два дня пытался но так и не смог отправить фото со стены вк в телеграм. Текст отправляет нормально но вот картинки никак. Пишет ошибку 400. Помогите пожалуйста понять что не так
0
09.10.2018 15:39:22
Да api действительно изменился, обновил статью, вот рабочая конструкция для отправки фото, обновил статью:
# Отправляем изображения
def send_posts_img(img):
    # Находим картинку с максимальным качеством
    if 'photo_2560' in img:
        print(img['photo_2560'])
        bot.send_photo(CHANNEL, img['photo_2560'])
        logging.info('Image: ' + img['photo_2560'])
    else:
        if 'photo_1280' in img:
            print(img['photo_1280'])
            bot.send_photo(CHANNEL, img['photo_1280'])
            logging.info('Image: ' + img['photo_1280'])
        else:
            if 'photo_807' in img:
                print(img['photo_807'])
                bot.send_photo(CHANNEL, img['photo_807'])
                logging.info('Image: ' + img['photo_807'])
            else:
                if 'photo_604' in img:
                    print(img['photo_604'])
                    bot.send_photo(CHANNEL, img['photo_604'])
                    logging.info('Image: ' + img['photo_604'])

0
09.10.2018 15:44:57
Пришли свой скрипт если не получиться, я посмотрю в чем может быть проблема)))
0
Сергей
15.01.2019 06:41:07
Привет, у меня тоже не работает фото.


# Отправляем изображения
def send_posts_img(img):
   # Находим картинку с максимальным качеством
   if 'photo_2560' in img:
       print(img['photo_2560'])
       bot.send_photo(CHANNEL, img['photo_2560'])
       logging.info('Image: ' + img['photo_2560'])
   else:
       if 'photo_1280' in img:
           print(img['photo_1280'])
           bot.send_photo(CHANNEL, img['photo_1280'])
           logging.info('Image: ' + img['photo_1280'])
       else:
           if 'photo_807' in img:
               print(img['photo_807'])
               bot.send_photo(CHANNEL, img['photo_807'])
               logging.info('Image: ' + img['photo_807'])
           else:
               if 'photo_604' in img:
                   print(img['photo_604'])
                   bot.send_photo(CHANNEL, img['photo_604'])
                   logging.info('Image: ' + img['photo_604'])
0
Артур
17.10.2018 16:39:37
Наконец то разобрался с выбором нужного фото. Теперь возник вопрос как запустить цикл чтобы он сам проверял новые сообщения
0
20.10.2018 00:11:03
я делал не цикл а просто запускал скрипт раз в час по cron в линукс, или можно воспользоваться "планировщиком заданий" если платформа виндовс
0
ГУРЫЧ
28.11.2018 23:41:11
if __name__ == '__main__':
    check_posts_vk()
    while True:
        check_posts_vk()
        sleep(30)
0
Сергей
18.10.2018 13:13:47
Скажите, а теоритически возможно организовать пересылку сообщений из телеграм чата в ватцап? Гуглил - не нашёл вразумительного ответа
0
20.10.2018 00:12:51
Теоретически можно, но на практике сложно, т.к. у вотцапа закрытый протокол и соответственно нет открытого API.
0
Иван
03.11.2018 18:51:56
Не работает отправка картинок, с текстом все гуд, и что на счет видео?
0
04.11.2018 23:46:22
Для видео надо парсер написать, все руки не доходят, следите за обновлениями на гитхабе https://github.com/Nikovit/bot_vk_to_telegram
0
Макс
09.01.2019 23:23:14
Подскажите пожалуйста, а можно считать последнее сообщение с чата и переслать к примеру на вебсокет. Хочу считывать телеграмм сигналы и отправлять по веб сокету в бота. в какую сторону копать?
0
22.01.2019 11:06:20
Так а вы код немного измените, не в ВК отправляете сообщение а в вэб сокет.
0
Джеки Чан
01.03.2019 16:07:34
спосибо!
0
Snek
12.03.2019 07:10:18
а этот бот может пересылать сообщения из чужой группы Вк?
0
12.03.2019 11:57:34
Да, конечно может!
0
Andrii
23.03.2019 23:00:04
если в посте 1 картинка + текст, то можно отправить в телеграм записью вида картинка + подпись снизу, а не разделять.
# Текст
text = post['text']

# если прикреплена 1 картинка
if len(images) == 1:
    url = max(img["sizes"], key=lambda size: size["type"])["url"]
    # отправляем
    bot.send_photo(CHANNEL, url, text)
    # print(url)
0
Герман
17.04.2019 01:28:15
Ребят выручайте, у меня не работает.
Я получил ключ доступа и добавил его в .ini туда же загнал все данные, включая и токен бота и все равно не работает
0
Александр
27.06.2019 22:24:20
А как быть с двух-фактурной авторизацией?
0
30.06.2019 13:41:28
С двух факторной авторизацией тоже должно работать, она ведь по факту и не применяется, доступ бота через API ключ.
0
Олег
03.07.2019 04:12:10
Здравствуйте. А как пересылать сообщения из чужих каналов и ботов в свой бот?
0
Олег
03.07.2019 04:14:13
Я имею в виду телеграм каналы и боты, из телеграм в телеграм
0
pythonerboy
19.07.2019 14:23:30
А как сделать, чтобы отправлял только картинку, текст и другое не брал из вк?
Какую строчку удалить в коде?
0
илья
27.08.2019 03:51:50
if __name__ == '__main__':
   check_posts_vk()
   while True:
       check_posts_vk()
0
илья
27.08.2019 03:52:08
так плохо делать ?
0
27.08.2019 16:17:07
Так теоретически вас vk api может заблокировать, у них есть ограничения по частоте запросов 20 запросов в секунду и по количеству запросов которое они держат в секрете, подробнее тут https://vk.com/dev.php?f=3.1.%20%D0%A7%D0%B0%D1%81%D1%82%D0%BE%D1%82%D0%BD%D1%8B%D0%B5%20%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D1%8F&method=api_requests

Правильно сделать как писали выше:
if __name__ == '__main__':
    check_posts_vk()
    while True:
        check_posts_vk()
        sleep(30)
0
илья
27.08.2019 03:59:31
а как сделать, чтобы картинка из статьи отображалась ?
0
27.08.2019 16:20:21
Для статей VK нужно написать свой метод.
0
alex
04.09.2019 20:24:13
Здравствуйте, пишет:

Traceback (most recent call last):
 File "C:\Users\sssst\Desktop\vk_to_tg.py", line 162, in <module>
   check_posts_vk()
 File "C:\Users\sssst\Desktop\vk_to_tg.py", line 84, in check_posts_vk
   send_posts_text(text)
 File "C:\Users\sssst\Desktop\vk_to_tg.py", line 138, in send_posts_text
   for msg in split(text):
 File "C:\Users\sssst\Desktop\vk_to_tg.py", line 143, in split
   if len(text) >= max_message_length:
NameError: name 'max_message_length' is not defined
0
06.09.2019 11:14:09
Не может найти переменную max_message_length, попробуйте скачать скрипт из репозитория гитхаб, подставить свои данные в settings.ini и запустите.

https://github.com/Nikovit/bot_vk_to_telegram
0
Дмитрий
19.09.2019 17:11:21
ошибка:  from telebot.types import InputMediaPhoto ModuleNotFoundError: No module named 'telebot.types'
сам модуль telebot 0.0.3 установлен
0
20.09.2019 13:58:12
А в самом начале скрипта библиотеки импортированы?

import telebot
и
from telebot.types import InputMediaPhoto

и обратите внимание на окружение в котором работаете, если работаете в virtualenv то следует переключиться на него.
0
Дмитрий
25.10.2019 22:53:54
Да, конечно импорт есть. Как-то удалось запустить, не понял, что это было :)

Можете подсказать по одной проблеме? Я не настолько силен в питоне, просто не могу сообразить, как собрать массив.
Идея такая: есть несколько фото, нужно отправить альбомом. Для отправки альбома использую bot.send_media_group(ch_id, media), но хочется отправить альбом с текстом.
Одно фото с текстом я могу отправить через bot.send_photo(ch_id, url, caption='text'), а чтобы это сделать с альбомом, надо к любому фото (например к первому) добавить caption. И вот как это сделать, я никак не могу сообразить. Точнее, не могу собрать media.
0
Илья
07.10.2019 01:20:20
Добрый день, Виталий!
Большое спасибо за опубликованную статью и код.

Я понимаю, что аудиозаписи заблокированы, но есть ли возможность извлечь названия музыкальный треков из поста в вконтакте, чтобы публиковать названия и исполнителя песни в чате телеграм?

Я хочу на следующем этапе публиковать их с вызовом другого бота (@vkm4bot)

Илья
0
Андрей
13.10.2019 19:38:19
здравствуйте. Хотелось бы уточнить, можно ли заделать бота, который будет пересылать информацию с какого-либо сайта в канал в телеграм. Готов к сотрудничеству со знающим свое дело человеком. Пишите в тг @gans_kelt
0
14.10.2019 20:58:59
Можно парсить сайт и вытягивать от туда информацию.
0
Руслан
19.10.2019 21:13:33
А если нужно получать только оповещения о новых сообщения в ВК в виде какого нить текста (не нужен сам текст сообщения или картинка). Эт упрощает задачу?
1
Johnny
04.12.2019 22:00:41
Низкий поклон Автору, всё завелось на ура. Прибольшое спасибо.

Не подскажете как бы сделать, чтобы альбомы фото отправлялись в одном сообщении? Может есть допиленный скрипт :)
Да и вообще копию поста бы делать. И фото и текст чтоб в одной мессаге... А то мало ли...

Спасибо ещё раз.
0
Виталий
23.05.2020 00:47:24
Поддержу благодарность и просьбу выложить вариант,в котором пост с 1 картинкой постится как картинка+текст в одном сообщении.
1
N1
05.12.2019 13:27:45
Большое человеческое спасибо! Бот отлично работает, запихнул в cron.
0
No. 3
06.03.2020 19:42:15
Добрый день. Пытаюсь запустить бота — он ничего не копирует из паблика,а сам через 3 секунды выключается. В консоли не пишет ничего? Почему это?
0
No. 3
06.03.2020 19:54:24
Всё, с проблемой разобрался.
Но мне нужно, чтоб бот работал постоянно, и всегда парсил новые посты из группы. Как подобное можно реализовать?
0
07.03.2020 23:03:02
Ответ на этот вопрос давали в комментариях чуть выше, тут два способа:
1. запускать скрипт через определенное время по расписанию средствами операционной системы.
2. добавить в конец скрипта конструкцию:
if __name__ == '__main__':
    check_posts_vk()
    while True:
        check_posts_vk()
        sleep(30)
0
Константин
06.05.2020 22:57:28
Спасибо большое за Ваш труд! Долго не мог найти нормально описанный процесс создания подобного бота для Телеграм. А мне он очень нужен для контрольной работы. Да и в целом тема очень интересная. Очень меня выручили! С меня донат)
0
07.05.2020 09:51:47
Спасибо, донат пришел  ;)  
0
Даймоник
26.07.2020 05:40:57
При запуске работает, но через какое-то время выдаёт такое:
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "vk_to_telegramm.py", line 206, in <module>
    check_posts_vk()
  File "vk_to_telegramm.py", line 126, in check_posts_vk
    lambda url: InputMediaPhoto(url), image_urls))
  File "/home/onilyxe/.local/lib/python3.7/site-packages/telebot/__init__.py", line 858, in send_media_group
    self.token, chat_id, media, disable_notification, reply_to_message_id, timeout)
  File "/home/onilyxe/.local/lib/python3.7/site-packages/telebot/apihelper.py", line 334, in send_media_group
    files=files if files else None)
  File "/home/onilyxe/.local/lib/python3.7/site-packages/telebot/apihelper.py", line 67, in _make_request
    timeout=(connect_timeout, read_timeout), proxies=proxy)
  File "/home/onilyxe/.local/lib/python3.7/site-packages/requests/sessions.py", line 530, in request
    resp = self.send(prep, **send_kwargs)
  File "/home/onilyxe/.local/lib/python3.7/site-packages/requests/sessions.py", line 643, in send
    r = adapter.send(request, **kwargs)
  File "/home/onilyxe/.local/lib/python3.7/site-packages/requests/adapters.py", line 498, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))
0
Даймоник
26.07.2020 05:52:07
Выше написано
File "vk_to_telegramm.py", line 206, in <module>
   check_posts_vk()
Тут у меня находиться
203 if __name__ == '__main__':
204     check_posts_vk()
205     while True:
206         check_posts_vk()
207         sleep(1500)
И так же
File "vk_to_telegramm.py", line 126, in check_posts_vk
   lambda url: InputMediaPhoto(url), image_urls))
тут у меня
121        if len(images) > 0:
122            image_urls = list(map(lambda img: max(
123                img["sizes"], key=lambda size: size["type"])["url"], images))
124            print(image_urls)
125            bot.send_media_group(CHANNEL, map(
126                lambda url: InputMediaPhoto(url), image_urls))