import re import time import shelve import requests from datetime import datetime from aiogram.methods import AnswerGuestQuery, SendPoll from aiogram.types import InlineQueryResultArticle, InputTextMessageContent, InputRichMessageContent, InputRichMessage, Message from aiogram.enums import ChatType from vars import * from config import * def is_blocked(user_id): current_time = time.time() if user_id and user_id in last_command_time: time_diff = current_time - last_command_time[user_id][0] if user_id in last_error_time: time_error_diff = current_time - last_error_time[user_id] else: last_error_time[user_id] = current_time time_error_diff = 1984 if time_diff < 3: alo = "🚫 *Ρ„Π°ΠΉΠ» Π½Π΅ Π²ΠΎΡˆΡ‘Π».* Ρ…Π²Π°Ρ‚ΠΈΡ‚ Ρ‚Π°ΠΊ быстро ΡΠ»Π°Ρ‚ΡŒ свои сообщСния.\n" \ f"ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ {round(3 - time_diff, 3)} сСкунд, Π±Ρ€Π°Ρ‚πŸ˜‘πŸ˜‘" elif last_command_time[user_id][1] and time_diff < 5: alo = "🚫 *Ρ„Π°ΠΉΠ» Π½Π΅ Π²ΠΎΡˆΡ‘Π».* я Π΅Ρ‰Ρ‘ Π½Π΅ сгСнСрировал ΠΏΡ€ΠΎΡˆΠ»ΠΎΠ΅ сообщСниС.\n" \ f"ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ {round(10 - time_diff, 3)} сСкунд, Ρ‡Ρ‚ΠΎΠ±Ρ‹ я Π·Π°ΠΊΡ€Ρ‹Π» Π³Π»Π°Π·Π° " \ "Π½Π° это da." elif len(last_command_time[user_id][2]) > 1 and time_diff > 5: last_command_time[user_id] = [current_time, False, []] return "", False elif len(last_command_time[user_id][2]) > 1: alo = "🚫 *Ρ„Π°ΠΉΠ» Π½Π΅ Π²ΠΎΡˆΡ‘Π».* Ρ…Π²Π°Ρ‚ΠΈΡ‚ Ρ‚Π°ΠΊ быстро ΡΠ»Π°Ρ‚ΡŒ свои сообщСния." else: return "", False if time_error_diff < 2: return "", True else: last_error_time[user_id] = current_time return alo, True return "", False def is_replied_bot(message, is_channel, user_text, config=None): if message.chat.type in [ChatType.GROUP, ChatType.SUPERGROUP, ChatType.CHANNEL]: if not is_channel: is_reply_to_bot = ( message.reply_to_message and message.reply_to_message.from_user and message.reply_to_message.from_user.is_bot and message.reply_to_message.from_user.username == BOT_USERNAME ) if config: if config.get("call") == "da": mentions_bot = any( user_text.lower().split(" ", 1)[0] == call for call in BOT_CALLS ) for call in BOT_CALLS: if call.startswith("@"): if call.lower() in user_text.lower(): mentions_bot = True else: mentions_bot = False else: mentions_bot = any( user_text.lower().startswith(call.lower()) for call in BOT_CALLS ) for call in BOT_CALLS: if call.startswith("@"): if call.lower() in user_text.lower(): mentions_bot = True if not (is_reply_to_bot or mentions_bot): return False return True async def g_answer( message, text, title="da", id="1", parse_mode="Markdown", reply_markup=None, user_id=None ): input_message_content = InputTextMessageContent( message_text=text, parse_mode=parse_mode ) if user_id: config = get_user_config(user_id) if config["markdown"] == "new": input_message_content=InputRichMessageContent( rich_message=InputRichMessage(markdown=text) ) return await message.answer_guest_query( result=InlineQueryResultArticle( id=id, reply_markup=reply_markup, title=title, input_message_content=input_message_content ) ) async def answer_poll( data, message, question, options, is_anonymous=True, poll_type="regular", allows_multiple_answers=False, correct_option_id=None, user_id=None ): return await data["bot"]( SendPoll( chat_id=user_id, question=question, options=options, is_anonymous=is_anonymous, type=poll_type, allows_multiple_answers=allows_multiple_answers, correct_option_id=correct_option_id, ) ) def send_to_vosk(audio): files = { "audio": ( "voice.ogg", audio, "audio/ogg" ) } r = requests.post( VOSK_API, files=files, timeout=15 ) return r.json() async def mans(message, text): if message.guest_query_id: return await message.answer_guest_query( result=InlineQueryResultArticle( id=1, reply_markup=None, title="da", input_message_content=InputTextMessageContent( message_text=text, parse_mode="Markdown" ) ) ) else: return await message.reply(text) def graph_emoji(status): match status: case "done": return "βœ…" case "error": return "🚫" case "make": return "πŸ’­" case "wait": return "⏳" case _: return "πŸ₯Ί" def make_graph(message, process): text = "" replied = message.reply_to_message or None user_id = message.from_user.id config = get_user_config(user_id) im_model = IMAGE_MODEL.split("*", 1) model = config["model"].split("*", 1) photo_a = process.get("photo_a", None) photo = process.get("photo", None) stt = process.get("stt", None) t = process.get("text", None) if photo_a: text += f"{graph_emoji(photo_a)} Π°Π½Π°Π»ΠΈΠ· Ρ„ΠΎΡ‚ΠΎ (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if photo: text += f"{graph_emoji(photo)} Π°Π½Π°Π»ΠΈΠ· Ρ„ΠΎΡ‚ΠΎ\n" if stt: text += f"{graph_emoji(stt)} stt (голос -> тСкст)\n" text += f"{graph_emoji(t)} запрос `{model[1]}` (`{model[0]}`)\n" if replied and replied.document: text += f"↳ {replied.document.file_size} Ρ„Π°ΠΉΠ» (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if message.document: text += f"↳ {message.document.file_size} Ρ„Π°ΠΉΠ»\n" if replied and replied.photo: text += "↳ Ρ„ΠΎΡ‚ΠΎ (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if message.photo: text += "↳ Ρ„ΠΎΡ‚ΠΎ\n" if replied and replied.location: text += "↳ гСолокация (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if message.location: text += "↳ гСолокация\n" if replied and replied.poll: text += "↳ опрос (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if message.poll: text += "↳ опрос\n" if replied and replied.voice: text += "↳ голосовоС сообщСниС (Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅)\n" if message.voice: text += "↳ голосовоС сообщСниС\n" if replied and replied.text: text += f"↳ {len(replied.text)} тСкст Π² ΠΎΡ‚Π²Π΅Ρ‚Π΅\n" return text def clean_tail_repeats(text: str, max_repeat: int = 10): if not text: return text, 0 original_length = len(text) cleaned = text pattern = rf"(.)\1{{{max_repeat},}}$" def limit_repeats(match): char = match.group(1) return char * max_repeat cleaned = re.sub(pattern, limit_repeats, cleaned) pattern_space = rf"((\S)\s+)\2{{{max_repeat},}}$" cleaned = re.sub( pattern_space, lambda m: (m.group(2) + " ") * min(max_repeat, len(m.group(1))), cleaned, ) words = cleaned.split() if len(words) >= 2: for i in range(1, min(len(words), 10)): if all(words[-j] == words[-j - 1] for j in range(i)): unique_words = words[: -(i + 1)] cleaned = " ".join(unique_words + [words[-1]]) break removed_count = original_length - len(cleaned) cleaned = re.sub(r" +", " ", cleaned) cleaned = cleaned.strip() return cleaned, removed_count def get_key(provider, model_full): prov = PROVIDERS.get(provider, {}) key = prov.get("key", "") if isinstance(key, list): parts = model_full.split("*") if len(parts) >= 3: try: idx = int(parts[2]) - 1 except ValueError: return key[0] if 0 <= idx < len(key): return key[idx] return key[0] return key _expanded_auto_models = None def expand_auto_models(): global _expanded_auto_models if _expanded_auto_models is not None: return _expanded_auto_models result = [] for entry in AUTO_MODELS: parts = entry.split("*", 2) if len(parts) < 2: continue provider = parts[0] model = parts[1] suffix = parts[2] if len(parts) >= 3 else None prov_key = PROVIDERS.get(provider, {}).get("key", "") if isinstance(prov_key, list) and not suffix: for i in range(len(prov_key)): result.append(f"{provider}*{model}*{i + 1}") else: result.append(entry) _expanded_auto_models = result return result def parse_response(text: str): if "connection error." in text: return { "type": "connection", "message": "ошибка ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Ρƒ. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° Π² настройках ΡƒΠ·Π±Π΅ΠΊΠ³ΠΏΡ‚.", } elif "model quota exceeded" in text: return { "type": "quota", "message": "Π»ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² закончился. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } elif "tier capacity exceeded." in text: return {"type": "tier", "message": "ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠΉ Π΅Ρ‰Ρ‘ Ρ€Π°Π·!"} elif "internal server" in text: return { "type": "int", "message": "ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° ошибка Π½Π° сторонС ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π°. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° Π² настройках ΡƒΠ·Π±Π΅ΠΊΠ³ΠΏΡ‚.", } elif "(incomplete chunked read)" in text: return { "type": "incomplete", "message": "сСрвСр ΠΏΠ΅Ρ‚ΡƒΡ… ΠΈ ΠΎΠ±ΠΎΡ€Π²Π°Π» соСдинСниС. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° Π² настройках ΡƒΠ·Π±Π΅ΠΊΠ³ΠΏΡ‚.", } elif "provider error" in text: return { "type": "provider", "message": "Π²Ρ€Π°Ρ‚ ошибка Π½Π° сторонС ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° ΠΌΠΎΠ΄Π΅Π»ΠΈ. смСни ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° ΠΈΠ»ΠΈ модСль.", } elif "rate limit" in text: return { "type": "quota", "message": "Π»ΠΈΠΌΠΈΡ‚ Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² закончился Ρƒ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° Π²Ρ€Π°Ρ‚. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } elif "response payload is not completed" in text or "not enough data to satisfy" in text or "server disconnected" in text or "connection reset" in text or "serverdisconnectederror" in text: return { "type": "incomplete", "message": "сСрвСр ΠΏΠ΅Ρ‚ΡƒΡ… ΠΈ ΠΎΠ±ΠΎΡ€Π²Π°Π» ΠΎΡ‚Π²Π΅Ρ‚ Π½Π° сСрСдинС. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } elif "timeout" in text or "timed out" in text: return { "type": "timeout", "message": "сСрвСр Π΄ΡƒΠΌΠ°Π» слишком Π΄ΠΎΠ»Π³ΠΎ ΠΈ ΡƒΠΌΠ΅Ρ€. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } elif "not found" in text or " 404" in text or "404 " in text or "no such model" in text or "does not exist" in text: return { "type": "notfound", "message": "модСль Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Π° Ρƒ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π°. смСни модСль Π² настройках.", } elif "forbidden" in text or "unauthorized" in text or " 403" in text or "403 " in text or " 401" in text or "401 " in text or "invalid api key" in text or "incorrect api key" in text: return { "type": "key", "message": "ΠΊΠ»ΡŽΡ‡ ΠΈΠ»ΠΈ доступ ΠΊ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Ρƒ сдох. смСни модСль ΠΈΠ»ΠΈ напиши хостСру.", } elif " 429" in text or "429 " in text or "exhausted" in text or "quota" in text: return { "type": "quota", "message": "Π»ΠΈΠΌΠΈΡ‚ запросов Ρƒ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° закончился. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } elif " 500" in text or "500 " in text or " 502" in text or "502 " in text or " 503" in text or "503 " in text or "bad gateway" in text: return { "type": "int", "message": "ошибка Π½Π° сторонС ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π°. ΠΏΠΎΠ΄ΠΎΠΆΠ΄ΠΈ ΠΈΠ»ΠΈ смСни модСль.", } else: return { "type": "unknown", "message": f"ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»Π° какая-Ρ‚ΠΎ ошибка ΠΏΡ€ΠΈ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ... {text}", } def galockinator(text): for _ in range(random.randint(1, 3)): if random.random() < 0.3: text += "☝️" else: text += "βœ…" return text def remove_think_tags(text): result = re.sub(r".*?", "", text, flags=re.DOTALL) result = re.sub(r".*?", "", text, flags=re.DOTALL) return result def get_all_users(only_notify=False): with shelve.open("users") as db: return {k: db[k] for k in db.keys() if not only_notify or db[k].get("notify") == "da"} def get_all_user_ids(only_notify=False): ids = [] with shelve.open("users") as db: for user_id in db.keys(): if only_notify and db[user_id].get("notify") != "da": continue try: ids.append(int(user_id)) except ValueError: pass return ids def set_user_config( user_id, model_name, stream=DEFAULT_STREAM, call=DEFAULT_CALL, prompt=DEFAULT_PROMPT, notify=DEFAULT_NOTIFY, markdown=DEFAULT_MARKDOWN, prompts={}, ): with shelve.open("users") as db: db[str(user_id)] = { "model": model_name, "stream": stream, "call": call, "prompt": prompt, "notify": notify, "markdown": markdown, "prompts": prompts, } def set_user_setting( user_id, model=None, stream=None, call=None, prompt=None, notify=None, markdown=None, prompts=None, ): with shelve.open("users") as db: user_id = str(user_id) if user_id not in db: db[user_id] = {} settings = db[user_id] if model is not None: settings["model"] = model if stream is not None: settings["stream"] = stream if call is not None: settings["call"] = call if prompt is not None: settings["prompt"] = prompt if notify is not None: settings["notify"] = notify if markdown is not None: settings["markdown"] = markdown if prompts is not None: settings["prompts"] = prompts db[user_id] = settings def delete_user_from_db(user_id): with shelve.open("users") as db: if str(user_id) in db: del db[str(user_id)] def reset_user_setting(user_id, key, value): with shelve.open("users") as db: user = db[str(user_id)] user[key] = value db[str(user_id)] = user def ensure_user( user_id, model_name=DEFAULT_MODEL, stream_mode=DEFAULT_STREAM, call_mode=DEFAULT_CALL, prompt_name=DEFAULT_PROMPT, notify_mode=DEFAULT_NOTIFY, markdown=DEFAULT_MARKDOWN, prompts={}, ): with shelve.open("users") as db: key = str(user_id) if key not in db: db[key] = { "model": model_name, "stream": stream_mode, "call": call_mode, "prompt": prompt_name, "notify": notify_mode, "markdown": markdown, "prompts": prompts, } def get_user_config(user_id): with shelve.open("users") as db: config = db.get(str(user_id), {}) defaults = { "model": DEFAULT_MODEL, "stream": DEFAULT_STREAM, "call": DEFAULT_CALL, "prompt": DEFAULT_PROMPT, "notify": DEFAULT_NOTIFY, "markdown": DEFAULT_MARKDOWN, "prompts": {}, } updated = False for key, value in defaults.items(): if key not in config: config[key] = value updated = True if updated: db[str(user_id)] = config return config def get_group_config(group_id): with shelve.open("groups") as db: config = db.get(str(group_id), {}) defaults = { "post_prompt": SYSTEM_PROMPTS[DEFAULT_PROMPT]["text"]+ADD_TO_PROMPT+OLD_MD, "post_reply": "da", "polls": "da", "lurk": "da", "lurk_chance": 10, "lurk_reply_chance": 2, } updated = False for key, value in defaults.items(): if key not in config: config[key] = value updated = True if updated: db[str(group_id)] = config return config def set_group_setting( group_id, post_prompt=None, post_reply=None, polls=None, lurk=None, lurk_chance=None, lurk_reply_chance=None, ): with shelve.open("groups") as db: group_id = str(group_id) if group_id not in db: db[group_id] = {} settings = db[group_id] if post_prompt is not None: settings["post_prompt"] = post_prompt if post_reply is not None: settings["post_reply"] = post_reply if polls is not None: settings["polls"] = polls if lurk is not None: settings["lurk"] = lurk if lurk_chance is not None: settings["lurk_chance"] = lurk_chance if lurk_reply_chance is not None: settings["lurk_reply_chance"] = lurk_reply_chance db[group_id] = settings def apply_time_tags(text): now = datetime.now() text = text.replace("{{date}}", now.strftime("%d.%m.%Y")) text = text.replace("{{time}}", now.strftime("%H:%M")) text = text.replace("{{now}}", now.strftime("%d.%m.%Y %H:%M")) return text def web_search(query): r = requests.get( "https://lite.duckduckgo.com/lite/", params={"q": query}, headers={"User-Agent": "Mozilla/5.0"}, timeout=10 ) r.raise_for_status() links = re.findall(r"]*class=['\"]result-link['\"][^>]*>(.*?)", r.text, re.DOTALL) snippets = re.findall(r"]*class=['\"]result-snippet['\"][^>]*>(.*?)", r.text, re.DOTALL) results = [] for i in range(min(5, len(links))): title = re.sub(r"<[^>]+>", "", links[i]).strip() snippet = re.sub(r"<[^>]+>", "", snippets[i]).strip()[:300] if i < len(snippets) else "" results.append(f"{title}\n{snippet}") if not results: return "Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ нашлось" return "\n\n".join(results) def wiki_search(query): r = requests.get( "https://ru.wikipedia.org/w/rest.php/v1/search/page", params={"q": query, "limit": 3}, headers={"User-Agent": "uzbekgpt/1.0"}, timeout=10 ) r.raise_for_status() results = [] for p in r.json().get("pages", []): title = p.get("title", "") excerpt = re.sub(r"<[^>]+>", "", p.get("excerpt", "")) results.append(f"{title}\n{excerpt[:400]}") if not results: return "Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ нашлось Π² Π²ΠΈΠΊΠΈΠΏΠ΅Π΄ΠΈΠΈ" return "\n\n".join(results) def extract_tool(text): m = re.search(r"(.*?)", text, re.DOTALL) if m: return "search", m.group(1).strip() m = re.search(r"(.*?)", text, re.DOTALL) if m: return "wiki", m.group(1).strip() return None def add_tokens(user_id, input_t, output_t): with shelve.open("tokens") as db: key = str(user_id) data = db.get(key, {"input": 0, "output": 0, "requests": 0}) data["input"] += input_t data["output"] += output_t data["requests"] += 1 db[key] = data def get_tokens(user_id): with shelve.open("tokens") as db: return db.get(str(user_id), {"input": 0, "output": 0, "requests": 0}) async def alert_admin(bot, provider_name, error_text): current = time.time() if provider_name in provider_alert_time and current - provider_alert_time[provider_name] < 600: return provider_alert_time[provider_name] = current try: await bot.send_message(ADMIN_ID, f"🚨 ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€ `{provider_name}` каТСтся ΡƒΠΏΠ°Π»:\n{str(error_text)[:300]}") except Exception as e: print(f" -- alert admin - {e}")