Revert "refactor: extract userbot to standalone repo, add as git submodule"

This reverts commit 3c383db9a7.
This commit is contained in:
2026-06-19 23:11:27 +02:00
parent d74a705d53
commit 1930600c40
134 changed files with 15257 additions and 4 deletions
+57
View File
@@ -0,0 +1,57 @@
from os import listdir
from pyrogram import Client, enums, filters
from pyrogram.types import Message
# noinspection PyUnresolvedReferences
from utils.misc import modules_help, prefix
# noinspection PyUnresolvedReferences
from utils.scripts import format_exc
@Client.on_message(filters.command(['lback'], prefix) & filters.me)
async def backup_database_cmd(_: Client, message: Message):
"""
Backup the database.
"""
if len(message.command) == 1:
await message.edit("[😇] I think you didn't specify the name of the bot.")
return
await message.edit_text("<b>I'm copying the database...</b>", parse_mode=enums.ParseMode.HTML)
try:
name = message.command[1].lower()
folders = listdir('/root/')
if name not in folders:
await message.edit('[😇] There is no such bot in the root folder.')
return
folder = listdir('/root/' + name)
for file in folder:
if file.endswith(('.db', '.sqlite', '.sqlite3')):
await message.reply_document(
document='/root/' + name + '/' + file,
caption='<code>Bot Database <b>' + name + '</b></code>',
parse_mode=enums.ParseMode.HTML,
)
return await message.delete()
folder = listdir('/root/' + name + '/assets')
for file in folder:
if file.endswith(('.db', '.sqlite', '.sqlite3')):
await message.reply_document(
document='/root/' + name + '/assets/' + file,
caption='<code>Bot Database <b>' + name + '</b></code>',
parse_mode=enums.ParseMode.HTML,
)
return await message.delete()
await message.edit('[😇] Database not found.')
except Exception as ex:
await message.edit_text(
f'Failed to back up the database!\n\n{format_exc(ex)}',
parse_mode=enums.ParseMode.HTML,
)
modules_help['autobackup'] = {
'lback [name]*': '<b>Backup database from folder</b>',
'lbackall': '<b>Backup all databases</b>',
}
+165
View File
@@ -0,0 +1,165 @@
#  Moon-Userbot - telegram userbot
#  Copyright (C) 2020-present Moon Userbot Organization
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
from pyrogram import Client, filters
from pyrogram.errors import MessageTooLong
from pyrogram.types import Message
from utils.db import db
from utils.misc import modules_help, prefix
def addtrg(channel_id):
channel_ids = db.get('custom.autofwd', 'chatto', default=[])
if channel_id not in channel_ids:
channel_ids.append(channel_id)
db.set('custom.autofwd', 'chatto', channel_ids)
def rmtrg(channel_id):
channel_ids = db.get('custom.autofwd', 'chatto', default=[])
if channel_id in channel_ids:
channel_ids.remove(channel_id)
db.set('custom.autofwd', 'chatto', channel_ids)
def addsrc(channel_id):
channel_ids = db.get('custom.autofwd', 'chatsrc', default=[])
if channel_id not in channel_ids:
channel_ids.append(channel_id)
db.set('custom.autofwd', 'chatsrc', channel_ids)
def rmsrc(channel_id):
channel_ids = db.get('custom.autofwd', 'chatsrc', default=[])
if channel_id in channel_ids:
channel_ids.remove(channel_id)
db.set('custom.autofwd', 'chatsrc', channel_ids)
def getfwd_data():
source_chats = db.get('custom.autofwd', 'chatsrc')
target_chats = db.get('custom.autofwd', 'chatto')
return source_chats, target_chats
@Client.on_message(filters.command(['addfwd_src', 'addfwd_to'], prefix) & filters.me)
async def addfwd(_, message: Message):
if message.command[0] == 'addfwd_src':
if len(message.command) > 1:
channel_id = message.text.split(maxsplit=1)[1]
if not channel_id.startswith('-100'):
channel_id = '-100' + channel_id
# chat id should be integer
if not channel_id.isdigit():
try:
channel_id = int(channel_id)
except Exception:
return await message.edit_text('Chat id should be in integer')
addsrc(channel_id=channel_id)
await message.edit_text(f'Auto Forwarding Enabled for Chat with id: <code>{channel_id}</code>')
else:
await message.edit_text('Chat id not provided!')
return
elif message.command[0] == 'addfwd_to':
if len(message.command) > 1:
channel_id = message.text.split(maxsplit=1)[1]
if not channel_id.startswith('-100'):
channel_id = '-100' + channel_id
# chat id should be integer
if not channel_id.isdigit():
try:
channel_id = int(channel_id)
except Exception:
return await message.edit_text('Chat id should be in integer')
addtrg(channel_id=channel_id)
await message.edit_text(f'Auto Forwarding Enabled to Chat with id: <code>{channel_id}</code>')
else:
await message.edit_text('Chat id not provided!')
return
@Client.on_message(filters.command(['delfwd_src', 'delfwd_to'], prefix) & filters.me)
async def delfwd(_, message: Message):
if message.command[0] == 'delfwd_src':
if len(message.command) > 1:
channel_id = message.text.split(maxsplit=1)[1]
if not channel_id.startswith('-100'):
channel_id = '-100' + channel_id
# chat id should be integer
if not channel_id.isdigit():
try:
channel_id = int(channel_id)
except Exception:
return await message.edit_text('Chat id should be in integer')
rmsrc(channel_id=channel_id)
await message.edit_text(f'Auto Forwarding Disabled for Chat with id: <code>{channel_id}</code>')
else:
await message.edit_text('Chat id not provided!')
return
elif message.command[0] == 'delfwd_to':
if len(message.command) > 1:
channel_id = message.text.split(maxsplit=1)[1]
if not channel_id.startswith('-100'):
channel_id = '-100' + channel_id
# chat id should be integer
if not channel_id.isdigit():
try:
channel_id = int(channel_id)
except Exception:
return await message.edit_text('Chat id should be in integer')
rmtrg(channel_id=channel_id)
await message.edit_text(f'Auto Forwarding Disabled to Chat with id: <code>{channel_id}</code>')
else:
await message.edit_text('Chat id not provided!')
return
@Client.on_message(filters.command('autofwd', prefix) & filters.me)
async def autofwd(_, message: Message):
source_chats, target_chats = getfwd_data()
return await message.edit_text(f'Source Chats: {source_chats}\nTarget Chats: {target_chats}')
@Client.on_message(filters.channel | filters.group)
async def autofwd_main(client: Client, message: Message):
chat_id = message.chat.id
source_chats = db.get('custom.autofwd', 'chatsrc')
target_chats = db.get('custom.autofwd', 'chatto')
if source_chats is not None and chat_id in source_chats and target_chats is not None:
for chat in target_chats:
try:
await message.copy(chat)
except Exception as e:
try:
await client.send_message(
'me',
f'Auto Forwarding Failed for Chat with id: <code>{chat_id}</code> to <code>{chat}</code>\n\n{e}',
)
except MessageTooLong:
await client.send_message(
'me',
f'Auto Forwarding Failed for Chat with id: <code>{chat_id}</code> to <code>{chat}</code>, Please check logs!',
)
modules_help['autofwd'] = {
'autofwd': 'Retrieve Data of auto fwd',
'addfwd_src [channel_id]*': 'Enable auto forwarding for a channel',
'addfwd_to [channel_id]*': 'Enable auto forwarding to a channel',
'delfwd_src [channel_id]*': 'Disable auto forwarding for a channel',
'delfwd_to [channel_id]*': 'Disable auto forwarding to a channel',
}
+230
View File
@@ -0,0 +1,230 @@
import os
import shutil
from pyrogram import Client, enums, filters
from pyrogram.types import Message
from utils import config
from utils.db import db
# noinspection PyUnresolvedReferences
from utils.misc import modules_help, prefix
from utils.scripts import format_exc, restart
if config.db_type in ['mongodb', 'mongo']:
import bson
def dump_mongo(collections, path, db_):
for coll in collections:
with open(os.path.join(path, f'{coll}.bson'), 'wb+') as f:
for doc in db_[coll].find():
f.write(bson.BSON.encode(doc))
def restore_mongo(path, db_):
for coll in os.listdir(path):
if coll.endswith('.bson'):
with open(os.path.join(path, coll), 'rb+') as f:
db_[coll.split('.')[0]].insert_many(bson.decode_all(f.read()))
@Client.on_message(filters.command(['backup', 'back'], prefix) & filters.me)
async def backup(client: Client, message: Message):
"""
Backup the database
"""
try:
if not os.path.exists('backups/'):
os.mkdir('backups/')
await message.edit('<b>Backing up database...</b>', parse_mode=enums.ParseMode.HTML)
if config.db_type in ['mongo', 'mongodb']:
dump_mongo(db._database.list_collection_names(), 'backups/', db._database)
return await message.edit(
'<b>Database backed up to:</b> <code>backups/</code> folder',
parse_mode=enums.ParseMode.HTML,
)
else:
shutil.copy(config.db_name, f'backups/{config.db_name}')
await client.send_document(
'me',
caption='<b>Database backup complete!\nType: </b>'
'<code>.restore</code> in response to this message to restore the database.',
document=f'backups/{config.db_name}',
parse_mode=enums.ParseMode.HTML,
)
return await message.edit(
'<b>Database backed up successfully! <code>(Check your favorites)</code></b>',
parse_mode=enums.ParseMode.HTML,
)
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
@Client.on_message(filters.command(['restore', 'res'], prefix) & filters.me)
async def restore(client: Client, message: Message):
"""
Restore the database
"""
try:
await message.edit('<b>Restoring database...</b>', parse_mode=enums.ParseMode.HTML)
if config.db_type in ['mongo', 'mongodb']:
restore_mongo('backups/', db._database)
return await message.edit(
'<b>Database restored from:</b> <code>backups/</code> folder',
parse_mode=enums.ParseMode.HTML,
)
else:
if not message.reply_to_message or not message.reply_to_message.document:
return await message.edit(
'<b>Reply to a document to restore the database.</b>',
parse_mode=enums.ParseMode.HTML,
)
elif not message.reply_to_message.document.file_name.casefold().endswith(('.db', '.sqlite', '.sqlite3')):
return await message.edit(
'<b>Reply to a database file to restore the database.</b>',
parse_mode=enums.ParseMode.HTML,
)
await message.reply_to_message.download(f'backups/{message.reply_to_message.document.file_name}')
shutil.copy(f'backups/{message.reply_to_message.document.file_name}', config.db_name)
await message.edit(
'<b>Database restored successfully!</b>',
parse_mode=enums.ParseMode.HTML,
)
restart()
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
@Client.on_message(filters.command(['backupmods', 'bms'], prefix) & filters.me)
async def backupmods(client: Client, message: Message):
"""
Backup the modules
"""
try:
if not os.path.exists('backups/'):
os.mkdir('backups/')
await message.edit('<b>Backing up modules...</b>', parse_mode=enums.ParseMode.HTML)
from utils.misc import modules_help
for mod in modules_help:
if os.path.isfile(f'modules/custom_modules/{mod}.py'):
f = open(f'backups/{mod}.py', 'wb')
f.write(open(f'modules/custom_modules/{mod}.py', 'rb').read())
await message.edit(
text='<b>All modules backed up to:</b> <code>backups/</code> folder',
parse_mode=enums.ParseMode.HTML,
)
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
@Client.on_message(filters.command(['backupmod', 'bm'], prefix) & filters.me)
async def backupmod(client: Client, message: Message):
"""
Backup the module
"""
try:
if not os.path.exists('backups/'):
os.mkdir('backups/')
try:
mod = message.text.split(maxsplit=1)[1].split('.')[0]
except Exception:
return await message.edit(
f'<b>Usage:</b> <code>{prefix}backupmod [module]</code>',
parse_mode=enums.ParseMode.HTML,
)
await message.edit('<b>Backing up module...</b>', parse_mode=enums.ParseMode.HTML)
if os.path.isfile(f'modules/custom_modules/{mod}.py'):
f = open(f'backups/{mod}.py', 'wb')
f.write(open(f'modules/custom_modules/{mod}.py', 'rb').read())
else:
return await message.edit(
f'<b>Module <code>{mod}</code> not found.</b>',
parse_mode=enums.ParseMode.HTML,
)
await message.reply_document(
document=f'backups/{mod}.py',
caption=f'<b>Module <code>{mod}</code> backed up to:</b> <code>backups/</code> folder',
parse_mode=enums.ParseMode.HTML,
)
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
@Client.on_message(filters.command(['restoremod', 'resmod'], prefix) & filters.me)
async def restoremod(client: Client, message: Message):
"""
Restore the module
"""
try:
if not os.path.exists('backups/'):
os.mkdir('backups/')
try:
mod = message.text.split(maxsplit=1)[1].split('.')[0]
except Exception:
return await message.edit(
f'<b>Usage:</b> <code>{prefix}restoremod [module]</code>',
parse_mode=enums.ParseMode.HTML,
)
await message.edit('<b>Restoring module...</b>', parse_mode=enums.ParseMode.HTML)
if os.path.isfile(f'backups/{mod}.py'):
f = open(f'modules/custom_modules/{mod}.py', 'wb')
f.write(open(f'backups/{mod}.py', 'rb').read())
else:
return await message.edit(
f'<b>Module <code>{mod}</code> not found.</b>',
parse_mode=enums.ParseMode.HTML,
)
await message.edit(
f'<b>Module <code>{mod}</code> restored successfully!</b>',
parse_mode=enums.ParseMode.HTML,
)
restart()
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
@Client.on_message(filters.command(['restoremods', 'resmods'], prefix) & filters.me)
async def restoremods(client: Client, message: Message):
"""
Restore the modules
"""
try:
if not os.path.exists('backups/'):
os.mkdir('backups/')
await message.edit('<b>Restoring modules...</b>', parse_mode=enums.ParseMode.HTML)
for mod in os.listdir('backups/'):
if mod.endswith('.py'):
if os.path.isfile(f'modules/{mod}'):
continue
f = open(f'modules/custom_modules/{mod}', 'wb')
f.write(open(f'backups/{mod}', 'rb').read())
await message.edit(
text='<b>All modules restored from:</b> <code>backups/</code> folder',
parse_mode=enums.ParseMode.HTML,
)
restart()
except Exception as e:
await message.edit(format_exc(e), parse_mode=enums.ParseMode.HTML)
modules_help['backup'] = {
'backup': '<b>Backup database</b>',
'restore [reply]': '<b>Restore database</b>',
'backupmod [name]': '<b>Backup mod</b>',
'backupmods': '<b>Backup all mods</b>',
'resmod [name]': '<b>Restore mod</b>',
'resmods': '<b>Restore all mods</b>',
}
+95
View File
@@ -0,0 +1,95 @@
import requests
from pyrogram import Client, filters
from utils.misc import modules_help, prefix
from utils.scripts import format_exc, import_library
pcp = import_library('pubchempy')
INATURALIST_API_URL = 'https://api.inaturalist.org/v1/observations'
def get_marine_life_details(species_name):
params = {
'taxon_name': species_name,
'quality_grade': 'research',
'iconic_taxa': 'Mollusca,Fish,Crustacea',
'per_page': 1,
}
response = requests.get(INATURALIST_API_URL, params=params, timeout=10)
if response.status_code == 200:
data = response.json()
if data['total_results'] > 0:
observation = data['results'][0]
species = observation['taxon']['name']
common_name = observation['taxon']['preferred_common_name']
photo_url = observation['photos'][0]['url'] if observation['photos'] else 'No photo available'
description = observation.get('description', 'No description available.')
return {
'species': species,
'common_name': common_name,
'photo_url': photo_url,
'description': description,
}
else:
return {'error': 'No marine life found for this species.'}
else:
return {'error': f'Error {response.status_code}: Unable to connect to iNaturalist API.'}
@Client.on_message(filters.command('camistry', prefix) & filters.me)
async def fetch_chemical_data_with_visual(_, message):
query = ' '.join(message.text.split()[1:]) # Combine query words properly
try:
# Fetch chemical data by name
results = pcp.get_compounds(query, 'name', record_type='3d')
if results:
compound = results[0]
# Send chemical information without SMILES structure
info = (
f'<b>Chemical Data for {compound.iupac_name}</b>\n\n'
f'<b>Molecular Formula:</b> <code>{compound.molecular_formula}</code>\n'
f'<b>Molecular Weight:</b> <code>{compound.molecular_weight}</code>\n'
f'<b>CID:</b> <code>{compound.cid}</code>\n'
f'<b>Synonyms:</b> <code>{", ".join(compound.synonyms[:5])}</code>\n'
)
await message.edit_text(info)
else:
await message.edit_text(f"No chemical data found for the query: '{query}'")
except pcp.PubChemHTTPError as http_err:
await message.edit_text(f'HTTP error occurred: {format_exc(http_err)}')
except Exception as e:
await message.edit_text(f'An error occurred: {format_exc(e)}')
@Client.on_message(filters.command('marinelife', prefix) & filters.me)
async def marine_life_command(_, message):
if len(message.command) < 2:
await message.edit_text(f'Please specify a species name. Example: {prefix}marinelife dolphin')
return
species_name = ' '.join(message.command[1:])
marine_life = get_marine_life_details(species_name)
if 'error' in marine_life:
await message.edit_text(marine_life['error'])
else:
reply_text = (
f'<b>Species</b>: <code>{marine_life["species"]}</code>\n'
f'<b>Common Name</b>: <code>{marine_life["common_name"]}</code>\n'
f'<b>Description</b>: <code>{marine_life["description"]}</code>\n'
f"<a href='{marine_life['photo_url']}'>Photo</a>"
)
await message.edit_text(reply_text)
modules_help['cama'] = {
'camistry [text]': ' getting camicale info',
'marinelife [text]': ' getting marinelife info',
}
+139
View File
@@ -0,0 +1,139 @@
import asyncio
import os
from collections import defaultdict
from pyrogram import Client, filters
from pyrogram.errors import (
FileReferenceExpired,
FileReferenceInvalid,
TopicClosed,
TopicDeleted,
)
from pyrogram.types import Message
from utils.db import db
from utils.misc import modules_help, prefix
mlog_enabled = filters.create(lambda _, __, ___: db.get('custom.mlog', 'status', False))
# Media cache and processing tasks
user_media_cache = defaultdict(list)
media_processing_tasks = {}
# Helper to get group-specific data
def get_group_data(group_id):
return db.get('custom.mlog', str(group_id), {})
# Helper to update group-specific data
def update_group_data(group_id, data):
db.set('custom.mlog', str(group_id), data)
@Client.on_message(filters.command(['mlog'], prefix) & filters.me)
async def mlog(_, message: Message):
if len(message.command) < 2 or message.command[1].lower() not in ['on', 'off']:
return await message.edit(f'<b>Usage:</b> <code>{prefix}mlog [on/off]</code>')
status = message.command[1].lower() == 'on'
db.set('custom.mlog', 'status', status)
await message.edit(f'<b>Media logging is now {"enabled" if status else "disabled"}</b>')
@Client.on_message(filters.command(['msetchat'], prefix) & filters.me)
async def set_chat(_, message: Message):
if len(message.command) < 2:
return await message.edit(f'<b>Usage:</b> <code>{prefix}msetchat [chat_id]</code>')
try:
chat_id = message.command[1]
chat_id = int('-100' + chat_id if not chat_id.startswith('-100') else chat_id)
db.set('custom.mlog', 'chat', chat_id)
await message.edit(f'<b>Chat ID set to {chat_id}</b>')
except ValueError:
await message.edit('<b>Invalid chat ID</b>')
@Client.on_message(mlog_enabled & filters.incoming & filters.private & filters.media & ~filters.me & ~filters.bot)
async def media_log(client: Client, message: Message):
user_id = message.from_user.id
user_media_cache[user_id].append(message)
if user_id not in media_processing_tasks:
media_processing_tasks[user_id] = asyncio.create_task(process_media(client, message.from_user))
async def process_media(client: Client, user):
await asyncio.sleep(5) # Wait to group incoming media
user_id = user.id
me = await client.get_me()
if user_id == me.id:
return
chat_id = db.get('custom.mlog', 'chat')
if not chat_id:
return await client.send_message(
'me',
f'Media Logger is on, but no Chat ID is set. Use {prefix}msetchat to set it.',
)
group_data = get_group_data(chat_id)
user_topics = group_data.get('user_topics', {})
topic_id = user_topics.get(str(user_id)) # Fetch user's topic ID if it exists
if not topic_id:
topic = await client.create_forum_topic(chat_id, user.first_name)
topic_id = topic.id
user_topics[str(user_id)] = topic_id # Store topic ID for this user
update_group_data(chat_id, {'user_topics': user_topics})
m = await client.send_message(
chat_id=chat_id,
message_thread_id=topic_id,
text=f'<b>Chat Name:</b> {user.full_name}\n<b>User ID:</b> {user_id}\n<b>Username:</b> @{user.username or "N/A"}\n<b>Phone No:</b> +{user.phone_number or "N/A"}',
)
await m.pin()
messages_to_process = user_media_cache.pop(user_id, [])
for media_message in messages_to_process:
try:
await media_message.copy(chat_id=chat_id, message_thread_id=topic_id)
await asyncio.sleep(1) # Delay between sending media
except (FileReferenceExpired, FileReferenceInvalid):
# Handle self-destruct photos and video notes
await handle_self_destruct_media(client, media_message, chat_id, topic_id)
except TopicDeleted:
topic = await client.create_forum_topic(chat_id, user.first_name)
topic_id = topic.id
user_topics[str(user_id)] = topic_id # Update the new topic ID
update_group_data(chat_id, {'user_topics': user_topics})
await client.send_message(
chat_id=chat_id,
message_thread_id=topic_id,
text=f'<b>Chat Name:</b> {user.full_name}\n<b>User ID:</b> {user_id}\n<b>Username:</b> @{user.username or "N/A"}\n<b>Phone No:</b> +{user.phone_number or "N/A"}',
)
await handle_self_destruct_media(client, media_message, chat_id, topic_id)
except TopicClosed:
await client.reopen_forum_topic(chat_id=chat_id, topic_id=topic_id)
await media_message.copy(chat_id=chat_id, message_thread_id=topic_id)
media_processing_tasks.pop(user_id, None)
async def handle_self_destruct_media(client: Client, message: Message, chat_id: int, topic_id: int):
try:
# Download the self-destructing media
file_path = await message.download()
if message.photo:
await client.send_photo(chat_id, file_path, message_thread_id=topic_id)
elif message.video_note:
await client.send_video(chat_id, file_path, message_thread_id=topic_id)
os.remove(file_path) # Clean up after sending
except Exception as e:
print(f'Error handling self-destructing media: {e}')
modules_help['mlog'] = {
'mlog [on/off]': 'Enable or disable media logging',
'msetchat [chat_id]': 'Set the chat ID for media logging',
}
+90
View File
@@ -0,0 +1,90 @@
from datetime import datetime
import aiohttp
from pyrogram import Client, filters
from pyrogram.types import Message
from utils.misc import modules_help, prefix
# Aladhan API credentials
ALADHAN_API_URL = 'https://api.aladhan.com/v1/timingsByCity'
DEFAULT_METHOD = 2 # Islamic Society of North America
DEFAULT_CITY = 'Lahore'
DEFAULT_COUNTRY = 'PK'
async def fetch_namaz_times(city_name: str, country_name: str) -> dict:
params = {'city': city_name, 'country': country_name, 'method': DEFAULT_METHOD}
async with aiohttp.ClientSession() as session:
try:
async with session.get(ALADHAN_API_URL, params=params) as response:
response.raise_for_status()
return await response.json()
except Exception as e:
return {'error': str(e)}
def format_time_12hr(time_str: str) -> str:
try:
# Split time into hours and minutes
hours, minutes = map(int, time_str.split(':'))
# Convert to 12-hour format
period = 'AM' if hours < 12 else 'PM'
if hours == 0:
hours = 12
elif hours > 12:
hours -= 12
elif hours == 12:
period = 'PM'
return f'{hours}:{minutes:02d} {period}'
except Exception as e:
return f'Error formatting time: {str(e)}'
@Client.on_message(filters.command('prayer', prefix) & filters.me)
async def namaz_times(client: Client, message: Message):
if message.reply_to_message:
city_name = message.reply_to_message.text
country_name = DEFAULT_COUNTRY # Default to Pakistan if no country is provided
else:
args = message.text.split(maxsplit=2)
if len(args) < 2:
city_name = DEFAULT_CITY
country_name = DEFAULT_COUNTRY
elif len(args) == 2:
city_name = args[1]
country_name = DEFAULT_COUNTRY
else:
city_name = args[1]
country_name = args[2]
data = await fetch_namaz_times(city_name, country_name)
if 'error' in data:
result = f'<b>Error:</b> <i>{data["error"]}</i>'
elif 'data' in data:
timings = data['data']['timings']
today = datetime.now().strftime('%Y-%m-%d')
formatted_timings = {
'Fajr': format_time_12hr(timings['Fajr']),
'Dhuhr': format_time_12hr(timings['Dhuhr']),
'Asr': format_time_12hr(timings['Asr']),
'Maghrib': format_time_12hr(timings['Maghrib']),
'Isha': format_time_12hr(timings['Isha']),
}
result = (
f'<b>Prayer Times for {city_name}, {country_name} on {today}:</b>\n\n'
f'<b>Fajr:</b> {formatted_timings["Fajr"]}\n'
f'<b>Dhuhr:</b> {formatted_timings["Dhuhr"]}\n'
f'<b>Asr:</b> {formatted_timings["Asr"]}\n'
f'<b>Maghrib:</b> {formatted_timings["Maghrib"]}\n'
f'<b>Isha:</b> {formatted_timings["Isha"]}\n'
)
else:
result = '<b>Error:</b> <i>Unable to get prayer times for the specified location.</i>'
await message.edit_text(result)
modules_help['namaz'] = {
'prayer [city_name] [country_name]': 'Shows the prayer times. Default to Pakistan if no country is provided'
}
+519
View File
@@ -0,0 +1,519 @@
# Moon-Userbot - telegram userbot
# Copyright (C) 2020-present Moon Userbot Organization
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import base64
import os
from io import BytesIO
import aiofiles
import aiohttp
import requests
from PIL import Image, ImageEnhance
from pyrogram import Client, enums, filters
from pyrogram.errors import MediaCaptionTooLong, MessageTooLong
from pyrogram.types import InputMediaPhoto, Message
from utils.misc import modules_help, prefix
from utils.scripts import format_exc
url = 'https://api.safone.co'
headers = {
'Accept-Language': 'en-US,en;q=0.9',
'Connection': 'keep-alive',
'DNT': '1',
'Referer': 'https://api.safone.dev/docs',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'accept': 'application/json',
'sec-ch-ua': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Linux"',
}
async def make_carbon(code):
url = 'https://carbonara.solopov.dev/api/cook'
async with aiohttp.ClientSession() as session, session.post(url, json={'code': code}) as resp:
image_data = await resp.read()
carbon_image = Image.open(BytesIO(image_data))
enhancer = ImageEnhance.Brightness(carbon_image)
bright_image = enhancer.enhance(1.0)
output_image = BytesIO()
bright_image.save(output_image, format='PNG', quality=95)
output_image.name = 'carbon.png'
return output_image
async def telegraph(title, user_name, content):
formatted_content = '<br>'.join(content.split('\n'))
formatted_content = '<p>' + formatted_content + '</p>'
data = {'title': title, 'content': formatted_content, 'author_name': user_name}
response = requests.post(url=f'{url}/telegraph/text', headers=headers, json=data, timeout=5)
result = response.json()
return result['url']
async def voice_characters():
response = requests.get(url=f'{url}/speech/characters', headers=headers, timeout=5)
result = response.json()
return ', '.join(result['characters'])
async def make_rayso(code: str, title: str, theme: str):
data = {
'code': code,
'title': title,
'theme': theme,
'padding': 64,
'language': 'auto',
'darkMode': False,
}
response = requests.post(f'{url}/rayso', data=data, headers=headers, timeout=10)
if response.status_code != 200:
return None
result = response.json()
try:
if result['error'] is not None:
return None
except KeyError:
pass
image_data = result['image']
file_name = 'rayso.png'
with open(file_name, 'wb') as f:
f.write(base64.b64decode(image_data))
return file_name
@Client.on_message(filters.command('asq', prefix) & filters.me)
async def asq(_, message: Message):
if len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
await message.edit_text('Query not provided!')
return
await message.edit_text('Processing...')
response = requests.get(url=f'{url}/asq?query={query}', headers=headers, timeout=5)
if response.status_code != 200:
await message.edit_text('Something went wrong!')
return
result = response.json()
ans = result['answer']
await message.edit_text(f'Q. {query}\n A. {ans}', parse_mode=enums.ParseMode.MARKDOWN)
@Client.on_message(filters.command('sgemini', prefix) & filters.me)
async def sgemini(_, message: Message):
if len(message.command) > 1:
prompt = message.text.split(maxsplit=1)[1]
else:
await message.edit_text('prompt not provided!')
return
await message.edit_text('Processing...')
response = requests.get(url=f'{url}/bard?query={prompt}', headers=headers, timeout=10)
if response.status_code != 200:
await message.edit_text('Something went wrong!')
return
result = response.json()
ans = result['message']
await message.edit_text(f'Prompt: {prompt}\n Ans: {ans}', parse_mode=enums.ParseMode.MARKDOWN)
@Client.on_message(filters.command('app', prefix) & filters.me)
async def app(client: Client, message: Message):
try:
chat_id = message.chat.id
await message.edit_text('Processing...')
if len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
message.edit_text("What should i search? You didn't provided me with any value to search")
response = requests.get(url=f'{url}/apps?query={query}&limit=1', headers=headers, timeout=5)
if response.status_code != 200:
await message.edit_text('Something went wrong')
return
result = response.json()
try:
coverImage_url = result['results'][0]['icon']
coverImage = requests.get(url=coverImage_url, timeout=10).content
async with aiofiles.open('coverImage.jpg', mode='wb') as f:
await f.write(coverImage)
except Exception:
coverImage = None
description = result['results'][0]['description']
developer = result['results'][0]['developer']
IsFree = result['results'][0]['free']
genre = result['results'][0]['genre']
package_name = result['results'][0]['id']
title = result['results'][0]['title']
price = result['results'][0]['price']
link = result['results'][0]['link']
rating = result['results'][0]['rating']
await message.delete()
await client.send_media_group(
chat_id,
[
InputMediaPhoto(
'coverImage.jpg',
caption=f'<b>Title:</b> <code>{title}</code>\n<b>Rating:</b> <code>{rating}</code>\n<b>IsFree:</b> <code>{IsFree}</code>\n<b>Price:</b> <code>{price}</code>\n<b>Package Name:</b> <code>{package_name}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Developer:</b> <code>{developer}\n<b>Description:</b> {description}\n<b>Link:</b> {link}',
)
],
)
except MediaCaptionTooLong:
description = description[:850]
await message.delete()
await client.send_media_group(
chat_id,
[
InputMediaPhoto(
'coverImage.jpg',
caption=f'<b>Title:</b> <code>{title}</code>\n<b>Rating:</b> <code>{rating}</code>\n<b>IsFree:</b> <code>{IsFree}</code>\n<b>Price:</b> <code>{price}</code>\n<b>Package Name:</b> <code>{package_name}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Developer:</b> <code>{developer}\n<b>Description:</b> {description}\n<b>Link:</b> {link}',
)
],
)
except Exception as e:
await message.edit_text(format_exc(e))
finally:
if os.path.exists('coverImage.jpg'):
os.remove('coverImage.jpg')
@Client.on_message(filters.command('tsearch', prefix) & filters.me)
async def tsearch(client: Client, message: Message):
try:
chat_id = message.chat.id
limit = 10
await message.edit_text('Processing...')
if len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
message.edit_text("What should i search? You didn't provided me with any value to search")
response = requests.get(url=f'{url}/torrent?query={query}&limit={limit}', headers=headers, timeout=10)
if response.status_code != 200:
await message.edit_text('Something went wrong')
return
result = response.json()
coverImage_url = result['results'][0]['thumbnail']
description = result['results'][0]['description']
genre = result['results'][0]['genre']
category = result['results'][0]['category']
title = result['results'][0]['name']
link = result['results'][0]['magnetLink']
link_result = await telegraph(title=title, user_name=message.from_user.first_name, content=link)
language = result['results'][0]['language']
size = result['results'][0]['size']
results = []
for i in range(min(limit, len(result['results']))):
descriptions = result['results'][i]['description']
genres = result['results'][i]['genre']
categorys = result['results'][i]['category']
titles = result['results'][i]['name']
links = result['results'][i]['magnetLink']
languages = result['results'][i]['language']
sizes = result['results'][i]['size']
r = f'<b>Title:</b> <code>{titles}</code>\n<b>Category:</b> <code>{categorys}</code>\n<b>Language:</b> <code>{languages}</code>\n<b>Size:</b> <code>{sizes}</code>\n<b>Genres:</b> <code>{genres}</code>\n<b>Description:</b> {descriptions}\n<b>Magnet Link:</b> <code>{links}</code><br>'
results.append(r)
all_results_content = '<br>'.join(results)
link_results = await telegraph(
title='Search Results',
user_name=message.from_user.first_name,
content=all_results_content,
)
if coverImage_url is not None:
coverImage = requests.get(url=coverImage_url, timeout=10).content
async with aiofiles.open('coverImage.jpg', mode='wb') as f:
await f.write(coverImage)
await message.delete()
await client.send_media_group(
chat_id,
[
InputMediaPhoto(
'coverImage.jpg',
caption=f"<b>Title:</b> <code>{title}</code>\n<b>Category:</b> <code>{category}</code>\n<b>Language:</b> <code>{language}</code>\n<b>Size:</b> <code>{size}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Description:</b> {description}\n<b>Magnet Link:</b> <a href='{link_result}'>Click Here</a>\n<b>More Results:</b> <a href='{link_results}'>Click Here</a>",
)
],
)
else:
await message.edit_text(
f"<b>Title:</b> <code>{title}</code>\n<b>Category:</b> <code>{category}</code>\n<b>Language:</b> <code>{language}</code>\n<b>Size:</b> <code>{size}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Description:</b> {description}\n<b>Magnet Link:</b> <a href='{link_result}'>Click Here</a>\n<b>More Results:</b> <a href='{link_results}'>Click Here</a>",
disable_web_page_preview=True,
)
except MediaCaptionTooLong:
description = description[:850]
await message.delete()
await client.send_media_group(
chat_id,
[
InputMediaPhoto(
'coverImage.jpg',
caption=f"<b>Title:</b> <code>{title}</code>\n<b>Category:</b> <code>{category}</code>\n<b>Language:</b> <code>{language}</code>\n<b>Size:</b> <code>{size}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Description:</b> {description}\n<b>Magnet Link:</b> <a href='{link_result}'>Click Here</a>",
)
],
)
except MessageTooLong:
description = description[:150]
await message.edit_text(
f"<b>Title:</b> <code>{title}</code>\n<b>Category:</b> <code>{category}</code>\n<b>Language:</b> <code>{language}</code>\n<b>Size:</b> <code>{size}</code>\n<b>Genres:</b> <code>{genre}</code>\n<b>Description:</b> {description}\n<b>Magnet Link:</b> <a href='{link_result}'>Click Here</a>",
disable_web_page_preview=True,
)
except Exception as e:
await message.edit_text(format_exc(e))
finally:
if os.path.exists('coverImage.jpg'):
os.remove('coverImage.jpg')
@Client.on_message(filters.command('stts', prefix) & filters.me)
async def tts(client: Client, message: Message):
characters = await voice_characters()
await message.edit_text('<code>Please Wait...</code>')
try:
if len(message.command) > 2:
character, prompt = message.text.split(maxsplit=2)[1:]
if character not in characters:
await message.edit_text(
f'<b>Usage: </b><code>{prefix}tts [character]* [text/reply to text]*</code>\n <b>Available Characters:</b> <blockquote>{characters}</blockquote>'
)
return
elif message.reply_to_message and len(message.command) > 1:
character = message.text.split(maxsplit=1)[1]
if character in characters:
prompt = message.reply_to_message.text
else:
await message.edit_text(
f'<b>Usage: </b><code>{prefix}tts [character]* [text/reply to text]*</code>\n <b>Available Characters:</b> <blockquote>{characters}</blockquote>'
)
return
else:
await message.edit_text(
f'<b>Usage: </b><code>{prefix}tts [character]* [text/reply to text]*</code>\n <b>Available Characters:</b> <blockquote>{characters}</blockquote>'
)
return
data = {'text': prompt, 'character': character}
response = requests.post(url=f'{url}/speech', headers=headers, json=data, timeout=10)
if response.status_code != 200:
await message.edit_text('Something went wrong')
return
result = response.json()
audio_data = result['audio']
audio_data = base64.b64decode(audio_data)
async with aiofiles.open(f'{prompt}.mp3', mode='wb') as f:
await f.write(audio_data)
await message.delete()
await client.send_audio(
chat_id=message.chat.id,
audio=f'{prompt}.mp3',
caption=f'<b>Characters:</b> <code>{character}</code>\n<b>Prompt:</b> <code>{prompt}</code>',
)
if os.path.exists(f'{prompt}.mp3'):
os.remove(f'{prompt}.mp3')
except KeyError:
try:
error = result['error']
await message.edit_text(error)
except KeyError:
await message.edit_text(
f'<b>Usage: </b><code>{prefix}tts [character]* [text/reply to text]*</code>\n <b>Available Characters:</b> <blockquote>{characters}</blockquote>'
)
except Exception as e:
await message.edit_text(format_exc(e))
@Client.on_message(filters.command(['carbonnowsh', 'carboon', 'carbon', 'cboon'], prefix) & filters.me)
async def carbon(client: Client, message: Message):
if message.reply_to_message:
text = message.reply_to_message.text
message_id = message.reply_to_message.id
elif len(message.command) > 1:
message_id = None
text = message.text.split(maxsplit=1)[1]
else:
await message.edit_text('Query not provided!')
return
await message.edit_text('Processing...')
image_file = await make_carbon(text)
await message.delete()
try:
await client.send_photo(
chat_id=message.chat.id,
photo=image_file,
caption=f'<b>Text:</b> <code>{text}</code>',
reply_to_message_id=message_id,
)
except MediaCaptionTooLong:
cap = text[:850]
await client.send_photo(
chat_id=message.chat.id,
photo=image_file,
caption=f'<b>Text:</b> <code>{cap}</code>',
reply_to_message_id=message_id,
)
except Exception as e:
await message.edit_text(format_exc(e))
if os.path.exists('carbon.png'):
os.remove('carbon.png')
@Client.on_message(filters.command('ccgen', prefix) & filters.me)
async def ccgen(_, message: Message):
if len(message.command) > 1:
bins = message.text.split(maxsplit=1)[1]
else:
await message.edit_text('Code not provided!')
return
await message.edit_text('Processing...')
response = requests.get(url=f'{url}/ccgen?bins={bins}', headers=headers, timeout=10)
if response.status_code != 200:
await message.edit_text('Something went wrong')
return
result = response.json()
cards = result['results'][0]['cards']
cards_str = '\n'.join([f'"{card}"' for card in cards])
bins = result['results'][0]['bin']
await message.edit_text(
f'Bins: <code>{bins}</code>\nTotal: <code>{len(cards)}</code>\nCards: \n<code>{cards_str}</code>'
)
@Client.on_message(filters.command('rayso', prefix) & filters.me)
async def rayso(client: Client, message: Message):
title = 'Untitled'
themes = [
'vercel',
'supabase',
'tailwind',
'clerk',
'mintlify',
'prisma',
'bitmap',
'noir',
'ice',
'sand',
'forest',
'mono',
'breeze',
'candy',
'crimson',
'falcon',
'meadow',
'midnight',
'raindrop',
'sunset',
]
if message.reply_to_message:
text = message.reply_to_message.text
message_id = message.reply_to_message.id
if 2 <= len(message.command) <= 3:
title = message.text.split(maxsplit=2)[1]
theme = message.text.split(maxsplit=2)[2].lower()
if theme not in themes:
theme = 'breeze'
elif len(message.command) > 1:
message_id = message.id
title = message.text.split(maxsplit=3)[1]
theme = message.text.split(maxsplit=3)[2]
if theme not in themes:
theme = 'breeze'
text = message.text.split(maxsplit=3)[3]
else:
await message.edit_text('Query not provided!')
return
await message.edit_text('Processing...')
image_file = await make_rayso(text, title, theme)
if image_file is None:
await message.edit_text('Something went wrong')
return
try:
await client.send_photo(
chat_id=message.chat.id,
photo=image_file,
caption=f'<b>Text:</b> <code>{text}</code>',
reply_to_message_id=message_id,
)
await message.delete()
except MediaCaptionTooLong:
cap = text[:850]
await client.send_photo(
chat_id=message.chat.id,
photo=image_file,
caption=f'<b>Text:</b> <code>{cap}</code>',
reply_to_message_id=message_id,
)
await message.delete()
except Exception as e:
await message.edit_text(format_exc(e))
if os.path.exists(image_file):
os.remove(image_file)
modules_help['safone'] = {
'asq [query]*': 'Asq',
'app [query]*': 'Search for an app on Play Store',
'tsearch [query]*': 'Search Torrent',
'tts [character]* [text/reply to text]*': 'Convert Text to Speech',
'sgemini [prompt]*': 'Gemini Model through safone api',
'carbon [code/file/reply]': 'Create beautiful image with your code',
'ccgen [bins]*': 'Generate credit cards',
'rayso [title]* [theme]* [text/reply to text]*': 'Create beautiful image with your text',
}
+504
View File
@@ -0,0 +1,504 @@
#  Moon-Userbot - telegram userbot
#  Copyright (C) 2020-present Moon Userbot Organization
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
import asyncio
import contextlib
import os
import requests
from modules.url import generate_screenshot
from pyrogram import Client, enums, filters
from pyrogram.types import Message
from utils.misc import modules_help, prefix
from utils.scripts import no_prefix
np = no_prefix(prefix)
# API URLs
BASE_URL = 'https://deliriussapi-oficial.vercel.app'
URL = f'{BASE_URL}/ia'
GOOGLE_SEARCH_URL = f'{BASE_URL}/search/googlesearch?query='
YOUTUBE_SEARCH_URL = f'{BASE_URL}/search/ytsearch?q='
MOVIE_SEARCH_URL = f'{BASE_URL}/search/movie?query='
APK_SEARCH_URL = 'https://bk9.fun/search/apkfab?q='
APK_DOWNLOAD_URL = 'https://bk9.fun/download/apkfab?url='
def clean_data(data):
parts = data.split('$@$')
if len(parts) > 1:
return parts[-1]
else:
return data
# Store search results temporarily
search_results = {}
# Helper Functions
def format_google_results(results):
results = results[:15]
return results, '\n\n'.join(
[f'{i + 1}. **[{item["title"]}]({item["url"]})**\n{item["description"]}' for i, item in enumerate(results)]
)
def format_youtube_results(results):
results = results[:15]
return results, '\n\n'.join(
[
f'{i + 1}. **[{item["title"]}]({item["url"]})**\nPublished by: [{item["author"]["name"]}]({item["author"]["url"]}) - {item["views"]} views - {item["duration"]}'
for i, item in enumerate(results)
]
)
def format_movie_results(results):
results = results[:15]
return results, '\n\n'.join(
[
f'{i + 1}. **{item["title"]}** ({item["release_date"]})\nRating: {item["vote_average"]}/10\nVotes: {item["vote_count"]}'
for i, item in enumerate(results)
]
)
def format_apk_results(results):
results = results[:15]
return results, '\n\n'.join([f'{i + 1}. [{item["title"]}]({item["link"]})' for i, item in enumerate(results)])
async def send_screenshot(client, message, url):
screenshot_data = generate_screenshot(url)
if screenshot_data:
await client.send_photo(
message.chat.id,
screenshot_data,
caption=f'Screenshot of <code>{url}</code>',
reply_to_message_id=message.id,
)
else:
await message.reply('Failed to take screenshot.')
async def delete_search_data(client, chat_id, message_id):
await asyncio.sleep(60)
for key in ['google', 'youtube', 'movie', 'apk']:
search_key = f'{chat_id}_{key}'
if search_key in search_results and search_results[search_key]['message_id'] == message_id:
del search_results[search_key]
with contextlib.suppress(BaseException):
await client.delete_messages(chat_id, message_id)
break
def format_spotify_result(data):
result = ''
for item in data[:15]: # Limit to 15 results
result += f'🎵 **{item["title"]}** by {item["artist"]}\n'
result += f'Album: {item["album"]}\n'
result += f'Duration: {item["duration"]}\n'
result += f'Popularity: {item["popularity"]}\n'
result += f'Publish Date: {item["publish"]}\n'
result += f'[Listen on Spotify]({item["url"]})\n\n'
return result
def format_lyrics_result(data):
return f'🎵 **{data["fullTitle"]}** by {data["artist"]}\n\n{data["lyrics"]}'
def format_soundcloud_result(data):
result = ''
for item in data[:15]: # Limit to 15 results
result += f'🎵 **{item["title"]}**\n'
result += f'Genre: {item["genre"]}\n'
result += f'Duration: {item["duration"] // 1000 // 60}:{item["duration"] // 1000 % 60}\n'
result += f'Likes: {item["likes"]}\n'
result += f'Plays: {item["play"]}\n'
result += f'[Listen on SoundCloud]({item["link"]})\n\n'
return result
def format_deezer_result(data):
result = ''
for item in data[:15]: # Limit to 15 results
result += f'🎵 **{item["title"]}** by {item["artist"]}\n'
result += f'Duration: {item["duration"]}\n'
result += f'Rank: {item["rank"]}\n'
result += f'[Listen on Deezer]({item["url"]})\n\n'
return result
def format_apple_music_result(data):
result = ''
for item in data[:15]: # Limit to 15 results
title = item.get('title', 'Unknown Title')
artists = item.get('artists', 'Unknown Artist')
music_type = item.get('type', 'Unknown Type')
url = item.get('url', '#')
result += f'🎵 **{title}** by {artists}\n'
result += f'Type: {music_type}\n'
result += f'[Listen on Apple Music]({url})\n\n'
return result
async def search_music(api_url, format_function, message, query):
await message.edit('Searching...')
url = f'{api_url}{query}&limit=10'
response = requests.get(url, timeout=10)
if response.status_code == 200:
try:
data = response.json() # Directly get the JSON data
if isinstance(data, list):
result = format_function(data)
elif isinstance(data, dict) and 'data' in data:
result = format_function(data['data'])
else:
result = 'No data found or unexpected format.'
await message.edit(result, parse_mode=enums.ParseMode.MARKDOWN)
except (ValueError, KeyError, TypeError) as e:
await message.edit(f'An error occurred while processing the data: {str(e)}')
elif response.json()['msg']:
await message.edit(response.json()['msg'])
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['gsearch'], prefix) & filters.me)
async def google_search(client: Client, message: Message):
if message.reply_to_message:
query = message.reply_to_message.text.strip()
elif len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
return await message.edit_text(f'{prefix}gsearch <query/reply to query>')
await message.edit('Searching...')
url = f'{GOOGLE_SEARCH_URL}{query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
results, formatted_results = format_google_results(data['data'])
search_message = await message.edit(
f'**Google Search Results for:** `{query}`\n\n{formatted_results}',
parse_mode=enums.ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
search_key = f'{message.chat.id}_google'
global search_results
search_results[search_key] = {
'results': results,
'message_id': search_message.id,
}
google_url = f'https://www.google.com/search?q={query}'
await send_screenshot(client, message, google_url)
asyncio.create_task(delete_search_data(client, message.chat.id, search_message.id))
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['ytsearch'], prefix) & filters.me)
async def youtube_search(client: Client, message: Message):
if message.reply_to_message:
query = message.reply_to_message.text.strip()
elif len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
return await message.edit_text(f'{prefix}ytsearch <query/reply to query>')
await message.edit('Searching...')
url = f'{YOUTUBE_SEARCH_URL}{query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
results, formatted_results = format_youtube_results(data['data'])
search_message = await message.edit(
f'**YouTube Search Results for:** `{query}`\n\n{formatted_results}',
parse_mode=enums.ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
search_key = f'{message.chat.id}_youtube'
global search_results
search_results[search_key] = {
'results': results,
'message_id': search_message.id,
}
youtube_url = f'https://www.youtube.com/results?search_query={query}'
await send_screenshot(client, message, youtube_url)
asyncio.create_task(delete_search_data(client, message.chat.id, search_message.id))
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['moviesearch'], prefix) & filters.me)
async def movie_search(client, message: Message):
if message.reply_to_message:
query = message.reply_to_message.text.strip()
elif len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
return await message.edit_text(f'{prefix}moviesearch <query/reply to query>')
await message.edit('Searching...')
url = f'{MOVIE_SEARCH_URL}{query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
results, formatted_results = format_movie_results(data['data'])
# Split the message into multiple parts if it's too long
parts = []
part_length = 4096 # Telegram's message length limit
for i in range(0, len(formatted_results), part_length):
parts.append(formatted_results[i : i + part_length])
for part in parts:
search_message = await message.reply(
f'**Movie Search Results for:** `{query}`\n\n{part}',
parse_mode=enums.ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
search_key = f'{message.chat.id}_movie'
global search_results
search_results[search_key] = {
'results': results,
'message_id': search_message.id,
}
asyncio.create_task(delete_search_data(client, message.chat.id, search_message.id))
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['apksearch'], prefix) & filters.me)
async def apk_search(client, message: Message):
if message.reply_to_message:
query = message.reply_to_message.text.strip()
elif len(message.command) > 1:
query = message.text.split(maxsplit=1)[1]
else:
return await message.edit_text(f'{prefix}apksearch <query/reply to query>')
await message.edit('Searching...')
url = f'{APK_SEARCH_URL}{query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
results, formatted_results = format_apk_results(data['BK9'])
search_message = await message.edit(
f'**APK Search Results for:** `{query}`\n\n{formatted_results}',
parse_mode=enums.ParseMode.MARKDOWN,
disable_web_page_preview=True,
)
search_key = f'{message.chat.id}_apk'
global search_results
search_results[search_key] = {
'results': results,
'message_id': search_message.id,
}
asyncio.create_task(delete_search_data(client, message.chat.id, search_message.id))
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['wgpt', 'gptweb'], prefix) & filters.me)
async def gptweb(_, message: Message):
if len(message.command) < 2:
await message.edit('Usage: `wgpt <query>`')
return
await message.edit('Thinking...')
query = ' '.join(message.command[1:])
url = f'{URL}/gptweb?text={query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
await message.edit(
f'**Question:**\n{query}\n**Answer:**\n{data["data"]}',
parse_mode=enums.ParseMode.MARKDOWN,
)
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['wgemini'], prefix) & filters.me)
async def gemini(_, message: Message):
if len(message.command) < 2:
await message.edit('Usage: `wgemini <query>`')
return
await message.edit('Thinking...')
query = ' '.join(message.command[1:])
url = f'{URL}/gemini?query={query}'
response = requests.get(url, timeout=10)
if response.status_code == 200:
data = response.json()
await message.edit(
f'**Question:**\n{query}\n**Answer:**\n{data["message"]}',
parse_mode=enums.ParseMode.MARKDOWN,
)
else:
await message.edit('An error occurred, please try again later.')
@Client.on_message(filters.command(['sputify'], prefix) & filters.me)
async def spotify_search(_, message: Message):
query = message.text.split(maxsplit=1)[1] if len(message.command) > 1 else message.reply_to_message.text
if not query:
await message.edit('Usage: spotify <query>')
return
await search_music(f'{BASE_URL}/search/spotify?q=', format_spotify_result, message, query)
@Client.on_message(filters.command(['lyrics'], prefix) & filters.me)
async def lyrics_search(_, message: Message):
query = message.text.split(maxsplit=1)[1] if len(message.command) > 1 else message.reply_to_message.text
if not query:
await message.edit('Usage: lyrics <song name>')
return
await search_music(f'{BASE_URL}/search/letra?query=', format_lyrics_result, message, query)
@Client.on_message(filters.command(['soundcloud'], prefix) & filters.me)
async def soundcloud_search(_, message: Message):
query = message.text.split(maxsplit=1)[1] if len(message.command) > 1 else message.reply_to_message.text
if not query:
await message.edit('Usage: soundcloud <query>')
return
await search_music(f'{BASE_URL}/search/soundcloud?q=', format_soundcloud_result, message, query)
@Client.on_message(filters.command(['deezer'], prefix) & filters.me)
async def deezer_search(_, message: Message):
query = message.text.split(maxsplit=1)[1] if len(message.command) > 1 else message.reply_to_message.text
if not query:
await message.edit('Usage: deezer <query>')
return
await search_music(f'{BASE_URL}/search/deezer?q=', format_deezer_result, message, query)
@Client.on_message(filters.command(['applemusic'], prefix) & filters.me)
async def applemusic_search(_, message: Message):
query = message.text.split(maxsplit=1)[1] if len(message.command) > 1 else message.reply_to_message.text
if not query:
await message.edit('Usage: applemusic <query>')
return
await search_music(f'{BASE_URL}/search/applemusic?text=', format_apple_music_result, message, query)
@Client.on_message(filters.reply & filters.text & filters.me & np)
async def handle_reply(client: Client, message: Message):
chat_id = message.chat.id
search_keys = [
f'{chat_id}_google',
f'{chat_id}_youtube',
f'{chat_id}_movie',
f'{chat_id}_apk',
]
for search_key in search_keys:
if search_key in search_results:
try:
# Check if the replied-to message is one of the bot's search result messages
if message.reply_to_message.from_user.id != (await client.get_me()).id:
return
index = int(message.text.strip()) - 1
results = search_results[search_key]['results']
search_message_id = search_results[search_key]['message_id']
if message.reply_to_message.id == search_message_id and 0 <= index < len(results):
await message.edit('Please wait...')
if search_key.endswith('_movie'):
# Send movie details with image
movie = results[index]
caption = (
f'**{movie["title"]}** ({movie["release_date"]})\n'
f'Original Title: {movie["original_title"]}\n'
f'Language: {movie["original_language"]}\n'
f'Overview: {movie["overview"]}\n'
f'Popularity: {movie["popularity"]}\n'
f'Rating: {movie["vote_average"]}/10\n'
f'Votes: {movie["vote_count"]}'
)
if 'image' in movie:
response = requests.get(movie['image'], timeout=10)
if response.status_code == 200:
with open('movie_image.jpg', 'wb') as f:
f.write(response.content)
await message.reply_photo(
photo='movie_image.jpg',
caption=caption,
parse_mode=enums.ParseMode.MARKDOWN,
)
os.remove('movie_image.jpg')
else:
await message.reply(caption, parse_mode=enums.ParseMode.MARKDOWN)
else:
await message.reply(caption, parse_mode=enums.ParseMode.MARKDOWN)
elif search_key.endswith('_apk'):
apk_url = f'{APK_DOWNLOAD_URL}{results[index]["link"]}'
fetch_apk_url = requests.get(apk_url, timeout=10)
if fetch_apk_url.status_code != 200:
await message.edit('Failed to fetch APK data.')
else:
data_apk = fetch_apk_url.json()
download_url = data_apk['BK9']['link']
size_apk = data_apk['BK9']['size']
apk_size = float(size_apk.split(' ')[0])
if 'GB' in size_apk or apk_size > 100:
await message.edit('File size is too large to download.')
else:
apk_file_name = f'{data_apk["BK9"]["title"]}.apk'
response = requests.get(download_url, timeout=10)
if response.status_code != 200:
await message.edit('Failed to download the APK file.')
else:
with open(apk_file_name, 'wb') as f:
f.write(response.content)
await message.reply_document(apk_file_name)
os.remove(apk_file_name)
else:
url = results[index]['url']
await send_screenshot(client, message, url)
await message.delete()
return
except ValueError:
pass
modules_help['sarethai'] = {
'wgpt [query]*': 'Ask anything to GPT-Web',
'gptweb [query]*': 'Ask anything to GPT-Web',
'wgemini [query]*': 'Ask anything to Gemini',
'sputify [query]*': 'Search for songs on Spotify',
'lyrics [song name]*': 'Get the lyrics of a song',
'soundcloud [query]*': 'Search for songs on SoundCloud',
'deezer [query]*': 'Search for songs on Deezer',
'applemusic [query]*': 'Search for songs on Apple Music',
'gsearch [query]*': 'Searches Google for the query.',
'ytsearch [query]*': 'Searches YouTube for the query.',
'moviesearch [query]*': 'Searches movies for the query and returns results.',
'apksearch [query]*': 'Searches APKs for the query and returns results.',
}
+70
View File
@@ -0,0 +1,70 @@
from asyncio import sleep
from pyrogram import Client, enums, filters
from pyrogram.types import Message
# noinspection PyUnresolvedReferences
from utils.misc import modules_help, prefix
# noinspection PyUnresolvedReferences
from utils.scripts import format_exc
now = {}
@Client.on_message(filters.command(['search'], prefix) & filters.me)
async def search_cmd(client: Client, message: Message):
if now.get(message.chat.id):
return await message.edit(
f'<b>You already have a search in progress!\nType: <code>{prefix}scancel</code> to cancel it.</b>',
parse_mode=enums.ParseMode.HTML,
)
await message.edit('<b>Start searching...</b>', parse_mode=enums.ParseMode.HTML)
finished = False
local = False
try:
cmd = message.command[1]
word = message.command[2].lower()
timeout = float(message.command[3]) if len(message.command) > 3 else 2
except Exception:
return await message.edit(
f'<b>Usage:</b> <code>{prefix}search [/cmd]* [search_word]* [timeout=2.0]</code>',
parse_mode=enums.ParseMode.HTML,
)
now[message.chat.id] = True
try:
await message.reply_text(quote=False, text=cmd, reply_to_message_id=None)
while not finished and now[message.chat.id]:
async for msg in client.get_chat_history(message.chat.id, limit=2):
if msg.from_user.id == message.from_user.id:
continue
elif word in msg.text.lower():
finished = True
local = True
if not local:
await sleep(timeout)
await message.reply_text(quote=False, text=cmd, reply_to_message_id=None)
else:
break
if now[message.chat.id]:
await message.reply_text('<b>Search finished!</b>', parse_mode=enums.ParseMode.HTML)
except Exception as ex:
await message.edit(format_exc(ex), parse_mode=enums.ParseMode.HTML)
now[message.chat.id] = False
@Client.on_message(filters.command(['scancel'], prefix) & filters.me)
async def scancel_cmd(_: Client, message: Message):
if not now.get(message.chat.id):
return await message.edit('<b>There is no search in progress!</b>', parse_mode=enums.ParseMode.HTML)
now[message.chat.id] = False
await message.edit('<b>Search cancelled!</b>', parse_mode=enums.ParseMode.HTML)
modules_help['search'] = {
'search [/cmd]* [search_word]* [timeout=2.0]': 'Search for a specific word in bot (while)',
'scancel': 'Cancel current search',
}
+58
View File
@@ -0,0 +1,58 @@
# copyright by https/t.me/shado_hackers
from pyrogram import Client, filters
from pyrogram.types import Message
from utils.misc import modules_help, prefix
from utils.scripts import format_exc, import_library
import_library('lxml_html_clean')
import_library('newspaper', 'newspaper3k')
nltk = import_library('nltk')
from newspaper import Article # noqa: E402
from newspaper.article import ArticleException # noqa: E402
nltk.download('all')
@Client.on_message(filters.command('summary', prefix) & filters.me)
async def summarize_article(_, message: Message):
"""
Summarize an article from a given URL.
Args:
client (Client): Pyrogram client instance.
message (Message): Incoming message.
Returns:
None
"""
# Extract the URL from the message text (removing the command part)
url = message.text.split(maxsplit=1)[1] if len(message.text.split()) > 1 else None
if not url:
await message.edit_text('Please provide a valid URL after the command.')
return
try:
# Extract and summarize the article
article = Article(url)
article.download()
article.parse()
article.nlp() # Uses NLP to analyze the article
response = f"""
<b>Article Summary</b>
<b>Title:</b> <code>{article.title}</code>
<b>Authors:</b> <code>{', '.join(article.authors) if article.authors else 'N/A'}</code>
<b>Summary:</b>
<pre>{article.summary}</pre>
"""
await message.edit_text(response)
except ArticleException:
return await message.edit_text('Unable to extract information from the provided URL.')
except Exception as e:
return await message.edit_text(f'An error occurred: {format_exc(e)}')
modules_help['summary'] = {'summary [url]': 'Reply with article links, getting summary of articles'}
+46
View File
@@ -0,0 +1,46 @@
# Moon-Userbot - telegram userbot
# Copyright (C) 2020-present Moon Userbot Organization
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from pyrogram import Client, enums, filters
from pyrogram.types import Message
from utils.misc import modules_help, prefix
ru_keys = """ёйцукенгшщзхъфывапролджэячсмитьбю.Ё"№;%:?ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭ/ЯЧСМИТЬБЮ,"""
en_keys = """`qwertyuiop[]asdfghjkl;'zxcvbnm,./~@#$%^&QWERTYUIOP{}ASDFGHJKL:"|ZXCVBNM<>?"""
table = str.maketrans(ru_keys + en_keys, en_keys + ru_keys)
@Client.on_message(filters.command(['switch', 'sw'], prefix) & filters.me)
async def switch(client: Client, message: Message):
if len(message.command) == 1:
if message.reply_to_message:
text = message.reply_to_message.text
else:
history = await client.get_history(message.chat.id, limit=2)
if history and history[1].from_user.is_self and history[1].text:
text = history[1].text
else:
await message.edit('<b>Text to switch not found</b>', parse_mode=enums.ParseMode.HTML)
return
else:
text = message.text.split(maxsplit=1)[1]
await message.edit(str.translate(text, table), parse_mode=enums.ParseMode.HTML)
modules_help['switch'] = {
'sw [reply/text for switch]*': 'Useful when you forgot to change the keyboard layout[RU]',
}
+94
View File
@@ -0,0 +1,94 @@
import os
import time
import requests
from pyrogram import Client, enums, filters
from pyrogram.errors import MessageTooLong
from pyrogram.types import Message
from utils.misc import modules_help, prefix
# Replace with your Gladia API key
gladia_key = 'your key '
gladia_url = 'https://api.gladia.io/v2/transcription/'
# Function to make fetch requests to the Gladia API
def make_fetch_request(url, headers, method='GET', data=None):
if method == 'POST':
response = requests.post(url, headers=headers, json=data, timeout=10)
else:
response = requests.get(url, headers=headers, timeout=10)
return response.json()
@Client.on_message(filters.command('transcribeyt', prefix) & filters.me)
async def transcribe_audio(_, message: Message):
"""
Transcribe an audio URL using the Gladia API.
Usage: .transcribe <audio_url>
"""
audio_url = message.text.split(maxsplit=1)[1] if len(message.text.split()) > 1 else None
# Check if a valid URL was provided
if not audio_url:
await message.reply('Please provide a valid audio URL.')
return
headers = {'x-gladia-key': gladia_key, 'Content-Type': 'application/json'}
request_data = {'audio_url': audio_url}
# Send initial request to Gladia API
status_message = await message.reply('- Sending initial request to Gladia API...')
initial_response = make_fetch_request(gladia_url, headers, 'POST', request_data)
# Check if the response contains the result_url
if 'result_url' not in initial_response:
await status_message.edit(f'Error in transcription request: {initial_response}')
return
result_url = initial_response['result_url']
await status_message.edit('Initial request sent. Polling for transcription results...')
# Polling for transcription result
while True:
poll_response = make_fetch_request(result_url, headers)
if poll_response.get('status') == 'done':
transcription = poll_response.get('result', {}).get('transcription', {}).get('full_transcript')
if transcription:
# Format the transcription result with HTML
result_html = f"""
<u><b>Transcription Result</b></u>
<br>
<pre>{transcription}</pre>
"""
try:
# Attempt to send transcription as a message
await message.reply_text(result_html, parse_mode=enums.ParseMode.HTML)
except MessageTooLong:
# Save the large response to a file
with open('transcription.txt', 'w') as f:
f.write(result_html)
# Read general details to include in the caption
general_details = "Here's the transcription result."
# Send the file with a caption
await message.reply_document(
'transcription.txt',
caption=f'<u><b>General Details</b></u>:\n{general_details}',
)
# Clean up by removing the file
os.remove('transcription.html')
else:
await status_message.edit('Transcription completed, but no transcript was found.')
break
else:
await status_message.edit(f'Transcription status: {poll_response.get("status")}')
time.sleep(30) # Wait for a few seconds before polling again
modules_help['transcribeyt'] = {'transcribeyt [yt video url]': 'Reply with a YT video link to get transcribed '}