diff --git a/coddrago/modules/dbmod.py b/coddrago/modules/dbmod.py
index a8f69c9..42d0cfd 100644
--- a/coddrago/modules/dbmod.py
+++ b/coddrago/modules/dbmod.py
@@ -13,7 +13,7 @@ class DBMod(loader.Module):
"close_btn": "❌ Close",
"back_btn": "⬅ Back",
"del_btn": "🗑 Delete",
- "del_all_btn": "💣 Delete all",
+ "del_all_btn": "❌ Delete all",
"not_found": "🔍 Key {key} not found",
"invalid_key": "⚠ Invalid key",
"page": "📄 Page {current}/{total}",
@@ -34,7 +34,7 @@ class DBMod(loader.Module):
"close_btn": "❌ Закрыть",
"back_btn": "⬅ Назад",
"del_btn": "🗑 Удалить",
- "del_all_btn": "💣 Удалить все",
+ "del_all_btn": "❌ Удалить все",
"not_found": "🔍 Ключ {key} не найден",
"invalid_key": "⚠ Некорректный ключ",
"page": "📄 Страница {current}/{total}",
@@ -240,6 +240,7 @@ class DBMod(loader.Module):
{
"text": self.strings["del_all_btn"],
"callback": self.confirm_delete_all,
+ "style": "danger",
"args": [key_path],
}
]
@@ -290,6 +291,7 @@ class DBMod(loader.Module):
{
"text": self.strings["back_btn"],
"callback": self.navigate_db,
+ "style": "primary",
"args": [key_path[:-1], parent_page],
}
)
@@ -329,6 +331,7 @@ class DBMod(loader.Module):
{
"text": self.strings["del_all_btn"],
"callback": self.confirm_delete_all,
+ "style": "danger",
"args": [key_path],
}
]
@@ -344,13 +347,16 @@ class DBMod(loader.Module):
{
"text": self.strings["del_btn"],
"callback": self.delete_key,
+ "styles": "danger",
"args": [key_path],
}
],
[
{
"text": self.strings["back_btn"],
+ "style": "primary",
"callback": self.navigate_db,
+ "style": "primary",
"args": [key_path[:-1], parent_page],
}
],
@@ -363,6 +369,7 @@ class DBMod(loader.Module):
{
"text": self.strings["del_btn"],
"callback": self.delete_key,
+ "style": "danger",
"args": [key_path],
}
],
@@ -370,6 +377,7 @@ class DBMod(loader.Module):
{
"text": self.strings["back_btn"],
"callback": self.navigate_db,
+ "style": "primary",
"args": [key_path[:-1], parent_page],
}
],
diff --git a/fajox1/famods/cocoon.py b/fajox1/famods/cocoon.py
index fd5ce3b..aff16ad 100644
--- a/fajox1/famods/cocoon.py
+++ b/fajox1/famods/cocoon.py
@@ -11,26 +11,54 @@
# Description: Взаимодействие с Cocoon от HikkaHost
# meta developer: @FAmods & @vsecoder_m
# meta banner: https://github.com/FajoX1/FAmods/blob/main/assets/banners/cocoon.png?raw=true
-# requires: openai httpx aiohttp
+# requires: openai httpx aiohttp bs4 markdown
# ---------------------------------------------------------------------------------
+import re
import html
+import uuid
import httpx
import asyncio
import logging
+import markdown
from openai import AsyncOpenAI
from typing import Optional, Any
from dataclasses import dataclass
+from bs4 import BeautifulSoup, NavigableString
from datetime import datetime, timezone, timedelta
+from telethon.tl.types import User
from telethon.errors import MessageNotModifiedError
+from aiogram.exceptions import TelegramBadRequest
+
from .. import loader, utils
+from ..inline.types import InlineCall
logger = logging.getLogger(__name__)
+TG_ALLOWED = {
+ "b",
+ "strong",
+ "i",
+ "em",
+ "u",
+ "ins",
+ "s",
+ "strike",
+ "del",
+ "a",
+ "code",
+ "pre",
+ "blockquote",
+ "emoji",
+ "tg-emoji",
+}
+TAG_MAP = {"strong": "b", "em": "i", "del": "s", "strike": "s", "ins": "u"}
+
+
@dataclass(frozen=True)
class Usage:
spent_nano: int
@@ -110,6 +138,92 @@ def _percent_remaining(spent: int, total: int) -> float:
return (remaining / total) * 100.0
+def md_to_tg_html(text: str) -> str:
+ if not text:
+ return ""
+
+ raw_html = markdown.markdown(text, extensions=["fenced_code", "tables", "nl2br"])
+ soup = BeautifulSoup(raw_html, "html.parser")
+
+ def stringify(node, lang=None):
+ res = ""
+
+ for child in node.children:
+ if isinstance(child, NavigableString):
+ res += html.escape(str(child))
+
+ elif child.name:
+ tag_name = child.name
+
+ if tag_name in ["h1", "h2", "h3", "h4", "h5", "h6"]:
+ content = stringify(child)
+ res += f"{content}\n\n"
+ elif tag_name == "p":
+ res += stringify(child) + "\n\n"
+ elif tag_name == "br":
+ res += "\n"
+ elif tag_name == "li":
+ res += f"• {stringify(child)}\n"
+ elif tag_name in ["ul", "ol"]:
+ res += stringify(child) + "\n"
+ elif tag_name == "tr":
+ res += "| " + stringify(child) + "\n"
+ elif tag_name in ["td", "th"]:
+ res += stringify(child) + " | "
+
+ else:
+ target_tag = TAG_MAP.get(tag_name, tag_name)
+
+ if target_tag in TG_ALLOWED:
+ inner_html = stringify(child)
+
+ if not inner_html.strip() and target_tag not in ["code", "pre"]:
+ res += inner_html
+ continue
+
+ if target_tag == "a":
+ href = child.get("href", "")
+ if href:
+ res += f'{inner_html}'
+ else:
+ res += inner_html
+ elif target_tag == "code":
+ cls = child.get("class", [])
+ if cls and cls[0].startswith("language-"):
+ res += f'{inner_html}'
+ else:
+ res += f"{inner_html}"
+ elif target_tag == "pre":
+ res += f"
{inner_html}"
+ else:
+ res += f"<{target_tag}>{inner_html}{target_tag}>"
+ else:
+ res += stringify(child)
+ return res
+
+ final_text = stringify(soup)
+
+ final_text = re.sub(r"\n{3,}", "\n\n", final_text)
+ return final_text.strip()
+
+
+def repair_html_tags(html_chunk: str) -> str:
+ if not html_chunk:
+ return ""
+
+ newline_placeholder = f"MARKER_{uuid.uuid4().hex}"
+
+ protected_html = html_chunk.replace("\n", newline_placeholder)
+
+ soup = BeautifulSoup(protected_html, "html.parser")
+
+ repaired_html = soup.decode_contents(formatter=None)
+
+ final_html = repaired_html.replace(newline_placeholder, "\n")
+
+ return final_html
+
+
@loader.tds
class Cocoon(loader.Module):
"""Взаимодействие с Cocoon от HikkaHost"""
@@ -126,24 +240,25 @@ class Cocoon(loader.Module):
"{thoughts}…" ), "answer": ( - "
{thoughts}\n\n" - "
{model}"
+ "{model}"
),
"usage": (
- "