Граємося з VK API та Python, частина 4: пишемо повідомлення прямо з терміналу (і не тільки)

13 червня 2015 22:36 OlegWock 1539 0

Добридень, пані та панове. Ви, напевно, вже здогадалися, що ми знову будемо творити щось цікаве за допомогою Python та VK API? Сьогодні ми напишемо невеличкий скрипт, що буде зберігати деяку інформацію з аккаунту. Якщо точніше, то лайкнуті пости та фото, список груп і повідомлення. Також за допомогою його можна буде писати та читати повідомлення. Ви спитаєте навіщо? А тому що ми будемо зберігати їх з чужого аккаунту. Розкажу детальніше. Іноді буває потрібно (читай цікаво) поглянути на чиюсь сторінку зсередини: почитати повідомлення, подивитися, що людина лайкала. Звісно, можна якось роздобути логін та пароль та зайти на сайті, але (скажу по своєму досвіду) навіть самі наївні "дівчата з 5-Б класу" не дадуть його добровільно. А от з access_token все куди простіше, можна під приводом накручування лайків чи чого іншого підсунути свій додаток і потім отримати access_token. Але ж біда, з ним на сайт не зайдеш. От тут у пригоді і стане наша програма.

Зміст

Невеличкий фокус. Отримати токен зі сторінки можна за цим посиланням. Просто скопіюйте його, в поле client_id вставте id свого додатку, а в scope потрібні дозволи.

Ну а тепер давайте писати код. Крім модулю vk ми ще будемо використовувати colorama для кольорового тексту в терміналі. Можливо, на стандартній віндовсній консолі не буде працювати, я точно не знаю, бо навіть під віндою (зараз на лінуксі) використовував ConEmu, чого і вам бажаю. Ні, серйозно, спробуйте, крута штука. Також для зручнішої роботи з кольорами та форматуванням тексту ми напишемо свою надбудову над str.format.

from colorama import Fore, Style, init
from pprint import pformat
import vk
from time import  sleep
import os, sys
formating = {"green": Fore.GREEN,
             "red": Fore.RED,
             "white": Fore.WHITE,
             "blue": Fore.BLUE,
             "cyan": Fore.CYAN,
             "yellow": Fore.YELLOW,
             "bold": Style.BRIGHT,
             "reset": Style.RESET_ALL}
mes_statuses = {1: "{bold}{blue}прочитане{reset} ==".format(**formating),
                0: "{bold}{red}непрочитане{reset} ==".format(**formating)}
term_size = 109 # ширина вікна терміналу
init() # запускаємо colorama
if sys.platform == 'win32': # бо смайлики викликають ексепшн
    os.system('chcp 65001')
# Шаблони для автора повідомлення та автора пересланого повідомлення 
MESSAGE_AUTHOR = "{cyan}{first_name} {last_name}{reset} == {green}(https://vk.com/id{bold}{red}{id}{reset}{green}){reset} == "
FWD_MESSAGE_AUTHOR = ">> {yellow}{first_name} {last_name}{reset} == {green}(https://vk.com/id{bold}{red}{id}{reset}{green}){reset}"
def format(str, *args, **kw): # власні милиці, без них ніяк
    buf = kw.copy() if kw else {}
    buf.update(formating)
    return str.format(*args, **buf)

Як бачите, наш format робить теж саме що й str.format, тільки автоматично вставляє кольори, тобто можна писати так:

format("{red}Codeguida{reset}")
format("{green} Oleg is {}{reset}", "raccoon")

Зауважте, що теги потрібно закривати, один раз, тобто після {reset} вивід стає звичайного кольору та стилю.

Ох, навіть не знаю з чого зараз почати. Давайте з самого цікавого, з повідомлень. Щоб не писати один і той самий код декілька разів ми напишемо допоміжну функцію для відображення повідомлень.

def printMessages(messages, outfile=sys.stdout):
    for mes in messages:
        if "from_id" in mes:
            user = vkapi.users.get(user_ids=mes['from_id'])[0]
        else:
            user = vkapi.users.get(user_ids=mes['user_id'])[0]
        print(format(MESSAGE_AUTHOR, **user), end='', file=outfile)
        print(mes_statuses[mes['read_state']], file=outfile)
        print(mes['body'], file=outfile)
        if 'fwd_messages' in mes: # перевірка чи прикріплені до повідомлення переслані повідомлення та їх друк
            for fwd_mes in mes['fwd_messages']:
                fwd_user = vkapi.users.get(user_ids=fwd_mes['user_id'])[0]
                print(format(FWD_MESSAGE_AUTHOR, **fwd_user), file=outfile)
                print(">>> {body}".format(**fwd_mes), file=outfile)
                sleep(0.25)
        if 'attachments' in mes:
            print(format("{yellow}{bold}Attachments:{reset}"), file=outfile)
            for a in mes['attachments']:
                print("=== Type: {type}".format(**a), file=outfile)
                print(pformat(a), file=outfile)
        print(format("{bold}{0}{reset}", "="*term_size))
        sleep(0.25)

