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

11 хв. читання

Добридень, пані та панове. Ви, напевно, вже здогадалися, що ми знову будемо творити щось цікаве за допомогою 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}\
".format(**like)
            elif mode == "photos":
                likes += "vk.com/photo{owner_id}_{id}\
".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}\
'.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).

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

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.8K
Приєднався: 6 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація

Читайте також: sleep python, python sleep, функція if