diff --git a/VERSION b/VERSION
index 68cc106..94a02a2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.0+85
+0.1.0+84
diff --git a/analytics.py b/analytics.py
index 369d8aa..cf18871 100644
--- a/analytics.py
+++ b/analytics.py
@@ -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 (
"
π Π‘ΡΠ°ΡΠΈΡΡΠΈΠΊΠ° Π±ΠΎΡΠ°
"
+ table("ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΠΈ", [("ΠΠΊΡΠΈΠ²Π½ΡΠ΅", "active_users")])
- + table("ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ", [("ΠΠΎΡΡΡ", "post_generations"), ("ΠΠ΄Π΅ΠΈ", "idea_generations"), ("ΠΡΠ°Π²ΠΊΠΈ", "post_revisions"), ("Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ", "generation_messages"), ("ΠΠΎΠΏΠΈΡΠΎΠ²Π°Π½ΠΈΠΉ ΠΏΠΎΡΡΠ°", "post_copies")])
+ + table("ΠΠ΅Π½Π΅ΡΠ°ΡΠΈΡ", [("ΠΠΎΡΡΡ", "post_generations"), ("ΠΠ΄Π΅ΠΈ", "idea_generations"), ("ΠΡΠ°Π²ΠΊΠΈ", "post_revisions"), ("Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ", "generation_messages")])
+ "Π’ΠΈΠΏΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ Π² Π³Π΅Π½Π΅ΡΠ°ΡΠΈΠΈ
" + _html_table(["Π’ΠΈΠΏ", *titles], generation_rows)
+ table("ΠΠ±ΡΡΠ΅Π½ΠΈΠ΅", [("Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ", "training_messages")])
+ "Π’ΠΈΠΏΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ Π² ΠΎΠ±ΡΡΠ΅Π½ΠΈΠΈ
" + _html_table(["Π’ΠΈΠΏ", *titles], training_type_rows)
diff --git a/bot.py b/bot.py
index 6c642e5..ffab1a2 100644
--- a/bot.py
+++ b/bot.py
@@ -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"
diff --git a/keyboards.py b/keyboards.py
index 1e41882..fc6f84a 100644
--- a/keyboards.py
+++ b/keyboards.py
@@ -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(
diff --git a/tests/test_writer_features.py b/tests/test_writer_features.py
index 78c78aa..d67f1a1 100644
--- a/tests/test_writer_features.py
+++ b/tests/test_writer_features.py
@@ -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()
\ No newline at end of file