249 lines
9.8 KiB
Python
249 lines
9.8 KiB
Python
import mysql.connector
|
|
import requests
|
|
import json
|
|
import os
|
|
import time
|
|
import random
|
|
from datetime import datetime
|
|
from mysql.connector import errorcode
|
|
from dotenv import load_dotenv
|
|
from ensure_db import ensure_database_structure
|
|
|
|
# Загрузка переменных окружения из .env файла
|
|
load_dotenv()
|
|
|
|
# Получение конфигурации из переменных окружения
|
|
VK_GROUP_ID = int(os.getenv('VK_GROUP_ID', -3342146))
|
|
VK_ACCESS_TOKEN = os.getenv('VK_ACCESS_TOKEN', '')
|
|
VK_API_VERSION = os.getenv('VK_API_VERSION', '5.131')
|
|
IS_EVENT = os.getenv('IS_EVENT', 'false').lower() in ('true', '1', 'yes', 'on')
|
|
AUTO_PUBLISH = os.getenv('AUTO_PUBLISH', 'false').lower() in ('true', '1', 'yes', 'on')
|
|
LOG_FILE = os.getenv('LOG_FILE', 'vk_loader.log') # Путь к лог-файлу
|
|
LOG_PREFIX = "VK_loader" # Уникальный префикс для идентификации скрипта
|
|
|
|
# Конфигурация MariaDB из .env
|
|
DB_CONFIG = {
|
|
'host': os.getenv('MDB_HOST', 'localhost'),
|
|
'user': os.getenv('MDB_USER', 'root'),
|
|
'password': os.getenv('MDB_PW', ''),
|
|
'database': os.getenv('MDBASE', 'vk_posts')
|
|
}
|
|
|
|
def log_message(message, max_retries=5, retry_delay=0.1):
|
|
"""
|
|
Записывает сообщение в лог-файл с обработкой блокировок
|
|
и идентификатором скрипта
|
|
"""
|
|
if not LOG_FILE:
|
|
return
|
|
|
|
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
# Добавляем идентификатор скрипта к сообщению
|
|
log_line = f"[{timestamp}] [{LOG_PREFIX}] {message}\n"
|
|
|
|
for attempt in range(max_retries):
|
|
try:
|
|
with open(LOG_FILE, 'a', encoding='utf-8') as log:
|
|
log.write(log_line)
|
|
return True
|
|
except (IOError, OSError) as e:
|
|
if "locked" in str(e).lower() and attempt < max_retries - 1:
|
|
# Случайная задержка для уменьшения коллизий
|
|
sleep_time = retry_delay * (1 + random.random() * 0.5)
|
|
time.sleep(sleep_time)
|
|
else:
|
|
# Если не удалось записать после всех попыток
|
|
print(f"Ошибка записи в лог: {e}")
|
|
print(f"Сообщение для лога: {log_line.strip()}")
|
|
return False
|
|
|
|
def get_vk_posts():
|
|
"""Получает последние 10 постов из VK группы"""
|
|
url = 'https://api.vk.ru/method/wall.get'
|
|
params = {
|
|
'owner_id': VK_GROUP_ID,
|
|
'count': 10,
|
|
'access_token': VK_ACCESS_TOKEN,
|
|
'v': VK_API_VERSION,
|
|
'extended': 1
|
|
}
|
|
|
|
try:
|
|
response = requests.get(url, params=params, timeout=10)
|
|
response.raise_for_status()
|
|
data = response.json()
|
|
|
|
if 'error' in data:
|
|
error_msg = f"VK API Error: {data['error']['error_msg']}"
|
|
log_message(error_msg)
|
|
raise Exception(error_msg)
|
|
|
|
return data['response']['items']
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
error_msg = f"Ошибка подключения к VK API: {e}"
|
|
log_message(error_msg)
|
|
raise Exception(error_msg)
|
|
|
|
def process_post(post):
|
|
"""Извлекает необходимые данные из поста VK"""
|
|
post_id = post['id']
|
|
vk_post_url = f"https://vk.ru/wall{VK_GROUP_ID}_{post_id}"
|
|
published_at = datetime.utcfromtimestamp(post['date']).strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
# Инициализация полей опроса
|
|
is_poll = 0
|
|
poll_question = None
|
|
poll_options = None
|
|
poll_multiple = False
|
|
poll_end_date = None
|
|
|
|
# Инициализация поля изображения
|
|
image_url = None
|
|
|
|
# Обработка вложений
|
|
for attachment in post.get('attachments', []):
|
|
if attachment['type'] == 'poll':
|
|
is_poll = 1
|
|
poll = attachment['poll']
|
|
poll_question = poll['question']
|
|
poll_options = json.dumps([answer['text'] for answer in poll['answers']])
|
|
poll_multiple = bool(poll.get('multiple', False))
|
|
|
|
if poll.get('end_date', 0) > 0:
|
|
poll_end_date = datetime.utcfromtimestamp(poll['end_date']).strftime('%Y-%m-%d %H:%M:%S')
|
|
|
|
elif attachment['type'] == 'photo' and not image_url:
|
|
# Берем только первое изображение
|
|
photo = attachment['photo']
|
|
sizes = photo.get('sizes', [])
|
|
if sizes:
|
|
# Выбираем изображение максимального качества
|
|
max_size = max(sizes, key=lambda s: s.get('width', 0) * s.get('height', 0))
|
|
image_url = max_size['url']
|
|
|
|
return {
|
|
'vk_post_id': post_id,
|
|
'text': post['text'],
|
|
'image_url': image_url,
|
|
'vk_post_url': vk_post_url,
|
|
'published_at': published_at,
|
|
'is_poll': is_poll,
|
|
'poll_question': poll_question,
|
|
'poll_options': poll_options,
|
|
'poll_multiple': poll_multiple,
|
|
'poll_end_date': poll_end_date
|
|
}
|
|
|
|
def save_to_database(posts):
|
|
"""Сохраняет новые посты в MariaDB с учетом настроек публикации"""
|
|
try:
|
|
conn = mysql.connector.connect(**DB_CONFIG)
|
|
cursor = conn.cursor()
|
|
|
|
new_posts_count = 0
|
|
for post in posts:
|
|
try:
|
|
# Добавляем поля is_event, marked_for_publication, shortname и action_number
|
|
cursor.execute('''
|
|
INSERT INTO posts (
|
|
vk_post_id, text, image_url, vk_post_url, published_at,
|
|
is_poll, poll_question, poll_options, poll_multiple, poll_end_date,
|
|
is_event, marked_for_publication, shortname, action_number
|
|
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
ON DUPLICATE KEY UPDATE vk_post_id = VALUES(vk_post_id)
|
|
''', (
|
|
post['vk_post_id'],
|
|
post['text'],
|
|
post['image_url'],
|
|
post['vk_post_url'],
|
|
post['published_at'],
|
|
post['is_poll'],
|
|
post['poll_question'],
|
|
post['poll_options'],
|
|
int(post['poll_multiple']),
|
|
post['poll_end_date'],
|
|
IS_EVENT,
|
|
int(AUTO_PUBLISH), # Значение из .env
|
|
None, # shortname - пока не используется, устанавливаем NULL
|
|
None # action_number - пока не используется, устанавливаем NULL
|
|
))
|
|
if cursor.rowcount > 0:
|
|
new_posts_count += 1
|
|
# Логируем добавление нового поста
|
|
log_message(f"Добавлен новый пост: ID {post['vk_post_id']}, Дата: {post['published_at']}, Автопубликация: {'Да' if AUTO_PUBLISH else 'Нет'}")
|
|
except mysql.connector.Error as err:
|
|
error_msg = f"Ошибка при вставке поста {post['vk_post_id']}: {err}"
|
|
log_message(error_msg)
|
|
continue
|
|
|
|
conn.commit()
|
|
return new_posts_count
|
|
|
|
except mysql.connector.Error as err:
|
|
error_msg = f"Ошибка подключения к базе данных: {err}"
|
|
log_message(error_msg)
|
|
return 0
|
|
finally:
|
|
if 'conn' in locals() and conn.is_connected():
|
|
cursor.close()
|
|
conn.close()
|
|
|
|
def vk_load_10():
|
|
"""
|
|
Экспортируемая функция для загрузки 10 последних постов из VK
|
|
Возвращает количество добавленных постов
|
|
"""
|
|
# Стартовая информация
|
|
start_msg = f"Старт скрипта | База: {DB_CONFIG['database']}@{DB_CONFIG['host']} | IS_EVENT: {IS_EVENT} | AUTO_PUBLISH: {AUTO_PUBLISH}"
|
|
print(start_msg)
|
|
log_message(start_msg)
|
|
|
|
# Обеспечиваем структуру БД
|
|
try:
|
|
ensure_database_structure()
|
|
except Exception as e:
|
|
error_msg = f"Не удалось создать структуру базы данных: {e}"
|
|
print(error_msg)
|
|
log_message(error_msg)
|
|
return 0
|
|
|
|
# Получаем посты из VK
|
|
try:
|
|
vk_posts = get_vk_posts()
|
|
status_msg = f"Получено {len(vk_posts)} постов из VK"
|
|
print(status_msg)
|
|
log_message(status_msg)
|
|
except Exception as e:
|
|
error_msg = f"Ошибка при получении данных из VK: {e}"
|
|
print(error_msg)
|
|
log_message(error_msg)
|
|
return 0
|
|
|
|
# Обрабатываем и сохраняем посты
|
|
processed_posts = [process_post(post) for post in vk_posts]
|
|
new_count = save_to_database(processed_posts)
|
|
|
|
# Фиксируем результат
|
|
if new_count > 0:
|
|
result_msg = f"Добавлено {new_count} новых постов в базу данных"
|
|
else:
|
|
result_msg = "Новые посты не обнаружены"
|
|
|
|
print(result_msg)
|
|
log_message(result_msg)
|
|
|
|
# Завершение работы
|
|
end_msg = "Работа скрипта завершена"
|
|
print(end_msg)
|
|
log_message(end_msg)
|
|
|
|
return new_count
|
|
|
|
def main():
|
|
"""Основная функция для запуска скрипта из командной строки"""
|
|
vk_load_10()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|