Store MAX publication URLs
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from vk_parser_app.constants import PUBLICATION_STATUS_PUBLISHED
|
||||||
|
from vk_parser_app.db import close_pool, fetch_int_setting, fetch_setting, get_pool
|
||||||
|
from vk_parser_app.workers.max_poster import MAXAPIClient, MAXPoster
|
||||||
|
|
||||||
|
|
||||||
|
async def main() -> None:
|
||||||
|
pool = await get_pool()
|
||||||
|
token = str(await fetch_setting("max_poster_bot_token", "") or "").strip()
|
||||||
|
base_url = str(await fetch_setting("max_poster_api_base_url", "https://platform-api2.max.ru") or "").strip()
|
||||||
|
if not token:
|
||||||
|
raise RuntimeError("max_poster_bot_token is empty")
|
||||||
|
|
||||||
|
rows = await pool.fetch(
|
||||||
|
"""
|
||||||
|
SELECT id, raw_post_id, external_id
|
||||||
|
FROM post_publications
|
||||||
|
WHERE platform='max'
|
||||||
|
AND status=$1
|
||||||
|
AND COALESCE(url, '')=''
|
||||||
|
AND COALESCE(external_id, '')<>''
|
||||||
|
ORDER BY published_at DESC NULLS LAST, id DESC
|
||||||
|
""",
|
||||||
|
PUBLICATION_STATUS_PUBLISHED,
|
||||||
|
)
|
||||||
|
|
||||||
|
poster = MAXPoster()
|
||||||
|
updated = 0
|
||||||
|
missing = 0
|
||||||
|
async with MAXAPIClient(
|
||||||
|
token,
|
||||||
|
base_url,
|
||||||
|
timeout_sec=max(30, await fetch_int_setting("max_poster_timeout_sec", 120)),
|
||||||
|
max_attempts=max(1, await fetch_int_setting("max_poster_max_attempts", 3)),
|
||||||
|
retry_backoff_max_sec=max(1, await fetch_int_setting("max_poster_retry_backoff_max_sec", 30)),
|
||||||
|
) as client:
|
||||||
|
for row in rows:
|
||||||
|
data = await client.get_message(str(row["external_id"]))
|
||||||
|
url = poster.message_url_from_response(data)
|
||||||
|
if not url:
|
||||||
|
missing += 1
|
||||||
|
continue
|
||||||
|
await pool.execute(
|
||||||
|
"""
|
||||||
|
UPDATE post_publications
|
||||||
|
SET url=$2,
|
||||||
|
updated_at=NOW()
|
||||||
|
WHERE id=$1
|
||||||
|
""",
|
||||||
|
int(row["id"]),
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
updated += 1
|
||||||
|
logger.info("MAX URL backfilled raw_post={} url={}", row["raw_post_id"], url)
|
||||||
|
|
||||||
|
logger.info("MAX URL backfill done: scanned={} updated={} missing={}", len(rows), updated, missing)
|
||||||
|
await close_pool()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -161,6 +161,9 @@ class MAXAPIClient:
|
|||||||
payload["attachments"] = attachments
|
payload["attachments"] = attachments
|
||||||
return await self.request("POST", f"/messages?chat_id={chat_id}", json=payload)
|
return await self.request("POST", f"/messages?chat_id={chat_id}", json=payload)
|
||||||
|
|
||||||
|
async def get_message(self, message_id: str) -> dict[str, Any]:
|
||||||
|
return await self.request("GET", f"/messages/{quote(message_id, safe='')}")
|
||||||
|
|
||||||
async def get_video_info(self, token: str) -> dict[str, Any]:
|
async def get_video_info(self, token: str) -> dict[str, Any]:
|
||||||
return await self.request("GET", f"/videos/{quote(token, safe='')}")
|
return await self.request("GET", f"/videos/{quote(token, safe='')}")
|
||||||
|
|
||||||
@@ -486,6 +489,13 @@ class MAXPoster:
|
|||||||
candidates.extend([body.get("id"), body.get("mid"), body.get("message_id")])
|
candidates.extend([body.get("id"), body.get("mid"), body.get("message_id")])
|
||||||
return next((str(value) for value in candidates if value), "")
|
return next((str(value) for value in candidates if value), "")
|
||||||
|
|
||||||
|
def message_url_from_response(self, data: dict[str, Any]) -> str | None:
|
||||||
|
candidates = [data.get("url")]
|
||||||
|
message = data.get("message") if isinstance(data.get("message"), dict) else {}
|
||||||
|
body = message.get("body") if isinstance(message.get("body"), dict) else {}
|
||||||
|
candidates.extend([message.get("url"), body.get("url")])
|
||||||
|
return next((str(value).strip() for value in candidates if str(value or "").strip()), None)
|
||||||
|
|
||||||
async def send_message_waiting_for_media(
|
async def send_message_waiting_for_media(
|
||||||
self,
|
self,
|
||||||
client: MAXAPIClient,
|
client: MAXAPIClient,
|
||||||
@@ -579,10 +589,15 @@ class MAXPoster:
|
|||||||
first_text = chunks[0] if chunks else ""
|
first_text = chunks[0] if chunks else ""
|
||||||
first = await self.send_message_waiting_for_media(client, first_text, attachments)
|
first = await self.send_message_waiting_for_media(client, first_text, attachments)
|
||||||
message = first.get("message") if isinstance(first.get("message"), dict) else first
|
message = first.get("message") if isinstance(first.get("message"), dict) else first
|
||||||
first_url = message.get("url") if isinstance(message, dict) else None
|
first_url = self.message_url_from_response(message) if isinstance(message, dict) else None
|
||||||
first_message_id = self.message_id_from_response(first)
|
first_message_id = self.message_id_from_response(first)
|
||||||
if first_message_id:
|
if first_message_id:
|
||||||
message_ids.append(first_message_id)
|
message_ids.append(first_message_id)
|
||||||
|
if not first_url:
|
||||||
|
try:
|
||||||
|
first_url = self.message_url_from_response(await client.get_message(first_message_id))
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("MAX message URL lookup failed message={}: {}", first_message_id, exc)
|
||||||
if self.auto_reaction_enabled and self.reaction_path_template:
|
if self.auto_reaction_enabled and self.reaction_path_template:
|
||||||
try:
|
try:
|
||||||
await client.react(self.reaction_path_template, first_message_id, self.auto_reaction)
|
await client.react(self.reaction_path_template, first_message_id, self.auto_reaction)
|
||||||
|
|||||||
Reference in New Issue
Block a user