Як бачите, я особливо не переймався відображенням прикріплень. Все тому що вони мають дуже багато видів і кожен слід обробляти по різному, тому ми просто відображаємо словник, що характеризує це прикріплення (який ми отримали з сайту), в зручному форматі.

Тепер давайте напишемо функції для відображення списку діалогів та конкретного діалогу.

def showDialogs(**kw):
    dialogs = vkapi.messages.getDialogs(**kw)
    printMessages([i['message'] for i in dialogs['items']])
def showDialog(**kw):
    messages = vkapi.messages.getHistory(**kw)
    printMessages(messages['items'][::-1]) # друкуємо перевернутий список (новіші знизу)

Тут нічого складного нема, тому рухаємося далі. Потрібно ще ж написати функцію для відправлення повідомлення.

def sendMessage(**kw):
    if 'user_id' in kw:
        user = vkapi.users.get(user_ids=kw['user_id'], name_case='dat')[0]
        print("Відправити повідомлення {first_name} {last_name}".format(**user))
    if 'message' not in kw:
        message = input(">> ")
        vkapi.messages.send(message=message, **kw)
    else:
        vkapi.messages.send(**kw)
    if 'user_id' in kw:
        yn = input("Відкрити діалог з користувачем? [y,т/n,н][n]: ")
        if 'yes' in yn.lower() or 'y' in yn.lower() or 'т' in yn.lower() or 'так' in yn.lower():
            showDialog(vkapi, count=10, user_id=kw['user_id'])

Тут все теж доволі просто, але все ж трохи роз'яснень не завадить. sendMessage(**kw) як і showDialogs(**kw) та showDialog(**kw) приймає аргументи по ключу так само як і відповідні методи VK API, тобто це лише обгортка над методами VK API. Якщо вказаний user_id (бо відправити повідомлення можна і за допомогою screen_name), то він друкує його ім'я та після відправлення пропонує відкрити діалог з ним.

На цьому функції для безпосередньої роботи з повідомленнями закінчуються, залишилися лише функції для копіювання даних. Давайте почнемо з лайків. Всі лайкнуті фото, пости та інше вк відображає на сторінці закладок, відповідно за допомогою методів API їх можна звідтіля дістати.

def copyiked(count=1, mode="posts"):
    likes = ''
    for i in range(count):
        if mode == "posts":
            liked = vkapi.fave.getPosts(count=100, offset=i*100)
        elif mode == "photos":
            liked = vkapi.fave.getPhotos(count=100, offset=i*100)
        for like in liked['items']:
            if mode == 'posts':
                likes += "vk.com/wall{owner_id}_{id}\n".format(**like)
            elif mode == "photos":
                likes += "vk.com/photo{owner_id}_{id}\n".format(**like)
        sleep(0.25)
    return likes

Ми зберігаємо тільки фото або пости, бо зазвичай в них найцікавіша інформація. Остачу ви можете прикрутити самі. Трюк з циклом, думаю, уже для вас зрозумілий, але все ж повторю, що ми зберігаємо 100*count записів/фото. Отримання списку груп так взагалі вийшло мініатюрним.

def copyGroups(count):
    groups = ''
    raw = vkapi.groups.get(count=count, extended=1, fields="screen_name")
    for group in raw['items']:
        groups += 'vk.com/{screen_name}\n'.format(**group)
    return groups

Зауважте, що групи видаються в такому порядку як користувач бачить їх на сайті, тобто ті що він відвідує частіше розташовані зверху. Ну а зараз найцікавіше: збереження історії листування в файл. По суті, там нема нічого нового, просто функція показу повідомлень, але запис іде не в термінал, а в файл.

def copyMessages(id, count, filename=None):
    messages = []
    user = vkapi.users.get(user_ids=id)[0]
    cur_user = vkapi.users.get()[0]
    users = {cur_user['id']: cur_user,
             user['id']: user}
    for i in range(count):
        buf = vkapi.messages.getHistory(user_id=id, count=200, offset=i*200)
        for m in buf['items']:
            messages.append(m)
        sleep(0.25)
    messages = messages[::-1]
    if filename == None:
        f = open(str(id) + "-messages.txt", 'w', encoding='utf-8')
    else:
        f = open(filename, 'w', encoding='utf-8')
    printMessages(messages, outfile=f)
    f.close()
def loadMessagees(filename):
    with open(filename, encoding='utf-8') as f:
        print(f.read())

Але вона все ж трохи відрізняється. Вона розрахована на великі обсяги повідомлень, тому запам'ятовує імена та зв'язує їх з id в словнику, щоб щоразу не робити запит до VK, а так все те саме що й в функції printMessages. Якщо ви відкриєте файл в текстовому редакторі то побачите якусь кашу з символів. Справа в тому що там зберігається всі коди кольорів, тому дивитися його краще через функцію loadMessages(filename).

На цьому все, грайтеся, та не переходьте на сторону зла :). Весь код доступний тут.

1539 6

Схожі матеріали:

Коментарі:

Авторизуйтесь, щоб залишити коментар.