Initial commit
This commit is contained in:
commit
f578c30ccc
|
@ -0,0 +1,3 @@
|
|||
__pycache__/
|
||||
sessions/
|
||||
config.yaml
|
|
@ -0,0 +1,16 @@
|
|||
telegram:
|
||||
api_id: 0
|
||||
api_hash: https://my.telegram.org
|
||||
slave_bot_token: https://t.me/BotFather
|
||||
config:
|
||||
prefixes:
|
||||
- .
|
||||
sessions:
|
||||
- blankie
|
||||
- knees
|
||||
- nezuko
|
||||
log_chat: -1001278205033
|
||||
spamwatch_api: https://t.me/SpamWatchBot
|
||||
log_user_joins: false
|
||||
log_user_adds: true
|
||||
log_reports: true
|
|
@ -0,0 +1,5 @@
|
|||
pyrogram
|
||||
tgcrypto
|
||||
requests
|
||||
aiohttp
|
||||
PyYAML
|
|
@ -0,0 +1,183 @@
|
|||
import os
|
||||
import html
|
||||
import time
|
||||
import logging
|
||||
import asyncio
|
||||
import traceback
|
||||
import functools
|
||||
import yaml
|
||||
import aiohttp
|
||||
from datetime import timedelta
|
||||
from pyrogram import Client, StopPropagation, ContinuePropagation
|
||||
from pyrogram.types import Chat, User
|
||||
from pyrogram.parser import parser
|
||||
from pyrogram.errors.exceptions.bad_request_400 import PeerIdInvalid, ChannelInvalid
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
with open('config.yaml') as config:
|
||||
config = yaml.safe_load(config)
|
||||
loop = asyncio.get_event_loop()
|
||||
help_dict = dict()
|
||||
|
||||
apps = []
|
||||
app_user_ids = dict()
|
||||
# this code here exists because i can't be fucked
|
||||
class Parser(parser.Parser):
|
||||
async def parse(self, text, mode):
|
||||
if mode == 'through':
|
||||
return text
|
||||
return await super().parse(text, mode)
|
||||
for session_name in config['config']['sessions']:
|
||||
app = Client(session_name, api_id=config['telegram']['api_id'], api_hash=config['telegram']['api_hash'], plugins={'root': os.path.join(__package__, 'plugins')}, parse_mode='html', workdir='sessions')
|
||||
app.parser = Parser(app)
|
||||
apps.append(app)
|
||||
slave = Client('sukuinote-slave', api_id=config['telegram']['api_id'], api_hash=config['telegram']['api_hash'], plugins={'root': os.path.join(__package__, 'slave-plugins')}, parse_mode='html', bot_token=config['telegram']['slave_bot_token'], workdir='sessions')
|
||||
slave.parser = Parser(slave)
|
||||
session = aiohttp.ClientSession()
|
||||
|
||||
async def get_entity(client, entity):
|
||||
entity_client = client
|
||||
if not isinstance(entity, Chat):
|
||||
try:
|
||||
entity = int(entity)
|
||||
except ValueError:
|
||||
pass
|
||||
except TypeError:
|
||||
entity = entity.id
|
||||
try:
|
||||
entity = await client.get_chat(entity)
|
||||
except (PeerIdInvalid, ChannelInvalid):
|
||||
for app in apps:
|
||||
if app != client:
|
||||
try:
|
||||
entity = await app.get_chat(entity)
|
||||
except (PeerIdInvalid, ChannelInvalid):
|
||||
pass
|
||||
else:
|
||||
entity_client = app
|
||||
break
|
||||
else:
|
||||
entity = await slave.get_chat(entity)
|
||||
entity_client = slave
|
||||
return entity, entity_client
|
||||
|
||||
async def get_user(client, entity):
|
||||
entity_client = client
|
||||
if not isinstance(entity, User):
|
||||
try:
|
||||
entity = int(entity)
|
||||
except ValueError:
|
||||
pass
|
||||
except TypeError:
|
||||
entity = entity.id
|
||||
try:
|
||||
entity = await client.get_users(entity)
|
||||
except PeerIdInvalid:
|
||||
for app in apps:
|
||||
if app != client:
|
||||
try:
|
||||
entity = await app.get_users(entity)
|
||||
except PeerIdInvalid:
|
||||
pass
|
||||
else:
|
||||
entity_client = app
|
||||
break
|
||||
else:
|
||||
entity = await slave.get_users(entity)
|
||||
entity_client = slave
|
||||
return entity, entity_client
|
||||
|
||||
def log_errors(func):
|
||||
@functools.wraps(func)
|
||||
async def wrapper(client, *args):
|
||||
try:
|
||||
await func(client, *args)
|
||||
except (StopPropagation, ContinuePropagation):
|
||||
raise
|
||||
except Exception:
|
||||
tb = traceback.format_exc()
|
||||
try:
|
||||
await slave.send_message(config['config']['log_chat'], f'Exception occured in {func.__name__}\n\n{tb}', parse_mode=None)
|
||||
except Exception:
|
||||
logging.exception('Failed to log exception for %s as slave', func.__name__)
|
||||
tb = traceback.format_exc()
|
||||
for app in apps:
|
||||
try:
|
||||
await app.send_message(config['config']['log_chat'], f'Exception occured in {func.__name__}\n\n{tb}', parse_mode=None)
|
||||
except Exception:
|
||||
logging.exception('Failed to log exception for %s as app', func.__name__)
|
||||
tb = traceback.format_exc()
|
||||
else:
|
||||
break
|
||||
raise
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
def public_log_errors(func):
|
||||
@functools.wraps(func)
|
||||
async def wrapper(client, message):
|
||||
try:
|
||||
await func(client, message)
|
||||
except (StopPropagation, ContinuePropagation):
|
||||
raise
|
||||
except Exception:
|
||||
await message.reply_text(traceback.format_exc(), parse_mode=None)
|
||||
raise
|
||||
return wrapper
|
||||
|
||||
# https://stackoverflow.com/a/49361727
|
||||
def format_bytes(size):
|
||||
size = int(size)
|
||||
# 2**10 = 1024
|
||||
power = 1000
|
||||
n = 0
|
||||
power_labels = {0 : '', 1: 'K', 2: 'M', 3: 'G', 4: 'T'}
|
||||
while size > power:
|
||||
size /= power
|
||||
n += 1
|
||||
return f"{size:.2f} {power_labels[n]+'B'}"
|
||||
|
||||
# https://stackoverflow.com/a/34325723
|
||||
def return_progress_string(current, total):
|
||||
filled_length = int(30 * current // total)
|
||||
return '[' + '=' * filled_length + ' ' * (30 - filled_length) + ']'
|
||||
|
||||
# https://stackoverflow.com/a/852718
|
||||
# https://stackoverflow.com/a/775095
|
||||
def calculate_eta(current, total, start_time):
|
||||
if not current:
|
||||
return '00:00:00'
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
seconds = (elapsed_time * (total / current)) - elapsed_time
|
||||
thing = ''.join(str(timedelta(seconds=seconds)).split('.')[:-1]).split(', ')
|
||||
thing[-1] = thing[-1].rjust(8, '0')
|
||||
return ', '.join(thing)
|
||||
|
||||
progress_callback_data = dict()
|
||||
async def progress_callback(current, total, reply, text, upload):
|
||||
message_identifier = (reply.chat.id, reply.message_id)
|
||||
last_edit_time, prevtext, start_time = progress_callback_data.get(message_identifier, (0, None, time.time()))
|
||||
if current == total:
|
||||
try:
|
||||
progress_callback_data.pop(message_identifier)
|
||||
except KeyError:
|
||||
pass
|
||||
elif (time.time() - last_edit_time) > 1:
|
||||
handle = 'Upload' if upload else 'Download'
|
||||
if last_edit_time:
|
||||
speed = format_bytes((total - current) / (time.time() - start_time))
|
||||
else:
|
||||
speed = '0 B'
|
||||
text = f'''{text}
|
||||
<code>{return_progress_string(current, total)}</code>
|
||||
|
||||
<b>Total Size:</b> {format_bytes(total)}
|
||||
<b>{handle}ed Size:</b> {format_bytes(current)}
|
||||
<b>{handle} Speed:</b> {speed}/s
|
||||
<b>ETA:</b> {calculate_eta(current, total, start_time)}'''
|
||||
if prevtext != text:
|
||||
await reply.edit_text(text)
|
||||
prevtext = text
|
||||
last_edit_time = time.time()
|
||||
progress_callback_data[message_identifier] = last_edit_time, prevtext, start_time
|
|
@ -0,0 +1,22 @@
|
|||
import asyncio
|
||||
from pyrogram import idle
|
||||
from . import loop, apps, slave, app_user_ids, session
|
||||
|
||||
async def main():
|
||||
async def _start_app(app):
|
||||
await app.start()
|
||||
asyncio.create_task(_get_me_loop(app))
|
||||
async def _get_me_loop(app):
|
||||
while True:
|
||||
try:
|
||||
me = await app.get_me()
|
||||
app_user_ids[me.id] = me
|
||||
except:
|
||||
pass
|
||||
await asyncio.sleep(60)
|
||||
await asyncio.gather(*(_start_app(app) for app in apps), slave.start())
|
||||
await idle()
|
||||
await asyncio.gather(*(app.stop() for app in apps), slave.stop())
|
||||
await session.close()
|
||||
|
||||
loop.run_until_complete(main())
|
|
@ -0,0 +1,54 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors, get_entity
|
||||
|
||||
ZWS = '\u200B'
|
||||
def _generate_sexy(entity, ping):
|
||||
text = entity.first_name
|
||||
if entity.last_name:
|
||||
text += f' {entity.last_name}'
|
||||
sexy_text = '<code>[DELETED]</code>' if entity.is_deleted else html.escape(text or 'Empty???')
|
||||
if not entity.is_deleted:
|
||||
if ping:
|
||||
sexy_text = f'<a href="tg://user?id={entity.id}">{sexy_text}</a>'
|
||||
elif entity.username:
|
||||
sexy_text = f'<a href="https://t.me/{entity.username}">{sexy_text}</a>'
|
||||
elif not ping:
|
||||
sexy_text = sexy_text.replace('@', f'@{ZWS}')
|
||||
if entity.is_bot:
|
||||
sexy_text += ' <code>[BOT]</code>'
|
||||
if entity.is_verified:
|
||||
sexy_text += ' <code>[VERIFIED]</code>'
|
||||
if entity.is_support:
|
||||
sexy_text += ' <code>[SUPPORT]</code>'
|
||||
if entity.is_scam:
|
||||
sexy_text += ' <code>[SCAM]</code>'
|
||||
return sexy_text
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['admin', 'admins'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def admins(client, message):
|
||||
chat, entity_client = message.chat, client
|
||||
command = message.command
|
||||
command.pop(0)
|
||||
if command:
|
||||
chat = ' '.join(command)
|
||||
try:
|
||||
chat = int(chat)
|
||||
except ValueError:
|
||||
pass
|
||||
chat, entity_client = await get_entity(client, chat)
|
||||
text_unping = text_ping = ''
|
||||
async for i in entity_client.iter_chat_members(chat.id, filter='administrators'):
|
||||
text_unping += f'\n[<code>{i.user.id}</code>] {_generate_sexy(i.user, False)}'
|
||||
text_ping += f'\n[<code>{i.user.id}</code>] {_generate_sexy(i.user, True)}'
|
||||
if i.title:
|
||||
text_unping += f' // {html.escape(i.title.replace("@", "@" + ZWS))}'
|
||||
text_ping += f' // {html.escape(i.title)}'
|
||||
reply = await message.reply_text(text_unping, disable_web_page_preview=True)
|
||||
await reply.edit_text(text_ping, disable_web_page_preview=True)
|
||||
|
||||
help_dict['admins'] = ('Admins',
|
||||
'''{prefix}admins <i>[chat]</i> - Lists the admins in <i>[chat]</i>
|
||||
Aliases: {prefix}admin''')
|
|
@ -0,0 +1,48 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types.messages_and_media import Photo
|
||||
from pyrogram.errors.exceptions.forbidden_403 import Forbidden
|
||||
from .. import slave, config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['anilist', 'al', 'alc', 'alchar', 'alcharacter', 'anilistc', 'anilistchar', 'anilistcharacter'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def anilist(client, message):
|
||||
bot = await slave.get_me()
|
||||
query = message.command
|
||||
page = 1
|
||||
character = 'c' in query.pop(0)
|
||||
if query and query[0].isnumeric():
|
||||
page = int(query.pop(0))
|
||||
page -= 1
|
||||
if page < 0:
|
||||
page = 0
|
||||
elif page > 9:
|
||||
page = 9
|
||||
query = ' '.join(query)
|
||||
if not query:
|
||||
return
|
||||
results = await client.get_inline_bot_results(bot.username or bot.id, f'al{"c" if character else ""} ' + query)
|
||||
if not results.results:
|
||||
await message.reply_text('No results')
|
||||
return
|
||||
try:
|
||||
await message.reply_inline_bot_result(results.query_id, results.results[page].id)
|
||||
except IndexError:
|
||||
await message.reply_text(f'There are only {len(results.results)} results')
|
||||
except Forbidden:
|
||||
text = {'message': results.results[page].send_message.message, 'entities': results.results[page].send_message.entities}
|
||||
try:
|
||||
photo = Photo._parse(client, results.results[page].photo)
|
||||
await message.reply_cached_media(photo.file_id, photo.file_ref, caption=text, parse_mode='through')
|
||||
except Forbidden:
|
||||
await message.reply_text(text, disable_web_page_preview=True, parse_mode='through')
|
||||
|
||||
help_dict['anilist'] = ('Anilist',
|
||||
'''{prefix}anilist <i><query></i> - Searches for anime/manga named <i><query></i> on Anilist
|
||||
Aliases: {prefix}al
|
||||
Can also be activated inline with: @{bot} anilist <i><query></i> or @{bot} al <i><query></i>
|
||||
|
||||
{prefix}anilistc <i><query></i> - Searches for characters named <i><query></i> on Anilist
|
||||
Aliases: {prefix}alc, alchar, alcharacter, anilistchar, anilistcharacter
|
||||
Can also be activated inline with: @{bot} anilistc <i><query></i> or @{bot} alc <i><query></i>''')
|
|
@ -0,0 +1,32 @@
|
|||
import html
|
||||
import tempfile
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, session, progress_callback, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command('cat', prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def cat(client, message):
|
||||
media = message.document
|
||||
if not media and not getattr(message.reply_to_message, 'empty', True):
|
||||
media = message.reply_to_message.document
|
||||
if not media:
|
||||
await message.reply_text('Document required')
|
||||
return
|
||||
done = False
|
||||
with tempfile.NamedTemporaryFile() as file:
|
||||
reply = await message.reply_text('Downloading...')
|
||||
await client.download_media(media, file_name=file.name, progress=progress_callback, progress_args=(reply, 'Downloading...', False))
|
||||
with open(file.name) as nfile:
|
||||
while True:
|
||||
chunk = nfile.read(4096)
|
||||
if not chunk:
|
||||
break
|
||||
chunk = f'<code>{html.escape(chunk)}</code>'
|
||||
if done:
|
||||
await message.reply_text(chunk, quote=False)
|
||||
else:
|
||||
await reply.edit_text(chunk)
|
||||
done = True
|
||||
|
||||
help_dict['cat'] = ('cat', '{prefix}cat <i>(as caption of text file or reply)</i> - Outputs file\'s text to Telegram')
|
|
@ -0,0 +1,65 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['d', 'del', 'delete'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def delete(client, message):
|
||||
messages = set((message.message_id,))
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
messages.add(reply.message_id)
|
||||
await client.delete_messages(message.chat.id, messages)
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['p', 'purge', 'sp', 'selfpurge'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def purge(client, message):
|
||||
selfpurge = 's' in message.command[0]
|
||||
ids = set((message.message_id,))
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
for i in await client.get_messages(message.chat.id, range(reply.message_id, message.message_id), replies=0):
|
||||
if selfpurge and not i.outgoing:
|
||||
continue
|
||||
ids.add(i.message_id)
|
||||
await client.delete_messages(message.chat.id, ids)
|
||||
|
||||
yeetpurge_info = {True: dict(), False: dict()}
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['yp', 'yeetpurge', 'syp', 'selfyeetpurge'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def yeetpurge(client, message):
|
||||
reply = message.reply_to_message
|
||||
if getattr(message, 'empty', True):
|
||||
await message.delete()
|
||||
return
|
||||
info = yeetpurge_info['s' in message.command[0]]
|
||||
if message.chat.id not in info:
|
||||
resp = await message.reply_text('Reply to end destination')
|
||||
info[message.chat.id] = (message.message_id, resp.message_id, reply.message_id)
|
||||
return
|
||||
og_message, og_resp, og_reply = info.pop(message.chat.id)
|
||||
messages = set((og_message, og_resp, message.message_id))
|
||||
for i in await client.get_messages(message.chat.id, range(og_reply, reply.message_id + 1), replies=0):
|
||||
if 's' in message.command[0] and not i.outgoing:
|
||||
continue
|
||||
messages.add(i.message_id)
|
||||
await client.delete_messages(message.chat.id, messages)
|
||||
|
||||
help_dict['delete'] = ('Delete',
|
||||
'''{prefix}delete <i>(as reply to a message)</i> - Deletes the replied to message
|
||||
Aliases: {prefix}d, {prefix}del
|
||||
|
||||
{prefix}purge <i>(as reply to a message)</i> - Purges the messages between the one you replied (and including the one you replied)
|
||||
Aliases: {prefix}p
|
||||
|
||||
{prefix}selfpurge <i>(as reply to a message)</i> - {prefix}p but only your messages
|
||||
Aliases: {prefix}sp
|
||||
|
||||
{prefix}yeetpurge <i>(as reply to a message)</i> - Purges messages in between
|
||||
Aliases: {prefix}yp
|
||||
|
||||
{prefix}selfyeetpurge <i>(as reply to a message)</i> - {prefix}yp but only your messages
|
||||
Aliases: {prefix}syp''')
|
|
@ -0,0 +1,209 @@
|
|||
import re
|
||||
import html
|
||||
import asyncio
|
||||
import datetime
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, get_entity, session, log_errors, public_log_errors
|
||||
|
||||
conversation_hack = dict()
|
||||
|
||||
DEAI_BAN_CODES = {
|
||||
"00": "Gban",
|
||||
"01": "Joinspam",
|
||||
"02": "Spambot",
|
||||
"03": "Generic spam",
|
||||
"04": "Scam",
|
||||
"05": "Illegal",
|
||||
"06": "Pornography",
|
||||
"07": "Nonsense",
|
||||
"08": "Chain bans",
|
||||
"09": "Special",
|
||||
"10": "Preemptive",
|
||||
"11": "Copyright",
|
||||
"12": "Admin rights abuse",
|
||||
"13": "Toxicity",
|
||||
"14": "Flood",
|
||||
"15": "Detected but not classified",
|
||||
"16": "Advanced detection",
|
||||
"17": "Reported",
|
||||
"18": "AI association",
|
||||
"19": "Impersonation",
|
||||
"20": "Malware",
|
||||
"21": "Ban evasion",
|
||||
"22": "PM spam",
|
||||
"23": "Spam adding members"
|
||||
}
|
||||
DEAI_MODULE_CODES = {
|
||||
"0": "Gban",
|
||||
"1": "Database parser",
|
||||
"2": "Realtime",
|
||||
"3": "Profiler",
|
||||
"4": "Scraper",
|
||||
"5": "Association analytics",
|
||||
"6": "Codename Autobahn",
|
||||
"7": "Codename Polizei",
|
||||
"8": "Codename Gestapo"
|
||||
}
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['einfo', 'externalinfo', 'sw', 'spamwatch', 'deai', 'sp', 'spamprotection', 'cas', 'combot', 'rose'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def fedstat(client, message):
|
||||
entity = message.from_user
|
||||
args = message.command
|
||||
command = args.pop(0).lower()
|
||||
if args:
|
||||
entity = ' '.join(args)
|
||||
elif not getattr(message.reply_to_message, 'empty', True):
|
||||
entity = message.reply_to_message.from_user or entity
|
||||
if isinstance(entity, str) and (not entity.isnumeric() and not entity.startswith('TEL-')):
|
||||
entity, entity_client = await get_entity(client, entity)
|
||||
if not isinstance(entity, str):
|
||||
entity = str(entity.id)
|
||||
if entity.startswith('TEL-') or int(entity) < 0 or command in ('sp', 'spamprotection'):
|
||||
await message.reply_text(f'Spam Protection:\n{await get_spam_protection(entity)}')
|
||||
elif command in ('sw', 'spamwatch'):
|
||||
await message.reply_text(f'Spamwatch:\n{await get_spamwatch(entity)}')
|
||||
elif command == 'deai':
|
||||
await message.reply_text(f'DEAI:\n{await get_deai(client, entity)}')
|
||||
elif command == 'rose':
|
||||
await message.reply_text(f'Rose Support:\n{await get_rose(client, entity)}')
|
||||
elif command in ('cas', 'combot'):
|
||||
await message.reply_text(f'CAS:\n{await get_cas(entity)}')
|
||||
else:
|
||||
spamwatch, deai, cas, spam_protection, rose = await asyncio.gather(get_spamwatch(entity), get_deai(client, entity), get_cas(entity), get_spam_protection(entity), get_rose(client, entity))
|
||||
await message.reply_text(f'''Spamwatch:
|
||||
{spamwatch}
|
||||
|
||||
CAS:
|
||||
{cas}
|
||||
|
||||
Rose Support:
|
||||
{rose}
|
||||
|
||||
DEAI:
|
||||
{deai}
|
||||
|
||||
Spam Protection:
|
||||
{spam_protection}''')
|
||||
|
||||
async def get_spamwatch(entity):
|
||||
async with session.get(f'https://api.spamwat.ch/banlist/{entity}', headers={'Authorization': f'Bearer {config["config"]["spamwatch_api"]}'}) as resp:
|
||||
json = await resp.json()
|
||||
if 'code' in json:
|
||||
return f'- <b>{json["code"]}:</b> {html.escape(json.get("error", ""))}'
|
||||
return f'''- <b>Banned on:</b> {str(datetime.datetime.fromtimestamp(json["date"]))}
|
||||
- <b>Reason:</b> {html.escape(json["reason"].strip())}'''
|
||||
|
||||
async def get_rose(client, entity):
|
||||
new_message = await client.send_message('missrose_bot', f'/fbanstat {entity} 86718661-6bfc-4bd0-9447-7c419eb08e69')
|
||||
identifier = (new_message.chat.id, new_message.message_id)
|
||||
conversation_hack[identifier] = None
|
||||
while not conversation_hack[identifier]:
|
||||
await asyncio.sleep(0.5)
|
||||
ntext = conversation_hack[identifier].split('\n')
|
||||
ntext.pop(0)
|
||||
if ntext:
|
||||
date = '-'.join(ntext.pop().split(' ')[-1].split('/')[::-1])
|
||||
reason = '\n'.join(ntext).strip()
|
||||
text = f'- <b>Banned on:</b> {date}'
|
||||
if reason:
|
||||
text += f'\n- <b>Reason:</b> {html.escape(reason)}'
|
||||
return text
|
||||
return '- <b>404:</b> Not Found'
|
||||
|
||||
async def get_deai(client, entity):
|
||||
new_message = await client.send_message('rsophiebot', f'/fcheck {entity} 845d33d3-0961-4e44-b4b5-4c57775fbdac')
|
||||
identifier = (new_message.chat.id, new_message.message_id)
|
||||
conversation_hack[identifier] = None
|
||||
while not conversation_hack[identifier]:
|
||||
await asyncio.sleep(0.5)
|
||||
ntext = conversation_hack[identifier].split('\n')
|
||||
ntext.pop(0)
|
||||
if ntext:
|
||||
ntext.pop(0)
|
||||
if ntext:
|
||||
text = '- <b>Reason:</b> '
|
||||
ntext.pop(0)
|
||||
reason = '\n'.join(ntext).strip()
|
||||
text += html.escape(reason) or 'None'
|
||||
match = re.match(r'(?:AIdetection:)?((?:0x\d{2} )+)risk:(\S+) mod:X([0-8])(?: cmt:(.+))?', reason)
|
||||
if match:
|
||||
text += '\n- <b>Ban Codes:</b>\n'
|
||||
for i in match.group(1).split(' '):
|
||||
if i:
|
||||
i = DEAI_BAN_CODES.get(i.strip()[2:], i.strip())
|
||||
text += f'--- {i}\n'
|
||||
text += f'- <b>Risk Factor:</b> {match.group(2).capitalize()}\n'
|
||||
text += f'- <b>Module:</b> {DEAI_MODULE_CODES.get(match.group(3), match.group(3))}'
|
||||
if (match.group(4) or '').strip():
|
||||
text += f'\n- <b>Comment:</b> {html.escape(match.group(4).strip())}'
|
||||
return text
|
||||
return '- <b>404:</b> Not Found'
|
||||
|
||||
async def get_cas(entity):
|
||||
async with session.get(f'https://api.cas.chat/check?user_id={entity}') as resp:
|
||||
json = await resp.json()
|
||||
if json['ok']:
|
||||
return f'''- <b>Banned on:</b> {str(datetime.datetime.fromisoformat(json["result"]["time_added"][:-1]))}
|
||||
- <b>Offenses:</b> {json["result"]["offenses"]}'''
|
||||
return f'- <b>XXX:</b> {html.escape(json.get("description", "XXX"))}'
|
||||
|
||||
async def get_spam_protection(entity):
|
||||
async with session.get(f'https://api.intellivoid.net/spamprotection/v1/lookup?query={entity}') as resp:
|
||||
json = await resp.json()
|
||||
if json['success']:
|
||||
text = ''
|
||||
if json['results']['attributes']['intellivoid_accounts_verified']:
|
||||
text += '- <b>Intellivoid Account Linked:</b> Yes\n'
|
||||
if json['results']['attributes']['is_potential_spammer']:
|
||||
text += '- <b>Potential Spammer:</b> Yes\n'
|
||||
if json['results']['attributes']['is_operator']:
|
||||
text += '- <b>Operator:</b> Yes\n'
|
||||
if json['results']['attributes']['is_agent']:
|
||||
text += '- <b>Agent:</b> Yes\n'
|
||||
if json['results']['attributes']['is_whitelisted']:
|
||||
text += '- <b>Whitelisted:</b> Yes\n'
|
||||
text += f'- <b>Ham/Spam Prediction:</b> {json["results"]["spam_prediction"]["ham_prediction"] or 0}/{json["results"]["spam_prediction"]["spam_prediction"] or 0}'
|
||||
if json['results']['language_prediction']['language']:
|
||||
text += f'''\n- <b>Language Prediction:</b> {json["results"]["language_prediction"]["language"]}
|
||||
- <b>Language Prediction Probability:</b> {json["results"]["language_prediction"]["probability"]}'''
|
||||
if json['results']['attributes']['is_blacklisted']:
|
||||
text += f'''\n- <b>Blacklist Flag:</b> {json["results"]["attributes"]["blacklist_flag"]}
|
||||
- <b>Blacklist Reason:</b> {json["results"]["attributes"]["blacklist_reason"]}'''
|
||||
if json['results']['attributes']['original_private_id']:
|
||||
text += f'\n- <b>Original Private ID:</b> {json["results"]["attributes"]["original_private_id"]}'
|
||||
return text
|
||||
return f'- <b>{json["response_code"]}</b>: {json["error"]["error_code"]}: {json["error"]["type"]}: {html.escape(json["error"]["message"])}'
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.chat(['rsophiebot', 'missrose_bot']) & filters.incoming & filters.regex('^Federation ban info:\n|You ain\'t fbanned in this fed\.|^Failed to get user: unable to getChatMember: Bad Request: chat not found$|^.+ is not banned in this fed\.$|^.+ is currently banned in Rose Support Official, for the following reason:\n\n'))
|
||||
async def fedstat_conversation_hack(client, message):
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
identifier = (reply.chat.id, reply.message_id)
|
||||
if identifier in conversation_hack:
|
||||
conversation_hack[identifier] = message.text
|
||||
await client.read_history(message.chat.id, message.message_id)
|
||||
|
||||
help_dict['einfo'] = ('External Info',
|
||||
'''{prefix}externalinfo <i><user></i> - Get extended info of <i><user></i>
|
||||
{prefix}externalinfo <i>(as reply to message)</i> - Get extended info of replied user
|
||||
Aliases: {prefix}extinfo, {prefix}einfo
|
||||
|
||||
{prefix}spamwatch <i><user></i> - Get Spamwatch info of <i><user></i>
|
||||
{prefix}spamwatch <i>(as reply to message)</i> - Get Spamwatch info of replied user
|
||||
Aliases: {prefix}sw
|
||||
|
||||
{prefix}cas <i><user></i> - Get Combot Anti Spam info of <i><user></i>
|
||||
{prefix}cas <i>(as reply to message)</i> - Get Combot Anti Spam info of replied user
|
||||
Aliases: {prefix}combot
|
||||
|
||||
{prefix}rose <i><user></i> - Get Rose Support Federation info of <i><user></i>
|
||||
{prefix}rose <i>(as reply to message)</i> - Get Rose Support Federation info of replied user
|
||||
|
||||
{prefix}deai <i><user></i> - Get DEAI info of <i><user></i>
|
||||
{prefix}deai <i>(as reply to message)</i> - Get DEAI info of replied user
|
||||
|
||||
{prefix}spamprotection <i><user></i> - Get Spam Protection info of <i><user></i>
|
||||
{prefix}spamprotection <i>(as reply to message)</i> - Get Spam Protection info of replied user
|
||||
Aliases: {prefix}sp''')
|
|
@ -0,0 +1,88 @@
|
|||
import os
|
||||
import html
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.errors.exceptions.bad_request_400 import MessageIdInvalid
|
||||
from .. import config, help_dict, log_errors, session, progress_callback, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['ls', 'hls', 'hiddenls'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def ls(client, message):
|
||||
dir = os.path.abspath(os.path.expanduser(' '.join(message.command[1:]) or '.'))
|
||||
text = ''
|
||||
folders = []
|
||||
files = []
|
||||
try:
|
||||
for i in sorted(os.listdir(dir)):
|
||||
if i.startswith('.') and 'h' not in message.command[0]:
|
||||
continue
|
||||
(folders if os.path.isdir(os.path.join(dir, i)) else files).append(i)
|
||||
except NotADirectoryError:
|
||||
text = f'<code>{html.escape(os.path.basename(dir))}</code>'
|
||||
except Exception as ex:
|
||||
text = f'{type(ex).__name__}: {html.escape(str(ex))}'
|
||||
else:
|
||||
for i in folders:
|
||||
text += f'<code>{html.escape(i)}</code>\n'
|
||||
for i in files:
|
||||
text += f'<code>{html.escape(i)}</code>\n'
|
||||
await message.reply_text(text or 'Empty', disable_web_page_preview=True)
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['ul', 'upload'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def upload(client, message):
|
||||
file = os.path.expanduser(' '.join(message.command[1:]))
|
||||
if not file:
|
||||
return
|
||||
text = f'Uploading {html.escape(file)}...'
|
||||
reply = await message.reply_text(text)
|
||||
try:
|
||||
await client.send_document(message.chat.id, file, progress=progress_callback, progress_args=(reply, text, True), force_document=True, reply_to_message_id=None if message.chat.type in ('private', 'bot') else message.message_id)
|
||||
except MessageIdInvalid:
|
||||
await message.reply_text('Upload cancelled!')
|
||||
else:
|
||||
await reply.delete()
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['dl', 'download'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def download(client, message):
|
||||
file = os.path.expanduser(' '.join(message.command[1:]) or './')
|
||||
if os.path.isdir(file):
|
||||
file = os.path.join(file, '')
|
||||
available_media = ("audio", "document", "photo", "sticker", "animation", "video", "voice", "video_note")
|
||||
download_message = None
|
||||
for i in available_media:
|
||||
if getattr(message, i, None):
|
||||
download_message = message
|
||||
break
|
||||
else:
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
for i in available_media:
|
||||
if getattr(reply, i, None):
|
||||
download_message = reply
|
||||
break
|
||||
if download_message is None:
|
||||
await message.reply_text('Media required')
|
||||
return
|
||||
text = 'Downloading...'
|
||||
reply = await message.reply_text(text)
|
||||
try:
|
||||
file = await download_message.download(file, progress=progress_callback, progress_args=(reply, text, False))
|
||||
except MessageIdInvalid:
|
||||
await message.reply_text('Download cancelled!')
|
||||
else:
|
||||
await reply.edit_text(f'Downloaded to {html.escape(file)}')
|
||||
|
||||
help_dict['files'] = ('Files',
|
||||
'''{prefix}ls <i>[directory]</i> - Lists files in <i>[directory]</i>
|
||||
{prefix}hiddenls <i>[directory]</i> - {prefix}ls but shows hidden files
|
||||
Aliases: {prefix}hls
|
||||
|
||||
{prefix}upload <i><file name></i> - Uploads <i><file name></i>
|
||||
Aliases: {prefix}ul
|
||||
|
||||
{prefix}download <i>[file name]</i> <i>(as reply or caption to a file)</i> - Downloads file to <i>[file name]</i>
|
||||
Aliases: {prefix}dl''')
|
|
@ -0,0 +1,40 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.errors.exceptions.forbidden_403 import Forbidden
|
||||
from .. import slave, config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command('help', prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def help(client, message):
|
||||
bot = await slave.get_me()
|
||||
module = message.command
|
||||
module.pop(0)
|
||||
module = ' '.join(module).lower().strip()
|
||||
results = await client.get_inline_bot_results(bot.username or bot.id, 'help')
|
||||
for a, i in enumerate(results.results):
|
||||
if a:
|
||||
internal_name = i.id[5:].split('-')
|
||||
internal_name.pop()
|
||||
internal_name = '-'.join(internal_name).lower().strip()
|
||||
external_name = i.title.lower().strip()
|
||||
if module in (internal_name, external_name):
|
||||
result = i
|
||||
break
|
||||
else:
|
||||
result = results.results[0]
|
||||
try:
|
||||
await message.reply_inline_bot_result(results.query_id, result.id)
|
||||
except Forbidden:
|
||||
if module:
|
||||
await message.reply_text({'message': result.send_message.message, 'entities': result.send_message.entities}, parse_mode='through')
|
||||
else:
|
||||
text = 'Avaliable plugins:\n'
|
||||
for i in help_dict:
|
||||
text += f'- {html.escape(help_dict[i][0])}\n'
|
||||
await message.reply_text(text)
|
||||
|
||||
help_dict['help'] = ('Help',
|
||||
'''{prefix}help - Shows list of plugins
|
||||
{prefix}help <i><plugin name></i> - Shows help for <i><plugin name></i>
|
||||
Can also be activated inline with: @{bot} help''')
|
|
@ -0,0 +1,113 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, get_entity, log_errors, public_log_errors
|
||||
|
||||
ZWS = '\u200B'
|
||||
def _generate_sexy(entity, ping):
|
||||
text = getattr(entity, 'title', None)
|
||||
if not text:
|
||||
text = entity.first_name
|
||||
if entity.last_name:
|
||||
text += f' {entity.last_name}'
|
||||
sexy_text = html.escape(text or 'Empty???')
|
||||
if ping and entity.type in ('private', 'bot'):
|
||||
sexy_text = f'<a href="tg://user?id={entity.id}">{sexy_text}</a>'
|
||||
elif entity.username:
|
||||
sexy_text = f'<a href="https://t.me/{entity.username}">{sexy_text}</a>'
|
||||
elif not ping:
|
||||
sexy_text = sexy_text.replace('@', f'@{ZWS}')
|
||||
if entity.type == 'bot':
|
||||
sexy_text += ' <code>[BOT]</code>'
|
||||
if entity.is_verified:
|
||||
sexy_text += ' <code>[VERIFIED]</code>'
|
||||
if entity.is_support:
|
||||
sexy_text += ' <code>[SUPPORT]</code>'
|
||||
if entity.is_scam:
|
||||
sexy_text += ' <code>[SCAM]</code>'
|
||||
return sexy_text
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['info', 'whois'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def info(client, message):
|
||||
entity = message.chat
|
||||
command = message.command
|
||||
command.pop(0)
|
||||
if command:
|
||||
entity = ' '.join(command)
|
||||
elif not getattr(message.reply_to_message, 'empty', True):
|
||||
entity = message.reply_to_message.from_user or message.reply_to_message.chat
|
||||
entity, entity_client = await get_entity(client, entity)
|
||||
text_ping = _generate_sexy(entity, True)
|
||||
text_unping = _generate_sexy(entity, False)
|
||||
text_ping += f'\n<b>ID:</b> <code>{entity.id}</code>'
|
||||
text_unping += f'\n<b>ID:</b> <code>{entity.id}</code>'
|
||||
if entity.dc_id:
|
||||
text_ping += f'\n<b>DC ID:</b> {entity.dc_id}'
|
||||
text_unping += f'\n<b>DC ID:</b> {entity.dc_id}'
|
||||
if entity.username:
|
||||
text_ping += f'\n<b>Username:</b> @{entity.username}'
|
||||
text_unping += f'\n<b>Username:</b> @{ZWS}{entity.username}'
|
||||
if entity.members_count:
|
||||
text_ping += f'\n<b>Members:</b> {entity.members_count}'
|
||||
text_unping += f'\n<b>Members:</b> {entity.members_count}'
|
||||
if entity.linked_chat:
|
||||
text_ping += f'\n<b>Linked Chat:</b> {_generate_sexy(entity.linked_chat, False)} [<code>{entity.linked_chat.id}</code>]'
|
||||
text_unping += f'\n<b>Linked Chat:</b> {_generate_sexy(entity.linked_chat, False)} [<code>{entity.linked_chat.id}</code>]'
|
||||
if entity.description:
|
||||
text_ping += f'\n<b>Description:</b>\n{html.escape(entity.description)}'
|
||||
text_unping += f'\n<b>Description:</b>\n{html.escape(entity.description.replace("@", "@" + ZWS))}'
|
||||
reply = await message.reply_text(text_unping, disable_web_page_preview=True)
|
||||
if text_ping != text_unping:
|
||||
await reply.edit_text(text_ping, disable_web_page_preview=True)
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command('id', prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def id(client, message):
|
||||
text_unping = '<b>Chat ID:</b>'
|
||||
if message.chat.username:
|
||||
text_unping = f'<a href="https://t.me/{message.chat.username}">{text_unping}</a>'
|
||||
text_unping += f' <code>{message.chat.id}</code>\n'
|
||||
text = '<b>Message ID:</b>'
|
||||
if message.link:
|
||||
text = f'<a href="{message.link}">{text}</a>'
|
||||
text += f' <code>{message.message_id}</code>\n'
|
||||
text_unping += text
|
||||
if message.from_user:
|
||||
text_unping += f'<b><a href="tg://user?id={message.from_user.id}">User ID:</a></b> <code>{message.from_user.id}</code>\n'
|
||||
text_ping = text_unping
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
text_unping += '\n'
|
||||
text = '<b>Replied Message ID:</b>'
|
||||
if reply.link:
|
||||
text = f'<a href="{reply.link}">{text}</a>'
|
||||
text += f' <code>{reply.message_id}</code>\n'
|
||||
text_unping += text
|
||||
text_ping = text_unping
|
||||
if reply.from_user:
|
||||
text = '<b>Replied User ID:</b>'
|
||||
if reply.from_user.username:
|
||||
text = f'<a href="https://t.me/{reply.from_user.username}">{text}</a>'
|
||||
text += f' <code>{reply.from_user.id}</code>\n'
|
||||
text_unping += text
|
||||
text_ping += f'<b><a href="tg://user?id={reply.from_user.id}">Replied User ID:</a></b> <code>{reply.from_user.id}</code>\n'
|
||||
if reply.forward_from:
|
||||
text_unping += '\n'
|
||||
text = '<b>Forwarded User ID:</b>'
|
||||
if reply.forward_from.username:
|
||||
text = f'<a href="https://t.me/{reply.forward_from.username}">{text}</a>'
|
||||
text += f' <code>{reply.forward_from.id}</code>\n'
|
||||
text_unping += text
|
||||
text_ping += f'\n<b><a href="tg://user?id={reply.forward_from.id}">Forwarded User ID:</a></b> <code>{reply.forward_from.id}</code>\n'
|
||||
reply = await message.reply_text(text_unping, disable_web_page_preview=True)
|
||||
if text_unping != text_ping:
|
||||
await reply.edit_text(text_ping, disable_web_page_preview=True)
|
||||
|
||||
help_dict['info'] = ('Info',
|
||||
'''{prefix}info <i><entity></i> - Get entity info
|
||||
{prefix}info <i>(as reply to message)</i> - Get entity info of replied user
|
||||
Aliases: {prefix}whois
|
||||
|
||||
{prefix}id <i>[maybe reply to message]</i> - Gets IDs''')
|
|
@ -0,0 +1,68 @@
|
|||
import html
|
||||
import asyncio
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, slave, log_errors
|
||||
|
||||
reported = set()
|
||||
lock = asyncio.Lock()
|
||||
|
||||
@Client.on_message(filters.regex(r'(?:^|\s+)@admins?(?:$|\W+)|^[/!](?:report|admins?)(?:$|\W+)') & filters.group)
|
||||
@log_errors
|
||||
async def log_reports(client, message):
|
||||
if not config['config']['log_reports']:
|
||||
return
|
||||
identifier = (message.chat.id, message.message_id)
|
||||
async with lock:
|
||||
if identifier in reported:
|
||||
return
|
||||
chat_text = html.escape(message.chat.title)
|
||||
if message.chat.username:
|
||||
chat_text = f'<a href="https://t.me/{message.chat.username}">{chat_text}</a>'
|
||||
text = f'<b>Report Event</b>\n- <b>Chat:</b> {chat_text} '
|
||||
if message.chat.is_verified:
|
||||
chat_text += '<code>[VERIFIED]</code> '
|
||||
if message.chat.is_support:
|
||||
chat_text += '<code>[SUPPORT]</code> '
|
||||
if message.chat.is_scam:
|
||||
chat_text += '<code>[SCAM]</code> '
|
||||
text += f'[<code>{message.chat.id}</code>]\n- <b>Reporter:</b> '
|
||||
user_text = message.from_user.first_name
|
||||
if message.from_user.last_name:
|
||||
user_text += f' {message.from_user.last_name}'
|
||||
user_text = '<code>[DELETED]</code>' if message.from_user.is_deleted else html.escape(user_text or 'Empty???')
|
||||
if message.from_user.is_verified:
|
||||
user_text += ' <code>[VERIFIED]</code>'
|
||||
if message.from_user.is_support:
|
||||
user_text += ' <code>[SUPPORT]</code>'
|
||||
if message.from_user.is_scam:
|
||||
user_text += ' <code>[SCAM]</code>'
|
||||
text += f'{user_text} [<code>{message.from_user.id}</code>]\n'
|
||||
start, end = message.matches[0].span()
|
||||
text += f'- <b><a href="{message.link}">Report Message'
|
||||
mtext = (message.text or message.caption or '').strip()
|
||||
if start or end < len(mtext):
|
||||
text += ':'
|
||||
text += '</a></b>'
|
||||
if start or end < len(mtext):
|
||||
text += f' {html.escape(mtext.strip()[:1000])}'
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
text += '\n- <b>Reportee:</b> '
|
||||
user_text = message.reply_to_message.from_user.first_name
|
||||
if message.reply_to_message.from_user.last_name:
|
||||
user_text += f' {message.reply_to_message.from_user.last_name}'
|
||||
user_text = '<code>[DELETED]</code>' if message.from_user.is_deleted else html.escape(user_text or 'Empty???')
|
||||
if message.reply_to_message.from_user.is_verified:
|
||||
user_text += ' <code>[VERIFIED]</code>'
|
||||
if message.reply_to_message.from_user.is_support:
|
||||
user_text += ' <code>[SUPPORT]</code>'
|
||||
if message.reply_to_message.from_user.is_scam:
|
||||
user_text += ' <code>[SCAM]</code>'
|
||||
text += f'{user_text} [<code>{message.reply_to_message.from_user.id}</code>]\n- <b><a href="{message.reply_to_message.link}">Reported Message'
|
||||
mtext = message.reply_to_message.text or message.reply_to_message.caption or ''
|
||||
if mtext.strip():
|
||||
text += ':'
|
||||
text += f'</a></b> {html.escape(mtext.strip()[:1000])}'
|
||||
reply = await slave.send_message(config['config']['log_chat'], text, disable_web_page_preview=True)
|
||||
reported.add(identifier)
|
||||
reported.add((reply.chat.id, reply.message_id))
|
|
@ -0,0 +1,44 @@
|
|||
import os
|
||||
import requests
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types.messages_and_media import Photo, Animation
|
||||
from pyrogram.errors.exceptions.forbidden_403 import Forbidden
|
||||
from .. import config, help_dict, log_errors, session, slave, public_log_errors
|
||||
|
||||
help_text = ''
|
||||
|
||||
resp = requests.get('https://nekos.life/api/v2/endpoints')
|
||||
json = resp.json()
|
||||
for i in json:
|
||||
_, i = i.split(' ', 1)
|
||||
i = i.strip()
|
||||
if i.startswith('/api/v2/img/<\''):
|
||||
for i in os.path.basename(i)[1:-1].split(', '):
|
||||
i = i[1:-1]
|
||||
if 'v3' in i:
|
||||
continue
|
||||
def _generate(i):
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(i, prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def func(client, message):
|
||||
bot = await slave.get_me()
|
||||
results = await client.get_inline_bot_results(bot.username or bot.id, i)
|
||||
result = results.results[0]
|
||||
if result.type == 'photo':
|
||||
file = Photo._parse(client, result.photo)
|
||||
else:
|
||||
file = Animation._parse(client, result.document, result.document.attributes, 'hello.mp4')
|
||||
try:
|
||||
await message.reply_cached_media(file.file_id, file.file_ref, caption=result.send_message.message, parse_mode=None)
|
||||
except Forbidden:
|
||||
await message.reply_text(result.send_message.message, parse_mode=None)
|
||||
return func
|
||||
func = _generate(i)
|
||||
globals()[i] = func
|
||||
locals()[i] = func
|
||||
func = None
|
||||
help_text += '{prefix}' + i.lower() + f' - Gets a {"gif" if "gif" in i else "picture"} of {i.lower()}\n'
|
||||
break
|
||||
|
||||
help_dict['nekos'] = ('Nekos.life', help_text + '\nCan also be activated inline with: @{bot} <i><command without dot></i>')
|
|
@ -0,0 +1,21 @@
|
|||
import time
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['ping', 'pong'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def ping_pong(client, message):
|
||||
strings = {
|
||||
'ping': 'Pong!',
|
||||
'pong': 'Ping!'
|
||||
}
|
||||
text = strings[message.command[0].lower()]
|
||||
start = time.time()
|
||||
reply = await message.reply_text(text)
|
||||
end = time.time()
|
||||
await reply.edit_text(f'{text}\n<i>{round((end-start)*1000)}ms</i>')
|
||||
|
||||
help_dict['ping'] = ('Ping',
|
||||
'''{prefix}ping - Pong!
|
||||
{prefix}pong - Ping!''')
|
|
@ -0,0 +1,15 @@
|
|||
import os
|
||||
import signal
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['poweroff', 'shutdown', 'stop'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def poweroff(client, message):
|
||||
await message.reply_text('Goodbye')
|
||||
os.kill(os.getpid(), signal.SIGINT)
|
||||
|
||||
help_dict['poweroff'] = ('Poweroff',
|
||||
'''{prefix}poweroff - Turns off the userbot
|
||||
Aliases: {prefix}shutdown, {prefix}stop''')
|
|
@ -0,0 +1,71 @@
|
|||
# https://greentreesnakes.readthedocs.io/
|
||||
import re
|
||||
import ast
|
||||
import sys
|
||||
import html
|
||||
import inspect
|
||||
import asyncio
|
||||
from io import StringIO
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, slave, apps, session, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.regex('^(?:' + '|'.join(map(re.escape, config['config']['prefixes'])) + r')exec\s+([\s\S]+)$'))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def pyexec(client, message):
|
||||
code = message.matches[0].group(1).strip()
|
||||
class UniqueExecReturnIdentifier:
|
||||
pass
|
||||
tree = ast.parse(code)
|
||||
obody = tree.body
|
||||
body = obody.copy()
|
||||
body.append(ast.Return(ast.Name('_ueri', ast.Load())))
|
||||
def _gf(body):
|
||||
# args: c, client, m, message, executing, r, reply, _ueri
|
||||
func = ast.AsyncFunctionDef('ex', ast.arguments([], [ast.arg(i, None, None) for i in ['c', 'client', 'm', 'message', 'executing', 'r', 'reply', '_ueri']], None, [], [], None, []), body, [], None, None)
|
||||
ast.fix_missing_locations(func)
|
||||
mod = ast.parse('')
|
||||
mod.body = [func]
|
||||
fl = locals().copy()
|
||||
exec(compile(mod, '<ast>', 'exec'), globals(), fl)
|
||||
return fl['ex']
|
||||
try:
|
||||
exx = _gf(body)
|
||||
except SyntaxError as ex:
|
||||
if ex.msg != "'return' with value in async generator":
|
||||
raise
|
||||
exx = _gf(obody)
|
||||
reply = await message.reply_text('Executing...')
|
||||
async_obj = exx(client, client, message, message, reply, message.reply_to_message, message.reply_to_message, UniqueExecReturnIdentifier)
|
||||
stdout = sys.stdout
|
||||
stderr = sys.stderr
|
||||
wrapped_stdout = StringIO()
|
||||
wrapped_stderr = StringIO()
|
||||
try:
|
||||
sys.stdout = wrapped_stdout
|
||||
sys.stderr = wrapped_stderr
|
||||
if inspect.isasyncgen(async_obj):
|
||||
returned = [i async for i in async_obj]
|
||||
else:
|
||||
returned = [await async_obj]
|
||||
if returned == [UniqueExecReturnIdentifier]:
|
||||
returned = []
|
||||
finally:
|
||||
sys.stdout = stdout
|
||||
sys.stderr = stderr
|
||||
wrapped_stderr.seek(0)
|
||||
wrapped_stdout.seek(0)
|
||||
output = ''
|
||||
wrapped_stderr_text = wrapped_stderr.read().strip()
|
||||
if wrapped_stderr_text:
|
||||
output += f'<code>{html.escape(wrapped_stderr_text)}</code>\n'
|
||||
wrapped_stdout_text = wrapped_stdout.read().strip()
|
||||
if wrapped_stdout_text:
|
||||
output += f'<code>{html.escape(wrapped_stdout_text)}</code>\n'
|
||||
for i in returned:
|
||||
output += f'<code>{html.escape(str(i).strip())}</code>\n'
|
||||
if not output.strip():
|
||||
output = 'Executed'
|
||||
await reply.edit_text(output)
|
||||
|
||||
help_dict['pyexec'] = ('Exec', '{prefix}exec <i><python code></i> - Executes python code')
|
|
@ -0,0 +1,28 @@
|
|||
import re
|
||||
import html
|
||||
import asyncio
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.regex('^(?:' + '|'.join(map(re.escape, config['config']['prefixes'])) + r')(?:(?:ba)?sh|shell|term(?:inal)?)\s+(.+)(?:\n([\s\S]+))?$'))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def shell(client, message):
|
||||
command = message.matches[0].group(1)
|
||||
stdin = message.matches[0].group(2)
|
||||
reply = await message.reply_text('Executing...')
|
||||
process = await asyncio.create_subprocess_shell(command, stdin=asyncio.subprocess.PIPE if stdin else None, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
stdout, stderr = await process.communicate(stdin.encode() if stdin else None)
|
||||
returncode = process.returncode
|
||||
text = f'<b>Exit Code:</b> <code>{returncode}</code>\n'
|
||||
stdout = stdout.decode().replace('\r', '').strip('\n')
|
||||
stderr = stderr.decode().replace('\r', '').strip('\n')
|
||||
if stderr:
|
||||
text += f'<code>{html.escape(stderr)}</code>\n'
|
||||
if stdout:
|
||||
text += f'<code>{html.escape(stdout)}</code>'
|
||||
await reply.edit_text(text)
|
||||
|
||||
help_dict['shell'] = ('Shell',
|
||||
'''{prefix}sh <i><command></i> \\n <i>[stdin]</i> - Executes <i><command></i> in shell
|
||||
Aliases: {prefix}bash, {prefix}shell, {prefix}term, {prefix}terminal''')
|
|
@ -0,0 +1,43 @@
|
|||
import html
|
||||
import googletrans
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, public_log_errors
|
||||
|
||||
PROBLEM_CODES = set(i for i in googletrans.LANGUAGES if '-' in i)
|
||||
translator = googletrans.Translator()
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['tr', 'translate'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def translate(client, message):
|
||||
reply = message.reply_to_message
|
||||
if getattr(reply, 'empty', True):
|
||||
await message.reply_text('Reply required')
|
||||
return
|
||||
text = reply.text or reply.caption
|
||||
if not text:
|
||||
await message.reply_text('Text required')
|
||||
return
|
||||
src_lang = 'auto'
|
||||
dest_lang = 'en'
|
||||
lang = ' '.join(message.command[1:]).lower()
|
||||
for i in PROBLEM_CODES:
|
||||
if lang.startswith(i):
|
||||
src_lang = i
|
||||
lang = lang[len(i):]
|
||||
if lang:
|
||||
dest_lang = lang[1:] or 'en'
|
||||
break
|
||||
else:
|
||||
lang = lang.split('-', 1)
|
||||
if len(lang) == 1:
|
||||
dest_lang = lang.pop(0) or dest_lang
|
||||
else:
|
||||
src_lang, dest_lang = lang
|
||||
def _translate():
|
||||
return translator.translate(text, src=src_lang, dest=dest_lang)
|
||||
await message.reply_text((await client.loop.run_in_executor(None, _translate)).text, parse_mode=None)
|
||||
|
||||
help_dict['translate'] = ('Translate',
|
||||
'''{prefix}translate <i>(as reply to text)</i> <i>[src]-[dest]</i> - Translates text and stuff
|
||||
Aliases: {prefix}tr''')
|
|
@ -0,0 +1,38 @@
|
|||
import html
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.errors.exceptions.forbidden_403 import Forbidden
|
||||
from .. import slave, config, help_dict, log_errors, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['ud', 'urbandictionary'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def ud(client, message):
|
||||
bot = await slave.get_me()
|
||||
query = message.command
|
||||
page = 1
|
||||
query.pop(0)
|
||||
if query and query[0].isnumeric():
|
||||
page = int(query.pop(0))
|
||||
page -= 1
|
||||
if page < 0:
|
||||
page = 0
|
||||
elif page > 9:
|
||||
page = 9
|
||||
query = ' '.join(query)
|
||||
if not query:
|
||||
return
|
||||
results = await client.get_inline_bot_results(bot.username or bot.id, 'ud' + query)
|
||||
if not results.results:
|
||||
await message.reply_text('There are no definitions')
|
||||
return
|
||||
try:
|
||||
await message.reply_inline_bot_result(results.query_id, results.results[page].id)
|
||||
except IndexError:
|
||||
await message.reply_text(f'There are only {len(results.results)} definitions')
|
||||
except Forbidden:
|
||||
await message.reply_text({'message': results.results[page].send_message.message, 'entities': results.results[page].send_message.entities}, disable_web_page_preview=True, parse_mode='through')
|
||||
|
||||
help_dict['ud'] = ('Urban Dictionary',
|
||||
'''{prefix}urbandictionary <i><query></i> - Gets the definition of <i><query></i> via Urban Dictionary
|
||||
Aliases: {prefix}ud
|
||||
Can also be activated inline with: @{bot} urbandictionary <i><query></i> or @{bot} ud <i><query></i>''')
|
|
@ -0,0 +1,52 @@
|
|||
import html
|
||||
import asyncio
|
||||
from pyrogram import Client, ContinuePropagation
|
||||
from pyrogram.raw.types import UpdateNewChannelMessage, UpdateNewMessage, MessageService, PeerChat, PeerChannel, MessageActionChatAddUser, MessageActionChatJoinedByLink
|
||||
from .. import config, log_errors, slave
|
||||
|
||||
def sexy_user_name(user):
|
||||
text = user.first_name
|
||||
if user.last_name:
|
||||
text += ' ' + user.last_name
|
||||
return f'{"<code>[DELETED]</code>" if user.deleted else html.escape(text or "Empty???")} [<code>{user.id}</code>]'
|
||||
|
||||
handled = set()
|
||||
lock = asyncio.Lock()
|
||||
@Client.on_raw_update()
|
||||
@log_errors
|
||||
async def log_user_joins(client, update, users, chats):
|
||||
if isinstance(update, (UpdateNewChannelMessage, UpdateNewMessage)):
|
||||
message = update.message
|
||||
if isinstance(message, MessageService):
|
||||
action = message.action
|
||||
if isinstance(action, (MessageActionChatAddUser, MessageActionChatJoinedByLink)):
|
||||
if isinstance(message.to_id, PeerChannel):
|
||||
chat_id = message.to_id.channel_id
|
||||
sexy_chat_id = int('-100' + str(chat_id))
|
||||
elif isinstance(message.to_id, PeerChat):
|
||||
chat_id = message.to_id.chat_id
|
||||
sexy_chat_id = -chat_id
|
||||
else:
|
||||
return
|
||||
is_join = isinstance(action, MessageActionChatJoinedByLink)
|
||||
if not is_join:
|
||||
is_join = action.users == [message.from_id]
|
||||
if is_join and not config['config']['log_user_joins']:
|
||||
raise ContinuePropagation
|
||||
if not is_join and not config['config']['log_user_adds']:
|
||||
raise ContinuePropagation
|
||||
text = f"<b>{'User Join Event' if is_join else 'User Add Event'}</b>\n- <b>Chat:</b> {html.escape(chats[chat_id].title)} [<code>{sexy_chat_id}</code>]\n"
|
||||
async with lock:
|
||||
if (sexy_chat_id, message.id) not in handled:
|
||||
if is_join:
|
||||
text += f'- <b>User:</b> {sexy_user_name(users[message.from_id])}\n'
|
||||
if isinstance(action, MessageActionChatJoinedByLink):
|
||||
text += f'- <b>Inviter:</b> {sexy_user_name(users[action.inviter_id])}'
|
||||
else:
|
||||
text += f'- <b>Adder:</b> {sexy_user_name(users[message.from_id])}\n- <b>Added Users:</b>\n'
|
||||
for user in action.users:
|
||||
text += f'--- {sexy_user_name(users[user])}\n'
|
||||
await slave.send_message(config['config']['log_chat'], text)
|
||||
handled.add((sexy_chat_id, message.id))
|
||||
return
|
||||
raise ContinuePropagation
|
|
@ -0,0 +1,85 @@
|
|||
import os
|
||||
import time
|
||||
import html
|
||||
import asyncio
|
||||
import datetime
|
||||
import tempfile
|
||||
from decimal import Decimal
|
||||
from urllib.parse import quote as urlencode
|
||||
from pyrogram import Client, filters
|
||||
from .. import config, help_dict, log_errors, session, progress_callback, public_log_errors
|
||||
|
||||
@Client.on_message(~filters.sticker & ~filters.via_bot & ~filters.edited & filters.outgoing & filters.command(['trace', 'tracemoe', 'whatanime', 'wa', 'wait'], prefixes=config['config']['prefixes']))
|
||||
@log_errors
|
||||
@public_log_errors
|
||||
async def whatanime(client, message):
|
||||
media = message.photo or message.animation or message.video or message.sticker or message.document
|
||||
if not media:
|
||||
reply = message.reply_to_message
|
||||
if not getattr(reply, 'empty', True):
|
||||
media = reply.photo or reply.animation or reply.video or reply.sticker or reply.document
|
||||
if not media:
|
||||
await message.reply_text('Photo or GIF or Video or Sticker required')
|
||||
return
|
||||
with tempfile.TemporaryDirectory() as tempdir:
|
||||
reply = await message.reply_text('Downloading...')
|
||||
path = await client.download_media(media, file_name=os.path.join(tempdir, '0'), progress=progress_callback, progress_args=(reply, 'Downloading...', False))
|
||||
new_path = os.path.join(tempdir, '1.png')
|
||||
proc = await asyncio.create_subprocess_exec('ffmpeg', '-i', path, '-frames:v', '1', new_path)
|
||||
await proc.communicate()
|
||||
await reply.edit_text('Uploading...')
|
||||
with open(new_path, 'rb') as file:
|
||||
async with session.post('https://trace.moe/api/search', data={'image': file}) as resp:
|
||||
json = await resp.json()
|
||||
if isinstance(json, str):
|
||||
await reply.edit_text(html.escape(json))
|
||||
else:
|
||||
try:
|
||||
match = json['docs'][0]
|
||||
except IndexError:
|
||||
await reply.edit_text('No match')
|
||||
else:
|
||||
nsfw = match['is_adult']
|
||||
title_native = match['title_native']
|
||||
title_english = match['title_english']
|
||||
title_romaji = match['title_romaji']
|
||||
synonyms = ', '.join(match['synonyms'])
|
||||
filename = match['filename']
|
||||
tokenthumb = match['tokenthumb']
|
||||
anilist_id = match['anilist_id']
|
||||
episode = match['episode']
|
||||
similarity = match['similarity']
|
||||
from_time = str(datetime.timedelta(seconds=match['from'])).split('.', 1)[0].rjust(8, '0')
|
||||
to_time = str(datetime.timedelta(seconds=match['to'])).split('.', 1)[0].rjust(8, '0')
|
||||
at_time = match['at']
|
||||
text = f'<a href="https://anilist.co/anime/{anilist_id}">{title_romaji}</a>'
|
||||
if title_english:
|
||||
text += f' ({title_english})'
|
||||
if title_native:
|
||||
text += f' ({title_native})'
|
||||
if synonyms:
|
||||
text += f'\n<b>Synonyms:</b> {synonyms}'
|
||||
text += f'\n<b>Similarity:</b> {(Decimal(similarity) * 100).quantize(Decimal(".01"))}%\n'
|
||||
if episode:
|
||||
text += f'<b>Episode:</b> {episode}\n'
|
||||
if nsfw:
|
||||
text += '<b>Hentai/NSFW:</b> Yes'
|
||||
async def _send_preview():
|
||||
url = f'https://media.trace.moe/video/{anilist_id}/{urlencode(filename)}?t={at_time}&token={tokenthumb}'
|
||||
with tempfile.NamedTemporaryFile() as file:
|
||||
async with session.get(url) as resp:
|
||||
while True:
|
||||
chunk = await resp.content.read(10)
|
||||
if not chunk:
|
||||
break
|
||||
file.write(chunk)
|
||||
file.seek(0)
|
||||
try:
|
||||
await reply.reply_video(file.name, caption=f'{from_time} - {to_time}')
|
||||
except Exception:
|
||||
await reply.reply_text('Cannot send preview :/')
|
||||
await asyncio.gather(reply.edit_text(text, disable_web_page_preview=True), _send_preview())
|
||||
|
||||
help_dict['whatanime'] = ('WhatAnime',
|
||||
'''{prefix}whatanime <i>(as caption of Photo/GIF/Video/Sticker or reply)</i> - Reverse searches anime, thanks to trace.moe
|
||||
Aliases: {prefix}trace, {prefix}tracemoe, {prefix}wa, {prefix}wait''')
|
|
@ -0,0 +1,234 @@
|
|||
import re
|
||||
import time
|
||||
import json
|
||||
import asyncio
|
||||
import datetime
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.parser import html as pyrogram_html
|
||||
from pyrogram.types import InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton, InlineQueryResultArticle, InlineQueryResultPhoto, InputMediaPhoto
|
||||
from .. import session, app_user_ids, log_errors
|
||||
|
||||
all_anilists = dict()
|
||||
anilists_lock = asyncio.Lock()
|
||||
|
||||
MEDIA_QUERY = '''query ($id: Int, $search: String) {
|
||||
Page (perPage: 10) {
|
||||
media (id: $id, search: $search) {
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
native
|
||||
}
|
||||
type
|
||||
format
|
||||
status
|
||||
description(asHtml: true)
|
||||
episodes
|
||||
duration
|
||||
chapters
|
||||
volumes
|
||||
genres
|
||||
synonyms
|
||||
averageScore
|
||||
nextAiringEpisode {
|
||||
airingAt
|
||||
timeUntilAiring
|
||||
}
|
||||
siteUrl
|
||||
}
|
||||
}
|
||||
}'''
|
||||
FORMAT_NAMES = {
|
||||
"TV": "TV",
|
||||
"TV_SHORT": "TV Short",
|
||||
"MOVIE": "Movie",
|
||||
"SPECIAL": "Special",
|
||||
"OVA": "OVA",
|
||||
"ONA": "ONA",
|
||||
"MUSIC": "Music",
|
||||
"MANGA": "Manga",
|
||||
"NOVEL": "Novel",
|
||||
"ONE_SHOT": "One Shot"
|
||||
}
|
||||
CHARACTER_QUERY = '''query ($id: Int, $search: String) {
|
||||
Page (perPage: 10) {
|
||||
characters (id: $id, search: $search) {
|
||||
name {
|
||||
full
|
||||
native
|
||||
alternative
|
||||
}
|
||||
description(asHtml: true)
|
||||
image {
|
||||
large
|
||||
}
|
||||
siteUrl
|
||||
}
|
||||
}
|
||||
}'''
|
||||
|
||||
async def generate_media(anilist):
|
||||
title_romaji = anilist['title']['romaji']
|
||||
title_english = anilist['title']['english']
|
||||
title_native = anilist['title']['native']
|
||||
type = anilist['type'].capitalize()
|
||||
format = anilist['format']
|
||||
format = FORMAT_NAMES.get(format, format)
|
||||
status = anilist['status'].replace('_', ' ').title()
|
||||
description = (anilist.get('description') or '').strip()
|
||||
episodes = anilist['episodes']
|
||||
duration = anilist['duration']
|
||||
chapters = anilist['chapters']
|
||||
volumes = anilist['volumes']
|
||||
genres = ', '.join(anilist['genres'])
|
||||
synonyms = ', '.join(anilist['synonyms'])
|
||||
average_score = anilist['averageScore']
|
||||
site_url = anilist['siteUrl']
|
||||
text = f'<a href="{site_url}">{title_romaji}</a>'
|
||||
if title_english:
|
||||
text += f' ({title_english})'
|
||||
if title_native:
|
||||
text += f' ({title_native})'
|
||||
if synonyms:
|
||||
text += f'\n<b>Synonyms:</b> {synonyms}'
|
||||
if genres:
|
||||
text += f'\n<b>Genres:</b> {genres}'
|
||||
text += f'\n<b>Type:</b> {type}\n'
|
||||
if anilist['type'] != 'MANGA':
|
||||
text += f'<b>Format:</b> {format}\n'
|
||||
text += f'<b>Status:</b> {status}\n'
|
||||
if anilist['nextAiringEpisode']:
|
||||
airing_at = str(datetime.datetime.fromtimestamp(anilist['nextAiringEpisode']['airingAt']))
|
||||
time_until_airing = str(datetime.timedelta(seconds=anilist['nextAiringEpisode']['timeUntilAiring']))
|
||||
text += f'<b>Airing At:</b> {airing_at}\n<b>Airing In:</b> {time_until_airing}\n'
|
||||
if average_score is not None:
|
||||
text += f'<b>Average Score:</b> {average_score}%\n'
|
||||
if episodes:
|
||||
text += f'<b>Episodes:</b> {episodes}\n'
|
||||
if duration:
|
||||
text += f'<b>Duration:</b> {duration} minutes per episode\n'
|
||||
if chapters:
|
||||
text += f'<b>Chapters:</b> {chapters}\n'
|
||||
if volumes:
|
||||
text += f'<b>Volumes:</b> {volumes}\n'
|
||||
if description:
|
||||
text += '<b>Description:</b>\n'
|
||||
parser = pyrogram_html.HTML(None)
|
||||
total_length = len((await parser.parse(text))['message'])
|
||||
if len(description) > 1023-total_length:
|
||||
description = description[:1022-total_length] + '…'
|
||||
text += description
|
||||
return text, f"https://img.anili.st/media/{anilist['id']}"
|
||||
|
||||
async def generate_character(anilist):
|
||||
title_full = anilist['name']['full']
|
||||
title_native = anilist['name']['native']
|
||||
title_alternative = ', '.join(anilist['name']['alternative'])
|
||||
description = (anilist['description'] or '').strip()
|
||||
site_url = anilist['siteUrl']
|
||||
image = anilist['image']['large']
|
||||
text = f'<a href="{site_url}">{title_full}</a>'
|
||||
if title_native:
|
||||
text += f' ({title_native})'
|
||||
if title_alternative:
|
||||
text += f'\n<b>Synonyms:</b> {title_alternative}'
|
||||
if description:
|
||||
text += '\n'
|
||||
parser = pyrogram_html.HTML(None)
|
||||
total_length = len((await parser.parse(text))['message'])
|
||||
if len(description) > 1023-total_length:
|
||||
description = description[:1022-total_length] + '…'
|
||||
text += description
|
||||
return text, image
|
||||
|
||||
@Client.on_inline_query(filters.regex(r'^a(?:ni)?l(?:ist)?(c(?:har(?:acter)?)?)?\s+(.+)$'))
|
||||
@log_errors
|
||||
async def anilist_query(client, inline_query):
|
||||
if inline_query.from_user.id not in app_user_ids:
|
||||
await inline_query.answer([
|
||||
InlineQueryResultArticle('...no', InputTextMessageContent('...no'))
|
||||
], cache_time=3600, is_personal=True)
|
||||
return
|
||||
character = bool(inline_query.matches[0].group(1))
|
||||
query = inline_query.matches[0].group(2).strip().lower()
|
||||
async with anilists_lock:
|
||||
if (character, query) not in all_anilists:
|
||||
async with session.post('https://graphql.anilist.co', data=json.dumps({'query': CHARACTER_QUERY if character else MEDIA_QUERY, 'variables': {'search': query}}), headers={'Content-Type': 'application/json', 'Accept': 'application/json'}) as resp:
|
||||
all_anilists[(character, query)] = (await resp.json())['data']['Page']['characters' if character else 'media']
|
||||
anilists = all_anilists[(character, query)]
|
||||
answers = []
|
||||
parser = pyrogram_html.HTML(client)
|
||||
for a, anilist in enumerate(anilists):
|
||||
text, image = await (generate_character if character else generate_media)(anilist)
|
||||
buttons = [InlineKeyboardButton('Back', 'anilist_back'), InlineKeyboardButton(f'{a + 1}/{len(anilists)}', 'anilist_nop'), InlineKeyboardButton('Next', 'anilist_next')]
|
||||
if not a:
|
||||
buttons.pop(0)
|
||||
if len(anilists) == a + 1:
|
||||
buttons.pop()
|
||||
split = text.split('\n', 1)
|
||||
title = (await parser.parse(split[0]))['message']
|
||||
try:
|
||||
description = (await parser.parse(split[1]))['message']
|
||||
except IndexError:
|
||||
description = None
|
||||
answers.append(InlineQueryResultPhoto(image, title=title, description=description, caption=text, reply_markup=InlineKeyboardMarkup([buttons]), id=f'anilist{a}-{time.time()}'))
|
||||
await inline_query.answer(answers, is_personal=True, is_gallery=False)
|
||||
|
||||
@Client.on_callback_query(filters.regex('^anilist_nop$'))
|
||||
@log_errors
|
||||
async def anilist_nop(client, callback_query):
|
||||
await callback_query.answer(cache_time=3600)
|
||||
|
||||
message_info = dict()
|
||||
message_lock = asyncio.Lock()
|
||||
@Client.on_chosen_inline_result()
|
||||
@log_errors
|
||||
async def anilist_chosen(client, inline_result):
|
||||
if inline_result.result_id.startswith('anilist'):
|
||||
match = re.match(r'^a(?:ni)?l(?:ist)?(c(?:har(?:acter)?)?)?\s+(.+)$', inline_result.query)
|
||||
if match:
|
||||
character = bool(match.group(1))
|
||||
query = match.group(2).strip().lower()
|
||||
if query:
|
||||
page = int(inline_result.result_id[7])
|
||||
message_info[inline_result.inline_message_id] = query, page, character
|
||||
async with anilists_lock:
|
||||
if (character, query) not in all_anilists:
|
||||
async with session.post('https://graphql.anilist.co', data=json.dumps({'query': CHARACTER_QUERY if character else MEDIA_QUERY, 'variables': {'search': query, 'page': 1, 'perPage': 10}}), headers={'Content-Type': 'application/json', 'Accept': 'application/json'}) as resp:
|
||||
all_anilists[(character, query)] = (await resp.json())['data']['Page']['characters' if character else 'media']
|
||||
return
|
||||
inline_result.continue_propagation()
|
||||
|
||||
@Client.on_callback_query(filters.regex('^anilist_(back|next)$'))
|
||||
@log_errors
|
||||
async def anilist_move(client, callback_query):
|
||||
if callback_query.from_user.id not in app_user_ids:
|
||||
await callback_query.answer('...no', cache_time=3600, show_alert=True)
|
||||
return
|
||||
async with message_lock:
|
||||
if callback_query.inline_message_id not in message_info:
|
||||
await callback_query.answer('This message is too old', cache_time=3600, show_alert=True)
|
||||
return
|
||||
query, page, character = message_info[callback_query.inline_message_id]
|
||||
opage = page
|
||||
if callback_query.matches[0].group(1) == 'back':
|
||||
page -= 1
|
||||
elif callback_query.matches[0].group(1) == 'next':
|
||||
page += 1
|
||||
if page < 0:
|
||||
page = 0
|
||||
elif page > 9:
|
||||
page = 9
|
||||
if page != opage:
|
||||
async with anilists_lock:
|
||||
anilists = all_anilists[(character, query)]
|
||||
text, image = await (generate_character if character else generate_media)(anilists[page])
|
||||
buttons = [InlineKeyboardButton('Back', 'anilist_back'), InlineKeyboardButton(f'{page + 1}/{len(anilists)}', 'anilist_nop'), InlineKeyboardButton('Next', 'anilist_next')]
|
||||
if not page:
|
||||
buttons.pop(0)
|
||||
if len(anilists) == page + 1:
|
||||
buttons.pop()
|
||||
await callback_query.edit_message_media(InputMediaPhoto(image, caption=text), reply_markup=InlineKeyboardMarkup([buttons]))
|
||||
message_info[callback_query.inline_message_id] = query, page, character
|
||||
await callback_query.answer()
|
|
@ -0,0 +1,112 @@
|
|||
import time
|
||||
import html
|
||||
import asyncio
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.parser import html as pyrogram_html
|
||||
from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from .. import config, help_dict, app_user_ids, log_errors
|
||||
|
||||
@Client.on_inline_query(filters.regex('^help$'))
|
||||
@log_errors
|
||||
async def main_help(client, inline_query):
|
||||
if inline_query.from_user.id not in app_user_ids:
|
||||
await inline_query.answer([
|
||||
InlineQueryResultArticle('...no', InputTextMessageContent('...no'))
|
||||
], cache_time=3600, is_personal=True)
|
||||
return
|
||||
buttons = []
|
||||
to_append = []
|
||||
prefixes = config['config']['prefixes'] or []
|
||||
if not isinstance(prefixes, list):
|
||||
prefixes = prefixes.split()
|
||||
prefixes = ', '.join(prefixes)
|
||||
prefix = prefixes[0]
|
||||
results = []
|
||||
parser = pyrogram_html.HTML(client)
|
||||
me = None
|
||||
for internal_name in help_dict:
|
||||
external_name, help_text = help_dict[internal_name]
|
||||
if '{bot}' in help_text:
|
||||
if not me:
|
||||
me = await client.get_me()
|
||||
text = f'Help for {html.escape(external_name)}:\nAvaliable prefixes: {prefixes}\n\n{help_text.format(prefix=prefix, bot=getattr(me, "username", None))}'
|
||||
to_append.append(InlineKeyboardButton(external_name, f'help_m{internal_name}'))
|
||||
if len(to_append) > 2:
|
||||
buttons.append(to_append)
|
||||
to_append = []
|
||||
results.append(InlineQueryResultArticle(external_name, InputTextMessageContent(text), reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('Back', 'help_back')]]), description=(await parser.parse(help_text.format(prefix=prefix, bot=getattr(me, 'username', None))))['message'], id=f'helpm{internal_name}-{time.time()}'))
|
||||
else:
|
||||
if to_append:
|
||||
buttons.append(to_append)
|
||||
results.insert(0, InlineQueryResultArticle('Main Menu', InputTextMessageContent('Select the plugin you want help with'), reply_markup=InlineKeyboardMarkup(buttons), id=f'helpa-{time.time()}'))
|
||||
await inline_query.answer(results, is_personal=True)
|
||||
|
||||
message_info = dict()
|
||||
lock = asyncio.Lock()
|
||||
@Client.on_chosen_inline_result()
|
||||
@log_errors
|
||||
async def help_chosen(client, inline_result):
|
||||
if inline_result.query == 'help':
|
||||
if inline_result.result_id.startswith('helpm'):
|
||||
location = inline_result.result_id[5:].split('-')
|
||||
location.pop()
|
||||
message_info[inline_result.inline_message_id] = '-'.join(location)
|
||||
return
|
||||
elif inline_result.result_id.startswith('helpa'):
|
||||
message_info[inline_result.inline_message_id] = None
|
||||
return
|
||||
inline_result.continue_propagation()
|
||||
|
||||
@Client.on_callback_query(filters.regex('^help_back$'))
|
||||
@log_errors
|
||||
async def help_back(client, callback_query):
|
||||
if callback_query.from_user.id not in app_user_ids:
|
||||
await callback_query.answer('...no', cache_time=3600, show_alert=True)
|
||||
return
|
||||
message_identifier = callback_query.inline_message_id
|
||||
async with lock:
|
||||
if message_info.get(message_identifier, True):
|
||||
buttons = []
|
||||
to_append = []
|
||||
for internal_name in help_dict:
|
||||
external_name, _ = help_dict[internal_name]
|
||||
to_append.append(InlineKeyboardButton(external_name, f'help_m{internal_name}'))
|
||||
if len(to_append) > 2:
|
||||
buttons.append(to_append)
|
||||
to_append = []
|
||||
if to_append:
|
||||
buttons.append(to_append)
|
||||
await callback_query.edit_message_text('Select the plugin you want help with', reply_markup=InlineKeyboardMarkup(buttons))
|
||||
message_info[message_identifier] = None
|
||||
await callback_query.answer()
|
||||
|
||||
@Client.on_callback_query(filters.regex('^help_m(.+)$'))
|
||||
@log_errors
|
||||
async def help_m(client, callback_query):
|
||||
if callback_query.from_user.id not in app_user_ids:
|
||||
await callback_query.answer('...no', cache_time=3600, show_alert=True)
|
||||
return
|
||||
message_identifier = callback_query.inline_message_id
|
||||
plugin = callback_query.matches[0].group(1)
|
||||
async with lock:
|
||||
if message_info.get(message_identifier) != plugin:
|
||||
if plugin not in help_dict:
|
||||
await callback_query.answer('What plugin?', cache_time=3600, show_alert=True)
|
||||
return
|
||||
external_name, help_text = help_dict[plugin]
|
||||
prefixes = config['config']['prefixes'] or []
|
||||
if not isinstance(prefixes, list):
|
||||
prefixes = prefixes.split()
|
||||
prefixes = ', '.join(prefixes)
|
||||
text = f'Help for {html.escape(external_name)}:\n'
|
||||
prefix = ''
|
||||
if prefixes:
|
||||
text += f'Avaliable prefixes: {prefixes}\n'
|
||||
prefix = prefixes[0]
|
||||
me = None
|
||||
if '{bot}' in help_text:
|
||||
me = await client.get_me()
|
||||
text += f'\n{help_text.format(prefix=prefix, bot=getattr(me, "username", None))}'
|
||||
await callback_query.edit_message_text(text, reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton('Back', 'help_back')]]))
|
||||
message_info[message_identifier] = plugin
|
||||
await callback_query.answer()
|
|
@ -0,0 +1,33 @@
|
|||
import os
|
||||
import requests
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import InputTextMessageContent, InlineQueryResultArticle, InlineQueryResultPhoto, InlineQueryResultAnimation
|
||||
from .. import log_errors, session, app_user_ids
|
||||
|
||||
resp = requests.get('https://nekos.life/api/v2/endpoints')
|
||||
json = resp.json()
|
||||
for i in json:
|
||||
_, i = i.split(' ', 1)
|
||||
i = i.strip()
|
||||
if i.startswith('/api/v2/img/<\''):
|
||||
for i in os.path.basename(i)[1:-1].split(', '):
|
||||
i = i[1:-1]
|
||||
if 'v3' in i:
|
||||
continue
|
||||
def _generate(i):
|
||||
@Client.on_inline_query(filters.regex(f'^{i}$'))
|
||||
@log_errors
|
||||
async def func(client, inline_query):
|
||||
if inline_query.from_user.id not in app_user_ids:
|
||||
await inline_query.answer([InlineQueryResultArticle('...no', InputTextMessageContent('...no'))], cache_time=3600, is_personal=True)
|
||||
return
|
||||
async with session.get(f'https://nekos.life/api/v2/img/{i}') as resp:
|
||||
url = (await resp.json())['url']
|
||||
call = InlineQueryResultAnimation if '.gif' == os.path.splitext(url)[1] else InlineQueryResultPhoto
|
||||
await inline_query.answer([call(url, caption=url, parse_mode=None)], cache_time=0)
|
||||
return func
|
||||
func = _generate(i)
|
||||
globals()[i] = func
|
||||
locals()[i] = func
|
||||
func = None
|
||||
break
|
|
@ -0,0 +1,101 @@
|
|||
import re
|
||||
import time
|
||||
import html
|
||||
import asyncio
|
||||
from urllib.parse import quote as urlencode
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.types import InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from .. import session, app_user_ids, log_errors
|
||||
|
||||
all_definitions = dict()
|
||||
definitions_lock = asyncio.Lock()
|
||||
@Client.on_inline_query(filters.regex('^u(?:rban)?d(?:ictionary)?(.+)$'))
|
||||
@log_errors
|
||||
async def ud(client, inline_query):
|
||||
if inline_query.from_user.id not in app_user_ids:
|
||||
await inline_query.answer([
|
||||
InlineQueryResultArticle('...no', InputTextMessageContent('...no'))
|
||||
], cache_time=3600, is_personal=True)
|
||||
return
|
||||
query = inline_query.matches[0].group(1).strip().lower()
|
||||
async with definitions_lock:
|
||||
if query not in all_definitions:
|
||||
async with session.get(f'https://api.urbandictionary.com/v0/define?term={urlencode(query)}') as resp:
|
||||
all_definitions[query] = (await resp.json())['list']
|
||||
definitions = all_definitions[query]
|
||||
answers = []
|
||||
for a, definition in enumerate(definitions):
|
||||
text = f'''<a href="{definition["permalink"]}">{html.escape(definition["word"])}</a>
|
||||
<b>Definition:</b>
|
||||
{html.escape(definition["definition"])}'''
|
||||
if definition['example']:
|
||||
text += f'\n<b>Examples:</b>\n{html.escape(definition["example"])}'
|
||||
buttons = [InlineKeyboardButton('Back', 'ud_back'), InlineKeyboardButton(f'{a + 1}/{len(definitions)}', 'ud_nop'), InlineKeyboardButton('Next', 'ud_next')]
|
||||
if not a:
|
||||
buttons.pop(0)
|
||||
if len(definitions) == a + 1:
|
||||
buttons.pop()
|
||||
answers.append(InlineQueryResultArticle(definition['word'], InputTextMessageContent(text, disable_web_page_preview=True), reply_markup=InlineKeyboardMarkup([buttons]), id=f'ud{a}-{time.time()}', description=definition['definition']))
|
||||
await inline_query.answer(answers, is_personal=True)
|
||||
|
||||
@Client.on_callback_query(filters.regex('^ud_nop$'))
|
||||
@log_errors
|
||||
async def ud_nop(client, callback_query):
|
||||
await callback_query.answer(cache_time=3600)
|
||||
|
||||
message_info = dict()
|
||||
message_lock = asyncio.Lock()
|
||||
@Client.on_chosen_inline_result()
|
||||
@log_errors
|
||||
async def ud_chosen(client, inline_result):
|
||||
if inline_result.result_id.startswith('ud'):
|
||||
match = re.match('^u(?:rban)?d(?:dictionary)?(.*)$', inline_result.query)
|
||||
if match:
|
||||
query = match.group(1).strip().lower()
|
||||
if query:
|
||||
page = int(inline_result.result_id[2])
|
||||
message_info[inline_result.inline_message_id] = query, page
|
||||
async with definitions_lock:
|
||||
if query not in all_definitions:
|
||||
async with session.get(f'https://api.urbandictionary.com/v0/define?term={urlencode(query)}') as resp:
|
||||
all_definitions[query] = (await resp.json())['list']
|
||||
return
|
||||
inline_result.continue_propagation()
|
||||
|
||||
@Client.on_callback_query(filters.regex('^ud_(back|next)$'))
|
||||
@log_errors
|
||||
async def ud_move(client, callback_query):
|
||||
if callback_query.from_user.id not in app_user_ids:
|
||||
await callback_query.answer('...no', cache_time=3600, show_alert=True)
|
||||
return
|
||||
async with message_lock:
|
||||
if callback_query.inline_message_id not in message_info:
|
||||
await callback_query.answer('This message is too old', cache_time=3600, show_alert=True)
|
||||
return
|
||||
query, page = message_info[callback_query.inline_message_id]
|
||||
opage = page
|
||||
if callback_query.matches[0].group(1) == 'back':
|
||||
page -= 1
|
||||
elif callback_query.matches[0].group(1) == 'next':
|
||||
page += 1
|
||||
if page < 0:
|
||||
page = 0
|
||||
elif page > 9:
|
||||
page = 9
|
||||
if page != opage:
|
||||
async with definitions_lock:
|
||||
definitions = all_definitions[query]
|
||||
definition = definitions[page]
|
||||
text = f'''<a href="{definition["permalink"]}">{html.escape(definition["word"])}</a>
|
||||
<b>Definition:</b>
|
||||
{html.escape(definition["definition"])}'''
|
||||
if definition['example']:
|
||||
text += f'\n<b>Examples:</b>\n{html.escape(definition["example"])}'
|
||||
buttons = [InlineKeyboardButton('Back', 'ud_back'), InlineKeyboardButton(f'{page + 1}/{len(definitions)}', 'ud_nop'), InlineKeyboardButton('Next', 'ud_next')]
|
||||
if not page:
|
||||
buttons.pop(0)
|
||||
if len(definitions) == page + 1:
|
||||
buttons.pop()
|
||||
await callback_query.edit_message_text(text, disable_web_page_preview=True, reply_markup=InlineKeyboardMarkup([buttons]))
|
||||
message_info[callback_query.inline_message_id] = query, page
|
||||
await callback_query.answer()
|
Loading…
Reference in New Issue