Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Умоляем Вконтакте напомнить про пельмешки

Anna | 15.06.2014 | нет комментариев
Сегодня меня опять попросили напомнить человеку о главной вещи в определённое время. Но что делать, если я и про свои-то дела забываю непрерывно, а уж тем больше про дела кого-то ещё? И здесь мне вновь помог мой любимый python.

Добросовестно говоря, обыкновенные программы-напоминалки, что в телефоне, что в компьютере, меня не устраивали из-за их ограниченности рамками устройства они абсолютно не решали задачу, когда необходимо напомнить о чём-то, но не мне. Решение пришло как-то само-собой. А что, если напоминания будут приходить как сообщения вконтакте? Если я не на рабочем месте — телефон свибрирует своим пуш-ап уведомлением, а за компьютером всё ещё очевиднее. Цель — написать скрипт, тот, что читает мои сообщения о напоминании и в заданное время напоминает кому необходимо о том, что, собственно, требуется. Ну раз идея пришла, я приступаю к её реализации.

Стартуем

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

Для начала обучим наш скрипт логиниться в эту социальную сеть. Всё легко, используем типовой mechanize.Browser()

br = mechanize.Browser()
br.set_handle_equiv(True)
br.set_handle_redirect(True)
br.set_handle_robots(False)
br.open('https://vk.com/')

br.select_form(nr=0)
br.form['email'] = name
br.form['pass'] = password
br.submit()

Вуаля! Мы зашли на свою страничку вконтакте. Сейчас используем изумительную специфика вконтакта — вероятность писать самому себе(кто не в курсе, как это делается — перейдите по ссылке vk.com/im?sel=id, где id — Ваш id в общественной сети. В моём случае это был 38591009).

Первым делом прочитаем эти сообщения. Для этого мне необходимо узнать свой id(он есть в коде основной страницы, причём повторяется уйма раз).

self_username = 'username'

def get_self_page_id(br):
    br.open('https://vk.com/' self_username)
    return br.response().read().split('<form action="/wall')[1].split('?')[0]

def check_messages(br):
    br.open('https://vk.com/im?sel=' get_self_page_id(br))
    response = br.response().read()

Мы видим последние 20 сообщений из тех, что сами же себе и отсылали. Нам этого довольно. Всякое сообщение имеет свой неповторимый(для пользователя) номер, нам это дюже благотворно. Дальше необходимо с ними немножко поиграть, Дабы поделить все сообщения, отделить текст от порядкового номера и обучить скрипт понимать, какие сообщения новые, а какие теснее не актуальны.

first_start = True
msg_numbers = [] #номера сообщений. Глобальная переменная, будет беречь номера сообщений, прочитанных в предыдущей итерации.

def play_with_messages(br, response):
    global first_start
    all_messages = response.split('class="messages bl_cont">')[1].split('<div id="mfoot"')[0].split('<a name="msg')
    all_numbers = []
    global msg_numbers
    for msg in all_messages:
        if msg != all_messages[0]:
            msg_num = msg.split('">')[0]
            all_numbers.append(msg_num)
    if first_start:
        msg_numbers = all_numbers
        first_start = False
    new_numbers = set(all_numbers) - set(all_numbers).intersection(set(msg_numbers))
    for num in new_numbers:
        reply_to_message(br, get_message_text(response, num)) #вызов функции результата на сообщение. Опишу её позднее.
    msg_numbers = all_numbers

Начинаем внутренний диалог

Отменно. Сейчас мы знаем какие сообщения поступили мне от меня незадолго. Осталось их осознать и сделать что-то в результат. Займёмся вначале первой задачей:

def reply_to_message(br, message):
    if message.find('напомнить') == -1:
        print 'nothing'
    else:
        print 'I obey, my lord'
        ms_words = message.split(' ')
        user = 'self'
        time_s = datetime.datetime.now().strftime('%H:%M')
        day_s = str(datetime.date.today())
        msg = 'something went wrong'
        times = message.split('|')
        if len(times) == 1:
            times = '1'
        else:
            times = int(times[1])
        if ms_words[1] == 'в':
            user = 'self'
            time_s = ms_words[2]
            msg = message.split('текст ')[1].split('|')[0]
        elif ms_words[1] == 'день':
            user = 'self'
            time_s = ms_words[4]
            day_s = ms_words[2]
            msg = message.split('текст ')[1].split('|')[0]
        elif ms_words[2] == 'в':
            user = get_page_id(br, ms_words[1])
            time_s = ms_words[3]
            msg = message.split('текст ')[1].split('|')[0]
        elif ms_words[2] == 'день':
            user = get_page_id(br, ms_words[1])
            time_s = ms_words[5]
            day_s = ms_words[3]
            msg = message.split('текст ')[1].split('|')[0]

        let_it_do(user, time_s, day_s, msg, times) #вызов функции, которая знает, что делать с полученными из сообщения значениями.

