#!/usr/bin/env python3 import re import sys import time import discord import logging import colorlog import requests from bs4 import BeautifulSoup import keys url_post = "https://linux-gaming.ru/posts.json" url_news = "https://linux-gaming.ru/c/news/6.json" url_vk_api = "https://api.vk.com/method/wall.post" url_changelog = "https://gitlab.eterfund.ru/Castro-Fidel/PortWINE/raw/master/data_from_portwine/changelog_ru" heads_site = { "Content-Type": "application/json", "Api-Key": keys.api_key_site, "Api-Username": "linux-gaming" } logger = logging.getLogger() logger.setLevel(logging.INFO) handler = colorlog.StreamHandler() handler.setFormatter(colorlog.ColoredFormatter( '%(log_color)s%(levelname)s: %(message)s', log_colors={ 'DEBUG': 'cyan', 'INFO': 'green', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red,bg_white', } )) logger.addHandler(handler) def main(): try: last_changelog, resp_changelog = resp_change() check_version(last_changelog, resp_changelog) check_discord_public() except Exception as err: logging.error(f"Ошибка исполнения функции main: {err}") def make_soup(resp_changelog): return BeautifulSoup(resp_changelog.text, 'html.parser') def html_to_text(html_content): soup = BeautifulSoup(html_content, 'html.parser') text = soup.get_text(separator='\n') links = [] for a in soup.find_all('a', href=True): links.append(a['href']) return text, links def remove_empty_lines(text_data): lines = text_data.splitlines() non_empty_lines = [line for line in lines if line.strip()] return '\n'.join(non_empty_lines) def script_content(script_ver, resp_changelog): soup = make_soup(resp_changelog) page_text = str(soup) page_text = page_text.replace("Вы можете помочь развитию проекта: https://linux-gaming.ru/donate/", '') # Находим текст до определенного текста, тега или класса (например, до тега
) last_text = f"###Scripts version {script_ver - 1}" last_text = str(last_text) index_last_text = page_text.find(last_text) if index_last_text != -1: changelog_text_last = page_text[:index_last_text] prev_text = f"###Scripts version {script_ver}" index_script_ver = changelog_text_last.find(prev_text) if index_script_ver != -1: changelog_text = changelog_text_last[index_script_ver:] post_text = (f"-----------------------------\nОбновление скриптов {script_ver}\n" f"-----------------------------\n") + changelog_text site_text = (f"[center][img]/uploads/default/original/1X/5cfa59077a5275971401fab0114e56f3ffdd0ec4.png[/img][" f"/center]\n{post_text}") post_data = { "title": f"Обновление скриптов {script_ver}", "raw": site_text, "category": keys.cat_num, "tags": ["scripts"] } params = { 'access_token': keys.api_key_vk, 'v': '5.199', # Версия API VK 'owner_id': keys.own_id, 'message': f'{post_text}', 'attachments': "photo-99238527_457244491" # Дополнительные параметры можно добавить здесь } return post_text, post_data, params def news_content(post_id): logging.info(f"Запрос содержимого поста новости с ID: {post_id}") response = response_get(f"https://linux-gaming.ru/t/{post_id}.json") if response and response.status_code == 200: topic_data = response.json() posts = topic_data.get('post_stream', {}).get('posts', []) # Найти первый пост for post in posts: if post.get('post_number') == 1: html_content = post.get('cooked', 'Нет содержимого') text_data, links = html_to_text(html_content) text_data = remove_empty_lines(text_data) logging.debug(text_data) return text_data, links logging.error(f"Первый пост не найден в теме с ID: {post_id}") return None else: logging.error(f"Не удалось получить содержимое поста с ID: {post_id}") return None def response_get(url): try: return requests.get(url, headers=heads_site) except requests.RequestException as err: logging.error(f"Ошибка запроса {err}") def resp_change(): resp_changelog = response_get(url_changelog) if resp_changelog and resp_changelog.status_code == 200: matches_changelog = re.findall(r'###Scripts version (\d+)###', resp_changelog.text) logging.info(f"Найдены версии в истории изменений: {matches_changelog}") last_changelog = int(max(matches_changelog)) logging.info(f"Последняя версия в истории изменений: {last_changelog}") return last_changelog, resp_changelog else: logging.error( f'Ошибка при запросе changelog: {resp_changelog.status_code if resp_changelog else "No Response"}') return None, None def resp_get(url): return response_get(url) def news(): resp_topics = resp_get(url_news) if resp_topics.status_code == 200: data = resp_topics.json() topics = data['topic_list']['topics'] list_titles_and_ids = [(topic['id'], str(topic['title'])) for topic in topics] filtered_list_titles_and_ids = [(id, title) for id, title in list_titles_and_ids if not title == ('Описание ' 'категории ' '«Новости»')] return filtered_list_titles_and_ids else: logging.error(f"Ошибка при запросе тем с сайта: {resp_topics.status_code if resp_topics else 'Нет доступа к сайту'}") return [] def site_post(url, headers, json): while True: title = json.get('title', 'No title') try: resp_post = requests.post(url=url, headers=headers, json=json) if resp_post.status_code == 200: logging.info("Новость опубликована на сайте!") return resp_post elif resp_post.status_code == 422: logging.warning(f'Новость "{title}" уже опубликована: {resp_post.status_code}') return resp_post else: logging.error(f'Ошибка при отправке новости "{title}" на сайт: {resp_post.status_code}') except requests.RequestException as error: logging.error(f'Ошибка при отправке новости "{title}" на сайт: {error}') time.sleep(900) def vk_post(url, params): try: # Отправляем POST-запрос к VK API resp_post = requests.post(url=url, params=params) if resp_post.status_code == 200: logging.info("Сообщение успешно опубликовано.") logging.info(resp_post.json()) # Выводим ответ сервера в формате JSON else: logging.error(f"Ошибка при публикации сообщения в ВК:, {resp_post.status_code}") return resp_post except requests.RequestException as err: logging.error(f"VK post failed: {err}") return None async def discord_post(post_text, client): channel = client.get_channel(keys.dicord_channel) await channel.send(f"{post_text}") async def get_discord_messages(client, channel_id): channel = client.get_channel(channel_id) if not channel: logging.error(f"ID канала Discord {channel_id} не существует") return [] messages = [] async for message in channel.history(limit=999999): messages.append(message.content) pattern = re.compile(r'-----------------------------\n(.*?)\n-----------------------------', re.DOTALL) for message in messages: matches = pattern.findall(message) if matches: messages.extend(matches) logging.debug(f"Найдены сообщения в дискорде: {messages}") return messages def check_version(last_changelog, resp_changelog): list_titles_and_ids = news() pattern = re.compile(r'Обновление скриптов (\d+)') def extract_number(title): match = pattern.search(title) if match: return int(match.group(1)) return None numbers = [extract_number(title) for _, title in list_titles_and_ids if extract_number(title) is not None] last_topics_script = max(numbers) logging.info(f"Последняя новость на сайте о версии: {last_topics_script}") #last_topics_script = 2297 if last_topics_script < last_changelog: list_new_ver = [] for script_ver in range(last_topics_script + 1, last_changelog + 1): list_new_ver.append(script_ver) logging.info(f"Найдена новая версия скрипта {script_ver}") changelog_text, post_data, params = script_content(script_ver, resp_changelog) if post_data: site_post(url_post, heads_site, post_data) # vk_post(url_vk_api, params=params) if not list_new_ver: logging.warning(f"Не найдена новая версия скрипта") sys.exit() else: logging.warning("Нет новых версий скриптов PortProton") def check_discord_public(): intents = discord.Intents.default() intents.messages = True client = discord.Client(intents=intents) @client.event async def on_ready(): logging.debug(f"Успешный логин в discord {client.user}") channel_id = keys.dicord_channel discord_messages = await get_discord_messages(client, channel_id) list_titles_and_ids = news() if list_titles_and_ids: list_for_public = [] for topic_id, topic_title in list_titles_and_ids: if topic_title not in discord_messages: list_for_public.append((topic_id, topic_title)) if list_for_public != []: logging.info(f"Новости для публикации в дискорд: {list_for_public}") else: logging.info(f"Новостей для публикации в дискорд нет") channel = client.get_channel(channel_id) if not channel: logging.error(f"ID канала Discord {channel_id} не существует") await client.close() return for topic_id, topic_title in reversed(list_for_public): text_data, links = news_content(topic_id) if text_data: content = f"-----------------------------\n{topic_title}\n-----------------------------\n" + text_data + "\n" + "----------------------------------------------------------" for link in links: if link not in content: content += f"\n{link}" # Разбиваем содержимое на части по 4000 символов for i in range(0, len(content), 4000): await channel.send(content[i:i+4000]) client.run(keys.discord_token) if __name__ == '__main__': main()