parser_version6
/
osnovaTG.py
229 строк · 10.0 Кб
1import asyncio2from aiogram import Bot, Dispatcher, types3from aiogram.types import Message4from aiogram.fsm.storage.memory import MemoryStorage5from aiogram.fsm.context import FSMContext6from aiogram.filters import Command7from aiogram.fsm.state import StatesGroup, State8from datetime import date, timedelta9from test2 import parse_and_save10import mysql.connector11import hashlib12
13# Замените на ваш токен и ID группы
14TOKEN = "7032031280:AAEjXQh-WNlqfY2el6a7cj0PtUvJNh-vLM0"15GROUP_ID = "--1002448868558"16
17# Инициализация бота и диспетчера
18bot = Bot(token=TOKEN, timeout=60)19storage = MemoryStorage()20dp = Dispatcher(storage=storage)21
22# Конфигурация базы данных
23config = {24'user': 'root',25'password': 'Uz94deco!',26'host': '127.0.0.1',27'database': 'parser'28}
29
30# Класс для состояний FSM
31class DaysForm(StatesGroup):32waiting_for_days = State()33
34# Функция для нормализации текста сообщения
35def normalize_text(text):36return ' '.join(text.strip().split()).lower() # Убираем лишние пробелы и приводим текст к нижнему регистру37
38# Функция для вычисления хеша текста сообщения
39def get_text_hash(text):40normalized_text = normalize_text(text)41return hashlib.sha256(normalized_text.encode('utf-8')).hexdigest()42
43# Функция для получения истории сообщений группы (до 1000 сообщений)
44async def get_group_messages(limit=1000):45all_messages = []46all_hashes = []47try:48offset_message_id = None # ID последнего полученного сообщения для смещения49
50while len(all_messages) < limit:51# Получаем 100 сообщений с возможным смещением52messages = await bot.get_chat_history(chat_id=GROUP_ID, limit=100, offset_message_id=offset_message_id)53
54if not messages:55break # Если сообщений больше нет, выходим из цикла56
57for message in messages:58if message.text:59normalized_message = normalize_text(message.text)60all_messages.append(normalized_message)61all_hashes.append(get_text_hash(normalized_message))62
63# Устанавливаем ID последнего сообщения для следующего смещения64offset_message_id = messages[-1].message_id65
66# Ограничиваем общее количество сообщений до запрашиваемого лимита67return all_messages[:limit], all_hashes[:limit]68
69except Exception as e:70print(f"Ошибка при получении истории сообщений группы: {e}")71return [], []72
73# Проверка наличия дубликатов по хешам сообщений в группе или базе данных
74async def is_duplicate(post_text, group_messages_hashes, conn):75post_text_hash = get_text_hash(post_text)76
77# Проверка на дубликат в истории сообщений группы78if post_text_hash in group_messages_hashes:79return True80
81# Проверка на дубликат в базе данных82cursor = conn.cursor()83cursor.execute("SELECT COUNT(*) FROM sent_posts WHERE post_hash = %s", (post_text_hash,))84count = cursor.fetchone()[0]85cursor.close()86
87return count > 088
89# Ограничиваем скорость запросов, чтобы избежать лимитов
90async def send_message_with_delay(text, conn):91try:92await bot.send_message(GROUP_ID, text)93
94# Сохраняем хеш отправленного сообщения в базе данных95post_text_hash = get_text_hash(text)96cursor = conn.cursor()97cursor.execute("INSERT INTO sent_posts (post_hash) VALUES (%s)", (post_text_hash,))98conn.commit()99cursor.close()100
101await asyncio.sleep(5) # Задержка между запросами, чтобы избежать лимита102except Exception as e:103print(f"Ошибка при отправке сообщения: {e}")104
105async def send_unique_posts():106try:107conn = mysql.connector.connect(**config)108cursor = conn.cursor(dictionary=True)109cursor.execute("SELECT post_text, time, channel_name FROM posts") # Добавили post_date и channel_name110posts = cursor.fetchall()111
112# Получаем до 1000 сообщений из группы и их хеши113group_messages, group_messages_hashes = await get_group_messages(1000)114
115for post in posts:116post_text = post['post_text']117time = post['time'].strftime('%d-%m-%Y') # Форматируем дату118channel_name = post['channel_name'] # Название канала119
120# Формируем текст для отправки121formatted_message = f"Канал: {channel_name}\nДата: {time}\n\n{post_text}"122
123is_dup = await is_duplicate(formatted_message, group_messages_hashes, conn)124
125if not is_dup:126await send_message_with_delay(formatted_message, conn)127print(f"Отправлен уникальный пост: {formatted_message}")128else:129print(f"Пост пропущен (дубликат): {formatted_message}")130
131except Exception as e:132print(f"Ошибка при отправке уникальных постов: {e}")133finally:134cursor.close()135conn.close()136
137# Загрузка ссылок и ключевых слов из файлов
138def load_links_and_keywords():139try:140with open('Links.txt', 'r', encoding='utf-8') as f:141links = [line.strip() for line in f.readlines()]142with open('keywords.txt', 'r', encoding='utf-8') as f:143keywords = [line.strip() for line in f.readlines()]144return links, keywords145except Exception as e:146print(f"Ошибка при загрузке ссылок или ключевых слов: {e}")147return [], []148
149# Загрузка количества дней из файла
150def load_days():151try:152with open('days.txt', 'r') as f:153return int(f.read().strip())154except Exception as e:155print(f"Ошибка при загрузке дней: {e}")156return 7 # Значение по умолчанию157
158# Сохранение количества дней в файл
159def save_days(days):160try:161with open('days.txt', 'w') as f:162f.write(str(days))163except Exception as e:164print(f"Ошибка при сохранении дней: {e}")165
166# Фоновая задача для парсинга каждые 15 минут
167async def scheduled_parsing():168while True:169try:170days = load_days() # Загружаем количество дней из файла171links, keywords = load_links_and_keywords()172nazvanie = ["Бойлерная", "Кремлёвский безБашенник", "Силовики", "НЕЗЫГАРЬ", "ОПЕР Слил", "SHOT", "Mash", "Baza", "ВЧК-ОГПУ", "КОНТЕКСТ"] # Названия каналов173
174start_date = date.today() - timedelta(days=days)175
176# Асинхронный запуск парсинга177await asyncio.to_thread(parse_and_save, links, nazvanie, keywords, start_date)178
179# Отправка уникальных постов180await send_unique_posts()181
182except Exception as e:183print(f"Ошибка в фоновом парсинге: {e}")184
185await asyncio.sleep(900) # Повтор каждые 15 минут186
187# Команда /start
188@dp.message(Command("start"))189async def start_command(message: Message, state: FSMContext):190await message.answer("""Здравствуйте, вас привествует бот по Парсеру данных из разных каналов.191Основные команды в боте:
192
193/start - запуск бота.
194/change_days - изменить время для парсера данных.""")195await message.answer("Сколько дней вы хотите парсить данные?")196await state.set_state(DaysForm.waiting_for_days)197
198# Обработка ответа на /start
199@dp.message(DaysForm.waiting_for_days)200async def process_days(message: Message, state: FSMContext):201if message.text.isdigit():202days = int(message.text)203save_days(days)204await message.answer(f"Дни для парсинга установлены: {days}")205await state.clear() # Очищаем состояние206else:207await message.answer("Пожалуйста, введите корректное число.")208
209# Команда /change_days для изменения количества дней
210@dp.message(Command("change_days"))211async def change_days_command(message: Message, state: FSMContext):212await message.answer("Сколько дней вы хотите установить для парсинга?")213await state.set_state(DaysForm.waiting_for_days)214
215# Закрытие сессии при завершении работы
216async def on_shutdown():217await bot.session.close() # Закрываем сессию бота218
219# Запуск бота и фоновой задачи
220async def main():221asyncio.create_task(scheduled_parsing()) # Запуск фоновой задачи для парсинга222await bot.delete_webhook(drop_pending_updates=True)223try:224await dp.start_polling(bot)225finally:226await on_shutdown() # Корректное завершение работы227
228if __name__ == "__main__":229asyncio.run(main())230