diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..f44e9d3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,11 @@
+FROM ghcr.io/anonymousx97/build_essentials:main
+
+# adding email and username to the bot
+RUN git config --global user.email "88324835+anonymousx97@users.noreply.github" \
+ git config --global user.name "anonymousx97"
+
+# Exposing Ports for Web Server
+EXPOSE 8080 22 8022
+
+# Adding Remote Container Start Command
+CMD bash -c "$(curl -fsSL https://raw.githubusercontent.com/anonymousx97/Docker/main/start)"
\ No newline at end of file
diff --git a/README.md b/README.md
index fe25169..c501477 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@
```
### Installation:
+ Note: If deploying using Dockerfile add UPSTREAM_REPO var with repo link as it's value.
+
```bash
# Install required packages.
apt install -y python3 git curl python-pip ffmpeg
diff --git a/app/api/instagram.py b/app/api/instagram.py
index f92543e..26e3f13 100755
--- a/app/api/instagram.py
+++ b/app/api/instagram.py
@@ -30,6 +30,8 @@ class Instagram(ScraperConfig):
async def api_dl(self):
+ if not Config.API_KEYS:
+ return
param = {"api_key": await self.get_key(), "url": self.url, "proxy": "residential", "js": False}
response = await aiohttp_tools.get_json(url="https://api.webscraping.ai/html", timeout=30, params=param)
if not response or "data" not in response or not response["data"]["shortcode_media"]:
@@ -62,4 +64,4 @@ class Instagram(ScraperConfig):
count = 0
ret_key = keys[count]
API_KEYS["counter"] = count
- return ret_key
+ return ret_key
\ No newline at end of file
diff --git a/app/api/reddit.py b/app/api/reddit.py
index fe43705..efcee49 100755
--- a/app/api/reddit.py
+++ b/app/api/reddit.py
@@ -45,7 +45,8 @@ class Reddit(ScraperConfig):
else:
self.link = json_.get("preview", {}).get("reddit_video_preview", {}).get("fallback_url", json_.get("url_overridden_by_dest", "")).strip()
-
+ if not self.link:
+ return
if self.link.endswith(".gif"):
self.gif = self.success = True
else:
diff --git a/app/api/ytdl.py b/app/api/ytdl.py
index 01555b5..a9bcd6e 100755
--- a/app/api/ytdl.py
+++ b/app/api/ytdl.py
@@ -1,6 +1,7 @@
import asyncio
import os
import time
+from urllib.parse import urlparse
import yt_dlp
@@ -39,7 +40,11 @@ class YT_DL(ScraperConfig):
}
async def download_or_extract(self):
+ if "/playlist" in self.url:
+ return
if "youtu" in self.url:
+ if not urlparse(self.url).query:
+ return
self._opts["format"] = "bv[ext=mp4][res=480]+ba[ext=m4a]/b[ext=mp4]"
if "shorts" in self.url:
self._opts["format"] = "bv[ext=mp4][res=720]+ba[ext=m4a]/b[ext=mp4]"
diff --git a/app/core/MediaHandler.py b/app/core/MediaHandler.py
index dd2cc77..787eae5 100644
--- a/app/core/MediaHandler.py
+++ b/app/core/MediaHandler.py
@@ -4,9 +4,6 @@ import os
import traceback
from urllib.parse import urlparse
-from pyrogram.errors import MediaEmpty, PhotoSaveFileInvalid, WebpageCurlFailed
-from pyrogram.types import InputMediaPhoto, InputMediaVideo
-
from app.api.gallerydl import Gallery_DL
from app.api.instagram import Instagram
from app.api.reddit import Reddit
@@ -14,9 +11,10 @@ from app.api.threads import Threads
from app.api.tiktok import Tiktok
from app.api.ytdl import YT_DL
from app.core import aiohttp_tools, shell
+from pyrogram.errors import MediaEmpty, PhotoSaveFileInvalid, WebpageCurlFailed
+from pyrogram.types import InputMediaPhoto, InputMediaVideo
# Thanks Jeel Patel for the concept TG[@jeelpatel231]
-
url_map = {
"tiktok.com": Tiktok,
"www.instagram.com": Instagram,
@@ -33,11 +31,15 @@ url_map = {
class ExtractAndSendMedia:
def __init__(self, message):
self.exceptions, self.media_objects = [], []
+ self.__client = message._client
self.message = message
self.doc = "-d" in message.flags
self.spoiler = "-s" in message.flags
self.sender = "" if "-ns" in message.flags else f"\nShared by : {self.extract_sender()}"
- self.args_ = {"chat_id": self.message.chat.id, "reply_to_message_id": message.reply_id}
+ self.args_ = {
+ "chat_id": self.message.chat.id,
+ "reply_to_message_id": message.reply_id,
+ }
def extract_sender(self):
author = self.message.author_signature
@@ -68,41 +70,41 @@ class ExtractAndSendMedia:
elif obj.group:
await self.send_group(obj, caption=caption)
elif obj.photo:
- await self.send_photo(obj.link, caption=caption)
+ await self.send(
+ media={"photo":obj.link},
+ method=self.__client.send_photo,
+ caption=caption,
+ has_spoiler=self.spoiler,
+ )
elif obj.video:
- await self.send_video(obj.link, thumb=obj.thumb, caption=caption)
+ await self.send(
+ media={"video":obj.link},
+ method=self.__client.send_video,
+ thumb=await aiohttp_tools.thumb_dl(obj.thumb),
+ caption=caption,
+ has_spoiler=self.spoiler,
+ )
elif obj.gif:
- await self.send_animation(obj.link, caption=caption)
+ await self.send(
+ media={"animation":obj.link},
+ method=self.__client.send_animation,
+ caption=caption,
+ has_spoiler=self.spoiler,
+ unsave=True,
+ )
except BaseException:
- self.exceptions.append(traceback.format_exc())
+ self.exceptions.append("\n".join([obj.caption_url.strip(), traceback.format_exc()]))
- async def send_photo(self, photo, caption):
+ async def send(self, media, method, **kwargs):
try:
try:
- await self.message._client.send_photo(**self.args_, photo=photo, caption=caption, has_spoiler=self.spoiler)
+ await method(**media, **self.args_, **kwargs)
except (MediaEmpty, WebpageCurlFailed):
- photo = await aiohttp_tools.in_memory_dl(photo)
- await self.message._client.send_photo(**self.args_, photo=photo, caption=caption, has_spoiler=self.spoiler)
+ key, value = list(media.items())[0]
+ media[key] = await aiohttp_tools.in_memory_dl(value)
+ await method(**media, **self.args_, **kwargs)
except PhotoSaveFileInvalid:
- await self.message._client.send_document(**self.args, document=photo, caption=caption, force_document=True)
-
- async def send_video(self, video, caption, thumb):
- try:
- await self.message._client.send_video(
- **self.args_, video=video, caption=caption, thumb=await aiohttp_tools.thumb_dl(thumb), has_spoiler=self.spoiler
- )
- except (MediaEmpty, WebpageCurlFailed):
- video = await aiohttp_tools.in_memory_dl(video)
- await self.message._client.send_video(
- **self.args_, video=video, caption=caption, thumb=await aiohttp_tools.thumb_dl(thumb), has_spoiler=self.spoiler
- )
-
- async def send_animation(self, gif, caption):
- try:
- await self.message._client.send_animation(**self.args_, animation=gif, caption=caption, unsave=True, has_spoiler=self.spoiler)
- except (MediaEmpty, WebpageCurlFailed):
- gif = await aiohttp_tools.in_memory_dl(gif)
- await self.message._client.send_animation(**self.args_, animation=gif, caption=caption, unsave=True, has_spoiler=self.spoiler)
+ await self.__client.send_document(**self.args_, document=media, caption=caption, force_document=True)
async def send_document(self, docs, caption, path=""):
if not path:
@@ -112,10 +114,10 @@ class ExtractAndSendMedia:
docs = glob.glob(f"{path}/*")
for doc in docs:
try:
- await self.message._client.send_document(**self.args_, document=doc, caption=caption, force_document=True)
+ await self.__client.send_document(**self.args_, document=doc, caption=caption, force_document=True)
except (MediaEmpty, WebpageCurlFailed):
doc = await aiohttp_tools.in_memory_dl(doc)
- await self.message._client.send_document(**self.args_, document=doc, caption=caption, force_document=True)
+ await self.__client.send_document(**self.args_, document=doc, caption=caption, force_document=True)
await asyncio.sleep(0.5)
async def send_group(self, media, caption):
@@ -125,9 +127,9 @@ class ExtractAndSendMedia:
sorted = await self.sort_media_urls(media.link, caption)
for data in sorted:
if isinstance(data, list):
- await self.message._client.send_media_group(**self.args_, media=data)
+ await self.__client.send_media_group(**self.args_, media=data)
else:
- await self.send_animation(data, caption=caption)
+ await self.send(media={"animation": data}, method=self.__client.send_animation, caption=caption, has_spoiler=self.spoiler, unsave=True)
await asyncio.sleep(1)
async def sort_media_path(self, path, caption):
diff --git a/app/core/client.py b/app/core/client.py
index 375dc9e..c876d79 100755
--- a/app/core/client.py
+++ b/app/core/client.py
@@ -16,12 +16,15 @@ from app.core.message import Message
class BOT(Client):
- _LOADED = False
def __init__(self):
+ if string := os.environ.get("STRING_SESSION"):
+ mode_arg = { "session_string": string }
+ else:
+ mode_arg = { "bot_token": os.environ.get("BOT_TOKEN") }
super().__init__(
name="bot",
- session_string=os.environ.get("STRING_SESSION"),
+ **mode_arg,
api_id=int(os.environ.get("API_ID")),
api_hash=os.environ.get("API_HASH"),
in_memory=True,
@@ -60,12 +63,11 @@ class BOT(Client):
async def import_modules(self):
for module_ in glob.glob("app/*/*.py"):
importlib.import_module(os.path.splitext(module_.replace("/", "."))[0])
- self._LOADED = True
- async def log(self, text, chat=None, func=None, name="log.txt"):
+ async def log(self, text, chat=None, func=None, name="log.txt",disable_web_page_preview=True):
if chat or func:
text = f"Function: {func}\nChat: {chat}\nTraceback:\n{text}"
- return await self.send_message(chat_id=Config.LOG_CHAT, text=text, name=name)
+ return await self.send_message(chat_id=Config.LOG_CHAT, text=text, name=name, disable_web_page_preview=disable_web_page_preview)
async def restart(self):
await aiohttp_tools.session_switch()
diff --git a/app/core/message.py b/app/core/message.py
index 2e47d08..1b98829 100755
--- a/app/core/message.py
+++ b/app/core/message.py
@@ -24,7 +24,7 @@ class Message(MSG):
self.reply_id = replied.id
def flags_n_input(self):
- self.flags = [i for i in self.text.split() if i.startswith("-")]
+ self.flags = [i for i in self.text.split() if i.startswith("-") ]
split_cmd_str = self.text.split(maxsplit=1)
if len(split_cmd_str) > 1:
self.input = split_cmd_str[-1]
diff --git a/app/core/scraper_config.py b/app/core/scraper_config.py
index a9232f7..5f83ef9 100644
--- a/app/core/scraper_config.py
+++ b/app/core/scraper_config.py
@@ -15,7 +15,7 @@ class ScraperConfig:
self.gif = False
def set_sauce(self, url):
- self.caption_url = f"\n\nSauce"
+ self.caption_url = f"\n\nSauce"
@classmethod
async def start(cls, url):
diff --git a/app/plugins/authorise.py b/app/plugins/authorise.py
index a94d6ac..b5a882e 100644
--- a/app/plugins/authorise.py
+++ b/app/plugins/authorise.py
@@ -94,7 +94,7 @@ async def add_chat(bot, message):
message_id=Config.AUTO_DL_MESSAGE_ID,
):
return await message.reply(err)
- await message.reply(f"{message.chat.title} Added to Authorised List.")
+ await message.reply(f"{message.chat.title or message.chat.first_name} Added to Authorised List.")
@bot.add_cmd(cmd="delchat")
@@ -110,7 +110,7 @@ async def add_chat(bot, message):
message_id=Config.AUTO_DL_MESSAGE_ID,
):
return await message.reply(err)
- await message.reply(f"{message.chat.title} Added Removed from Authorised List.")
+ await message.reply(f"{message.chat.title or message.chat.first_name} Added Removed from Authorised List.")
diff --git a/app/plugins/bot.py b/app/plugins/bot.py
index 20d5f16..a49cf76 100755
--- a/app/plugins/bot.py
+++ b/app/plugins/bot.py
@@ -29,4 +29,4 @@ async def restart(bot, message):
@bot.add_cmd(cmd="update")
async def chat_update(bot, message):
await bot.set_filter_list()
- await message.reply("Filters Refreshed")
+ await message.reply("Filters Refreshed")
\ No newline at end of file
diff --git a/app/plugins/loader.py b/app/plugins/loader.py
new file mode 100644
index 0000000..2cb01a7
--- /dev/null
+++ b/app/plugins/loader.py
@@ -0,0 +1,22 @@
+import importlib
+import sys
+import traceback
+
+from app import bot
+
+@bot.add_cmd(cmd="load")
+async def loader(bot, message):
+ if (
+ not message.replied
+ or not message.replied.document
+ or not message.replied.document.file_name.endswith(".py")
+ ):
+ return await message.reply("reply to a plugin.")
+ file_name = message.replied.document.file_name.rstrip(".py")
+ sys.modules.pop(f"app.temp.{file_name}", None)
+ plugin = await message.replied.download("app/temp/")
+ try:
+ importlib.import_module(f"app.temp.{file_name}")
+ except BaseException:
+ return await message.reply(str(traceback.format_exc()))
+ await message.reply(f"Loaded {file_name}.py.")
diff --git a/app/plugins/song.py b/app/plugins/song.py
new file mode 100644
index 0000000..346a10c
--- /dev/null
+++ b/app/plugins/song.py
@@ -0,0 +1,56 @@
+import asyncio
+import glob
+import shutil
+from time import time
+from urllib.parse import urlparse
+
+import yt_dlp
+from app import bot
+from app.api.ytdl import FakeLogger
+from app.core.aiohttp_tools import in_memory_dl
+
+domains = ["www.youtube.com", "youtube.com", "m.youtube.com", "youtu.be", "www.youtube-nocookie.com", "music.youtube.com"]
+
+
+@bot.add_cmd(cmd="song")
+async def song_dl(bot, message):
+ reply_query = None
+ audio_file = None
+ artist = None
+ if message.replied:
+ for link in message.replied.text.split():
+ if urlparse(link).netloc in domains:
+ reply_query = link
+ break
+ query = reply_query or message.flt_input
+ if not query:
+ return await message.reply("Give a song name or link to download.")
+ response = await message.reply("Searching....")
+ dl_path = f"downloads/{time()}/"
+ query_or_search = query if query.startswith("http") else f"ytsearch:{query}"
+ if "-m" in message.flags:
+ aformat = "mp3"
+ else:
+ aformat = "opus"
+ yt_opts = {
+ "logger": FakeLogger(),
+ "outtmpl": dl_path + "%(title)s.%(ext)s",
+ "format": "bestaudio",
+ "postprocessors": [{"key": "FFmpegExtractAudio", "preferredcodec": aformat}, {"key": "FFmpegMetadata"}, {"key": "EmbedThumbnail"}],
+ }
+ ytdl = yt_dlp.YoutubeDL(yt_opts)
+ yt_info = await asyncio.to_thread(ytdl.extract_info, query_or_search)
+ if not query_or_search.startswith("http"):
+ yt_info = yt_info["entries"][0]
+ duration = yt_info["duration"]
+ artist = yt_info["channel"]
+ thumb = await in_memory_dl(yt_info["thumbnail"])
+ down_path = glob.glob(dl_path + "*")
+ if not down_path:
+ return await response.edit("Not found")
+ await response.edit("Uploading....")
+ for audio_file in down_path:
+ if audio_file.endswith((".opus", ".mp3")):
+ await message.reply_audio(audio=audio_file, duration=int(duration), performer=str(artist), thumb=thumb)
+ await response.delete()
+ shutil.rmtree(dl_path, ignore_errors=True)
\ No newline at end of file
diff --git a/app/plugins/tools.py b/app/plugins/tools.py
index 742ad13..a640083 100644
--- a/app/plugins/tools.py
+++ b/app/plugins/tools.py
@@ -7,7 +7,7 @@ from pyrogram.enums import ParseMode
from app import Config
from app.core import shell
-from app.core.aiohttp_tools import SESSION, in_memory_dl
+from app.core import aiohttp_tools as aio
# Run shell commands
diff --git a/app/social_dl.py b/app/social_dl.py
index 85a322a..449b400 100644
--- a/app/social_dl.py
+++ b/app/social_dl.py
@@ -11,7 +11,7 @@ async def dl(bot, message):
media = await ExtractAndSendMedia.process(message)
if media.exceptions:
exceptions = "\n".join(media.exceptions)
- await bot.log(text=exceptions, func="DL", chat=message.chat.id, name="traceback.txt")
+ await bot.log(text=exceptions, func="DL", chat=message.chat.title or message.chat.first_name, name="traceback.txt")
return await reply.edit(f"Media Download Failed.")
if media.media_objects:
await message.delete()
@@ -26,7 +26,7 @@ async def cmd_dispatcher(bot, message):
try:
await func(bot, parsed_message)
except BaseException:
- await bot.log(text=str(traceback.format_exc()), chat=message.chat.id, func=func.__name__, name="traceback.txt")
+ await bot.log(text=str(traceback.format_exc()), chat=message.chat.title or message.chat.first_name, func=func.__name__, name="traceback.txt")
@bot.on_message(filters.chat_filter)
@@ -36,4 +36,4 @@ async def dl_dispatcher(bot, message):
try:
await func(bot, parsed_message)
except BaseException:
- await bot.log(text=str(traceback.format_exc()), chat=message.chat.id, func=func.__name__, name="traceback.txt")
+ await bot.log(text=str(traceback.format_exc()), chat=message.chat.title or message.chat.first_name, func=func.__name__, name="traceback.txt")
diff --git a/run b/run
new file mode 100644
index 0000000..fec2c3d
--- /dev/null
+++ b/run
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+if [ "$API_PORT" ] ; then
+py_code="
+from aiohttp import web
+app = web.Application()
+app.router.add_get('/', lambda _: web.Response(text='Web Server Running...'))
+web.run_app(app, host='0.0.0.0', port=$API_PORT, reuse_port=True, print=None)
+"
+ python3 -q -c "$py_code" & echo "Dummy Web Server Started..."
+
+fi
+
+python3 -m app
\ No newline at end of file