diff --git a/.env.sample b/.env.sample index 51b53d7bb7..39401bc3af 100644 --- a/.env.sample +++ b/.env.sample @@ -1,4 +1,4 @@ -# Don't use quotes( " and ' ) +# Don't use quotes( " or ' ) API_ID= API_HASH= diff --git a/.github/workflows/pylint.yaml b/.github/workflows/pylint.yaml index 8c71add5c5..73d091aed8 100644 --- a/.github/workflows/pylint.yaml +++ b/.github/workflows/pylint.yaml @@ -1,7 +1,9 @@ -name: PyLint +name: PyLint Ultroid on: push: - branches: [ dev ] + branches: + - patch-1 + - patch-2 paths: - "**.py" jobs: @@ -12,22 +14,26 @@ jobs: - name: Setup Python uses: actions/setup-python@v1 with: - python-version: 3.8 + python-version: 3.9 cache: "pip" - name: Install Python lint libraries run: pip install autopep8 autoflake isort black - name: Check for showstoppers run: | + autopep8 --verbose --in-place --recursive --aggressive --aggressive pyUltroid/*.py autopep8 --verbose --in-place --recursive --aggressive --aggressive assistant/*.py autopep8 --verbose --in-place --recursive --aggressive --aggressive assistant/manager/*.py autopep8 --verbose --in-place --recursive --aggressive --aggressive plugins/*.py - name: Remove unused imports and variables run: | + autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports pyUltroid/*.py autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports assistant/*.py autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports assistant/manager/*.py autoflake --in-place --recursive --remove-all-unused-imports --remove-unused-variables --ignore-init-module-imports plugins/*.py - name: lint with isort and black run: | + isort --profile black pyUltroid/*.py + black --fast pyUltroid/*.py isort assistant/*.py isort assistant/manager/*.py black --fast assistant/*.py @@ -37,9 +43,8 @@ jobs: - uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: 'pylint: auto fixes' - commit_options: '--no-verify' + commit_options: '--verify' repository: . - commit_user_name: buddhhu - commit_user_email: 48654350+buddhhu@users.noreply.github.com - commit_author: Amit Sharma <48654350+buddhhu@users.noreply.github.com> - + commit_user_name: ufoptg + commit_user_email: ufperth@protonmail.com + commit_author: ufoptg diff --git a/assistant/callbackstuffs.py b/assistant/callbackstuffs.py index 3cdd332044..6799a0d7a4 100644 --- a/assistant/callbackstuffs.py +++ b/assistant/callbackstuffs.py @@ -176,6 +176,8 @@ def text_to_url(event): "buttons": [ [Button.inline("Remove.bg API", data="abs_rmbg")], [Button.inline("DEEP API", data="abs_dapi")], + [Button.inline("OpenAI API", data="abs_openapi")], + [Button.inline("šŸŒ€ŹŠŹ„āŠ•Ö„šŸŒ€ API", data="abs_uapi")], [Button.inline("OCR API", data="abs_oapi")], [Button.inline("Ā« Back", data="setter")], ], @@ -195,6 +197,18 @@ def text_to_url(event): "text": "Get Your Deep Api from deepai.org and send here.", "back": "cbs_apiset", }, + "uapi": { + "var": "UFOPAPI", + "name": "UFoP API Key", + "text": "Contact šŸŒ€ŹŠŹ„āŠ•Ö„šŸŒ€ or Visit @PerthUnity_Bot Support Group\n\n /cancel to cancel", + "back": "cbs_apiset", + }, + "openapi": { + "var": "OPENAI_API", + "name": "OPENAI API Key", + "text": "Visit openai.com for an OPENAI Api key!\n\n /cancel to cancel", + "back": "cbs_apiset", + }, "oapi": { "var": "OCR_API", "name": "Ocr Api Key", @@ -556,7 +570,9 @@ async def emoji(event): var = "EMOJI_IN_HELP" name = f"Emoji in `{HNDLR}help` menu" async with event.client.conversation(pru) as conv: - await conv.send_message("Send emoji u want to set šŸ™ƒ.\n\nUse /cancel to cancel.") + await conv.send_message( + "Send emoji u want to set šŸ™ƒ.\n\nUse /cancel to cancel." + ) response = conv.wait_event(events.NewMessage(chats=pru)) response = await response themssg = response.message.message diff --git a/assistant/pmbot.py b/assistant/pmbot.py index 316f8eae6d..9fa8ba584d 100644 --- a/assistant/pmbot.py +++ b/assistant/pmbot.py @@ -9,6 +9,8 @@ # --------------------------------------- Imports -------------------------------------------- # +import asyncio +import logging import os from telethon.errors.rpcerrorlist import UserNotParticipantError @@ -25,8 +27,59 @@ from . import * botb = KeyManager("BOTBLS", cast=list) -FSUB = udB.get_key("PMBOT_FSUB") +FSUB = Keys.PMBOT_FSUB +PMBOTGROUP = Keys.LOG_CHANNEL CACHE = {} +SUDOS = Keys.SUDOS +PMUSERS = [OWNER_ID, SUDOS] +logging.basicConfig( + format="%(asctime)s | %(name)s [%(levelname)s] : %(message)s", + level=logging.INFO, + datefmt="%m/%d/%Y, %H:%M:%S", +) +logger = logging.getLogger("DEBUGGING") + +# --------------------------------------- Functions -------------------------------------------- # + + +async def forward_to_multiple(event, *user_ids): + results = [] + tasks = [] + + for user_id in user_ids: + task = asyncio.create_task(event.forward_to(user_id)) + tasks.append(task) + + for task in tasks: + try: + result = await task + results.append(result) + except Exception as e: + results.append(str(e)) + + return results + + +async def check_reply_from_bot(event): + if (event.is_private and event.is_reply) or ( + event.chat_id == PMBOTGROUP and event.is_reply and event.reply_to_msg_id + ): + if event.chat_id == PMBOTGROUP: + replied_message = await event.client.get_messages( + event.chat_id, ids=event.reply_to_msg_id + ) + if replied_message.from_id: + entity = replied_message.from_id.user_id + else: + return False + return entity == 6176247391 + else: + # For private messages, no need to check the entity, as it's + # already a reply + return True + return False + + # --------------------------------------- Incoming -------------------------------------------- # @@ -37,7 +90,6 @@ ) async def on_new_mssg(event): who = event.sender_id - # doesn't reply to that user anymore if event.text.startswith("/") or who == OWNER_ID: return if FSUB: @@ -72,9 +124,12 @@ async def on_new_mssg(event): if MSG and BTTS: return await event.reply(MSG, buttons=BTTS) xx = await event.forward_to(OWNER_ID) + zz = await event.forward_to(PMBOTGROUP) if event.fwd_from: await xx.reply(f"From {inline_mention(event.sender)} [`{event.sender_id}`]") + await zz.reply(f"From {inline_mention(event.sender)} [`{event.sender_id}`]") add_stuff(xx.id, who) + add_stuff(zz.id, who) # --------------------------------------- Outgoing -------------------------------------------- # @@ -82,30 +137,74 @@ async def on_new_mssg(event): @asst_cmd( load=AST_PLUGINS, - from_users=[OWNER_ID], + from_users=PMUSERS, incoming=True, - func=lambda e: e.is_private and e.is_reply, + func=check_reply_from_bot, ) async def on_out_mssg(event): x = event.reply_to_msg_id - to_user = get_who(x) + logger.info(f"msg_id: {x}") + if event.chat_id == PMBOTGROUP: + group_to_user = get_who(x) + else: + to_user = get_who(x) + + if event.reply_to_msg_id: + replied_message = await event.client.get_messages( + event.chat_id, ids=event.reply_to_msg_id + ) + if ( + replied_message + and replied_message.fwd_from + and replied_message.fwd_from.from_id + and replied_message.fwd_from.from_id.user_id != 6176247391 + ): + return if event.text.startswith("/who"): try: - k = await asst.get_entity(to_user) - photu = await event.client.download_profile_photo(k.id) - await event.reply( - f"ā€¢ **Name :** {get_display_name(k)}\nā€¢ **ID :** `{k.id}`\nā€¢ **Link :** {inline_mention(k)}", - file=photu, - ) - if photu: - os.remove(photu) - return + if event.is_private and to_user: + k = await asst.get_entity(to_user) + photu = await event.client.download_profile_photo(k.id) + await event.reply( + f"ā€¢ **Name :** {get_display_name(k)}\nā€¢ **ID :** `{k.id}`\nā€¢ **Link :** {inline_mention(k)}", + file=photu, + ) + if photu: + os.remove(photu) + return + elif event.chat_id == PMBOTGROUP and group_to_user: + k = await asst.get_entity(group_to_user) + photu = await event.client.download_profile_photo(k.id) + await event.reply( + f"ā€¢ **Name :** {get_display_name(k)}\nā€¢ **ID :** `{k.id}`\nā€¢ **Link :** {inline_mention(k)}", + file=photu, + ) + if photu: + os.remove(photu) + return + else: + return await event.reply( + "Unable to determine the user. Please reply to a specific message." + ) except BaseException as er: return await event.reply(f"**ERROR : **{str(er)}") elif event.text.startswith("/"): return - if to_user: - await asst.send_message(to_user, event.message) + + if event.chat_id == PMBOTGROUP: + if group_to_user: + await asst.send_message(group_to_user, event.message) + else: + return await event.reply( + "Unable to determine the user. Please reply to a specific message." + ) + elif event.sender_id in PMUSERS: + if to_user: + await asst.send_message(to_user, event.message) + else: + return await event.reply( + "Unable to determine the user. Please reply to a specific message." + ) # --------------------------------------- Ban/Unban -------------------------------------------- # diff --git a/assistant/start.py b/assistant/start.py index c8dcb3c700..12226ed032 100644 --- a/assistant/start.py +++ b/assistant/start.py @@ -115,17 +115,21 @@ async def ultroid(event): await event.reply( f"Hey there {mention}, this is Ultroid Assistant of {me}!\n\n{ok}", file=udB.get_key("STARTMEDIA"), - buttons=[Button.inline("Info.", data="ownerinfo")] - if Owner_info_msg - else None, + buttons=( + [Button.inline("Info.", data="ownerinfo")] + if Owner_info_msg + else None + ), ) else: await event.reply( udB.get_key("STARTMSG").format(me=me, mention=mention), file=udB.get_key("STARTMEDIA"), - buttons=[Button.inline("Info.", data="ownerinfo")] - if Owner_info_msg - else None, + buttons=( + [Button.inline("Info.", data="ownerinfo")] + if Owner_info_msg + else None + ), ) else: name = get_display_name(event.sender) diff --git a/plugins/_chatactions.py b/plugins/_chatactions.py index 8f1a9d3baa..a8e849dfa2 100644 --- a/plugins/_chatactions.py +++ b/plugins/_chatactions.py @@ -6,7 +6,9 @@ # . import asyncio +from typing import Union +import requests from telethon import events from telethon.errors.rpcerrorlist import UserNotParticipantError from telethon.tl.functions.channels import GetParticipantRequest @@ -18,8 +20,13 @@ from pyUltroid.dB.gban_mute_db import is_gbanned from pyUltroid.dB.greetings_db import get_goodbye, get_welcome, must_thank from pyUltroid.dB.nsfw_db import is_profan -from pyUltroid.fns.helper import inline_mention -from pyUltroid.fns.tools import async_searcher, create_tl_btn, get_chatbot_reply +from pyUltroid.fns.helper import check_reply_to, inline_mention +from pyUltroid.fns.tools import ( + async_searcher, + create_tl_btn, + get_chatbot_reply, + get_oracle_reply, +) try: from ProfanityDetector import detector @@ -28,6 +35,38 @@ from . import LOG_CHANNEL, LOGS, asst, get_string, types, udB, ultroid_bot from ._inline import something +# ------------------------- UFoP Bans -------------------------# + + +class UFoPBan: + def __init__(self, api_key: str = None): + self.api_key = api_key + + def _make_request( + self, method: str, url: str, params: dict = None, json_data: dict = None + ): + headers = {"accept": "application/json", "api-key": self.api_key} + try: + response = requests.request( + method, url, headers=headers, params=params, json=json_data + ) + return response.json() + except requests.RequestException: + pass + + def get_ufop_ban( + self, user_id: int = None, banlist: bool = False + ) -> Union[dict, str]: + if banlist: + url = "https://ufoptg-ufop-api.hf.space/UFoP/bans" + payload = {"user_id": user_id} + return self._make_request("GET", url, params=payload) + else: + raise ValueError("Error: banlist must be True") + + +# ------------------------- Huge Thanks to @xtdevs -------------------------# + @ultroid_bot.on(events.ChatAction()) async def Function(event): @@ -97,6 +136,30 @@ async def DummyHandler(ult): except BaseException: pass + + if udB.get_key("UFoP_BANS"): + ufop_api_key = udB.get_key("UFOPAPI") + clients = UFoPBan(ufop_api_key) + try: + UFoP_banned = clients.get_ufop_ban(user_id=user.id, banlist=True) + + if UFoP_banned and UFoP_banned.get("sukuna", {}).get( + "is_banned", False + ): + await ult.client.edit_permissions( + chat.id, + user.id, + view_messages=False, + ) + await ult.respond( + f"**šŸŒ€ŹŠŹ„āŠ•Ö„šŸŒ€:** Banned user detected and banned!\n" + f'Sibyl User ID: {UFoP_banned["sukuna"]["sibyl_user_id"]}\n' + f'Ban Reason: {UFoP_banned["sukuna"]["reason"]}', + ) + + except Exception as e: + LOGS.exception(f"Error checking UFoP: {e}") + reason = is_gbanned(user.id) if reason and chat.admin_rights: try: @@ -195,30 +258,112 @@ async def DummyHandler(ult): @ultroid_bot.on(events.NewMessage(incoming=True)) async def chatBot_replies(e): - sender = await e.get_sender() - if not isinstance(sender, types.User) or sender.bot: - return - if check_echo(e.chat_id, e.sender_id): - try: - await e.respond(e.message) - except Exception as er: - LOGS.exception(er) - key = udB.get_key("CHATBOT_USERS") or {} - if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]: - msg = await get_chatbot_reply(e.message.message) - if msg: - sleep = udB.get_key("CHATBOT_SLEEP") or 1.5 - await asyncio.sleep(sleep) - await e.reply(msg) - chat = await e.get_chat() - if e.is_group and sender.username: - await uname_stuff(e.sender_id, sender.username, sender.first_name) - elif e.is_private and chat.username: - await uname_stuff(e.sender_id, chat.username, chat.first_name) - if detector and is_profan(e.chat_id) and e.text: - x, y = detector(e.text) - if y: - await e.delete() + xxrep = await check_reply_to(e) + + if xxrep: + sender = await e.get_sender() + if not isinstance(sender, types.User) or sender.bot: + return + if check_echo(e.chat_id, e.sender_id): + try: + await e.respond(e.message) + except Exception as er: + LOGS.exception(er) + key = udB.get_key("CHATBOT_USERS") or {} + if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]: + # Simulate typing indicator + async with e.client.action(e.chat_id, "typing"): + msg = await get_chatbot_reply(e.message.message) + if msg: + sleep = udB.get_key("CHATBOT_SLEEP") or 1.5 + await asyncio.sleep(sleep) + + # Check if the message length exceeds a certain threshold + if len(msg) > 4096: + # Create a temporary text file + with tempfile.NamedTemporaryFile( + mode="w+", delete=False + ) as temp_file: + temp_file.write(msg) + + # Send the text file with a caption + await e.client.send_file( + e.chat_id, + temp_file.name, + caption="Here is the response in a text file.", + ) + + # Delete the temporary text file + os.remove(temp_file.name) + else: + # Send the message directly + await e.reply(msg) + + chat = await e.get_chat() + if e.is_group and sender.username: + await uname_stuff(e.sender_id, sender.username, sender.first_name) + elif e.is_private and chat.username: + await uname_stuff(e.sender_id, chat.username, chat.first_name) + if detector and is_profan(e.chat_id) and e.text: + x, y = detector(e.text) + if y: + await e.delete() + + +@ultroid_bot.on(events.NewMessage(incoming=True)) +async def oracleBot_replies(e): + xxxrep = await check_reply_to(e) + + if xxxrep: + sender = await e.get_sender() + if not isinstance(sender, types.User) or sender.bot: + return + if check_echo(e.chat_id, e.sender_id): + try: + await e.respond(e.message) + except Exception as er: + LOGS.exception(er) + key = udB.get_key("ORACLE_USERS") or {} + if e.text and key.get(e.chat_id) and sender.id in key[e.chat_id]: + # Simulate typing indicator + async with e.client.action(e.chat_id, "typing"): + msg = await get_oracle_reply( + e.message.message, user_id=sender.id, mongo_url=MONGO_URI + ) + if msg: + sleep = udB.get_key("ORACLE_SLEEP") or 1.5 + await asyncio.sleep(sleep) + + # Check if the message length exceeds a certain threshold + if len(msg) > 4096: + # Create a temporary text file + with tempfile.NamedTemporaryFile( + mode="w+", delete=False + ) as temp_file: + temp_file.write(msg) + + # Send the text file with a caption + await e.client.send_file( + e.chat_id, + temp_file.name, + caption="Here is the response in a text file", + ) + + # Delete the temporary text file + os.remove(temp_file.name) + else: + # Send the message directly + await e.reply(msg) + + chat = await e.get_chat() + if e.is_group and sender.username: + await uname_stuff(e.sender_id, sender.username, sender.first_name) + elif e.is_private and chat.username: + await uname_stuff(e.sender_id, chat.username, chat.first_name) + if detector and is_profan(e.chat_id) and e.text: + x, y = detector(e.text) + if y: + await e.delete() @ultroid_bot.on(events.Raw(types.UpdateUserName)) diff --git a/plugins/_help.py b/plugins/_help.py index b36101e7f5..cb25c6e475 100644 --- a/plugins/_help.py +++ b/plugins/_help.py @@ -108,6 +108,7 @@ async def _help(ult): await ult.eor("Error šŸ¤” occured.") else: try: + load = await ult.eor("āœØ") results = await ult.client.inline_query(asst.me.username, "ultd") except BotMethodInvalidError: z = [] @@ -132,5 +133,5 @@ async def _help(ult): ) except BotInlineDisabledError: return await ult.eor(get_string("help_3")) + await load.delete() await results[0].click(chat.id, reply_to=ult.reply_to_msg_id, hide_via=True) - await ult.delete() diff --git a/plugins/beautify.py b/plugins/beautify.py index 903ab13197..9ad5057b43 100644 --- a/plugins/beautify.py +++ b/plugins/beautify.py @@ -12,11 +12,12 @@ import os import random +from secrets import token_hex +from urllib.parse import urlencode from telethon.utils import get_display_name -from urllib.parse import urlencode -from . import Carbon, ultroid_cmd, get_string, inline_mention -from secrets import token_hex + +from . import Carbon, get_string, inline_mention, ultroid_cmd _colorspath = "resources/colorlist.txt" @@ -108,7 +109,9 @@ async def pass_on(ult): try: from playwright.async_api import async_playwright except ImportError: - await ult.eor("`playwright` is not installed!\nPlease install it to use this command..") + await ult.eor( + "`playwright` is not installed!\nPlease install it to use this command.." + ) return proc = await ult.eor(get_string("com_1")) spli = ult.text.split() @@ -139,11 +142,7 @@ async def pass_on(ult): text = msg.message title = get_display_name(msg.sender) name = token_hex(8) + ".png" - data = { - "darkMode": dark, - "theme": theme, - "title": title - } + data = {"darkMode": dark, "theme": theme, "title": title} url = f"https://ray.so/#{urlencode(data)}" async with async_playwright() as play: chrome = await play.chromium.launch() @@ -157,8 +156,6 @@ async def pass_on(ult): async with page.expect_download() as dl: dled = await dl.value await dled.save_as(name) - await proc.reply( - file=name - ) + await proc.reply(file=name) await proc.try_delete() os.remove(name) diff --git a/plugins/chatbot.py b/plugins/chatbot.py index f050be1955..cf1ebdd9a5 100644 --- a/plugins/chatbot.py +++ b/plugins/chatbot.py @@ -15,6 +15,82 @@ from . import LOGS, eod, get_string, inline_mention, udB, ultroid_cmd +@ultroid_cmd(pattern="repoai") +async def im_oracle(event): + if event.reply_to: + message = (await event.get_reply_message()).text.strip() + else: + try: + message = event.text.split(" ", 1)[1] + except IndexError: + return await eod(event, get_string("tban_1"), time=10) + reply_ = await get_orcale_reply( + query=message, user_id=ultroid_bot.me.id, mongo_url=MONGO_URI + ) + await event.eor(reply_) + + +@ultroid_cmd(pattern="addoai") +async def add_oracle(event): + await oracle_bot_fn(event, type_="add") + + +@ultroid_cmd(pattern="remoai") +async def rem_oracle(event): + await oracle_bot_fn(event, type_="remov") + + +@ultroid_cmd(pattern="listoai") +async def listoracle(event): + key = udB.get_key("ORACLE_USERS") or {} + users = key.get(event.chat_id, []) + if not users: + return await event.eor(get_string("chab_2"), time=5) + msg = "**Total List Of Oracle Enabled Users In This Chat :**\n\n" + for i in users: + try: + user = await event.client.get_entity(int(i)) + user = inline_mention(user) + except BaseException: + user = f"`{i}`" + msg += f"ā€¢ {user}\n" + await event.eor(msg, link_preview=False) + + +async def oracle_bot_fn(event, type_): + if event.reply_to: + user_ = (await event.get_reply_message()).sender + else: + temp = event.text.split(maxsplit=1) + try: + user_ = await event.client.get_entity(await event.client.parse_id(temp[1])) + except BaseException as er: + LOGS.exception(er) + user_ = event.chat if event.is_private else None + if not user_: + return await eod( + event, + get_string("chab_1"), + ) + key = udB.get_key("ORACLE_USERS") or {} + chat = event.chat_id + user = user_.id + if type_ == "add": + if key.get(chat): + if user not in key[chat]: + key[chat].append(user) + else: + key.update({chat: [user]}) + elif type_ == "remov": + if key.get(chat): + if user in key[chat]: + key[chat].remove(user) + if chat in key and not key[chat]: + del key[chat] + udB.set_key("ORACLE_USERS", key) + await event.eor(f"**Oracle:**\n{type_}ed {inline_mention(user_)}") + + @ultroid_cmd(pattern="repai") async def im_lonely_chat_with_me(event): if event.reply_to: diff --git a/plugins/extra.py b/plugins/extra.py index f9abda572a..16899c9984 100644 --- a/plugins/extra.py +++ b/plugins/extra.py @@ -11,6 +11,9 @@ import asyncio +from telethon.errors import FloodWaitError + +from . import * from . import get_string, ultroid_cmd @@ -83,3 +86,35 @@ async def _(e): ) else: await e.try_delete() + + +@ultroid_cmd( + pattern="delmsgs", +) +async def delete_messages(event): + # Get the search phrase from the command + search_phrase = event.raw_text.split(" ", 1)[1] + + # Get the chat ID of the group + chat_id = event.chat_id + + # Get the messages in the chat + async for message in ultroid_bot.iter_messages(chat_id): + if message.text and search_phrase.lower() in message.text.lower(): + try: + await ultroid_bot.delete_messages(chat_id, message) + except FloodWaitError as e: + # If a FloodWaitError occurs, wait for the specified time + # before retrying + wait_time = e.seconds + 5 + logger.warning( + f"FloodWaitError occurred. Waiting for {wait_time} seconds." + ) + await asyncio.sleep(wait_time) + continue + + # Reply to the command with a confirmation message + await event.reply(f"Messages containing the phrase '{search_phrase}' deleted.") + logger.info( + f"Deleted messages containing the phrase '{search_phrase}' in chat {chat_id}" + ) diff --git a/plugins/globaltools.py b/plugins/globaltools.py index 087db364c4..bbf31c5d22 100644 --- a/plugins/globaltools.py +++ b/plugins/globaltools.py @@ -344,9 +344,11 @@ async def _(e): ungban(userid) if isinstance(peer, User): await e.client(UnblockRequest(userid)) - await xx.edit( - f"`Ungbaned` {name} in {chats} `chats.\nRemoved from gbanwatch.`", + ungb_msg = ( + f"#UNGBAN\n`Ungbanned` {name} in {chats} `chats.\nRemoved from gbanwatch.`" ) + await xx.edit(ungb_msg) + await asst.send_message(LOG_CHANNEL, ungb_msg) @ultroid_cmd(pattern="gban( (.*)|$)", fullsudo=True) @@ -424,10 +426,11 @@ async def _(e): gban(userid, reason) if isinstance(user, User): await e.client(BlockRequest(userid)) - gb_msg = f"**#Gbanned** {name} `in {chats} chats and added to gbanwatch!`" + gb_msg = f"#GBAN\n**Gbanned** {name} `in {chats} chats and added to gbanwatch!`" if reason: gb_msg += f"\n**Reason** : {reason}" await xx.edit(gb_msg) + await asst.send_message(LOG_CHANNEL, gb_msg) @ultroid_cmd(pattern="g(admin|)cast( (.*)|$)", fullsudo=True) diff --git a/plugins/nightmode.py b/plugins/nightmode.py index 5719cd7dc1..a2ea2dbd7c 100644 --- a/plugins/nightmode.py +++ b/plugins/nightmode.py @@ -117,7 +117,9 @@ async def open_grp(): ), ) ) - await ultroid_bot.send_message(chat, "**NightMode Off**\n\nGroup Opened šŸ„³.") + await ultroid_bot.send_message( + chat, "**NightMode Off**\n\nGroup Opened šŸ„³." + ) except Exception as er: LOGS.info(er) diff --git a/plugins/stickertools.py b/plugins/stickertools.py index 9191aa90e6..494a29bbf7 100644 --- a/plugins/stickertools.py +++ b/plugins/stickertools.py @@ -115,9 +115,11 @@ async def pack_kangish(_): stiks.append( types.InputStickerSetItem( document=x, - emoji=random.choice(["šŸ˜", "šŸ‘", "šŸ˜‚"]) - if local - else (i.attributes[1]).alt, + emoji=( + random.choice(["šŸ˜", "šŸ‘", "šŸ˜‚"]) + if local + else (i.attributes[1]).alt + ), ) ) try: diff --git a/plugins/tools.py b/plugins/tools.py index a1f3628236..e3ff04d71e 100644 --- a/plugins/tools.py +++ b/plugins/tools.py @@ -24,8 +24,8 @@ Reply a User to Get His Id Without Replying You Will Get the Chat's Id -ā€¢ `{i}sg ` - Get His Name History of the replied user. +ā€¢ `{i}sg ` or `{i}sgu ` + Get Name History of the user. ā€¢ `{i}tr <(reply to) a message>` Get translated message. @@ -36,11 +36,12 @@ ā€¢ `{i}shorturl ` shorten any url... """ +import asyncio import glob import io import os +import re import secrets -from asyncio.exceptions import TimeoutError as AsyncTimeout try: import cv2 @@ -57,6 +58,7 @@ WebShot = None from telethon.errors.rpcerrorlist import MessageTooLongError, YouBlockedUserError +from telethon.tl.functions.contacts import UnblockRequest as unblock from telethon.tl.types import ( ChannelParticipantAdmin, ChannelParticipantsBots, @@ -80,6 +82,23 @@ from . import humanbytes as hb from . import inline_mention, is_url_ok, json_parser, mediainfo, ultroid_cmd +# -------------- Sangmata stuff --------------# + + +def sanga_seperator(sanga_list): + string = "".join(info[info.find("\n") + 1 :] for info in sanga_list) + string = re.sub(r"^$\n", "", string, flags=re.MULTILINE) + name, username = string.split("Usernames**") + name = name.split("Names")[1] + return name, username + + +def mentionuser(name, userid): + return f"[{name}](tg://user?id={userid})" + + +# -------------- Sangmata stuff --------------# + @ultroid_cmd(pattern="tr( (.*)|$)", manager=True) async def _(event): @@ -338,55 +357,70 @@ async def _(e): await e.delete() -@ultroid_cmd( - pattern="sg( (.*)|$)", +@ultroid( + pattern="sg(|u)(?:\\s|$)([\\s\\S]*)", + fullsudo=True, ) -async def lastname(steal): - mat = steal.pattern_match.group(1).strip() - message = await steal.get_reply_message() - if mat: - try: - user_id = await steal.client.parse_id(mat) - except ValueError: - user_id = mat - elif message: - user_id = message.sender_id - else: - return await steal.eor("`Use this command with reply or give Username/id...`") - chat = "@SangMataInfo_bot" - id = f"/search_id {user_id}" - lol = await steal.eor(get_string("com_1")) +async def sangmata(event): + "To get name/username history." + cmd = event.pattern_match.group(1) + user = event.pattern_match.group(2) + reply = await event.get_reply_message() + if not user and reply: + user = str(reply.sender_id) + if not user: + await event.edit( + "`Reply to user's text message to get name/username history or give userid/username`", + ) + await asyncio.sleep(10) + return await event.delete() + try: - async with steal.client.conversation(chat) as conv: + if user.isdigit(): + userinfo = await ultroid_bot.get_entity(int(user)) + else: + userinfo = await ultroid_bot.get_entity(user) + except ValueError: + userinfo = None + if not isinstance(userinfo, types.User): + await event.edit("`Can't fetch the user...`") + await asyncio.sleep(10) + return await event.delete() + + await event.edit("`Processing...`") + async with event.client.conversation("@SangMata_beta_bot") as conv: + try: + await conv.send_message(userinfo.id) + except YouBlockedUserError: + await catub(unblock("SangMata_beta_bot")) + await conv.send_message(userinfo.id) + responses = [] + while True: try: - msg = await conv.send_message(id) - response = await conv.get_response() - respond = await conv.get_response() - responds = await conv.get_response() - except YouBlockedUserError: - return await lol.edit("Please unblock @sangmatainfo_bot and try again") - if ( - (response and response.text == "No records found") - or (respond and respond.text == "No records found") - or (responds and responds.text == "No records found") - ): - await lol.edit("No records found for this user") - await steal.client.delete_messages(conv.chat_id, [msg.id, response.id]) - elif response.text.startswith("šŸ”—"): - await lol.edit(respond.message) - await lol.reply(responds.message) - elif respond.text.startswith("šŸ”—"): - await lol.edit(response.message) - await lol.reply(responds.message) - else: - await lol.edit(respond.message) - await lol.reply(response.message) - await steal.client.delete_messages( - conv.chat_id, - [msg.id, responds.id, respond.id, response.id], - ) - except AsyncTimeout: - await lol.edit("Error: @SangMataInfo_bot is not responding!.") + response = await conv.get_response(timeout=2) + except asyncio.TimeoutError: + break + responses.append(response.text) + await event.client.send_read_acknowledge(conv.chat_id) + + if not responses: + await event.edit("`Bot can't fetch results`") + await asyncio.sleep(10) + await event.delete() + if "No records found" in responses or "No data available" in responses: + await event.edit("`The user doesn't have any record`") + await asyncio.sleep(10) + await event.delete() + + names, usernames = sanga_seperator(responses) + check = (usernames, "Username") if cmd == "u" else (names, "Name") + user_name = ( + f"{userinfo.first_name} {userinfo.last_name}" + if userinfo.last_name + else userinfo.first_name + ) + output = f"**āžœ User Info :** {mentionuser(user_name, userinfo.id)}\n**āžœ {check[1]} History :**\n{check[0]}" + await event.edit(output) @ultroid_cmd(pattern="webshot( (.*)|$)") diff --git a/pyUltroid/__init__.py b/pyUltroid/__init__.py index 366bc7dc8f..681bcf3213 100644 --- a/pyUltroid/__init__.py +++ b/pyUltroid/__init__.py @@ -42,6 +42,10 @@ class ULTConfig: udB = UltroidDB() update_envs() + from .dB.base import Keys as Keyz + + Keys = Keyz(udB) + LOGS.info(f"Connecting to {udB.name}...") if udB.ping(): LOGS.info(f"Connected to {udB.name} Successfully!") diff --git a/pyUltroid/__main__.py b/pyUltroid/__main__.py index 70100e01d0..1853fe3618 100644 --- a/pyUltroid/__main__.py +++ b/pyUltroid/__main__.py @@ -18,7 +18,6 @@ def main(): WasItRestart, autopilot, customize, - fetch_ann, plug, ready, startup_stuff, diff --git a/pyUltroid/_misc/_supporter.py b/pyUltroid/_misc/_supporter.py index f098d76dc5..dfe6196b96 100644 --- a/pyUltroid/_misc/_supporter.py +++ b/pyUltroid/_misc/_supporter.py @@ -118,6 +118,7 @@ class Config((object)): PM_DATA = os.environ.get("PM_DATA", "ENABLE") DEEP_AI = os.environ.get("DEEP_AI", None) TAG_LOG = os.environ.get("TAG_LOG", None) + UFOPAPI = os.environ.get("UFOPAPI", "h32dfKsl8pQUxsNftkogIuDF32pYTwKluIY8emI1Hs") else: DB_URI = None diff --git a/pyUltroid/_misc/_wrappers.py b/pyUltroid/_misc/_wrappers.py index a1475fdf71..8e041ecd36 100644 --- a/pyUltroid/_misc/_wrappers.py +++ b/pyUltroid/_misc/_wrappers.py @@ -52,7 +52,7 @@ async def eod(event, text=None, **kwargs): async def _try_delete(event): try: return await event.delete() - except (MessageDeleteForbiddenError): + except MessageDeleteForbiddenError: pass except BaseException as er: from . import LOGS diff --git a/pyUltroid/dB/base.py b/pyUltroid/dB/base.py index de26c1542d..d1cbd79f67 100644 --- a/pyUltroid/dB/base.py +++ b/pyUltroid/dB/base.py @@ -42,3 +42,24 @@ def remove(self, item): def contains(self, item): return item in self.get() + + +class Keys: + def __init__(self, udB): + self.udB = udB + self.load_keys() + + def load_keys(self): + # Fetch keys dynamically from the database + all_keys = sorted(self.udB.keys()) + valid_keys = [ + key + for key in all_keys + if not key.isdigit() + and not key.startswith("-") + and not key.startswith("_") + and not key.startswith("GBAN_REASON_") + ] + + for key in valid_keys: + setattr(self, key, self.udB.get_key(key)) diff --git a/pyUltroid/dB/botchat_db.py b/pyUltroid/dB/botchat_db.py index 7b70b574b6..19ab177a01 100644 --- a/pyUltroid/dB/botchat_db.py +++ b/pyUltroid/dB/botchat_db.py @@ -5,7 +5,6 @@ # PLease read the GNU Affero General Public License in # . - from .. import udB diff --git a/pyUltroid/exceptions.py b/pyUltroid/exceptions.py index b898a4115b..71f2592c82 100644 --- a/pyUltroid/exceptions.py +++ b/pyUltroid/exceptions.py @@ -10,13 +10,10 @@ """ -class pyUltroidError(Exception): - ... +class pyUltroidError(Exception): ... -class DependencyMissingError(ImportError): - ... +class DependencyMissingError(ImportError): ... -class RunningAsFunctionLibError(pyUltroidError): - ... +class RunningAsFunctionLibError(pyUltroidError): ... diff --git a/pyUltroid/fns/custom_markdown.py b/pyUltroid/fns/custom_markdown.py new file mode 100644 index 0000000000..4162172e8f --- /dev/null +++ b/pyUltroid/fns/custom_markdown.py @@ -0,0 +1,37 @@ +# Ultroid - UserBot +# Copyright (C) 2021-2023 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . + +from telethon import types +from telethon.extensions import markdown + +from . import * + + +class CustomMarkdown: + @staticmethod + def parse(text): + text, entities = markdown.parse(text) + for i, e in enumerate(entities): + if isinstance(e, types.MessageEntityTextUrl): + if e.url == "spoiler": + entities[i] = types.MessageEntitySpoiler(e.offset, e.length) + elif e.url.startswith("emoji/"): + entities[i] = types.MessageEntityCustomEmoji( + e.offset, e.length, int(e.url.split("/")[1]) + ) + return text, entities + + @staticmethod + def unparse(text, entities): + for i, e in enumerate(entities or []): + if isinstance(e, types.MessageEntityCustomEmoji): + entities[i] = types.MessageEntityTextUrl( + e.offset, e.length, f"emoji/{e.document_id}" + ) + if isinstance(e, types.MessageEntitySpoiler): + entities[i] = types.MessageEntityTextUrl(e.offset, e.length, "spoiler") + return markdown.unparse(text, entities) diff --git a/pyUltroid/fns/google_image.py b/pyUltroid/fns/google_image.py new file mode 100644 index 0000000000..a31d418569 --- /dev/null +++ b/pyUltroid/fns/google_image.py @@ -0,0 +1,1152 @@ +#!/usr/bin/env python +# In[ ]: +# coding: utf-8 +# Ultroid - UserBot +# Copyright (C) 2021-2023 TeamUltroid +# +# This file is a part of < https://github.com/TeamUltroid/Ultroid/ > +# PLease read the GNU Affero General Public License in +# . +###### Searching and Downloading Google Images to the local disk ###### + + +import codecs +import datetime +import http.client +import json +import os +import re +import ssl +import sys +import time # Importing the time library to check the time of code execution +import urllib.request +from http.client import BadStatusLine +from urllib.parse import quote +from urllib.request import HTTPError, Request, URLError, urlopen + +# Import Libraries +from .. import LOGS +from .tools import async_searcher + +http.client._MAXHEADERS = 1000 + +args_list = [ + "keywords", + "keywords_from_file", + "prefix_keywords", + "suffix_keywords", + "limit", + "format", + "color", + "color_type", + "usage_rights", + "size", + "exact_size", + "aspect_ratio", + "type", + "time", + "time_range", + "delay", + "url", + "single_image", + "output_directory", + "image_directory", + "no_directory", + "proxy", + "similar_images", + "specific_site", + "metadata", + "extract_metadata", + "socket_timeout", + "thumbnail", + "thumbnail_only", + "language", + "prefix", + "chromedriver", + "related_images", + "safe_search", + "no_numbering", + "offset", + "no_download", + "save_source", + "ignore_urls", +] + + +class googleimagesdownload: + def __init__(self): + pass + + # Downloading entire Web Document (Raw Page Content) + async def download_page(self, url): + try: + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36" + } + + # req = urllib.request.Request(url, headers=headers) + # resp = urllib.request.urlopen(req) + # return str(resp.read()) + resp = await async_searcher(url, re_content=True, headers=headers) + return str(resp) + except Exception as er: + LOGS.exception( + "Could not open URL. Please check your internet connection and/or ssl settings \n" + "If you are using proxy, make sure your proxy settings is configured correctly" + ) + raise er + + # Download Page for more than 100 images + + def download_extended_page(self, url, chromedriver): + from selenium import webdriver + from selenium.webdriver.common.keys import Keys + + options = webdriver.ChromeOptions() + options.add_argument("--no-sandbox") + options.add_argument("--headless") + + try: + browser = webdriver.Chrome(chromedriver, chrome_options=options) + except Exception as e: + LOGS.info( + "Looks like we cannot locate the path the 'chromedriver' (use the '--chromedriver' " + "argument to specify the path to the executable.) or google chrome browser is not " + "installed on your machine (exception: %s)" % e + ) + sys.exit() + browser.set_window_size(1024, 768) + + # Open the link + browser.get(url) + time.sleep(1) + + element = browser.find_element_by_tag_name("body") + # Scroll down + for _ in range(30): + element.send_keys(Keys.PAGE_DOWN) + time.sleep(0.3) + + try: + browser.find_element_by_id("smb").click() + for _ in range(50): + element.send_keys(Keys.PAGE_DOWN) + time.sleep(0.3) # bot id protection + except BaseException: + for _ in range(10): + element.send_keys(Keys.PAGE_DOWN) + time.sleep(0.3) # bot id protection + + time.sleep(0.5) + + source = browser.page_source # page source + # close the browser + browser.close() + + return source + + # Correcting the escape characters for python2 + + def replace_with_byte(self, match): + return chr(int(match.group(0)[1:], 8)) + + def repair(self, brokenjson): + # up to 3 digits for byte values up to FF + invalid_escape = re.compile(r"\\[0-7]{1,3}") + return invalid_escape.sub(self.replace_with_byte, brokenjson) + + # Finding 'Next Image' from the given raw page + + def get_next_tab(self, s): + start_line = s.find('class="dtviD"') + if start_line == -1: # If no links are found then give an error! + end_quote = 0 + link = "no_tabs" + return link, "", end_quote + start_line = s.find('class="dtviD"') + start_content = s.find('href="', start_line + 1) + end_content = s.find('">', start_content + 1) + url_item = f"https://www.google.com{str(s[start_content + 6:end_content])}" + url_item = url_item.replace("&", "&") + start_line_2 = s.find('class="dtviD"') + s = s.replace("&", "&") + start_content_2 = s.find(":", start_line_2 + 1) + end_content_2 = s.find("&usg=", start_content_2 + 1) + url_item_name = str(s[start_content_2 + 1 : end_content_2]) + chars = url_item_name.find(",g_1:") + chars_end = url_item_name.find(":", chars + 6) + if chars_end == -1: + updated_item_name = (url_item_name[chars + 5 :]).replace("+", " ") + else: + updated_item_name = (url_item_name[chars + 5 : chars_end]).replace("+", " ") + return url_item, updated_item_name, end_content + + # Getting all links with the help of '_images_get_next_image' + + def get_all_tabs(self, page): + tabs = {} + while True: + item, item_name, end_content = self.get_next_tab(page) + if item == "no_tabs": + break + if len(item_name) > 100 or item_name == "background-color": + break + # Append all the links in the list named 'Links' + tabs[item_name] = item + # Timer could be used to slow down the request for image + # downloads + time.sleep(0.1) + page = page[end_content:] + return tabs + + # Format the object in readable format + + def format_object(self, object): + data = object[1] + main = data[3] + info = data[9] + return { + "image_height": main[2], + "image_width": main[1], + "image_link": main[0], + "image_format": main[0][-1 * (len(main[0]) - main[0].rfind(".") - 1) :], + "image_description": info["2003"][3], + "image_source": info["2003"][2], + "image_thumbnail_url": data[2][0], + } + + # function to download single image + + def single_image(self, image_url): + main_directory = "downloads" + extensions = (".jpg", ".gif", ".png", ".bmp", ".svg", ".webp", ".ico") + url = image_url + try: + os.makedirs(main_directory) + except OSError as e: + if e.errno != 17: + raise + req = Request( + url, + headers={ + "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" + }, + ) + + response = urlopen(req, None, 10) + data = response.read() + response.close() + + image_name = str(url[(url.rfind("/")) + 1 :]) + if "?" in image_name: + image_name = image_name[: image_name.find("?")] + # if ".jpg" in image_name or ".gif" in image_name or ".png" in + # image_name or ".bmp" in image_name or ".svg" in image_name or ".webp" + # in image_name or ".ico" in image_name: + if any(map(lambda extension: extension in image_name, extensions)): + file_name = f"{main_directory}/{image_name}" + else: + file_name = f"{main_directory}/{image_name}.jpg" + image_name = f"{image_name}.jpg" + + try: + with open(file_name, "wb") as output_file: + output_file.write(data) + except OSError as e: + raise e + + def similar_images(self, similar_images): + try: + searchUrl = ( + "https://www.google.com/searchbyimage?site=search&sa=X&image_url=" + + similar_images + ) + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" + } + + req1 = urllib.request.Request(searchUrl, headers=headers) + resp1 = urllib.request.urlopen(req1) + content = str(resp1.read()) + l1 = content.find("AMhZZ") + l2 = content.find("&", l1) + urll = content[l1:l2] + + newurl = f"https://www.google.com/search?tbs=sbi:{urll}&site=search&sa=X" + req2 = urllib.request.Request(newurl, headers=headers) + urllib.request.urlopen(req2) + l3 = content.find("/search?sa=X&q=") + l4 = content.find(";", l3 + 19) + return content[l3 + 19 : l4] + except BaseException: + return "Cloud not connect to Google Images endpoint" + + # Building URL parameters + def build_url_parameters(self, arguments): + if arguments["language"]: + lang = "&lr=" + lang_param = { + "Arabic": "lang_ar", + "Chinese (Simplified)": "lang_zh-CN", + "Chinese (Traditional)": "lang_zh-TW", + "Czech": "lang_cs", + "Danish": "lang_da", + "Dutch": "lang_nl", + "English": "lang_en", + "Estonian": "lang_et", + "Finnish": "lang_fi", + "French": "lang_fr", + "German": "lang_de", + "Greek": "lang_el", + "Hebrew": "lang_iw ", + "Hungarian": "lang_hu", + "Icelandic": "lang_is", + "Italian": "lang_it", + "Japanese": "lang_ja", + "Korean": "lang_ko", + "Latvian": "lang_lv", + "Lithuanian": "lang_lt", + "Norwegian": "lang_no", + "Portuguese": "lang_pt", + "Polish": "lang_pl", + "Romanian": "lang_ro", + "Russian": "lang_ru", + "Spanish": "lang_es", + "Swedish": "lang_sv", + "Turkish": "lang_tr", + } + lang_url = lang + lang_param[arguments["language"]] + else: + lang_url = "" + + if arguments["time_range"]: + json_acceptable_string = arguments["time_range"].replace("'", '"') + d = json.loads(json_acceptable_string) + time_range = ",cdr:1,cd_min:" + d["time_min"] + ",cd_max:" + d["time_max"] + else: + time_range = "" + + if arguments["exact_size"]: + size_array = [x.strip() for x in arguments["exact_size"].split(",")] + exact_size = f",isz:ex,iszw:{str(size_array[0])},iszh:{str(size_array[1])}" + else: + exact_size = "" + + built_url = "&tbs=" + counter = 0 + params = { + "color": [ + arguments["color"], + { + "red": "ic:specific,isc:red", + "orange": "ic:specific,isc:orange", + "yellow": "ic:specific,isc:yellow", + "green": "ic:specific,isc:green", + "teal": "ic:specific,isc:teel", + "blue": "ic:specific,isc:blue", + "purple": "ic:specific,isc:purple", + "pink": "ic:specific,isc:pink", + "white": "ic:specific,isc:white", + "gray": "ic:specific,isc:gray", + "black": "ic:specific,isc:black", + "brown": "ic:specific,isc:brown", + }, + ], + "color_type": [ + arguments["color_type"], + { + "full-color": "ic:color", + "black-and-white": "ic:gray", + "transparent": "ic:trans", + }, + ], + "usage_rights": [ + arguments["usage_rights"], + { + "labeled-for-reuse-with-modifications": "sur:fmc", + "labeled-for-reuse": "sur:fc", + "labeled-for-noncommercial-reuse-with-modification": "sur:fm", + "labeled-for-nocommercial-reuse": "sur:f", + }, + ], + "size": [ + arguments["size"], + { + "large": "isz:l", + "medium": "isz:m", + "icon": "isz:i", + ">400*300": "isz:lt,islt:qsvga", + ">640*480": "isz:lt,islt:vga", + ">800*600": "isz:lt,islt:svga", + ">1024*768": "visz:lt,islt:xga", + ">2MP": "isz:lt,islt:2mp", + ">4MP": "isz:lt,islt:4mp", + ">6MP": "isz:lt,islt:6mp", + ">8MP": "isz:lt,islt:8mp", + ">10MP": "isz:lt,islt:10mp", + ">12MP": "isz:lt,islt:12mp", + ">15MP": "isz:lt,islt:15mp", + ">20MP": "isz:lt,islt:20mp", + ">40MP": "isz:lt,islt:40mp", + ">70MP": "isz:lt,islt:70mp", + }, + ], + "type": [ + arguments["type"], + { + "face": "itp:face", + "photo": "itp:photo", + "clipart": "itp:clipart", + "line-drawing": "itp:lineart", + "animated": "itp:animated", + }, + ], + "time": [ + arguments["time"], + { + "past-24-hours": "qdr:d", + "past-7-days": "qdr:w", + "past-month": "qdr:m", + "past-year": "qdr:y", + }, + ], + "aspect_ratio": [ + arguments["aspect_ratio"], + { + "tall": "iar:t", + "square": "iar:s", + "wide": "iar:w", + "panoramic": "iar:xw", + }, + ], + "format": [ + arguments["format"], + { + "jpg": "ift:jpg", + "gif": "ift:gif", + "png": "ift:png", + "bmp": "ift:bmp", + "svg": "ift:svg", + "webp": "webp", + "ico": "ift:ico", + "raw": "ift:craw", + }, + ], + } + for value in params.values(): + if value[0] is not None: + ext_param = value[1][value[0]] + # counter will tell if it is first param added or not + if counter == 0: + # add it to the built url + built_url += ext_param + else: + built_url = f"{built_url},{ext_param}" + counter += 1 + return lang_url + built_url + exact_size + time_range + + # building main search URL + + def build_search_url( + self, search_term, params, url, similar_images, specific_site, safe_search + ): + # check the args and choose the URL + if url: + url = url + elif similar_images: + keywordem = self.similar_images(similar_images) + url = ( + "https://www.google.com/search?q=" + + keywordem + + "&espv=2&biw=1366&bih=667&site=webhp&source=lnms&tbm=isch&sa=X&ei=XosDVaCXD8TasATItgE&ved=0CAcQ_AUoAg" + ) + elif specific_site: + url = ( + "https://www.google.com/search?q=" + + quote(search_term.encode("utf-8")) + + "&as_sitesearch=" + + specific_site + + "&espv=2&biw=1366&bih=667&site=webhp&source=lnms&tbm=isch" + + params + + "&sa=X&ei=XosDVaCXD8TasATItgE&ved=0CAcQ_AUoAg" + ) + else: + url = ( + "https://www.google.com/search?q=" + + quote(search_term.encode("utf-8")) + + "&espv=2&biw=1366&bih=667&site=webhp&source=lnms&tbm=isch" + + params + + "&sa=X&ei=XosDVaCXD8TasATItgE&ved=0CAcQ_AUoAg" + ) + + # safe search check + if safe_search: + # check safe_search + safe_search_string = "&safe=active" + url = url + safe_search_string + + return url + + # measures the file size + + def file_size(self, file_path): + if os.path.isfile(file_path): + file_info = os.stat(file_path) + size = file_info.st_size + for x in ["bytes", "KB", "MB", "GB", "TB"]: + if size < 1024.0: + return "%3.1f %s" % (size, x) + size /= 1024.0 + return size + + # keywords from file + def keywords_from_file(self, file_name): + search_keyword = [] + with codecs.open(file_name, "r", encoding="utf-8-sig") as f: + if ".csv" in file_name or ".txt" in file_name: + search_keyword.extend( + line.replace("\n", "").replace("\r", "") + for line in f + if line not in ["\n", "\r\n"] + ) + else: + LOGS.info( + "Invalid file type: Valid file types are either .txt or .csv \n" + "exiting..." + ) + sys.exit() + return search_keyword + + # make directories + def create_directories(self, main_directory, dir_name, thumbnail, thumbnail_only): + dir_name_thumbnail = f"{dir_name} - thumbnail" + # make a search keyword directory + try: + if not os.path.exists(main_directory): + os.makedirs(main_directory) + time.sleep(0.15) + path = dir_name + sub_directory = os.path.join(main_directory, path) + if not os.path.exists(sub_directory): + os.makedirs(sub_directory) + if thumbnail or thumbnail_only: + sub_directory_thumbnail = os.path.join( + main_directory, dir_name_thumbnail + ) + if not os.path.exists(sub_directory_thumbnail): + os.makedirs(sub_directory_thumbnail) + except OSError as e: + if e.errno != 17: + raise + + # Download Image thumbnails + + def download_image_thumbnail( + self, + image_url, + main_directory, + dir_name, + return_image_name, + socket_timeout, + no_download, + save_source, + img_src, + ): + if no_download: + return "success", "Printed url without downloading" + try: + req = Request( + image_url, + headers={ + "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" + }, + ) + try: + # timeout time to download an image + timeout = float(socket_timeout) if socket_timeout else 10 + response = urlopen(req, None, timeout) + data = response.read() + response.close() + + path = ( + main_directory + + "/" + + dir_name + + " - thumbnail" + + "/" + + return_image_name + ) + + try: + with open(path, "wb") as output_file: + output_file.write(data) + if save_source: + list_path = f"{main_directory}/{save_source}.txt" + with open(list_path, "a") as list_file: + list_file.write(path + "\t" + img_src + "\n") + except OSError as e: + download_status = "fail" + download_message = ( + "OSError on an image...trying next one..." + " Error: " + str(e) + ) + + download_status = "success" + download_message = ( + f"Completed Image Thumbnail ====> {return_image_name}" + ) + + except UnicodeEncodeError as e: + download_status = "fail" + download_message = ( + "UnicodeEncodeError on an image...trying next one..." + + " Error: " + + str(e) + ) + + except HTTPError as e: # If there is any HTTPError + download_status = "fail" + download_message = ( + "HTTPError on an image...trying next one..." + " Error: " + str(e) + ) + + except URLError as e: + download_status = "fail" + download_message = ( + "URLError on an image...trying next one..." + " Error: " + str(e) + ) + + except ssl.CertificateError as e: + download_status = "fail" + download_message = ( + "CertificateError on an image...trying next one..." + + " Error: " + + str(e) + ) + + except IOError as e: # If there is any IOError + download_status = "fail" + download_message = ( + "IOError on an image...trying next one..." + " Error: " + str(e) + ) + return download_status, download_message + + # Download Images + + def download_image( + self, + image_url, + image_format, + main_directory, + dir_name, + count, + socket_timeout, + prefix, + no_numbering, + no_download, + save_source, + img_src, + thumbnail_only, + format, + ignore_urls, + ): + if ignore_urls and any(url in image_url for url in ignore_urls.split(",")): + return ( + "fail", + "Image ignored due to 'ignore url' parameter", + None, + image_url, + ) + if thumbnail_only: + return ( + "success", + "Skipping image download...", + str(image_url[(image_url.rfind("/")) + 1 :]), + image_url, + ) + if no_download: + return "success", "Printed url without downloading", None, image_url + try: + req = Request( + image_url, + headers={ + "User-Agent": "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.27 Safari/537.17" + }, + ) + try: + # timeout time to download an image + timeout = float(socket_timeout) if socket_timeout else 10 + response = urlopen(req, None, timeout) + data = response.read() + response.close() + + extensions = [ + ".jpg", + ".jpeg", + ".gif", + ".png", + ".bmp", + ".svg", + ".webp", + ".ico", + ] + # keep everything after the last '/' + image_name = str(image_url[(image_url.rfind("/")) + 1 :]) + if format and (not image_format or image_format != format): + download_status = "fail" + download_message = "Wrong image format returned. Skipping..." + return_image_name = "" + absolute_path = "" + return ( + download_status, + download_message, + return_image_name, + absolute_path, + ) + + if ( + image_format == "" + or not image_format + or f".{image_format}" not in extensions + ): + download_status = "fail" + download_message = "Invalid or missing image format. Skipping..." + return_image_name = "" + absolute_path = "" + return ( + download_status, + download_message, + return_image_name, + absolute_path, + ) + if image_name.lower().find(f".{image_format}") < 0: + image_name = f"{image_name}.{image_format}" + else: + image_name = image_name[ + : ( + image_name.lower().find(f".{image_format}") + + (len(image_format) + 1) + ) + ] + + # prefix name in image + prefix = f"{prefix} " if prefix else "" + path = ( + f"{main_directory}/{dir_name}/{prefix}{image_name}" + if no_numbering + else f"{main_directory}/{dir_name}/{prefix}{str(count)}.{image_name}" + ) + try: + with open(path, "wb") as output_file: + output_file.write(data) + if save_source: + list_path = f"{main_directory}/{save_source}.txt" + with open(list_path, "a") as list_file: + list_file.write(path + "\t" + img_src + "\n") + absolute_path = os.path.abspath(path) + except OSError as e: + download_status = "fail" + download_message = ( + "OSError on an image...trying next one..." + " Error: " + str(e) + ) + return_image_name = "" + absolute_path = "" + + # return image name back to calling method to use it for + # thumbnail downloads + download_status = "success" + download_message = ( + f"Completed Image ====> {prefix}{str(count)}.{image_name}" + ) + return_image_name = prefix + str(count) + "." + image_name + + except UnicodeEncodeError as e: + download_status = "fail" + download_message = ( + "UnicodeEncodeError on an image...trying next one..." + + " Error: " + + str(e) + ) + return_image_name = "" + absolute_path = "" + + except URLError as e: + download_status = "fail" + download_message = ( + "URLError on an image...trying next one..." + " Error: " + str(e) + ) + return_image_name = "" + absolute_path = "" + + except BadStatusLine as e: + download_status = "fail" + download_message = ( + "BadStatusLine on an image...trying next one..." + + " Error: " + + str(e) + ) + return_image_name = "" + absolute_path = "" + + except HTTPError as e: # If there is any HTTPError + download_status = "fail" + download_message = ( + "HTTPError on an image...trying next one..." + " Error: " + str(e) + ) + return_image_name = "" + absolute_path = "" + + except URLError as e: + download_status = "fail" + download_message = ( + "URLError on an image...trying next one..." + " Error: " + str(e) + ) + return_image_name = "" + absolute_path = "" + + except ssl.CertificateError as e: + download_status = "fail" + download_message = ( + "CertificateError on an image...trying next one..." + + " Error: " + + str(e) + ) + return_image_name = "" + absolute_path = "" + + except IOError as e: # If there is any IOError + download_status = "fail" + download_message = ( + "IOError on an image...trying next one..." + " Error: " + str(e) + ) + return_image_name = "" + absolute_path = "" + + return download_status, download_message, return_image_name, absolute_path + + # Finding 'Next Image' from the given raw page + + def _get_next_item(self, s): + start_line = s.find("rg_meta notranslate") + if start_line == -1: # If no links are found then give an error! + end_quote = 0 + link = "no_links" + return link, end_quote + start_line = s.find('class="rg_meta notranslate">') + start_object = s.find("{", start_line + 1) + end_object = s.find("", start_object + 1) + object_raw = str(s[start_object:end_object]) + # remove escape characters based on python version + try: + object_decode = bytes(object_raw, "utf-8").decode("unicode_escape") + final_object = json.loads(object_decode) + except BaseException: + final_object = "" + return final_object, end_object + + # Getting all links with the help of '_images_get_next_image' + + def _get_image_objects(self, s): + start_line = s.find("AF_initDataCallback({key: \\'ds:1\\'") - 10 + start_object = s.find("[", start_line + 1) + end_object = s.find("", start_object + 1) - 4 + object_raw = str(s[start_object:end_object]) + object_decode = bytes(object_raw[:-1], "utf-8").decode("unicode_escape") + # LOGS.info(_format.paste_text(object_decode[:-15])) + return json.loads(object_decode[:-15])[31][0][12][2] + + def _get_all_items(self, page, main_directory, dir_name, limit, arguments): + items = [] + abs_path = [] + errorCount = 0 + i = 0 + count = 1 + # LOGS.info(f"page : {_format.paste_text(page)}") + image_objects = self._get_image_objects(page) + while count < limit + 1: + if not image_objects: + print("no_links") + break + else: + # format the item for readability + try: + object = self.format_object(image_objects[i]) + # download the images + ( + download_status, + download_message, + return_image_name, + absolute_path, + ) = self.download_image( + object["image_link"], + object["image_format"], + main_directory, + dir_name, + count, + arguments["socket_timeout"], + arguments["prefix"], + arguments["no_numbering"], + arguments["no_download"], + arguments["save_source"], + object["image_source"], + arguments["thumbnail_only"], + arguments["format"], + arguments["ignore_urls"], + ) + except (TypeError, IndexError) as er: + LOGS.debug(er) + download_status = None + + if download_status == "success": + # download image_thumbnails + if arguments["thumbnail"] or arguments["thumbnail_only"]: + ( + download_status, + download_message_thumbnail, + ) = self.download_image_thumbnail( + object["image_thumbnail_url"], + main_directory, + dir_name, + return_image_name, + arguments["socket_timeout"], + arguments["no_download"], + arguments["save_source"], + object["image_source"], + arguments["ignore_urls"], + ) + + count += 1 + object["image_filename"] = return_image_name + # Append all the links in the list named 'Links' + items.append(object) + abs_path.append(absolute_path) + else: + errorCount += 1 + + # delay param + if arguments["delay"]: + time.sleep(int(arguments["delay"])) + i += 1 + if count < limit: + LOGS.info( + "\n\nUnfortunately all " + + str(limit) + + " could not be downloaded because some images were not downloadable. " + + str(count - 1) + + " is all we got for this search filter!" + ) + return items, errorCount, abs_path + + # Bulk Download + + async def download(self, arguments): + paths_agg = {} + # for input coming from other python files + if __name__ != "__main__": + # if the calling file contains config_file param + if "config_file" in arguments: + records = [] + json_file = json.load(open(arguments["config_file"])) + for item in json_file["Records"]: + arguments = {i: None for i in args_list} + for key, value in item.items(): + arguments[key] = value + records.append(arguments) + total_errors = 0 + for rec in records: + paths, errors = await self.download_executor(rec) + for i in paths: + paths_agg[i] = paths[i] + total_errors += errors + return paths_agg, total_errors + # if the calling file contains params directly + paths, errors = await self.download_executor(arguments) + for i in paths: + paths_agg[i] = paths[i] + return paths_agg, errors + # for input coming from CLI + paths, errors = await self.download_executor(arguments) + for i in paths: + paths_agg[i] = paths[i] + return paths_agg, errors + + async def download_executor(self, arguments): + paths = {} + errorCount = None + for arg in args_list: + if arg not in arguments: + arguments[arg] = None + # Initialization and Validation of user arguments + if arguments["keywords"]: + search_keyword = [str(item) for item in arguments["keywords"].split(",")] + + if arguments["keywords_from_file"]: + search_keyword = self.keywords_from_file(arguments["keywords_from_file"]) + + # both time and time range should not be allowed in the same query + if arguments["time"] and arguments["time_range"]: + raise ValueError( + "Either time or time range should be used in a query. Both cannot be used at the same time." + ) + + # both time and time range should not be allowed in the same query + if arguments["size"] and arguments["exact_size"]: + raise ValueError( + 'Either "size" or "exact_size" should be used in a query. Both cannot be used at the same time.' + ) + + # both image directory and no image directory should not be allowed in + # the same query + if arguments["image_directory"] and arguments["no_directory"]: + raise ValueError( + "You can either specify image directory or specify no image directory, not both!" + ) + + # Additional words added to keywords + if arguments["suffix_keywords"]: + suffix_keywords = [ + " " + str(sk) for sk in arguments["suffix_keywords"].split(",") + ] + else: + suffix_keywords = [""] + + # Additional words added to keywords + if arguments["prefix_keywords"]: + prefix_keywords = [ + str(sk) + " " for sk in arguments["prefix_keywords"].split(",") + ] + else: + prefix_keywords = [""] + + # Setting limit on number of images to be downloaded + limit = int(arguments["limit"]) if arguments["limit"] else 100 + if arguments["url"]: + current_time = str(datetime.datetime.now()).split(".")[0] + search_keyword = [current_time.replace(":", "_")] + + if arguments["similar_images"]: + current_time = str(datetime.datetime.now()).split(".")[0] + search_keyword = [current_time.replace(":", "_")] + + # If single_image or url argument not present then keywords is + # mandatory argument + if ( + arguments["single_image"] is None + and arguments["url"] is None + and arguments["similar_images"] is None + and arguments["keywords"] is None + and arguments["keywords_from_file"] is None + ): + LOGS.info( + "-------------------------------\n" + "Uh oh! Keywords is a required argument \n\n" + "Please refer to the documentation on guide to writing queries \n" + "https://github.com/hardikvasa/google-images-download#examples" + "\n\nexiting!\n" + "-------------------------------" + ) + sys.exit() + + # If this argument is present, set the custom output directory + main_directory = arguments["output_directory"] or "downloads" + # Proxy settings + if arguments["proxy"]: + os.environ["http_proxy"] = arguments["proxy"] + os.environ["https_proxy"] = arguments["proxy"] + # Initialization Complete + total_errors = 0 + for pky in prefix_keywords: # 1.for every prefix keywords + for sky in suffix_keywords: # 2.for every suffix keywords + for ii, e in enumerate(search_keyword): # 3.for every main keyword + iteration = ( + "\n" + + "Item no.: " + + str(ii + 1) + + " -->" + + " Item name = " + + (pky) + + (e) + + (sky) + ) + search_term = pky + e + sky + + if arguments["image_directory"]: + dir_name = arguments["image_directory"] + elif arguments["no_directory"]: + dir_name = "" + else: + dir_name = search_term + ( + "-" + arguments["color"] if arguments["color"] else "" + ) # sub-directory + + if not arguments["no_download"]: + self.create_directories( + main_directory, + dir_name, + arguments["thumbnail"], + arguments["thumbnail_only"], + ) # create directories in OS + + params = self.build_url_parameters( + arguments + ) # building URL with params + + url = self.build_search_url( + search_term, + params, + arguments["url"], + arguments["similar_images"], + arguments["specific_site"], + arguments["safe_search"], + ) # building main search url + + if limit < 101: + # download page + raw_html = await self.download_page(url) + else: + raw_html = self.download_extended_page( + url, arguments["chromedriver"] + ) + + items, errorCount, abs_path = self._get_all_items( + raw_html, main_directory, dir_name, limit, arguments + ) # get all image items and download images + paths[pky + e + sky] = abs_path + + # dumps into a json file + if arguments["extract_metadata"]: + try: + if not os.path.exists("logs"): + os.makedirs("logs") + except OSError as e: + LOGS.exception(e) + with open("logs/" + e + ".json", "w") as json_file: + json.dump(items, json_file, indent=4, sort_keys=True) + # Related images + if arguments["related_images"]: + tabs = self.get_all_tabs(raw_html) + for key, value in tabs.items(): + final_search_term = search_term + " - " + key + if limit < 101: + new_raw_html = await self.download_page( + value + ) # download page + else: + new_raw_html = self.download_extended_page( + value, arguments["chromedriver"] + ) + self.create_directories( + main_directory, + final_search_term, + arguments["thumbnail"], + arguments["thumbnail_only"], + ) + self._get_all_items( + new_raw_html, + main_directory, + search_term + " - " + key, + limit, + arguments, + ) + + total_errors += errorCount + return paths, total_errors diff --git a/pyUltroid/fns/helper.py b/pyUltroid/fns/helper.py index 16d00ea177..e3e6e9fb8b 100644 --- a/pyUltroid/fns/helper.py +++ b/pyUltroid/fns/helper.py @@ -96,6 +96,30 @@ def inline_mention(user, custom=None, html=False): return f"[{mention_text}](https://t.me/{user.username})" return mention_text +async def check_reply_to(event): + replytoIDS = [event.client.me.id] + if (event.is_private and event.is_reply) or ( + event.is_reply and event.reply_to_msg_id + ): + try: + replied_message = await event.client.get_messages( + event.chat_id, ids=event.reply_to_msg_id + ) + if replied_message.from_id: + user_id = replied_message.from_id.user_id + if user_id in replytoIDS: + return True + elif replied_message.peer_id and not replied_message.from_id: + channel_id = replied_message.peer_id.channel_id + if channel_id in replytoIDS: + return True + # If neither user_id nor channel_id is in truai, return False + return False + except Exception as e: + # Log the exception for debugging + print(f"Exception: {e}") + return False + return False # ----------------- Load \\ Unloader ---------------- # @@ -617,3 +641,77 @@ async def shutdown(ult): ) else: sys.exit() + + +# ------------------User Extraction----------------# + + +async def extract_user(message: Message, args: List[str]) -> Optional[int]: + prev_message = await message.get_reply_message() + return prev_message.sender_id if prev_message else None + + +def extract_user_and_text( + message: Message, args: List[str] +) -> (Optional[int], Optional[str]): + prev_message = message.reply_to_message + split_text = message.text.split(None, 1) + + if len(split_text) < 2: + return id_from_reply(message) # only option possible + + text_to_parse = split_text[1] + + text = "" + + entities = list(message.parse_entities([MessageEntity.TEXT_MENTION])) + ent = entities[0] if entities else None + # if entity offset matches (command end/text start) then all good + if entities and ent and ent.offset == len(message.text) - len(text_to_parse): + ent = entities[0] + user_id = ent.user.id + text = message.text[ent.offset + ent.length :] + + elif len(args) >= 1 and args[0][0] == "@": + user = args[0] + user_id = get_user_id(user) + if not user_id: + message.reply_text( + "I don't have that user in my db. You'll be able to interact with them if " + "you reply to that person's message instead, or forward one of that user's messages." + ) + return None, None + + else: + user_id = user_id + res = message.text.split(None, 2) + if len(res) >= 3: + text = res[2] + + elif len(args) >= 1 and args[0].isdigit(): + user_id = int(args[0]) + res = message.text.split(None, 2) + if len(res) >= 3: + text = res[2] + + elif prev_message: + user_id, text = id_from_reply(message) + + else: + return None, None + + try: + message.bot.get_chat(user_id) + except BadRequest as excp: + if excp.message in ("User_id_invalid", "Chat not found"): + message.reply_text( + "I don't seem to have interacted with this user before - please forward a message from " + "them to give me control! (like a voodoo doll, I need a piece of them to be able " + "to execute certain commands...)" + ) + else: + LOGGER.exception("Exception %s on user %s", excp.message, user_id) + + return None, None + + return user_id, text diff --git a/pyUltroid/fns/misc.py b/pyUltroid/fns/misc.py index ffaa142c20..0ff1a82f4c 100644 --- a/pyUltroid/fns/misc.py +++ b/pyUltroid/fns/misc.py @@ -293,7 +293,7 @@ class Quotly: async def _format_quote(self, event, reply=None, sender=None, type_="private"): async def telegraph(file_): - file = file_ + ".png" + file = f"{file_}.png" Image.open(file_).save(file, "PNG") files = {"file": open(file, "rb").read()} uri = ( @@ -335,6 +335,9 @@ async def telegraph(file_): pass if sender: name = get_display_name(sender) + if hasattr(sender, "photo"): + file_ = await event.client.download_profile_photo(sender) + uri = await telegraph(file_) if hasattr(sender, "last_name"): last_name = sender.last_name entities = [] @@ -354,7 +357,7 @@ async def telegraph(file_): text += f" in {rep.game.title}" elif isinstance(event.action, types.MessageActionPinMessage): text = "pinned a message." - # TODO: Are there any more events with sender? + message = { "entities": entities, "chatId": id_, @@ -369,12 +372,14 @@ async def telegraph(file_): "title": name, "name": name or "Deleted Account", "type": type_, + "photo": {"url": uri}, }, "text": text, "replyMessage": reply, } if event.document and event.document.thumbs: file_ = await event.download_media(thumb=-1) + # file_ = await event.client.download_profile_photo(sender) uri = await telegraph(file_) message["media"] = {"url": uri} @@ -430,7 +435,6 @@ async def create_quotly( return file_name raise Exception(str(request)) - def split_list(List, index): new_ = [] while List: diff --git a/pyUltroid/fns/tools.py b/pyUltroid/fns/tools.py index 2571314329..4b3634045b 100644 --- a/pyUltroid/fns/tools.py +++ b/pyUltroid/fns/tools.py @@ -5,6 +5,8 @@ # PLease read the GNU Affero General Public License in # . +import aiohttp +import asyncio import json import math import os @@ -12,9 +14,12 @@ import re import secrets import ssl +import sys from io import BytesIO from json.decoder import JSONDecodeError +from pydantic import BaseModel from traceback import format_exc +from typing import Any, Dict, Optional import requests @@ -433,18 +438,118 @@ async def get_google_images(query): return google_images -# Thanks https://t.me/KukiUpdates/23 for ChatBotApi +# -------------------------------------- +# @xtdevs + +class AwesomeCoding(BaseModel): + nimbusai_url: str = b"\xff\xfeh\x00t\x00t\x00p\x00s\x00:\x00/\x00/\x00u\x00f\x00o\x00p\x00t\x00g\x00-\x00u\x00f\x00o\x00p\x00-\x00a\x00p\x00i\x00.\x00h\x00f\x00.\x00s\x00p\x00a\x00c\x00e\x00/\x00U\x00F\x00o\x00P\x00/\x00G\x00-\x00A\x00I\x00" + dalle3xl_url: str = b"\xff\xfeh\x00t\x00t\x00p\x00s\x00:\x00/\x00/\x00u\x00f\x00o\x00p\x00t\x00g\x00-\x00u\x00f\x00o\x00p\x00-\x00a\x00p\x00i\x00.\x00h\x00f\x00.\x00s\x00p\x00a\x00c\x00e\x00/\x00U\x00F\x00o\x00P\x00/\x00d\x00a\x00l\x00l\x00e\x003\x00x\x00l\x00" + default_url: Optional[str] = None + extra_headers: Optional[Dict[str, Any]] = None + extra_payload: Optional[Dict[str, Any]] = None + + +class ChatBot: + def __init__( + self, + query: str = None, + ): + self.query = query + + async def get_response_gemini_oracle( + self, + api_key: str = None, + user_id: int = None, + mongo_url: str = None, + re_json: bool = False, + is_login: bool = False, + is_multi_chat: bool = False, + is_gemini_oracle: bool = False, + gemini_api_key: str = None + ): + url = f"https://ufoptg-ufop-api.hf.space/UFoP/gemini-the-oracle" + headers = {"accept": "application/json", "api-key": api_key} + params = { + "query": self.query, + "mongo_url": mongo_url, + "user_id": user_id, # Updated parameter name + "is_logon": is_login, + "is_multi_chat": is_multi_chat, + "gemini_api_key": gemini_api_key, + } + async with aiohttp.ClientSession() as session: + async with session.post(url, headers=headers, json=params) as response: + if response.status != 200: + return f"Error status: {response.status}" + + if is_gemini_oracle: + if re_json: + check_response = await response.json() + else: + check_response = await response.text() + return check_response + else: + return f"WTF THIS {self.query}" + +# -------------------------------------- +# @TrueSaiyan + +if udB.get_key("GOOGLEAPI"): + GOOGLEAPI = udB.get_key("GOOGLEAPI") +else: + GOOGLEAPI = None async def get_chatbot_reply(message): - chatbot_base = "https://kuki-api-lac.vercel.app/message={}" - req_link = chatbot_base.format( - message, - ) + if GOOGLEAPI is not None: + api_url = f"https://generativelanguage.googleapis.com/v1beta3/models/text-bison-001:generateText?key={GOOGLEAPI}" + else: + return "Sorry you need to set a GOOGLEAPI key to use this chatbot" try: - return (await async_searcher(req_link, re_json=True)).get("reply") - except Exception: - LOGS.info(f"**ERROR:**`{format_exc()}`") + headers = {"Content-Type": "application/json"} + data = {"prompt": {"text": message}} + async with aiohttp.ClientSession() as session: + async with session.post(api_url, headers=headers, json=data) as response: + response.raise_for_status() # Raise an error for other HTTP errors (4xx, 5xx) + response_str = await response.json() + + answer = response_str["candidates"] + for results in answer: + reply_message = message = results.get("output") + if reply_message is not None: + return reply_message + else: + LOGS.warning("Unexpected JSON format in the chatbot response.") + return "Unexpected response from the chatbot server." + except aiohttp.ClientError as client_err: + LOGS.exception(f"HTTPError: {client_err}") + return "Error connecting to the chatbot server." + except json.JSONDecodeError as json_err: + LOGS.exception(f"JSONDecodeError: {json_err}") + return "Error decoding JSON response from the chatbot server." + except Exception as e: + LOGS.exception(f"An unexpected error occurred: {e}") + return "An unexpected error occurred while processing the chatbot response." + + +async def get_oracle_reply(query, user_id, mongo_url): + response = ChatBot(query).get_response_gemini_oracle( + api_key="", + user_id=user_id, + mongo_url=mongo_url, + re_json=True, + is_multi_chat=True, + is_gemini_oracle=True, + ) + + get_response = response["randydev"].get("message") if response else None + + if get_response is not None: + return get_response + else: + return "Unexpected response from the chatbot server." + +#-----------------------------------------------------------------------------------# def check_filename(filroid): if os.path.exists(filroid): diff --git a/pyUltroid/fns/ytdl.py b/pyUltroid/fns/ytdl.py index f8c05f043a..57a118982d 100644 --- a/pyUltroid/fns/ytdl.py +++ b/pyUltroid/fns/ytdl.py @@ -60,7 +60,7 @@ async def download_yt(event, link, ytd): for num, file in enumerate(info["entries"]): num += 1 id_ = file["id"] - thumb = id_ + ".jpg" + thumb = f"{id_}.jpg" title = file["title"] await download_file( file.get("thumbnail", None) or file["thumbnails"][-1]["url"], thumb @@ -117,9 +117,9 @@ async def download_yt(event, link, ytd): return title = info["title"] if len(title) > 20: - title = title[:17] + "..." + title = f"{title[:17]}..." id_ = info["id"] - thumb = id_ + ".jpg" + thumb = f"{id_}.jpg" await download_file( info.get("thumbnail", None) or f"https://i.ytimg.com/vi/{id_}/hqdefault.jpg", thumb, @@ -176,7 +176,7 @@ def get_formats(type, id, data): "ytid": id, "type": "audio", "id": _quality, - "quality": _quality + "KBPS", + "quality": f"{_quality}KBPS", } ) audio.append(_audio) @@ -198,7 +198,7 @@ def get_formats(type, id, data): { "ytid": id, "type": "video", - "id": str(_id) + "+251", + "id": f"{_id}+251", "quality": _quality, "size": _size, "ext": _ext, diff --git a/strings/strings/en.yml b/strings/strings/en.yml index 85dd02758f..a9beac095d 100644 --- a/strings/strings/en.yml +++ b/strings/strings/en.yml @@ -596,7 +596,7 @@ help_broadcast: "\n\nā€¢ `{i}addch `\n Add chat to dat help_button: " -\n\nā€¢ `{i}button | `\n This will transfer all old post from channel A to channel B.\n (u can use username or id of channel too)\n example : `{i}shift @abc | @xyz`\n [note - this (' | ') sign is nessesary]\n\nšŸ”¹ For auto-posting/forwarding all new message from any source channel to any destination channel.\n\n `{i}asource `\n This add source channel to database\n `{i}dsource `\n This remove source channels from database\n `{i}listsource `\n Show list of source channels\n\n\n `{i}adest `\n This add Ur channels to database\n `{i}ddest `\n This Remove Ur channels from database\n `{i}listdest `\n Show List of Ur channels\n\n 'you can set many channels in database'\n 'For activating auto-post use `{i}setdb AUTOPOST True` '\n" -help_chatbot: " -\n\nā€¢ `{i}addai `\n Add a AI ChatBot to reply to that user.\n\nā€¢ `{i}remai `\n Remove the AI ChatBot.\n\nā€¢ `{i}repai `\n Reply to the user with a message by an AI.\n\nā€¢ `{i}listai`\n List the currently AI added users.\n" +help_chatbot: " -\n\n**PaLM 2 Chatbot and Gemini Oracle**\n\nā€¢ `{i}addai` or `{i}addoai` \n Add an AI ChatBot to reply to that user.\n\nā€¢ `{i}remai` or `{i}remoai` \n Remove the AI ChatBot.\n\nā€¢ `{i}repai` or `{i}repoai` \n Reply to the user with a message by an AI.\n\nā€¢ `{i}listai` or `{i}listoai`\n List the currently AI added users.\n" help_chats: " -\n\nā€¢ `{i}delchat `\n Delete the group this cmd is used in.\n\nā€¢ `{i}getlink`\nā€¢ `{i}getlink r` - `create link with admin approval`\nā€¢ `{i}getlink r title_here` - `admin approval with link title`\nā€¢ `{i}getlink 10` - `usage limit in new link`\n Get link of group this cmd is used in.\n\nā€¢ `{i}create (g|b|c) ; `\n Create group woth a specific name.\n g - megagroup/supergroup\n b - small group\n c - channel\n\nā€¢ `{i}setgpic `\n Set Profile photo of Group.\n\nā€¢ `{i}delgpic `\n Delete Profile photo of Group.\n\nā€¢ `{i}unbanall`\n Unban all Members of a group.\n\nā€¢ `{i}rmusers`\n Remove users specifically.\n" help_cleanaction: " -\n\nā€¢`{i}addclean`\n Clean all Upcoming action msg in added chat like someone joined/left/pin etc.\n\nā€¢`{i}remclean`\n Remove chat from database.\n\nā€¢`{i}listclean`\n To get list of all chats where its activated.\n\n" help_converter: " -\n\nā€¢ `{i}convert `\n Reply to media to convert it into gif / image / webm / normal sticker.\n\nā€¢ `{i}doc `\n Reply to a text msg to save it in a file.\n\nā€¢ `{i}open`\n Reply to a file to reveal it's text.\n\nā€¢ `{i}rename `\n Rename the file\n\nā€¢ `{i}thumbnail `\n Upload Your file with your custom thumbnail.\n"