Добавить поддержку Яндекс Маркета (goods-feedback API)

- app/services/ym.py: клиент Partner API — getGoodsFeedbacks с пагинацией и
  фильтром по дате, updateGoodsFeedbackComment (ответ), deleteGoodsFeedbackComment
  (удаление), обработка новых отзывов по аналогии с WB/Ozon
- воркер ym_worker, включается при заданных YM_API_KEY и YM_BUSINESS_ID
- кнопки отправки/удаления ответа, платформа в сообщении и в таблице,
  статистика /stat и /allstat
- переменные YM_API_KEY, YM_BUSINESS_ID, YM_CHECK_INTERVAL, PRODUCTS_YM_SHEET_LINK
- README и .env.example

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 20:13:38 +03:00
co-authored by Claude Fable 5.1
parent 1098ff4991
commit 4534ab2683
10 changed files with 481 additions and 24 deletions
+9 -2
View File
@@ -2,6 +2,7 @@ import asyncio
import html
import logging
from asyncio import Queue
from urllib.parse import quote
from datetime import datetime
from aiogram import Router, types
@@ -15,6 +16,9 @@ from app.services.sheets import add_rows_to_sheet
logger = logging.getLogger(__name__)
router = Router()
PLATFORM_NAMES = {"ozon": "Ozon", "wb": "Wildberries", "ym": "Яндекс Маркет"}
PLATFORM_TAGS = {"ozon": "🟦 #Ozon", "wb": "🟪 #Wildberries", "ym": "🟨 #ЯндексМаркет"}
send_queue = Queue()
sheet_queue = Queue()
@@ -52,7 +56,7 @@ def render_review_to_sheet(review: Review) -> list[str]:
# Порядок столбцов таблицы (12 колонок): 1–8 данные, 9 тональность, 10 причина, 11 критичность, 12 Тэги
return [
formatted_date,
"Ozon" if review.platform == "ozon" else "Wildberries",
PLATFORM_NAMES.get(review.platform, review.platform),
review.product_name or "-",
review.product_category or "-",
review.product_subcategory or "-",
@@ -192,6 +196,9 @@ async def send_review(review: Review) -> None:
)
if review.platform == "ozon":
product_url = f"https://www.ozon.ru/product/{review.product_id}/"
elif review.platform == "ym":
# У Маркета нет публичной ссылки по SKU продавца — ведём в поиск по нему
product_url = f"https://market.yandex.ru/search?text={quote(review.product_id)}"
else:
product_url = (
f"https://www.wildberries.ru/catalog/{review.product_id}/detail.aspx"
@@ -219,7 +226,7 @@ async def send_review(review: Review) -> None:
+ f"{'✅ Положительный' if review.is_good else '⚠️ Отрицательный'} отзыв №{count + 1}\n"
f"📅 Дата: {formatted_date}\n"
f"📦 Товар: <code>{escaped_product_id}</code> #{category_hashtag}\n"
f"{'🟪 #Wildberries' if review.platform == 'wb' else '🟦 #Ozon'}\n"
f"{PLATFORM_TAGS.get(review.platform, '#' + review.platform)}\n"
f"🔗 <a href='{product_url}'>Ссылка</a>\n"
f"⭐ Оценка: <code>{review.rating}/5</code>\n"
f"💬 Отзыв: {escaped_text}\n\n"