diff --git a/.gitignore b/.gitignore
index f529a2ed9b..d67488e57b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -153,8 +153,11 @@ dmypy.json
.vscode
# Others
+backups/
*.mp3
*.mp4
*.otf
*.png
*.ttf
+shell_output.txt
+error.txt
diff --git a/AstrakoBot/__init__.py b/AstrakoBot/__init__.py
index c667abbaea..830553c8c5 100644
--- a/AstrakoBot/__init__.py
+++ b/AstrakoBot/__init__.py
@@ -17,6 +17,10 @@
)
LOGGER = logging.getLogger(__name__)
+log_warning_only = ["telethon.client.updates", "apscheduler.scheduler", "apscheduler.executors.default", "telethon.network.mtprotosender", "telethon.client"]
+
+for i in log_warning_only:
+ logging.getLogger(i).setLevel(logging.WARNING)
# if version < 3.6, stop bot.
if sys.version_info[0] < 3 or sys.version_info[1] < 6:
@@ -83,6 +87,8 @@
WEATHER_API = os.environ.get("WEATHER_API", None)
ALLOW_CHATS = os.environ.get("ALLOW_CHATS", True)
+ BACKUP_PASS = os.environ.get("BACKUP_PASS", True)
+ DROP_UPDATES = os.environ.get("DROP_UPDATES", False)
try:
BL_CHATS = set(int(x) for x in os.environ.get("BL_CHATS", "").split())
@@ -129,6 +135,7 @@
API_HASH = Config.API_HASH
DB_URI = Config.SQLALCHEMY_DATABASE_URI
+ DB_NAME = Config.DB_NAME
DONATION_LINK = Config.DONATION_LINK
LOAD = Config.LOAD
NO_LOAD = Config.NO_LOAD
@@ -146,6 +153,8 @@
SPAMWATCH_API = Config.SPAMWATCH_API
INFOPIC = Config.INFOPIC
WEATHER_API = Config.WEATHER_API
+ BACKUP_PASS = Config.BACKUP_PASS
+ DROP_UPDATES = Config.DROP_UPDATES
try:
BL_CHATS = set(int(x) for x in Config.BL_CHATS or [])
diff --git a/AstrakoBot/__main__.py b/AstrakoBot/__main__.py
index b83be07733..421fb889a3 100644
--- a/AstrakoBot/__main__.py
+++ b/AstrakoBot/__main__.py
@@ -20,12 +20,13 @@
StartTime,
telethn,
updater,
+ DROP_UPDATES,
)
# needed to dynamically load modules
# NOTE: Module order is not guaranteed, specify that in the config file!
from AstrakoBot.modules import ALL_MODULES
-from AstrakoBot.modules.helper_funcs.chat_status import is_user_admin
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot.modules.helper_funcs.misc import paginate_modules
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode, Update
from telegram.error import (
@@ -169,13 +170,6 @@ def send_help(chat_id, text, keyboard=None):
)
-def test(update: Update, context: CallbackContext):
- # pprint(eval(str(update)))
- # update.effective_message.reply_text("Hola tester! _I_ *have* `markdown`", parse_mode=ParseMode.MARKDOWN)
- update.effective_message.reply_text("This person edited a message")
- print(update.effective_message)
-
-
def start(update: Update, context: CallbackContext):
args = context.args
uptime = get_readable_time((time.time() - StartTime))
@@ -202,7 +196,7 @@ def start(update: Update, context: CallbackContext):
match = re.match("stngs_(.*)", args[0].lower())
chat = dispatcher.bot.getChat(match.group(1))
- if is_user_admin(chat, update.effective_user.id):
+ if user_is_admin(chat, update.effective_user.id):
send_settings(match.group(1), update.effective_user.id, False)
else:
send_settings(match.group(1), update.effective_user.id, True)
@@ -256,43 +250,23 @@ def start(update: Update, context: CallbackContext):
),
)
else:
- update.effective_message.reply_text(
- "I'm awake already!\nHaven't slept since: {}".format(
- uptime
- ),
- parse_mode=ParseMode.HTML,
- )
-
+ try:
+ update.effective_message.reply_text(
+ "I'm awake already!\nHaven't slept since: {}".format(
+ uptime
+ ),
+ parse_mode=ParseMode.HTML,
+ )
+ except BadRequest:
+ pass # chat_checker will take care of it, just don't error
# for test purposes
def error_callback(update: Update, context: CallbackContext):
error = context.error
try:
raise error
- except Unauthorized:
- print("no nono1")
- print(error)
- # remove update.message.chat_id from conversation list
- except BadRequest:
- print("no nono2")
- print("BadRequest caught")
- print(error)
-
- # handle malformed requests - read more below!
- except TimedOut:
- print("no nono3")
- # handle slow connection problems
- except NetworkError:
- print("no nono4")
- # handle other connection problems
- except ChatMigrated as err:
- print("no nono5")
- print(err)
- # the chat_id of a group has changed, use e.new_chat_id instead
- except TelegramError:
+ except:
print(error)
- # handle all other telegram related errors
-
def help_button(update: Update, context: CallbackContext):
query = update.callback_query
@@ -301,8 +275,6 @@ def help_button(update: Update, context: CallbackContext):
next_match = re.match(r"help_next\((.+?)\)", query.data)
back_match = re.match(r"help_back", query.data)
- print(query.message.chat.id)
-
try:
if mod_match:
module = mod_match.group(1)
@@ -548,7 +520,7 @@ def get_settings(update: Update, context: CallbackContext):
# ONLY send settings in PM
if chat.type != chat.PRIVATE:
- if is_user_admin(chat, user.id):
+ if user_is_admin(chat, user.id):
text = "Click here to get this chat's settings, as well as yours."
msg.reply_text(
text,
@@ -626,7 +598,6 @@ def migrate_chats(update: Update, context: CallbackContext):
def main():
- test_handler = CommandHandler("test", test, run_async=True)
start_handler = CommandHandler("start", start, run_async=True)
help_handler = CommandHandler("help", get_help, run_async=True)
@@ -638,7 +609,6 @@ def main():
donate_handler = CommandHandler("donate", donate, run_async=True)
migrate_handler = MessageHandler(Filters.status_update.migrate, migrate_chats)
- # dispatcher.add_handler(test_handler)
dispatcher.add_handler(start_handler)
dispatcher.add_handler(help_handler)
dispatcher.add_handler(settings_handler)
@@ -660,12 +630,11 @@ def main():
else:
LOGGER.info("Using long polling.")
- updater.start_polling(timeout=15, read_latency=4, drop_pending_updates=True)
+ allowed_updates = ['message', 'edited_message', 'callback_query', 'callback_query', 'my_chat_member',
+ 'chat_member', 'chat_join_request', 'channel_post', 'edited_channel_post', 'inline_query']
+ updater.start_polling(timeout=15, read_latency=4, drop_pending_updates=DROP_UPDATES, allowed_updates = allowed_updates)
- if len(argv) not in (1, 3, 4):
- telethn.disconnect()
- else:
- telethn.run_until_disconnected()
+ telethn.run_until_disconnected()
updater.idle()
diff --git a/AstrakoBot/modules/admin.py b/AstrakoBot/modules/admin.py
index a3493d462a..56269ae46f 100644
--- a/AstrakoBot/modules/admin.py
+++ b/AstrakoBot/modules/admin.py
@@ -15,7 +15,7 @@
user_admin,
ADMIN_CACHE,
)
-
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member
from AstrakoBot.modules.helper_funcs.extraction import (
extract_user,
extract_user_and_text,
@@ -68,7 +68,7 @@ def promote(update: Update, context: CallbackContext) -> str:
return
# set same perms as bot - bot can't assign higher perms than itself!
- bot_member = chat.get_member(bot.id)
+ bot_member = get_bot_member(chat.id)
try:
bot.promoteChatMember(
@@ -358,7 +358,7 @@ def invite(update: Update, context: CallbackContext):
if chat.username:
update.effective_message.reply_text(f"https://t.me/{chat.username}")
elif chat.type in [chat.SUPERGROUP, chat.CHANNEL]:
- bot_member = chat.get_member(bot.id)
+ bot_member = get_bot_member(chat.id)
if bot_member.can_invite_users:
invitelink = bot.exportChatInviteLink(chat.id)
update.effective_message.reply_text(invitelink)
@@ -399,14 +399,12 @@ def adminlist(update: Update, context: CallbackContext):
administrators = bot.getChatAdministrators(chat_id)
text = "Admins in {}:".format(html.escape(update.effective_chat.title))
- bot_admin_list = []
-
for admin in administrators:
user = admin.user
status = admin.status
custom_title = admin.custom_title
- if user.first_name == "":
+ if not user.first_name:
name = "ā Deleted Account"
else:
name = "{}".format(
@@ -415,19 +413,12 @@ def adminlist(update: Update, context: CallbackContext):
)
)
- if user.is_bot:
- bot_admin_list.append(name)
- administrators.remove(admin)
- continue
-
- # if user.username:
- # name = escape_markdown("@" + user.username)
if status == "creator":
text += "\n š Creator:"
text += "\n ⢠{}\n".format(name)
- if custom_title:
- text += f" āā {html.escape(custom_title)}\n"
+ # if user.username:
+ # name = escape_markdown("@" + user.username)
text += "\nš± Admins:"
@@ -439,7 +430,7 @@ def adminlist(update: Update, context: CallbackContext):
status = admin.status
custom_title = admin.custom_title
- if user.first_name == "":
+ if not user.first_name:
name = "ā Deleted Account"
else:
name = "{}".format(
@@ -475,10 +466,6 @@ def adminlist(update: Update, context: CallbackContext):
text += "\n ⢠{}".format(admin)
text += "\n"
- text += "\nš¤ Bots:"
- for each_bot in bot_admin_list:
- text += "\n ⢠{}".format(each_bot)
-
try:
msg.edit_text(text, parse_mode=ParseMode.HTML)
except BadRequest: # if original message is deleted
diff --git a/AstrakoBot/modules/afk.py b/AstrakoBot/modules/afk.py
index 7255162b27..f59e8d01f5 100644
--- a/AstrakoBot/modules/afk.py
+++ b/AstrakoBot/modules/afk.py
@@ -180,7 +180,7 @@ def __gdpr__(user_id):
AFK_HANDLER = DisableAbleCommandHandler("afk", afk, run_async=True)
AFK_REGEX_HANDLER = DisableAbleMessageHandler(
- Filters.regex(r"^(?i)brb(.*)$"), afk, friendly="afk"
+ Filters.regex(r"(?i)^brb(.*)$"), afk, friendly="afk"
)
NO_AFK_HANDLER = MessageHandler(Filters.all & Filters.chat_type.groups, no_longer_afk, run_async=True)
AFK_REPLY_HANDLER = MessageHandler(Filters.all & Filters.chat_type.groups, reply_afk, run_async=True)
diff --git a/AstrakoBot/modules/android.py b/AstrakoBot/modules/android.py
index b963f17d39..f66acb3b79 100644
--- a/AstrakoBot/modules/android.py
+++ b/AstrakoBot/modules/android.py
@@ -7,7 +7,7 @@
from bs4 import BeautifulSoup
from requests import get
from telegram import Bot, Update, ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
-from telegram.ext import Updater, CommandHandler, MessageHandler
+from telegram.ext import Updater, MessageHandler
from telegram.ext import CallbackContext, run_async
from ujson import loads
from yaml import load, Loader
@@ -16,7 +16,11 @@
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from AstrakoBot.modules.github import getphh
from AstrakoBot.modules.helper_funcs.misc import delete
+from AstrakoBot.modules.disable import DisableAbleCommandHandler
+rget_headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36"
+}
def magisk(update: Update, context: CallbackContext):
message = update.effective_message
@@ -45,6 +49,52 @@ def magisk(update: Update, context: CallbackContext):
if cleartime:
context.dispatcher.run_async(delete, delmsg, cleartime.time)
+def kernelsu(update: Update, context: CallbackContext):
+ message = update.effective_message
+ chat = update.effective_chat
+ repos = [
+ ("KernelSU", "tiann/KernelSU"),
+ ("KernelSU-Next", "KernelSU-Next/KernelSU-Next")
+ ]
+
+ msg = "*Latest KernelSU Releases:*\n\n"
+
+ for repo_name, repo_path in repos:
+ try:
+ api_url = f"https://api.github.com/repos/{repo_path}/releases/latest"
+ response = get(api_url, headers=rget_headers)
+ response.raise_for_status()
+ data = response.json()
+
+ msg += f"*{repo_name}:*\n"
+ msg += f'⢠Release - [{data["tag_name"]}]({data["html_url"]})\n'
+
+ apk_assets = [asset for asset in data["assets"] if asset["name"].lower().endswith(".apk")]
+ if apk_assets:
+ for asset in apk_assets:
+ msg += f'⢠APK - [{asset["name"]}]({asset["browser_download_url"]})\n'
+ else:
+ msg += "⢠APK - No APK assets found\n"
+
+ msg += "\n"
+
+ except Exception as e:
+ msg += f"*{repo_name}:* Error fetching data ({str(e)})\n\n"
+ continue
+
+ if "Error fetching data" in msg:
+ msg += "\nā ļø Failed to fetch some releases, try again later."
+
+ delmsg = message.reply_text(
+ text=msg,
+ parse_mode=ParseMode.MARKDOWN,
+ disable_web_page_preview=True,
+ )
+
+ cleartime = get_clearcmd(chat.id, "kernelsu")
+
+ if cleartime:
+ context.dispatcher.run_async(delete, delmsg, cleartime.time)
def checkfw(update: Update, context: CallbackContext):
args = context.args
@@ -55,14 +105,15 @@ def checkfw(update: Update, context: CallbackContext):
temp, csc = args
model = f'sm-' + temp if not temp.upper().startswith('SM-') else temp
fota = get(
- f'http://fota-cloud-dn.ospserver.net/firmware/{csc.upper()}/{model.upper()}/version.xml'
+ f'https://fota-cloud-dn.ospserver.net/firmware/{csc.upper()}/{model.upper()}/version.xml',
+ headers=rget_headers
)
if fota.status_code != 200:
msg = f"Couldn't check for {temp.upper()} and {csc.upper()}, please refine your search or try again later!"
else:
- page = BeautifulSoup(fota.content, 'lxml')
+ page = BeautifulSoup(fota.content, 'xml')
os = page.find("latest").get("o")
if page.find("latest").text.strip():
@@ -102,7 +153,8 @@ def getfw(update: Update, context: CallbackContext):
temp, csc = args
model = f'sm-' + temp if not temp.upper().startswith('SM-') else temp
fota = get(
- f'http://fota-cloud-dn.ospserver.net/firmware/{csc.upper()}/{model.upper()}/version.xml'
+ f'https://fota-cloud-dn.ospserver.net/firmware/{csc.upper()}/{model.upper()}/version.xml',
+ headers=rget_headers
)
if fota.status_code != 200:
@@ -113,10 +165,7 @@ def getfw(update: Update, context: CallbackContext):
url2 = f'https://www.sammobile.com/samsung/firmware/{model.upper()}/{csc.upper()}/'
url3 = f'https://sfirmware.com/samsung-{model.lower()}/#tab=firmwares'
url4 = f'https://samfw.com/firmware/{model.upper()}/{csc.upper()}/'
- fota = get(
- f'http://fota-cloud-dn.ospserver.net/firmware/{csc.upper()}/{model.upper()}/version.xml'
- )
- page = BeautifulSoup(fota.content, 'lxml')
+ page = BeautifulSoup(fota.content, 'xml')
os = page.find("latest").get("o")
msg = ""
if page.find("latest").text.strip():
@@ -129,10 +178,10 @@ def getfw(update: Update, context: CallbackContext):
msg += f'⢠Android: `{os}`\n'
msg += '\n'
msg += f'*Downloads for {model.upper()} and {csc.upper()}*\n'
- btn = [[InlineKeyboardButton(text=f"samfrew.com", url = url1)]]
- btn += [[InlineKeyboardButton(text=f"sammobile.com", url = url2)]]
- btn += [[InlineKeyboardButton(text=f"sfirmware.com", url = url3)]]
- btn += [[InlineKeyboardButton(text=f"samfw.com", url = url4)]]
+ btn = [[InlineKeyboardButton(text=f"Samfrew", url = url1)]]
+ btn += [[InlineKeyboardButton(text=f"Sammobile", url = url2)]]
+ btn += [[InlineKeyboardButton(text=f"SFirmware", url = url3)]]
+ btn += [[InlineKeyboardButton(text=f"Samfw (Recommended)", url = url4)]]
else:
msg = 'Give me something to fetch, like:\n`/getfw SM-N975F DBT`'
@@ -222,13 +271,13 @@ def orangefox(update: Update, context: CallbackContext):
if device:
link = get(f"https://api.orangefox.download/v3/releases/?codename={device}&sort=date_desc&limit=1")
- if link.status_code == 404:
+ page = loads(link.content)
+ file_id = page["data"][0]["_id"] if "data" in page else ""
+ link = get(f"https://api.orangefox.download/v3/devices/get?codename={device}")
+ page = loads(link.content)
+ if "detail" in page and page["detail"] == "Not Found":
msg = f"OrangeFox recovery is not avaliable for {device}"
else:
- page = loads(link.content)
- file_id = page["data"][0]["_id"]
- link = get(f"https://api.orangefox.download/v3/devices/get?codename={device}")
- page = loads(link.content)
oem = page["oem_name"]
model = page["model_name"]
full_name = page["full_name"]
@@ -240,7 +289,7 @@ def orangefox(update: Update, context: CallbackContext):
version = page["version"]
changelog = page["changelog"][0]
size = str(round(float(page["size"]) / 1024 / 1024, 1)) + "MB"
- dl_link = page["mirrors"]["NL"]
+ dl_link = page["mirrors"][next(iter(page["mirrors"]))]
date = datetime.fromtimestamp(page["date"])
md5 = page["md5"]
msg = f"*Latest OrangeFox Recovery for the {full_name}*\n\n"
@@ -315,6 +364,8 @@ def twrp(update: Update, context: CallbackContext):
*Available commands:*\n
*Magisk:*
⢠`/magisk`, `/su`, `/root`: fetches latest magisk\n
+*KernelSU:*
+⢠`/kernelsu`: fetches latest kernelsu\n
*OrangeFox Recovery Project:*
⢠`/orangefox` ``: fetches lastest OrangeFox Recovery available for a given device codename\n
*TWRP:*
@@ -328,16 +379,17 @@ def twrp(update: Update, context: CallbackContext):
⢠`/getfw ` - Samsung only - gets firmware download links from samfrew, sammobile and sfirmwares for the given device
"""
-MAGISK_HANDLER = CommandHandler(["magisk", "root", "su"], magisk, run_async=True)
-ORANGEFOX_HANDLER = CommandHandler("orangefox", orangefox, run_async=True)
-TWRP_HANDLER = CommandHandler("twrp", twrp, run_async=True)
-GETFW_HANDLER = CommandHandler("getfw", getfw, run_async=True)
-CHECKFW_HANDLER = CommandHandler("checkfw", checkfw, run_async=True)
-PHH_HANDLER = CommandHandler("phh", phh, run_async=True)
-MIUI_HANDLER = CommandHandler("miui", miui, run_async=True)
-
+MAGISK_HANDLER = DisableAbleCommandHandler(["magisk", "root", "su"], magisk, run_async=True)
+KERNELSU_HANDLER = DisableAbleCommandHandler("kernelsu", kernelsu, run_async=True)
+ORANGEFOX_HANDLER = DisableAbleCommandHandler("orangefox", orangefox, run_async=True)
+TWRP_HANDLER = DisableAbleCommandHandler("twrp", twrp, run_async=True)
+GETFW_HANDLER = DisableAbleCommandHandler("getfw", getfw, run_async=True)
+CHECKFW_HANDLER = DisableAbleCommandHandler("checkfw", checkfw, run_async=True)
+PHH_HANDLER = DisableAbleCommandHandler("phh", phh, run_async=True)
+MIUI_HANDLER = DisableAbleCommandHandler("miui", miui, run_async=True)
dispatcher.add_handler(MAGISK_HANDLER)
+dispatcher.add_handler(KERNELSU_HANDLER)
dispatcher.add_handler(ORANGEFOX_HANDLER)
dispatcher.add_handler(TWRP_HANDLER)
dispatcher.add_handler(GETFW_HANDLER)
@@ -346,5 +398,5 @@ def twrp(update: Update, context: CallbackContext):
dispatcher.add_handler(MIUI_HANDLER)
__mod_name__ = "Android"
-__command_list__ = ["magisk", "root", "su", "orangefox", "twrp", "checkfw", "getfw", "phh", "miui"]
-__handlers__ = [MAGISK_HANDLER, ORANGEFOX_HANDLER, TWRP_HANDLER, GETFW_HANDLER, CHECKFW_HANDLER, PHH_HANDLER, MIUI_HANDLER]
+__command_list__ = ["magisk", "kernelsu", "root", "su", "orangefox", "twrp", "checkfw", "getfw", "phh", "miui"]
+__handlers__ = [MAGISK_HANDLER, KERNELSU_HANDLER, ORANGEFOX_HANDLER, TWRP_HANDLER, GETFW_HANDLER, CHECKFW_HANDLER, PHH_HANDLER, MIUI_HANDLER]
diff --git a/AstrakoBot/modules/antiflood.py b/AstrakoBot/modules/antiflood.py
index 1ae174ccf0..31a882c2d8 100644
--- a/AstrakoBot/modules/antiflood.py
+++ b/AstrakoBot/modules/antiflood.py
@@ -7,7 +7,6 @@
from AstrakoBot import WHITELIST_USERS, dispatcher
from AstrakoBot.modules.helper_funcs.chat_status import (
bot_admin,
- is_user_admin,
user_admin,
user_admin_no_reply,
)
@@ -27,6 +26,7 @@
from AstrakoBot.modules.connection import connected
from AstrakoBot.modules.helper_funcs.alternate import send_message
from AstrakoBot.modules.sql.approve_sql import is_approved
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
FLOOD_GROUP = 3
@@ -39,8 +39,8 @@ def check_flood(update: Update, context: CallbackContext) -> str:
if not user: # ignore channels
return ""
- # ignore admins and whitelists
- if is_user_admin(chat, user.id) or user.id in WHITELIST_USERS:
+ # ignore admins, whitelists and telegram's native tech
+ if user_is_admin(chat, user.id) or user.id in WHITELIST_USERS or user.id == 777000:
sql.update_flood(chat.id, None)
return ""
# ignore approved users
@@ -54,11 +54,11 @@ def check_flood(update: Update, context: CallbackContext) -> str:
try:
getmode, getvalue = sql.get_flood_setting(chat.id)
if getmode == 1:
- chat.kick_member(user.id)
+ chat.ban_member(user.id)
execstrings = "Banned"
tag = "BANNED"
elif getmode == 2:
- chat.kick_member(user.id)
+ chat.ban_member(user.id)
chat.unban_member(user.id)
execstrings = "Kicked"
tag = "KICKED"
@@ -70,7 +70,7 @@ def check_flood(update: Update, context: CallbackContext) -> str:
tag = "MUTED"
elif getmode == 4:
bantime = extract_time(msg, getvalue)
- chat.kick_member(user.id, until_date=bantime)
+ chat.ban_member(user.id, until_date=bantime)
execstrings = "Banned for {}".format(getvalue)
tag = "TBAN"
elif getmode == 5:
diff --git a/AstrakoBot/modules/approve.py b/AstrakoBot/modules/approve.py
index 3c081a3fcd..902925c58a 100644
--- a/AstrakoBot/modules/approve.py
+++ b/AstrakoBot/modules/approve.py
@@ -101,8 +101,11 @@ def approved(update: Update, context: CallbackContext):
msg = "The following users are approved.\n"
approved_users = sql.list_approved(message.chat_id)
for i in approved_users:
- member = chat.get_member(int(i.user_id))
- msg += f"- `{i.user_id}`: {member.user['first_name']}\n"
+ try:
+ member = chat.get_member(int(i.user_id))
+ msg += f"- `{i.user_id}`: {member.user['first_name']}\n"
+ except BadRequest:
+ sql.disapprove(message.chat_id, i.user_id)
if msg.endswith("approved.\n"):
message.reply_text(f"No users are approved in {chat_title}.")
return ""
diff --git a/AstrakoBot/modules/backups.py b/AstrakoBot/modules/backups.py
index fa7fce5445..9e08430858 100644
--- a/AstrakoBot/modules/backups.py
+++ b/AstrakoBot/modules/backups.py
@@ -57,7 +57,14 @@ def import_data(update: Update, context: CallbackContext):
with BytesIO() as file:
file_info.download(out=file)
file.seek(0)
- data = json.load(file)
+ try:
+ data = json.load(file)
+ except json.decoder.JSONDecodeError:
+ msg.reply_text("Backup seems to be invalid or corrupted!")
+ return
+ except UnicodeDecodeError:
+ msg.reply_text("This is not a backup file!")
+ return
# only import one group
if len(data) > 1 and str(chat.id) not in data:
@@ -115,6 +122,8 @@ def import_data(update: Update, context: CallbackContext):
else:
text = "Backup fully restored"
msg.reply_text(text, parse_mode="markdown")
+ else:
+ msg.reply_text("Reply to a valid backup file!")
@user_admin
diff --git a/AstrakoBot/modules/bans.py b/AstrakoBot/modules/bans.py
index 8c0da91271..9f771d9fa6 100644
--- a/AstrakoBot/modules/bans.py
+++ b/AstrakoBot/modules/bans.py
@@ -19,13 +19,14 @@
bot_admin,
can_restrict,
connection_status,
- is_user_admin,
is_user_ban_protected,
is_user_in_chat,
user_admin,
user_can_ban,
can_delete,
+ is_bot_admin,
)
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot.modules.helper_funcs.extraction import extract_user_and_text
from AstrakoBot.modules.helper_funcs.string_handling import extract_time
from AstrakoBot.modules.log_channel import gloggable, loggable
@@ -100,7 +101,7 @@ def ban(update: Update, context: CallbackContext) -> str:
log += "\nReason: {}".format(reason)
try:
- chat.kick_member(user_id)
+ chat.ban_member(user_id)
if silent:
if message.reply_to_message:
@@ -122,7 +123,7 @@ def ban(update: Update, context: CallbackContext) -> str:
return log
except BadRequest as excp:
- if excp.message == "Reply message not found":
+ if excp.message == "Reply message not found" or excp.message == "Message can't be deleted":
# Do not reply
if silent:
return log
@@ -199,7 +200,7 @@ def temp_ban(update: Update, context: CallbackContext) -> str:
log += "\nReason: {}".format(reason)
try:
- chat.kick_member(user_id, until_date=bantime)
+ chat.ban_member(user_id, until_date=bantime)
bot.sendMessage(
chat.id,
f"Banned! User {mention_html(member.user.id, html.escape(member.user.first_name))} "
@@ -294,7 +295,7 @@ def punch(update: Update, context: CallbackContext) -> str:
@can_restrict
def punchme(update: Update, context: CallbackContext):
user_id = update.effective_message.from_user.id
- if is_user_admin(update.effective_chat, user_id):
+ if user_is_admin(update.effective_chat, user_id):
update.effective_message.reply_text("I wish I could... but you're an admin.")
return
@@ -353,11 +354,8 @@ def unban(update: Update, context: CallbackContext) -> str:
return log
-@connection_status
-@bot_admin
-@can_restrict
@gloggable
-def selfunban(context: CallbackContext, update: Update) -> str:
+def selfunban(update: Update, context: CallbackContext) -> str:
message = update.effective_message
user = update.effective_user
bot, args = context.bot, context.args
@@ -370,7 +368,19 @@ def selfunban(context: CallbackContext, update: Update) -> str:
message.reply_text("Give a valid chat ID.")
return
- chat = bot.getChat(chat_id)
+ try:
+ chat = bot.getChat(chat_id)
+ except:
+ message.reply_text("It doesn't look like I'm listed as a member of this chat.")
+ return
+
+ if (chat.type == "private"):
+ message.reply_text("How am i going to unban you from a private chat?")
+ return
+
+ if not is_bot_admin(chat, bot.id):
+ message.reply_text("I'm sorry but i am not an admin there!")
+ return
try:
member = chat.get_member(user.id)
diff --git a/AstrakoBot/modules/blacklist.py b/AstrakoBot/modules/blacklist.py
index 93967c8f4c..6634783bf7 100644
--- a/AstrakoBot/modules/blacklist.py
+++ b/AstrakoBot/modules/blacklist.py
@@ -387,7 +387,7 @@ def del_blacklist(update: Update, context: CallbackContext):
return
elif getmode == 5:
message.delete()
- chat.kick_member(user.id)
+ chat.ban_member(user.id)
bot.sendMessage(
chat.id,
f"Banned {user.first_name} for using Blacklisted word: {trigger}",
@@ -396,7 +396,7 @@ def del_blacklist(update: Update, context: CallbackContext):
elif getmode == 6:
message.delete()
bantime = extract_time(message, value)
- chat.kick_member(user.id, until_date=bantime)
+ chat.ban_member(user.id, until_date=bantime)
bot.sendMessage(
chat.id,
f"Banned {user.first_name} until '{value}' for using Blacklisted word: {trigger}!",
diff --git a/AstrakoBot/modules/blacklist_stickers.py b/AstrakoBot/modules/blacklist_stickers.py
index 40de97a766..4ed78b0a1a 100644
--- a/AstrakoBot/modules/blacklist_stickers.py
+++ b/AstrakoBot/modules/blacklist_stickers.py
@@ -423,7 +423,7 @@ def del_blackliststicker(update: Update, context: CallbackContext):
return
elif getmode == 5:
message.delete()
- chat.kick_member(user.id)
+ chat.ban_member(user.id)
bot.sendMessage(
chat.id,
"{} banned because using '{}' which in blacklist stickers".format(
@@ -435,7 +435,7 @@ def del_blackliststicker(update: Update, context: CallbackContext):
elif getmode == 6:
message.delete()
bantime = extract_time(message, value)
- chat.kick_member(user.id, until_date=bantime)
+ chat.ban_member(user.id, until_date=bantime)
bot.sendMessage(
chat.id,
"{} banned for {} because using '{}' which in blacklist stickers".format(
diff --git a/AstrakoBot/modules/cleaner.py b/AstrakoBot/modules/cleaner.py
index f94f4dab77..d0af505dac 100644
--- a/AstrakoBot/modules/cleaner.py
+++ b/AstrakoBot/modules/cleaner.py
@@ -8,6 +8,7 @@
dev_plus,
user_admin,
)
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member
from AstrakoBot.modules.sql import cleaner_sql as sql
from telegram import ParseMode, Update
from telegram.ext import (
@@ -46,7 +47,7 @@ def clean_blue_text_must_click(update: Update, context: CallbackContext):
bot = context.bot
chat = update.effective_chat
message = update.effective_message
- if chat.get_member(bot.id).can_delete_messages and sql.is_enabled(chat.id):
+ if get_bot_member(chat.id).can_delete_messages and sql.is_enabled(chat.id):
fst_word = message.text.strip().split(None, 1)[0]
if len(fst_word) > 1 and any(
@@ -204,7 +205,7 @@ def bluetext_ignore_list(update: Update, context: CallbackContext):
for x in local_ignore_list:
text += f" - {x}\n"
- if text == "":
+ if not text:
text = "No commands are currently ignored from bluetext cleaning."
message.reply_text(text)
return
diff --git a/AstrakoBot/modules/clear_cmd.py b/AstrakoBot/modules/clear_cmd.py
index 75644a6729..63be30c39a 100644
--- a/AstrakoBot/modules/clear_cmd.py
+++ b/AstrakoBot/modules/clear_cmd.py
@@ -4,7 +4,7 @@
import AstrakoBot.modules.sql.clear_cmd_sql as sql
from AstrakoBot import dispatcher
from AstrakoBot.modules.helper_funcs.chat_status import user_admin, connection_status
-
+from AstrakoBot.modules.helper_funcs.string_handling import parse_to_seconds
@user_admin
@connection_status
@@ -28,7 +28,7 @@ def clearcmd(update: Update, context: CallbackContext):
"lyrics",
"magisk",
"miui",
- "notes",
+ "notes",
"orangefox",
"phh",
"ping",
@@ -53,7 +53,7 @@ def clearcmd(update: Update, context: CallbackContext):
if commands:
msg += "*Command - Time*\n"
for cmd in commands:
- msg += f"`{cmd.cmd} - {cmd.time} secs`\n"
+ msg += f"`{cmd.cmd} - {cmd.time} secs`\n"
else:
msg = f"No deletion time has been set for any command in *{chat.title}*"
@@ -83,14 +83,17 @@ def clearcmd(update: Update, context: CallbackContext):
if time == "restore":
sql.del_clearcmd(chat.id, cmd)
msg = f"Removed `{cmd}` from list"
- elif (5 <= int(time) <= 300):
- sql.set_clearcmd(chat.id, cmd, time)
- msg = f"`{cmd}` output will be deleted after *{time}* seconds in *{chat.title}*"
else:
- msg = "Time must be between 5 and 300 seconds"
+ time = parse_to_seconds(time)
+ if not time:
+ msg = "Time must be in seconds or minutes"
+ elif (5 <= time <= 300):
+ sql.set_clearcmd(chat.id, cmd, time)
+ msg = f"`{cmd}` output will be deleted after *{time}* seconds in *{chat.title}*"
+ else:
+ msg = "Time must be between 5 and 300 seconds"
else:
msg = "Specify a valid command. Use `/clearcmd list` to see available commands"
-
else:
msg = "I don't understand what are you trying to do. Check module help for more details"
diff --git a/AstrakoBot/modules/cron_jobs.py b/AstrakoBot/modules/cron_jobs.py
new file mode 100644
index 0000000000..92c7e43888
--- /dev/null
+++ b/AstrakoBot/modules/cron_jobs.py
@@ -0,0 +1,117 @@
+import glob
+import os
+import shutil
+import datetime
+import subprocess
+from time import sleep
+from telegram.ext.commandhandler import CommandHandler
+
+from telegram.update import Update
+from telegram.ext.callbackcontext import CallbackContext
+
+from AstrakoBot import DB_NAME, OWNER_ID, dispatcher, LOGGER as log, BACKUP_PASS
+from AstrakoBot.modules.helper_funcs.chat_status import owner_plus
+
+@owner_plus
+def backup_now(_: Update, ctx: CallbackContext):
+ cronjob.run(dispatcher=dispatcher)
+
+@owner_plus
+def stop_jobs(update: Update, _: CallbackContext):
+ print(j.stop())
+ update.effective_message.reply_text("Scheduler has been shut down")
+
+@owner_plus
+def start_jobs(update: Update, _: CallbackContext):
+ print(j.start())
+ update.effective_message.reply_text("Scheduler started")
+
+zip_pass = BACKUP_PASS
+
+def backup_db(_: CallbackContext):
+ bot = dispatcher.bot
+ tmpmsg = "Performing backup, Please wait..."
+ tmp = bot.send_message(OWNER_ID, tmpmsg)
+ datenow = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
+ dbbkpname = "db_{}_{}.tar".format(bot.username, datenow)
+ bkplocation = "backups/{}".format(datenow)
+ bkpcmd = "pg_dump {} --format=tar > {}/{}".format(DB_NAME, bkplocation, dbbkpname)
+
+ if not os.path.exists(bkplocation):
+ os.makedirs(bkplocation)
+ log.info("performing db backup")
+ loginfo = "db backup"
+ term(bkpcmd, loginfo)
+ if not os.path.exists('{}/{}'.format(bkplocation, dbbkpname)):
+ bot.send_message(OWNER_ID, "An error occurred during the db backup")
+ tmp.edit_text("Backup Failed!")
+ sleep(8)
+ tmp.delete()
+ return
+ else:
+ log.info("copying config, and logs to backup location")
+ if os.path.exists('log.txt'):
+ print("logs copied")
+ shutil.copyfile('log.txt', '{}/log.txt'.format(bkplocation))
+ if os.path.exists('AstrakoBot/config.py'):
+ print("config copied")
+ shutil.copyfile('AstrakoBot/config.py', '{}/config.py'.format(bkplocation))
+ log.info("zipping the backup")
+ zipcmd = "zip -s 45m --password '{}' {} {}/*".format(zip_pass, bkplocation, bkplocation)
+ zipinfo = "zipping db backup"
+ log.info("zip started")
+ term(zipcmd, zipinfo)
+ log.info("zip done")
+ sleep(1)
+ for file in glob.glob('backups/{}'.format(f'{datenow}.z*')):
+ with open(file, 'rb') as bkp:
+ nm = "{} backup \n".format(bot.username) + datenow
+ try:
+ bot.send_document(OWNER_ID,
+ document=bkp,
+ caption=nm,
+ timeout=20
+ )
+ except Exception as err:
+ bot.send_message(OWNER_ID, "Failed to send backup:\n {}".format(err))
+ log.info("removing zipped files")
+ shutil.rmtree("backups/{}".format(datenow))
+ for file in glob.glob('backups/{}'.format(f'202*.z*')):
+ if os.path.isfile(file) \
+ and os.path.getmtime(file) < (datetime.datetime.now() - datetime.timedelta(days=5)).timestamp():
+ os.remove(file)
+
+ log.info("backup done")
+ tmp.edit_text("Backup complete!")
+ sleep(5)
+ tmp.delete()
+
+@owner_plus
+def del_bkp_fldr(update: Update, _: CallbackContext):
+ shutil.rmtree("backups")
+ update.effective_message.reply_text("'backups' directory has been purged!")
+
+def term(cmd, info):
+ process = subprocess.Popen(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
+ )
+ stdout, stderr = process.communicate()
+ stderr = stderr.decode()
+ stdout = stdout.decode()
+ if stdout:
+ log.info(f"{info} successful!")
+ log.info(f"{stdout}")
+ if stderr:
+ log.error(f"error while running {info}")
+ log.info(f"{stderr}")
+
+from AstrakoBot import updater as u
+# run the backup daliy at 1:00
+twhen = datetime.datetime.strptime('01:00', '%H:%M').time()
+j = u.job_queue
+cronjob = j.run_daily(callback=backup_db, name="database backups", time=twhen)
+
+dispatcher.add_handler(CommandHandler("backupdb", backup_now, run_async=True))
+dispatcher.add_handler(CommandHandler("stopjobs", stop_jobs, run_async=True))
+dispatcher.add_handler(CommandHandler("startjobs", start_jobs, run_async=True))
+dispatcher.add_handler(CommandHandler("purgebackups", del_bkp_fldr, run_async=True))
diff --git a/AstrakoBot/modules/currency_converter.py b/AstrakoBot/modules/currency_converter.py
index cd0acccafb..7453e72c90 100644
--- a/AstrakoBot/modules/currency_converter.py
+++ b/AstrakoBot/modules/currency_converter.py
@@ -5,7 +5,6 @@
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from AstrakoBot.modules.helper_funcs.misc import delete
-
def convert(update: Update, context: CallbackContext):
chat = update.effective_chat
args = update.effective_message.text.split(" ")
@@ -13,30 +12,52 @@ def convert(update: Update, context: CallbackContext):
if len(args) == 4:
try:
orig_cur_amount = float(args[1])
-
except ValueError:
update.effective_message.reply_text("Invalid amount of currency")
return
orig_cur = args[2].upper()
-
new_cur = args[3].upper()
+ api_version = "v6"
+ data = None
- request_url = (
- f"https://www.alphavantage.co/query"
- f"?function=CURRENCY_EXCHANGE_RATE"
- f"&from_currency={orig_cur}"
- f"&to_currency={new_cur}"
- f"&apikey={CASH_API_KEY}"
- )
- response = requests.get(request_url).json()
try:
- current_rate = float(
- response["Realtime Currency Exchange Rate"]["5. Exchange Rate"]
- )
- except KeyError:
- update.effective_message.reply_text("Currency not supported.")
+ request_url = f"https://v6.exchangerate-api.com/v6/{CASH_API_KEY}/latest/{orig_cur}"
+ response = requests.get(request_url)
+ data = response.json()
+
+ if data.get('result') == 'error':
+ api_version = "v4"
+ request_url = f"https://api.exchangerate-api.com/v4/latest/{orig_cur}"
+ response = requests.get(request_url)
+ data = response.json()
+
+ except Exception as e:
+ update.effective_message.reply_text(f"API Error: {str(e)}")
+ return
+
+ if not data or 'result' in data and data['result'] == 'error':
+ error_key = 'error_type' if api_version == "v4" else 'error-type'
+ error_msg = data.get(error_key, 'Unknown API error') if data else 'Empty API response'
+ update.effective_message.reply_text(f"Error: {error_msg}")
return
+
+ if api_version == "v6":
+ if not data.get('conversion_rates'):
+ update.effective_message.reply_text("Invalid API response format")
+ return
+ rates = data['conversion_rates']
+ else:
+ if not data.get('rates'):
+ update.effective_message.reply_text("Invalid API response format")
+ return
+ rates = data['rates']
+
+ if new_cur not in rates:
+ update.effective_message.reply_text(f"Currency {new_cur} is not supported.")
+ return
+
+ current_rate = rates[new_cur]
new_cur_amount = round(orig_cur_amount * current_rate, 5)
delmsg = update.effective_message.reply_text(
f"{orig_cur_amount} {orig_cur} = {new_cur_amount} {new_cur}"
@@ -47,16 +68,13 @@ def convert(update: Update, context: CallbackContext):
else:
delmsg = update.effective_message.reply_text(
- f"*Invalid Args!!:* Required 3 but passed {len(args) -1}",
+ f"*Invalid Arguments!* Required 3 parameters but got {len(args)-1}",
parse_mode=ParseMode.MARKDOWN,
)
cleartime = get_clearcmd(chat.id, "cash")
-
if cleartime:
context.dispatcher.run_async(delete, delmsg, cleartime.time)
-
CONVERTER_HANDLER = CommandHandler("cash", convert, run_async=True)
-
dispatcher.add_handler(CONVERTER_HANDLER)
diff --git a/AstrakoBot/modules/cust_filters.py b/AstrakoBot/modules/cust_filters.py
index 145d572762..c6125eba3e 100644
--- a/AstrakoBot/modules/cust_filters.py
+++ b/AstrakoBot/modules/cust_filters.py
@@ -84,7 +84,7 @@ def list_handlers(update: Update, context: CallbackContext):
if len(entry) + len(filter_list) > telegram.MAX_MESSAGE_LENGTH:
deletion(update, context, send_message(
update.effective_message,
- filter_list.format(chat_name),
+ filter_list.replace("{}", chat_name, 1),
parse_mode=telegram.ParseMode.MARKDOWN,
))
filter_list = entry
@@ -93,7 +93,7 @@ def list_handlers(update: Update, context: CallbackContext):
deletion(update, context, send_message(
update.effective_message,
- filter_list.format(chat_name),
+ filter_list.replace("{}", chat_name, 1),
parse_mode=telegram.ParseMode.MARKDOWN,
))
diff --git a/AstrakoBot/modules/debug.py b/AstrakoBot/modules/debug.py
index 7b56b33d7e..82d8806e8c 100644
--- a/AstrakoBot/modules/debug.py
+++ b/AstrakoBot/modules/debug.py
@@ -54,6 +54,10 @@ async def i_do_nothing_yes(event):
@dev_plus
def logs(update: Update, context: CallbackContext):
user = update.effective_user
+ if os.path.getsize('log.txt') > (45 * 1024 * 1024):
+ update.effective_message.reply_text("Log file is too big to be sent!")
+ return
+
with open("log.txt", "rb") as f:
context.bot.send_document(document=f, filename=f.name, chat_id=user.id)
diff --git a/AstrakoBot/modules/disable.py b/AstrakoBot/modules/disable.py
index f7f7f9af85..2921b1f432 100644
--- a/AstrakoBot/modules/disable.py
+++ b/AstrakoBot/modules/disable.py
@@ -22,9 +22,9 @@
from AstrakoBot.modules.helper_funcs.chat_status import (
connection_status,
- is_user_admin,
user_admin,
)
+ from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot.modules.sql import disable_sql as sql
from telegram.ext.dispatcher import run_async
@@ -65,7 +65,7 @@ def check_update(self, update):
return None
chat = update.effective_chat
user = update.effective_user
- if user.id == 1087968824:
+ if not user.id or user.id == 1087968824:
user_id = chat.id
else:
user_id = user.id
@@ -78,7 +78,7 @@ def check_update(self, update):
# check if command was disabled
is_disabled = command[
0
- ] in ADMIN_CMDS and is_user_admin(chat, user.id)
+ ] in ADMIN_CMDS and user_is_admin(chat, user.id)
if not is_disabled:
return None
else:
diff --git a/AstrakoBot/modules/feds.py b/AstrakoBot/modules/feds.py
index cafe5fc48b..4fc667490c 100644
--- a/AstrakoBot/modules/feds.py
+++ b/AstrakoBot/modules/feds.py
@@ -6,6 +6,7 @@
import time
import uuid
from io import BytesIO
+from html import escape
import AstrakoBot.modules.sql.feds_sql as sql
from AstrakoBot import (
@@ -18,9 +19,9 @@
dispatcher,
)
from AstrakoBot.modules.disable import DisableAbleCommandHandler
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member, user_is_admin
from AstrakoBot.modules.helper_funcs.alternate import send_message
from AstrakoBot.modules.helper_funcs.chat_status import (
- is_user_admin,
can_delete,
)
from AstrakoBot.modules.helper_funcs.extraction import (
@@ -28,9 +29,10 @@
extract_user,
extract_user_fban,
)
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot.modules.helper_funcs.string_handling import markdown_parser
from telegram import (
- InlineKeyboardButton,
+ Chat, InlineKeyboardButton,
InlineKeyboardMarkup,
MessageEntity,
ParseMode,
@@ -41,7 +43,7 @@
CallbackContext,
CallbackQueryHandler,
CommandHandler,
- run_async,
+ Filters, MessageHandler, run_async,
)
from telegram.utils.helpers import mention_html, mention_markdown
@@ -54,6 +56,7 @@
# Time spended on updating version to v2 = 26+ hours by @AyraHikari
# Total spended for making this features is 68+ hours
# LOGGER.info("Original federation module by MrYacha, reworked by Mizukito Akito (@peaktogoo) on Telegram.")
+from AstrakoBot.modules.sql.users_sql import get_user_com_chats
FBAN_ERRORS = {
"User is an administrator of the chat",
@@ -92,13 +95,13 @@ def new_fed(update: Update, context: CallbackContext):
"Federations can only be created by privately messaging me."
)
return
- if len(message.text) == 1:
+ if not context.args:
send_message(
update.effective_message, "Please write the name of the federation!"
)
return
fednam = message.text.split(None, 1)[1]
- if not fednam == "":
+ if fednam:
fed_id = str(uuid.uuid4())
fed_name = fednam
LOGGER.info(fed_id)
@@ -125,7 +128,7 @@ def new_fed(update: Update, context: CallbackContext):
try:
bot.send_message(
EVENT_LOGS,
- "New Federation: {}\nID: {}".format(fed_name, fed_id),
+ "New Federation: {}\nID: {}".format(escape(str(fed_name)), fed_id),
parse_mode=ParseMode.HTML,
)
except:
@@ -210,7 +213,7 @@ def fed_chat(update: Update, context: CallbackContext):
fed_id = sql.get_fed_id(chat.id)
user_id = update.effective_message.from_user.id
- if not is_user_admin(update.effective_chat, user_id):
+ if not user_is_admin(update.effective_chat, user_id):
update.effective_message.reply_text(
"You must be an admin to execute this command"
)
@@ -225,7 +228,7 @@ def fed_chat(update: Update, context: CallbackContext):
info = sql.get_fed_info(fed_id)
text = "This group is part of the following federation:"
- text += "\n{} (ID: {})".format(info["fname"], fed_id)
+ text += "\n{} (ID: {})".format(escape(str(info["fname"])), fed_id)
update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
@@ -350,25 +353,18 @@ def user_join_fed(update: Update, context: CallbackContext):
if is_user_fed_owner(fed_id, user.id) or user.id in SUDO_USERS:
user_id = extract_user(msg, args)
- if user_id:
- user = bot.get_chat(user_id)
- elif not msg.reply_to_message and not args:
- user = msg.from_user
- elif not msg.reply_to_message and (
- not args
- or (
- len(args) >= 1
- and not args[0].startswith("@")
- and not args[0].isdigit()
- and not msg.parse_entities([MessageEntity.TEXT_MENTION])
- )
- ):
- msg.reply_text("I cannot extract user from this message")
+ if not user_id:
+ msg.reply_text("Who are you trying to promote?")
return
- else:
- LOGGER.warning("error")
+ user = bot.get_chat(user_id)
+
getuser = sql.search_user_in_fed(fed_id, user_id)
fed_id = sql.get_fed_id(chat.id)
+ if not fed_id:
+ update.effective_message.reply_text(
+ "This group is not in any federation!"
+ )
+ return
info = sql.get_fed_info(fed_id)
get_owner = ast.literal_eval(info["fusers"])["owner"]
get_owner = bot.get_chat(get_owner).id
@@ -410,28 +406,13 @@ def user_demote_fed(update: Update, context: CallbackContext):
fed_id = sql.get_fed_id(chat.id)
- if is_user_fed_owner(fed_id, user.id):
+ if is_user_fed_owner(fed_id, user.id) or user.id in SUDO_USERS:
msg = update.effective_message
user_id = extract_user(msg, args)
- if user_id:
- user = bot.get_chat(user_id)
-
- elif not msg.reply_to_message and not args:
- user = msg.from_user
-
- elif not msg.reply_to_message and (
- not args
- or (
- len(args) >= 1
- and not args[0].startswith("@")
- and not args[0].isdigit()
- and not msg.parse_entities([MessageEntity.TEXT_MENTION])
- )
- ):
- msg.reply_text("I cannot extract user from this message")
+ if not user_id:
+ msg.reply_text("Who are you trying to demote?")
return
- else:
- LOGGER.warning("error")
+ user = bot.get_chat(user_id)
if user_id == bot.id:
update.effective_message.reply_text(
@@ -476,10 +457,6 @@ def fed_info(update: Update, context: CallbackContext):
return
info = sql.get_fed_info(fed_id)
- if is_user_fed_admin(fed_id, user.id) is False:
- update.effective_message.reply_text("Only a federation admin can do this!")
- return
-
owner = bot.get_chat(info["owner"])
try:
owner_name = owner.first_name + " " + owner.last_name
@@ -494,7 +471,7 @@ def fed_info(update: Update, context: CallbackContext):
text = "ā¹ļø Federation Information:"
text += "\nFedID: {}".format(fed_id)
- text += "\nName: {}".format(info["fname"])
+ text += "\nName: {}".format(escape(str(info["fname"])))
text += "\nCreator: {}".format(mention_html(owner.id, owner_name))
text += "\nAll Admins: {}".format(TotalAdminFed)
getfban = sql.get_all_fban_users(fed_id)
@@ -533,31 +510,45 @@ def fed_admin(update: Update, context: CallbackContext):
chat = update.effective_chat
info = sql.get_fed_info(fed_id)
- text = "Federation Admin {}:\n\n".format(info["fname"])
+ text = "Federation Admin {}:\n\n".format(escape(str(info["fname"])))
text += "š Owner:\n"
owner = bot.get_chat(info["owner"])
try:
owner_name = owner.first_name + " " + owner.last_name
- except:
- owner_name = owner.first_name
- text += " ⢠{}\n".format(mention_html(owner.id, owner_name))
+ except BaseException:
+ owner_name = owner.first_name or 'Deleted'
+ text += " ⢠{} ({})\n".format(mention_html(owner.id, owner_name), escape(str(owner.id)))
members = sql.all_fed_members(fed_id)
+ zombies = []
if len(members) == 0:
text += "\nš± There are no admins in this federation"
else:
text += "\nš± Admin:\n"
for x in members:
- user = bot.get_chat(x)
- text += " ⢠{}\n".format(mention_html(user.id, user.first_name))
+ try:
+ user = bot.get_chat(x)
+ except BadRequest:
+ continue
+
+ if not user.first_name:
+ zombies.append(user.id)
+ continue
+ text += " ⢠{} ({})\n".format(mention_html(user.id, user.first_name), escape(str(user.id)))
+
+ if zombies:
+ text += "\nš§ Zombies:\n"
+ for user_id in zombies:
+ text += " ⢠{}\n".format(escape(str(user_id)))
update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
def fed_ban(update: Update, context: CallbackContext):
- bot, args = context.bot, context.args
- chat = update.effective_chat
- user = update.effective_user
+ bot, args = context.bot, context.args # type: telegram.Bot, List[str]
+ chat = update.effective_chat # Type: Optional[Chat]
+ user = update.effective_user # Type: Optional[User]
+ silent: bool = False
if chat.type == "private":
send_message(
@@ -591,6 +582,12 @@ def fed_ban(update: Update, context: CallbackContext):
message.reply_text("You don't seem to be referring to a user")
return
+ if not reason:
+ message.reply_text(
+ "You must provide a reason!"
+ )
+ return
+
if user_id == bot.id:
message.reply_text(
"What is funnier than kicking the group creator? Self sacrifice."
@@ -632,7 +629,7 @@ def fed_ban(update: Update, context: CallbackContext):
if not str(user_id).isdigit():
send_message(update.effective_message, excp.message)
return
- elif len(str(user_id)) != 9:
+ elif len(str(user_id)) not in [9, 10]:
send_message(update.effective_message, "That's not a user!")
return
isvalid = False
@@ -656,7 +653,7 @@ def fed_ban(update: Update, context: CallbackContext):
# starting = "The reason fban is replaced for {} in the Federation {}.".format(user_target, fed_name)
# send_message(update.effective_message, starting, parse_mode=ParseMode.HTML)
- # if reason == "":
+ # if not reason:
# reason = "No reason given."
temp = sql.un_fban_user(fed_id, fban_user_id)
@@ -688,7 +685,7 @@ def fed_ban(update: Update, context: CallbackContext):
"\nUser: {}"
"\nUser ID: {}"
"\nReason: {}".format(
- fed_name,
+ escape(str(fed_name)),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -698,33 +695,33 @@ def fed_ban(update: Update, context: CallbackContext):
)
# Send message to owner if fednotif is enabled
if getfednotif:
- bot.send_message(
- info["owner"],
- "FedBan reason updated"
- "\nFederation: {}"
- "\nFederation Admin: {}"
- "\nUser: {}"
- "\nUser ID: {}"
- "\nReason: {}".format(
- fed_name,
- mention_html(user.id, user.first_name),
- user_target,
- fban_user_id,
- reason,
- ),
- parse_mode="HTML",
- )
+ try:
+ bot.send_message(
+ info["owner"],
+ "FedBan reason updated"
+ "\nFederation: {}"
+ "\nFederation Admin: {}"
+ "\nUser: {}"
+ "\nUser ID: {}"
+ "\nReason: {}".format(
+ escape(str(fed_name)),
+ mention_html(user.id, user.first_name),
+ user_target,
+ fban_user_id,
+ reason,
+ ),
+ parse_mode="HTML",
+ )
+ except Unauthorized:
+ pass
if message.text.startswith("/s"):
silent = True
if not can_delete(chat, context.bot.id):
return ""
- else:
- silent = False
# If fedlog is set, then send message, except fedlog is current chat
- get_fedlog = sql.get_fed_log(fed_id)
- if get_fedlog:
+ if get_fedlog := sql.get_fed_log(fed_id):
if int(get_fedlog) != int(chat.id):
bot.send_message(
get_fedlog,
@@ -734,7 +731,7 @@ def fed_ban(update: Update, context: CallbackContext):
"\nUser: {}"
"\nUser ID: {}"
"\nReason: {}".format(
- fed_name,
+ escape(str(fed_name)),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -742,7 +739,17 @@ def fed_ban(update: Update, context: CallbackContext):
),
parse_mode="HTML",
)
+ common_chats = get_user_com_chats(fban_user_id)
for fedschat in fed_chats:
+ if fedschat not in common_chats:
+ continue
+ if not get_bot_member(fedschat).can_restrict_members:
+ sql.chat_leave_fed(fedschat)
+ bot.send_message(
+ chat.id,
+ "I don't have rights to restrict users on this chat, left fed!",
+ )
+ continue
try:
# Do not spam all fed chats
"""
@@ -751,9 +758,9 @@ def fed_ban(update: Update, context: CallbackContext):
"\nFederation Admin: {}" \
"\nUser: {}" \
"\nUser ID: {}" \
- "\nReason: {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML")
+ "\nReason: {}".format(escape(str(fed_name)), mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML")
"""
- bot.kick_chat_member(fedschat, fban_user_id)
+ bot.ban_chat_member(fedschat, fban_user_id)
except BadRequest as excp:
if excp.message in FBAN_ERRORS:
try:
@@ -767,13 +774,25 @@ def fed_ban(update: Update, context: CallbackContext):
)
continue
elif excp.message == "User_id_invalid":
+ LOGGER.debug(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(excp))
+ )
break
else:
LOGGER.warning(
- "Could not fban on {} because: {}".format(chat, excp.message)
+ "Couldn't fban user {} in fed {} in chat {} \n\nreason: {}".format(fban_user_id, fed_id, chat.id, str(excp))
)
- except TelegramError:
+ except TelegramError as e:
+ LOGGER.debug(
+ "fban error, (TelegramError)\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(e))
+ )
pass
+ except Exception as e:
+ LOGGER.warning(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(fban_user_id, fed_id, chat.id, str(e))
+ )
# Also do not spam all fed admins
"""
send_to_list(bot, FEDADMIN,
@@ -782,7 +801,7 @@ def fed_ban(update: Update, context: CallbackContext):
"\nFederation Admin: {}" \
"\nUser: {}" \
"\nUser ID: {}" \
- "\nReason: {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason),
+ "\nReason: {}".format(escape(str(fed_name)), mention_html(user.id, user.first_name), user_target, fban_user_id, reason),
html=True)
"""
@@ -792,8 +811,17 @@ def fed_ban(update: Update, context: CallbackContext):
for fedsid in subscriber:
all_fedschat = sql.all_fed_chats(fedsid)
for fedschat in all_fedschat:
+ if fedsid not in common_chats:
+ continue
+ if not get_bot_member(fedschat).can_restrict_members:
+ sql.chat_leave_fed(fedschat)
+ bot.send_message(
+ chat.id,
+ "I don't have rights to restrict users on this chat, left fed!",
+ )
+ continue
try:
- bot.kick_chat_member(fedschat, fban_user_id)
+ bot.ban_chat_member(fedschat, fban_user_id)
except BadRequest as excp:
if excp.message in FBAN_ERRORS:
try:
@@ -808,6 +836,10 @@ def fed_ban(update: Update, context: CallbackContext):
)
continue
elif excp.message == "User_id_invalid":
+ LOGGER.debug(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(excp))
+ )
break
else:
LOGGER.warning(
@@ -815,9 +847,25 @@ def fed_ban(update: Update, context: CallbackContext):
fedschat, excp.message
)
)
- except TelegramError:
+ except TelegramError as e:
+ LOGGER.debug(
+ "fban error, (TelegramError)\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(e))
+ )
pass
+ except Exception as e:
+ LOGGER.warning(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(e))
+ )
# send_message(update.effective_message, "Fedban Reason has been updated.")
+ if silent:
+ try:
+ if message.reply_to_message:
+ message.reply_to_message.delete()
+ message.delete()
+ except:
+ pass
return
fed_name = info["fname"]
@@ -826,7 +874,7 @@ def fed_ban(update: Update, context: CallbackContext):
# user_target, fed_name)
# update.effective_message.reply_text(starting, parse_mode=ParseMode.HTML)
- # if reason == "":
+ # if not reason:
# reason = "No reason given."
x = sql.fban_user(
@@ -854,7 +902,7 @@ def fed_ban(update: Update, context: CallbackContext):
"\nUser: {}"
"\nUser ID: {}"
"\nReason: {}".format(
- fed_name,
+ escape(str(fed_name)),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -867,30 +915,31 @@ def fed_ban(update: Update, context: CallbackContext):
silent = True
if not can_delete(chat, context.bot.id):
return ""
- else:
- silent = False
# Send message to owner if fednotif is enabled
if getfednotif:
- bot.send_message(
- info["owner"],
- "New FedBan"
- "\nFederation: {}"
- "\nFederation Admin: {}"
- "\nUser: {}"
- "\nUser ID: {}"
- "\nReason: {}".format(
- fed_name,
- mention_html(user.id, user.first_name),
- user_target,
- fban_user_id,
- reason,
- ),
- parse_mode="HTML",
- )
+ try:
+ bot.send_message(
+ info["owner"],
+ "New FedBan"
+ "\nFederation: {}"
+ "\nFederation Admin: {}"
+ "\nUser: {}"
+ "\nUser ID: {}"
+ "\nReason: {}".format(
+ escape(str(fed_name)),
+ mention_html(user.id, user.first_name),
+ user_target,
+ fban_user_id,
+ reason,
+ ),
+ parse_mode="HTML",
+ )
+ except Unauthorized:
+ pass
+
# If fedlog is set, then send message, except fedlog is current chat
- get_fedlog = sql.get_fed_log(fed_id)
- if get_fedlog:
+ if get_fedlog:= sql.get_fed_log(fed_id):
if int(get_fedlog) != int(chat.id):
bot.send_message(
get_fedlog,
@@ -900,7 +949,7 @@ def fed_ban(update: Update, context: CallbackContext):
"\nUser: {}"
"\nUser ID: {}"
"\nReason: {}".format(
- fed_name,
+ escape(str(fed_name)),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -908,9 +957,19 @@ def fed_ban(update: Update, context: CallbackContext):
),
parse_mode="HTML",
)
- chats_in_fed = 0
+ # chats_in_fed = 0
+ common_chats = get_user_com_chats(fban_user_id)
for fedschat in fed_chats:
- chats_in_fed += 1
+ if fedschat not in common_chats:
+ continue
+ if not get_bot_member(fedschat).can_restrict_members:
+ sql.chat_leave_fed(fedschat)
+ bot.send_message(
+ chat.id,
+ "I don't have rights to restrict users on this chat, left fed!",
+ )
+ continue
+ # chats_in_fed += 1
try:
# Do not spamming all fed chats
"""
@@ -919,20 +978,33 @@ def fed_ban(update: Update, context: CallbackContext):
"\nFederation Admin: {}" \
"\nUser: {}" \
"\nUser ID: {}" \
- "\nReason: {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML")
+ "\nReason: {}".format(escape(str(fed_name)), mention_html(user.id, user.first_name), user_target, fban_user_id, reason), parse_mode="HTML")
"""
- bot.kick_chat_member(fedschat, fban_user_id)
+ bot.ban_chat_member(fedschat, fban_user_id)
except BadRequest as excp:
if excp.message in FBAN_ERRORS:
pass
elif excp.message == "User_id_invalid":
- break
+ LOGGER.debug(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(excp))
+ )
+ # break
else:
LOGGER.warning(
- "Could not fban on {} because: {}".format(chat, excp.message)
+ "Couldn't fban user {} in fed {} in chat {} \n\nreason: {}".format(fban_user_id, fed_id,
+ chat.id, str(excp))
)
- except TelegramError:
+ except TelegramError as e:
+ LOGGER.debug(
+ "fban error, (TelegramError)\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(excp))
+ )
pass
+ except Exception as e:
+ LOGGER.warning(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(fban_user_id, fed_id, chat.id, str(e))
+ )
# Also do not spamming all fed admins
"""
@@ -942,51 +1014,76 @@ def fed_ban(update: Update, context: CallbackContext):
"\nFederation Admin: {}" \
"\nUser: {}" \
"\nUser ID: {}" \
- "\nReason: {}".format(fed_name, mention_html(user.id, user.first_name), user_target, fban_user_id, reason),
+ "\nReason: {}".format(escape(str(fed_name)), mention_html(user.id, user.first_name), user_target, fban_user_id, reason),
html=True)
"""
- # Fban for fed subscriber
- subscriber = list(sql.get_subscriber(fed_id))
- if len(subscriber) != 0:
- for fedsid in subscriber:
- all_fedschat = sql.all_fed_chats(fedsid)
- for fedschat in all_fedschat:
- try:
- bot.kick_chat_member(fedschat, fban_user_id)
- except BadRequest as excp:
- if excp.message in FBAN_ERRORS:
- try:
- dispatcher.bot.getChat(fedschat)
- except Unauthorized:
- targetfed_id = sql.get_fed_id(fedschat)
- sql.unsubs_fed(fed_id, targetfed_id)
- LOGGER.info(
- "Chat {} has unsubbed from the fed {} because I was kicked".format(
- fedschat, info["fname"]
- )
- )
- continue
- elif excp.message == "User_id_invalid":
- break
- else:
- LOGGER.warning(
- "Unable to execute fban on {} because: {}".format(
- fedschat, excp.message
+ # Fban for fed subscriber
+ subscriber = list(sql.get_subscriber(fed_id))
+ if len(subscriber) != 0:
+ for fedsid in subscriber:
+ all_fedschat = sql.all_fed_chats(fedsid)
+ for fedschat in all_fedschat:
+ if fedsid not in common_chats:
+ continue
+ if not get_bot_member(fedschat).can_restrict_members:
+ sql.chat_leave_fed(fedschat)
+ bot.send_message(
+ chat.id,
+ "I don't have rights to restrict users on this chat, left fed!",
+ )
+ continue
+ try:
+ bot.ban_chat_member(fedschat, fban_user_id)
+ except BadRequest as excp:
+ if excp.message in FBAN_ERRORS:
+ try:
+ dispatcher.bot.getChat(fedschat)
+ except Unauthorized:
+ targetfed_id = sql.get_fed_id(fedschat)
+ sql.unsubs_fed(fed_id, targetfed_id)
+ LOGGER.info(
+ "Chat {} has unsubbed from the fed {} because I was kicked".format(
+ fedschat, info["fname"]
)
)
- except TelegramError:
- pass
+ continue
+ elif excp.message == "User_id_invalid":
+ LOGGER.debug(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(excp))
+ )
+ break
+ else:
+ LOGGER.warning(
+ "Unable to execute fban on {} because: {}".format(
+ fedschat, excp.message
+ )
+ )
+ except TelegramError as e:
+ LOGGER.debug(
+ "fban error, (TelegramError)\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(e))
+ )
+ pass
+ except Exception as e:
+ LOGGER.warning(
+ "fban error\nUnable to fban user {} in fed {} in chat {} \n\nreason: {}".format(
+ fban_user_id, fed_id, chat.id, str(e))
+ )
# if chats_in_fed == 0:
# send_message(update.effective_message, "Fedban affected 0 chats. ")
# elif chats_in_fed > 0:
# send_message(update.effective_message,
# "Fedban affected {} chats. ".format(chats_in_fed))
-
+
if silent:
- if message.reply_to_message:
- message.reply_to_message.delete()
- message.delete()
+ try:
+ if message.reply_to_message:
+ message.reply_to_message.delete()
+ message.delete()
+ except:
+ pass
return
@@ -1071,7 +1168,7 @@ def unfban(update: Update, context: CallbackContext):
"\nFederation Admin: {}"
"\nUser: {}"
"\nUser ID: {}".format(
- info["fname"],
+ escape(str(info["fname"])),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -1080,20 +1177,24 @@ def unfban(update: Update, context: CallbackContext):
)
# Send message to owner if fednotif is enabled
if getfednotif:
- bot.send_message(
- info["owner"],
- "Un-FedBan"
- "\nFederation: {}"
- "\nFederation Admin: {}"
- "\nUser: {}"
- "\nUser ID: {}".format(
- info["fname"],
- mention_html(user.id, user.first_name),
- user_target,
- fban_user_id,
- ),
- parse_mode="HTML",
- )
+ try:
+ bot.send_message(
+ info["owner"],
+ "Un-FedBan"
+ "\nFederation: {}"
+ "\nFederation Admin: {}"
+ "\nUser: {}"
+ "\nUser ID: {}".format(
+ escape(str(info["fname"])),
+ mention_html(user.id, user.first_name),
+ user_target,
+ fban_user_id,
+ ),
+ parse_mode="HTML",
+ )
+ except Unauthorized:
+ pass
+
# If fedlog is set, then send message, except fedlog is current chat
get_fedlog = sql.get_fed_log(fed_id)
if get_fedlog:
@@ -1105,7 +1206,7 @@ def unfban(update: Update, context: CallbackContext):
"\nFederation Admin: {}"
"\nUser: {}"
"\nUser ID: {}".format(
- info["fname"],
+ escape(str(info["fname"])),
mention_html(user.id, user.first_name),
user_target,
fban_user_id,
@@ -1379,7 +1480,7 @@ def fed_ban_list(update: Update, context: CallbackContext):
getfban = sql.get_all_fban_users(fed_id)
if len(getfban) == 0:
update.effective_message.reply_text(
- "The federation ban list of {} is empty".format(info["fname"]),
+ "The federation ban list of {} is empty".format(escape(str(info["fname"]))),
parse_mode=ParseMode.HTML,
)
return
@@ -1476,13 +1577,13 @@ def fed_ban_list(update: Update, context: CallbackContext):
return
text = "{} users have been banned from the federation {}:\n".format(
- len(getfban), info["fname"]
+ len(getfban), escape(str(info["fname"]))
)
for users in getfban:
getuserinfo = sql.get_all_fban_users_target(fed_id, users)
if getuserinfo is False:
text = "There are no users banned from the federation {}".format(
- info["fname"]
+ escape(str(info["fname"]))
)
break
user_name = getuserinfo["first_name"]
@@ -1567,6 +1668,7 @@ def fed_chats(update: Update, context: CallbackContext):
bot, args = context.bot, context.args
chat = update.effective_chat
user = update.effective_user
+ chat_name = ""
if chat.type == "private":
send_message(
@@ -1591,12 +1693,12 @@ def fed_chats(update: Update, context: CallbackContext):
getlist = sql.all_fed_chats(fed_id)
if len(getlist) == 0:
update.effective_message.reply_text(
- "No users are fbanned from the federation {}".format(info["fname"]),
+ "No users are fbanned from the federation {}".format(escape(str(info["fname"]))),
parse_mode=ParseMode.HTML,
)
return
- text = "New chat joined the federation {}:\n".format(info["fname"])
+ text = "New chat joined the federation {}:\n".format(escape(str(info["fname"])))
for chats in getlist:
try:
chat_name = dispatcher.bot.getChat(chats).title
@@ -1608,7 +1710,10 @@ def fed_chats(update: Update, context: CallbackContext):
)
)
continue
- text += " ⢠{} ({})\n".format(chat_name, chats)
+ except BadRequest as excp:
+ if excp.message in FBAN_ERRORS:
+ pass
+ text += " ⢠{} ({})\n".format(chat_name, chats)
try:
update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
@@ -1701,7 +1806,7 @@ def fed_import_bans(update: Update, context: CallbackContext):
reading = file.read().decode("UTF-8")
splitting = reading.split("\n")
for x in splitting:
- if x == "":
+ if not x:
continue
try:
data = json.loads(x)
@@ -1897,7 +2002,7 @@ def fed_stat_user(update: Update, context: CallbackContext):
parse_mode="markdown",
)
return
- if user_name == "" or user_name is None:
+ if not user_name:
user_name = "He/she"
if not reason:
send_message(
@@ -1911,12 +2016,12 @@ def fed_stat_user(update: Update, context: CallbackContext):
send_message(update.effective_message, teks, parse_mode="markdown")
return
user_name, fbanlist = sql.get_user_fbanlist(str(user_id))
- if user_name == "":
+ if not user_name:
try:
user_name = bot.get_chat(user_id).first_name
except BadRequest:
user_name = "He/she"
- if user_name == "" or user_name is None:
+ if not user_name:
user_name = "He/she"
if len(fbanlist) == 0:
send_message(
@@ -1934,7 +2039,7 @@ def fed_stat_user(update: Update, context: CallbackContext):
elif not msg.reply_to_message and not args:
user_id = msg.from_user.id
user_name, fbanlist = sql.get_user_fbanlist(user_id)
- if user_name == "":
+ if not user_name:
user_name = msg.from_user.first_name
if len(fbanlist) == 0:
send_message(
@@ -2273,21 +2378,54 @@ def is_user_fed_owner(fed_id, user_id):
return False
-# There's no handler for this yet, but updating for v12 in case its used
-def welcome_fed(update: Update, context: CallbackContext):
- bot, args = context.bot, context.args
+def enforce_fed(update: Update, ctx: CallbackContext):
chat = update.effective_chat
user = update.effective_user
+ msg = update.effective_message
fed_id = sql.get_fed_id(chat.id)
- fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user.id)
- if fban:
+
+ if fed_id and not get_bot_member(chat.id).can_restrict_members:
+ sql.chat_leave_fed(chat.id)
+ ctx.bot.send_message(
+ chat.id,
+ "I don't have rights to restrict users on this chat, left fed!",
+ )
+ return
+
+ if user and not user_is_admin(chat, user.id):
+ fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user.id)
+ if fban:
+ check_and_ban(update, chat, user.id, fbanreason)
+
+ if msg.new_chat_members:
+ new_members = update.effective_message.new_chat_members
+ for mem in new_members:
+ fban, fbanreason, fbantime = sql.get_fban_user(fed_id, mem.id)
+ if fban:
+ check_and_ban(update, chat, mem.id, fbanreason)
+
+ # this is optional for now because it needs to check if user is in the chat
+ # and I rather not make more requests to tg on every reply
+ # if msg.reply_to_message:
+ # user = msg.reply_to_message.from_user
+ # mem = chat.get_member(user.id)
+ # if mem.status in ["administrator", "creator", "kicked"]:
+ # return
+ # fban, fbanreason, fbantime = sql.get_fban_user(fed_id, user.id)
+ # if fban and user and not user_is_admin(chat, user.id):
+ # check_and_ban(update, chat, user.id, fbanreason)
+
+
+def check_and_ban(update, chat: Chat, user_id: int, reason: str):
+ try:
+ chat.ban_member(user_id)
update.effective_message.reply_text(
- "This user is banned in the current federation! I will remove him."
+ f"This user is banned in the current federation! I will remove him.\n{'Reason: {}'.format(reason) if reason else ''}",
+ allow_sending_without_reply = True
)
- bot.kick_chat_member(chat.id, user.id)
- return True
- else:
- return False
+ except:
+ pass
+
def __stats__():
@@ -2430,6 +2568,8 @@ def fed_user_help(update: Update, context: CallbackContext):
FED_OWNER_HELP_HANDLER = CommandHandler("fedownerhelp", fed_owner_help, run_async=True)
FED_ADMIN_HELP_HANDLER = CommandHandler("fedadminhelp", fed_admin_help, run_async=True)
FED_USER_HELP_HANDLER = CommandHandler("feduserhelp", fed_user_help, run_async=True)
+FBAN_ENFORCE = MessageHandler(Filters.all & Filters.chat_type.groups, enforce_fed, run_async=True)
+
dispatcher.add_handler(NEW_FED_HANDLER)
dispatcher.add_handler(DEL_FED_HANDLER)
@@ -2461,3 +2601,4 @@ def fed_user_help(update: Update, context: CallbackContext):
dispatcher.add_handler(FED_OWNER_HELP_HANDLER)
dispatcher.add_handler(FED_ADMIN_HELP_HANDLER)
dispatcher.add_handler(FED_USER_HELP_HANDLER)
+dispatcher.add_handler(FBAN_ENFORCE, 20)
diff --git a/AstrakoBot/modules/fun.py b/AstrakoBot/modules/fun.py
index 9087412b88..c953cc063a 100644
--- a/AstrakoBot/modules/fun.py
+++ b/AstrakoBot/modules/fun.py
@@ -5,7 +5,7 @@
import AstrakoBot.modules.fun_strings as fun_strings
from AstrakoBot import dispatcher
from AstrakoBot.modules.disable import DisableAbleCommandHandler
-from AstrakoBot.modules.helper_funcs.chat_status import is_user_admin
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot.modules.helper_funcs.extraction import extract_user
from AstrakoBot.modules.helper_funcs.misc import delete
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
@@ -13,8 +13,6 @@
from telegram.error import BadRequest
from telegram.ext import CallbackContext, run_async
-GIF_ID = "CgACAgQAAx0CSVUvGgAC7KpfWxMrgGyQs-GUUJgt-TSO8cOIDgACaAgAAlZD0VHT3Zynpr5nGxsE"
-
def runs(update: Update, context: CallbackContext):
deletion(update, context, update.effective_message.reply_text(random.choice(fun_strings.RUN_STRINGS)))
@@ -27,12 +25,17 @@ def sanitize(update: Update, context: CallbackContext):
if message.reply_to_message
else message.from_user.first_name
)
- reply_animation = (
- message.reply_to_message.reply_animation
+
+ reply_msg = (
+ message.reply_to_message
if message.reply_to_message
- else message.reply_animation
+ else message
)
- deletion(update, context, reply_animation(random.choice(fun_strings.GIFS), caption=f"*Sanitizes {name}*"))
+
+ try:
+ deletion(update, context, reply_msg.reply_animation(random.choice(fun_strings.GIFS), caption=f"*Sanitizes {name}*"))
+ except BadRequest:
+ deletion(update, context, reply_msg.reply_text(f"*Sanitizes {name}*"))
def slap(update: Update, context: CallbackContext):
@@ -54,7 +57,7 @@ def slap(update: Update, context: CallbackContext):
if isinstance(temp, list):
if temp[2] == "tmute":
- if is_user_admin(chat, message.from_user.id):
+ if user_is_admin(chat, message.from_user.id):
reply_text(temp[1])
return
@@ -137,20 +140,6 @@ def roll(update: Update, context: CallbackContext):
deletion(update, context, update.message.reply_text(random.choice(range(1, 7))))
-def shout(update: Update, context: CallbackContext):
- args = context.args
- text = " ".join(args)
- result = []
- result.append(" ".join(list(text)))
- for pos, symbol in enumerate(text[1:]):
- result.append(symbol + " " + " " * pos + symbol)
- result = list("\n".join(result))
- result[0] = text[0]
- result = "".join(result)
- msg = "```\n" + result + "```"
- deletion(update, context, update.effective_message.reply_text(msg, parse_mode="MARKDOWN"))
-
-
def toss(update: Update, context: CallbackContext):
deletion(update, context, update.message.reply_text(random.choice(fun_strings.TOSS)))
@@ -212,90 +201,6 @@ def table(update: Update, context: CallbackContext):
deletion(update, context, reply_text(random.choice(fun_strings.TABLE)))
-normiefont = [
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
-]
-weebyfont = [
- "å",
- "ä¹",
- "å",
- "å",
- "ä¹",
- "äø",
- "å¶",
- "å",
- "å·„",
- "äø",
- "éæ",
- "ä¹",
- "ä»",
- "š Ø",
- "å£",
- "å°ø",
- "ćæ",
- "å°ŗ",
- "äø",
- "äø
",
- "åµ",
- "ćŖ",
- "å±±",
- "ä¹",
- "äø«",
- "ä¹",
-]
-
-
-def weebify(update: Update, context: CallbackContext):
- args = context.args
- message = update.effective_message
- string = ""
-
- if message.reply_to_message:
- string = message.reply_to_message.text.lower().replace(" ", " ")
-
- if args:
- string = " ".join(args).lower()
-
- if not string:
- deletion(update, context, message.reply_text("Usage is `/weebify `", parse_mode=ParseMode.MARKDOWN))
- return
-
- for normiecharacter in string:
- if normiecharacter in normiefont:
- weebycharacter = weebyfont[normiefont.index(normiecharacter)]
- string = string.replace(normiecharacter, weebycharacter)
-
- if message.reply_to_message:
- deletion(update, context, message.reply_to_message.reply_text(string))
- else:
- deletion(update, context, message.reply_text(string))
-
-
def deletion(update: Update, context: CallbackContext, delmsg):
chat = update.effective_chat
cleartime = get_clearcmd(chat.id, "fun")
@@ -335,11 +240,7 @@ def deletion(update: Update, context: CallbackContext, delmsg):
DECIDE_HANDLER = DisableAbleCommandHandler("decide", decide, run_async=True)
EIGHTBALL_HANDLER = DisableAbleCommandHandler("8ball", eightball, run_async=True)
TABLE_HANDLER = DisableAbleCommandHandler("table", table, run_async=True)
-SHOUT_HANDLER = DisableAbleCommandHandler("shout", shout, run_async=True)
-WEEBIFY_HANDLER = DisableAbleCommandHandler("weebify", weebify, run_async=True)
-dispatcher.add_handler(WEEBIFY_HANDLER)
-dispatcher.add_handler(SHOUT_HANDLER)
dispatcher.add_handler(SANITIZE_HANDLER)
dispatcher.add_handler(RUNS_HANDLER)
dispatcher.add_handler(SLAP_HANDLER)
@@ -366,8 +267,6 @@ def deletion(update: Update, context: CallbackContext, delmsg):
"table",
"pat",
"sanitize",
- "shout",
- "weebify",
"8ball",
]
__handlers__ = [
@@ -382,7 +281,5 @@ def deletion(update: Update, context: CallbackContext, delmsg):
DECIDE_HANDLER,
TABLE_HANDLER,
SANITIZE_HANDLER,
- SHOUT_HANDLER,
- WEEBIFY_HANDLER,
EIGHTBALL_HANDLER,
]
diff --git a/AstrakoBot/modules/fun_strings.py b/AstrakoBot/modules/fun_strings.py
index 02fced88ae..c090aabd25 100644
--- a/AstrakoBot/modules/fun_strings.py
+++ b/AstrakoBot/modules/fun_strings.py
@@ -20,9 +20,9 @@
)
GIFS = [
- "CgACAgQAAx0CSVUvGgAC7KpfWxMrgGyQs-GUUJgt-TSO8cOIDgACaAgAAlZD0VHT3Zynpr5nGxsE",
- "CgACAgUAAx0CU_rCTAABAjdgX1s4NVaeCls6YaH3p43vgdCRwQIAAqsAA4P_MFUYQhyoR-kgpRsE",
- "CgACAgUAAx0CU_rCTAABAjdSX1s3fq5iEJ64YeQLKI8cD7CSuSEAAlUBAAJu09hW5iqWB0hTPD4bBA",
+ "CgACAgUAAxkBAAM5Z9v5Sb4bJusBpEGzUII4mkJy8kkAAqsAA4P_MFVYEKwLUZWtHTYE",
+ "CgACAgUAAxkBAAM7Z9v5dv9WG5PlhxgWzj7Tj5l3TtUAAlUBAAJu09hWKztDjBmVh5k2BA",
+ "CgACAgQAAxkBAAM9Z9v5h7FGfqljNG2mBgqsdfq32h8AAmgIAAJWQ9FRSM-c1Iswcac2BA",
]
SLAP_SAITAMA_TEMPLATES = (
diff --git a/AstrakoBot/modules/github.py b/AstrakoBot/modules/github.py
index 079ff45bb0..991b364052 100755
--- a/AstrakoBot/modules/github.py
+++ b/AstrakoBot/modules/github.py
@@ -33,7 +33,7 @@
def getphh(index):
- recentRelease = api.getReleaseData(api.getData("phhusson/treble_experimentations"), index)
+ recentRelease = api.getReleaseData(api.getData("TrebleDroid/treble_experimentations"), index)
if recentRelease is None:
return "The specified release could not be found"
author = api.getAuthor(recentRelease)
@@ -60,7 +60,7 @@ def getphh(index):
# do not async
def getData(url, index):
if not api.getData(url):
- return "Invalid / combo"
+ return "Invalid / combo.\nAre you sure this repository has a release file?"
recentRelease = api.getReleaseData(api.getData(url), index)
if recentRelease is None:
return "The specified release could not be found"
@@ -207,7 +207,7 @@ def delRepo(update: Update, context: CallbackContext):
def listRepo(update: Update, context: CallbackContext):
chat_id = update.effective_chat.id
chat = update.effective_chat
- chat_name = chat.title or chat.first or chat.username
+ chat_name = chat.title or chat.first_name or chat.username
repo_list = sql.get_all_repos(str(chat_id))
msg = "*List of repo shotcuts in {}:*\n"
des = "You can get repo shortcuts by using `/fetch repo`, or `&repo`.\n"
diff --git a/AstrakoBot/modules/global_bans.py b/AstrakoBot/modules/global_bans.py
index e2365ebc9a..b31ac68116 100644
--- a/AstrakoBot/modules/global_bans.py
+++ b/AstrakoBot/modules/global_bans.py
@@ -15,6 +15,7 @@
from telegram.utils.helpers import mention_html
import AstrakoBot.modules.sql.global_bans_sql as sql
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member, user_is_admin
from AstrakoBot.modules.sql.users_sql import get_user_com_chats
from AstrakoBot import (
DEV_USERS,
@@ -30,7 +31,6 @@
dispatcher,
)
from AstrakoBot.modules.helper_funcs.chat_status import (
- is_user_admin,
support_plus,
user_admin,
)
@@ -87,6 +87,12 @@ def gban(update: Update, context: CallbackContext):
)
return
+ if not reason:
+ message.reply_text(
+ "You must provide a reason!"
+ )
+ return
+
if int(user_id) in DEV_USERS:
message.reply_text("This is a developer user\nI can't act against our own.")
return
@@ -208,7 +214,7 @@ def gban(update: Update, context: CallbackContext):
continue
try:
- bot.kick_chat_member(chat_id, user_id)
+ bot.ban_chat_member(chat_id, user_id)
gbanned_chats += 1
except BadRequest as excp:
@@ -373,10 +379,17 @@ def ungban(update: Update, context: CallbackContext):
if ungban_time > 60:
ungban_time = round((ungban_time / 60), 2)
- message.reply_text(f"Person has been un-gbanned. Took {ungban_time} min")
+ text = (f"Person has been un-gbanned. Took {ungban_time} min")
else:
- message.reply_text(f"Person has been un-gbanned. Took {ungban_time} sec")
+ text = (f"Person has been un-gbanned. Took {ungban_time} sec")
+ try:
+ message.reply_text(text)
+ except BadRequest:
+ context.bot.send_message(
+ chat_id=update.effective_chat.id,
+ text=text
+ )
@support_plus
def gbanlist(update: Update, context: CallbackContext):
@@ -414,20 +427,28 @@ def check_and_ban(update, user_id, should_message=True):
sw_ban = None
if sw_ban:
- update.effective_chat.kick_member(user_id)
+ update.effective_chat.ban_member(user_id)
if should_message:
- update.effective_message.reply_text(
+ text = (
f"Alert: this user is globally banned.\n"
f"*bans them from here*.\n"
f"Appeal chat: {SPAMWATCH_SUPPORT_CHAT}\n"
f"User ID: {sw_ban.id}\n"
- f"Ban Reason: {html.escape(sw_ban.reason)}",
- parse_mode=ParseMode.HTML,
+ f"Ban Reason: {html.escape(sw_ban.reason)}"
)
+ try:
+ update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
+ except BadRequest:
+ update.effective_chat.bot.send_message(
+ chat_id=update.effective_chat.id,
+ text=text,
+ parse_mode=ParseMode.HTML
+ )
+
return
if sql.is_user_gbanned(user_id):
- update.effective_chat.kick_member(user_id)
+ update.effective_chat.ban_member(user_id)
if should_message:
text = (
f"Alert: this user is globally banned.\n"
@@ -438,36 +459,38 @@ def check_and_ban(update, user_id, should_message=True):
user = sql.get_gbanned_user(user_id)
if user.reason:
text += f"\nBan Reason: {html.escape(user.reason)}"
- update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
+ try:
+ update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
+ except BadRequest:
+ update.effective_chat.bot.send_message(
+ chat_id=update.effective_chat.id,
+ text=text,
+ parse_mode=ParseMode.HTML
+ )
+def enforce_gban(update: Update, _: CallbackContext):
+ chat = update.effective_chat
+ if not get_bot_member(chat.id).can_restrict_members:
+ return
-def enforce_gban(update: Update, context: CallbackContext):
- # Not using @restrict handler to avoid spamming - just ignore if cant gban.
- bot = context.bot
- try:
- restrict_permission = update.effective_chat.get_member(
- bot.id
- ).can_restrict_members
- except Unauthorized:
+ if not sql.does_chat_gban(update.effective_chat.id):
return
- if sql.does_chat_gban(update.effective_chat.id) and restrict_permission:
- user = update.effective_user
- chat = update.effective_chat
- msg = update.effective_message
- if user and not is_user_admin(chat, user.id):
- check_and_ban(update, user.id)
- return
+ user = update.effective_user
+ msg = update.effective_message
+
+ if user and not user_is_admin(chat, user.id):
+ check_and_ban(update, user.id)
- if msg.new_chat_members:
- new_members = update.effective_message.new_chat_members
- for mem in new_members:
- check_and_ban(update, mem.id)
+ if msg.new_chat_members:
+ new_members = update.effective_message.new_chat_members
+ for mem in new_members:
+ check_and_ban(update, mem.id)
- if msg.reply_to_message:
- user = msg.reply_to_message.from_user
- if user and not is_user_admin(chat, user.id):
- check_and_ban(update, user.id, should_message=False)
+ if msg.reply_to_message:
+ user = msg.reply_to_message.from_user
+ if user and not user_is_admin(chat, user.id):
+ check_and_ban(update, user.id, should_message=False)
@user_admin
diff --git a/AstrakoBot/modules/gtranslator.py b/AstrakoBot/modules/gtranslator.py
index 3808860796..5e7390dca0 100644
--- a/AstrakoBot/modules/gtranslator.py
+++ b/AstrakoBot/modules/gtranslator.py
@@ -1,88 +1,162 @@
-from emoji import UNICODE_EMOJI
-from google_trans_new import LANGUAGES, google_translator
from telegram import ParseMode, Update
from telegram.ext import CallbackContext, run_async
+import requests
+import urllib.parse
from AstrakoBot import dispatcher
from AstrakoBot.modules.disable import DisableAbleCommandHandler
from AstrakoBot.modules.helper_funcs.misc import delete
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
+languages = {
+ "af": "Afrikaans",
+ "sq": "Albanian",
+ "am": "Amharic",
+ "ar": "Arabic",
+ "hy": "Armenian",
+ "az": "Azerbaijani",
+ "eu": "Basque",
+ "be": "Belarusian",
+ "bn": "Bengali",
+ "bs": "Bosnian",
+ "bg": "Bulgarian",
+ "ca": "Catalan",
+ "ceb": "Cebuano",
+ "ny": "Chichewa",
+ "zh-cn": "Chinese",
+ "co": "Corsican",
+ "hr": "Croatian",
+ "cs": "Czech",
+ "da": "Danish",
+ "nl": "Dutch",
+ "en": "English",
+ "eo": "Esperanto",
+ "et": "Estonian",
+ "tl": "Filipino",
+ "fi": "Finnish",
+ "fr": "French",
+ "fy": "Frisian",
+ "gl": "Galician",
+ "ka": "Georgian",
+ "de": "German",
+ "el": "Greek",
+ "gu": "Gujarati",
+ "ht": "Haitian Creole",
+ "ha": "Hausa",
+ "haw": "Hawaiian",
+ "iw": "Hebrew",
+ "hi": "Hindi",
+ "hmn": "Hmong",
+ "hu": "Hungarian",
+ "is": "Icelandic",
+ "ig": "Igbo",
+ "id": "Indonesian",
+ "ga": "Irish",
+ "it": "Italian",
+ "ja": "Japanese",
+ "jw": "Javanese",
+ "kn": "Kannada",
+ "kk": "Kazakh",
+ "km": "Khmer",
+ "rw": "Kinyarwanda",
+ "ko": "Korean",
+ "ku": "Kurdish",
+ "ky": "Kyrgyz",
+ "lo": "Lao",
+ "la": "Latin",
+ "lv": "Latvian",
+ "lt": "Lithuanian",
+ "lb": "Luxembourgish",
+ "mk": "Macedonian",
+ "mg": "Malagasy",
+ "ms": "Malay",
+ "ml": "Malayalam",
+ "mt": "Maltese",
+ "mi": "Maori",
+ "mr": "Marathi",
+ "mn": "Mongolian",
+ "my": "Myanmar",
+ "ne": "Nepali",
+ "no": "Norwegian",
+ "or": "Odia",
+ "ps": "Pashto",
+ "fa": "Persian",
+ "pl": "Polish",
+ "pt": "Portuguese",
+ "pa": "Punjabi",
+ "ro": "Romanian",
+ "ru": "Russian",
+ "sm": "Samoan",
+ "gd": "Scots Gaelic",
+ "sr": "Serbian",
+ "st": "Sesotho",
+ "sn": "Shona",
+ "sd": "Sindhi",
+ "si": "Sinhala",
+ "sk": "Slovak",
+ "sl": "Slovenian",
+ "so": "Somali",
+ "es": "Spanish",
+ "su": "Sundanese",
+ "sw": "Swahili",
+ "sv": "Swedish",
+ "tg": "Tajik",
+ "ta": "Tamil",
+ "tt": "Tatar",
+ "te": "Telugu",
+ "th": "Thai",
+ "tr": "Turkish",
+ "tk": "Turkmen",
+ "uk": "Ukrainian",
+ "ur": "Urdu",
+ "ug": "Uyghur",
+ "uz": "Uzbek",
+ "vi": "Vietnamese",
+ "cy": "Welsh",
+ "xh": "Xhosa",
+ "yi": "Yiddish",
+ "yo": "Yoruba",
+ "zu": "Zulu"
+}
+
+
+def tr(text, srcLang, targetLang):
+ text = urllib.parse.quote_plus(text)
+
+ # Try to get response at least 3 times
+ for _ in range(3):
+ try:
+ response = requests.get("https://translate.googleapis.com/translate_a/single?client=gtx&sl={}&tl={}&dt=t&q={}".format(srcLang, targetLang, text), timeout=4)
+ break
+ except (requests.exceptions.Timeout, requests.exceptions.ConnectionError):
+ pass
+ else:
+ return ""
+
+ if response.status_code != 200:
+ return ""
+ data = response.json()
+ translated_text = ""
+ for item in data[0]:
+ translated_text += item[0]
+ return translated_text
def totranslate(update: Update, context: CallbackContext):
message = update.effective_message
chat = update.effective_chat
- problem_lang_code = []
+ args = context.args
+ reply = message.reply_to_message
text = ""
- for key in LANGUAGES:
- if "-" in key:
- problem_lang_code.append(key)
-
- try:
- if message.reply_to_message:
- args = update.effective_message.text.split(None, 1)
- if message.reply_to_message.text:
- text = message.reply_to_message.text
- elif message.reply_to_message.caption:
- text = message.reply_to_message.caption
-
- try:
- source_lang = args[1].split(None, 1)[0]
- except (IndexError, AttributeError):
- source_lang = "en"
-
- else:
- args = update.effective_message.text.split(None, 2)
- text = args[2]
- source_lang = args[1]
-
- if source_lang.count("-") == 2:
- for lang in problem_lang_code:
- if lang in source_lang:
- if source_lang.startswith(lang):
- dest_lang = source_lang.rsplit("-", 1)[1]
- source_lang = source_lang.rsplit("-", 1)[0]
- else:
- dest_lang = source_lang.split("-", 1)[1]
- source_lang = source_lang.split("-", 1)[0]
- elif source_lang.count("-") == 1:
- for lang in problem_lang_code:
- if lang in source_lang:
- dest_lang = source_lang
- source_lang = None
- break
- if dest_lang is None:
- dest_lang = source_lang.split("-")[1]
- source_lang = source_lang.split("-")[0]
- else:
- dest_lang = source_lang
- source_lang = None
-
- exclude_list = UNICODE_EMOJI.keys()
- for emoji in exclude_list:
- if emoji in text:
- text = text.replace(emoji, "")
-
- trl = google_translator()
- if source_lang is None:
- detection = trl.detect(text)
- trans_str = trl.translate(text, lang_tgt=dest_lang)
- delmsg = message.reply_text(
- f"Translated from `{detection[0]}` to `{dest_lang}`:\n`{trans_str}`",
- parse_mode=ParseMode.MARKDOWN,
- )
- else:
- trans_str = trl.translate(text, lang_tgt=dest_lang, lang_src=source_lang)
- delmsg = message.reply_text(
- f"Translated from `{source_lang}` to `{dest_lang}`:\n`{trans_str}`",
- parse_mode=ParseMode.MARKDOWN,
- )
-
- deletion(update, context, delmsg)
-
- except IndexError:
- delmsg = update.effective_message.reply_text(
- "Reply to messages or write messages from other languages āāfor translating into the intended language\n\n"
+ if reply:
+ if reply.document or reply.video or reply.animation or reply.photo:
+ if reply.caption:
+ text = reply.caption
+ elif reply.text:
+ text = reply.text
+
+ if not text:
+ delmsg = message.reply_text(
+ "Reply to messages from other languages for translating into the intended language\n\n"
"Example: `/tr en-ml` to translate from English to Malayalam\n"
"Or use: `/tr ml` for automatic detection and translating it into Malayalam.\n"
"See [List of Language Codes](t.me/OnePunchSupport/12823) for a list of language codes.",
@@ -90,11 +164,57 @@ def totranslate(update: Update, context: CallbackContext):
disable_web_page_preview=True,
)
deletion(update, context, delmsg)
- except ValueError:
- delmsg = update.effective_message.reply_text("The intended language is not found!")
- deletion(update, context, delmsg)
+ return
+ if not args:
+ srcLang = "auto"
+ targetLang = "en" # Default to english if no arguments were given
+ elif "-" in args[0]:
+ try:
+ srcLang = args[0].split('-')[0]
+ except:
+ srcLang = "auto"
+ try:
+ targetLang = args[0].split('-')[1]
+ except:
+ message.reply_text("Well damn, that does not look like a target language")
+ return
else:
+ try:
+ srcLang = args[0]
+ targetLang = args[1]
+ except:
+ targetLang = args[0]
+ srcLang = "auto"
+
+ # Convert them to lowercase so we don't get issues
+ srcLang = srcLang.lower()
+ targetLang = targetLang.lower()
+
+
+ # If target language is 'english', set it to 'en'
+ for key, value in languages.items():
+ if value.lower() == targetLang:
+ targetLang = key
+ break
+
+ # If source language is 'english', set it to 'en'
+ for key, value in languages.items():
+ if value.lower() == srcLang:
+ srcLang = key
+ break
+
+ # Make sure language actually exists
+ if targetLang not in languages:
+ message.reply_text("That does not look like a valid language.")
+ return
+
+ reply_msg = message.reply_text("Translating to {}...".format(languages[targetLang]))
+ translated = tr(text, srcLang, targetLang)
+ if not translated:
+ reply_msg.edit_text("Failed to translate to {}. Try again in a few seconds or try another target language!".format(languages[targetLang]))
return
+ reply_msg.edit_text("Translated to {}:\n\n".format(languages[targetLang]) + translated)
+ return
def deletion(update: Update, context: CallbackContext, delmsg):
diff --git a/AstrakoBot/modules/helper_funcs/admin_status.py b/AstrakoBot/modules/helper_funcs/admin_status.py
new file mode 100644
index 0000000000..1d9cdf2514
--- /dev/null
+++ b/AstrakoBot/modules/helper_funcs/admin_status.py
@@ -0,0 +1,102 @@
+from threading import RLock
+
+from cachetools import TTLCache
+
+from telegram import Chat, ChatMember, TelegramError, Update
+from telegram.ext import CallbackContext, ChatMemberHandler
+from telegram.error import Unauthorized
+
+from AstrakoBot import SUDO_USERS, dispatcher
+
+BOT_ADMIN_CACHE = TTLCache(maxsize = 512, ttl = 60 * 30)
+USER_ADMIN_CACHE = TTLCache(maxsize = 512, ttl = 60 * 30)
+RLOCK = RLock()
+
+
+def get_bot_member(chat_id: int) -> ChatMember:
+ try:
+ return BOT_ADMIN_CACHE[chat_id]
+ except KeyError:
+ try:
+ mem = dispatcher.bot.getChatMember(chat_id, dispatcher.bot.id)
+ BOT_ADMIN_CACHE[chat_id] = mem
+ return mem
+ except:
+ bot_user = dispatcher.bot.get_me()
+ ghost = ChatMember(
+ user=bot_user,
+ status='member',
+ can_be_edited=False,
+ is_anonymous=False,
+ can_manage_chat=False,
+ can_delete_messages=False,
+ can_manage_video_chats=False,
+ can_restrict_members=False,
+ can_promote_members=False,
+ can_change_info=False,
+ can_invite_users=False,
+ can_post_messages=False,
+ can_edit_messages=False,
+ can_pin_messages=False,
+ can_manage_topics=False,
+ can_send_messages=False,
+ )
+ return ghost
+
+
+def user_is_admin(chat: Chat, user_id: int) -> bool:
+ if chat.type == "private" or user_id in SUDO_USERS:
+ return True
+
+ member: ChatMember = get_mem_from_cache(user_id, chat.id)
+
+ if not member: # not in cache so not an admin
+ return False
+
+ return member.status in ["administrator", "creator"] # check if user is admin
+
+
+def get_mem_from_cache(user_id: int, chat_id: int) -> ChatMember:
+ with RLOCK:
+ try:
+ for i in USER_ADMIN_CACHE[chat_id]:
+ if i.user.id == user_id:
+ return i
+
+ except KeyError:
+ try:
+ admins = dispatcher.bot.getChatAdministrators(chat_id)
+ except Unauthorized:
+ return None
+ USER_ADMIN_CACHE[chat_id] = admins
+ for i in admins:
+ if i.user.id == user_id:
+ return i
+
+
+def admincacheupdates(update: Update, _: CallbackContext):
+ try:
+ oldstat = update.chat_member.old_chat_member.status
+ newstat = update.chat_member.new_chat_member.status
+ except AttributeError:
+ return
+ if (
+ oldstat == "administrator"
+ and newstat != "administrator"
+ or oldstat != "administrator"
+ and newstat == "administrator"
+ ):
+
+ USER_ADMIN_CACHE[update.effective_chat.id] = update.effective_chat.get_administrators()
+
+
+def botstatchanged(update: Update, _: CallbackContext):
+ if update.effective_chat.type != "private":
+ try:
+ BOT_ADMIN_CACHE[update.effective_chat.id] = update.effective_chat.get_member(dispatcher.bot.id)
+ except TelegramError:
+ pass
+
+
+dispatcher.add_handler(ChatMemberHandler(botstatchanged, ChatMemberHandler.MY_CHAT_MEMBER, run_async=True), group=-20)
+dispatcher.add_handler(ChatMemberHandler(admincacheupdates, ChatMemberHandler.CHAT_MEMBER, run_async=True), group=-21)
diff --git a/AstrakoBot/modules/helper_funcs/chat_status.py b/AstrakoBot/modules/helper_funcs/chat_status.py
index 288b4dd56d..1d2021b454 100644
--- a/AstrakoBot/modules/helper_funcs/chat_status.py
+++ b/AstrakoBot/modules/helper_funcs/chat_status.py
@@ -15,6 +15,8 @@
from telegram import Chat, ChatMember, ParseMode, Update
from telegram.ext import CallbackContext
+from telegram.error import Unauthorized
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member, user_is_admin
# stores admemes in memory for 10 min.
ADMIN_CACHE = TTLCache(maxsize=512, ttl=60 * 10, timer=perf_counter)
@@ -33,45 +35,18 @@ def is_sudo_plus(chat: Chat, user_id: int, member: ChatMember = None) -> bool:
return user_id in SUDO_USERS or user_id in DEV_USERS
-def is_user_admin(chat: Chat, user_id: int, member: ChatMember = None) -> bool:
- if (
- chat.type == "private"
- or user_id in SUDO_USERS
- or user_id in DEV_USERS
- or chat.all_members_are_administrators
- or user_id in [1087968824]
- ): # Count telegram and Group Anonymous as admin
- return True
- if not member:
- with THREAD_LOCK:
- # try to fetch from cache first.
- try:
- return user_id in ADMIN_CACHE[chat.id]
- except KeyError:
- # keyerror happend means cache is deleted,
- # so query bot api again and return user status
- # while saving it in cache for future useage...
- chat_admins = dispatcher.bot.getChatAdministrators(chat.id)
- admin_list = [x.user.id for x in chat_admins]
- ADMIN_CACHE[chat.id] = admin_list
-
- return user_id in admin_list
- else:
- return member.status in ("administrator", "creator")
-
-
def is_bot_admin(chat: Chat, bot_id: int, bot_member: ChatMember = None) -> bool:
if chat.type == "private" or chat.all_members_are_administrators:
return True
if not bot_member:
- bot_member = chat.get_member(bot_id)
+ bot_member = get_bot_member(chat.id)
return bot_member.status in ("administrator", "creator")
def can_delete(chat: Chat, bot_id: int) -> bool:
- return chat.get_member(bot_id).can_delete_messages
+ return get_bot_member(chat.id).can_delete_messages
def is_user_ban_protected(chat: Chat, user_id: int, member: ChatMember = None) -> bool:
@@ -135,15 +110,22 @@ def is_dev_plus_func(update: Update, context: CallbackContext, *args, **kwargs):
update.effective_message.delete()
except:
pass
- else:
- update.effective_message.reply_text(
- "This is a developer restricted command."
- " You do not have permissions to run this."
- )
return is_dev_plus_func
+def sudo_plus_silent(func):
+ @wraps(func)
+ def is_sudo_plus_func(update: Update, context: CallbackContext, *args, **kwargs):
+ bot = context.bot
+ user = update.effective_user
+ chat = update.effective_chat
+
+ if user and is_sudo_plus(chat, user.id):
+ return func(update, context, *args, **kwargs)
+
+ return is_sudo_plus_func
+
def sudo_plus(func):
@wraps(func)
def is_sudo_plus_func(update: Update, context: CallbackContext, *args, **kwargs):
@@ -160,10 +142,6 @@ def is_sudo_plus_func(update: Update, context: CallbackContext, *args, **kwargs)
update.effective_message.delete()
except:
pass
- else:
- update.effective_message.reply_text(
- "Who dis non-admin telling me what to do? You want a punch?"
- )
return is_sudo_plus_func
@@ -212,7 +190,7 @@ def is_admin(update: Update, context: CallbackContext, *args, **kwargs):
user = update.effective_user
chat = update.effective_chat
- if user and is_user_admin(chat, user.id):
+ if user and user_is_admin(chat, user.id):
return func(update, context, *args, **kwargs)
elif not user:
pass
@@ -238,7 +216,7 @@ def is_not_admin_no_reply(
user = update.effective_user
chat = update.effective_chat
- if user and is_user_admin(chat, user.id):
+ if user and user_is_admin(chat, user.id):
return func(update, context, *args, **kwargs)
elif not user:
pass
@@ -258,7 +236,7 @@ def is_not_admin(update: Update, context: CallbackContext, *args, **kwargs):
user = update.effective_user
chat = update.effective_chat
- if user and not is_user_admin(chat, user.id):
+ if user and not user_is_admin(chat, user.id):
return func(update, context, *args, **kwargs)
elif not user:
pass
@@ -323,7 +301,7 @@ def pin_rights(update: Update, context: CallbackContext, *args, **kwargs):
else:
cant_pin = f"I can't pin messages in {update_chat_title}!\nMake sure I'm admin and can pin messages there."
- if chat.get_member(bot.id).can_pin_messages:
+ if get_bot_member(chat.id).can_pin_messages:
return func(update, context, *args, **kwargs)
else:
update.effective_message.reply_text(cant_pin, parse_mode=ParseMode.HTML)
@@ -347,7 +325,7 @@ def promote_rights(update: Update, context: CallbackContext, *args, **kwargs):
f"Make sure I'm admin there and can appoint new admins."
)
- if chat.get_member(bot.id).can_promote_members:
+ if get_bot_member(chat.id).can_promote_members:
return func(update, context, *args, **kwargs)
else:
update.effective_message.reply_text(cant_promote, parse_mode=ParseMode.HTML)
@@ -368,7 +346,7 @@ def restrict_rights(update: Update, context: CallbackContext, *args, **kwargs):
else:
cant_restrict = f"I can't restrict people in {update_chat_title}!\nMake sure I'm admin there and can restrict users."
- if chat.get_member(bot.id).can_restrict_members:
+ if get_bot_member(chat.id).can_restrict_members:
return func(update, context, *args, **kwargs)
else:
update.effective_message.reply_text(
@@ -401,6 +379,9 @@ def user_is_banhammer(update: Update, context: CallbackContext, *args, **kwargs)
def connection_status(func):
@wraps(func)
def connected_status(update: Update, context: CallbackContext, *args, **kwargs):
+ if not update.effective_user:
+ return connected_status
+
conn = connected(
context.bot,
update,
diff --git a/AstrakoBot/modules/helper_funcs/extraction.py b/AstrakoBot/modules/helper_funcs/extraction.py
index d8b63fc199..0640b5acdd 100644
--- a/AstrakoBot/modules/helper_funcs/extraction.py
+++ b/AstrakoBot/modules/helper_funcs/extraction.py
@@ -12,6 +12,8 @@ def id_from_reply(message):
return None, None
user_id = prev_message.from_user.id
res = message.text.split(None, 1)
+ if prev_message.sender_chat:
+ user_id = prev_message.sender_chat.id
if len(res) < 2:
return user_id, ""
return user_id, res[1]
diff --git a/AstrakoBot/modules/helper_funcs/filters.py b/AstrakoBot/modules/helper_funcs/filters.py
index 035a8bec01..c892550ea3 100644
--- a/AstrakoBot/modules/helper_funcs/filters.py
+++ b/AstrakoBot/modules/helper_funcs/filters.py
@@ -45,3 +45,11 @@ def filter(self, message: Message):
)
has_text = _HasText()
+
+ class _IsAnonChannel(MessageFilter):
+ def filter(self, message: Message):
+ if (message.from_user and message.from_user.id == 136817688 ):
+ return True
+ return False
+
+ is_anon_channel = _IsAnonChannel()
diff --git a/AstrakoBot/modules/helper_funcs/msg_types.py b/AstrakoBot/modules/helper_funcs/msg_types.py
index 446a53f60e..b8ac4c34fd 100644
--- a/AstrakoBot/modules/helper_funcs/msg_types.py
+++ b/AstrakoBot/modules/helper_funcs/msg_types.py
@@ -137,20 +137,19 @@ def get_welcome_type(msg: Message):
data_type = Types.VIDEO_NOTE
buttons = []
+ argumen = None
# determine what the contents of the filter are - text, image, sticker, etc
- if args:
- if msg.reply_to_message:
- argumen = (
- msg.reply_to_message.caption if msg.reply_to_message.caption else ""
- )
- offset = 0 # offset is no need since target was in reply
- entities = msg.reply_to_message.parse_entities()
- else:
- argumen = args[1]
- offset = len(argumen) - len(
- msg.text
- ) # set correct offset relative to command + notename
- entities = msg.parse_entities()
+ if msg.reply_to_message:
+ argumen = msg.reply_to_message.caption or msg.reply_to_message.text or ""
+ offset = 0 # offset is no need since target was in reply
+ entities = msg.reply_to_message.parse_entities()
+ elif args and len(args) > 1:
+ argumen = args[1]
+ offset = len(argumen) - len(
+ msg.text
+ ) # set correct offset relative to command + notename
+ entities = msg.parse_entities()
+ if argumen:
text, buttons = button_markdown_parser(
argumen, entities=entities, offset=offset
)
diff --git a/AstrakoBot/modules/helper_funcs/string_handling.py b/AstrakoBot/modules/helper_funcs/string_handling.py
index 4164c7d241..2c6aa14b10 100644
--- a/AstrakoBot/modules/helper_funcs/string_handling.py
+++ b/AstrakoBot/modules/helper_funcs/string_handling.py
@@ -261,24 +261,37 @@ def extract_time(message, time_val):
return ""
if unit == "m":
- bantime = int(time.time() + int(time_num) * 60)
+ bantime = int(time.time()) + int(time_num) * 60
elif unit == "h":
- bantime = int(time.time() + int(time_num) * 60 * 60)
+ bantime = int(time.time()) + int(time_num) * 60 * 60
elif unit == "d":
- bantime = int(time.time() + int(time_num) * 24 * 60 * 60)
+ bantime = int(time.time()) + int(time_num) * 24 * 60 * 60
else:
# how even...?
return ""
return bantime
else:
message.reply_text(
- "Invalid time type specified. Expected m,h, or d, got: {}".format(
+ "Invalid time type specified. Expected m, h, or d, got: {}".format(
time_val[-1]
)
)
return ""
+def parse_to_seconds(time_str):
+ if time_str.isdigit():
+ return int(time_str) or None
+
+ match = re.match(r"\s*([\d.]+)\s*([a-z]*)\s*", time_str, re.IGNORECASE)
+ if len(time_str) > 50 or not match:
+ return None
+
+ value, unit = float(match[1]), match[2].lower()
+ multipliers = {'d': 86400, 'h': 3600, 'm': 60, 's': 1}
+ return int(value * multipliers.get(unit[:1], 0)) or None
+
+
def markdown_to_html(text):
text = text.replace("*", "**")
text = text.replace("`", "```")
diff --git a/AstrakoBot/modules/linux.py b/AstrakoBot/modules/linux.py
new file mode 100644
index 0000000000..340fea7c16
--- /dev/null
+++ b/AstrakoBot/modules/linux.py
@@ -0,0 +1,77 @@
+import requests
+from AstrakoBot import dispatcher
+from telegram import Update, ParseMode
+from telegram.ext import CallbackContext, CommandHandler, run_async
+from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
+from AstrakoBot.modules.helper_funcs.misc import delete
+from datetime import datetime
+import re
+
+def linux_kernels(update: Update, context: CallbackContext):
+ chat = update.effective_chat
+ headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0"}
+
+ try:
+ response = requests.get("https://www.kernel.org/releases.json", headers=headers)
+ response.raise_for_status()
+ data = response.json()
+ except Exception as e:
+ update.effective_message.reply_text(f"Error fetching kernel data: {str(e)}")
+ return
+
+ releases = data.get("releases", [])
+ if not releases:
+ update.effective_message.reply_text("No kernel releases found.")
+ return
+
+ message = "Linux Kernel Versions\n\n"
+ for release in releases[:10]:
+ version = release.get("version", "Unknown")
+ moniker = release.get("moniker", "").lower()
+ timestamp = release.get("released", {}).get("timestamp", 0)
+ source_url = release.get("gitweb", "#")
+
+ category = "MAINLINE"
+ if release.get("iseol"):
+ category = "EOL"
+ elif "longterm" in moniker:
+ category = "LTS"
+ elif "stable" in moniker:
+ category = "STABLE"
+ elif "linux-next" in moniker:
+ category = "NEXT"
+
+ message += (
+ f"⢠{version}\n"
+ f" ā {category} "
+ f"({datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d')})\n\n"
+ )
+
+ message += "Source: kernel.org"
+
+ try:
+ delmsg = update.effective_message.reply_text(
+ message,
+ parse_mode=ParseMode.HTML,
+ disable_web_page_preview=True
+ )
+ except Exception as e:
+ update.effective_message.reply_text(f"Error formatting message: {str(e)}")
+ return
+
+ cleartime = get_clearcmd(chat.id, "kernels")
+ if cleartime:
+ context.dispatcher.run_async(delete, delmsg, cleartime.time)
+
+__help__ = """
+*Available commands:*\n
+*Linux Kernel:*
+⢠`/kernels`: fetches linux kernels information\n
+"""
+
+KERNELS_HANDLER = CommandHandler("kernels", linux_kernels, run_async=True)
+dispatcher.add_handler(KERNELS_HANDLER)
+
+__mod_name__ = "Linux"
+__command_list__ = ["kernels"]
+__handlers__ = [KERNELS_HANDLER]
diff --git a/AstrakoBot/modules/locks.py b/AstrakoBot/modules/locks.py
index 17a9117f38..2d8c6cfebc 100644
--- a/AstrakoBot/modules/locks.py
+++ b/AstrakoBot/modules/locks.py
@@ -1,5 +1,6 @@
import html
+import telegram.ext as tg
from telegram import Message, Chat, ParseMode, MessageEntity, Update
from telegram import TelegramError, ChatPermissions
from telegram.error import BadRequest
@@ -10,11 +11,11 @@
from alphabet_detector import AlphabetDetector
import AstrakoBot.modules.sql.locks_sql as sql
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from AstrakoBot import dispatcher, SUDO_USERS, LOGGER
from AstrakoBot.modules.disable import DisableAbleCommandHandler
from AstrakoBot.modules.helper_funcs.chat_status import (
can_delete,
- is_user_admin,
user_not_admin,
is_bot_admin,
user_admin,
@@ -23,6 +24,7 @@
from AstrakoBot.modules.connection import connected
from AstrakoBot.modules.sql.approve_sql import is_approved
from AstrakoBot.modules.helper_funcs.alternate import send_message, typing_action
+from AstrakoBot.modules.helper_funcs.filters import CustomFilters
ad = AlphabetDetector()
@@ -40,6 +42,7 @@
"game": Filters.game,
"location": Filters.location,
"egame": Filters.dice,
+ "anonchannel": CustomFilters.is_anon_channel,
"rtl": "rtl",
"button": "button",
"inline": "inline",
@@ -93,6 +96,25 @@
REST_GROUP = 2
+class CustomCommandHandler(tg.CommandHandler):
+ def __init__(self, command, callback, **kwargs):
+ super().__init__(command, callback, **kwargs)
+
+ def check_update(self, update):
+ if super().check_update(update) and not (
+ sql.is_restr_locked(update.effective_chat.id, 'messages') and not user_is_admin(update.effective_chat,
+ update.effective_user.id)):
+ args = update.effective_message.text.split()[1:]
+ filter_result = self.filters(update)
+ if filter_result:
+ return args, filter_result
+ else:
+ return False
+
+
+CommandHandler = CustomCommandHandler
+
+
# NOT ASYNC
def restr_members(
bot, chat_id, members, messages=False, media=False, other=False, previews=False
@@ -100,15 +122,18 @@ def restr_members(
for mem in members:
if mem.user in SUDO_USERS:
pass
+ elif mem.user == 777000 or mem.user == 1087968824:
+ pass
try:
bot.restrict_chat_member(
chat_id,
mem.user,
+ permissions=ChatPermissions(
can_send_messages=messages,
can_send_media_messages=media,
can_send_other_messages=other,
can_add_web_page_previews=previews,
- )
+ ))
except TelegramError:
pass
@@ -218,6 +243,18 @@ def lock(update: Update, context: CallbackContext) -> str:
),
)
+ context.bot.restrict_chat_member(chat.id, int(777000), permissions=ChatPermissions(
+ can_send_messages=True,
+ can_send_media_messages=True,
+ can_send_other_messages=True,
+ can_add_web_page_previews=True))
+
+ context.bot.restrict_chat_member(chat.id, int(1087968824), permissions=ChatPermissions(
+ can_send_messages=True,
+ can_send_media_messages=True,
+ can_send_other_messages=True,
+ can_add_web_page_previews=True))
+
send_message(update.effective_message, text, parse_mode="markdown")
return (
"{}:"
@@ -255,7 +292,7 @@ def unlock(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
user = update.effective_user
message = update.effective_message
- if is_user_admin(chat, message.from_user.id):
+ if user_is_admin(chat, message.from_user.id):
if len(args) >= 1:
ltype = args[0].lower()
if ltype in LOCK_TYPES:
@@ -426,7 +463,7 @@ def del_lockables(update: Update, context: CallbackContext):
)
return
- chat.kick_member(new_mem.id)
+ chat.ban_member(new_mem.id)
send_message(
update.effective_message,
"Only admins are allowed to add bots in this chat! Get outta here.",
@@ -469,6 +506,7 @@ def build_lock_message(chat_id):
locklist.append("button = `{}`".format(locks.button))
locklist.append("egame = `{}`".format(locks.egame))
locklist.append("inline = `{}`".format(locks.inline))
+ locklist.append("anonchannel = `{}`".format(locks.anonchannel))
permissions = dispatcher.bot.get_chat(chat_id).permissions
permslist.append("messages = `{}`".format(permissions.can_send_messages))
permslist.append("media = `{}`".format(permissions.can_send_media_messages))
diff --git a/AstrakoBot/modules/log_channel.py b/AstrakoBot/modules/log_channel.py
index 28a0b28ffb..4ff68871a1 100644
--- a/AstrakoBot/modules/log_channel.py
+++ b/AstrakoBot/modules/log_channel.py
@@ -39,9 +39,16 @@ def log_action(
result += "\nEvent Stamp: {}".format(
datetime.utcnow().strftime(datetime_fmt)
)
+ try:
+ if message and chat.type == chat.SUPERGROUP:
+ if chat.username:
+ result += f'\nLink: click here'
+ else:
+ cid = str(chat.id).replace("-100", '')
+ result += f'\nLink: click here'
+ except AttributeError:
+ result += '\nLink: No link for manual actions.'
- if message.chat.type == chat.SUPERGROUP and message.chat.username:
- result += f'\nLink: click here'
log_chat = sql.get_chat_log_channel(chat.id)
if log_chat:
send_log(context, log_chat, chat.id, result)
@@ -109,13 +116,16 @@ def logging(update: Update, context: CallbackContext):
log_channel = sql.get_chat_log_channel(chat.id)
if log_channel:
- log_channel_info = bot.get_chat(log_channel)
- message.reply_text(
- f"This group has all it's logs sent to:"
- f" {escape_markdown(log_channel_info.title)} (`{log_channel}`)",
- parse_mode=ParseMode.MARKDOWN,
- )
-
+ try:
+ log_channel_info = bot.get_chat(log_channel)
+ message.reply_text(
+ f"This group has all it's logs sent to:"
+ f" {escape_markdown(log_channel_info.title)} (`{log_channel}`)",
+ parse_mode=ParseMode.MARKDOWN,
+ )
+ except Unauthorized:
+ sql.stop_chat_logging(chat.id)
+ message.reply_text("No log channel has been set for this group!")
else:
message.reply_text("No log channel has been set for this group!")
diff --git a/AstrakoBot/modules/media.py b/AstrakoBot/modules/media.py
index c8be181dc1..32dc955ba9 100644
--- a/AstrakoBot/modules/media.py
+++ b/AstrakoBot/modules/media.py
@@ -14,7 +14,7 @@
*Text to speech:*
⢠`/tts `: convert text to speech\n
*Youtube:*
- ⢠`/youtube`, `/yt` ``: download songs or videos from youtube in standar quality
+ ⢠`/youtube`, `/yt` ` [type] [quality]`: download songs or videos from youtube. Quality limit is 720p; Type can be either video or audio.
"""
__mod_name__ = "Media"
diff --git a/AstrakoBot/modules/misc.py b/AstrakoBot/modules/misc.py
index 778d0e5a7d..239f95181f 100644
--- a/AstrakoBot/modules/misc.py
+++ b/AstrakoBot/modules/misc.py
@@ -90,7 +90,7 @@ def markdown_help(update: Update, context: CallbackContext):
*Markdown:*
⢠`/markdownhelp`*:* quick summary of how markdown works in telegram - can only be called in private chats\n
*Paste:*
- ⢠`/paste`*:* saves replied content to `nekobin.com` and replies with a url\n
+ ⢠`/paste`*:* saves replied content to `dpaste.com` and replies with a url\n
*React:*
⢠`/react`*:* reacts with a random reaction\n
*Urban Dictonary:*
diff --git a/AstrakoBot/modules/muting.py b/AstrakoBot/modules/muting.py
index 09ab544ea4..0b8fae2be8 100644
--- a/AstrakoBot/modules/muting.py
+++ b/AstrakoBot/modules/muting.py
@@ -6,8 +6,10 @@
bot_admin,
can_restrict,
connection_status,
- is_user_admin,
user_admin,
+ user_can_ban,
+ can_delete,
+ is_user_ban_protected,
)
from AstrakoBot.modules.helper_funcs.extraction import (
extract_user,
@@ -15,6 +17,8 @@
)
from AstrakoBot.modules.helper_funcs.string_handling import extract_time
from AstrakoBot.modules.log_channel import loggable
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member, user_is_admin
+
from telegram import Bot, Chat, ChatPermissions, ParseMode, Update
from telegram.error import BadRequest
from telegram.ext import CallbackContext, CommandHandler, run_async
@@ -39,7 +43,7 @@ def check_user(user_id: int, bot: Bot, chat: Chat) -> Optional[str]:
reply = "I'm not gonna MUTE myself, How high are you?"
return reply
- if is_user_admin(chat, user_id, member):
+ if is_user_ban_protected(chat, user_id):
reply = "Can't. Find someone else to mute but not this one."
return reply
@@ -53,6 +57,8 @@ def check_user(user_id: int, bot: Bot, chat: Chat) -> Optional[str]:
@connection_status
@bot_admin
@user_admin
+@user_can_ban
+@can_restrict
@loggable
def mute(update: Update, context: CallbackContext) -> str:
bot = context.bot
@@ -65,6 +71,12 @@ def mute(update: Update, context: CallbackContext) -> str:
user_id, reason = extract_user_and_text(message, args)
reply = check_user(user_id, bot, chat)
+ silent = False
+ if message.text.startswith("/s") or message.text.startswith("!s"):
+ silent = True
+ if not can_delete(chat, context.bot.id):
+ return ""
+
if reply:
message.reply_text(reply)
return ""
@@ -84,18 +96,24 @@ def mute(update: Update, context: CallbackContext) -> str:
if member.can_send_messages is None or member.can_send_messages:
chat_permissions = ChatPermissions(can_send_messages=False)
bot.restrict_chat_member(chat.id, user_id, chat_permissions)
- reply = (
- f"āMute Event\n"
- f" ⢠User: {mention_html(member.user.id, html.escape(member.user.first_name))}\n"
- f" ⢠Time: no expiration date"
- )
- if reason:
- reply += f"\n ⢠Reason: {html.escape(reason)}"
- bot.sendMessage(chat.id, reply, parse_mode=ParseMode.HTML)
+ if not silent:
+ reply = (
+ f"āMute Event\n"
+ f" ⢠User: {mention_html(member.user.id, html.escape(member.user.first_name))}\n"
+ f" ⢠Time: no expiration date"
+ )
+ if reason:
+ reply += f"\n ⢠Reason: {html.escape(reason)}"
+ bot.sendMessage(chat.id, reply, parse_mode=ParseMode.HTML)
+ else:
+ message.delete()
return log
else:
- message.reply_text("This user is already muted!")
+ if not silent:
+ message.reply_text("This user is already muted!")
+ else:
+ message.delete()
return ""
@@ -103,6 +121,8 @@ def mute(update: Update, context: CallbackContext) -> str:
@connection_status
@bot_admin
@user_admin
+@user_can_ban
+@can_restrict
@loggable
def unmute(update: Update, context: CallbackContext) -> str:
bot, args = context.bot, context.args
@@ -110,23 +130,33 @@ def unmute(update: Update, context: CallbackContext) -> str:
user = update.effective_user
message = update.effective_message
+ silent = False
+ if message.text.startswith("/s") or message.text.startswith("!s"):
+ silent = True
+ if not can_delete(chat, context.bot.id):
+ return ""
+
user_id = extract_user(message, args)
if not user_id:
- message.reply_text(
- "You'll need to either give me a username to unmute, or reply to someone to be unmuted."
- )
+ if silent:
+ message.delete()
+ else:
+ message.reply_text(
+ "You'll need to either give me a username to unmute, or reply to someone to be unmuted."
+ )
return ""
member = chat.get_member(int(user_id))
if member.status != "kicked" and member.status != "left":
if (
- member.can_send_messages
- and member.can_send_media_messages
- and member.can_send_other_messages
- and member.can_add_web_page_previews
+ member.can_send_messages is not False
+ and member.can_send_media_messages is not False
+ and member.can_send_other_messages is not False
+ and member.can_add_web_page_previews is not False
):
- message.reply_text("This user already has the right to speak.")
+ if not silent:
+ message.reply_text("This user already has the right to speak.")
else:
chat_permissions = ChatPermissions(
can_send_messages=True,
@@ -142,11 +172,14 @@ def unmute(update: Update, context: CallbackContext) -> str:
bot.restrict_chat_member(chat.id, int(user_id), chat_permissions)
except BadRequest:
pass
- bot.sendMessage(
- chat.id,
- f"I shall allow {html.escape(member.user.first_name)} to text!",
- parse_mode=ParseMode.HTML,
- )
+ if not silent:
+ bot.sendMessage(
+ chat.id,
+ f"I shall allow {html.escape(member.user.first_name)} to text!",
+ parse_mode=ParseMode.HTML,
+ )
+ else:
+ message.delete()
return (
f"{html.escape(chat.title)}:\n"
f"#UNMUTE\n"
@@ -154,11 +187,14 @@ def unmute(update: Update, context: CallbackContext) -> str:
f"User: {mention_html(member.user.id, member.user.first_name)}"
)
else:
- message.reply_text(
- "This user isn't even in the chat, unmuting them won't make them talk more than they "
- "already do!"
- )
+ if not silent:
+ message.reply_text(
+ "This user isn't even in the chat, unmuting them won't make them talk more than they "
+ "already do!"
+ )
+ if silent:
+ message.delete()
return ""
@@ -166,6 +202,7 @@ def unmute(update: Update, context: CallbackContext) -> str:
@bot_admin
@can_restrict
@user_admin
+@user_can_ban
@loggable
def temp_mute(update: Update, context: CallbackContext) -> str:
bot, args = context.bot, context.args
@@ -176,6 +213,12 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
user_id, reason = extract_user_and_text(message, args)
reply = check_user(user_id, bot, chat)
+ silent = False
+ if message.text.startswith("/s") or message.text.startswith("!s"):
+ silent = True
+ if not can_delete(chat, context.bot.id):
+ return ""
+
if reply:
message.reply_text(reply)
return ""
@@ -183,7 +226,10 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
member = chat.get_member(user_id)
if not reason:
- message.reply_text("You haven't specified a time to mute this user for!")
+ if not silent:
+ message.reply_text("You haven't specified a time to mute this user for!")
+ else:
+ message.delete()
return ""
split_reason = reason.split(None, 1)
@@ -210,11 +256,11 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
log += f"\nReason: {reason}"
try:
- if member.can_send_messages is None or member.can_send_messages:
- chat_permissions = ChatPermissions(can_send_messages=False)
- bot.restrict_chat_member(
- chat.id, user_id, chat_permissions, until_date=mutetime
- )
+ chat_permissions = ChatPermissions(can_send_messages=False)
+ bot.restrict_chat_member(
+ chat.id, user_id, chat_permissions, until_date=mutetime
+ )
+ if not silent:
reply = (
f"āMute Event\n"
f" ⢠User: {mention_html(member.user.id, html.escape(member.user.first_name))}\n"
@@ -223,14 +269,17 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
if reason:
reply += f"\n ⢠Reason: {html.escape(reason)}"
bot.sendMessage(chat.id, reply, parse_mode=ParseMode.HTML)
- return log
else:
- message.reply_text("This user is already muted.")
+ message.delete()
+ return log
except BadRequest as excp:
if excp.message == "Reply message not found":
# Do not reply
- message.reply_text(f"Muted for {time_val}!", quote=False)
+ if not silent:
+ message.reply_text(f"Muted for {time_val}!", quote=False)
+ else:
+ message.delete()
return log
else:
LOGGER.warning(update)
@@ -241,7 +290,10 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
chat.id,
excp.message,
)
- message.reply_text("Well damn, I can't mute that user.")
+ if not silent:
+ message.reply_text("Well damn, I can't mute that user.")
+ else:
+ message.delete()
return ""
@@ -253,9 +305,9 @@ def temp_mute(update: Update, context: CallbackContext) -> str:
⢠`/unmute `*:* unmutes a user. Can also be used as a reply, muting the replied to user.
"""
-MUTE_HANDLER = CommandHandler("mute", mute, run_async=True)
-UNMUTE_HANDLER = CommandHandler("unmute", unmute, run_async=True)
-TEMPMUTE_HANDLER = CommandHandler(["tmute", "tempmute"], temp_mute, run_async=True)
+MUTE_HANDLER = CommandHandler(["mute", "smute"], mute, run_async=True)
+UNMUTE_HANDLER = CommandHandler(["unmute", "sunmute"], unmute, run_async=True)
+TEMPMUTE_HANDLER = CommandHandler(["tmute", "tempmute", "stmute", "stempmute"], temp_mute, run_async=True)
dispatcher.add_handler(MUTE_HANDLER)
dispatcher.add_handler(UNMUTE_HANDLER)
diff --git a/AstrakoBot/modules/notes.py b/AstrakoBot/modules/notes.py
index cb99136854..7bde6bafbd 100644
--- a/AstrakoBot/modules/notes.py
+++ b/AstrakoBot/modules/notes.py
@@ -81,13 +81,12 @@ def get(update: Update, context: CallbackContext, notename, show_none=True, no_f
)
except BadRequest as excp:
if excp.message == "Message to forward not found":
- message.reply_text(
- "This message seems to have been lost - I'll remove it "
- "from your notes list."
- )
- sql.rm_note(note_chat_id, notename)
- else:
raise
+ message.reply_text(
+ "This message seems to have been lost - I'll remove it "
+ "from your notes list."
+ )
+ sql.rm_note(note_chat_id, notename)
else:
try:
bot.forward_message(
@@ -95,15 +94,14 @@ def get(update: Update, context: CallbackContext, notename, show_none=True, no_f
)
except BadRequest as excp:
if excp.message == "Message to forward not found":
- message.reply_text(
- "Looks like the original sender of this note has deleted "
- "their message - sorry! Get your bot admin to start using a "
- "message dump to avoid this. I'll remove this note from "
- "your saved notes."
- )
- sql.rm_note(note_chat_id, notename)
- else:
raise
+ message.reply_text(
+ "Looks like the original sender of this note has deleted "
+ "their message - sorry! Get your bot admin to start using a "
+ "message dump to avoid this. I'll remove this note from "
+ "your saved notes."
+ )
+ sql.rm_note(note_chat_id, notename)
else:
VALID_NOTE_FORMATTERS = [
"first",
@@ -173,6 +171,8 @@ def get(update: Update, context: CallbackContext, notename, show_none=True, no_f
try:
setting = getprivatenotes(chat_id)
if note.msgtype in (sql.Types.BUTTON_TEXT, sql.Types.TEXT):
+ text = re.sub(r'\n{3,}', '\n\n', text)
+
if setting:
bot.send_message(
user.id,
@@ -309,7 +309,10 @@ def slash_get(update: Update, context: CallbackContext):
def save(update: Update, context: CallbackContext):
chat_id = update.effective_chat.id
msg = update.effective_message # type: Optional[Message]
-
+ m = msg.text.split(' ', 1)
+ if len(m) == 1:
+ msg.reply_text("Provide something to save")
+ return
note_name, text, data_type, content, buttons = get_note_type(msg)
note_name = note_name.lower()
if data_type is None:
diff --git a/AstrakoBot/modules/paste.py b/AstrakoBot/modules/paste.py
index a95e263850..3dfd1c3d65 100644
--- a/AstrakoBot/modules/paste.py
+++ b/AstrakoBot/modules/paste.py
@@ -4,7 +4,6 @@
from telegram import ParseMode, Update
from telegram.ext import CallbackContext, run_async
-
def paste(update: Update, context: CallbackContext):
args = context.args
message = update.effective_message
@@ -19,16 +18,23 @@ def paste(update: Update, context: CallbackContext):
message.reply_text("What am I supposed to do with this?")
return
- key = (
- requests.post("https://nekobin.com/api/documents", json={"content": data})
- .json()
- .get("result")
- .get("key")
- )
-
- url = f"https://nekobin.com/{key}"
-
- reply_text = f"Nekofied to *Nekobin* : {url}"
+ reply_text = "Dpaste failed!"
+ try:
+ response = requests.post(
+ "https://dpaste.com/api/v2/",
+ data={
+ "content": data,
+ "syntax": "text",
+ "expiry_days": 7
+ },
+ headers={"User-Agent": "Mozilla/5.0"},
+ timeout=10
+ )
+ response.raise_for_status()
+ url = response.text.strip()
+ reply_text = f"Dpasted: {url}"
+ except Exception as e:
+ reply_text = f"Dpaste failed: {e}"
message.reply_text(
reply_text, parse_mode=ParseMode.MARKDOWN, disable_web_page_preview=True
diff --git a/AstrakoBot/modules/plet.py b/AstrakoBot/modules/plet.py
index e8becb3355..2a669a6f28 100644
--- a/AstrakoBot/modules/plet.py
+++ b/AstrakoBot/modules/plet.py
@@ -24,10 +24,16 @@
def plet(update: Update, context: CallbackContext):
chat = update.effective_chat
message = update.effective_message
- if not message.reply_to_message:
+
+ msg = ""
+ if context.args:
msg = message.text.split(None, 1)[1]
- else:
- msg = message.reply_to_message.text
+ elif message.reply_to_message:
+ msg = message.reply_to_message.caption or message.reply_to_message.text or ""
+
+ if not msg:
+ message.reply_text("What should i plet?")
+ return
# the processed photo becomes too long and unreadable + the telegram doesn't support any longer dimensions + you have the lulz.
if (len(msg)) > 39:
@@ -62,12 +68,17 @@ def plet(update: Update, context: CallbackContext):
maxsize = 1024, 896
if image.size[0] > maxsize[0]:
- image.thumbnail(maxsize, Image.ANTIALIAS)
+ attr = Image.ANTIALIAS if hasattr(Image, 'ANTIALIAS') else Image.Resampling.LANCZOS
+ image.thumbnail(maxsize, attr)
# put processed image in a buffer and then upload cause async
with BytesIO() as buffer:
buffer.name = "image.png"
- image.save(buffer, "PNG")
+ try:
+ image.save(buffer, "PNG")
+ except SystemError:
+ message.reply_text("Invalid characters detected. Try again with something readable.")
+ return
buffer.seek(0)
delmsg = context.bot.send_sticker(chat_id=message.chat_id, sticker=buffer)
diff --git a/AstrakoBot/modules/purge.py b/AstrakoBot/modules/purge.py
index d99b6ac489..5a480d9016 100644
--- a/AstrakoBot/modules/purge.py
+++ b/AstrakoBot/modules/purge.py
@@ -48,9 +48,6 @@ async def purge_messages(event):
messages.append(event.reply_to_msg_id)
for msg_id in range(message_id, delete_to + 1):
messages.append(msg_id)
- if len(messages) == 100:
- await event.client.delete_messages(event.chat_id, messages)
- messages = []
try:
await event.client.delete_messages(event.chat_id, messages)
diff --git a/AstrakoBot/modules/qr_code.py b/AstrakoBot/modules/qr_code.py
new file mode 100644
index 0000000000..bed69f1902
--- /dev/null
+++ b/AstrakoBot/modules/qr_code.py
@@ -0,0 +1,122 @@
+import requests
+from AstrakoBot import dispatcher
+from telegram import Update, ParseMode
+from telegram.ext import CallbackContext, CommandHandler, run_async
+from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
+from AstrakoBot.modules.helper_funcs.misc import delete
+from datetime import datetime
+
+from urllib.parse import quote
+from telegram import Update
+from telegram.ext import CallbackContext
+import requests
+from io import BytesIO
+
+def qr_encode(update: Update, context: CallbackContext):
+ message = update.effective_message
+ data = None
+
+ if message.reply_to_message:
+ replied_msg = message.reply_to_message
+ data = replied_msg.caption or replied_msg.text
+ if not data:
+ message.reply_text("Replied message has no text/caption!")
+ return
+ else:
+ if not context.args:
+ message.reply_text("Please provide text or reply to a message!\nExample: /qr_encode hello world")
+ return
+ data = ' '.join(context.args)
+
+ try:
+ encoded_data = quote(data)
+ qr_url = f"https://api.qrserver.com/v1/create-qr-code/?data={encoded_data}"
+
+ response = requests.get(qr_url)
+ response.raise_for_status()
+
+ img_file = BytesIO(response.content)
+ img_file.name = "qrcode.png"
+
+ message.reply_document(
+ document=img_file,
+ filename="qrcode.png"
+ )
+
+ except requests.exceptions.RequestException as e:
+ message.reply_text(f"Failed to generate QR code: {str(e)}")
+ except Exception as e:
+ message.reply_text(f"An error occurred: {str(e)}")
+
+def qr_decode(update: Update, context: CallbackContext):
+ message = update.effective_message
+ target = message.reply_to_message or message
+
+ photo = target.photo[-1] if target.photo else None
+ document = target.document
+
+ file = None
+ file_size = 0
+ if photo:
+ file_size = photo.file_size
+ file = photo.get_file()
+ elif document and document.mime_type.startswith('image/'):
+ file_size = document.file_size
+ file = document.get_file()
+ else:
+ message.reply_text("Please reply to or send an image containing a QR code")
+ return
+
+ if file_size > 1048576:
+ message.reply_text("Image too large! Max 1MB allowed")
+ return
+
+ try:
+ img_data = BytesIO()
+ file.download(out=img_data)
+ img_data.seek(0)
+
+ response = requests.post(
+ 'https://api.qrserver.com/v1/read-qr-code/',
+ files={'file': ('qrcode', img_data, 'application/octet-stream')}
+ )
+ response.raise_for_status()
+ result = response.json()
+
+ if result and isinstance(result, list):
+ symbol_list = result[0].get('symbol', [{}])
+ if not symbol_list:
+ return message.reply_text("No QR symbol data found")
+ text = symbol_list[0].get('data')
+ if text:
+ allowed_controls = {'\t', '\n', '\r'}
+ if all(c.isprintable() or c in allowed_controls for c in text):
+ return message.reply_text(f"Decoded QR content:\n\n{text}")
+ else:
+ return message.reply_text("QR Code contains unprintable characters")
+ else:
+ return message.reply_text("QR Code contains no readable data")
+ else:
+ return message.reply_text("Invalid or empty QR Code response")
+
+ except requests.exceptions.RequestException as e:
+ message.reply_text(f"API Error: {str(e)}")
+ except Exception as e:
+ message.reply_text(f"Decoding failed: {str(e)}")
+
+__help__ = """
+*Available commands:*\n
+*QR Code:*
+⢠`/qr`, `/qr_encode`: Encode text to QR Code\n
+⢠`/qr_decode`: Decode QR Code to text\n
+"""
+
+ENCODE_HANDLER = CommandHandler(["qr", "qr_encode"], qr_encode, run_async=True)
+DECODE_HANDLER = CommandHandler("qr_decode", qr_decode, run_async=True)
+
+dispatcher.add_handler(ENCODE_HANDLER)
+dispatcher.add_handler(DECODE_HANDLER)
+
+__mod_name__ = "QR Code"
+__command_list__ = ["qr", "qr_encode", "qr_decode"]
+__handlers__ = [ENCODE_HANDLER, DECODE_HANDLER]
diff --git a/AstrakoBot/modules/quotly.py b/AstrakoBot/modules/quotly.py
index b21fe1b9eb..28be9c80ed 100644
--- a/AstrakoBot/modules/quotly.py
+++ b/AstrakoBot/modules/quotly.py
@@ -1,5 +1,6 @@
from PIL import Image, ImageDraw, ImageFont, ImageOps
from telethon.tl import types, functions
+from telethon.errors.rpcerrorlist import UserNotParticipantError
from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode
import emoji
@@ -26,29 +27,6 @@
async def process(msg, user, client, reply, replied=None):
- if not os.path.isdir("resources"):
- os.mkdir("resources", 0o755)
- urllib.request.urlretrieve(
- "https://github.com/erenmetesar/modules-repo/raw/master/Roboto-Regular.ttf",
- "resources/Roboto-Regular.ttf",
- )
- urllib.request.urlretrieve(
- "https://github.com/erenmetesar/modules-repo/raw/master/Quivira.otf",
- "resources/Quivira.otf",
- )
- urllib.request.urlretrieve(
- "https://github.com/erenmetesar/modules-repo/raw/master/Roboto-Medium.ttf",
- "resources/Roboto-Medium.ttf",
- )
- urllib.request.urlretrieve(
- "https://github.com/erenmetesar/modules-repo/raw/master/DroidSansMono.ttf",
- "resources/DroidSansMono.ttf",
- )
- urllib.request.urlretrieve(
- "https://github.com/erenmetesar/modules-repo/raw/master/Roboto-Italic.ttf",
- "resources/Roboto-Italic.ttf",
- )
-
# Importing fonts and gettings the size of text
font = ImageFont.truetype("resources/Roboto-Medium.ttf", 43, encoding="utf-16")
font2 = ImageFont.truetype("resources/Roboto-Regular.ttf", 33, encoding="utf-16")
@@ -90,13 +68,20 @@ async def process(msg, user, client, reply, replied=None):
title = details.participant.rank if details.participant.rank else "Creator"
elif isinstance(details.participant, types.ChannelParticipantAdmin):
title = details.participant.rank if details.participant.rank else "Admin"
+ except UserNotParticipantError:
+ pass
except TypeError:
pass
titlewidth = font2.getsize(title)[0]
# Get user name
- lname = "" if not user.last_name else user.last_name
- tot = user.first_name + " " + lname
+ tot = (
+ f"{user.first_name} {user.last_name}" if (user.first_name and user.last_name) else
+ user.first_name or
+ getattr(user, 'title', '') or
+ getattr(user, 'username', '') or
+ "Bot"
+ )
namewidth = fallback.getsize(tot)[0] + 10
@@ -143,8 +128,14 @@ async def process(msg, user, client, reply, replied=None):
y = 80
if replied:
# Creating a big canvas to gather all the elements
- replname = "" if not replied.sender.last_name else replied.sender.last_name
- reptot = replied.sender.first_name + " " + replname
+ reptot = (
+ f"{replied.sender.first_name} {replied.sender.last_name}".strip()
+ if getattr(replied.sender, 'last_name', None)
+ else replied.sender.first_name or
+ getattr(replied.sender, 'title', '') or
+ getattr(replied.sender, 'username', '') or
+ "Bot"
+ )
replywidth = font2.getsize(reptot)[0]
if reply.sticker:
sticker = await reply.download_media()
@@ -430,6 +421,7 @@ async def quotly(event):
return
reply = await event.get_reply_message()
if reply is None:
+ await event.reply("What to quote?")
return
msg = reply.message
repliedreply = await reply.get_reply_message()
diff --git a/AstrakoBot/modules/remote_cmds.py b/AstrakoBot/modules/remote_cmds.py
index 50402fbf6e..0c805532b4 100644
--- a/AstrakoBot/modules/remote_cmds.py
+++ b/AstrakoBot/modules/remote_cmds.py
@@ -7,6 +7,7 @@
)
from AstrakoBot.modules.helper_funcs.extraction import extract_user_and_text
from AstrakoBot.modules.helper_funcs.filters import CustomFilters
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member
from telegram import Update, ChatPermissions
from telegram.error import BadRequest
from telegram.ext import CallbackContext, CommandHandler, run_async
@@ -119,7 +120,7 @@ def rban(update: Update, context: CallbackContext):
if (
not is_bot_admin(chat, bot.id)
- or not chat.get_member(bot.id).can_restrict_members
+ or not get_bot_member(chat.id).can_restrict_members
):
message.reply_text(
"I can't restrict people there! Make sure I'm admin and can ban users."
@@ -144,7 +145,7 @@ def rban(update: Update, context: CallbackContext):
return
try:
- chat.kick_member(user_id)
+ chat.ban_member(user_id)
message.reply_text("Banned from chat!")
except BadRequest as excp:
if excp.message == "Reply message not found":
@@ -201,7 +202,7 @@ def runban(update: Update, context: CallbackContext):
if (
not is_bot_admin(chat, bot.id)
- or not chat.get_member(bot.id).can_restrict_members
+ or not get_bot_member(chat.id).can_restrict_members
):
message.reply_text(
"I can't unrestrict people there! Make sure I'm admin and can unban users."
@@ -285,7 +286,7 @@ def rkick(update: Update, context: CallbackContext):
if (
not is_bot_admin(chat, bot.id)
- or not chat.get_member(bot.id).can_restrict_members
+ or not get_bot_member(chat.id).can_restrict_members
):
message.reply_text(
"I can't restrict people there! Make sure I'm admin and can punch users."
@@ -367,7 +368,7 @@ def rmute(update: Update, context: CallbackContext):
if (
not is_bot_admin(chat, bot.id)
- or not chat.get_member(bot.id).can_restrict_members
+ or not get_bot_member(chat.id).can_restrict_members
):
message.reply_text(
"I can't restrict people there! Make sure I'm admin and can mute users."
@@ -451,7 +452,7 @@ def runmute(update: Update, context: CallbackContext):
if (
not is_bot_admin(chat, bot.id)
- or not chat.get_member(bot.id).can_restrict_members
+ or not get_bot_member(chat.id).can_restrict_members
):
message.reply_text(
"I can't unrestrict people there! Make sure I'm admin and can unban users."
diff --git a/AstrakoBot/modules/reverse.py b/AstrakoBot/modules/reverse.py
index 6dcb9cd7ed..db536c2073 100644
--- a/AstrakoBot/modules/reverse.py
+++ b/AstrakoBot/modules/reverse.py
@@ -114,7 +114,7 @@ def reverse(update: Update, context: CallbackContext):
os.remove(imagename)
match = ParseSauce(fetchUrl + "&hl=en")
guess = match["best_guess"]
- if match["override"] and not match["override"] == "":
+ if match["override"]:
imgspage = match["override"]
else:
imgspage = match["similar_images"]
diff --git a/AstrakoBot/modules/sed.py b/AstrakoBot/modules/sed.py
index db43e204cb..b1d578100a 100644
--- a/AstrakoBot/modules/sed.py
+++ b/AstrakoBot/modules/sed.py
@@ -7,6 +7,7 @@
from AstrakoBot.modules.helper_funcs.regex_helper import infinite_loop_check
from telegram import Update
from telegram.ext import CallbackContext, Filters, run_async
+from AstrakoBot.modules.helper_funcs.chat_status import sudo_plus_silent
DELIMITERS = ("/", ":", "|", "_")
@@ -56,7 +57,7 @@ def separate_sed(sed_string):
flags = sed_string[counter:]
return replace, replace_with, flags.lower()
-
+@sudo_plus_silent
def sed(update: Update, context: CallbackContext):
sed_result = separate_sed(update.effective_message.text)
if sed_result and update.effective_message.reply_to_message:
@@ -116,7 +117,7 @@ def sed(update: Update, context: CallbackContext):
__help__ = """
- ⢠`s//(/)`*:* Reply to a message with this to perform a sed operation on that message, replacing all \
+ ⢠`s//(/)` *(sudo users only):* Reply to a message with this to perform a sed operation on that message, replacing all \
occurrences of 'text1' with 'text2'. Flags are optional, and currently include 'i' for ignore case, 'g' for global, \
or nothing. Delimiters include `/`, `_`, `|`, and `:`. Text grouping is supported. The resulting message cannot be \
larger than {}.
diff --git a/AstrakoBot/modules/shout.py b/AstrakoBot/modules/shout.py
index 7aec7fe8bb..00d41801b8 100644
--- a/AstrakoBot/modules/shout.py
+++ b/AstrakoBot/modules/shout.py
@@ -6,7 +6,18 @@
def shout(update: Update, context: CallbackContext):
args = context.args
- text = " ".join(args)
+ message = update.effective_message
+
+ text = ""
+ if args:
+ text = " ".join(args)
+ elif message.reply_to_message:
+ text = message.reply_to_message.caption or message.reply_to_message.text or ""
+
+ if not text:
+ message.reply_text("What should i shout?")
+ return
+
result = []
result.append(" ".join([s for s in text]))
for pos, symbol in enumerate(text[1:]):
@@ -15,7 +26,7 @@ def shout(update: Update, context: CallbackContext):
result[0] = text[0]
result = "".join(result)
msg = "```\n" + result + "```"
- return update.effective_message.reply_text(msg, parse_mode="MARKDOWN")
+ return message.reply_text(msg, parse_mode="MARKDOWN")
SHOUT_HANDLER = DisableAbleCommandHandler("shout", shout, run_async=True)
diff --git a/AstrakoBot/modules/sql/cust_filters_sql.py b/AstrakoBot/modules/sql/cust_filters_sql.py
index e42849ff71..63f346f88c 100644
--- a/AstrakoBot/modules/sql/cust_filters_sql.py
+++ b/AstrakoBot/modules/sql/cust_filters_sql.py
@@ -357,7 +357,7 @@ def __migrate_filters():
else:
file_type = Types.TEXT
- print(str(x.chat_id), x.keyword, x.reply, file_type.value)
+ #print(str(x.chat_id), x.keyword, x.reply, file_type.value)
if file_type == Types.TEXT:
filt = CustomFilters(
str(x.chat_id), x.keyword, x.reply, file_type.value, None
diff --git a/AstrakoBot/modules/sql/feds_sql.py b/AstrakoBot/modules/sql/feds_sql.py
index b509515592..3b4c8c7bed 100644
--- a/AstrakoBot/modules/sql/feds_sql.py
+++ b/AstrakoBot/modules/sql/feds_sql.py
@@ -149,7 +149,7 @@ def get_user_fban(fed_id, user_id):
def get_user_admin_fed_name(user_id):
user_feds = []
for f in FEDERATION_BYFEDID:
- if int(user_id) in ast.literal_eval(ast.literal_eval(FEDERATION_BYFEDID[f]["fusers"])["members"]):
+ if int(user_id) in list(set(ast.literal_eval(ast.literal_eval(FEDERATION_BYFEDID[f]["fusers"])["members"]))):
user_feds.append(FEDERATION_BYFEDID[f]["fname"])
return user_feds
@@ -165,7 +165,7 @@ def get_user_owner_fed_name(user_id):
def get_user_admin_fed_full(user_id):
user_feds = []
for f in FEDERATION_BYFEDID:
- if int(user_id) in ast.literal_eval(ast.literal_eval(FEDERATION_BYFEDID[f]["fusers"])["members"]):
+ if int(user_id) in list(set(ast.literal_eval(ast.literal_eval(FEDERATION_BYFEDID[f]["fusers"])["members"]))):
user_feds.append({"fed_id": f, "fed": FEDERATION_BYFEDID[f]})
return user_feds
@@ -184,7 +184,7 @@ def get_user_fbanlist(user_id):
fedname = []
for x in banlist:
if banlist[x].get(user_id):
- if user_name == "":
+ if not user_name:
user_name = banlist[x][user_id].get("first_name")
fedname.append([x, banlist[x][user_id].get("reason")])
return user_name, fedname
@@ -327,7 +327,7 @@ def search_user_in_fed(fed_id, user_id):
if getfed is None:
return False
getfed = ast.literal_eval(getfed["fusers"])["members"]
- if user_id in ast.literal_eval(getfed):
+ if user_id in list(set(ast.literal_eval(getfed))):
return True
else:
return False
@@ -344,7 +344,7 @@ def user_demote_fed(fed_id, user_id):
fed_log = getfed["flog"]
# Temp set
try:
- members = ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])
+ members = list(set(ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])))
except ValueError:
return False
members.remove(user_id)
@@ -394,7 +394,7 @@ def user_join_fed(fed_id, user_id):
fed_rules = getfed["frules"]
fed_log = getfed["flog"]
# Temp set
- members = ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])
+ members = list(set(ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])))
members.append(user_id)
# Set user
FEDERATION_BYOWNER[str(owner_id)]["fusers"] = str(
@@ -456,7 +456,7 @@ def all_fed_users(fed_id):
if getfed is None:
return False
fed_owner = ast.literal_eval(ast.literal_eval(getfed["fusers"])["owner"])
- fed_admins = ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])
+ fed_admins = list(set(ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])))
fed_admins.append(fed_owner)
return fed_admins
@@ -464,7 +464,7 @@ def all_fed_users(fed_id):
def all_fed_members(fed_id):
with FEDS_LOCK:
getfed = FEDERATION_BYFEDID.get(str(fed_id))
- fed_admins = ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])
+ fed_admins = list(set(ast.literal_eval(ast.literal_eval(getfed["fusers"])["members"])))
return fed_admins
@@ -557,11 +557,11 @@ def multi_fban_user(
SESSION.add(r)
counter += 1
- if str(str(counter)[-2:]) == "00":
- print(user_id)
- print(first_name)
- print(reason)
- print(counter)
+ #if str(str(counter)[-2:]) == "00":
+ #print(user_id)
+ #print(first_name)
+ #print(reason)
+ #print(counter)
try:
SESSION.commit()
except:
@@ -570,7 +570,7 @@ def multi_fban_user(
finally:
SESSION.commit()
__load_all_feds_banned()
- print("Done")
+ #print("Done")
return counter
@@ -717,7 +717,7 @@ def set_fed_log(fed_id, chat_id):
)
SESSION.merge(fed)
SESSION.commit()
- print(fed_log)
+ #print(fed_log)
return True
diff --git a/AstrakoBot/modules/sql/locks_sql.py b/AstrakoBot/modules/sql/locks_sql.py
index 124fdd6994..7d6c59bcf5 100644
--- a/AstrakoBot/modules/sql/locks_sql.py
+++ b/AstrakoBot/modules/sql/locks_sql.py
@@ -27,6 +27,7 @@ class Permissions(BASE):
button = Column(Boolean, default=False)
egame = Column(Boolean, default=False)
inline = Column(Boolean, default=False)
+ anonchannel = Column(Boolean, default=False)
def __init__(self, chat_id):
self.chat_id = str(chat_id) # ensure string
@@ -47,6 +48,7 @@ def __init__(self, chat_id):
self.button = False
self.egame = False
self.inline = False
+ self.anonchannel = False
def __repr__(self):
return "" % self.chat_id
@@ -145,6 +147,8 @@ def update_lock(chat_id, lock_type, locked):
curr_perm.egame = locked
elif lock_type == "inline":
curr_perm.inline = locked
+ elif lock_type == 'anonchannel':
+ curr_perm.anonchannel = locked
SESSION.add(curr_perm)
SESSION.commit()
@@ -214,6 +218,8 @@ def is_locked(chat_id, lock_type):
return curr_perm.egame
elif lock_type == "inline":
return curr_perm.inline
+ elif lock_type == "anonchannel":
+ return curr_perm.anonchannel
def is_restr_locked(chat_id, lock_type):
diff --git a/AstrakoBot/modules/sql/users_sql.py b/AstrakoBot/modules/sql/users_sql.py
index 9fb699161b..a4d6e919c0 100644
--- a/AstrakoBot/modules/sql/users_sql.py
+++ b/AstrakoBot/modules/sql/users_sql.py
@@ -218,7 +218,7 @@ def del_user(user_id):
SESSION.commit()
return True
- ChatMembers.query.filter(ChatMembers.user == user_id).delete()
+ SESSION.query(ChatMembers).filter(ChatMembers.user == user_id).delete()
SESSION.commit()
SESSION.close()
return False
diff --git a/AstrakoBot/modules/stickers.py b/AstrakoBot/modules/stickers.py
index 0075deba98..0f85a20e35 100644
--- a/AstrakoBot/modules/stickers.py
+++ b/AstrakoBot/modules/stickers.py
@@ -1,12 +1,15 @@
import os
import math
+from io import BytesIO
+from urllib.error import HTTPError
+
import requests
import urllib.request as urllib
from PIL import Image
from html import escape
from bs4 import BeautifulSoup as bs
-from telegram import ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
+from telegram import Bot, ParseMode, InlineKeyboardMarkup, InlineKeyboardButton
from telegram import TelegramError, Update
from telegram.ext import run_async, CallbackContext
from telegram.utils.helpers import mention_html
@@ -17,6 +20,16 @@
combot_stickers_url = "https://combot.org/telegram/stickers?q="
+def get_sticker_count(bot: Bot, packname: str) -> int:
+ resp = bot._request.post(
+ f"{bot.base_url}/getStickerSet",
+ {
+ "name": packname,
+ },
+ )
+ return len(resp["stickers"])
+
+
def stickerid(update: Update, context: CallbackContext):
msg = update.effective_message
if msg.reply_to_message and msg.reply_to_message.sticker:
@@ -39,248 +52,237 @@ def stickerid(update: Update, context: CallbackContext):
def cb_sticker(update: Update, context: CallbackContext):
msg = update.effective_message
+ stp = ""
split = msg.text.split(" ", 1)
if len(split) == 1:
msg.reply_text("Provide some name to search for pack.")
return
text = requests.get(combot_stickers_url + split[1]).text
soup = bs(text, "lxml")
- results = soup.find_all("a", {"class": "sticker-pack__btn"})
- titles = soup.find_all("div", "sticker-pack__title")
- if not results:
- msg.reply_text("No results found :(.")
- return
- reply = f"Stickers for *{split[1]}*:"
- for result, title in zip(results, titles):
- link = result["href"]
- reply += f"\n⢠[{title.get_text()}]({link})"
- msg.reply_text(reply, parse_mode=ParseMode.MARKDOWN)
-
+ results = soup.find_all("div", {"class": "sticker-pack__header"})
+ for pack in results:
+ if pack.button:
+ title_ = (pack.find("div", {"class": "sticker-pack__title"})).text
+ link_ = (pack.a).get("href")
+ stp += f"\n⢠[{title_}]({link_})"
+ if not stp:
+ stp = "Stickers not found"
+ msg.reply_text(stp, parse_mode=ParseMode.MARKDOWN)
def getsticker(update: Update, context: CallbackContext):
- bot = context.bot
msg = update.effective_message
- chat_id = update.effective_chat.id
if msg.reply_to_message and msg.reply_to_message.sticker:
file_id = msg.reply_to_message.sticker.file_id
+ # Check if it's an animated file
+ is_animated = msg.reply_to_message.sticker.is_animated
+ bot = context.bot
+ # Get the file and put it into a memory buffer
new_file = bot.get_file(file_id)
- new_file.download("sticker.png")
- bot.send_document(chat_id, document=open("sticker.png", "rb"))
- os.remove("sticker.png")
+ sticker_data = new_file.download(out=BytesIO())
+ # go back to the start of the buffer
+ sticker_data.seek(0)
+ filename = "animated_sticker.tgs.rename_me" if is_animated else "sticker.png"
+ chat_id = update.effective_chat.id
+ # Send the document
+ bot.send_document(chat_id,
+ document=sticker_data,
+ filename=filename,
+ disable_content_type_detection=True
+ )
else:
update.effective_message.reply_text(
"Please reply to a sticker for me to upload its PNG."
)
-
def kang(update: Update, context: CallbackContext):
+ ppref = ""
msg = update.effective_message
user = update.effective_user
args = context.args
- packnum = 0
- packname = "a" + str(user.id) + "_by_" + context.bot.username
- packname_found = 0
- max_stickers = 120
- while packname_found == 0:
- try:
- stickerset = context.bot.get_sticker_set(packname)
- if len(stickerset.stickers) >= max_stickers:
- packnum += 1
- packname = (
- "a"
- + str(packnum)
- + "_"
- + str(user.id)
- + "_by_"
- + context.bot.username
- )
- else:
- packname_found = 1
- except TelegramError as e:
- if e.message == "Stickerset_invalid":
- packname_found = 1
- kangsticker = "kangsticker.png"
is_animated = False
- file_id = ""
+ is_video = False
+ file_id = None
+ sticker_emoji = "š¤"
+ sticker_data = None
+
+ # The kang syntax is as follows:
+ # /kang š¤
+ # /kang http://whatever.com/sticker.png š¤
+ # It can be animated or not.
+
+ # first check if we're syntactically correct.
+ if not msg.reply_to_message and not args:
+ # this is quite a bit more difficult, we need to get all their packs managed by us.
+ packs = ""
+ # start with finding non-animated packs.
+ packnum = 0
+ # Initial pack name for non-animated
+ packname = f"a{user.id}_by_{context.bot.username}"
+ # Max non-animated stickers in a pack
+ max_stickers = 120
+
+ # Find the packs
+ while True:
+ last_set = False
+ try:
+ if get_sticker_count(context.bot, packname) >= max_stickers:
+ packnum += 1
+ if is_animated:
+ packname = f"animated{packnum}_{user.id}_by_{context.bot.username}"
+ ppref = "animated"
+ elif is_video:
+ packname = f"vid{packnum}_{user.id}_by_{context.bot.username}"
+ ppref = "vid"
+ else:
+ packname = f"a{packnum}_{user.id}_by_{context.bot.username}"
+ ppref = ""
+ else:
+ last_set = True
+ packs += f"[{ppref}pack{packnum if packnum != 0 else ''}](t.me/addstickers/{packname})\n"
+ except TelegramError as e:
+ if e.message == "Stickerset_invalid":
+ last_set = True
+ else:
+ #print(e)
+ break # something went wrong, leave the loop and send what we have.
- if msg.reply_to_message:
- if msg.reply_to_message.sticker:
- if msg.reply_to_message.sticker.is_animated:
+ # If we're done checking bot animated and non-animated packs
+ # exit the loop and send our pack message.
+ if last_set and is_animated:
+ break
+ elif last_set:
+ # move to checking animated packs. Start with the first pack
+ packname = f"animated_{user.id}_by_{context.bot.username}"
+ # reset our counter
+ packnum = 0
+ # Animated packs have a max of 50 stickers
+ max_stickers = 50
+ # tell the loop we're looking at animated stickers now
is_animated = True
- file_id = msg.reply_to_message.sticker.file_id
- elif msg.reply_to_message.photo:
- file_id = msg.reply_to_message.photo[-1].file_id
- elif msg.reply_to_message.document:
- file_id = msg.reply_to_message.document.file_id
+ # if they have no packs, change our message
+ if not packs:
+ packs = "Looks like you don't have any packs! Please reply to a sticker, or image to kang it and create a new pack!"
else:
- msg.reply_text("Yea, I can't kang that.")
+ packs = "Please reply to a sticker, or image to kang it!\nOh, by the way, here are your packs:\n" + packs
- kang_file = context.bot.get_file(file_id)
- if not is_animated:
- kang_file.download("kangsticker.png")
+ # Send our list as a reply
+ msg.reply_text(packs, parse_mode=ParseMode.MARKDOWN)
+ # Don't continue processing the command.
+ return
+
+ # User sent /kang in reply to a message
+ if rep := msg.reply_to_message:
+ if rep.sticker:
+ is_animated = rep.sticker.is_animated
+ is_video = rep.sticker.is_video
+ file_id = rep.sticker.file_id
+ # also grab the emoji if the user wishes
+ if not args:
+ sticker_emoji = rep.sticker.emoji
+ elif rep.photo:
+ file_id = rep.photo[-1].file_id
+ elif rep.video:
+ file_id = rep.video.file_id
+ is_video = True
+ elif rep.animation:
+ file_id = rep.animation.file_id
+ is_video = True
+ elif doc := rep.document:
+ file_id = rep.document.file_id
+ if doc.mime_type == 'video/webm':
+ is_video = True
else:
- kang_file.download("kangsticker.tgs")
+ msg.reply_text("Yea, I can't steal that.")
+ return
+ # Check if they have an emoji specified.
if args:
- sticker_emoji = str(args[0])
- elif msg.reply_to_message.sticker and msg.reply_to_message.sticker.emoji:
- sticker_emoji = msg.reply_to_message.sticker.emoji
- else:
- sticker_emoji = "š¤"
+ sticker_emoji = args[0]
- if not is_animated:
- try:
- im = Image.open(kangsticker)
- maxsize = (512, 512)
- if (im.width and im.height) < 512:
- size1 = im.width
- size2 = im.height
- if im.width > im.height:
- scale = 512 / size1
- size1new = 512
- size2new = size2 * scale
- else:
- scale = 512 / size2
- size1new = size1 * scale
- size2new = 512
- size1new = math.floor(size1new)
- size2new = math.floor(size2new)
- sizenew = (size1new, size2new)
- im = im.resize(sizenew)
- else:
- im.thumbnail(maxsize)
- if not msg.reply_to_message.sticker:
- im.save(kangsticker, "PNG")
- context.bot.add_sticker_to_set(
- user_id=user.id,
- name=packname,
- png_sticker=open("kangsticker.png", "rb"),
- emojis=sticker_emoji,
- )
- msg.reply_text(
- f"Sticker successfully added to [pack](t.me/addstickers/{packname})"
- + f"\nEmoji is: {sticker_emoji}",
- parse_mode=ParseMode.MARKDOWN,
- )
-
- except OSError as e:
+ # Download the data
+ kang_file = context.bot.get_file(file_id)
+ sticker_data = kang_file.download(out=BytesIO())
+ # move to the front of the buffer.
+ sticker_data.seek(0)
+ else: # user sent /kang with url
+ url = args[0]
+ # set the emoji if they specify it.
+ if len(args) >= 2:
+ sticker_emoji = args[1]
+ # open the URL, downlaod the image and write to
+ # a buffer object we can use elsewhere.
+ sticker_data = BytesIO()
+ try:
+ resp = urllib.urlopen(url)
+
+ # check the mime-type first, you can't kang a .html file.
+ mime = resp.getheader('Content-Type')
+ if mime not in ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'application/x-tgsticker']:
msg.reply_text("I can only kang images m8.")
- print(e)
return
- except TelegramError as e:
- if e.message == "Stickerset_invalid":
- makepack_internal(
- update,
- context,
- msg,
- user,
- sticker_emoji,
- packname,
- packnum,
- png_sticker=open("kangsticker.png", "rb"),
- )
- elif e.message == "Sticker_png_dimensions":
- im.save(kangsticker, "PNG")
- context.bot.add_sticker_to_set(
- user_id=user.id,
- name=packname,
- png_sticker=open("kangsticker.png", "rb"),
- emojis=sticker_emoji,
- )
- msg.reply_text(
- f"Sticker successfully added to [pack](t.me/addstickers/{packname})"
- + f"\nEmoji is: {sticker_emoji}",
- parse_mode=ParseMode.MARKDOWN,
- )
- elif e.message == "Invalid sticker emojis":
- msg.reply_text("Invalid emoji(s).")
- elif e.message == "Stickers_too_much":
- msg.reply_text("Max packsize reached. Press F to pay respecc.")
- elif e.message == "Internal Server Error: sticker set not found (500)":
- msg.reply_text(
- "Sticker successfully added to [pack](t.me/addstickers/%s)"
- % packname
- + "\n"
- "Emoji is:" + " " + sticker_emoji,
- parse_mode=ParseMode.MARKDOWN,
- )
- print(e)
+ # check if it's an animated sticker type
+ if mime == "application/x-tgsticker":
+ is_animated = True
+ # write our sticker data to a buffer object
+ sticker_data.write(resp.read())
+ # move to the front of the buffer.
+ sticker_data.seek(0)
+ except ValueError:
+ # If they gave an invalid URL
+ msg.reply_text("Yea, that's not a URL I can download from.")
+ return
+ except HTTPError as e:
+ # if we're not allowed there for some reason
+ msg.reply_text(f"Error downloading the file: {e.code} {e.msg}")
+ return
- else:
- packname = "animated" + str(user.id) + "_by_" + context.bot.username
- packname_found = 0
- max_stickers = 50
- while packname_found == 0:
- try:
- stickerset = context.bot.get_sticker_set(packname)
- if len(stickerset.stickers) >= max_stickers:
- packnum += 1
- packname = (
- "animated"
- + str(packnum)
- + "_"
- + str(user.id)
- + "_by_"
- + context.bot.username
- )
- else:
- packname_found = 1
- except TelegramError as e:
- if e.message == "Stickerset_invalid":
- packname_found = 1
- try:
- context.bot.add_sticker_to_set(
- user_id=user.id,
- name=packname,
- tgs_sticker=open("kangsticker.tgs", "rb"),
- emojis=sticker_emoji,
- )
- msg.reply_text(
- f"Sticker successfully added to [pack](t.me/addstickers/{packname})"
- + f"\nEmoji is: {sticker_emoji}",
- parse_mode=ParseMode.MARKDOWN,
- )
- except TelegramError as e:
- if e.message == "Stickerset_invalid":
- makepack_internal(
- update,
- context,
- msg,
- user,
- sticker_emoji,
- packname,
- packnum,
- tgs_sticker=open("kangsticker.tgs", "rb"),
- )
- elif e.message == "Invalid sticker emojis":
- msg.reply_text("Invalid emoji(s).")
- elif e.message == "Internal Server Error: sticker set not found (500)":
- msg.reply_text(
- "Sticker successfully added to [pack](t.me/addstickers/%s)"
- % packname
- + "\n"
- "Emoji is:" + " " + sticker_emoji,
- parse_mode=ParseMode.MARKDOWN,
- )
- print(e)
-
- elif args:
+ packnum = 0
+ packname_found = False
+ invalid = False
+
+ # now determine the pack name we should use by default
+ if is_animated:
+ packname = f"animated_{user.id}_by_{context.bot.username}"
+ max_stickers = 50
+ elif is_video:
+ packname = f"vid_{user.id}_by_{context.bot.username}"
+ max_stickers = 50
+ else:
+ packname = f"a{user.id}_by_{context.bot.username}"
+ max_stickers = 120
+
+ # Find if the pack is full already
+ while not packname_found:
try:
- try:
- urlemoji = msg.text.split(" ")
- png_sticker = urlemoji[1]
- sticker_emoji = urlemoji[2]
- except IndexError:
- sticker_emoji = "š¤"
- urllib.urlretrieve(png_sticker, kangsticker)
- im = Image.open(kangsticker)
- maxsize = (512, 512)
+ if get_sticker_count(context.bot, packname) >= max_stickers:
+ packnum += 1
+ if is_animated:
+ packname = f"animated{packnum}_{user.id}_by_{context.bot.username}"
+ elif is_video:
+ packname = f"vid{packnum}_{user.id}_by_{context.bot.username}"
+ else:
+ packname = f"a{packnum}_{user.id}_by_{context.bot.username}"
+ else:
+ packname_found = True
+ except TelegramError as e:
+ if e.message == "Stickerset_invalid":
+ packname_found = True
+ # we will need to create the sticker pack
+ invalid = True
+ else:
+ raise
+
+ # if the image isn't animated, ensure it's the right size/format with PIL
+ if not is_animated and not is_video:
+ # handle non-animated stickers.
+ try:
+ im = Image.open(sticker_data)
if (im.width and im.height) < 512:
size1 = im.width
size2 = im.height
- if im.width > im.height:
+ if size1 > size2:
scale = 512 / size1
size1new = 512
size2new = size2 * scale
@@ -293,85 +295,72 @@ def kang(update: Update, context: CallbackContext):
sizenew = (size1new, size2new)
im = im.resize(sizenew)
else:
+ maxsize = (512, 512)
im.thumbnail(maxsize)
- im.save(kangsticker, "PNG")
- msg.reply_photo(photo=open("kangsticker.png", "rb"))
- context.bot.add_sticker_to_set(
- user_id=user.id,
- name=packname,
- png_sticker=open("kangsticker.png", "rb"),
- emojis=sticker_emoji,
+ # Saved the resized sticker in memory
+ sticker_data = BytesIO()
+ im.save(sticker_data, 'PNG')
+ # seek to start of the image data
+ sticker_data.seek(0)
+ except OSError as e:
+ msg.reply_text("I can only steal images m8.")
+ return
+
+ # actually add the damn sticker to the pack, animated or not.
+ try:
+ # Add the sticker to the pack if it doesn't exist already
+ if invalid:
+ # Since Stickerset_invalid will also try to create a pack we might as
+ # well just reuse that code and avoid typing it all again.
+ raise TelegramError("Stickerset_invalid")
+ context.bot.add_sticker_to_set(
+ user_id=user.id,
+ name=packname,
+ tgs_sticker = sticker_data if is_animated else None,
+ webm_sticker = sticker_data if is_video else None,
+ png_sticker = sticker_data if not is_animated and not is_video else None,
+ emojis=sticker_emoji,
+ )
+ msg.reply_text(
+ f"Sticker successfully added to [pack](t.me/addstickers/{packname})"
+ + f"\nEmoji is: {sticker_emoji}",
+ parse_mode=ParseMode.MARKDOWN,
+ )
+ except TelegramError as e:
+ if e.message == "Stickerset_invalid":
+ # if we need to make a sticker pack, make one and make this the
+ # first sticker in the pack.
+ makepack_internal(
+ update,
+ context,
+ msg,
+ user,
+ sticker_emoji,
+ packname,
+ packnum,
+ tgs_sticker=sticker_data if is_animated else None,
+ webm_sticker=sticker_data if is_video else None,
+ png_sticker=sticker_data if not is_animated and not is_video else None,
)
+ elif e.message == "Stickers_too_much":
+ msg.reply_text("Max packsize reached. Press F to pay respecc.")
+ elif e.message == "Invalid sticker emojis":
+ msg.reply_text("I can't kang with that emoji!")
+ elif e.message == "Sticker_video_nowebm":
msg.reply_text(
- f"Sticker successfully added to [pack](t.me/addstickers/{packname})"
- + f"\nEmoji is: {sticker_emoji}",
+ "This media format isn't supported, I need it in a webm format, "
+ "[see this guide](https://core.telegram.org/stickers/webm-vp9-encoding).",
parse_mode=ParseMode.MARKDOWN,
+ disable_web_page_preview = True,
+ )
+ elif e.message == "Internal Server Error: sticker set not found (500)":
+ msg.reply_text(
+ f"Sticker successfully added to [pack](t.me/addstickers/{packname})\n"
+ + f"Emoji is: {sticker_emoji}", parse_mode=ParseMode.MARKDOWN
)
- except OSError as e:
- msg.reply_text("I can only kang images m8.")
- print(e)
- return
- except TelegramError as e:
- if e.message == "Stickerset_invalid":
- makepack_internal(
- update,
- context,
- msg,
- user,
- sticker_emoji,
- packname,
- packnum,
- png_sticker=open("kangsticker.png", "rb"),
- )
- elif e.message == "Sticker_png_dimensions":
- im.save(kangsticker, "PNG")
- context.bot.add_sticker_to_set(
- user_id=user.id,
- name=packname,
- png_sticker=open("kangsticker.png", "rb"),
- emojis=sticker_emoji,
- )
- msg.reply_text(
- "Sticker successfully added to [pack](t.me/addstickers/%s)"
- % packname
- + "\n"
- + "Emoji is:"
- + " "
- + sticker_emoji,
- parse_mode=ParseMode.MARKDOWN,
- )
- elif e.message == "Invalid sticker emojis":
- msg.reply_text("Invalid emoji(s).")
- elif e.message == "Stickers_too_much":
- msg.reply_text("Max packsize reached. Press F to pay respecc.")
- elif e.message == "Internal Server Error: sticker set not found (500)":
- msg.reply_text(
- "Sticker successfully added to [pack](t.me/addstickers/%s)"
- % packname
- + "\n"
- "Emoji is:" + " " + sticker_emoji,
- parse_mode=ParseMode.MARKDOWN,
- )
- print(e)
- else:
- packs = "Please reply to a sticker, or image to kang it!\nOh, by the way. here are your packs:\n"
- if packnum > 0:
- firstpackname = "a" + str(user.id) + "_by_" + context.bot.username
- for i in range(0, packnum + 1):
- if i == 0:
- packs += f"[pack](t.me/addstickers/{firstpackname})\n"
- else:
- packs += f"[pack{i}](t.me/addstickers/{packname})\n"
else:
- packs += f"[pack](t.me/addstickers/{packname})"
- msg.reply_text(packs, parse_mode=ParseMode.MARKDOWN)
- try:
- if os.path.isfile("kangsticker.png"):
- os.remove("kangsticker.png")
- elif os.path.isfile("kangsticker.tgs"):
- os.remove("kangsticker.tgs")
- except:
- pass
+ msg.reply_text(f"Oops! looks like something happened that shouldn't happen! ({e.message})")
+ raise
def makepack_internal(
@@ -383,63 +372,68 @@ def makepack_internal(
packname,
packnum,
png_sticker=None,
+ webm_sticker=None,
tgs_sticker=None,
):
- name = user.first_name
- name = name[:50]
+ name = user.first_name[:50]
try:
extra_version = ""
if packnum > 0:
- extra_version = " " + str(packnum)
- if png_sticker:
- success = context.bot.create_new_sticker_set(
- user.id,
- packname,
- f"{name}s kang pack" + extra_version,
- png_sticker=png_sticker,
- emojis=emoji,
- )
- if tgs_sticker:
- success = context.bot.create_new_sticker_set(
- user.id,
- packname,
- f"{name}s animated kang pack" + extra_version,
- tgs_sticker=tgs_sticker,
- emojis=emoji,
- )
+ extra_version = f" {packnum}"
+ success = context.bot.create_new_sticker_set(
+ user.id,
+ packname,
+ f"{name}s {'animated ' if tgs_sticker else 'video ' if webm_sticker else ''}kang pack{extra_version}",
+ tgs_sticker=tgs_sticker or None,
+ webm_sticker=webm_sticker or None,
+ png_sticker=png_sticker or None,
+ emojis=emoji,
+ )
except TelegramError as e:
- print(e)
- if e.message == "Sticker set name is already occupied":
+ #print(e)
+ if e.message == 'Sticker set name is already occupied':
msg.reply_text(
- "Your pack can be found [here](t.me/addstickers/%s)" % packname,
+ 'Your pack can be found [here](t.me/addstickers/%s)'
+ % packname,
parse_mode=ParseMode.MARKDOWN,
)
- elif e.message in ("Peer_id_invalid", "bot was blocked by the user"):
+
+ return
+ elif e.message in ('Peer_id_invalid', 'bot was blocked by the user'):
msg.reply_text(
- "Contact me in PM first.",
+ 'Contact me in PM first.',
reply_markup=InlineKeyboardMarkup(
[
[
InlineKeyboardButton(
- text="Start", url=f"t.me/{context.bot.username}"
+ text='Start',
+ url=f't.me/{context.bot.username}',
)
]
]
),
)
- elif e.message == "Internal Server Error: created sticker set not found (500)":
+
+ return
+ elif (
+ e.message
+ == 'Internal Server Error: created sticker set not found (500)'
+ ):
+ success = True
+ elif e.message == 'Sticker_video_nowebm':
msg.reply_text(
- "Sticker pack successfully created. Get it [here](t.me/addstickers/%s)"
- % packname,
+ "This media format isn't supported, I need it in a webm format, "
+ "[see this guide](https://core.telegram.org/stickers/webm-vp9-encoding).",
parse_mode=ParseMode.MARKDOWN,
+ disable_web_page_preview = True,
)
- return
-
+ return
+ else:
+ success = False
if success:
msg.reply_text(
- "Sticker pack successfully created. Get it [here](t.me/addstickers/%s)"
- % packname,
+ f"Sticker pack successfully created. Get it [here](t.me/addstickers/{packname})",
parse_mode=ParseMode.MARKDOWN,
)
else:
diff --git a/AstrakoBot/modules/super_users.py b/AstrakoBot/modules/super_users.py
index 15b2d1b476..25482f2fbf 100644
--- a/AstrakoBot/modules/super_users.py
+++ b/AstrakoBot/modules/super_users.py
@@ -59,6 +59,11 @@ def addsudo(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
rt = ""
@@ -121,6 +126,11 @@ def addsupport(
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
rt = ""
@@ -132,13 +142,21 @@ def addsupport(
with open(ELEVATED_USERS_FILE, "r") as infile:
data = json.load(infile)
+ if user_id in DEV_USERS:
+ message.reply_text("Huh? he is more than support!")
+ return ""
+
if user_id in SUDO_USERS:
- rt += "Demoted from sudo to support user"
- data["sudos"].remove(user_id)
- SUDO_USERS.remove(user_id)
+ if user.id in DEV_USERS:
+ rt += "Demoted from sudo to support user"
+ data["sudos"].remove(user_id)
+ SUDO_USERS.remove(user_id)
+ else:
+ message.reply_text("This user is already sudo")
+ return ""
if user_id in SUPPORT_USERS:
- message.reply_text("This user is already sudo")
+ message.reply_text("This user is already support")
return ""
if user_id in WHITELIST_USERS:
@@ -176,6 +194,11 @@ def addwhitelist(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
rt = ""
@@ -187,10 +210,18 @@ def addwhitelist(update: Update, context: CallbackContext) -> str:
with open(ELEVATED_USERS_FILE, "r") as infile:
data = json.load(infile)
+ if user_id in DEV_USERS:
+ message.reply_text("Huh? he is more than whitelist!")
+ return ""
+
if user_id in SUDO_USERS:
- rt += "Demoted from sudo to whitelist user"
- data["sudos"].remove(user_id)
- SUDO_USERS.remove(user_id)
+ if user.id in DEV_USERS:
+ rt += "Demoted from sudo to whitelist user"
+ data["sudos"].remove(user_id)
+ SUDO_USERS.remove(user_id)
+ else:
+ message.reply_text("This user is already sudo")
+ return ""
if user_id in SUPPORT_USERS:
rt += "Demoted from support user to whitelist user"
@@ -231,6 +262,11 @@ def removesudo(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
reply = check_user_id(user_id, bot)
@@ -277,6 +313,11 @@ def removesupport(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
reply = check_user_id(user_id, bot)
@@ -319,6 +360,11 @@ def removewhitelist(update: Update, context: CallbackContext) -> str:
chat = update.effective_chat
bot, args = context.bot, context.args
user_id = extract_user(message, args)
+
+ if not user_id:
+ message.reply_text("Invalid user!")
+ return ""
+
user_member = bot.getChat(user_id)
reply = check_user_id(user_id, bot)
@@ -357,14 +403,21 @@ def whitelistlist(update: Update, context: CallbackContext):
bot = context.bot
message = update.effective_message
msg = "Whitelist users:\n"
+ zombies = []
for each_user in WHITELIST_USERS:
user_id = int(each_user)
try:
user = bot.get_chat(user_id)
-
+ if not user.first_name:
+ zombies.append(user_id)
+ continue
msg += f"⢠{mention_html(user_id, html.escape(user.first_name))}\n"
except TelegramError:
pass
+ if zombies:
+ msg += "\nZombies:\n"
+ for user_id in zombies:
+ msg += f"⢠{mention_html(user_id, html.escape(str(user_id)))}\n"
message.reply_text(msg, parse_mode=ParseMode.HTML)
@@ -373,13 +426,21 @@ def supportlist(update: Update, context: CallbackContext):
bot = context.bot
message = update.effective_message
msg = "Support users:\n"
+ zombies = []
for each_user in SUPPORT_USERS:
user_id = int(each_user)
try:
user = bot.get_chat(user_id)
+ if not user.first_name:
+ zombies.append(user_id)
+ continue
msg += f"⢠{mention_html(user_id, html.escape(user.first_name))}\n"
except TelegramError:
pass
+ if zombies:
+ msg += "\nZombies:\n"
+ for user_id in zombies:
+ msg += f"⢠{mention_html(user_id, html.escape(str(user_id)))}\n"
message.reply_text(msg, parse_mode=ParseMode.HTML)
@@ -389,13 +450,21 @@ def sudolist(update: Update, context: CallbackContext):
message = update.effective_message
true_sudo = list(set(SUDO_USERS) - set(DEV_USERS))
msg = "Sudo users:\n"
+ zombies = []
for each_user in true_sudo:
user_id = int(each_user)
try:
user = bot.get_chat(user_id)
+ if not user.first_name:
+ zombies.append(user_id)
+ continue
msg += f"⢠{mention_html(user_id, html.escape(user.first_name))}\n"
except TelegramError:
pass
+ if zombies:
+ msg += "\nZombies:\n"
+ for user_id in zombies:
+ msg += f"⢠{mention_html(user_id, html.escape(str(user_id)))}\n"
message.reply_text(msg, parse_mode=ParseMode.HTML)
@@ -405,13 +474,21 @@ def devlist(update: Update, context: CallbackContext):
message = update.effective_message
true_dev = list(set(DEV_USERS) - {OWNER_ID})
msg = "Developer users:\n"
+ zombies = []
for each_user in true_dev:
user_id = int(each_user)
try:
user = bot.get_chat(user_id)
+ if not user.first_name:
+ zombies.append(user_id)
+ continue
msg += f"⢠{mention_html(user_id, html.escape(user.first_name))}\n"
except TelegramError:
pass
+ if zombies:
+ msg += "\nZombies:\n"
+ for user_id in zombies:
+ msg += f"⢠{mention_html(user_id, html.escape(str(user_id)))}\n"
message.reply_text(msg, parse_mode=ParseMode.HTML)
diff --git a/AstrakoBot/modules/systools.py b/AstrakoBot/modules/systools.py
index f195b89766..cb20897145 100644
--- a/AstrakoBot/modules/systools.py
+++ b/AstrakoBot/modules/systools.py
@@ -65,6 +65,16 @@ def get_size(bytes, suffix="B"):
def convert(speed):
return round(int(speed) / 1048576, 2)
+def get_network_speed():
+ old = psutil.net_io_counters()
+
+ time.sleep(1)
+
+ new = psutil.net_io_counters()
+
+ upload = (new.bytes_sent - old.bytes_sent)
+ download = (new.bytes_recv - old.bytes_recv)
+ return upload, download
@owner_plus
def shell(update: Update, context: CallbackContext):
@@ -122,21 +132,23 @@ def status(update: Update, context: CallbackContext):
uname = platform.uname()
msg += "*System information*\n"
msg += f"OS: `{uname.system}`\n"
- msg += f"Version: `{uname.version}`\n"
+ msg += f"Version: `{uname.version.split(' ')[0]}`\n"
msg += f"Release: `{uname.release}`\n"
msg += f"Processor: `{uname.processor}`\n"
boot_time_timestamp = psutil.boot_time()
bt = datetime.fromtimestamp(boot_time_timestamp)
msg += f"Boot time: `{bt.day}/{bt.month}/{bt.year} - {bt.hour}:{bt.minute}:{bt.second}`\n"
msg += f"CPU cores: `{psutil.cpu_count(logical=False)} physical, {psutil.cpu_count()} logical`\n"
- msg += f"CPU freq: `{psutil.cpu_freq().current:.2f}Mhz`\n"
+ msg += f"CPU freq: `{psutil.cpu_freq().current:.2f}Mhz`\n" if psutil.cpu_freq() else ""
msg += f"CPU usage: `{psutil.cpu_percent()}%`\n"
ram = psutil.virtual_memory()
- msg += f"RAM: `{get_size(ram.total)} - {get_size(ram.used)} used ({ram.percent}%)`\n"
+ msg += f"RAM: `{get_size(ram.total)} - {get_size(ram.used)} used ({round(ram.used / ram.total * 100, 2)}%)`\n"
disk = psutil.disk_usage('/')
msg += f"Disk usage: `{get_size(disk.total)} total - {get_size(disk.used)} used ({disk.percent}%)`\n"
swap = psutil.swap_memory()
msg += f"SWAP: `{get_size(swap.total)} - {get_size(swap.used)} used ({swap.percent}%)`\n"
+ upload, download = get_network_speed()
+ msg += f"Network: `⬠{get_size(download)}/s | ⬠{get_size(upload)}/s`\n"
message.reply_text(
text = msg,
diff --git a/AstrakoBot/modules/tts.py b/AstrakoBot/modules/tts.py
index b28cba4bde..e590fcf6d8 100644
--- a/AstrakoBot/modules/tts.py
+++ b/AstrakoBot/modules/tts.py
@@ -1,7 +1,9 @@
+import uuid
import os
from datetime import datetime
from typing import List
from gtts import gTTS
+from gtts.tts import gTTSError
from telegram import Update, ChatAction, ParseMode
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
@@ -12,48 +14,53 @@
def tts(update: Update, context: CallbackContext):
- args = context.args
message = update.effective_message
chat = update.effective_chat
- delmsg = ""
+ args = context.args
+ delmsg = None
+ text = ""
if message.reply_to_message:
- delmsg = message.reply_to_message.text
-
- if args:
- delmsg = " ".join(args).lower()
-
- current_time = datetime.strftime(datetime.now(), "%d.%m.%Y %H:%M:%S")
- filename = datetime.now().strftime("%d%m%y-%H%M%S%f")
- update.message.chat.send_action(ChatAction.RECORD_AUDIO)
- lang = "ml"
- tts = gTTS(delmsg, lang)
- tts.save("k.mp3")
- with open("k.mp3", "rb") as f:
- linelist = list(f)
- linecount = len(linelist)
- if linecount == 1:
- update.message.chat.send_action(ChatAction.RECORD_AUDIO)
- lang = "en"
- tts = gTTS(delmsg, lang)
- tts.save("k.mp3")
- with open("k.mp3", "rb") as speech:
- delmsg = update.message.reply_voice(speech, quote=False)
-
- os.remove("k.mp3")
-
+ text = message.reply_to_message.text or message.reply_to_message.caption or ""
+ elif args:
+ text = " ".join(args).lower()
else:
- delmsg = message.reply_text(
- "Reply a message or give something like:\n`/tts `",
- parse_mode = ParseMode.MARKDOWN
+ message.reply_text(
+ "Reply to a message or use: /tts ",
+ parse_mode=ParseMode.MARKDOWN
)
+ return
+
+ if not text.strip():
+ message.reply_text("Please provide valid text to convert to speech.")
+ return
+
+ filename = f"tts_{uuid.uuid4()}.mp3"
+
+ try:
+ message.chat.send_action(ChatAction.RECORD_AUDIO)
+
+ tts = gTTS(text=text, lang='en', tld='com')
+ tts.save(filename)
+
+ with open(filename, 'rb') as audio_file:
+ delmsg = message.reply_voice(
+ voice=audio_file,
+ quote=False
+ )
- cleartime = get_clearcmd(chat.id, "tts")
+ cleartime = get_clearcmd(chat.id, "tts")
- if cleartime:
- context.dispatcher.run_async(delete, delmsg, cleartime.time)
+ if cleartime:
+ context.dispatcher.run_async(delete, delmsg, cleartime.time)
+ except Exception as e:
+ message.reply_text(f"Failed to connect to the TTS service: {e}")
+ try:
+ os.remove(filename)
+ except:
+ pass
TTS_HANDLER = DisableAbleCommandHandler("tts", tts, run_async=True)
dispatcher.add_handler(TTS_HANDLER)
diff --git a/AstrakoBot/modules/ud.py b/AstrakoBot/modules/ud.py
index bbed60b121..d723a839f7 100644
--- a/AstrakoBot/modules/ud.py
+++ b/AstrakoBot/modules/ud.py
@@ -5,7 +5,7 @@
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from telegram import ParseMode, Update
from telegram.ext import CallbackContext, run_async
-
+import re
def ud(update: Update, context: CallbackContext):
message = update.effective_message
@@ -18,7 +18,15 @@ def ud(update: Update, context: CallbackContext):
reply_text = f'*{text}*\n\n{results["list"][0]["definition"]}\n\n_{results["list"][0]["example"]}_'
except:
reply_text = "No results found."
- delmsg = message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN)
+ try:
+ delmsg = message.reply_text(reply_text, parse_mode=ParseMode.MARKDOWN)
+ except:
+ reply_text = re.sub(r'\*(.*?)\*', r'\1', reply_text)
+ plain_text = reply_text.split('\n')
+ for i in range(1, len(plain_text)):
+ plain_text[i] = plain_text[i].replace('_', '')
+ plain_text = '\n'.join(plain_text)
+ delmsg = message.reply_text(plain_text)
cleartime = get_clearcmd(chat.id, "ud")
diff --git a/AstrakoBot/modules/userinfo.py b/AstrakoBot/modules/userinfo.py
index 147009b44b..e89e572555 100644
--- a/AstrakoBot/modules/userinfo.py
+++ b/AstrakoBot/modules/userinfo.py
@@ -3,15 +3,23 @@
import os
import requests
+from telegram.user import User
from telethon.tl.functions.channels import GetFullChannelRequest
from telethon.tl.types import ChannelParticipantsAdmins
-from telethon import events
+from telethon import events, types
from telegram import MAX_MESSAGE_LENGTH, ParseMode, Update, MessageEntity
from telegram.ext import CallbackContext, CommandHandler, Filters
from telegram.ext.dispatcher import run_async
from telegram.error import BadRequest
from telegram.utils.helpers import escape_markdown, mention_html
+from telethon.errors import (
+ ChannelInvalidError,
+ ChannelPrivateError,
+ ChatAdminRequiredError,
+ PeerIdInvalidError,
+ UserNotParticipantError,
+)
from AstrakoBot import (
DEV_USERS,
@@ -35,7 +43,6 @@
from AstrakoBot.modules.helper_funcs.misc import delete
from AstrakoBot import telethn as AstrakoBotTelethonClient, SUDO_USERS, SUPPORT_USERS
-
def get_id(update: Update, context: CallbackContext):
bot, args = context.bot, context.args
message = update.effective_message
@@ -58,12 +65,17 @@ def get_id(update: Update, context: CallbackContext):
)
else:
-
- user = bot.get_chat(user_id)
- msg.reply_text(
- f"{html.escape(user.first_name)}'s id is {user.id}.",
- parse_mode=ParseMode.HTML,
- )
+ try:
+ user = bot.get_chat(user_id)
+ msg.reply_text(
+ f"{html.escape(user.first_name)}'s id is {user.id}.",
+ parse_mode=ParseMode.HTML,
+ )
+ except:
+ msg.reply_text(
+ f"Their id is {user_id}.",
+ parse_mode=ParseMode.HTML,
+ )
else:
@@ -82,37 +94,130 @@ def get_id(update: Update, context: CallbackContext):
events.NewMessage(pattern="/ginfo ", from_users=(SUDO_USERS or []) + (SUPPORT_USERS or []))
)
async def group_info(event) -> None:
- chat = event.text.split(" ", 1)[1]
+ target_entity = None
+ entity_id_str = None
+
+ if event.is_reply:
+ replied_msg = await event.get_reply_message()
+ if replied_msg and replied_msg.sender_id:
+ target_entity = replied_msg.sender_id
+ entity_id_str = str(target_entity)
+ else:
+ await event.reply("Could not identify the user from the replied message.")
+ return
+ else:
+ parts = event.text.split(" ", 1)
+ if len(parts) > 1:
+ entity_id_str = parts[1].strip()
+ if entity_id_str.startswith('@'):
+ target_entity = entity_id_str
+ else:
+ try:
+ target_entity = int(entity_id_str)
+ except ValueError:
+ await event.reply("Invalid ID or username format.")
+ return
+ else:
+ target_entity = event.chat_id
+ entity_id_str = str(target_entity)
+
+ if target_entity is None:
+ await event.reply("Could not determine the target entity.")
+ return
+
try:
- entity = await event.client.get_entity(chat)
- totallist = await event.client.get_participants(
- entity, filter=ChannelParticipantsAdmins
- )
- ch_full = await event.client(GetFullChannelRequest(channel=entity))
- except:
+ entity = await event.client.get_entity(target_entity)
+ except (ValueError, PeerIdInvalidError, ChannelInvalidError, ChannelPrivateError) as e:
await event.reply(
- "Can't for some reason, maybe it is a private one or that I am banned there."
+ f"Could not find or access the specified chat/channel (`{entity_id_str}`). "
+ "It might be invalid, private, or I might lack permissions."
)
return
- msg = f"**ID**: `{entity.id}`"
- msg += f"\n**Title**: `{entity.title}`"
- msg += f"\n**Datacenter**: `{entity.photo.dc_id}`"
- msg += f"\n**Video PFP**: `{entity.photo.has_video}`"
- msg += f"\n**Supergroup**: `{entity.megagroup}`"
- msg += f"\n**Restricted**: `{entity.restricted}`"
- msg += f"\n**Scam**: `{entity.scam}`"
- msg += f"\n**Slowmode**: `{entity.slowmode_enabled}`"
- if entity.username:
- msg += f"\n**Username**: {entity.username}"
- msg += "\n\n**Member Stats:**"
- msg += f"\n`Admins:` `{len(totallist)}`"
- msg += f"\n`Users`: `{totallist.total}`"
- msg += "\n\n**Admins List:**"
- for x in totallist:
- msg += f"\n⢠[{x.id}](tg://user?id={x.id})"
- msg += f"\n\n**Description**:\n`{ch_full.full_chat.about}`"
- await event.reply(msg)
+ except Exception as e:
+ await event.reply(f"An unexpected error occurred while fetching info for `{entity_id_str}`.")
+ return
+ msg = f"**Info for:** `{entity_id_str}`\n"
+ msg += f"**Type:** `{type(entity).__name__}`\n"
+ msg += f"**ID:** `{entity.id}`\n"
+
+ if isinstance(entity, (types.Chat, types.Channel)):
+ msg += f"**Title:** `{entity.title}`\n"
+ if hasattr(entity, 'username') and entity.username:
+ msg += f"**Username:** @{entity.username}\n"
+ else:
+ msg += f"**Username:** `None`\n"
+
+ if hasattr(entity.photo, 'dc_id'):
+ msg += f"**Photo DC:** `{entity.photo.dc_id}`\n"
+ if hasattr(entity.photo, 'has_video'):
+ msg += f"**Video PFP:** `{entity.photo.has_video}`\n"
+
+ msg += f"**Scam:** `{getattr(entity, 'scam', 'N/A')}`\n"
+ msg += f"**Restricted:** `{getattr(entity, 'restricted', 'N/A')}`\n"
+ if getattr(entity, 'restriction_reason', None):
+ msg += f"**Restriction Reason:** `{entity.restriction_reason}`\n"
+
+ if isinstance(entity, types.Channel):
+ msg += f"**Supergroup:** `{entity.megagroup}`\n"
+ msg += f"**Broadcast Channel:** `{entity.broadcast}`\n"
+ msg += f"**Verified:** `{entity.verified}`\n"
+ msg += f"**Gigagroup:** `{getattr(entity, 'gigagroup', 'N/A')}`\n"
+ msg += f"**Slowmode Enabled:** `{entity.slowmode_enabled}`\n"
+
+ full_chat_info = None
+ admin_list = []
+ participant_count = "N/A"
+ admin_count = "N/A"
+ about = "N/A"
+
+ try:
+ if isinstance(entity, types.Channel):
+ full_chat_info = await event.client(GetFullChannelRequest(channel=entity))
+ about = full_chat_info.full_chat.about
+ participant_count = getattr(full_chat_info.full_chat, 'participants_count', 'N/A')
+ elif isinstance(entity, types.Chat):
+ full_chat_info = await event.client(GetFullChatRequest(chat_id=entity.id))
+ about = getattr(full_chat_info.full_chat, 'about', 'N/A')
+ if hasattr(full_chat_info, 'users'):
+ participant_count = len(full_chat_info.users)
+
+ try:
+ admins = await event.client.get_participants(
+ entity, filter=ChannelParticipantsAdmins
+ )
+ admin_count = len(admins) if admins else 0 # Handle None case
+ admin_list = [f"⢠[{admin.id}](tg://user?id={admin.id})" for admin in admins]
+ except ChatAdminRequiredError:
+ admin_list.append("_(Admin permissions required to list)_")
+ admin_count = "N/A (No Perms)"
+ except UserNotParticipantError:
+ admin_list.append("_(Bot is not in this chat/channel)_")
+ admin_count = "N/A (Not Participant)"
+ except Exception as e:
+ admin_list.append("_(Could not retrieve admin list)_")
+ admin_count = "N/A (Error)"
+
+
+ msg += "\n**Stats:**\n"
+ msg += f"`Participants:` `{participant_count}`\n"
+ msg += f"`Admins:` `{admin_count}`\n"
+
+ if admin_list:
+ msg += "\n**Admins List:**"
+ msg += "\n" + "\n".join(admin_list) # Join list items with newlines
+
+ msg += f"\n\n**Description:**\n`{about}`"
+
+ except (ChatAdminRequiredError, UserNotParticipantError) as e:
+ msg += "\n\n**(Could not retrieve full details like description or participant counts due to permissions or bot not being a participant)**"
+ except Exception as e:
+ msg += "\n\n**(An error occurred while retrieving full details)**"
+
+ else:
+ msg += "\n**(Unsupported entity type)**"
+
+ await event.reply(msg, link_preview=False)
def gifid(update: Update, context: CallbackContext):
msg = update.effective_message
@@ -131,11 +236,27 @@ def info(update: Update, context: CallbackContext):
chat = update.effective_chat
user_id = extract_user(update.effective_message, args)
- if user_id:
+ if user_id and int(user_id) != 777000 and int(user_id) != 1087968824:
user = bot.get_chat(user_id)
+ elif user_id and int(user_id) == 777000:
+ message.reply_text(
+ "This is Telegram. Unless you manually entered this reserved account's ID, it is likely a old broadcast from a linked channel."
+ )
+ return
+
+ elif user_id and int(user_id) == 1087968824:
+ message.reply_text(
+ "This is Group Anonymous Bot. Unless you manually entered this reserved account's ID, it is likely a broadcast from a linked channel or anonymously sent message."
+ )
+ return
+
elif not message.reply_to_message and not args:
- user = message.from_user
+ user = (
+ message.sender_chat
+ if message.sender_chat is not None
+ else message.from_user
+ )
elif not message.reply_to_message and (
not args
@@ -157,128 +278,163 @@ def info(update: Update, context: CallbackContext):
else:
return
+
+ rep = message.reply_text("Appraising...", parse_mode=ParseMode.HTML)
+
+ if hasattr(user, 'type') and user.type != "private":
+ text = (
+ f"Chat Info: "
+ f"\nID: {user.id}"
+ f"\nTitle: {user.title}"
+ )
+ if user.username:
+ text += f"\nUsername: @{html.escape(user.username)}"
+ text += f"\nChat Type: {user.type.capitalize()}"
+
+ if INFOPIC:
+ try:
+ profile = bot.getChat(user.id).photo
+ _file = bot.get_file(profile["big_file_id"])
+ _file.download(f"{user.id}.png")
+
+ delmsg = message.reply_document(
+ document=open(f"{user.id}.png", "rb"),
+ caption=(text),
+ parse_mode=ParseMode.HTML,
+ )
- rep = message.reply_text("Appraising...", parse_mode=ParseMode.HTML)
+ os.remove(f"{user.id}.png")
+ # Incase chat don't have profile pic, send normal text
+ except:
+ delmsg = message.reply_text(
+ text, parse_mode=ParseMode.HTML, disable_web_page_preview=True
+ )
- text = (
- f"User info:\n"
- f"ID: {user.id}\n"
- f"First Name: {html.escape(user.first_name)}"
- )
+ else:
+ delmsg = message.reply_text(
+ text, parse_mode=ParseMode.HTML, disable_web_page_preview=True
+ )
- if user.last_name:
- text += f"\nLast Name: {html.escape(user.last_name)}"
+ else:
+ text = (
+ f"User info:\n"
+ f"ID: {user.id}\n"
+ f"First Name: {mention_html(user.id, user.first_name or 'None')}"
+ )
- if user.username:
- text += f"\nUsername: @{html.escape(user.username)}"
+ if user.last_name:
+ text += f"\nLast Name: {html.escape(user.last_name)}"
- text += f"\nPermalink: {mention_html(user.id, 'link')}"
+ if user.username:
+ text += f"\nUsername: @{html.escape(user.username)}"
- if chat.type != "private" and user_id != bot.id:
- _stext = "\nPresence: {}"
+ text += f"\nPermalink: {mention_html(user.id, 'link')}"
- afk_st = is_afk(user.id)
- if afk_st:
- text += _stext.format("AFK")
- else:
- status = status = bot.get_chat_member(chat.id, user.id).status
- if status:
- if status == "left":
- text += _stext.format("Not here")
- if status == "kicked":
- text += _stext.format("Banned")
- elif status == "member":
- text += _stext.format("Detected")
- elif status in {"administrator", "creator"}:
- text += _stext.format("Admin")
+ if chat.type != "private" and user_id != bot.id:
+ _stext = "\nPresence: {}"
- try:
- spamwtc = sw.get_ban(int(user.id))
- if spamwtc:
- text += "\n\nThis person is Spamwatched!"
- text += f"\nReason: {spamwtc.reason}"
- text += "\nAppeal at @SpamWatchSupport"
- else:
- pass
- except:
- pass # don't crash if api is down somehow...
-
- disaster_level_present = False
-
- if user.id == OWNER_ID:
- text += "\n\nUser level: god"
- disaster_level_present = True
- elif user.id in DEV_USERS:
- text += "\n\nUser level: developer"
- disaster_level_present = True
- elif user.id in SUDO_USERS:
- text += "\n\nUser level: sudo"
- disaster_level_present = True
- elif user.id in SUPPORT_USERS:
- text += "\n\nUser level: support"
- disaster_level_present = True
- elif user.id in WHITELIST_USERS:
- text += "\n\nUser level: whitelist"
- disaster_level_present = True
-
- # if disaster_level_present:
- # text += ' [?]'.format(
- # bot.username)
+ afk_st = is_afk(user.id)
+ if afk_st:
+ text += _stext.format("AFK")
+ else:
+ status = bot.get_chat_member(chat.id, user.id).status
+ if status:
+ if status == "left":
+ text += _stext.format("Not here")
+ if status == "kicked":
+ text += _stext.format("Banned")
+ elif status == "member":
+ text += _stext.format("Detected")
+ elif status in {"administrator", "creator"}:
+ text += _stext.format("Admin")
- try:
- user_member = chat.get_member(user.id)
- if user_member.status == "administrator":
- result = requests.post(
- f"https://api.telegram.org/bot{TOKEN}/getChatMember?chat_id={chat.id}&user_id={user.id}"
- )
- result = result.json()["result"]
- if "custom_title" in result.keys():
- custom_title = result["custom_title"]
- text += f"\n\nTitle:\n{custom_title}"
- except BadRequest:
- pass
-
- for mod in USER_INFO:
try:
- mod_info = mod.__user_info__(user.id).strip()
- except TypeError:
- mod_info = mod.__user_info__(user.id, chat.id).strip()
- if mod_info:
- text += "\n\n" + mod_info
+ spamwtc = sw.get_ban(int(user.id))
+ if spamwtc:
+ text += "\n\nThis person is Spamwatched!"
+ text += f"\nReason: {spamwtc.reason}"
+ text += "\nAppeal at @SpamWatchSupport"
+ else:
+ pass
+ except:
+ pass # don't crash if api is down somehow...
+
+ disaster_level_present = False
+
+ if user.id == OWNER_ID:
+ text += "\n\nUser level: god"
+ disaster_level_present = True
+ elif user.id in DEV_USERS:
+ text += "\n\nUser level: developer"
+ disaster_level_present = True
+ elif user.id in SUDO_USERS:
+ text += "\n\nUser level: sudo"
+ disaster_level_present = True
+ elif user.id in SUPPORT_USERS:
+ text += "\n\nUser level: support"
+ disaster_level_present = True
+ elif user.id in WHITELIST_USERS:
+ text += "\n\nUser level: whitelist"
+ disaster_level_present = True
+
+ # if disaster_level_present:
+ # text += ' [?]'.format(
+ # bot.username)
- if INFOPIC:
try:
- profile = context.bot.get_user_profile_photos(user.id).photos[0][-1]
- _file = bot.get_file(profile["file_id"])
- _file.download(f"{user.id}.png")
+ user_member = chat.get_member(user.id)
+ if user_member.status == "administrator":
+ result = requests.post(
+ f"https://api.telegram.org/bot{TOKEN}/getChatMember?chat_id={chat.id}&user_id={user.id}"
+ )
+ result = result.json()["result"]
+ if "custom_title" in result.keys():
+ custom_title = result["custom_title"]
+ text += f"\n\nTitle:\n{custom_title}"
+ except BadRequest:
+ pass
- delmsg = message.reply_document(
- document=open(f"{user.id}.png", "rb"),
- caption=(text),
- parse_mode=ParseMode.HTML,
- )
+ for mod in USER_INFO:
+ try:
+ mod_info = mod.__user_info__(user.id).strip()
+ except TypeError:
+ mod_info = mod.__user_info__(user.id, chat.id).strip()
+ if mod_info:
+ text += "\n\n" + mod_info
+
+ text += "\n\n" + biome(user.id)
+
+ if INFOPIC:
+ try:
+ profile = context.bot.get_user_profile_photos(user.id).photos[0][-1]
+ _file = bot.get_file(profile["file_id"])
+ _file.download(f"{user.id}.png")
+
+ delmsg = message.reply_document(
+ document=open(f"{user.id}.png", "rb"),
+ caption=(text),
+ parse_mode=ParseMode.HTML,
+ )
- os.remove(f"{user.id}.png")
- # Incase user don't have profile pic, send normal text
- except IndexError:
+ os.remove(f"{user.id}.png")
+ # Incase user don't have profile pic, send normal text
+ except IndexError:
+ delmsg = message.reply_text(
+ text, parse_mode=ParseMode.HTML, disable_web_page_preview=True
+ )
+
+ else:
delmsg = message.reply_text(
text, parse_mode=ParseMode.HTML, disable_web_page_preview=True
- )
-
- else:
- delmsg = message.reply_text(
- text, parse_mode=ParseMode.HTML, disable_web_page_preview=True
- )
-
+ )
+
rep.delete()
-
-
+
cleartime = get_clearcmd(chat.id, "info")
-
+
if cleartime:
context.dispatcher.run_async(delete, delmsg, cleartime.time)
-
def about_me(update: Update, context: CallbackContext):
bot, args = context.bot, context.args
message = update.effective_message
@@ -434,7 +590,7 @@ def gdpr(update: Update, context: CallbackContext):
parse_mode=ParseMode.MARKDOWN)
-def __user_info__(user_id):
+def biome(user_id):
bio = html.escape(sql.get_user_bio(user_id) or "")
me = html.escape(sql.get_user_me_info(user_id) or "")
result = ""
@@ -478,7 +634,7 @@ def __gdpr__(user_id):
"""
SET_BIO_HANDLER = DisableAbleCommandHandler("setbio", set_about_bio, run_async=True)
-GET_BIO_HANDLER = DisableAbleCommandHandler("bio", about_bio)
+GET_BIO_HANDLER = DisableAbleCommandHandler("bio", about_bio, run_async=True)
STATS_HANDLER = CommandHandler("stats", stats, run_async=True)
ID_HANDLER = DisableAbleCommandHandler("id", get_id, run_async=True)
diff --git a/AstrakoBot/modules/users.py b/AstrakoBot/modules/users.py
index bdad0aff9a..f64c24e2b7 100644
--- a/AstrakoBot/modules/users.py
+++ b/AstrakoBot/modules/users.py
@@ -1,3 +1,4 @@
+import contextlib
from io import BytesIO
from time import sleep
@@ -15,6 +16,7 @@
from AstrakoBot import DEV_USERS, LOGGER, OWNER_ID, dispatcher
from AstrakoBot.modules.helper_funcs.chat_status import dev_plus, sudo_plus
from AstrakoBot.modules.sql.users_sql import get_all_users
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member
USERS_GROUP = 4
CHAT_GROUP = 5
@@ -99,23 +101,57 @@ def broadcast(update: Update, context: CallbackContext):
)
-def log_user(update: Update, context: CallbackContext):
+def log_user(update: Update, _: CallbackContext):
chat = update.effective_chat
msg = update.effective_message
sql.update_user(msg.from_user.id, msg.from_user.username, chat.id, chat.title)
- if msg.reply_to_message:
+ if rep := msg.reply_to_message:
sql.update_user(
- msg.reply_to_message.from_user.id,
- msg.reply_to_message.from_user.username,
+ rep.from_user.id,
+ rep.from_user.username,
chat.id,
chat.title,
)
+ if rep.forward_from:
+ sql.update_user(
+ rep.forward_from.id,
+ rep.forward_from.username,
+ )
+
+ if rep.entities:
+ for entity in rep.entities:
+ if entity.type in ["text_mention", "mention"]:
+ with contextlib.suppress(AttributeError):
+ sql.update_user(entity.user.id, entity.user.username)
+ if rep.sender_chat and not rep.is_automatic_forward:
+ sql.update_user(
+ rep.sender_chat.id,
+ rep.sender_chat.username,
+ chat.id,
+ chat.title,
+ )
+
if msg.forward_from:
sql.update_user(msg.forward_from.id, msg.forward_from.username)
+ if msg.entities:
+ for entity in msg.entities:
+ if entity.type in ["text_mention", "mention"]:
+ with contextlib.suppress(AttributeError):
+ sql.update_user(entity.user.id, entity.user.username)
+
+ if msg.new_chat_members:
+ for user in msg.new_chat_members:
+ if user.id == msg.from_user.id: # we already added that in the first place
+ continue
+ sql.update_user(user.id, user.username, chat.id, chat.title)
+
+ if req := update.chat_join_request:
+ sql.update_user(req.from_user.id, req.from_user.username, chat.id, chat.title)
+
@sudo_plus
def chats(update: Update, context: CallbackContext):
@@ -146,9 +182,9 @@ def chats(update: Update, context: CallbackContext):
def chat_checker(update: Update, context: CallbackContext):
bot = context.bot
try:
- if update.effective_message.chat.get_member(bot.id).can_send_messages is False:
+ if get_bot_member(update.effective_chat.id).can_send_messages is False:
bot.leaveChat(update.effective_message.chat.id)
- except Unauthorized:
+ except:
pass
diff --git a/AstrakoBot/modules/wallpaper.py b/AstrakoBot/modules/wallpaper.py
index 3b42e8e2b9..3004eaa4db 100644
--- a/AstrakoBot/modules/wallpaper.py
+++ b/AstrakoBot/modules/wallpaper.py
@@ -1,6 +1,6 @@
from random import randint
-import requests as r
+import requests
from AstrakoBot import SUPPORT_CHAT, WALL_API, dispatcher
from AstrakoBot.modules.disable import DisableAbleCommandHandler
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
@@ -8,8 +8,7 @@
from telegram import Update
from telegram.ext import CallbackContext, run_async
-# Wallpapers module by @TheRealPhoenix using wall.alphacoders.com
-
+PIXABAY_API = WALL_API
def wall(update: Update, context: CallbackContext):
chat_id = update.effective_chat.id
@@ -23,21 +22,20 @@ def wall(update: Update, context: CallbackContext):
return
else:
caption = query
- term = query.replace(" ", "%20")
- json_rep = r.get(
- f"https://wall.alphacoders.com/api2.0/get.php?auth={WALL_API}&method=search&term={term}"
- ).json()
- if not json_rep.get("success"):
+ term = query.replace(" ", "+")
+ response = requests.get(f"https://pixabay.com/api/?key={PIXABAY_API}&q={term}&image_type=photo&per_page=200")
+
+ if response.status_code != 200:
msg.reply_text(f"An error occurred! Report this @{SUPPORT_CHAT}")
else:
- wallpapers = json_rep.get("wallpapers")
+ data = response.json()
+ wallpapers = data.get("hits")
if not wallpapers:
msg.reply_text("No results found! Refine your search.")
return
else:
- index = randint(0, len(wallpapers) - 1) # Choose random index
- wallpaper = wallpapers[index]
- wallpaper = wallpaper.get("url_image")
+ index = randint(0, len(wallpapers) - 1) if len(wallpapers) > 1 else 0
+ wallpaper = wallpapers[index].get("largeImageURL")
wallpaper = wallpaper.replace("\\", "")
delmsg_preview = bot.send_photo(
chat_id,
diff --git a/AstrakoBot/modules/warns.py b/AstrakoBot/modules/warns.py
index 2858d32d2b..8e93602887 100644
--- a/AstrakoBot/modules/warns.py
+++ b/AstrakoBot/modules/warns.py
@@ -8,10 +8,11 @@
from AstrakoBot.modules.helper_funcs.chat_status import (
bot_admin,
can_restrict,
- is_user_admin,
user_admin,
+ user_can_ban,
user_admin_no_reply,
can_delete,
+ is_user_ban_protected,
)
from AstrakoBot.modules.helper_funcs.extraction import (
extract_text,
@@ -23,6 +24,7 @@
from AstrakoBot.modules.helper_funcs.string_handling import split_quotes
from AstrakoBot.modules.log_channel import loggable
from AstrakoBot.modules.sql import warns_sql as sql
+from AstrakoBot.modules.helper_funcs.admin_status import user_is_admin
from telegram import (
CallbackQuery,
Chat,
@@ -54,8 +56,8 @@
def warn(
user: User, chat: Chat, reason: str, message: Message, warner: User = None
) -> str:
- if is_user_admin(chat, user.id):
- # message.reply_text("Damn admins, They are too far to be One Punched!")
+ if is_user_ban_protected(chat, user.id):
+ message.reply_text("Damn admins, They are too far to be One Punched!")
return
if user.id in WHITELIST_USERS:
@@ -85,7 +87,7 @@ def warn(
)
else: # ban
- chat.kick_member(user.id)
+ chat.ban_member(user.id)
reply = (
f"āBan Event\n"
f" ⢠User: {mention_html(user.id, user.first_name)}\n"
@@ -135,7 +137,7 @@ def warn(
)
try:
- message.reply_text(reply, reply_markup=keyboard, parse_mode=ParseMode.HTML)
+ message.reply_text(reply, reply_markup=keyboard, parse_mode=ParseMode.HTML, allow_sending_without_reply=True)
except BadRequest as excp:
if excp.message == "Reply message not found":
# Do not reply
@@ -179,6 +181,7 @@ def button(update: Update, context: CallbackContext) -> str:
@user_admin
+@user_can_ban
@can_restrict
@loggable
def warn_user(update: Update, context: CallbackContext) -> str:
@@ -194,6 +197,7 @@ def warn_user(update: Update, context: CallbackContext) -> str:
if (
message.reply_to_message
and message.reply_to_message.from_user.id == user_id
+ and not message.text.startswith("/d")
):
return warn(
message.reply_to_message.from_user,
@@ -208,8 +212,34 @@ def warn_user(update: Update, context: CallbackContext) -> str:
message.reply_text("That looks like an invalid User ID to me.")
return ""
+@user_admin
+@user_can_ban
+@bot_admin
+@loggable
+def rm_last_warn(update: Update, context: CallbackContext) -> str:
+ args = context.args
+ message: Optional[Message] = update.effective_message
+ chat: Optional[Chat] = update.effective_chat
+ user: Optional[User] = update.effective_user
+
+ user_id = extract_user(message, args)
+
+ if user_id:
+ sql.remove_warn(user_id, chat.id)
+ message.reply_text("Latest warning has been removed!")
+ unwarned = chat.get_member(user_id).user
+ return (
+ f"{html.escape(chat.title)}:\n"
+ f"#UNWARN\n"
+ f"Admin: {mention_html(user.id, user.first_name)}\n"
+ f"User: {mention_html(unwarned.id, unwarned.first_name)}"
+ )
+ else:
+ message.reply_text("No user has been designated!")
+ return ""
@user_admin
+@user_can_ban
@bot_admin
@loggable
def reset_warns(update: Update, context: CallbackContext) -> str:
@@ -266,6 +296,7 @@ def warns(update: Update, context: CallbackContext):
# Dispatcher handler stop - do not async
@user_admin
+@user_can_ban
def add_warn_filter(update: Update, context: CallbackContext):
chat: Optional[Chat] = update.effective_chat
msg: Optional[Message] = update.effective_message
@@ -299,6 +330,7 @@ def add_warn_filter(update: Update, context: CallbackContext):
@user_admin
+@user_can_ban
def remove_warn_filter(update: Update, context: CallbackContext):
chat: Optional[Chat] = update.effective_chat
msg: Optional[Message] = update.effective_message
@@ -383,6 +415,7 @@ def reply_filter(update: Update, context: CallbackContext) -> str:
@user_admin
+@user_can_ban
@loggable
def set_warn_limit(update: Update, context: CallbackContext) -> str:
args = context.args
@@ -394,6 +427,8 @@ def set_warn_limit(update: Update, context: CallbackContext) -> str:
if args[0].isdigit():
if int(args[0]) < 3:
msg.reply_text("The minimum warn limit is 3!")
+ elif int(args[0]) > 9999:
+ msg.reply_text("Well damn, that's a too big value!")
else:
sql.set_warn_limit(chat.id, int(args[0]))
msg.reply_text("Updated the warn limit to {}".format(args[0]))
@@ -413,6 +448,7 @@ def set_warn_limit(update: Update, context: CallbackContext) -> str:
@user_admin
+@user_can_ban
def set_warn_strength(update: Update, context: CallbackContext):
args = context.args
chat: Optional[Chat] = update.effective_chat
@@ -490,6 +526,7 @@ def __chat_settings__(chat_id, user_id):
*Admins only:*
⢠`/warn `*:* warn a user. After 3 warns, the user will be banned from the group. Can also be used as a reply.
⢠`/dwarn `*:* warn a user and delete the message. After 3 warns, the user will be banned from the group. Can also be used as a reply.
+ ⢠`/rmwarn`, `/unwarn` ``*:* removes user's latest warning. Can also be used as a reply.
⢠`/resetwarn `*:* reset the warns for a user. Can also be used as a reply.
⢠`/addwarn `*:* set a warning filter on a certain keyword. If you want your keyword to \
be a sentence, encompass it with quotes, as such: `/addwarn "very angry" This is an angry user`.
@@ -501,6 +538,7 @@ def __chat_settings__(chat_id, user_id):
__mod_name__ = "Warnings"
WARN_HANDLER = CommandHandler(["warn", "dwarn"], warn_user, filters=Filters.chat_type.groups, run_async=True)
+UNWARN_HANDLER = CommandHandler(["unwarn", "rmwarn"], rm_last_warn, filters=Filters.chat_type.groups, run_async=True)
RESET_WARN_HANDLER = CommandHandler(
["resetwarn", "resetwarns"], reset_warns, filters=Filters.chat_type.groups, run_async=True
)
@@ -522,6 +560,7 @@ def __chat_settings__(chat_id, user_id):
)
dispatcher.add_handler(WARN_HANDLER)
+dispatcher.add_handler(UNWARN_HANDLER)
dispatcher.add_handler(CALLBACK_QUERY_HANDLER)
dispatcher.add_handler(RESET_WARN_HANDLER)
dispatcher.add_handler(MYWARNS_HANDLER)
diff --git a/AstrakoBot/modules/weather.py b/AstrakoBot/modules/weather.py
index 4e03187538..be67ad869e 100644
--- a/AstrakoBot/modules/weather.py
+++ b/AstrakoBot/modules/weather.py
@@ -125,6 +125,33 @@ def sun(unix):
)
return xx
+ ## AirQuality
+ air_url = f"http://api.openweathermap.org/data/2.5/air_pollution?lat={latitude}&lon={longitude}&appid={APPID}"
+ air_data = json.loads(get(air_url).text)
+
+ into_dicts = air_data['list'][0]
+ air_qi = into_dicts['main']
+ aqi = int(air_qi['aqi'])
+
+
+ ## Pollutant concentration
+ # airinfo = into_dicts['components']
+ # components_co = airinfo["co"]
+ # components_no = airinfo["no"]
+
+
+ def air_qual(aqin):
+ if aqin == 1:
+ return "Good"
+ elif aqin == 2:
+ return "Fair"
+ elif aqin == 3:
+ return 'Moderate'
+ elif aqin == 4:
+ return 'Poor'
+ elif aqin == 5:
+ return "Very Poor"
+
msg = f"*{cityname}, {fullc_n}*\n"
msg += f"`Longitude: {longitude}`\n"
msg += f"`Latitude: {latitude}`\n\n"
@@ -135,8 +162,9 @@ def sun(unix):
msg += f"⢠**Humidity:** `{humidity}%`\n"
msg += f"⢠**Wind:** `{kmph[0]} km/h`\n"
msg += f"⢠**Sunrise**: `{sun(sunrise)}`\n"
- msg += f"⢠**Sunset**: `{sun(sunset)}`"
-
+ msg += f"⢠**Sunset**: `{sun(sunset)}`\n"
+ msg += f"⢠**Air Quality**: `{air_qual(aqi)}`"
+
else:
msg = "Please specify a city or country"
diff --git a/AstrakoBot/modules/weebify.py b/AstrakoBot/modules/weebify.py
index 5018f5f0f0..c1d3f81588 100644
--- a/AstrakoBot/modules/weebify.py
+++ b/AstrakoBot/modules/weebify.py
@@ -67,7 +67,8 @@ def weebify(update: Update, context: CallbackContext):
string = ""
if message.reply_to_message:
- string = message.reply_to_message.text.lower().replace(" ", " ")
+ text = message.reply_to_message.text or message.reply_to_message.caption or ""
+ string = text.lower().replace(" ", " ")
if args:
string = " ".join(args).lower()
diff --git a/AstrakoBot/modules/welcome.py b/AstrakoBot/modules/welcome.py
index fb64ad4c8f..4a8b2341e3 100644
--- a/AstrakoBot/modules/welcome.py
+++ b/AstrakoBot/modules/welcome.py
@@ -31,6 +31,7 @@
from AstrakoBot.modules.log_channel import loggable
from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from AstrakoBot.modules.sql.global_bans_sql import is_user_gbanned
+from AstrakoBot.modules.helper_funcs.admin_status import get_bot_member
from telegram import (
ChatPermissions,
InlineKeyboardButton,
@@ -45,7 +46,7 @@
CommandHandler,
Filters,
MessageHandler,
- run_async,
+ run_async, ChatMemberHandler,
)
from telegram.utils.helpers import escape_markdown, mention_html, mention_markdown
@@ -75,78 +76,104 @@
# do not async
-def send(update, message, keyboard, backup_message):
+def send(update, message, keyboard, backup_message, reply_to_message=None):
+ if not message:
+ return
+
chat = update.effective_chat
- cleanserv = sql.clean_service(chat.id)
- reply = update.message.message_id
- # Clean service welcome
- if cleanserv:
- try:
- dispatcher.bot.delete_message(chat.id, update.message.message_id)
- except BadRequest:
- pass
- reply = False
try:
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
+ message,
+ parse_mode=ParseMode.MARKDOWN,
+ reply_markup=keyboard,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
+ )
+ except TypeError:
+ msg = dispatcher.bot.send_message(chat.id,
message,
parse_mode=ParseMode.MARKDOWN,
reply_markup=keyboard,
- reply_to_message_id=reply,
+ allow_sending_without_reply=True,
)
except BadRequest as excp:
if excp.message == "Reply message not found":
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
message,
parse_mode=ParseMode.MARKDOWN,
reply_markup=keyboard,
quote=False,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
)
elif excp.message == "Button_url_invalid":
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
markdown_parser(
- backup_message + "\nNote: the current message has an invalid url "
- "in one of its buttons. Please update."
+ backup_message +
+ "\nNote: the current message has an invalid url "
+ "in one of its buttons. Please update.",
),
parse_mode=ParseMode.MARKDOWN,
- reply_to_message_id=reply,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
)
elif excp.message == "Unsupported url protocol":
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
markdown_parser(
- backup_message + "\nNote: the current message has buttons which "
+ backup_message +
+ "\nNote: the current message has buttons which "
"use url protocols that are unsupported by "
- "telegram. Please update."
+ "telegram. Please update.",
),
parse_mode=ParseMode.MARKDOWN,
- reply_to_message_id=reply,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
)
elif excp.message == "Wrong url host":
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
markdown_parser(
- backup_message + "\nNote: the current message has some bad urls. "
- "Please update."
+ backup_message +
+ "\nNote: the current message has some bad urls. "
+ "Please update.",
),
parse_mode=ParseMode.MARKDOWN,
- reply_to_message_id=reply,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
)
LOGGER.warning(message)
LOGGER.warning(keyboard)
LOGGER.exception("Could not parse! got invalid url host errors")
- elif excp.message == "Have no rights to send a message":
+ elif excp.message == "Have no rights to send a message" or excp.message == "Topic_closed":
return
else:
- msg = update.effective_message.reply_text(
+ msg = dispatcher.bot.send_message(chat.id,
markdown_parser(
- backup_message + "\nNote: An error occured when sending the "
- "custom message. Please update."
+ backup_message +
+ "\nNote: An error occured when sending the "
+ "custom message. Please update.",
),
parse_mode=ParseMode.MARKDOWN,
- reply_to_message_id=reply,
+ disable_web_page_preview=True,
+ allow_sending_without_reply=True,
+ )
+ LOGGER.exception(
+ "An error occured when sending a custom message to %s",
+ chat.id,
)
- LOGGER.exception("Error in welcome")
return msg
+def welcomeFilter(update: Update, context: CallbackContext):
+ if update.effective_chat.type != "group" and update.effective_chat.type != "supergroup":
+ return
+ if nm := update.chat_member.new_chat_member:
+ om = update.chat_member.old_chat_member
+ if nm.status == nm.MEMBER and (om.status == nm.KICKED or om.status == nm.LEFT):
+ return new_member(update, context)
+ if (nm.status == nm.KICKED or nm.status == nm.LEFT) and \
+ (om.status == nm.MEMBER or om.status == nm.ADMINISTRATOR or om.status == nm.CREATOR):
+ return left_member(update, context)
+
@loggable
def new_member(update: Update, context: CallbackContext):
bot, job_queue = context.bot, context.job_queue
@@ -157,270 +184,235 @@ def new_member(update: Update, context: CallbackContext):
should_welc, cust_welcome, cust_content, welc_type = sql.get_welc_pref(chat.id)
welc_mutes = sql.welcome_mutes(chat.id)
human_checks = sql.get_human_checks(user.id, chat.id)
+ new_mem = update.chat_member.new_chat_member.user
- new_members = update.effective_message.new_chat_members
+ if new_mem.id == bot.id and not AstrakoBot.ALLOW_CHATS:
+ with suppress(BadRequest):
+ dispatcher.bot.send_message(chat.id, f"Groups are disabled for {bot.first_name}, I'm outta here.")
+ bot.leave_chat(update.effective_chat.id)
+ return
+
+ welcome_log = None
+ res = None
+ sent = None
+ should_mute = True
+ welcome_bool = True
+ media_wel = False
+ keyboard = None
+ backup_message = ""
+ reply = None
- for new_mem in new_members:
- if new_mem.id == bot.id and not AstrakoBot.ALLOW_CHATS:
- with suppress(BadRequest):
- update.effective_message.reply_text(f"Groups are disabled for {bot.first_name}, I'm outta here.")
- bot.leave_chat(update.effective_chat.id)
+ if sw is not None:
+ sw_ban = sw.get_ban(new_mem.id)
+ if sw_ban:
return
- welcome_log = None
- res = None
- sent = None
- should_mute = True
- welcome_bool = True
- media_wel = False
+ if is_user_gbanned(new_mem.id):
+ return
- if sw is not None:
- sw_ban = sw.get_ban(new_mem.id)
- if sw_ban:
- return
+ if should_welc:
- if is_user_gbanned(new_mem.id):
- return
+ # Give the owner a special welcome
+ if new_mem.id == OWNER_ID:
+ deletion(update, context, dispatcher.bot.send_message(chat.id,
+ "Oh, Genos? Let's get this moving."
+ ))
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"Bot Owner just joined the group"
+ )
- if should_welc:
+ # Welcome Devs
+ elif new_mem.id in DEV_USERS:
+ deletion(update, context, dispatcher.bot.send_message(chat.id,
+ "Whoa! A developer user just joined!",
+ ))
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"Bot Dev just joined the group"
+ )
- reply = update.message.message_id
- cleanserv = sql.clean_service(chat.id)
- # Clean service welcome
- if cleanserv:
- try:
- dispatcher.bot.delete_message(chat.id, update.message.message_id)
- except BadRequest:
- pass
- reply = False
-
- # Give the owner a special welcome
- if new_mem.id == OWNER_ID:
- deletion(update, context, update.effective_message.reply_text(
- "Oh, Genos? Let's get this moving.", reply_to_message_id=reply
- ))
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"Bot Owner just joined the group"
- )
- continue
-
- # Welcome Devs
- elif new_mem.id in DEV_USERS:
- deletion(update, context, update.effective_message.reply_text(
- "Whoa! A developer user just joined!",
- reply_to_message_id=reply,
- ))
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"Bot Dev just joined the group"
- )
- continue
-
- # Welcome Sudos
- elif new_mem.id in SUDO_USERS:
- deletion(update, context, update.effective_message.reply_text(
- "Huh! A sudo user just joined! Stay Alert!",
- reply_to_message_id=reply,
- ))
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"Bot Sudo just joined the group"
- )
- continue
-
- # Welcome Support
- elif new_mem.id in SUPPORT_USERS:
- deletion(update, context, update.effective_message.reply_text(
- "Huh! A support user just joined!",
- reply_to_message_id=reply,
- ))
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"Bot Support just joined the group"
- )
- continue
-
- # Welcome Whitelisted
- elif new_mem.id in WHITELIST_USERS:
- deletion(update, context, update.effective_message.reply_text(
- "Oof! A whitelist user just joined!", reply_to_message_id=reply
- ))
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"Bot whitelisted just joined the group"
- )
- continue
-
- # Welcome yourself
- elif new_mem.id == bot.id:
- creator = None
- for x in bot.bot.get_chat_administrators(update.effective_chat.id):
- if x.status == "creator":
- creator = x.user
- break
- if creator:
- bot.send_message(
- JOIN_LOGGER,
- "#NEW_GROUP\nGroup name: {}\nID: {}\nCreator: {}".format(
- html.escape(chat.title), chat.id, html.escape(creator)
- ),
- parse_mode=ParseMode.HTML,
- )
- else:
- bot.send_message(
- JOIN_LOGGER,
- "#NEW_GROUP\nGroup name: {}\nID: {}".format(
- html.escape(chat.title), chat.id
- ),
- parse_mode=ParseMode.HTML,
- )
- update.effective_message.reply_text(
- "Hello everybody!", reply_to_message_id=reply
- )
- continue
+ # Welcome Sudos
+ elif new_mem.id in SUDO_USERS:
+ deletion(update, context, dispatcher.bot.send_message(chat.id,
+ "Huh! A sudo user just joined! Stay Alert!",
+ ))
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"Bot Sudo just joined the group"
+ )
- else:
- buttons = sql.get_welc_buttons(chat.id)
- keyb = build_keyboard(buttons)
+ # Welcome Support
+ elif new_mem.id in SUPPORT_USERS:
+ deletion(update, context, dispatcher.bot.send_message(chat.id,
+ "Huh! A support user just joined!",
+ ))
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"Bot Support just joined the group"
+ )
- if welc_type not in (sql.Types.TEXT, sql.Types.BUTTON_TEXT):
- media_wel = True
-
- first_name = (
- new_mem.first_name or "PersonWithNoName"
- ) # edge case of empty name - occurs for some bugs.
-
- if cust_welcome:
- if cust_welcome == sql.DEFAULT_WELCOME:
- cust_welcome = random.choice(
- sql.DEFAULT_WELCOME_MESSAGES
- ).format(first=escape_markdown(first_name))
-
- if new_mem.last_name:
- fullname = escape_markdown(f"{first_name} {new_mem.last_name}")
- else:
- fullname = escape_markdown(first_name)
- count = chat.get_member_count()
- mention = mention_markdown(new_mem.id, escape_markdown(first_name))
- if new_mem.username:
- username = "@" + escape_markdown(new_mem.username)
- else:
- username = mention
-
- valid_format = escape_invalid_curly_brackets(
- cust_welcome, VALID_WELCOME_FORMATTERS
- )
- res = valid_format.format(
- first=escape_markdown(first_name),
- last=escape_markdown(new_mem.last_name or first_name),
- fullname=escape_markdown(fullname),
- username=username,
- mention=mention,
- count=count,
- chatname=escape_markdown(chat.title),
- id=new_mem.id,
- )
+ # Welcome Whitelisted
+ elif new_mem.id in WHITELIST_USERS:
+ deletion(update, context, dispatcher.bot.send_message(chat.id,
+ "Oof! A whitelist user just joined!",
+ ))
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"Bot whitelisted just joined the group"
+ )
+
+ # Welcome yourself
+ elif new_mem.id == bot.id:
+ dispatcher.bot.send_message(chat.id,
+ "Thanks for adding me! Join https://t.me/AstrakoBotSupport for support.",
+ disable_web_page_preview=True,
+ )
+
+ bot.send_message(
+ JOIN_LOGGER,
+ "#NEW_GROUP\nGroup name: {}\nID: {}".format(
+ html.escape(chat.title), chat.id
+ ),
+ parse_mode=ParseMode.HTML,
+ )
+ else:
+ buttons = sql.get_welc_buttons(chat.id)
+ keyb = build_keyboard(buttons)
+
+ if welc_type not in (sql.Types.TEXT, sql.Types.BUTTON_TEXT):
+ media_wel = True
+
+ first_name = (
+ new_mem.first_name or "PersonWithNoName"
+ ) # edge case of empty name - occurs for some bugs.
+
+ if cust_welcome:
+ if cust_welcome == sql.DEFAULT_WELCOME:
+ cust_welcome = random.choice(
+ sql.DEFAULT_WELCOME_MESSAGES
+ ).format(first=escape_markdown(first_name))
+ if new_mem.last_name:
+ fullname = escape_markdown(f"{first_name} {new_mem.last_name}")
else:
- res = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format(
- first=escape_markdown(first_name)
- )
- keyb = []
+ fullname = escape_markdown(first_name)
+ count = chat.get_member_count()
+ mention = mention_markdown(new_mem.id, escape_markdown(first_name))
+ if new_mem.username:
+ username = "@" + escape_markdown(new_mem.username)
+ else:
+ username = mention
- backup_message = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format(
+ valid_format = escape_invalid_curly_brackets(
+ cust_welcome, VALID_WELCOME_FORMATTERS
+ )
+ res = valid_format.format(
+ first=escape_markdown(first_name),
+ last=escape_markdown(new_mem.last_name or first_name),
+ fullname=escape_markdown(fullname),
+ username=username,
+ mention=mention,
+ count=count,
+ chatname=escape_markdown(chat.title),
+ id=new_mem.id,
+ )
+
+ else:
+ res = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format(
first=escape_markdown(first_name)
)
- keyboard = InlineKeyboardMarkup(keyb)
+ keyb = []
- else:
- welcome_bool = False
- res = None
- keyboard = None
- backup_message = None
- reply = None
-
- # User exceptions from welcomemutes
- if (
- is_user_ban_protected(chat, new_mem.id, chat.get_member(new_mem.id))
- or human_checks
- ):
- should_mute = False
- # Join welcome: soft mute
- if new_mem.is_bot:
- should_mute = False
-
- if user.id == new_mem.id:
- if should_mute:
- if welc_mutes == "soft":
- bot.restrict_chat_member(
- chat.id,
- new_mem.id,
- permissions=ChatPermissions(
- can_send_messages=True,
- can_send_media_messages=False,
- can_send_other_messages=False,
- can_invite_users=False,
- can_pin_messages=False,
- can_send_polls=False,
- can_change_info=False,
- can_add_web_page_previews=False,
- ),
- until_date=(int(time.time() + 24 * 60 * 60)),
+ backup_message = random.choice(sql.DEFAULT_WELCOME_MESSAGES).format(
+ first=escape_markdown(first_name)
+ )
+ keyboard = InlineKeyboardMarkup(keyb)
+ else:
+ welcome_bool = False
+ backup_message = None
+
+ # User exceptions from welcomemutes
+ if (
+ is_user_ban_protected(chat, new_mem.id, chat.get_member(new_mem.id))
+ or human_checks
+ ):
+ should_mute = False
+ # Join welcome: soft mute
+ if new_mem.is_bot:
+ should_mute = False
+
+ if user.id == new_mem.id:
+ if should_mute:
+ if welc_mutes == "soft":
+ bot.restrict_chat_member(
+ chat.id,
+ new_mem.id,
+ permissions=ChatPermissions(
+ can_send_messages=True,
+ can_send_media_messages=False,
+ can_send_other_messages=False,
+ can_invite_users=False,
+ can_pin_messages=False,
+ can_send_polls=False,
+ can_change_info=False,
+ can_add_web_page_previews=False,
+ ),
+ until_date=(int(time.time() + 24 * 60 * 60)),
+ )
+ if welc_mutes == "strong":
+ welcome_bool = False
+ if not media_wel:
+ VERIFIED_USER_WAITLIST.update(
+ {
+ new_mem.id: {
+ "should_welc": should_welc,
+ "media_wel": False,
+ "status": False,
+ "update": update,
+ "res": res,
+ "keyboard": keyboard,
+ "backup_message": backup_message,
+ }
+ }
)
- if welc_mutes == "strong":
- welcome_bool = False
- if not media_wel:
- VERIFIED_USER_WAITLIST.update(
- {
- new_mem.id: {
- "should_welc": should_welc,
- "media_wel": False,
- "status": False,
- "update": update,
- "res": res,
- "keyboard": keyboard,
- "backup_message": backup_message,
- }
+ else:
+ VERIFIED_USER_WAITLIST.update(
+ {
+ new_mem.id: {
+ "should_welc": should_welc,
+ "chat_id": chat.id,
+ "status": False,
+ "media_wel": True,
+ "cust_content": cust_content,
+ "welc_type": welc_type,
+ "res": res,
+ "keyboard": keyboard,
}
- )
- else:
- VERIFIED_USER_WAITLIST.update(
+ }
+ )
+ new_join_mem = f'{html.escape(new_mem.first_name)}'
+ message = dispatcher.bot.send_message(chat.id,
+ f"{new_join_mem}, click the button below to prove you're human.\nYou have 60 seconds.",
+ reply_markup=InlineKeyboardMarkup(
+ [
{
- new_mem.id: {
- "should_welc": should_welc,
- "chat_id": chat.id,
- "status": False,
- "media_wel": True,
- "cust_content": cust_content,
- "welc_type": welc_type,
- "res": res,
- "keyboard": keyboard,
- }
+ InlineKeyboardButton(
+ text="Yes, I'm human.",
+ callback_data=f"user_join_({new_mem.id})",
+ )
}
- )
- new_join_mem = f'{html.escape(new_mem.first_name)}'
- message = msg.reply_text(
- f"{new_join_mem}, click the button below to prove you're human.\nYou have 60 seconds.",
- reply_markup=InlineKeyboardMarkup(
- [
- {
- InlineKeyboardButton(
- text="Yes, I'm human.",
- callback_data=f"user_join_({new_mem.id})",
- )
- }
- ]
- ),
- parse_mode=ParseMode.HTML,
- reply_to_message_id=reply,
- )
+ ]
+ ),
+ parse_mode=ParseMode.HTML,
+ )
+ if get_bot_member(chat.id).can_restrict_members:
bot.restrict_chat_member(
chat.id,
new_mem.id,
@@ -435,62 +427,72 @@ def new_member(update: Update, context: CallbackContext):
can_add_web_page_previews=False,
),
)
- job_queue.run_once(
- partial(check_not_bot, new_mem, chat.id, message.message_id),
- 60,
- name="welcomemute",
- )
+ job_queue.run_once(
+ partial(check_not_bot, new_mem, chat.id, message.message_id),
+ 60,
+ name="welcomemute",
+ )
- if welcome_bool:
- if media_wel:
+ if welcome_bool:
+ if media_wel:
+ # Stickers have no caption, send separately
+ if welc_type == sql.Types.STICKER:
+ sent = ENUM_FUNC_MAP[welc_type](
+ chat.id,
+ cust_content,
+ reply_markup=keyboard
+ ) and ENUM_FUNC_MAP[sql.Types.TEXT](
+ chat.id,
+ res,
+ parse_mode="markdown"
+ )
+ else:
sent = ENUM_FUNC_MAP[welc_type](
chat.id,
cust_content,
caption=res,
reply_markup=keyboard,
- reply_to_message_id=reply,
- parse_mode="markdown",
+ parse_mode="markdown"
)
- else:
- sent = send(update, res, keyboard, backup_message)
- deletion(update, context, sent)
- prev_welc = sql.get_clean_pref(chat.id)
- if prev_welc:
- try:
- bot.delete_message(chat.id, prev_welc)
- except BadRequest:
- pass
-
- if sent:
- sql.set_clean_welcome(chat.id, sent.message_id)
+ else:
+ sent = send(update, res, keyboard, backup_message)
+ deletion(update, context, sent)
+ prev_welc = sql.get_clean_pref(chat.id)
+ if prev_welc:
+ try:
+ bot.delete_message(chat.id, prev_welc)
+ except BadRequest:
+ pass
- if welcome_log:
- return welcome_log
+ if sent:
+ sql.set_clean_welcome(chat.id, sent.message_id)
- if user.id == new_mem.id:
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_JOINED\n"
- f"User: {mention_html(user.id, user.first_name)}\n"
- f"ID: {user.id}"
- )
- elif new_mem.is_bot and user.id != new_mem.id:
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#BOT_ADDED\n"
- f"Bot: {mention_html(new_mem.id, new_mem.first_name)}\n"
- f"ID: {new_mem.id}"
- )
- else:
- welcome_log = (
- f"{html.escape(chat.title)}\n"
- f"#USER_ADDED\n"
- f"User: {mention_html(new_mem.id, new_mem.first_name)}\n"
- f"ID: {new_mem.id}"
- )
+ if welcome_log:
return welcome_log
- return ""
+ if user.id == new_mem.id:
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_JOINED\n"
+ f"User: {mention_html(user.id, user.first_name)}\n"
+ f"ID: {user.id}"
+ )
+ elif new_mem.is_bot and user.id != new_mem.id:
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#BOT_ADDED\n"
+ f"Bot: {mention_html(new_mem.id, new_mem.first_name)}\n"
+ f"ID: {new_mem.id}"
+ )
+ else:
+ welcome_log = (
+ f"{html.escape(chat.title)}\n"
+ f"#USER_ADDED\n"
+ f"User: {mention_html(new_mem.id, new_mem.first_name)}\n"
+ f"ID: {new_mem.id}"
+ )
+ return welcome_log
+
def check_not_bot(member, chat_id, message_id, context):
@@ -514,6 +516,19 @@ def check_not_bot(member, chat_id, message_id, context):
pass
+def cleanServiceFilter(u: Update, _):
+ if u.effective_message.left_chat_member or u.effective_message.new_chat_members:
+ return handleCleanService(u)
+
+
+def handleCleanService(update: Update):
+ if sql.clean_service(update.effective_chat.id):
+ try:
+ dispatcher.bot.delete_message(update.effective_chat.id, update.message.message_id)
+ except BadRequest:
+ pass
+
+
def left_member(update: Update, context: CallbackContext):
bot = context.bot
chat = update.effective_chat
@@ -524,17 +539,8 @@ def left_member(update: Update, context: CallbackContext):
return
if should_goodbye:
- reply = update.message.message_id
- cleanserv = sql.clean_service(chat.id)
- # Clean service welcome
- if cleanserv:
- try:
- dispatcher.bot.delete_message(chat.id, update.message.message_id)
- except BadRequest:
- pass
- reply = False
- left_mem = update.effective_message.left_chat_member
+ left_mem = update.chat_member.new_chat_member.user
if left_mem:
# Thingy for spamwatched users
@@ -553,16 +559,15 @@ def left_member(update: Update, context: CallbackContext):
# Give the owner a special goodbye
if left_mem.id == OWNER_ID:
- update.effective_message.reply_text(
- "Oi! Genos! He left..", reply_to_message_id=reply
+ dispatcher.bot.send_message(chat.id,
+ "Oi! Genos! He left..",
)
return
# Give the devs a special goodbye
elif left_mem.id in DEV_USERS:
- update.effective_message.reply_text(
+ dispatcher.bot.send_message(chat.id,
"See you later dev!",
- reply_to_message_id=reply,
)
return
@@ -624,13 +629,29 @@ def left_member(update: Update, context: CallbackContext):
deletion(update, context, delmsg)
+def get_welcome_kwargs(welcome_type, chat, welcome_m, keyboard):
+ kwargs = {
+ 'reply_markup': keyboard,
+ }
+
+ # Add caption (except for stickers)
+ if welcome_type != sql.Types.STICKER:
+ kwargs['caption'] = welcome_m
+ kwargs['parse_mode'] = ParseMode.MARKDOWN
+
+ # Add web preview disable (only for supported types)
+ if welcome_type in {sql.Types.TEXT, sql.Types.PHOTO}:
+ kwargs['disable_web_page_preview'] = True
+
+ return kwargs
+
@user_admin
def welcome(update: Update, context: CallbackContext):
args = context.args
chat = update.effective_chat
# if no args, show current replies.
if not args or args[0].lower() == "noformat":
- noformat = True
+ noformat = bool(args and args[0].lower() == "noformat")
pref, welcome_m, cust_content, welcome_type = sql.get_welc_pref(chat.id)
update.effective_message.reply_text(
f"This chat has it's welcome setting set to: `{pref}`.\n"
@@ -638,7 +659,7 @@ def welcome(update: Update, context: CallbackContext):
parse_mode=ParseMode.MARKDOWN,
)
- if welcome_type == sql.Types.BUTTON_TEXT or welcome_type == sql.Types.TEXT:
+ if welcome_type in [sql.Types.BUTTON_TEXT, sql.Types.TEXT]:
buttons = sql.get_welc_buttons(chat.id)
if noformat:
welcome_m += revert_buttons(buttons)
@@ -652,20 +673,17 @@ def welcome(update: Update, context: CallbackContext):
else:
buttons = sql.get_welc_buttons(chat.id)
if noformat:
- welcome_m += revert_buttons(buttons)
- ENUM_FUNC_MAP[welcome_type](chat.id, cust_content, caption=welcome_m)
+ if welcome_m:
+ welcome_m += revert_buttons(buttons)
+ ENUM_FUNC_MAP[welcome_type](chat.id, cust_content, caption=welcome_m)
+ else:
+ ENUM_FUNC_MAP[welcome_type](chat.id, cust_content)
else:
keyb = build_keyboard(buttons)
keyboard = InlineKeyboardMarkup(keyb)
- ENUM_FUNC_MAP[welcome_type](
- chat.id,
- cust_content,
- caption=welcome_m,
- reply_markup=keyboard,
- parse_mode=ParseMode.MARKDOWN,
- disable_web_page_preview=True,
- )
+ kwargs = get_welcome_kwargs(welcome_type, chat, welcome_m, keyboard)
+ ENUM_FUNC_MAP[welcome_type](chat.id, cust_content, **kwargs)
elif len(args) >= 1:
if args[0].lower() in ("on", "yes"):
@@ -692,7 +710,7 @@ def goodbye(update: Update, context: CallbackContext):
chat = update.effective_chat
if not args or args[0] == "noformat":
- noformat = True
+ noformat = bool(args and args[0].lower() == "noformat")
pref, goodbye_m, goodbye_type = sql.get_gdbye_pref(chat.id)
update.effective_message.reply_text(
f"This chat has it's goodbye setting set to: `{pref}`.\n"
@@ -712,14 +730,13 @@ def goodbye(update: Update, context: CallbackContext):
send(update, goodbye_m, keyboard, sql.DEFAULT_GOODBYE)
- else:
- if noformat:
- ENUM_FUNC_MAP[goodbye_type](chat.id, goodbye_m)
+ elif noformat:
+ ENUM_FUNC_MAP[goodbye_type](chat.id, goodbye_m)
- else:
- ENUM_FUNC_MAP[goodbye_type](
- chat.id, goodbye_m, parse_mode=ParseMode.MARKDOWN
- )
+ else:
+ ENUM_FUNC_MAP[goodbye_type](
+ chat.id, goodbye_m, parse_mode=ParseMode.MARKDOWN
+ )
elif len(args) >= 1:
if args[0].lower() in ("on", "yes"):
@@ -968,33 +985,45 @@ def user_button(update: Update, context: CallbackContext):
member_dict["status"] = True
VERIFIED_USER_WAITLIST.update({user.id: member_dict})
query.answer(text="Yeet! You're a human, unmuted!")
- bot.restrict_chat_member(
- chat.id,
- user.id,
- permissions=ChatPermissions(
- can_send_messages=True,
- can_invite_users=True,
- can_pin_messages=True,
- can_send_polls=True,
- can_change_info=True,
- can_send_media_messages=True,
- can_send_other_messages=True,
- can_add_web_page_previews=True,
- ),
- )
+ if get_bot_member(chat.id).can_restrict_members:
+ bot.restrict_chat_member(
+ chat.id,
+ user.id,
+ permissions=ChatPermissions(
+ can_send_messages=True,
+ can_invite_users=True,
+ can_pin_messages=True,
+ can_send_polls=True,
+ can_change_info=True,
+ can_send_media_messages=True,
+ can_send_other_messages=True,
+ can_add_web_page_previews=True,
+ ),
+ )
try:
bot.deleteMessage(chat.id, message.message_id)
except:
pass
if member_dict["should_welc"]:
if member_dict["media_wel"]:
- sent = ENUM_FUNC_MAP[member_dict["welc_type"]](
- member_dict["chat_id"],
- member_dict["cust_content"],
- caption=member_dict["res"],
- reply_markup=member_dict["keyboard"],
- parse_mode="markdown",
- )
+ if member_dict["welc_type"] == sql.Types.STICKER:
+ sent = ENUM_FUNC_MAP[member_dict["welc_type"]](
+ member_dict["chat_id"],
+ member_dict["cust_content"],
+ reply_markup=member_dict["keyboard"],
+ ) and ENUM_FUNC_MAP[sql.Types.TEXT](
+ member_dict["chat_id"],
+ member_dict["res"],
+ parse_mode="markdown",
+ )
+ else:
+ sent = ENUM_FUNC_MAP[member_dict["welc_type"]](
+ member_dict["chat_id"],
+ member_dict["cust_content"],
+ caption=member_dict["res"],
+ reply_markup=member_dict["keyboard"],
+ parse_mode="markdown",
+ )
else:
sent = send(
member_dict["update"],
@@ -1118,8 +1147,15 @@ def __chat_settings__(chat_id, user_id):
⢠`/welcomehelp`*:* view more formatting information for custom welcome/goodbye messages.
"""
-NEW_MEM_HANDLER = MessageHandler(Filters.status_update.new_chat_members, new_member, run_async=True)
-LEFT_MEM_HANDLER = MessageHandler(Filters.status_update.left_chat_member, left_member, run_async=True)
+dispatcher.add_handler(
+ ChatMemberHandler(
+ welcomeFilter, ChatMemberHandler.CHAT_MEMBER, run_async=True
+ ), group=-100)
+
+dispatcher.add_handler(
+ MessageHandler(Filters.chat_type.groups, cleanServiceFilter), group=100)
+
+
WELC_PREF_HANDLER = CommandHandler("welcome", welcome, filters=Filters.chat_type.groups, run_async=True)
GOODBYE_PREF_HANDLER = CommandHandler("goodbye", goodbye, filters=Filters.chat_type.groups, run_async=True)
SET_WELCOME = CommandHandler("setwelcome", set_welcome, filters=Filters.chat_type.groups, run_async=True)
@@ -1135,8 +1171,6 @@ def __chat_settings__(chat_id, user_id):
WELCOME_MUTE_HELP = CommandHandler("welcomemutehelp", welcome_mute_help, run_async=True)
BUTTON_VERIFY_HANDLER = CallbackQueryHandler(user_button, pattern=r"user_join_", run_async=True)
-dispatcher.add_handler(NEW_MEM_HANDLER)
-dispatcher.add_handler(LEFT_MEM_HANDLER)
dispatcher.add_handler(WELC_PREF_HANDLER)
dispatcher.add_handler(GOODBYE_PREF_HANDLER)
dispatcher.add_handler(SET_WELCOME)
@@ -1153,8 +1187,6 @@ def __chat_settings__(chat_id, user_id):
__mod_name__ = "Welcomes/Goodbyes"
__command_list__ = []
__handlers__ = [
- NEW_MEM_HANDLER,
- LEFT_MEM_HANDLER,
WELC_PREF_HANDLER,
GOODBYE_PREF_HANDLER,
SET_WELCOME,
diff --git a/AstrakoBot/modules/wiki.py b/AstrakoBot/modules/wiki.py
index bce0262180..d4b7b31726 100644
--- a/AstrakoBot/modules/wiki.py
+++ b/AstrakoBot/modules/wiki.py
@@ -28,7 +28,7 @@ def wiki(update: Update, context: CallbackContext):
msg += f"Read more: https://en.wikipedia.org/wiki/{definition}"
if len(msg) > 4000:
with open("result.txt", "w") as f:
- f.write(f"{result}\n\nUwU OwO OmO UmU")
+ f.write(f"{res}\n\nUwU OwO OmO UmU")
with open("result.txt", "rb") as f:
delmsg = context.bot.send_document(
document=f,
diff --git a/AstrakoBot/modules/youtube.py b/AstrakoBot/modules/youtube.py
index e6765e0ac1..fc5b084bdb 100644
--- a/AstrakoBot/modules/youtube.py
+++ b/AstrakoBot/modules/youtube.py
@@ -1,173 +1,155 @@
-import os, glob, json
-
+import os
+import json
+import random
+import string
from datetime import datetime
-from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from telegram import Bot, Update, ParseMode, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CommandHandler, CallbackQueryHandler, CallbackContext, run_async
+from AstrakoBot.modules.sql.clear_cmd_sql import get_clearcmd
from AstrakoBot import dispatcher
from AstrakoBot.modules.disable import DisableAbleCommandHandler
from AstrakoBot.modules.helper_funcs.misc import delete
-from youtubesearchpython import VideosSearch
+import pytube
+from moviepy.editor import *
-from youtube_dl import YoutubeDL
+def get_random():
+ letters = string.ascii_lowercase
+ return ''.join(random.choice(letters) for i in range(10))
+def is_ytl(url):
+ return 'youtube.com/watch?v=' in url or 'youtu.be/' in url
-def youtube(update: Update, context: CallbackContext):
- bot = context.bot
- message = update.effective_message
- chat = update.effective_chat
- yt = message.text[len("/youtube ") :]
- if yt:
- search = VideosSearch(yt, limit=1)
- result = search.result()
+def format_link(youtube_link):
+ if "youtu.be/" in youtube_link:
+ youtube_link = youtube_link.replace('youtu.be/', 'youtube.com/watch?v=')
+ if '&ab_channel' in youtube_link:
+ youtube_link = youtube_link.split('&ab_channel')[0]
+ return youtube_link
+
+def dyt_video(youtube_link, resolution, filename):
+ youtube_link = format_link(youtube_link)
+ youtube = pytube.YouTube(youtube_link)
+ # Try to get video length at least 5 times
+ for i in range(5):
try:
- url = result["result"][0]["link"]
- title = result["result"][0]["title"]
+ video_length = youtube.length / 60
+ break
except:
- return message.reply_text(
- "Failed to find song or video",
- )
-
- buttons = [
- [
- InlineKeyboardButton("šµ", callback_data=f"youtube;audio;{url}"),
- InlineKeyboardButton("š„", callback_data=f"youtube;video;{url}"),
- InlineKeyboardButton("š«", callback_data=f"youtube;cancel;"""),
- ]
- ]
-
- msg = "*Preparing to upload file:*\n"
- msg += f"`{title}`\n"
- delmsg = message.reply_text(
- msg,
- parse_mode=ParseMode.MARKDOWN,
- reply_markup = InlineKeyboardMarkup(buttons)
- )
+ if i == 4:
+ return "Could not get video length! Try again in a few seconds or try another video."
- else:
- delmsg = message.reply_text("Specify a song or video"
- )
- cleartime = get_clearcmd(chat.id, "youtube")
-
- if cleartime:
- context.dispatcher.run_async(delete, delmsg, cleartime.time)
+ if video_length > 10: # 10 minutes limit for video
+ return "Video is longer than 10 minutes! Try again with a shorter one."
+
+ video_streams = youtube.streams
+ available_resolutions = [stream.resolution for stream in video_streams if stream.resolution is not None]
+ if resolution not in available_resolutions:
+ resolution = max(available_resolutions, key=lambda x: int(x[:-1]))
+
+ video_stream = youtube.streams.filter(resolution=resolution, progressive=True).order_by('resolution').desc().first()
+ audio_stream = youtube.streams.filter(only_audio=True).order_by('abr').desc().first()
+
+ video_file = video_stream.download(filename="{}.mp4".format(get_random()))
+ audio_file = audio_stream.download(filename="{}.mp3".format(get_random()))
+
+ video_clip = VideoFileClip(video_file)
+ audio_clip = AudioFileClip(audio_file)
+ final_clip = video_clip.set_audio(audio_clip)
+ final_clip.write_videofile(filename, codec='libx264', audio_codec='aac')
+
+ try:
+ os.remove(video_file)
+ os.remove(audio_file)
+ except:
+ pass
+
+ return ""
+
+def dyt_audio(youtube_link, filename):
+ youtube_link = format_link(youtube_link)
+ youtube = pytube.YouTube(youtube_link)
+
+ # Try to get video length at least 5 times, even with this, pytube may fail sometimes.
+ for i in range(5):
+ try:
+ video_length = youtube.length / 60
+ break
+ except:
+ if i == 5:
+ return "Could not get video length! Try again in a few seconds or try another video."
+ if video_length > 30: # 30 minutes limit for audio
+ return "Audio is longer than 30 minutes! Try again with a shorter one."
-def youtube_callback(update: Update, context: CallbackContext):
- bot = context.bot
+ audio_stream = youtube.streams.filter(only_audio=True, abr="128kbps").first()
+
+ try:
+ audio_stream.download(filename=filename)
+ except:
+ return "Unknown Error."
+ return ""
+
+def youtube(update: Update, context: CallbackContext):
+ message_id = update.message.message_id
message = update.effective_message
chat = update.effective_chat
- query = update.callback_query
-
- media = query.data.split(";")
- media_type = media[1]
- media_url = media[2]
-
- if media_type == "audio":
- deltext = message.edit_text("Processing song...")
- opts = {
- "format": "bestaudio/best",
- "addmetadata": True,
- "geo_bypass": True,
- "nocheckcertificate": True,
- "postprocessors": [
- {
- "key": "FFmpegExtractAudio",
- "preferredcodec": "mp3",
- "preferredquality": "128",
- }
- ],
- "outtmpl": "%(title)s.%(etx)s",
- "quiet": True,
- "logtostderr": False,
- }
-
- codec = "mp3"
-
- with YoutubeDL(opts) as rip:
- rip_data = rip.extract_info(media_url, download=False, process=False)
- if int(rip_data['duration'] / 60) < 10:
- try:
- rip_data = rip.extract_info(media_url)
- delmsg = bot.send_audio(
- chat_id = chat.id,
- audio = open(f"{rip_data['title']}.{codec}", "rb"),
- duration = int(rip_data['duration']),
- title = str(rip_data['title']),
- parse_mode = ParseMode.HTML
- )
- context.dispatcher.run_async(delete, deltext, 0)
- except:
- delmsg = message.edit_text(
- "Song is too large for processing, or any other error happened. Try again later"
- )
- else:
- delmsg = message.edit_text(
- "Song is too large for processing. Duration is limited to 10 minutes max"
- )
-
- elif media_type == "video":
- deltext = message.edit_text("Processing video...")
- opts = {
- "format": "best",
- "addmetadata": True,
- "geo_bypass": True,
- "nocheckcertificate": True,
- "postprocessors": [
- {
- "key": "FFmpegVideoConvertor",
- "preferedformat": "mp4",
- }
- ],
- "outtmpl": "%(title)s.mp4",
- "quiet": True,
- "logtostderr": False,
- }
-
- codec = "mp4"
-
- with YoutubeDL(opts) as rip:
- rip_data = rip.extract_info(media_url, download=False, process=False)
- if int(rip_data['duration'] / 60) < 10:
- try:
- rip_data = rip.extract_info(media_url)
- delmsg = bot.send_video(
- chat_id = chat.id,
- video = open(f"{rip_data['title']}.{codec}", "rb"),
- duration = int(rip_data['duration']),
- caption = rip_data['title'],
- supports_streaming = True,
- parse_mode = ParseMode.HTML
- )
- context.dispatcher.run_async(delete, deltext, 0)
- except:
- delmsg = message.edit_text(
- "Video is too large for processing, or any other error happened. Try again later"
- )
- else:
- delmsg = message.edit_text(
- "Video is too large for processing. Duration is limited to 10 minutes max"
- )
+ args = context.args
+ chat_id = update.effective_chat.id
+ if not args or not is_ytl(args[0]):
+ message.reply_text("Specify a song or video!")
+ return
+ yt = args[0]
+ avail_res = ["720p", "480p", "360p", "240p", "144p"] # Limit to HD due to file size
+ types = ["video", "audio"]
+ type = "video"
+ res = avail_res[0]
+ for i in types:
+ if i in args:
+ type = i
+ break
+ for i in avail_res:
+ if i in args or i[:-1] in args:
+ res = i
+ if not res.endswith("p"):
+ res += "p"
+ break
+
+ filename = get_random()
+
+ if type == "video":
+ filename += ".mp4"
+ msg = message.reply_text("Downloading as video, Please wait...")
+ ret = dyt_video(yt, res, filename)
+ caption = "Type: mp4\nQuality: {}".format(res)
+ if not ret:
+ msg.edit_text("Uploading...")
+ with open(filename, "rb") as video:
+ context.bot.send_video(chat_id=chat_id, video=video, caption=caption, reply_to_message_id=message_id)
+ msg.delete()
+ else:
+ msg.edit_text(ret)
+
else:
- delmsg = message.edit_text("Canceling...")
- context.dispatcher.run_async(delete, delmsg, 1)
+ filename += ".mp3"
+ msg = message.reply_text("Downloading as mp3 audio, Please wait...")
+ ret = dyt_audio(yt, filename)
+ caption = "Type: mp3\nQuality: 128kbps".format(res)
+ if not ret:
+ msg.edit_text("Uploading...")
+ with open(filename, "rb") as audio:
+ context.bot.send_audio(chat_id=chat_id, audio=audio, caption=caption.format(type), reply_to_message_id=message_id)
+ msg.delete()
+ else:
+ msg.edit_text(ret)
try:
- os.remove(f"{rip_data['title']}.{codec}")
+ os.remove(filename)
except Exception:
pass
- cleartime = get_clearcmd(chat.id, "youtube")
-
- if cleartime:
- context.dispatcher.run_async(delete, delmsg, cleartime.time)
-
+ return
YOUTUBE_HANDLER = DisableAbleCommandHandler(["youtube", "yt"], youtube, run_async = True)
-YOUTUBE_CALLBACKHANDLER = CallbackQueryHandler(
- youtube_callback, pattern="youtube*", run_async=True
-)
dispatcher.add_handler(YOUTUBE_HANDLER)
-dispatcher.add_handler(YOUTUBE_CALLBACKHANDLER)
diff --git a/AstrakoBot/sample_config.py b/AstrakoBot/sample_config.py
index cff0ed2aa0..b2a6a64133 100644
--- a/AstrakoBot/sample_config.py
+++ b/AstrakoBot/sample_config.py
@@ -3,6 +3,7 @@
import os
+
def get_user_list(config, key):
with open("{}/AstrakoBot/{}".format(os.getcwd(), config), "r") as json_file:
return json.load(json_file)[key]
@@ -30,6 +31,7 @@ class Config(object):
# RECOMMENDED
SQLALCHEMY_DATABASE_URI = "something://somewhat:user@hosturl:port/databasename" # needed for any database modules
+ DB_NAME = "databasename" # needed for cron_jobs module, use same databasename from SQLALCHEMY_DATABASE_URI
LOAD = []
NO_LOAD = ["rss", "cleaner", "connection", "math"]
WEBHOOK = False
@@ -59,16 +61,18 @@ class Config(object):
BAN_STICKER = "" # banhammer marie sticker id, the bot will send this sticker before banning or kicking a user in chat.
ALLOW_EXCL = True # Allow ! commands as well as / (Leave this to true so that blacklist can work)
CASH_API_KEY = (
- "awoo" # Get your API key from https://www.alphavantage.co/support/#api-key
+ "awoo" # Get your API key from https://app.exchangerate-api.com/dashboard
)
TIME_API_KEY = "awoo" # Get your API key from https://timezonedb.com/api
WALL_API = (
- "awoo" # For wallpapers, get one from https://wall.alphacoders.com/api.php
+ "awoo" # For wallpapers, get one from https://pixabay.com/api/docs
)
AI_API_KEY = "awoo" # For chatbot, get one from https://coffeehouse.intellivoid.net/dashboard
BL_CHATS = [] # List of groups that you want blacklisted.
SPAMMERS = None
-
+
+ BACKUP_PASS = "12345" # The password used for the cron backups zip
+ DROP_UPDATES = False # whether to drop the pending updates or not
class Production(Config):
LOGGER = True
diff --git a/README.md b/README.md
index 5bd687fd44..19c4c1d9d0 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
-
+
diff --git a/requirements.txt b/requirements.txt
index 0b456e4a9f..b17aed3c49 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,10 +1,10 @@
future
-emoji
+emoji==1.7.0
lxml
beautifulsoup4
requests
-sqlalchemy
-python-telegram-bot==13.9
+sqlalchemy==1.4.11
+python-telegram-bot==13.14
psycopg2-binary
feedparser
pynewtonmath
@@ -23,12 +23,12 @@ speedtest-cli
coffeehouse
regex
bleach
-git+https://github.com/starry69/python-markdown2.git
+markdown2
wikipedia
telethon
spamwatch
alphabet_detector
-pyrate-limiter
+pyrate-limiter==2.8.1
covid
datetime
pytz
@@ -40,10 +40,10 @@ hachoir
pybase64
pySmartDL
validators
-youtube_dl
-youtube-search-python
gtts
pretty_errors
tswift
PyYAML
humanize
+pytube
+moviepy
diff --git a/resources/DroidSansMono.ttf b/resources/DroidSansMono.ttf
new file mode 100644
index 0000000000..b7bf5b4aa8
Binary files /dev/null and b/resources/DroidSansMono.ttf differ
diff --git a/resources/Quivira.otf b/resources/Quivira.otf
new file mode 100644
index 0000000000..8064cae278
Binary files /dev/null and b/resources/Quivira.otf differ
diff --git a/resources/Roboto-Italic.ttf b/resources/Roboto-Italic.ttf
new file mode 100644
index 0000000000..f0f33dbdfa
Binary files /dev/null and b/resources/Roboto-Italic.ttf differ
diff --git a/resources/Roboto-Medium.ttf b/resources/Roboto-Medium.ttf
new file mode 100644
index 0000000000..8798341989
Binary files /dev/null and b/resources/Roboto-Medium.ttf differ
diff --git a/resources/Roboto-Regular.ttf b/resources/Roboto-Regular.ttf
new file mode 100644
index 0000000000..7d9a6c4c32
Binary files /dev/null and b/resources/Roboto-Regular.ttf differ
diff --git a/run.sh b/run.sh
new file mode 100755
index 0000000000..ce61ac3691
--- /dev/null
+++ b/run.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+source venv/bin/activate
+python3.10 -m AstrakoBot