October 8

meo

import loggingimport osimport aiohttpimport asyncioimport htmlimport refrom aiogram import Bot, Dispatcher, typesfrom aiogram.utils import executorAPI_TOKEN = '6840158361:AAFgABnSDVZfl1qIXPkPM6HqOeFWg4ErX9s'DANBOORU_USERNAME = 'ferganteus'DANBOORU_API_KEY = 'xc5FBbvR2JFfNhjWis6cDS5Q'ADMIN_ID = 1861544615 # Ваш Telegram ID для отправки логовbot = Bot(token=API_TOKEN)dp = Dispatcher(bot)# Настройка логированияlogging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)# Получаем и сохраняем username бота при запускеasync def on_startup(dp): bot_info = await bot.get_me() dp.bot.username = bot_info.username logging.info(f"Бот запущен: @{dp.bot.username}")# Асинхронная функция для отправки логов админуasync def send_log(message: str): try: await bot.send_message(ADMIN_ID, message) except Exception as e: logger.error(f"Не удалось отправить лог админу: {e}")# Функция для ограничения количества теговdef limit_tags(tag_string, limit=5): tags = [tag.strip() for tag in tag_string.split(' ') if tag.strip()] if len(tags) > limit: return ' '.join(tags[:limit]) + ' ...' else: return ' '.join(tags)async def fetch_posts(session, tags, page): url = 'https://danbooru.donmai.us/posts.json' params = { 'login': DANBOORU_USERNAME, 'api_key': DANBOORU_API_KEY, 'page': page, 'limit': 50 } if tags: params['tags'] = tags else: params['tags'] = 'status:any' headers = { 'User-Agent': 'Mozilla/5.0 (compatible; Bot/1.0; +http://yourdomain.com/bot)', 'Accept': 'application/json', } try: async with session.get(url, params=params, headers=headers, timeout=10) as response: if response.status != 200: error_message = f'Ошибка при получении постов: {response.status}' logger.error(error_message) await send_log(error_message) return [] data = await response.json() return data except Exception as e: error_message = f"Ошибка при получении постов: {e}" logger.error(error_message) await send_log(f"Ошибка при получении постов:\n{error_message}") return []@dp.inline_handler()async def inline_query_handler(query: types.InlineQuery): tags = query.query.strip() offset = int(query.offset) if query.offset else 1 user = query.from_user user_info = f"Пользователь: @{user.username or user.id} (ID: {user.id})\nЗапрос: {tags}" await send_log(user_info) try: async with aiohttp.ClientSession() as session: posts = await fetch_posts(session, tags, offset) except Exception as e: error_message = f"Ошибка при обращении к API: {e}" logger.error(error_message, exc_info=True) await send_log(error_message) return results = [] seen_ids = set() if not posts: results.append(types.InlineQueryResultArticle( id="1", title="Ничего не найдено", input_message_content=types.InputTextMessageContent( message_text="По заданным тегам ничего не найдено." ), description="По заданным тегам ничего не найдено." )) await query.answer(results=results, cache_time=1, is_personal=True) return for post in posts: post_id = post.get('id') if not post_id or post_id in seen_ids: continue seen_ids.add(post_id) rating = post.get('rating', 's') if rating == 'e': continue # Пропускаем откровенный контент file_ext = post.get('file_ext', '').lower() supported_extensions = ['jpg', 'jpeg', 'png', 'gif'] if file_ext not in supported_extensions: continue # Пропускаем неподдерживаемые форматы file_size = post.get('file_size', 0) max_inline_size = 5 * 1024 * 1024 # 5 МБ if file_size > max_inline_size: # Используем уменьшенную версию изображения image_url = post.get('large_file_url') or post.get('sample_file_url') if not image_url: continue # Если уменьшенной версии нет, пропускаем пост else: image_url = post.get('file_url') if not image_url or not image_url.startswith('http'): continue thumb_url = post.get('preview_file_url', image_url) # Получаем источник из раздела "Information" source = post.get('source') if source and source.startswith('http'): source_url = source # Проверяем, если источник ведёт на i.pximg.net if 'i.pximg.net' in source_url: # Пытаемся извлечь ID работы из URL match = re.search(r'/(\d+)(?:_p\d+)?', source_url) if match: pixiv_id = match.group(1) source_url = f"https://www.pixiv.net/artworks/{pixiv_id}" else: source_url = f"https://danbooru.donmai.us/posts/{post_id}" else: source_url = f"https://danbooru.donmai.us/posts/{post_id}" author_raw = post.get('tag_string_artist', 'Неизвестен') author = html.escape(limit_tags(author_raw)) characters_raw = post.get('tag_string_character', 'Не указаны') characters = html.escape(limit_tags(characters_raw)) copyright_raw = post.get('tag_string_copyright', 'Не указано') copyright = html.escape(limit_tags(copyright_raw)) rating_text = {'s': 'Safe', 'q': 'Questionable', 'e': 'Explicit'}.get(rating, 'Unknown') reverse_search_tag = characters_raw.strip().replace(' ', '_') if not reverse_search_tag: reverse_search_tag = tags reverse_author_tag = author_raw.strip().replace(' ', '_') if not reverse_author_tag: reverse_author_tag = tags # Создание инлайн-кнопок buttons = [ [ types.InlineKeyboardButton( text='🌐 Source', url=source_url ), types.InlineKeyboardButton( text=f'🔖 Rating: {rating_text}', callback_data='rating_info' ) ], [ types.InlineKeyboardButton( text='🔄 Reverse Character', switch_inline_query_current_chat=reverse_search_tag ), types.InlineKeyboardButton( text='🎨 Reverse Author', switch_inline_query_current_chat=reverse_author_tag ) ], [ types.InlineKeyboardButton( text='📥 Original Size', url=f"https://t.me/{dp.bot.username}?start=original_{post_id}" ) ] ] markup = types.InlineKeyboardMarkup(inline_keyboard=buttons) caption = ( f"<b>Author:</b> {author}\n" f"<b>Character:</b> {characters}\n" f"<b>Copyright:</b> {copyright}" ) # Ограничение длины подписи MAX_CAPTION_LENGTH = 1024 if len(caption) > MAX_CAPTION_LENGTH: caption = caption[:MAX_CAPTION_LENGTH - 3] + '...' results.append( types.InlineQueryResultPhoto( id=str(post_id), photo_url=image_url, thumb_url=thumb_url, title=f"Post ID: {post_id}", description=f"Rating: {rating_text}", caption=caption, parse_mode='HTML', reply_markup=markup ) ) # Устанавливаем next_offset для пагинации if len(posts) >= 50: next_offset = str(offset + 1) else: next_offset = '' try: await query.answer( results=results, cache_time=1, is_personal=True, next_offset=next_offset ) except Exception as e: error_message = f"Ошибка при отправке инлайн-ответа: {e}" logger.error(error_message, exc_info=True) await send_log(error_message)@dp.message_handler(commands=['start'])async def send_welcome(message: types.Message): args = message.get_args() if args.startswith("original_"): post_id = args.split('_')[1] await send_original_size(message, post_id) else: await message.reply("Привет! Введите команду или воспользуйтесь инлайн-режимом.")async def send_original_size(message, post_id): user_id = message.from_user.id # Получаем информацию о посте url = f'https://danbooru.donmai.us/posts/{post_id}.json' params = { 'login': DANBOORU_USERNAME, 'api_key': DANBOORU_API_KEY, } headers = { 'User-Agent': 'Mozilla/5.0 (compatible; Bot/1.0; +http://yourdomain.com/bot)', 'Accept': 'application/json', } try: async with aiohttp.ClientSession() as session: async with session.get(url, params=params, headers=headers, timeout=10) as response: if response.status != 200: error_message = f'Ошибка при получении поста {post_id}: {response.status}' logger.error(error_message) await send_log(error_message) return post = await response.json() except Exception as e: error_message = f"Ошибка при получении поста {post_id}: {e}" logger.error(error_message) await send_log(f"Ошибка при получении поста {post_id}:\n{error_message}") return file_url = post.get('file_url') if not file_url or not file_url.startswith('http'): error_message = f"Некорректный file_url для поста {post_id}" logger.error(error_message) await send_log(error_message) return # Скачиваем файл filename = file_url.split('/')[-1] file_path = f'temp/{filename}' os.makedirs('temp', exist_ok=True) try: async with aiohttp.ClientSession() as session: async with session.get(file_url) as resp: if resp.status == 200: with open(file_path, 'wb') as f: f.write(await resp.read()) else: error_message = f'Ошибка при скачивании файла {file_url}: {resp.status}' logger.error(error_message) await send_log(error_message) return except Exception as e: error_message = f"Ошибка при скачивании файла {file_url}: {e}" logger.error(error_message) await send_log(f"Ошибка при скачивании файла {file_url}:\n{error_message}") return # Отправляем файл пользователю в личные сообщения try: await bot.send_document( chat_id=user_id, document=types.InputFile(file_path), caption=f'Оригинальный файл поста {post_id}', parse_mode='HTML' ) except Exception as e: error_message = f"Ошибка при отправке файла пользователю {user_id}: {e}" logger.error(error_message) await send_log(f"Ошибка при отправке файла пользователю {user_id}:\n{error_message}") finally: # Удаляем файл после отправки if os.path.exists(file_path): os.remove(file_path)@dp.callback_query_handler(lambda c: c.data == 'rating_info')async def send_rating_info(callback_query: types.CallbackQuery): await callback_query.answer( text='Rating показывает уровень контента:\nS (Safe) - Безопасно\nQ (Questionable) - Сомнительно\nE (Explicit) - Откровенно', show_alert=True )if __name__ == '__main__': executor.start_polling(dp, skip_updates=True, on_startup=on_startup)