Revert "feat(posts): copy button on generated posts with copy-count analytics"
This reverts commit 88230c92b5.
This commit is contained in:
1 parent
37d8f80236
commit
796a77d7f7
5 files changed
+6
-69
No files matched your search
+1
-6
@@ -121,12 +121,7 @@ def all_metrics(period: str = "all", db_path: str | None = None) -> dict[str, ob
|
||||
f"SELECT mode, message_type, COUNT(*) AS total FROM events WHERE event_type='user_message' AND message_type IS NOT NULL{(' AND created_at >= ?' if period != 'all' else '')} GROUP BY mode, message_type",
|
||||
args,
|
||||
).fetchall()
|
||||
copy_where = " AND created_at >= ?" if where else ""
|
||||
copy_row = connection.execute(
|
||||
f"SELECT COUNT(*) AS total FROM events WHERE event_type = 'post_copy'{copy_where}", args
|
||||
).fetchone()
|
||||
result = {key: int(row[key] or 0) for key in ("users", "active_users", "post_generations", "idea_generations", "post_revisions", "generation_messages", "training_messages")}
|
||||
result["post_copies"] = int(copy_row["total"] or 0)
|
||||
result["message_types"] = {(str(r["mode"]), str(r["message_type"])): int(r["total"]) for r in types}
|
||||
result["period"] = period
|
||||
return result
|
||||
@@ -161,7 +156,7 @@ def render_report(period: str = "all", db_path: str | None = None) -> str:
|
||||
return (
|
||||
"<h2>📊 Статистика бота</h2>"
|
||||
+ table("Пользователи", [("Активные", "active_users")])
|
||||
+ table("Генерация", [("Посты", "post_generations"), ("Идеи", "idea_generations"), ("Правки", "post_revisions"), ("Сообщения", "generation_messages"), ("Копирований поста", "post_copies")])
|
||||
+ table("Генерация", [("Посты", "post_generations"), ("Идеи", "idea_generations"), ("Правки", "post_revisions"), ("Сообщения", "generation_messages")])
|
||||
+ "<h3>Типы сообщений в генерации</h3>" + _html_table(["Тип", *titles], generation_rows)
|
||||
+ table("Обучение", [("Сообщения", "training_messages")])
|
||||
+ "<h3>Типы сообщений в обучении</h3>" + _html_table(["Тип", *titles], training_type_rows)
|
||||
|
||||
@@ -41,7 +41,7 @@ from raw_updates import RawUpdateMiddleware, init_raw_updates_db
|
||||
|
||||
from apinex_client import APINEX_STT_URL, POST_HISTORY_CHAR_LIMIT, POST_HISTORY_LIMIT, classify_intent, classify_prompt_injection, create_tone_profile, create_training_summary, current_operation_id, diagnostic_operation, extract_posts_from_images, extract_transcript, generate_ideas, generate_posts, generate_posts_from_images, transcribe_audio
|
||||
from help_message import WELCOME_RICH_HTML, help_plain_view, help_view, rich_to_plain
|
||||
from keyboards import MAIN_MENU_VALUE, SUBSCRIPTION_ACTION_VALUE, SUPPORT_VALUE, TRAINING_INFO_VALUE, TRAINING_RESET_VALUE, help_inline_keyboard, main_menu_keyboard, mode_keyboard, post_copy_keyboard, resolve_button_text, subscription_plans_keyboard
|
||||
from keyboards import MAIN_MENU_VALUE, SUBSCRIPTION_ACTION_VALUE, SUPPORT_VALUE, TRAINING_INFO_VALUE, TRAINING_RESET_VALUE, help_inline_keyboard, main_menu_keyboard, mode_keyboard, resolve_button_text, subscription_plans_keyboard
|
||||
from payments import send_subscription_invoice
|
||||
import support
|
||||
import analytics
|
||||
@@ -758,21 +758,6 @@ async def clear_command(message: Message, state: FSMContext) -> None:
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("copy_post:"))
|
||||
async def post_copy_pressed(callback: CallbackQuery) -> None:
|
||||
"""Кнопка «Скопировать»: текст копирует сам Telegram (copy_text), здесь пишем только статистику."""
|
||||
if callback.from_user is not None:
|
||||
await asyncio.to_thread(analytics.record_event, callback.from_user.id, "generation", "post_copy")
|
||||
message = callback.message
|
||||
if isinstance(message, Message):
|
||||
try:
|
||||
await message.edit_reply_markup(reply_markup=None)
|
||||
except Exception as exc:
|
||||
if "message is not modified" not in str(exc).casefold():
|
||||
logger.debug("Copy button removal failed: %s", str(exc)[:120])
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith("training:reset:"))
|
||||
async def confirm_training_reset(callback: CallbackQuery, state: FSMContext) -> None:
|
||||
parts = (callback.data or "").split(":")
|
||||
@@ -901,8 +886,8 @@ async def _send_idea(message: Message, idea: dict[str, str], user_id: int) -> No
|
||||
|
||||
|
||||
async def _send_three_posts(message: Message, posts: list[str], user_id: int | None = None, origin: str = "generated") -> None:
|
||||
for index, post in enumerate(posts):
|
||||
await message.answer(post, reply_markup=post_copy_keyboard(post, index))
|
||||
for post in posts:
|
||||
await message.answer(post)
|
||||
if user_id is not None:
|
||||
await asyncio.to_thread(save_generated_posts, user_id, posts, origin)
|
||||
event_type = "post_revision" if origin == "revised" else "post_generation"
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from aiogram.types import (
|
||||
CopyTextButton,
|
||||
InlineKeyboardButton,
|
||||
InlineKeyboardMarkup,
|
||||
KeyboardButton,
|
||||
@@ -102,19 +101,6 @@ def subscription_plans_keyboard() -> InlineKeyboardMarkup:
|
||||
])
|
||||
|
||||
|
||||
def post_copy_keyboard(post_text: str, post_index: int) -> InlineKeyboardMarkup:
|
||||
"""Кнопка копирования поста: клик копирует текст (copy_text), нажатие пишется в статистику (callback_data)."""
|
||||
return InlineKeyboardMarkup(
|
||||
inline_keyboard=[[
|
||||
InlineKeyboardButton(
|
||||
text="📋 Скопировать",
|
||||
copy_text=CopyTextButton(text=post_text),
|
||||
callback_data=f"copy_post:{post_index}",
|
||||
),
|
||||
]],
|
||||
)
|
||||
|
||||
|
||||
def help_inline_keyboard() -> InlineKeyboardMarkup:
|
||||
"""Стандартная inline-клавиатура для навигации по разделам справки."""
|
||||
return InlineKeyboardMarkup(
|
||||
|
||||
@@ -3,7 +3,7 @@ import json
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, cast
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import apinex_client
|
||||
import bot
|
||||
@@ -1222,34 +1222,5 @@ class TrainingSummaryTests(unittest.IsolatedAsyncioTestCase):
|
||||
send.assert_not_awaited()
|
||||
|
||||
|
||||
class PostCopyButtonTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_send_three_posts_attaches_copy_button(self):
|
||||
message = SimpleNamespace(answer=AsyncMock())
|
||||
with (
|
||||
patch.object(bot, "save_generated_posts"),
|
||||
patch.object(bot.analytics, "record_event"),
|
||||
patch.object(bot.asyncio, "to_thread", new=AsyncMock()),
|
||||
):
|
||||
await bot._send_three_posts(cast(Any, message), ["Пост А", "Пост Б"], user_id=None)
|
||||
first = message.answer.await_args_list[0]
|
||||
button = first.kwargs["reply_markup"].inline_keyboard[0][0]
|
||||
self.assertEqual(button.copy_text.text, "Пост А")
|
||||
self.assertEqual(button.callback_data, "copy_post:0")
|
||||
|
||||
async def test_copy_callback_records_event_and_clears_button(self):
|
||||
callback = SimpleNamespace(
|
||||
from_user=SimpleNamespace(id=42),
|
||||
message=MagicMock(spec=bot.Message),
|
||||
answer=AsyncMock(),
|
||||
)
|
||||
callback.message.edit_reply_markup = AsyncMock()
|
||||
with patch.object(bot.asyncio, "to_thread", new=AsyncMock()) as to_thread:
|
||||
await bot.post_copy_pressed(cast(Any, callback))
|
||||
to_thread.assert_awaited_once()
|
||||
self.assertEqual(to_thread.await_args.args[1:], (42, "generation", "post_copy"))
|
||||
callback.message.edit_reply_markup.assert_awaited_once_with(reply_markup=None)
|
||||
callback.answer.assert_awaited_once()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user