Browse Source

vkdigest: python3

master
Maxim Likhachev 6 years ago
parent
commit
8bfeb88e34
  1. 408
      vkdigest.py

408
vkdigest.py

@ -0,0 +1,408 @@ @@ -0,0 +1,408 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2019, Maxim Lihachev, <envrm@yandex.ru>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import sys
import json
import vk_api
import getopt
import string
import datetime
import configparser
import lib.markup
from lib.markup import oneliner as _
from smtplib import SMTP_SSL as SMTP
from email.mime.text import MIMEText
############################################################################
def usage():
print('''
vkdigest сценарий для получения сообщений из сообществ vk.com
с выводом в html и/или отправкой по электронной почте.
Аргументы командной строки:
-h --help - вывести справку по использованию
-f --file - загрузить сообщества из файла
-u --url - открыть сообщество по адресу
-m --mail - отправить дайжест по электронной почте
-s --subj - тема сообщения, макросы: {DATE}, {URL}, {COMMENT}
-t --title - HTML-заголовок, то же самое, что и --subj
-c --cli - вывести дайжест на экран (по умолчанию)
-o --out - вывести дайжест в файл
''')
sys.exit(2)
############################################################################
# Конфигурационный файл
CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'conf/vkdigest.ini')
# Вывод на экран
# -c | --cli
CLI_OUTPUT = True
# Вывод в файл
# -f <filename>
# --file <filename>
FILE_OUTPUT = ''
# Отправка почты по умолчанию
SEND_MAIL = False
# Путь до css-файла
CSS_FILE = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.path.pardir, 'css/vkdigest.css')
)
############################################################################
def readConfig(config_file):
'''Чтение конфигурационного файла'''
global settings
settings = configparser.ConfigParser()
settings._interpolation = configparser.ExtendedInterpolation()
settings.read(config_file)
return settings
def opt(parameter):
'''Получение опции из файла'''
setting = parameter.split(":")
return settings.get(setting[0], setting[1])
def enabled(opt):
return opt.lower() in ("yes", "true", "t", "1")
def timestamp_to_date(timestamp, fmt='%Y-%m-%d %H:%M:%S'):
'''Преобразование временного штампа в читаемую дату'''
return datetime.datetime.fromtimestamp(int(timestamp)).strftime(fmt)
def today():
'''Текущая дата'''
return int(
datetime.datetime.strptime(
datetime.datetime.today().strftime('%Y-%m-%d'), '%Y-%m-%d'
).strftime("%s")
)
############################################################################
def make_title():
'''Заголовок страницы и тема письма'''
return MAIL_SUBJECT.format(
URL=opt('mail:url'),
COMMENT=opt('mail:comment'),
DATE=timestamp_to_date(today(), '%Y-%m-%d')
)
# Псевдоним для функции send_email
make_subject = make_title
def send_email(message):
'''Отправка письма с дайжестом'''
subject = make_subject()
try:
msg = MIMEText(message, 'html')
msg['Subject'] = subject
msg['From'] = opt('mail:sender')
conn = SMTP(opt('mail:SMTPserver'))
conn.set_debuglevel(False)
conn.login(opt('mail:username'), opt('mail:password'))
try:
conn.sendmail(
opt('mail:sender'), opt('mail:destination'), msg.as_string()
)
finally:
conn.quit()
except Exception as exc:
sys.exit("mail failed; %s" % str(exc))
############################################################################
def auth(login, password):
'''Аутентификация в vk.com'''
vk_session = vk_api.VkApi(login, password)
try:
vk_session.auth()
return vk_session.get_api()
except vk_api.AuthError as error_msg:
print(error_msg)
return
def only_today(wall, date=None):
'''Записи за указанную дату'''
return [post for post in wall if post['date'] >= today()]
def get_group_url(group):
'''Адрес группы'''
return group.split(' ')[0]
def get_group_name(url):
'''HTTP-имя группы'''
return url.split('/')[-1]
def get_attachments(attachments, type):
'''Получение вложений'''
return [attachment[type]
for attachment
in attachments
if attachment['type'] == type]
def group_info(name):
'''Информация о сообществе'''
# TODO: если репост со страницы пользователя, то вставляет название группы
return vk.groups.getById(group_ids=name)[0]
def wall_url(group_id, post_id):
'''Ссылка на конкретный пост'''
return "http://vk.com/wall" + str(group_id) + '_' + str(post_id)
def wall(name):
'''Получение записей со стены сообщества'''
id = group_info(get_group_name(name))['id']
content = vk.wall.get(owner_id=-id)['items']
if enabled(opt('digest:only_today')):
content = only_today(content)
return content
############################################################################
def html_toc(groups):
'''Содержание дайжеста со ссылками на группы'''
HTML.h2("Сообщества:")
HTML.ul()
for group in groups:
if group.strip():
group_name = get_group_name(get_group_url(group))
info = group_info(group_name)
HTML.li(_.a(info['name'], href='#' + group_name))
HTML.ul.close()
HTML.br()
HTML.hr()
def groups_info(input_file):
'''Информация о группах, перечисленных в файле'''
f = open(input_file, "r")
groups = f.readlines()
f.close()
if len(groups) > 1:
html_toc(groups)
for group in groups:
if group.strip():
url = get_group_url(group)
MAIL_URL = url
if '#' in group:
MAIL_COMMENT = group.split('#')[1]
group_info_html(url)
wall_html(url)
def group_info_html(name):
'''Информация о группе в формате HTML'''
group_name = get_group_name(name)
info = group_info(group_name)
HTML.div(class_='group-info')
HTML.img(src=info['photo_50'])
if enabled(opt('digest:add_links')):
HTML.h1(_.a(info['name'], href="http://vk.com/" + info['screen_name']),
id=group_name
)
else:
HTML.h1(info['name'], id=group_name)
HTML.div.close()
HTML.br()
HTML.hr()
def wall_html(name):
'''Сообщения со стены сообщества в формате HTML'''
info = wall(name)
for post in info:
if post['text'] or ('attachments' in post):
post_html(post)
# Репост
elif 'copy_history' in post:
post_html(post['copy_history'][0], True)
def photo_size(photo, get_size="max"):
'''Ссылка на фотографию большего или меньшего размера'''
index = -1 if get_size == "max" else 0
size = sorted([p['type'] for p in photo['sizes']])[index]
return list(filter(lambda p: p['type'] == size, photo['sizes']))[0]['url']
def post_html(content, repost=False):
'''Информация о посте в формате HTML'''
HTML.div(class_="post")
if repost:
original_group = group_info(abs(content['owner_id']))
original_name = original_group['name']
original_url = wall_url(content['from_id'], content['id'])
# Дата поста
HTML.h4()
if enabled(opt('digest:add_links')):
HTML.add(_.a(timestamp_to_date(content['date']), href=wall_url(content['from_id'], content['id'])) + (' via ' + _.a(original_name, href=original_url) if repost else ''))
else:
HTML.add(timestamp_to_date(content['date']) + (' via ' + original_name if repost else ''))
HTML.h4.close()
# Текст поста
HTML.p(tuple(content['text'].splitlines()))
# Документы
if 'attachments' in content:
for photo in get_attachments(content['attachments'], 'photo'):
HTML.a(_.img(src=photo_size(photo, "min")), href=photo_size(photo, "max"))
for document in get_attachments(content['attachments'], 'doc'):
HTML.ul(_.li(_.a(document['title'], href=document['url'])))
for link in get_attachments(content['attachments'], 'link'):
HTML.ul(_.li(_.a(link['title'], href=link['url'])))
HTML.br()
HTML.div.close() # <div class="post">
HTML.hr()
############################################################################
try:
opts, args = getopt.getopt(sys.argv[1:], 'f:u:s:t:o:mch', ['file=', 'url=', 'subj=', 'title=', 'out=', 'mail', 'cli', 'help'])
except getopt.GetoptError:
usage()
if not opts:
usage()
input_file = ''
url = ''
CLI_OUTPUT_FLAG = False
for option, arg in opts:
if option in ('-h', '--help'):
usage()
elif option in ('-f', '--file'):
input_file = arg
elif option in ('-u', '--url'):
url = arg
elif option in ('-s', '--subj'):
MAIL_SUBJECT = arg
elif option in ('-t', '--title'):
MAIL_SUBJECT = arg
elif option in ('-o', '--out'):
FILE_OUTPUT = arg
CLI_OUTPUT = False
elif option in ('-m', '--mail'):
SEND_MAIL = True
CLI_OUTPUT = False
elif option in ('-c', '--cli'):
CLI_OUTPUT_FLAG = True
else:
usage()
if CLI_OUTPUT_FLAG:
CLI_OUTPUT = True
############################################################################
# Чтение файла настроек
settings = []
readConfig(CONFIG_FILE)
MAIL_SUBJECT = opt('mail:subject')
# Учётная запись vk.com
vk = auth(opt('vk:username'), opt('vk:password'))
HTML = lib.markup.page()
HTML.init(
title=make_title(),
lang="ru",
charset="utf-8",
css=(CSS_FILE if os.path.isfile(CSS_FILE) else "")
)
if input_file:
if os.path.isfile(input_file):
groups_info(input_file)
else:
print("Неправильный входной файл ", input_file)
elif url:
group_info_html(url)
wall_html(url)
else:
usage()
############################################################################
if SEND_MAIL:
send_email(HTML())
if FILE_OUTPUT:
with open(FILE_OUTPUT, 'w') as outfile:
outfile.write(HTML())
if CLI_OUTPUT:
print(HTML)
Loading…
Cancel
Save