diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 9f03b7b..2345dc7 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -28,4 +28,4 @@ jobs: repository: . commit_user_name: kaif-00z commit_user_email: 88398455+kaif-00z@users.noreply.github.com - commit_author: kaif-00z <88398455+kaif-00z@users.noreply.github.com> \ No newline at end of file + commit_author: kaif-00z <88398455+kaif-00z@users.noreply.github.com> diff --git a/.gitignore b/.gitignore index a1b9cbb..30f8d21 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .env -__pycache__/ \ No newline at end of file +__pycache__/ +test.py \ No newline at end of file diff --git a/.sample.env b/.sample.env index e6c3e74..ec2e0d3 100644 --- a/.sample.env +++ b/.sample.env @@ -4,13 +4,18 @@ BOT_TOKEN= MAIN_CHANNEL=-1001111 LOG_CHANNEL=-1001111 CLOUD_CHANNEL=-1001111 -REDIS_URI= -REDIS_PASSWORD= +FIREBASE_SERVICE_ACCOUNT_FILE= +FIREBASE_URL= OWNER=1872074304 BACKUP_CHANNEL=-1001111 # Optional Environment Variable +API_ID= +API_HASH= +FORCESUB_CHANNEL_LINK= +FORCESUB_CHANNEL= +SESSION= SEND_SCHEDULE= THUMBNAIL= CRF= \ No newline at end of file diff --git a/README.md b/README.md index e229129..8a8a234 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,19 @@ ## Description Of Latest Update +- Added Separate Anime Channel Upload +-
Click Here To See How Separate Anime Channel Upload Look.sepul1sepul2
+- Added Button Upload Support (File Store) +-
Click Here To See How Button Upload Look.btnul
+- Added ForceSub +- Added 480p Support +- Added Broadcast +- Major Modification In FFMPEG Code. +- Modified Anime Searcher +- Admin Panel Fixed - ReWritten Whole Program (Fully OOPs Based) - Optimized Core - Added Heroku Support -- Added Button Upload Support (File Store) --
Click Here To See How Button Upload Look.btnul
- Added Custom CRF Support ## Contributing @@ -40,9 +48,9 @@ - `BOT_TOKEN` - Get This From @Botfather In Telegram. -- `REDIS_URI` - Get This From redis.com +- `FIREBASE_URL` - Get This From Firebase Realtime Database Console. -- `REDIS_PASSWORD` - Get This From redis.com +- `FIREBASE_SERVICE_ACCOUNT_FILE` - Get This From Firebase Realtime Database Console. - `MAIN_CHANNEL` - ID of Channel Where Anime Will Upload. @@ -54,6 +62,8 @@ ### OPTIONAL VARIABLES +- `SESSION` - Telethon Session String Of Your Telegram Account. + - `BACKUP_CHANNEL` - ID of Channel Where Anime Will Be Saved As BackUP if You Are Using Button Upload Option Then Make Sure To SET Backup Channel. - `THUMBNAIL` - JPG/PNG Link of Thumbnail FIle. @@ -78,7 +88,7 @@ ## Commands -[![Comand](https://graph.org/file/82176674097989fae68d4.png)](https://github.com/kaif-00z/AutoAnimeBot/) +[![Comand](https://graph.org/file/ca8de14ba0b1d3b71af1f.jpg)](https://github.com/kaif-00z/AutoAnimeBot/) **Uploading of Ongoing Animes Is Automatic** diff --git a/app.json b/app.json index c0f67fa..5022ce5 100644 --- a/app.json +++ b/app.json @@ -22,18 +22,24 @@ "value": "" }, + "SESSION": { + "description": "Telethon Session String", + "value": "", + "required": false + }, + "OWNER": { "description": "Get ur Id Of Telegram nd Paste Here.", "value": "" }, - "REDIS_URI": { - "description": "Redis endpoint URL, from redislabs.com", + "FIREBASE_URL": { + "description": "Firebase endpoint URL, from Firebase Realtime Database", "value": "" }, - "REDIS_PASSWORD": { - "description": "Redis endpoint password, from redislabs.com", + "FIREBASE_SERVICE_ACCOUNT_FILE": { + "description": "Firebase Service Account File Link, from Firebase Realtime Database", "value": "" }, diff --git a/auto_env_gen.py b/auto_env_gen.py index fb9db3b..d71552b 100644 --- a/auto_env_gen.py +++ b/auto_env_gen.py @@ -19,13 +19,16 @@ DATA = {} ENV = """ +API_ID={} +API_HASH={} BOT_TOKEN={} +SESSION={} MAIN_CHANNEL={} LOG_CHANNEL={} CLOUD_CHANNEL={} BACKUP_CHANNEL={} -REDIS_URI={} -REDIS_PASSWORD={} +FIREBASE_URL={} +FIREBASE_SERVICE_ACCOUNT_FILE={} OWNER={} """ @@ -35,21 +38,24 @@ async def generate_session_string(): api_hash = input("Enter your API_HASH: ") if api_id and api_hash: async with TelegramClient(StringSession(), api_id, api_hash) as client: + DATA["api_id"] = api_id + DATA["api_hash"] = api_hash + DATA["session"] = str(client.session.save()) return (str(client.session.save()), api_id, api_hash) print("API_ID and HASH Not Found!") sys.exit(1) -def get_redis(): - redis_uri = input("Enter your Redis URI: ") - redis_pass = input("Enter your Redis Password: ") - if redis_uri and redis_pass: - DATA["redis_uri"] = redis_uri - DATA["redis_pass"] = redis_pass +def get_firebase(): + uri = input("Enter your Firebase Realtime Database Url: ") + _pass = input("Enter your Firebase Realtime Database Service Account File Link: ") + if uri and _pass: + DATA["firebase_uri"] = uri + DATA["firebase_pass"] = _pass return True else: - DATA["redis_uri"] = "" - DATA["redis_pass"] = "" + DATA["firebase_uri"] = "" + DATA["firebase_pass"] = "" return False @@ -72,13 +78,16 @@ async def create_channel(client, title): def generate_env(): txt = ENV.format( + DATA["api_id"], + DATA["api_hash"], DATA["bot_token"], + DATA["session"], DATA["Ongoing Anime 2024"], DATA["Ongoing Anime Logs"], DATA["Ongoing Anime Samples And SS"], DATA["Ongoing Anime Backup"], - DATA.get("redis_uri") or "", - DATA.get("redis_pass") or "", + DATA.get("firebase_uri") or "", + DATA.get("firebase_pass") or "", DATA["owner_id"], ) with open(".env", "w") as f: @@ -160,10 +169,10 @@ async def auto_maker(): print(format_exc()) sys.exit(1) print("Succesfully Created Channel...") - db = get_redis() + db = get_firebase() if not db: print( - "Generating .env Without Redis URI and Password. Now You Have To Add it Manually!" + "Generating .env Without Firebase URI and Service Account. Now You Have To Add it Manually!" ) generate_env() diff --git a/bot.py b/bot.py index 1a3b5c5..03dc379 100644 --- a/bot.py +++ b/bot.py @@ -34,7 +34,7 @@ tools = Tools() tools.init_dir() -bot = Bot(None) +bot = Bot() dB = DataBase() subsplease = SubsPlease(dB) torrent = Torrent() @@ -48,22 +48,24 @@ ) ) async def _start(event): - msg_id = event.pattern_match.group(1) xnx = await event.reply("`Please Wait...`") + msg_id = event.pattern_match.group(1) + dB.add_broadcast_user(event.sender_id) if Var.FORCESUB_CHANNEL and Var.FORCESUB_CHANNEL_LINK: is_user_joined = await bot.is_joined(Var.FORCESUB_CHANNEL, event.sender_id) if is_user_joined: pass else: return await xnx.edit( - f"**Please Join {Var.FORCESUB_CHANNEL_LINK} To Use This Bot**", + f"**Please Join The Following Channel To Use This Bot 🫑**", buttons=[ + [Button.url("πŸš€ JOIN CHANNEL", url=Var.FORCESUB_CHANNEL_LINK)], [ Button.url( "♻️ REFRESH", url=f"https://t.me/{((await bot.get_me()).username)}?start={msg_id}", ) - ] + ], ], ) if msg_id: @@ -78,8 +80,7 @@ async def _start(event): await event.reply(file=[i for i in msg]) else: if event.sender_id == Var.OWNER: - await xnx.delete() - return await event.reply( + return await xnx.edit( "** < ADMIN PANEL > **", buttons=admin.admin_panel(), ) @@ -123,6 +124,16 @@ async def _(e): await admin._btn_t(e) +@bot.on(events.callbackquery.CallbackQuery(data="scul")) +async def _(e): + await admin._sep_c_t(e) + + +@bot.on(events.callbackquery.CallbackQuery(data="cast")) +async def _(e): + await admin.broadcast_bt(e) + + @bot.on(events.callbackquery.CallbackQuery(data="bek")) async def _(e): await e.edit(buttons=admin.admin_panel()) @@ -131,8 +142,24 @@ async def _(e): async def anime(data): try: torr = [data.get("480p"), data.get("720p"), data.get("1080p")] - poster = await tools._poster(bot, AnimeInfo(torr[0].title)) + anime_info = AnimeInfo(torr[0].title) + poster = await tools._poster(bot, anime_info) + if dB.is_separate_channel_upload(): + chat_info = await tools.get_chat_info(bot, anime_info, dB) + await poster.edit( + buttons=[ + [ + Button.url( + f"EPISODE {anime_info.data.get('episode_number', '')}".strip(), + url=chat_info["invite_link"], + ) + ] + ] + ) + poster = await tools._poster(bot, anime_info, chat_info["chat_id"]) btn = [[]] + original_upload = dB.is_original_upload() + button_upload = dB.is_button_upload() for i in torr: try: filename = f"downloads/{i.title}" @@ -143,8 +170,8 @@ async def anime(data): bot, dB, { - "original_upload": dB.is_original_upload(), - "button_upload": dB.is_button_upload(), + "original_upload": original_upload, + "button_upload": button_upload, }, filename, AnimeInfo(i.title), diff --git a/core/bot.py b/core/bot.py index 01b6075..60d82c9 100644 --- a/core/bot.py +++ b/core/bot.py @@ -17,8 +17,10 @@ # credit to t.me/kAiF_00z (github.com/kaif-00z) # little bit inspired from pyUltroid.BaseClient +import asyncio import sys from logging import Logger +from traceback import format_exc from pyrogram import Client from telethon import TelegramClient @@ -29,7 +31,13 @@ AuthKeyDuplicatedError, ) from telethon.errors.rpcerrorlist import UserNotParticipantError -from telethon.tl.functions.channels import GetParticipantRequest +from telethon.sessions import StringSession +from telethon.tl.functions.channels import ( + CreateChannelRequest, + EditPhotoRequest, + GetParticipantRequest, +) +from telethon.tl.functions.messages import ExportChatInviteRequest from functions.config import Var from libs.logger import LOGS, TelethonLogger @@ -38,7 +46,6 @@ class Bot(TelegramClient): def __init__( self, - session, api_id=None, api_hash=None, bot_token=None, @@ -54,7 +61,7 @@ def __init__( kwargs["api_id"] = api_id or Var.API_ID kwargs["api_hash"] = api_hash or Var.API_HASH kwargs["base_logger"] = TelethonLogger - super().__init__(session, **kwargs) + super().__init__(None, **kwargs) self.pyro_client = Client( name="pekka", api_id=kwargs["api_id"], @@ -62,6 +69,11 @@ def __init__( bot_token=bot_token or Var.BOT_TOKEN, in_memory=True, ) + self.user_client = None + if Var.SESSION: + self.user_client = TelegramClient( + StringSession(Var.SESSION), kwargs["api_id"], kwargs["api_hash"] + ) self.run_in_loop(self.start_client(bot_token=bot_token or Var.BOT_TOKEN)) def __repr__(self): @@ -73,6 +85,8 @@ async def start_client(self, **kwargs): self.logger.info("Trying to login.") try: await self.start(**kwargs) + if self.user_client: + await self.user_client.start() await self.pyro_client.start() except ApiIdInvalidError: self.logger.critical("API ID and API_HASH combination does not match!") @@ -92,6 +106,9 @@ async def start_client(self, **kwargs): me = f"@{self.me.username}" if self._log_at: self.logger.info(f"Logged in as {me}") + if self.user_client: + user_me = await self.user_client.get_me() + self.logger.info(f"Logged in as @{user_me.username}") self._bot = await self.is_bot() async def upload_anime(self, file, caption, thumb=None, is_button=False): @@ -109,11 +126,11 @@ async def upload_anime(self, file, caption, thumb=None, is_button=False): ) return post - async def upload_poster(self, file, caption): + async def upload_poster(self, file, caption, channel_id=None): post = await self.send_file( - Var.MAIN_CHANNEL, + channel_id if channel_id else Var.MAIN_CHANNEL, file=file, - caption=caption, + caption=caption or "", ) return post @@ -124,6 +141,55 @@ async def is_joined(self, channel_id, user_id): except UserNotParticipantError: return False + async def create_channel(self, title: str, logo=None): + try: + r = await self.user_client( + CreateChannelRequest( + title=title, + about="Powered By github.com/kaif-00z/AutoAnimeBot", + megagroup=False, + ) + ) + created_chat_id = r.chats[0].id + chat_id = int(f"-100{created_chat_id}") + await asyncio.sleep(2) + await self.user_client.edit_admin( + int(chat_id), + f"{((await self.get_me()).username)}", + post_messages=True, + edit_messages=True, + delete_messages=True, + ban_users=True, + pin_messages=True, + add_admins=True, + ) + if logo: + try: + await self.user_client( + EditPhotoRequest( + chat_id, (await self.user_client.upload_file(logo)) + ) + ) + except BaseException: + pass + return chat_id + except BaseException: + LOGS.error(format_exc()) + + async def generate_invite_link(self, channel_id): + try: + data = await self.user_client( + ExportChatInviteRequest( + peer=channel_id, + title=f"Generated By Ongoing Anime Bot", + request_needed=False, + usage_limit=None, + ) + ) + return data.link + except BaseException: + LOGS.error(format_exc()) + def run_in_loop(self, function): return self.loop.run_until_complete(function) diff --git a/database/__init__.py b/database/__init__.py index ab4d002..f43c6b4 100644 --- a/database/__init__.py +++ b/database/__init__.py @@ -18,38 +18,42 @@ from traceback import format_exc -from redis import Redis - from functions.config import Var +from libs.firebasewarp import FireDB from libs.logger import LOGS class DataBase: def __init__(self): try: - LOGS.info("Trying Connect With Redis database") - redis_info = Var.REDIS_URI.split(":") - self.dB = Redis( - host=redis_info[0], - port=redis_info[1], - password=Var.REDIS_PASS, - charset="utf-8", - decode_responses=True, - ) - LOGS.info("Successfully Connected to Redis database") + LOGS.info("Trying Connect With Firebase Database") + self.dB = FireDB(Var) + LOGS.info("Successfully Connected to Firebase Database") except Exception as error: LOGS.exception(format_exc()) LOGS.critical(str(error)) exit() - self.cache = {} - self.re_cache() + self.cache = self.dB.getall() + LOGS.info(f"Succesfully Sync Database!!!") def add_anime(self, name): data = self.cache.get("ANIMES_UPLOADED") or [] if name not in data: data.append(name) self.cache["ANIMES_UPLOADED"] = data - self.dB.set("ANIMES_UPLOADED", str(data)) + self.dB.create_data("ANIMES_UPLOADED", data) + + def toggle_separate_channel_upload(self): + data = self.cache.get("SEPARATE_CHANNEL_UPLOAD") or False + if data: + data = False + else: + data = True + self.cache["SEPARATE_CHANNEL_UPLOAD"] = data + self.dB.create_data("SEPARATE_CHANNEL_UPLOAD", data) + + def is_separate_channel_upload(self): + return self.cache.get("SEPARATE_CHANNEL_UPLOAD") or False def toggle_original_upload(self): data = self.cache.get("OG_UPLOAD") or False @@ -58,7 +62,7 @@ def toggle_original_upload(self): else: data = True self.cache["OG_UPLOAD"] = data - self.dB.set("OG_UPLOAD", str(data)) + self.dB.create_data("OG_UPLOAD", data) def is_original_upload(self): return self.cache.get("OG_UPLOAD") or False @@ -70,7 +74,7 @@ def toggle_button_upload(self): else: data = True self.cache["BUTTON_UPLOAD"] = data - self.dB.set("BUTTON_UPLOAD", str(data)) + self.dB.create_data("BUTTON_UPLOAD", data) def is_button_upload(self): return self.cache.get("BUTTON_UPLOAD") or False @@ -81,14 +85,26 @@ def is_anime_uploaded(self, name): return True return False + def add_anime_channel_info(self, title, _data): + data = self.cache.get("ANIME_CHANNEL_INFO") or {} + data.update({title: _data}) + self.cache["ANIME_CHANNEL_INFO"] = data + self.dB.create_data(f"ANIME_CHANNEL_INFO/{title}", _data) + + def get_anime_channel_info(self, title): + data = self.cache.get("ANIME_CHANNEL_INFO") or {} + if data.get(title): + return data[title] + return {} + def get_anime_uploaded_list(self): return self.cache.get("ANIMES_UPLOADED") or [] - def store_items(self, _hash, list): + def store_items(self, _hash, _list): data = self.cache.get("FILESTORE") or {} - data.update({_hash: list}) + data.update({_hash: _list}) self.cache["FILESTORE"] = data - self.dB.set("FILESTORE", str(data)) + self.dB.create_data(f"FILESTORE/{_hash}", _list) def get_store_items(self, _hash): data = self.cache.get("FILESTORE") or {} @@ -96,7 +112,12 @@ def get_store_items(self, _hash): return data[_hash] return [] - def re_cache(self): - for key in self.dB.keys(): - self.cache.update({key: eval(self.dB.get(key) or "[]")}) - LOGS.info(f"Succesfully Sync Database!!!") + def add_broadcast_user(self, user_id): + data = self.cache.get("BROADCAST") or [] + if user_id not in data: + data.append(int(user_id)) + self.cache["BROADCAST"] = data + self.dB.create_data("BROADCAST", data) + + def get_broadcast_user(self): + return self.cache.get("BROADCAST") or [] diff --git a/functions/config.py b/functions/config.py index 09f4eec..caf320b 100644 --- a/functions/config.py +++ b/functions/config.py @@ -25,11 +25,14 @@ class Var: API_ID = config("API_ID", default=6, cast=int) API_HASH = config("API_HASH", default="eb06d4abfb49dc3eeb1aeb98ae0f581e") BOT_TOKEN = config("BOT_TOKEN", default=None) + SESSION = config("SESSION", default=None) # Database Credentials - REDIS_URI = config("REDIS_URI", default=None) - REDIS_PASS = config("REDIS_PASSWORD", default=None) + FIREBASE_URL = config("FIREBASE_URL", default=None) + FIREBASE_SERVICE_ACCOUNT_FILE = config( + "FIREBASE_SERVICE_ACCOUNT_FILE", default=None + ) # Channels Ids diff --git a/functions/info.py b/functions/info.py index ebcfde9..a7c56e2 100644 --- a/functions/info.py +++ b/functions/info.py @@ -16,10 +16,9 @@ # if you are using this following code then don't forgot to give proper # credit to t.me/kAiF_00z (github.com/kaif-00z) -from datetime import datetime +from traceback import format_exc import anitopy -import pytz from libs.kitsu import RawAnimeInfo from libs.logger import LOGS @@ -31,14 +30,11 @@ def __init__(self, name): self.CAPTION = """ **{} ━━━━━━━━━━━━━━━ -β€£ Language : Japanese [English-Sub] -β€£ Quality : 480p, 720p, 1080p -β€£ Season : {} -β€£ Episode : {} -━━━━━━━━━━━━━━━ -γ€£ Next Airing Episode: {} -γ€£ Next Airing Episode Date: {} -━━━━━━━━━━━━━━━** +β€£ Language:** `Japanese [ESub]` +**β€£ Quality:** `480p|720p|1080p` +**β€£ Season:** `{}` +**β€£ Episode:** `{}` +**━━━━━━━━━━━━━━━** """ self.proper_name = self.get_proper_name_for_func(name) self.name = name @@ -48,9 +44,9 @@ async def get_english(self): anime_name = self.data.get("anime_title") try: anime = (await self.kitsu.search(self.proper_name)) or {} - return anime.get("english_title").strip() or anime_name - except Exception as error: - LOGS.error(str(error)) + return anime.get("english_title") or anime_name + except BaseException: + LOGS.error(str(format_exc())) return anime_name.strip() async def get_poster(self): @@ -58,8 +54,8 @@ async def get_poster(self): if self.proper_name: anime_poster = await self.kitsu.search(self.proper_name) return anime_poster.get("poster_img") or None - except Exception as error: - LOGS.error(str(error)) + except BaseException: + LOGS.error(str(format_exc())) async def get_cover(self): try: @@ -68,29 +64,23 @@ async def get_cover(self): if anime_poster.get("anilist_id"): return anime_poster.get("anilist_poster") return None - except Exception as error: - LOGS.error(str(error)) + except BaseException: + LOGS.error(str(format_exc())) async def get_caption(self): try: if self.proper_name or self.data: - anime = (await self.kitsu.search(self.proper_name)) or {} - next_ = anime.get("next_airing_ep", {}) return self.CAPTION.format( - anime.get("english_title").strip() or self.data.get("anime_title"), - self.data.get("anime_season") or 1, - self.data.get("episode_number") or "N/A", - next_.get("episode") or "N/A", + (await self.get_english()), + str(self.data.get("anime_season") or 1).zfill(2), ( - datetime.fromtimestamp( - next_.get("airingAt"), tz=pytz.timezone("Asia/Kolkata") - ).strftime("%A, %B %d, %Y") - if next_.get("airingAt") + str(self.data.get("episode_number")).zfill(2) + if self.data.get("episode_number") else "N/A" ), ) - except Exception as error: - LOGS.error(str(error)) + except BaseException: + LOGS.error(str(format_exc())) return "" async def rename(self, original=False): @@ -115,6 +105,7 @@ async def rename(self, original=False): return self.name except Exception as error: LOGS.error(str(error)) + LOGS.exception(format_exc()) return self.name def get_proper_name_for_func(self, name): @@ -134,3 +125,4 @@ def get_proper_name_for_func(self, name): return anime_name except Exception as error: LOGS.error(str(error)) + LOGS.exception(format_exc()) diff --git a/functions/tools.py b/functions/tools.py index 0faa095..86d4468 100644 --- a/functions/tools.py +++ b/functions/tools.py @@ -77,6 +77,7 @@ async def cover_dl(self, link): await file.write(image) return fn except Exception as error: + LOGS.exception(format_exc()) LOGS.error(str(error)) async def mediainfo(self, file, bot): @@ -98,12 +99,32 @@ async def mediainfo(self, file, bot): ) return page.get("url") except Exception as error: + LOGS.exception(format_exc()) LOGS.error(str(error)) - async def _poster(self, bot, anime_info): + async def _poster(self, bot, anime_info, channel_id=None): thumb = await self.cover_dl((await anime_info.get_cover())) caption = await anime_info.get_caption() - return await bot.upload_poster(thumb or "assest/poster_not_found.jpg", caption) + return await bot.upload_poster( + thumb or "assest/poster_not_found.jpg", + caption, + channel_id if channel_id else None, + ) + + async def get_chat_info(self, bot, anime_info, dB): + try: + chat_info = dB.get_anime_channel_info(anime_info.proper_name) + if not chat_info: + chat_id = await bot.create_channel( + (await anime_info.get_english()), + (await self.cover_dl((await anime_info.get_poster()))), + ) + invite_link = await bot.generate_invite_link(chat_id) + chat_info = {"chat_id": chat_id, "invite_link": invite_link} + dB.add_anime_channel_info(anime_info.proper_name, chat_info) + return chat_info + except BaseException: + LOGS.error(str(format_exc())) def init_dir(self): if not os.path.exists("thumb.jpg"): @@ -143,7 +164,7 @@ async def rename_file(self, dl, out): return True, out async def compress(self, dl, out): - cmd = f'''{Var.FFMPEG} -i """{dl}""" -metadata "Encoded By"="https://github.com/kaif-00z/AutoAnimeBot/" -preset ultrafast -c:v libx265 -crf {Var.CRF} -map 0:v -c:a aac -map 0:a -c:s copy -map 0:s? """{out}""" -y''' + cmd = f'''{Var.FFMPEG} -i """{dl}""" -metadata "Encoded By"="https://github.com/kaif-00z/AutoAnimeBot/" -map 0:v -map 0:a -map 0:s -c:v libx264 -x265-params 'bframes=8:psy-rd=1:ref=3:aq-mode=3:aq-strength=0.8:deblock=1,1' -pix_fmt yuv420p -crf {Var.CRF} -c:a libopus -b:a 32k -ac 2 -ab 32k -vbr 2 -level 3.1 -threads 2 -preset veryfast """{out}""" -y''' process = await asyncio.create_subprocess_shell( cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) @@ -234,3 +255,4 @@ async def gen_ss_sam(self, _hash, filename): return _hash, out except Exception as error: LOGS.error(str(error)) + LOGS.exception(format_exc()) diff --git a/functions/utils.py b/functions/utils.py index f4976ab..a00eb17 100644 --- a/functions/utils.py +++ b/functions/utils.py @@ -16,9 +16,9 @@ # if you are using this following code then don't forgot to give proper # credit to t.me/kAiF_00z (github.com/kaif-00z) -from telethon import Button +from telethon import Button, events -from core.bot import Bot +from core.bot import Bot, Var, asyncio from database import DataBase @@ -37,6 +37,8 @@ def admin_panel(self): Button.inline("🎞️ Encode [Toogle]", data="entg"), ], [Button.inline("πŸ”˜ Button Upload [Toogle]", data="butg")], + [Button.inline("πŸ—ƒοΈ Separate Channel Upload [Toogle]", data="scul")], + [Button.inline("πŸ”Š Broadcast", data="cast")], ] return btn @@ -65,10 +67,69 @@ async def _encode_t(self, e): ) async def _btn_t(self, e): - if self.db.is_button_upload: + if self.db.is_separate_channel_upload(): + return await e.edit( + "`You Can't On/Off The Button Upload When Seprate Channel Is Enabled`", + buttons=self.back_btn(), + ) + if self.db.is_button_upload(): self.db.toggle_button_upload() return await e.edit( - "`Successfully On The Button Upload`", buttons=self.back_btn() + "`Successfully Off The Button Upload`", buttons=self.back_btn() ) self.db.toggle_button_upload() - return await e.edit("`Successfully Off The Upload`", buttons=self.back_btn()) + return await e.edit("`Successfully On The Upload`", buttons=self.back_btn()) + + async def _sep_c_t(self, e): + if Var.SESSION: + if self.db.is_button_upload(): + if self.db.is_separate_channel_upload(): + self.db.toggle_separate_channel_upload() + return await e.edit( + "`Successfully Off The Separate Channel Upload`", + buttons=self.back_btn(), + ) + self.db.toggle_separate_channel_upload() + return await e.edit( + "`Successfully On The Separate Channel Upload`", + buttons=self.back_btn(), + ) + else: + return await e.edit( + "`To Use The Separate Channel Upload First You Have To Enable Button Upload`", + buttons=self.back_btn(), + ) + else: + return await e.edit( + "`To Use The Separate Channel Upload First You Have To Add SESSION Variable in The Bot", + buttons=self.back_btn(), + ) + + async def broadcast_bt(self, e): + users = self.db.get_broadcast_user() + await e.edit("**Please Use This Feature Responsibly ⚠️**") + await e.reply( + f"**Send a single Message To Broadcast πŸ˜‰**\n\n**There are** `{len(users)}` **Users Currently Using MeπŸ‘‰πŸ»**.\n\nSend /cancel to Cancel Process." + ) + async with e.client.conversation(e.sender_id) as cv: + reply = cv.wait_event(events.NewMessage(from_users=e.sender_id)) + repl = await reply + await e.delete() + if repl.text and repl.text.startswith("/cancel"): + return await repl.reply("`Broadcast Cancelled`") + sent = await repl.reply("`πŸ—£οΈ Broadcasting Your Post...`") + done, er = 0, 0 + for user in users: + try: + if repl.poll: + await repl.forward_to(int(user)) + else: + await e.client.send_message(int(user), repl.message) + await asyncio.sleep(0.2) + done += 1 + except BaseException as ex: + er += 1 + print(ex) + await sent.edit( + f"**Broadcast Completed To** `{done}` **Users.**\n**Error in** `{er}` **Users.**" + ) diff --git a/libs/firebasewarp.py b/libs/firebasewarp.py new file mode 100644 index 0000000..72f50a3 --- /dev/null +++ b/libs/firebasewarp.py @@ -0,0 +1,57 @@ +from traceback import format_exc + +import firebase_admin +import requests +from firebase_admin import db + +from libs.logger import LOGS + + +def firebase_auth(Var): + if Var.FIREBASE_SERVICE_ACCOUNT_FILE and Var.FIREBASE_URL: + if not Var.FIREBASE_SERVICE_ACCOUNT_FILE.startswith("https://"): + LOGS.error("Firebase Service Account File Link is Wrong!") + exit() + + service_acc = requests.get(Var.FIREBASE_SERVICE_ACCOUNT_FILE).json() + + try: + cred = firebase_admin.credentials.Certificate(service_acc) + firebase_admin.initialize_app(cred, {"databaseURL": Var.FIREBASE_URL}) + return db.reference() + except BaseException: + try: + return db.reference() + except BaseException: + LOGS.error(str(format_exc())) + exit() + + +class FireDB: + def __init__(self, Var): + self.db = firebase_auth(Var) + if not self.db: + LOGS.info("Something Went Wrong With FireBase") + exit() + + def getall(self): + return self.db.get() or {} + + @property + def og(self): + return self.db + + def create_data(self, path, data): + return self.db.child(path).set(data) + + def read_data(self, path): + value = self.db.child(path).get() + if isinstance(value, list): + value = list(filter(lambda item: item is not None, value)) + return value + + def update_data(self, path, data): + return self.db.child(path).update(data) + + def delete_data(self, path): + return self.db.child(path).delete() diff --git a/libs/kitsu.py b/libs/kitsu.py index ff6349c..2bde0a1 100644 --- a/libs/kitsu.py +++ b/libs/kitsu.py @@ -31,7 +31,7 @@ async def search(self, query: str): except BaseException: _raw_data = {} if not raw_data: - data = self.alt_anilist(query) + data = {} # self.alt_anilist(query) return data data = {} data["anime_id"] = raw_data.get("id") @@ -50,7 +50,7 @@ async def search(self, query: str): # data["score"] = raw_data.get("attributes", {}).get("averageRating") or "N/A" data["type"] = raw_data.get("attributes", {}).get("showType") or "TV" data["runtime"] = raw_data.get("attributes", {}).get("episodeLength") or 24 - return {**data, **_raw_data} + return {**(data if data else {}), **(_raw_data if _raw_data else {})} async def searcher( self, @@ -64,6 +64,8 @@ async def searcher( links = (await data.json())["data"] for index in range(len(links)): res_data = await self.re_searcher(links[index]["links"]["self"]) + if res_data["data"]["attributes"]["status"] == "tba": + continue if "current" != res_data["data"]["attributes"]["status"]: if ( res_data["data"]["attributes"]["endDate"] diff --git a/libs/logger.py b/libs/logger.py index 63614a0..0ca86ad 100644 --- a/libs/logger.py +++ b/libs/logger.py @@ -18,6 +18,7 @@ import asyncio import logging +from traceback import format_exc from telethon import Button, TelegramClient from telethon.errors.rpcerrorlist import FloodWaitError @@ -41,7 +42,7 @@ """ Auto Anime Bot ©️ t.me/kAiF_00z (github.com/kaif-00z) - v0.0.6 (original) + v0.0.7 (original) (2023-24) [All Rigth Reserved] @@ -109,4 +110,5 @@ async def report_error(self, msg, log=False): except ConnectionError: await self.client.connect() except Exception as err: + LOGS.exception(format_exc()) LOGS.error(str(err)) diff --git a/requirements.txt b/requirements.txt index 126029d..8e769a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ python-decouple -redis +firebase-admin lxml_html_clean aiohttp aiofiles