diff --git a/Zect/database/afkdb.py b/Zect/database/afkdb.py index abb27e8..4ec8cd7 100644 --- a/Zect/database/afkdb.py +++ b/Zect/database/afkdb.py @@ -27,8 +27,11 @@ async def set_unafk(): async def get_afk_status(): result = await collection.find_one({"_id": 1}) - status = result["afk_status"] - return status + if not result: + return False + else: + status = result["afk_status"] + return status async def afk_stuff(): diff --git a/Zect/database/pmpermitdb.py b/Zect/database/pmpermitdb.py index 3bce801..11d1831 100644 --- a/Zect/database/pmpermitdb.py +++ b/Zect/database/pmpermitdb.py @@ -41,6 +41,8 @@ async def set_limit(limit): async def get_pm_settings(): result = await collection.find_one({"_id": 1}) + if not result: + return pmpermit = result["pmpermit"] pm_message = result.get("pmpermit_message", PMPERMIT_MESSAGE) block_message = result.get("block_message", BLOCKED) diff --git a/Zect/modules/admin.py b/Zect/modules/admin.py index 66f96ba..72dd578 100644 --- a/Zect/modules/admin.py +++ b/Zect/modules/admin.py @@ -7,7 +7,7 @@ from pyrogram.methods.chats.get_chat_members import Filters as ChatMemberFilters from Zect import app, CMD_HELP -from Zect.helpers.pyrohelper import get_arg +from Zect.helpers.pyrohelper import get_arg, get_args from Zect.helpers.adminhelpers import CheckAdmin from config import PREFIX @@ -49,7 +49,7 @@ async def ban_hammer(_, message: Message): ) await message.edit(f"{get_user.first_name} has been banned.") except: - await message.edit("I can't ban this user.") + await message.edit("**I can't ban this user.**") @app.on_message(filters.command("unban", PREFIX) & filters.me) @@ -221,3 +221,64 @@ async def pin_message(_, message: Message): # RIP. await asyncio.sleep(3) await message.delete() + +@app.on_message(filters.command("promote", PREFIX) & filters.me) +async def promote(client, message: Message): + if await CheckAdmin(message) is False: + await message.edit("**I am not admin.**") + return + title = None + reply = message.reply_to_message + if reply: + user = reply.from_user["id"] + title = get_arg(message) + else: + args = get_args(message) + if len(args) != 1 and len(args) != 2: + await message.edit("**Whome should I promote**") + return + user = args[0] + if len(args) > 1: + title = " ".join(args[1:]) + try: + await app.promote_chat_member(message.chat.id, user, can_pin_messages=True) + await message.edit("**Promoted**") + except Exception as e: + await message.edit(f"{e}") + if title: + try: + await app.set_administrator_title(message.chat.id, user) + except: + pass + + +@app.on_message(filters.command("demote", PREFIX) & filters.me) +async def demote(client, message: Message): + if await CheckAdmin(message) is False: + await message.edit("**I am not admin.**") + return + reply = message.reply_to_message + if reply: + user = reply.from_user["id"] + else: + user = get_arg(message) + if not user: + await message.edit("**Whome should I demote?**") + return + try: + await app.promote_chat_member( + message.chat.id, + user, + is_anonymous=False, + can_change_info=False, + can_delete_messages=False, + can_edit_messages=False, + can_invite_users=False, + can_promote_members=False, + can_restrict_members=False, + can_pin_messages=False, + can_post_messages=False, + ) + await message.edit("**Demoted**") + except Exception as e: + await message.edit(f"{e}") diff --git a/Zect/modules/kang.py b/Zect/modules/kang.py new file mode 100644 index 0000000..3194903 --- /dev/null +++ b/Zect/modules/kang.py @@ -0,0 +1,241 @@ +import io +import os +import random +import time + +from PIL import Image +from pyrogram import emoji, filters +from pyrogram.raw.functions.messages import GetStickerSet +from pyrogram.raw.types import InputStickerSetShortName +from pyrogram.errors import YouBlockedUser, StickersetInvalid +from Zect.helpers.pyrohelper import get_args +from Zect import app +from config import PREFIX + + +convo = "" + + +@app.on_message(filters.command("kang", PREFIX) & filters.me) +async def kang(client, message): + user = await app.get_me() + replied = message.reply_to_message + photo = None + emoji_ = None + is_anim = False + resize = False + if replied and replied.media: + if replied.photo: + resize = True + elif replied.document and "image" in replied.document.mime_type: + resize = True + elif replied.document and "tgsticker" in replied.document.mime_type: + is_anim = True + elif replied.sticker: + if not replied.sticker.file_name: + await message.edit("`Sticker has no Name!`") + return + emoji_ = replied.sticker.emoji + is_anim = replied.sticker.is_animated + if not replied.sticker.file_name.endswith(".tgs"): + resize = True + else: + await message.edit("`Unsupported File!`") + return + await message.edit(f"`{random.choice(KANGING_STR)}`") + photo = await app.download_media(message=replied) + else: + await message.edit("`I can't kang that...`") + return + if photo: + args = get_args(message) + pack = 1 + if len(args) == 2: + emoji_, pack = args + elif len(args) == 1: + if args[0].isnumeric(): + pack = int(args[0]) + else: + emoji_ = args[0] + + if emoji_ and emoji_ not in ( + getattr(emoji, a) for a in dir(emoji) if not a.startswith("_") + ): + emoji_ = None + if not emoji_: + emoji_ = "🤔" + + u_name = user.username + if u_name: + u_name = "@" + u_name + else: + u_name = user.first_name or user.id + packname = f"a{user.id}_by_zect_{pack}" + custom_packnick = f"{u_name}'s kang pack" + packnick = f"{custom_packnick} Vol.{pack}" + cmd = "/newpack" + if resize: + photo = resize_photo(photo) + if is_anim: + packname += "_anim" + packnick += " (Animated)" + cmd = "/newanimated" + exist = False + try: + exist = await app.send( + GetStickerSet(stickerset=InputStickerSetShortName(short_name=packname)) + ) + except StickersetInvalid: + pass + if exist is not False: + try: + await app.send_message("Stickers", "/addsticker") + except YouBlockedUser: + await message.edit("first **unblock** @Stickers") + return + await app.send_message("Stickers", packname) + limit = "50" if is_anim else "120" + while limit in await get_response(message): + pack += 1 + packname = f"a{user.id}_by_zect_{pack}" + packnick = f"{custom_packnick} Vol.{pack}" + if is_anim: + packname += "_anim" + packnick += " (Animated)" + await message.edit( + "`Switching to Pack " + str(pack) + " due to insufficient space`" + ) + await app.send_message("Stickers", packname) + if await get_response(message) == "Invalid pack selected": + await app.send_message("Stickers", cmd) + await get_response(message) + await app.send_message("Stickers", packnick) + await get_response(message) + await app.send_document("Stickers", photo) + await get_response(message) + await app.send_message("Stickers", emoji_) + await get_response(message) + await app.send_message("Stickers", "/publish") + if is_anim: + time.sleep(0.2) + await app.send_message( + "Stickers", f"<{packnick}>", parse_mode=None + ) + time.sleep(0.2) + await app.send_message("Stickers", "/skip") + time.sleep(0.2) + await app.send_message("Stickers", packname) + out = f"[kanged](t.me/addstickers/{packname})" + await message.edit( + f"**Sticker** {out} __in a Different Pack__**!**" + ) + return + await app.send_document("Stickers", photo) + time.sleep(0.2) + rsp = await get_response(message) + if "Sorry, the file type is invalid." in rsp: + await message.edit( + "`Failed to add sticker, use` @Stickers " + "`bot to add the sticker manually.`" + ) + return + await app.send_message("Stickers", emoji_) + await get_response(message) + await app.send_message("Stickers", "/done") + else: + await message.edit("`Brewing a new Pack...`") + try: + await app.send_message("Stickers", cmd) + except YouBlockedUser: + await message.edit("first **unblock** @Stickers") + return + await app.send_message("Stickers", packnick) + await get_response(message) + await app.send_document("Stickers", photo) + time.sleep(0.2) + rsp = await get_response(message) + if "Sorry, the file type is invalid." in rsp: + await message.edit( + "`Failed to add sticker, use` @Stickers " + "`bot to add the sticker manually.`" + ) + return + await app.send_message("Stickers", emoji_) + await get_response(message) + await app.send_message("Stickers", "/publish") + if is_anim: + time.sleep(0.2) + await app.send_message("Stickers", f"<{packnick}>", parse_mode=None) + time.sleep(0.2) + await app.send_message("Stickers", "/skip") + await get_response(message) + await app.send_message("Stickers", packname) + out = f"[kanged](t.me/addstickers/{packname})" + await message.edit(f"**Sticker** {out}**!**") + await app.read_history("Stickers") + if os.path.exists(str(photo)): + os.remove(photo) + + +@app.on_message(filters.command("stkrinfo", PREFIX) & filters.me) +async def sticker_pack_info_(client, message): + replied = message.reply_to_message + if not replied: + await message.edit("`I can't fetch info from nothing, can I ?!`") + return + if not replied.sticker: + await message.edit("`Reply to a sticker to get the pack details`") + return + await message.edit("`Fetching details of the sticker pack, please wait..`") + get_stickerset = await app.send( + GetStickerSet( + stickerset=InputStickerSetShortName(short_name=replied.sticker.set_name) + ) + ) + pack_emojis = [] + for document_sticker in get_stickerset.packs: + if document_sticker.emoticon not in pack_emojis: + pack_emojis.append(document_sticker.emoticon) + out_str = ( + f"**Sticker Title:** `{get_stickerset.set.title}\n`" + f"**Sticker Short Name:** `{get_stickerset.set.short_name}`\n" + f"**Archived:** `{get_stickerset.set.archived}`\n" + f"**Official:** `{get_stickerset.set.official}`\n" + f"**Masks:** `{get_stickerset.set.masks}`\n" + f"**Animated:** `{get_stickerset.set.animated}`\n" + f"**Stickers In Pack:** `{get_stickerset.set.count}`\n" + f"**Emojis In Pack:**\n{' '.join(pack_emojis)}" + ) + await message.edit(out_str) + + +def resize_photo(photo: str) -> io.BytesIO: + """ Resize the given photo to 512x512 """ + image = Image.open(photo) + maxsize = 512 + scale = maxsize / max(image.width, image.height) + new_size = (int(image.width * scale), int(image.height * scale)) + image = image.resize(new_size, Image.LANCZOS) + resized_photo = io.BytesIO() + resized_photo.name = "sticker.png" + image.save(resized_photo, "PNG") + os.remove(photo) + return resized_photo + + +async def get_response(message): + return [x async for x in app.iter_history("Stickers", limit=1)][0].text + + +KANGING_STR = ( + "Using Witchery to kang this sticker...", + "Plagiarising hehe...", + "Inviting this sticker over to my pack...", + "Kanging this sticker...", + "Hey that's a nice sticker!\nMind if I kang?!..", + "hehe me stel ur stikér\nhehe.", + "Ay look over there (☉。☉)!→\nWhile I kang this...", + "Roses are red violets are blue, kanging this sticker so my pacc looks cool", + "Imprisoning this sticker...", + "Mr.Steal Your Sticker is stealing this sticker... ", +) diff --git a/Zect/modules/updater.py b/Zect/modules/updater.py new file mode 100644 index 0000000..f82d56f --- /dev/null +++ b/Zect/modules/updater.py @@ -0,0 +1,156 @@ +import asyncio +import sys +from os import environ, execle, path, remove +from config import HEROKU_API, HEROKU_APP_NAME, PREFIX + +from git import Repo +from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError + +from pyrogram import filters +from Zect import app +from Zect.helpers.pyrohelper import get_arg + +UPSTREAM_REPO_URL = "https://github.com/okay-retard/ZectUserBot" +requirements_path = path.join( + path.dirname(path.dirname(path.dirname(__file__))), "requirements.txt" +) + + +async def gen_chlog(repo, diff): + ch_log = "" + d_form = "On %d/%m/%y at %H:%M:%S" + for c in repo.iter_commits(diff): + ch_log += f"**#{c.count()}** : {c.committed_datetime.strftime(d_form)} : [{c.summary}]({UPSTREAM_REPO_URL.rstrip('/')}/commit/{c}) by `{c.author}`\n" + return ch_log + + +async def updateme_requirements(): + reqs = str(requirements_path) + try: + process = await asyncio.create_subprocess_shell( + " ".join([sys.executable, "-m", "pip", "install", "-r", reqs]), + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + await process.communicate() + return process.returncode + except Exception as e: + return repr(e) + + +@app.on_message(filters.command("update", PREFIX) & filters.me) +async def upstream(client, message): + status = await message.edit("`Checking for updates, please wait....`") + conf = get_arg(message) + off_repo = UPSTREAM_REPO_URL + try: + txt = "`Oops.. Updater cannot continue due to " + txt += "some problems occured`\n\n**LOGTRACE:**\n" + repo = Repo() + except NoSuchPathError as error: + await status.edit(f"{txt}\n`directory {error} is not found`") + repo.__del__() + return + except GitCommandError as error: + await status.edit(f"{txt}\n`Early failure! {error}`", time=10) + repo.__del__() + return + except InvalidGitRepositoryError as error: + if conf != "now": + await status.edit(f"**Unfortunately, the directory {error} does not seem to be a git repository.Or Maybe it just needs a sync verification. But we can fix that by force updating the userbot using** `.update now.`") + return + repo = Repo.init() + origin = repo.create_remote("upstream", off_repo) + origin.fetch() + repo.create_head("master", origin.refs.master) + repo.heads.master.set_tracking_branch(origin.refs.master) + repo.heads.master.checkout(True) + ac_br = repo.active_branch.name + if ac_br != "master": + await status.edit(f"**[UPDATER]:**` You are on ({ac_br})\n Please change to master branch.`") + repo.__del__() + return + try: + repo.create_remote("upstream", off_repo) + except BaseException: + pass + ups_rem = repo.remote("upstream") + ups_rem.fetch(ac_br) + changelog = await gen_chlog(repo, f"HEAD..upstream/{ac_br}") + if "now" not in conf: + if changelog: + changelog_str = f"**New UPDATE available for [[{ac_br}]]({UPSTREAM_REPO_URL}/tree/{ac_br}):\n\nCHANGELOG**\n\n{changelog}" + if len(changelog_str) > 4096: + await status.edit("`Changelog is too big, view the file to see it.`") + file = open("output.txt", "w+") + file.write(changelog_str) + file.close() + await app.send_document( + message.chat.id, + "output.txt", + caption="Do `.update now` to update.", + reply_to_message_id=status.message_id + ) + remove("output.txt") + else: + return await status.edit("{changelog_str}\n\nDo `.update now` to update." + ) + else: + await status.edit(f"\n`Your BOT is` **up-to-date** `with` **[[{ac_br}]]({UPSTREAM_REPO_URL}/tree/{ac_br})**\n" + ) + repo.__del__() + return + if HEROKU_API is not None: + import heroku3 + + heroku = heroku3.from_key(HEROKU_API) + heroku_app = None + heroku_applications = heroku.apps() + if not HEROKU_APP_NAME: + await status.edit( + "`Please set up the HEROKU_APP_NAME variable to be able to update userbot.`" + ) + repo.__del__() + return + for app in heroku_applications: + if app.name == HEROKU_APP_NAME: + heroku_app = app + break + if heroku_app is None: + await status.edit( + f"{txt}\n`Invalid Heroku credentials for updating userbot dyno.`" + ) + repo.__del__() + return + await status.edit("`Userbot dyno build in progress, please wait for it to complete.`") + ups_rem.fetch(ac_br) + repo.git.reset("--hard", "FETCH_HEAD") + heroku_git_url = heroku_app.git_url.replace( + "https://", "https://api:" + HEROKU_API + "@" + ) + if "heroku" in repo.remotes: + remote = repo.remote("heroku") + remote.set_url(heroku_git_url) + else: + remote = repo.create_remote("heroku", heroku_git_url) + try: + remote.push(refspec=f"HEAD:refs/heads/{ac_br}", force=True) + except GitCommandError as error: + await status.edit(f"{txt}\n`Here is the error log:\n{error}`") + repo.__del__() + return + await status.edit("`Successfully Updated!\nRestarting, please wait...`") + else: + # Classic Updater, pretty straightforward. + try: + ups_rem.pull(ac_br) + except GitCommandError: + repo.git.reset("--hard", "FETCH_HEAD") + await updateme_requirements() + await status.edit( + "`Successfully Updated!\nBot is restarting... Wait for a second!`", + ) + # Spin a new instance of bot + args = [sys.executable, "./resources/startup/deploy.sh"] + execle(sys.executable, *args, environ) + return diff --git a/app.json b/app.json index 24d6c2b..50060bf 100644 --- a/app.json +++ b/app.json @@ -18,6 +18,14 @@ "description": "This is for the database account, you can obtain it from mongodb.com.", "required": true }, + "HEROKU_API": { + "description": "Your heroku api key if using heroku.", + "required": false + }, + "HEROKU_APP_NAME": { + "description": "Your heroku app name if using heroku.", + "required": false + }, "PREFIX": { "description": "Command handler.", "required": true, diff --git a/config.py b/config.py index 1a0dfc3..c4d9223 100644 --- a/config.py +++ b/config.py @@ -4,5 +4,7 @@ API_ID = int(os.getenv("API_ID")) MONGO_URI = os.getenv("MONGO_URI") SESSION = os.getenv("SESSION") +HEROKU_API = os.getenv("HEROKU_API") +HEROKU_APP_NAME = os.getenv("HEROKU_APP_NAME") PREFIX = os.getenv("PREFIX") -LOG_CHAT = int(os.getenv("LOG_CHAT")) \ No newline at end of file +LOG_CHAT = int(os.getenv("LOG_CHAT")) diff --git a/requirements.txt b/requirements.txt index 9b617c0..25f8d5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,5 @@ dnspython requests asyncio pillow +GitPython +heroku3