Тут я спличу полученные сообщения и заношу в переменные соответствующие значения. В всеобщем, отвечаю на вопросы «кому напомнить?», «что напомнить?», «когда и сколько раз это сделать?». Синтаксис сообщения/команды предпочел не трудный: напомнить [кому] [дата] в [время] текст [текст сообщения]|[сколько раз]. Вот пример:«напомнить tenoclock в 14:10 текст Следующий тест | 4»

Так наш робот видит внутренний диалог

Для хранения заданий я предпочел базу данных sqlite3. Нагрузка у нас минимальная, разворачивается она абсолютно без усилий. Сейчас приступим к записи заданий в базу данных, по пути проверяя валидность даты и времени. Выглядит это вот так:

def valid_time(time_text):
    try:
        datetime.datetime.strptime(time_text, '%H:%M')
        return True
    except ValueError:
        send_message(br_fake, get_self_page_id(br), 'неверный формат времени')
        return False

def valid_date(date_text):
    try:
        datetime.datetime.strptime(date_text, '%Y-%m-%d')
        return True
    except ValueError:
        send_message(br_fake, get_self_page_id(br), 'неверный формат даты')
        return False

def let_it_do(user, time_s, day_s, message, times):
    if valid_time(time_s) and valid_date(day_s):
        c = conn.cursor()
        c.execute("INSERT INTO reminder (time, date, user, message, times) VALUES (?,?,?,?,?)",(time_s, day_s, user, message, str(times)))
        conn.commit()

Финишная прямая

Мы теснее близки к финалу! Задания наш робот получил, себе их записал. По сути, осталось только их исполнить. Здесь я столкнулся с маленький сложностью. Скрипт непрерывно читает мои сообщения ко мне, следственно, если он будет отправлять их в данный-же диалог, то в непрочитанных у меня ничего висеть не будет. А это нехорошо. Задача решилась заведением фэйкового аккаунта для этого случая. Сейчас если скрипт напоминает мне о чём-то, он пишет со второго аккаунта, если же необходимо напомнить кому-то иному, то он пишет от моего имени, чтобы людей не пугать.

Собственно вот пара функций, которые отвечают за чтение из базы и отсылку сообщений:

def check_answers():
    conn = sqlite3.connect('reminder.db')
    rows = get_rows(conn)
    for row in rows:
        print row[5]
        c = conn.cursor()
        if row[3] == 'self':
            pass
            send_message(br_fake, get_self_page_id(br), row[4].encode('utf-8'))
        else:
            send_message(br, row[3], row[4].encode('utf-8'))
        if row[5] == '1' or row[5] == 1:
            c.execute("DELETE FROM reminder WHERE id = ?;", str(row[0]))
        else:
            time_s = (datetime.datetime.now() datetime.timedelta(seconds=60)).strftime('%H:%M')
            num = int(row[5]) - 1
            c.execute("UPDATE reminder SET time = ?, times = ? WHERE id = ?",(time_s, str(num), row[0]))
        conn.commit()

def send_message(br, id, message):
    br.open('https://vk.com/im?sel=' id)
    br.select_form(nr=0)
    br.form['message'] = message
    br.submit()

Ну и позже отсылки сообщений скрипт удаляет запись из базы, если она не востребована(если необходимо повторить ещё сколько-то раз, то переносит время напоминания на минуту вперёд и сокращает число оставшихся отправок)

Подводим результаты

Так скрипт, тот, что укладывается в 200 строк кода, решает задачу напоминаний себе и иным, применяя социальную сеть вконтакте. Целиком его дозволено скачать отсель. Если он внезапно кому-то необходим, то советую не собирать из кусков статьи, тут только функциональные вещи. Некоторые вспомогательные штуки остались за кадром. Я запустил его на одном из своих vps. Пока, как бы как, комфортно.


Робот указывает мне, что делать. В воскресенье! Дожили

Позже несложных модификаций сюда так-же дозволено включить всякие другие функции управления системой, если скрипт запущен на удалённом компьютере. Поставить тот же торрент на скачивание, скажем. А так, в целом, дозволено реализовать веб-сервис, тот, что будет заниматься напоминаниями для всех, кто попросит(реальнобота, как в, теснее позабытых теперь, irc и icq) Но эти вещи теснее не относятся к данной статье. Буду дюже рад, если кому-то это было пригодным.

Каждому спасибо за внимание.

UPD: Не торопитесь разжигать факелы и точить вилы по поводу API. Я осмысленно его не использую в этом скрипте из-за некоторых неудобных мест. А именно авторизации и работы с диалогами. Однако, в первых же комментариях я раскрыл эту тему. Цель этой статьи показать, как стремительно и, фактически, не применяя сторонней информации, помимо познания родного для разработчика языка, сделать жизнь несколько проще.

UPD #2: Тот же скрипт, но с применением мощного API Вконтакте дозволено взять здесь. Сейчас работает без применения фэйкового аккаунта. Это непререкаемый плюс.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB