Отправка личных сообщений ВКонтакте задним числом

Примечание: это гостевой пост. Статья написана одним из читателей блога.

Здравствуйте. Сегодня мы научимся изменять дату отправки личных сообщений в социальной сети ВКонтакте. Хочу сказать сразу, что я не рассказываю что-то новое. В интернете полно аналогичных методов и я сам какие-то куски кода брал оттуда. Поэтому, не судите строго.

Итак, во первых, нам нужно иметь полный доступ к двум аккаунтам ВК. Например, первый аккаунт Ваш, а второй фэйковый. Во вторых, Вы должны иметь хоть какие-то азы в программировании. Но не волнуйтесь. Большую часть работы сделаю я сам. Все способы являются легкими и быстрыми в исполнении из-за «дыр» в данной социальной сети. Однако, нет гарантий, что каждый метод у Вас будет работать. Но хоть один точно сработает.

Начнем.

Первый способ. Метод вложенной переписки.

Алгоритм: создаем переписку, редактируем, меняем дату, отсылаем назад в виде пересылки.
Самым обычным способом, создаем переписку между двумя аккаунтами ВКонтакте. Подключаем каждый аккаунт к API. Для этого достаточно перейти по данной ссылке и разрешить сбор информации с аккаунта:

https://oauth.vk.com/authorize?client_id=3245775&redirect_uri=http://api.vk.com/blank.html&scope=messages&display=page&response_type=token

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

Далее, вытащим личные сообщения. Я буду использовать Python. Чтобы получить историю сообщений используем messages.getHistory [https://vk.com/dev/messages.getHistory]:

# -*- coding: utf-8 -*-

import codecs
import ConfigParser
import datetime
import json
import sys
import urllib2
from urllib import urlencode

import vk_auth

def _api(method, params, token):
    params.append(("access_token", token))
    url = "https://api.vk.com/method/%s?%s" % (method, urlencode(params))

    return json.loads(urllib2.urlopen(url).read())["response"]

# read config values

Config = ConfigParser.ConfigParser()
Config.read("config.ini")

login = Config.get("auth", "username")
password = Config.get("auth", "password")
messages_id = Config.get("messages", "chat_id")
messages_type = Config.get("messages", "chat_type")
app_id = Config.get("application", "app_id")

# some chat preparation

if messages_type == "interlocutor":
    is_chat = False
elif messages_type == "chat":
    is_chat = True
else:
    sys.exit("Messages type must be either interlocutor or chat.")

# auth to get token

try:
    token, user_id = vk_auth.auth(login, password, app_id, 'messages')
except RuntimeError:
    sys.exit("Incorrect login/password. Please check it.")

sys.stdout.write('Authorized vk\n')

# get some information about chat

selector = "chat_id" if is_chat else "uid"
messages = _api("messages.getHistory", [(selector, messages_id)], token)

out = codecs.open(
    'vk_exported_dialogue_%s%s.txt' % ('ui' if not is_chat else 'c', messages_id),
    "w+", "utf-8"
)

human_uids = [messages[1]["uid"]]

# Export uids from dialogue.
# Due to vk.api, start from 1.
for i in range(1, 100):
    try:
        if messages[i]["uid"] != human_uids[0]:
            human_uids.append(messages[i]["uid"])
    except IndexError:
        pass

# Export details from uids
human_details = _api(
    "users.get",
    [("uids", ','.join(str(v) for v in human_uids))],
    token
)

human_details_index = {}
for human_detail in human_details:
    human_details_index[human_detail["uid"]] = human_detail

def write_message(who, to_write):
    out.write(u'[{date}] {full_name}:\n {message} \n\n\n'.format(**{
        'date': datetime.datetime.fromtimestamp(
            int(to_write["date"])).strftime('%Y-%m-%d %H:%M:%S'),
            'full_name': '%s %s' % (
                human_details_index[who]["first_name"], human_details_index[who]["last_name"]),
                'message': to_write["body"].replace('', '\n')
    }))

mess = 0
max_part = 200 # Due to vk.api

cnt = messages[0]
sys.stdout.write("Count of messages: %s\n" % cnt)

while mess != cnt:
    # Try to retrieve info anyway

while True:
    try:
        message_part = _api(
            "messages.getHistory",
            [(selector, messages_id), ("offset", mess), ("count", max_part), ("rev", 1)],
            token
        )
    except Exception as e:
        sys.stderr.write('Got error %s, continue...\n' % e)
        continue
    break

try:
    for i in range(1, 201):
        write_message(message_part[i]["uid"], message_part[i])
except IndexError:
    break

result = mess + max_part
if result > cnt:
    result = (mess - cnt) + mess
    mess = result
    sys.stdout.write("Exported %s messages of %s\n" % (mess, cnt))

out.close()
sys.stdout.write(‘done!\n')

Далее, для простоты, поставим одну и ту же дату на все сообщения. Я не претендую на оптимизированный код и очевидно, что его можно ужать, но я хочу продемонстрировать как можно подробную работу парсера с подобными XML:

while (true) {
    doc = XDocument . Load("https://api.vk.com/method/messages.getHistory.xml?uid=12345&offset=" + m + "&count=200&access_token=" + token);
    foreach (XElement el in doc . Root . Elements()) {
        if (el . Name . ToString() == "message") {
            foreach (XElement el_msg in el . Elements()) {
                if (el_msg . Name == "date") {
                    foreach (XElement el_date in el_msg . Elements()) {
                        if (el_date . Name == " 1403320931") {
                            el_date . Name = '1403429673';
                            // то есть меняем дату на любую другую
                            k++;
                        }
                    }
                }
            }
        }
    }
    m += 200;
}

Я думаю, что основная идея здесь понятна. Осталось вернуть сообщения на сервер. Для этого нам понадобиться функция message.send(). Объект message должен обратиться к полю date, но сервер ВКонтакте устанавливает время у себя на сервере сам. Однако, если мы отправим сообщения как вложенную переписку — все получиться.

Наши письма должны имеют вложенную структуру, значит надо вызывать метод парсинга рекурсивно. Но в Java можно поступить проще. Всё, что находиться внутри цикла forEach, мы будем помещать в отдельный класс. В следствии, в методе accept будет происходить всё то, что было бы в теле цикла. В VkApiMessage есть поле fwd_messages. В нем будет хранится список из объектов пересланных сообщений VkApiMessage:

private static class MessageConsumer implements Consumer<VKApiMessage> {
    @Override public void accept(VKApiMessage message) {}
}

messages.stream().forEach(new MessageConsumer());
if (!message.fwd_messages.isEmpty()) {
    System.out.println("<div class='wall'>");
    message.fwd_messages.forEach(new MessageConsumer());
    System.out.println("</div>");
}

Далее, просто отправляем сообщения назад на сервер. Здесь, думаю, все очевидно. Аналогично, можно легко использовать поле “attachment” объекта message. [https://vk.com/dev/messages.send]

В общем, результат выглядит ничем не хуже оригинала в ВК:

Второй способ. Метод изменения GMT на сервере.

Алгоритм: меняем время на сервере, отправляем сообщения.

Второй метод несколько легче первого, так как нам не нужно старые(но измененные) сообщения возвращать назад на сервер (кстати, иногда, это не получается сделать из-за “глупого” сервера). Второй же способ работает более надежно.

Здесь нам не нужны готовые сообщения. Мы их создадим сами и перешлем на сервер, предварительно изменив GMT (часовой пояс). Для таких целей нам понадобиться абузоустойчивый VPS. Желательно на базе CentOS.

Сначала, меняем время и дату на сервере. Опять же, это можно сделать любым другим способом. Можно так:

# mv /etc/localtime /etc/localtime.bak
ln -s /usr/share/zoneinfo/Europe/USA /etc/localtime
# date MMDDhhmm
# date 03182015
# man date

Настраиваем наш сервер под данные сервера ВК:
IP:87.240.182.185
Browser: Mozilla/5.0 (compatible; vkShare; +http://vk.com/dev/Share)
Port: 36035
Host: srv185-182-240-87.vk.com

Не забываем про регистрация на API (см. Первый способ). Отправляем сообщения через уже знакомую функцию message.send().

Объект message, описывающий личное сообщение, возвращает поле “date” — дата отправки сообщения в формате unixtime. Обратите внимание на фразу “дата отправки”. Ничего не говорится про дату получения сообщения сервером ВКонтакте. [https://vk.com/dev/message]

Вот и все. Результат аналогичный первому. Все очень просто.

Третий способ. Сниффер трафика.

Алгоритм: качаем/покупаем сниффер, радуемся.
Самый простой метод. Но, к сожалению, методика зависит от качества программы, которую мы качаем из интернета.
Итак, можно просто попробовать скачать/купить что-то типо Charles, словить запрос, редактировать его (дату) и дублировать. Программа перехватывает пакет, меняет время отправки внутри пакета и отправляет контакту. Сервис ВК получает уже заданное unixtime сообщение. Таких снифферов в интернете куча. Но качественных и бесплатных найти сложно. Однако, они стоят не так дорого.

Как Вы уже поняли, отправить личное сообщение задним числом не составляет труда. Я не написал выше полностью все коды, а только самые необходимые и сложные. Дописать остальные, думаю, не составит труда. Большое спасибо.

4 комментария(ев) к записи “Отправка личных сообщений ВКонтакте задним числом”

  1. Артем:

    О, спасибо. Надо попробовать тоже самое на php сделать)

  2. Tim:

    Здравствуйте.Спасибо за статью
    Подскажите пожалуйста,а можно ли комментировать фото задним числом?Спасибо заранее

  3. ant-coding:

    Интересно. Спасибо

  4. Иннокентий:

    Добрый день. Извиняюсь за беспокойство) Хочу пригласть Вас на наш закрытый вебинар по партизанскому маркетингу, доступ для Вас и Вашей команды бесплатный по ссылке http://holabeeen.ru/

Оставить комментарий

(обязательно)

(обязательно)