├── .netrc
├── bot
├── modules
│ ├── __init__.py
│ ├── delete.py
│ ├── list.py
│ ├── mirror_status.py
│ ├── count.py
│ ├── shell.py
│ ├── mediainfo.py
│ ├── speedtest.py
│ ├── watch.py
│ ├── updates.py
│ ├── usage.py
│ ├── clone.py
│ ├── cancel_mirror.py
│ ├── eval.py
│ ├── authorize.py
│ ├── config.py
│ └── search.py
├── helper
│ ├── ext_utils
│ │ ├── __init__.py
│ │ ├── exceptions.py
│ │ ├── db_handler.py
│ │ ├── fs_utils.py
│ │ └── bot_utils.py
│ ├── mirror_utils
│ │ ├── __init__.py
│ │ ├── status_utils
│ │ │ ├── __init__.py
│ │ │ ├── listeners.py
│ │ │ ├── tar_status.py
│ │ │ ├── extract_status.py
│ │ │ ├── status.py
│ │ │ ├── upload_status.py
│ │ │ ├── telegram_download_status.py
│ │ │ ├── youtube_dl_download_status.py
│ │ │ ├── gdownload_status.py
│ │ │ ├── mega_download_status.py
│ │ │ └── aria_download_status.py
│ │ ├── upload_utils
│ │ │ └── __init__.py
│ │ └── download_utils
│ │ │ ├── __init__.py
│ │ │ ├── download_helper.py
│ │ │ ├── telegram_downloader.py
│ │ │ ├── direct_link_generator_license.md
│ │ │ ├── aria2_download.py
│ │ │ ├── youtube_dl_download_helper.py
│ │ │ ├── mega_downloader.py
│ │ │ └── direct_link_generator.py
│ ├── telegram_helper
│ │ ├── __init__.py
│ │ ├── button_build.py
│ │ ├── bot_commands.py
│ │ ├── filters.py
│ │ └── message_utils.py
│ ├── custom_filters.py
│ └── __init__.py
├── __init__.py
└── __main__.py
├── captain-definition
├── heroku.yml
├── .gitmodules
├── requirements-cli.txt
├── .gitignore
├── aria.bat
├── vendor
└── cmrudl.py
│ ├── .gitignore
│ ├── .editorconfig
│ └── Readme.md
├── start.sh
├── requirements.txt
├── Dockerfile
├── generate_drive_token.py
├── aria.sh
├── config_sample.env
├── add_to_team_drive.py
├── extract
├── pextract
├── app.json
└── gen_sa_accounts.py
/.netrc:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/modules/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/upload_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/captain-definition:
--------------------------------------------------------------------------------
1 | {
2 | "schemaVersion": 2,
3 | "dockerfilePath": "./Dockerfile"
4 | }
5 |
--------------------------------------------------------------------------------
/heroku.yml:
--------------------------------------------------------------------------------
1 | build:
2 | docker:
3 | worker: Dockerfile
4 | run:
5 | worker: bash start.sh
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/cmrudl.py"]
2 | path = vendor/cmrudl.py
3 | url = https://github.com/JrMasterModelBuilder/cmrudl.py.git
4 |
--------------------------------------------------------------------------------
/requirements-cli.txt:
--------------------------------------------------------------------------------
1 | oauth2client
2 | google-api-python-client
3 | progress
4 | progressbar2
5 | httplib2shim
6 | google_auth_oauthlib
7 | pyrogram
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | config.env
2 | *auth_token.txt
3 | *.pyc
4 | downloads/*
5 | download/*
6 | data*
7 | .vscode
8 | .idea
9 | *.json
10 | *.pickle
11 | authorized_chats.txt
12 | log.txt
13 | accounts/*
14 |
--------------------------------------------------------------------------------
/aria.bat:
--------------------------------------------------------------------------------
1 | aria2c --enable-rpc --rpc-listen-all=false --rpc-listen-port 6800 --max-connection-per-server=10 --rpc-max-request-size=1024M --seed-time=0.01 --min-split-size=10M --follow-torrent=mem --split=10 --daemon=true --allow-overwrite=true
2 |
--------------------------------------------------------------------------------
/vendor/cmrudl.py/.gitignore:
--------------------------------------------------------------------------------
1 | # OS files
2 | .DS_Store
3 | .DS_Store?
4 | ._*
5 | .Spotlight-V100
6 | .Trashes
7 | Icon?
8 | ehthumbs.db
9 | Thumbs.db
10 |
11 | # Python files
12 | __pycache__/
13 | *.py[cod]
14 | *$py.class
15 |
--------------------------------------------------------------------------------
/vendor/cmrudl.py/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | indent_style = tab
7 | indent_size = 4
8 | charset = utf8
9 |
10 | [*.{yml,yaml}]
11 | indent_style = space
12 | indent_size = 2
13 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/exceptions.py:
--------------------------------------------------------------------------------
1 | class DirectDownloadLinkException(Exception):
2 | """Not method found for extracting direct download link from the http link"""
3 | pass
4 |
5 |
6 | class NotSupportedExtractionArchive(Exception):
7 | """The archive format use is trying to extract is not supported"""
8 | pass
9 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | if [[ -n $TOKEN_PICKLE_URL ]]; then
2 | wget -q $TOKEN_PICKLE_URL -O /usr/src/app/token.pickle
3 | fi
4 |
5 | if [[ -n $ACCOUNTS_ZIP_URL ]]; then
6 | wget -q $ACCOUNTS_ZIP_URL -O /usr/src/app/accounts.zip
7 | unzip accounts.zip -d /usr/src/app/accounts
8 | rm accounts.zip
9 | fi
10 |
11 | ./aria.sh; python3 -m bot
12 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp
2 | aria2p
3 | appdirs
4 | beautifulsoup4
5 | feedparser
6 | gitpython
7 | google-api-python-client
8 | google-auth-httplib2
9 | google-auth-oauthlib
10 | heroku3
11 | html-telegraph-poster
12 | js2py
13 | lk21
14 | lxml
15 | messages
16 | psutil
17 | psycopg2-binary
18 | pybase64
19 | pyrogram
20 | python-dotenv
21 | python-magic
22 | python-telegram-bot
23 | pytz
24 | requests
25 | speedtest-cli
26 | telegraph
27 | tenacity
28 | TgCrypto
29 | youtube_dl
30 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM breakdowns/mega-sdk-python:latest
2 |
3 | WORKDIR /usr/src/app
4 | RUN chmod 777 /usr/src/app
5 |
6 | COPY requirements.txt .
7 | RUN pip3 install --no-cache-dir -r requirements.txt
8 |
9 | COPY extract /usr/local/bin
10 | COPY pextract /usr/local/bin
11 | RUN chmod +x /usr/local/bin/extract && chmod +x /usr/local/bin/pextract
12 | COPY . .
13 | COPY .netrc /root/.netrc
14 | RUN chmod 600 /usr/src/app/.netrc
15 | RUN chmod +x aria.sh
16 |
17 | CMD ["bash","start.sh"]
18 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/button_build.py:
--------------------------------------------------------------------------------
1 | from telegram import InlineKeyboardButton
2 |
3 | class ButtonMaker:
4 | def __init__(self):
5 | self.button = []
6 |
7 | def buildbutton(self, key, link):
8 | self.button.append(InlineKeyboardButton(text = key, url = link))
9 |
10 | def build_menu(self, n_cols, footer_buttons=None, header_buttons=None):
11 | menu = [self.button[i:i + n_cols] for i in range(0, len(self.button), n_cols)]
12 | if header_buttons:
13 | menu.insert(0, header_buttons)
14 | if footer_buttons:
15 | menu.append(footer_buttons)
16 | return menu
17 |
--------------------------------------------------------------------------------
/bot/helper/custom_filters.py:
--------------------------------------------------------------------------------
1 | from pyrogram import filters
2 |
3 | def callback_data(data):
4 | def func(flt, client, callback_query):
5 | return callback_query.data in flt.data
6 |
7 | data = data if isinstance(data, list) else [data]
8 | return filters.create(
9 | func,
10 | 'CustomCallbackDataFilter',
11 | data=data
12 | )
13 |
14 | def callback_chat(chats):
15 | def func(flt, client, callback_query):
16 | return callback_query.message.chat.id in flt.chats
17 |
18 | chats = chats if isinstance(chats, list) else [chats]
19 | return filters.create(
20 | func,
21 | 'CustomCallbackChatsFilter',
22 | chats=chats
23 | )
24 |
--------------------------------------------------------------------------------
/generate_drive_token.py:
--------------------------------------------------------------------------------
1 | import pickle
2 | import os
3 | from google_auth_oauthlib.flow import InstalledAppFlow
4 | from google.auth.transport.requests import Request
5 |
6 | credentials = None
7 | __G_DRIVE_TOKEN_FILE = "token.pickle"
8 | __OAUTH_SCOPE = ["https://www.googleapis.com/auth/drive"]
9 | if os.path.exists(__G_DRIVE_TOKEN_FILE):
10 | with open(__G_DRIVE_TOKEN_FILE, 'rb') as f:
11 | credentials = pickle.load(f)
12 | if credentials is None or not credentials.valid:
13 | if credentials and credentials.expired and credentials.refresh_token:
14 | credentials.refresh(Request())
15 | else:
16 | flow = InstalledAppFlow.from_client_secrets_file(
17 | 'credentials.json', __OAUTH_SCOPE)
18 | credentials = flow.run_console(port=0)
19 |
20 | # Save the credentials for the next run
21 | with open(__G_DRIVE_TOKEN_FILE, 'wb') as token:
22 | pickle.dump(credentials, token)
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/listeners.py:
--------------------------------------------------------------------------------
1 | class MirrorListeners:
2 | def __init__(self, context, update):
3 | self.bot = context
4 | self.update = update
5 | self.message = update.message
6 | self.uid = self.message.message_id
7 |
8 | def onDownloadStarted(self):
9 | raise NotImplementedError
10 |
11 | def onDownloadProgress(self):
12 | raise NotImplementedError
13 |
14 | def onDownloadComplete(self):
15 | raise NotImplementedError
16 |
17 | def onDownloadError(self, error: str):
18 | raise NotImplementedError
19 |
20 | def onUploadStarted(self):
21 | raise NotImplementedError
22 |
23 | def onUploadProgress(self):
24 | raise NotImplementedError
25 |
26 | def onUploadComplete(self, link: str):
27 | raise NotImplementedError
28 |
29 | def onUploadError(self, error: str):
30 | raise NotImplementedError
31 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/tar_status.py:
--------------------------------------------------------------------------------
1 | from .status import Status
2 | from bot.helper.ext_utils.bot_utils import get_readable_file_size, MirrorStatus
3 |
4 |
5 | class TarStatus(Status):
6 | def __init__(self, name, path, size):
7 | self.__name = name
8 | self.__path = path
9 | self.__size = size
10 |
11 | # The progress of Tar function cannot be tracked. So we just return dummy values.
12 | # If this is possible in future,we should implement it
13 |
14 | def progress(self):
15 | return '0'
16 |
17 | def speed(self):
18 | return '0'
19 |
20 | def name(self):
21 | return self.__name
22 |
23 | def path(self):
24 | return self.__path
25 |
26 | def size(self):
27 | return get_readable_file_size(self.__size)
28 |
29 | def eta(self):
30 | return '0s'
31 |
32 | def status(self):
33 | return MirrorStatus.STATUS_ARCHIVING
34 |
35 | def processed_bytes(self):
36 | return 0
37 |
--------------------------------------------------------------------------------
/vendor/cmrudl.py/Readme.md:
--------------------------------------------------------------------------------
1 | # cmrudl.py
2 | cmrudl.py - Cloud Mail.RU download script
3 | # Usage
4 | ```
5 | usage: cmrudl.py [-h] [-v] [-V] [-D] [-B BUFFER] [-t TIMEOUT] [-M] [-d DIR]
6 | url [file]
7 | cmrudl.py 1.0.2
8 | cmrudl.py 1.0.3
9 | Copyright (c) 2019 JrMasterModelBuilder MPL-2.0
10 | positional arguments:
11 | url URL
12 | file FILE
13 | optional arguments:
14 | -h, --help show this help message and exit
15 | -v, --version Print version
16 | -V, --verbose Verbose mode
17 | -D, --debug Debug output
18 | -B BUFFER, --buffer BUFFER
19 | Buffer size
20 | -t TIMEOUT, --timeout TIMEOUT
21 | Request timeout in seconds
22 | -M, --mtime Use server modified time
23 | -d DIR, --dir DIR Output directory
24 | ```
25 | # Bugs
26 | If you find a bug or have compatibility issues, please open a ticket under issues section for this repository.
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/extract_status.py:
--------------------------------------------------------------------------------
1 | from .status import Status
2 | from bot.helper.ext_utils.bot_utils import get_readable_file_size, MirrorStatus
3 |
4 |
5 | class ExtractStatus(Status):
6 | def __init__(self, name, path, size):
7 | self.__name = name
8 | self.__path = path
9 | self.__size = size
10 |
11 | # The progress of extract function cannot be tracked. So we just return dummy values.
12 | # If this is possible in future,we should implement it
13 |
14 | def progress(self):
15 | return '0'
16 |
17 | def speed(self):
18 | return '0'
19 |
20 | def name(self):
21 | return self.__name
22 |
23 | def path(self):
24 | return self.__path
25 |
26 | def size(self):
27 | return get_readable_file_size(self.__size)
28 |
29 | def eta(self):
30 | return '0s'
31 |
32 | def status(self):
33 | return MirrorStatus.STATUS_EXTRACTING
34 |
35 | def processed_bytes(self):
36 | return 0
37 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/download_helper.py:
--------------------------------------------------------------------------------
1 | # An abstract class which will be inherited by the tool specific classes like aria2_helper or mega_download_helper
2 | import threading
3 |
4 |
5 | class MethodNotImplementedError(NotImplementedError):
6 | def __init__(self):
7 | super(self, 'Not implemented method')
8 |
9 |
10 | class DownloadHelper:
11 | def __init__(self):
12 | self.name = '' # Name of the download; empty string if no download has been started
13 | self.size = 0.0 # Size of the download
14 | self.downloaded_bytes = 0.0 # Bytes downloaded
15 | self.speed = 0.0 # Download speed in bytes per second
16 | self.progress = 0.0
17 | self.progress_string = '0.00%'
18 | self.eta = 0 # Estimated time of download complete
19 | self.eta_string = '0s' # A listener class which have event callbacks
20 | self._resource_lock = threading.Lock()
21 |
22 | def add_download(self, link: str, path):
23 | raise MethodNotImplementedError
24 |
25 | def cancel_download(self):
26 | # Returns None if successfully cancelled, else error string
27 | raise MethodNotImplementedError
28 |
--------------------------------------------------------------------------------
/bot/modules/delete.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | import threading
3 | from telegram import Update
4 | from bot import dispatcher, LOGGER
5 | from bot.helper.telegram_helper.message_utils import auto_delete_message, sendMessage
6 | from bot.helper.telegram_helper.filters import CustomFilters
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 | from bot.helper.mirror_utils.upload_utils import gdriveTools
9 |
10 |
11 | def deletefile(update, context):
12 | msg_args = update.message.text.split(None, 1)
13 | msg = ''
14 | try:
15 | link = msg_args[1]
16 | LOGGER.info(msg_args[1])
17 | except IndexError:
18 | msg = 'Send a link along with command'
19 |
20 | if msg == '' :
21 | drive = gdriveTools.GoogleDriveHelper()
22 | msg = drive.deletefile(link)
23 | LOGGER.info(f"DeleteFileCmd : {msg}")
24 | reply_message = sendMessage(msg, context.bot, update)
25 |
26 | threading.Thread(target=auto_delete_message, args=(context.bot, update.message, reply_message)).start()
27 |
28 | delete_handler = CommandHandler(command=BotCommands.DeleteCommand, callback=deletefile, filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
29 | dispatcher.add_handler(delete_handler)
30 |
--------------------------------------------------------------------------------
/bot/modules/list.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
3 | from bot import LOGGER, dispatcher
4 | from bot.helper.telegram_helper.message_utils import sendMessage, sendMarkup, editMessage
5 | from bot.helper.telegram_helper.filters import CustomFilters
6 | import threading
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 |
9 |
10 | def list_drive(update, context):
11 | try:
12 | search = update.message.text.split(' ',maxsplit=1)[1]
13 | LOGGER.info(f"Searching: {search}")
14 | reply = sendMessage('Searching..... Please wait!', context.bot, update)
15 | gdrive = GoogleDriveHelper(None)
16 | msg, button = gdrive.drive_list(search)
17 |
18 | if button:
19 | editMessage(msg, reply, button)
20 | else:
21 | editMessage('No result found', reply, button)
22 |
23 | except IndexError:
24 | sendMessage('Send a search key along with command', context.bot, update)
25 |
26 |
27 | list_handler = CommandHandler(BotCommands.ListCommand, list_drive, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
28 | dispatcher.add_handler(list_handler)
29 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/status.py:
--------------------------------------------------------------------------------
1 | # Generic status class. All other status classes must inherit this class
2 |
3 |
4 | class Status:
5 |
6 | def progress(self):
7 | """
8 | Calculates the progress of the mirror (upload or download)
9 | :return: progress in percentage
10 | """
11 | raise NotImplementedError
12 |
13 | def speed(self):
14 | """:return: speed in bytes per second"""
15 | raise NotImplementedError
16 |
17 | def name(self):
18 | """:return name of file/directory being processed"""
19 | raise NotImplementedError
20 |
21 | def path(self):
22 | """:return path of the file/directory"""
23 | raise NotImplementedError
24 |
25 | def size(self):
26 | """:return Size of file folder"""
27 | raise NotImplementedError
28 |
29 | def eta(self):
30 | """:return ETA of the process to complete"""
31 | raise NotImplementedError
32 |
33 | def status(self):
34 | """:return String describing what is the object of this class will be tracking (upload/download/something
35 | else) """
36 | raise NotImplementedError
37 |
38 | def processed_bytes(self):
39 | """:return The size of file that has been processed (downloaded/uploaded/archived)"""
40 | raise NotImplementedError
41 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/bot_commands.py:
--------------------------------------------------------------------------------
1 | class _BotCommands:
2 | def __init__(self):
3 | self.StartCommand = 'start'
4 | self.MirrorCommand = 'mirror'
5 | self.UnzipMirrorCommand = 'unzipmirror'
6 | self.TarMirrorCommand = 'tarmirror'
7 | self.CancelMirror = 'cancel'
8 | self.CancelAllCommand = 'cancelall'
9 | self.ListCommand = 'list'
10 | self.StatusCommand = 'status'
11 | self.AuthorizedUsersCommand = 'users'
12 | self.AuthorizeCommand = 'authorize'
13 | self.UnAuthorizeCommand = 'unauthorize'
14 | self.AddSudoCommand = 'addsudo'
15 | self.RmSudoCommand = 'rmsudo'
16 | self.PingCommand = 'ping'
17 | self.RestartCommand = 'restart'
18 | self.StatsCommand = 'stats'
19 | self.HelpCommand = 'help'
20 | self.LogCommand = 'log'
21 | self.SpeedCommand = 'speedtest'
22 | self.CloneCommand = 'clone'
23 | self.CountCommand = 'count'
24 | self.WatchCommand = 'watch'
25 | self.TarWatchCommand = 'tarwatch'
26 | self.DeleteCommand = 'del'
27 | self.UsageCommand = 'usage'
28 | self.MediaInfoCommand = 'mediainfo'
29 | self.ConfigMenuCommand = 'config'
30 | self.ShellCommand = 'shell'
31 | self.UpdateCommand = 'update'
32 |
33 | BotCommands = _BotCommands()
34 |
--------------------------------------------------------------------------------
/bot/modules/mirror_status.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from bot import dispatcher, status_reply_dict, DOWNLOAD_STATUS_UPDATE_INTERVAL, status_reply_dict_lock
3 | from bot.helper.telegram_helper.message_utils import *
4 | from time import sleep
5 | from bot.helper.ext_utils.bot_utils import get_readable_message
6 | from telegram.error import BadRequest
7 | from bot.helper.telegram_helper.filters import CustomFilters
8 | from bot.helper.telegram_helper.bot_commands import BotCommands
9 | import threading
10 |
11 |
12 | def mirror_status(update, context):
13 | message = get_readable_message()
14 | if len(message) == 0:
15 | message = "No active downloads"
16 | reply_message = sendMessage(message, context.bot, update)
17 | threading.Thread(target=auto_delete_message, args=(bot, update.message, reply_message)).start()
18 | return
19 | index = update.effective_chat.id
20 | with status_reply_dict_lock:
21 | if index in status_reply_dict.keys():
22 | deleteMessage(bot, status_reply_dict[index])
23 | del status_reply_dict[index]
24 | sendStatusMessage(update, context.bot)
25 | deleteMessage(context.bot, update.message)
26 |
27 |
28 | mirror_status_handler = CommandHandler(BotCommands.StatusCommand, mirror_status,
29 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
30 | dispatcher.add_handler(mirror_status_handler)
31 |
--------------------------------------------------------------------------------
/aria.sh:
--------------------------------------------------------------------------------
1 | export MAX_DOWNLOAD_SPEED=0
2 | tracker_list=$(curl -Ns https://raw.githubusercontent.com/XIU2/TrackersListCollection/master/all.txt https://ngosang.github.io/trackerslist/trackers_all_http.txt https://newtrackon.com/api/all https://raw.githubusercontent.com/DeSireFire/animeTrackerList/master/AT_all.txt https://raw.githubusercontent.com/hezhijie0327/Trackerslist/main/trackerslist_tracker.txt https://raw.githubusercontent.com/hezhijie0327/Trackerslist/main/trackerslist_exclude.txt | awk '$0' | tr '\n\n' ',')
3 | export MAX_CONCURRENT_DOWNLOADS=7
4 |
5 | aria2c --enable-rpc --rpc-listen-all=false --rpc-listen-port 6800 --check-certificate=false \
6 | --max-connection-per-server=10 --rpc-max-request-size=1024M \
7 | --bt-tracker="[$tracker_list]" --bt-max-peers=0 --bt-tracker-connect-timeout=300 --bt-stop-timeout=1200 --min-split-size=10M \
8 | --follow-torrent=mem --split=10 \
9 | --daemon=true --allow-overwrite=true --max-overall-download-limit=$MAX_DOWNLOAD_SPEED \
10 | --max-overall-upload-limit=1K --max-concurrent-downloads=$MAX_CONCURRENT_DOWNLOADS \
11 | --peer-id-prefix=-qB4350- --user-agent=qBittorrent/4.3.5 --peer-agent=qBittorrent/4.3.5 \
12 | --disk-cache=64M --file-allocation=prealloc --continue=true \
13 | --max-file-not-found=5 --max-tries=20 --auto-file-renaming=true \
14 | --bt-enable-lpd=true --seed-time=0.01 --seed-ratio=1.0 \
15 | --content-disposition-default-utf8=true --http-accept-gzip=true --reuse-uri=true --netrc-path=/usr/src/app/.netrc
16 |
--------------------------------------------------------------------------------
/bot/modules/count.py:
--------------------------------------------------------------------------------
1 | # Implement By https://github.com/anasty17
2 | # © https://github.com/breakdowns/slam-mirrorbot
3 |
4 | from telegram.ext import CommandHandler
5 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
6 | from bot.helper.telegram_helper.message_utils import deleteMessage, sendMessage
7 | from bot.helper.telegram_helper.filters import CustomFilters
8 | from bot.helper.telegram_helper.bot_commands import BotCommands
9 | from bot import dispatcher
10 |
11 |
12 | def countNode(update, context):
13 | args = update.message.text.split(" ", maxsplit=1)
14 | if len(args) > 1:
15 | link = args[1]
16 | msg = sendMessage(f"Counting: {link}", context.bot, update)
17 | gd = GoogleDriveHelper()
18 | result = gd.count(link)
19 | deleteMessage(context.bot, msg)
20 | if update.message.from_user.username:
21 | uname = f'@{update.message.from_user.username}'
22 | else:
23 | uname = f'{update.message.from_user.first_name}'
24 | if uname is not None:
25 | cc = f'\n\ncc: {uname}'
26 | sendMessage(result + cc, context.bot, update)
27 | else:
28 | sendMessage("Provide G-Drive Shareable Link to Count.", context.bot, update)
29 |
30 | count_handler = CommandHandler(BotCommands.CountCommand, countNode, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
31 | dispatcher.add_handler(count_handler)
32 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/upload_status.py:
--------------------------------------------------------------------------------
1 | from .status import Status
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3 | from bot import DOWNLOAD_DIR
4 |
5 |
6 | class UploadStatus(Status):
7 | def __init__(self, obj, size, listener):
8 | self.obj = obj
9 | self.__size = size
10 | self.uid = listener.uid
11 | self.message = listener.message
12 |
13 | def path(self):
14 | return f"{DOWNLOAD_DIR}{self.uid}"
15 |
16 | def processed_bytes(self):
17 | return self.obj.uploaded_bytes
18 |
19 | def size_raw(self):
20 | return self.__size
21 |
22 | def size(self):
23 | return get_readable_file_size(self.__size)
24 |
25 | def status(self):
26 | return MirrorStatus.STATUS_UPLOADING
27 |
28 | def name(self):
29 | return self.obj.name
30 |
31 | def progress_raw(self):
32 | try:
33 | return self.obj.uploaded_bytes / self.__size * 100
34 | except ZeroDivisionError:
35 | return 0
36 |
37 | def progress(self):
38 | return f'{round(self.progress_raw(), 2)}%'
39 |
40 | def speed_raw(self):
41 | """
42 | :return: Upload speed in Bytes/Seconds
43 | """
44 | return self.obj.speed()
45 |
46 | def speed(self):
47 | return f'{get_readable_file_size(self.speed_raw())}/s'
48 |
49 | def eta(self):
50 | try:
51 | seconds = (self.__size - self.obj.uploaded_bytes) / self.speed_raw()
52 | return f'{get_readable_time(seconds)}'
53 | except ZeroDivisionError:
54 | return '-'
55 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/telegram_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3 | from .status import Status
4 |
5 |
6 | class TelegramDownloadStatus(Status):
7 | def __init__(self, obj, listener):
8 | self.obj = obj
9 | self.uid = listener.uid
10 | self.message = listener.message
11 |
12 | def gid(self):
13 | return self.obj.gid
14 |
15 | def path(self):
16 | return f"{DOWNLOAD_DIR}{self.uid}"
17 |
18 | def processed_bytes(self):
19 | return self.obj.downloaded_bytes
20 |
21 | def size_raw(self):
22 | return self.obj.size
23 |
24 | def size(self):
25 | return get_readable_file_size(self.size_raw())
26 |
27 | def status(self):
28 | return MirrorStatus.STATUS_DOWNLOADING
29 |
30 | def name(self):
31 | return self.obj.name
32 |
33 | def progress_raw(self):
34 | return self.obj.progress
35 |
36 | def progress(self):
37 | return f'{round(self.progress_raw(), 2)}%'
38 |
39 | def speed_raw(self):
40 | """
41 | :return: Download speed in Bytes/Seconds
42 | """
43 | return self.obj.download_speed
44 |
45 | def speed(self):
46 | return f'{get_readable_file_size(self.speed_raw())}/s'
47 |
48 | def eta(self):
49 | try:
50 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
51 | return f'{get_readable_time(seconds)}'
52 | except ZeroDivisionError:
53 | return '-'
54 |
55 | def download(self):
56 | return self.obj
57 |
--------------------------------------------------------------------------------
/bot/modules/shell.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | from bot import LOGGER, dispatcher
3 | from telegram import ParseMode
4 | from telegram.ext import CommandHandler
5 | from bot.helper.telegram_helper.filters import CustomFilters
6 | from bot.helper.telegram_helper.bot_commands import BotCommands
7 |
8 |
9 | def shell(update, context):
10 | message = update.effective_message
11 | cmd = message.text.split(' ', 1)
12 | if len(cmd) == 1:
13 | message.reply_text('No command to execute was given.')
14 | return
15 | cmd = cmd[1]
16 | process = subprocess.Popen(
17 | cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
18 | stdout, stderr = process.communicate()
19 | reply = ''
20 | stderr = stderr.decode()
21 | stdout = stdout.decode()
22 | if stdout:
23 | reply += f"*Stdout*\n`{stdout}`\n"
24 | LOGGER.info(f"Shell - {cmd} - {stdout}")
25 | if stderr:
26 | reply += f"*Stderr*\n`{stderr}`\n"
27 | LOGGER.error(f"Shell - {cmd} - {stderr}")
28 | if len(reply) > 3000:
29 | with open('shell_output.txt', 'w') as file:
30 | file.write(reply)
31 | with open('shell_output.txt', 'rb') as doc:
32 | context.bot.send_document(
33 | document=doc,
34 | filename=doc.name,
35 | reply_to_message_id=message.message_id,
36 | chat_id=message.chat_id)
37 | else:
38 | message.reply_text(reply, parse_mode=ParseMode.MARKDOWN)
39 |
40 |
41 | SHELL_HANDLER = CommandHandler(BotCommands.ShellCommand, shell,
42 | filters=CustomFilters.owner_filter, run_async=True)
43 | dispatcher.add_handler(SHELL_HANDLER)
44 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/youtube_dl_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import DOWNLOAD_DIR
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3 | from .status import Status
4 | from bot.helper.ext_utils.fs_utils import get_path_size
5 |
6 | class YoutubeDLDownloadStatus(Status):
7 | def __init__(self, obj, listener):
8 | self.obj = obj
9 | self.uid = listener.uid
10 | self.message = listener.message
11 |
12 | def gid(self):
13 | return self.obj.gid
14 |
15 | def path(self):
16 | return f"{DOWNLOAD_DIR}{self.uid}"
17 |
18 | def processed_bytes(self):
19 | if self.obj.downloaded_bytes != 0:
20 | return self.obj.downloaded_bytes
21 | else:
22 | return get_path_size(f"{DOWNLOAD_DIR}{self.uid}")
23 |
24 | def size_raw(self):
25 | return self.obj.size
26 |
27 | def size(self):
28 | return get_readable_file_size(self.size_raw())
29 |
30 | def status(self):
31 | return MirrorStatus.STATUS_DOWNLOADING
32 |
33 | def name(self):
34 | return self.obj.name
35 |
36 | def progress_raw(self):
37 | return self.obj.progress
38 |
39 | def progress(self):
40 | return f'{round(self.progress_raw(), 2)}%'
41 |
42 | def speed_raw(self):
43 | """
44 | :return: Download speed in Bytes/Seconds
45 | """
46 | return self.obj.download_speed
47 |
48 | def speed(self):
49 | return f'{get_readable_file_size(self.speed_raw())}/s'
50 |
51 | def eta(self):
52 | try:
53 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
54 | return f'{get_readable_time(seconds)}'
55 | except:
56 | return '-'
57 |
58 | def download(self):
59 | return self.obj
60 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/gdownload_status.py:
--------------------------------------------------------------------------------
1 | from .status import Status
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus, get_readable_file_size, get_readable_time
3 | from bot import DOWNLOAD_DIR
4 |
5 |
6 | class DownloadStatus(Status):
7 | def __init__(self, obj, size, listener, gid):
8 | self.dobj = obj
9 | self.__dsize = size
10 | self.uid = listener.uid
11 | self.message = listener.message
12 | self.__dgid = gid
13 |
14 | def path(self):
15 | return f"{DOWNLOAD_DIR}{self.uid}"
16 |
17 | def processed_bytes(self):
18 | return self.dobj.downloaded_bytes
19 |
20 | def size_raw(self):
21 | return self.__dsize
22 |
23 | def size(self):
24 | return get_readable_file_size(self.__dsize)
25 |
26 | def status(self):
27 | return MirrorStatus.STATUS_DOWNLOADING
28 |
29 | def name(self):
30 | return self.dobj.name
31 |
32 | def gid(self) -> str:
33 | return self.__dgid
34 |
35 | def progress_raw(self):
36 | try:
37 | return self.dobj.downloaded_bytes / self.__dsize * 100
38 | except ZeroDivisionError:
39 | return 0
40 |
41 | def progress(self):
42 | return f'{round(self.progress_raw(), 2)}%'
43 |
44 | def speed_raw(self):
45 | """
46 | :return: Download speed in Bytes/Seconds
47 | """
48 | return self.dobj.dspeed()
49 |
50 | def speed(self):
51 | return f'{get_readable_file_size(self.speed_raw())}/s'
52 |
53 | def eta(self):
54 | try:
55 | seconds = (self.__dsize - self.dobj.downloaded_bytes) / self.speed_raw()
56 | return f'{get_readable_time(seconds)}'
57 | except ZeroDivisionError:
58 | return '-'
59 |
60 | def download(self):
61 | return self.dobj
62 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/mega_download_status.py:
--------------------------------------------------------------------------------
1 | from bot.helper.ext_utils.bot_utils import get_readable_file_size,MirrorStatus, get_readable_time
2 | from bot import DOWNLOAD_DIR
3 | from .status import Status
4 |
5 |
6 | class MegaDownloadStatus(Status):
7 |
8 | def __init__(self, obj, listener):
9 | self.uid = obj.uid
10 | self.listener = listener
11 | self.obj = obj
12 | self.message = listener.message
13 |
14 | def name(self) -> str:
15 | return self.obj.name
16 |
17 | def progress_raw(self):
18 | try:
19 | return round(self.processed_bytes() / self.obj.size * 100,2)
20 | except ZeroDivisionError:
21 | return 0.0
22 |
23 | def progress(self):
24 | """Progress of download in percentage"""
25 | return f"{self.progress_raw()}%"
26 |
27 | def status(self) -> str:
28 | return MirrorStatus.STATUS_DOWNLOADING
29 |
30 | def processed_bytes(self):
31 | return self.obj.downloaded_bytes
32 |
33 | def eta(self):
34 | try:
35 | seconds = (self.size_raw() - self.processed_bytes()) / self.speed_raw()
36 | return f'{get_readable_time(seconds)}'
37 | except ZeroDivisionError:
38 | return '-'
39 |
40 | def size_raw(self):
41 | return self.obj.size
42 |
43 | def size(self) -> str:
44 | return get_readable_file_size(self.size_raw())
45 |
46 | def downloaded(self) -> str:
47 | return get_readable_file_size(self.obj.downloadedBytes)
48 |
49 | def speed_raw(self):
50 | return self.obj.speed
51 |
52 | def speed(self) -> str:
53 | return f'{get_readable_file_size(self.speed_raw())}/s'
54 |
55 | def gid(self) -> str:
56 | return self.obj.gid
57 |
58 | def path(self) -> str:
59 | return f"{DOWNLOAD_DIR}{self.uid}"
60 |
61 | def download(self):
62 | return self.obj
63 |
--------------------------------------------------------------------------------
/bot/modules/mediainfo.py:
--------------------------------------------------------------------------------
1 | # Suggested by - @d0n0t (https://github.com/code-rgb/USERGE-X/issues/9)
2 | # Copyright (C) 2020 BY - GitHub.com/code-rgb [TG - @deleteduser420]
3 | # All rights reserved.
4 |
5 | import os
6 | from pyrogram import filters
7 | from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup
8 | from bot import app
9 | from bot.helper.telegram_helper.bot_commands import BotCommands
10 | from bot.helper import post_to_telegraph, runcmd, safe_filename
11 |
12 | @app.on_message(filters.command(BotCommands.MediaInfoCommand))
13 | async def mediainfo(client, message):
14 | reply = message.reply_to_message
15 | if not reply:
16 | await message.reply_text("Reply to Media first")
17 | return
18 | process = await message.reply_text("`Processing...`")
19 | x_media = None
20 | available_media = (
21 | "audio",
22 | "document",
23 | "photo",
24 | "sticker",
25 | "animation",
26 | "video",
27 | "voice",
28 | "video_note",
29 | "new_chat_photo",
30 | )
31 | for kind in available_media:
32 | x_media = getattr(reply, kind, None)
33 | if x_media is not None:
34 | break
35 | if x_media is None:
36 | await process.edit_text("Reply To a Valid Media Format")
37 | return
38 | media_type = str(type(x_media)).split("'")[1]
39 | file_path = safe_filename(await reply.download())
40 | output_ = await runcmd(f'mediainfo "{file_path}"')
41 | out = None
42 | if len(output_) != 0:
43 | out = output_[0]
44 | body_text = f"""
45 |
{x_media}
47 | {out or 'Not Supported'}
51 | """
52 | text_ = media_type.split(".")[-1].upper()
53 | link = post_to_telegraph(media_type, body_text)
54 | markup = InlineKeyboardMarkup([[InlineKeyboardButton(text=text_, url=link)]])
55 | await process.edit_text("ℹ️ MEDIA INFO", reply_markup=markup)
56 |
--------------------------------------------------------------------------------
/bot/helper/telegram_helper/filters.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import MessageFilter
2 | from telegram import Message
3 | from bot import AUTHORIZED_CHATS, SUDO_USERS, OWNER_ID, download_dict, download_dict_lock
4 |
5 |
6 | class CustomFilters:
7 | class _OwnerFilter(MessageFilter):
8 | def filter(self, message):
9 | return bool(message.from_user.id == OWNER_ID)
10 |
11 | owner_filter = _OwnerFilter()
12 |
13 | class _AuthorizedUserFilter(MessageFilter):
14 | def filter(self, message):
15 | id = message.from_user.id
16 | return bool(id in AUTHORIZED_CHATS or id == OWNER_ID)
17 |
18 | authorized_user = _AuthorizedUserFilter()
19 |
20 | class _AuthorizedChat(MessageFilter):
21 | def filter(self, message):
22 | return bool(message.chat.id in AUTHORIZED_CHATS)
23 |
24 | authorized_chat = _AuthorizedChat()
25 |
26 | class _SudoUser(MessageFilter):
27 | def filter(self,message):
28 | return bool(message.from_user.id in SUDO_USERS)
29 |
30 | sudo_user = _SudoUser()
31 |
32 | class _MirrorOwner(MessageFilter):
33 | def filter(self, message: Message):
34 | user_id = message.from_user.id
35 | if user_id == OWNER_ID:
36 | return True
37 | args = str(message.text).split(' ')
38 | if len(args) > 1:
39 | # Cancelling by gid
40 | with download_dict_lock:
41 | for message_id, status in download_dict.items():
42 | if status.gid() == args[1] and status.message.from_user.id == user_id:
43 | return True
44 | else:
45 | return False
46 | if not message.reply_to_message and len(args) == 1:
47 | return True
48 | # Cancelling by replying to original mirror message
49 | reply_user = message.reply_to_message.from_user.id
50 | return bool(reply_user == user_id)
51 | mirror_owner_filter = _MirrorOwner()
52 |
--------------------------------------------------------------------------------
/bot/modules/speedtest.py:
--------------------------------------------------------------------------------
1 | from speedtest import Speedtest
2 | from bot.helper.telegram_helper.filters import CustomFilters
3 | from bot import dispatcher, AUTHORIZED_CHATS
4 | from bot.helper.telegram_helper.bot_commands import BotCommands
5 | from telegram import Update, ParseMode
6 | from telegram.ext import Filters, CommandHandler
7 |
8 |
9 | def speedtest(update, context):
10 | message = update.effective_message
11 | ed_msg = message.reply_text("Running Speed Test . . . ")
12 | test = Speedtest()
13 | test.get_best_server()
14 | test.download()
15 | test.upload()
16 | test.results.share()
17 | result = test.results.dict()
18 | path = (result['share'])
19 | string_speed = f'''
20 | Server
21 | Name: {result['server']['name']}
22 | Country: {result['server']['country']}, {result['server']['cc']}
23 | Sponsor: {result['server']['sponsor']}
24 | Latency: {result['server']['latency']}
25 |
26 | SpeedTest Results
27 | Upload: {speed_convert(result['upload'] / 8)}
28 | Download: {speed_convert(result['download'] / 8)}
29 | Ping: {result['ping']} ms
30 | ISP: {result['client']['isp']}
31 | '''
32 | ed_msg.delete()
33 | try:
34 | update.effective_message.reply_photo(path, string_speed, parse_mode=ParseMode.HTML)
35 | except:
36 | update.effective_message.reply_text(string_speed, parse_mode=ParseMode.HTML)
37 |
38 | def speed_convert(size):
39 | """Hi human, you can't read bytes?"""
40 | power = 2 ** 10
41 | zero = 0
42 | units = {0: "", 1: "Kb/s", 2: "MB/s", 3: "Gb/s", 4: "Tb/s"}
43 | while size > power:
44 | size /= power
45 | zero += 1
46 | return f"{round(size, 2)} {units[zero]}"
47 |
48 |
49 | SPEED_HANDLER = CommandHandler(BotCommands.SpeedCommand, speedtest,
50 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
51 |
52 | dispatcher.add_handler(SPEED_HANDLER)
53 |
--------------------------------------------------------------------------------
/config_sample.env:
--------------------------------------------------------------------------------
1 | #Remove this line before deploying
2 | _____REMOVE_THIS_LINE_____=True
3 |
4 | # ENTER BOT TOKEN (Get your BOT_TOKEN by talking to @botfather)
5 | BOT_TOKEN = ""
6 | GDRIVE_FOLDER_ID = ""
7 | OWNER_ID =
8 | DOWNLOAD_DIR = "/usr/src/app/downloads"
9 | DOWNLOAD_STATUS_UPDATE_INTERVAL = 5
10 | AUTO_DELETE_MESSAGE_DURATION = 20
11 | IS_TEAM_DRIVE = ""
12 | TELEGRAM_API =
13 | TELEGRAM_HASH = ""
14 | DATABASE_URL = ""
15 | UPSTREAM_REPO = "https://github.com/breakdowns/slam-mirrorbot"
16 | UPSTREAM_BRANCH = "master"
17 | # OPTIONAL CONFIG
18 | AUTHORIZED_CHATS = ""
19 | IGNORE_PENDING_REQUESTS = ""
20 | USE_SERVICE_ACCOUNTS = ""
21 | INDEX_URL = ""
22 | UPTOBOX_TOKEN = ""
23 | MEGA_API_KEY = ""
24 | MEGA_EMAIL_ID = ""
25 | MEGA_PASSWORD = ""
26 | BLOCK_MEGA_FOLDER = ""
27 | BLOCK_MEGA_LINKS = ""
28 | STOP_DUPLICATE_MIRROR = ""
29 | STOP_DUPLICATE_CLONE = ""
30 | STOP_DUPLICATE_MEGA = ""
31 | SHORTENER = ""
32 | SHORTENER_API = ""
33 | IMAGE_URL = "https://telegra.ph/file/db03910496f06094f1f7a.jpg"
34 | # If you want to use Credentials externally from Index Links, fill these vars with the direct links
35 | # These are optional, if you don't know, simply leave them, don't fill anything in them.
36 | ACCOUNTS_ZIP_URL = ""
37 | TOKEN_PICKLE_URL = ""
38 | # To use limit leave space between number and unit. Available units is (gb or GB, tb or TB)
39 | TORRENT_DIRECT_LIMIT = ""
40 | CLONE_LIMIT = ""
41 | MEGA_LIMIT = ""
42 | # Fill only if you deploying with heroku-cli and Goorm IDE
43 | HEROKU_API_KEY = ""
44 | HEROKU_APP_NAME = ""
45 | # View Link button to open file Index Link in browser instead of direct download link, you can figure out if it's compatible with your Index code or not, open any video from you Index and check if the END of link from browser link bar is ?a=view, if yes make it "True" it will work (Compatible with Bhadoo Index Code)
46 | VIEW_LINK = "False"
47 | # Add more buttons (Three buttons are already added of Drive Link, Index Link, and View Link, you can add extra buttons too, these are optional)
48 | # If you don't know what are below entries, simply leave them, Don't fill anything in them.
49 | BUTTON_FOUR_NAME = ""
50 | BUTTON_FOUR_URL = ""
51 | BUTTON_FIVE_NAME = ""
52 | BUTTON_FIVE_URL = ""
53 | BUTTON_SIX_NAME = ""
54 | BUTTON_SIX_URL = ""
55 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/db_handler.py:
--------------------------------------------------------------------------------
1 | import psycopg2
2 | from psycopg2 import Error
3 | from bot import AUTHORIZED_CHATS, SUDO_USERS, DB_URI, LOGGER
4 |
5 | class DbManger:
6 | def __init__(self):
7 | self.err = False
8 |
9 | def connect(self):
10 | try:
11 | self.conn = psycopg2.connect(DB_URI)
12 | self.cur = self.conn.cursor()
13 | except psycopg2.DatabaseError as error :
14 | LOGGER.error("Error in dbMang : ", error)
15 | self.err = True
16 |
17 | def disconnect(self):
18 | self.cur.close()
19 | self.conn.close()
20 |
21 | def db_auth(self,chat_id: int):
22 | self.connect()
23 | if self.err :
24 | return "There's some error check log for details"
25 | else:
26 | sql = 'INSERT INTO users VALUES ({});'.format(chat_id)
27 | self.cur.execute(sql)
28 | self.conn.commit()
29 | self.disconnect()
30 | AUTHORIZED_CHATS.add(chat_id)
31 | return 'Authorized successfully'
32 |
33 | def db_unauth(self,chat_id: int):
34 | self.connect()
35 | if self.err :
36 | return "There's some error check log for details"
37 | else:
38 | sql = 'DELETE from users where uid = {};'.format(chat_id)
39 | self.cur.execute(sql)
40 | self.conn.commit()
41 | self.disconnect()
42 | AUTHORIZED_CHATS.remove(chat_id)
43 | if chat_id in SUDO_USERS:
44 | SUDO_USERS.remove(chat_id)
45 | return 'Unauthorized successfully'
46 |
47 | def db_addsudo(self,chat_id: int):
48 | self.connect()
49 | if self.err :
50 | return "There's some error check log for details"
51 | else:
52 | if chat_id in AUTHORIZED_CHATS:
53 | sql = 'UPDATE users SET sudo = TRUE where uid = {};'.format(chat_id)
54 | self.cur.execute(sql)
55 | self.conn.commit()
56 | self.disconnect()
57 | SUDO_USERS.add(chat_id)
58 | return 'Successfully promoted as Sudo'
59 | else:
60 | sql = 'INSERT INTO users VALUES ({},TRUE);'.format(chat_id)
61 | self.cur.execute(sql)
62 | self.conn.commit()
63 | self.disconnect()
64 | AUTHORIZED_CHATS.add(chat_id)
65 | SUDO_USERS.add(chat_id)
66 | return 'Successfully Authorized and promoted as Sudo'
67 |
68 | def db_rmsudo(self,chat_id: int):
69 | self.connect()
70 | if self.err :
71 | return "There's some error check log for details"
72 | else:
73 | sql = 'UPDATE users SET sudo = FALSE where uid = {};'.format(chat_id)
74 | self.cur.execute(sql)
75 | self.conn.commit()
76 | self.disconnect()
77 | SUDO_USERS.remove(chat_id)
78 | return 'Successfully removed from Sudo'
79 |
--------------------------------------------------------------------------------
/bot/modules/watch.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from telegram import Bot, Update
3 | from bot import Interval, DOWNLOAD_DIR, DOWNLOAD_STATUS_UPDATE_INTERVAL, dispatcher, LOGGER
4 | from bot.helper.ext_utils.bot_utils import setInterval
5 | from bot.helper.telegram_helper.message_utils import update_all_messages, sendMessage, sendStatusMessage
6 | from .mirror import MirrorListener
7 | from bot.helper.mirror_utils.download_utils.youtube_dl_download_helper import YoutubeDLHelper
8 | from bot.helper.telegram_helper.bot_commands import BotCommands
9 | from bot.helper.telegram_helper.filters import CustomFilters
10 | import threading
11 |
12 |
13 | def _watch(bot: Bot, update, isTar=False):
14 | mssg = update.message.text
15 | message_args = mssg.split(' ')
16 | name_args = mssg.split('|')
17 | try:
18 | link = message_args[1]
19 | except IndexError:
20 | msg = f"/{BotCommands.WatchCommand} [youtube-dl supported link] [quality] |[CustomName] to mirror with youtube-dl.\n\n"
21 | msg += "Note: Quality and custom name are optional\n\nExample of quality: audio, 144, 240, 360, 480, 720, 1080, 2160."
22 | msg += "\n\nIf you want to use custom filename, enter it after |"
23 | msg += f"\n\nExample:\n/{BotCommands.WatchCommand} https://youtu.be/Pk_TthHfLeE 720 |Slam\n\n"
24 | msg += "This file will be downloaded in 720p quality and it's name will be Slam"
25 | sendMessage(msg, bot, update)
26 | return
27 | try:
28 | if "|" in mssg:
29 | mssg = mssg.split("|")
30 | qual = mssg[0].split(" ")[2]
31 | if qual == "":
32 | raise IndexError
33 | else:
34 | qual = message_args[2]
35 | if qual != "audio":
36 | qual = f'bestvideo[height<={qual}]+bestaudio/best[height<={qual}]'
37 | except IndexError:
38 | qual = "bestvideo+bestaudio/best"
39 | try:
40 | name = name_args[1]
41 | except IndexError:
42 | name = ""
43 | reply_to = update.message.reply_to_message
44 | if reply_to is not None:
45 | tag = reply_to.from_user.username
46 | else:
47 | tag = None
48 | pswd = ""
49 | listener = MirrorListener(bot, update, pswd, isTar, tag)
50 | ydl = YoutubeDLHelper(listener)
51 | threading.Thread(target=ydl.add_download,args=(link, f'{DOWNLOAD_DIR}{listener.uid}', qual, name)).start()
52 | sendStatusMessage(update, bot)
53 | if len(Interval) == 0:
54 | Interval.append(setInterval(DOWNLOAD_STATUS_UPDATE_INTERVAL, update_all_messages))
55 |
56 |
57 | def watchTar(update, context):
58 | _watch(context.bot, update, True)
59 |
60 |
61 | def watch(update, context):
62 | _watch(context.bot, update)
63 |
64 |
65 | mirror_handler = CommandHandler(BotCommands.WatchCommand, watch,
66 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
67 | tar_mirror_handler = CommandHandler(BotCommands.TarWatchCommand, watchTar,
68 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
69 | dispatcher.add_handler(mirror_handler)
70 | dispatcher.add_handler(tar_mirror_handler)
71 |
--------------------------------------------------------------------------------
/add_to_team_drive.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | from google.oauth2.service_account import Credentials
3 | import googleapiclient.discovery, json, progress.bar, glob, sys, argparse, time
4 | from google_auth_oauthlib.flow import InstalledAppFlow
5 | from google.auth.transport.requests import Request
6 | import os, pickle
7 |
8 | stt = time.time()
9 |
10 | parse = argparse.ArgumentParser(
11 | description='A tool to add service accounts to a shared drive from a folder containing credential files.')
12 | parse.add_argument('--path', '-p', default='accounts',
13 | help='Specify an alternative path to the service accounts folder.')
14 | parse.add_argument('--credentials', '-c', default='./credentials.json',
15 | help='Specify the relative path for the credentials file.')
16 | parse.add_argument('--yes', '-y', default=False, action='store_true', help='Skips the sanity prompt.')
17 | parsereq = parse.add_argument_group('required arguments')
18 | parsereq.add_argument('--drive-id', '-d', help='The ID of the Shared Drive.', required=True)
19 |
20 | args = parse.parse_args()
21 | acc_dir = args.path
22 | did = args.drive_id
23 | credentials = glob.glob(args.credentials)
24 |
25 | try:
26 | open(credentials[0], 'r')
27 | print('>> Found credentials.')
28 | except IndexError:
29 | print('>> No credentials found.')
30 | sys.exit(0)
31 |
32 | if not args.yes:
33 | # input('Make sure the following client id is added to the shared drive as Manager:\n' + json.loads((open(
34 | # credentials[0],'r').read()))['installed']['client_id'])
35 | input('>> Make sure the **Google account** that has generated credentials.json\n is added into your Team Drive '
36 | '(shared drive) as Manager\n>> (Press any key to continue)')
37 |
38 | creds = None
39 | if os.path.exists('token_sa.pickle'):
40 | with open('token_sa.pickle', 'rb') as token:
41 | creds = pickle.load(token)
42 | # If there are no (valid) credentials available, let the user log in.
43 | if not creds or not creds.valid:
44 | if creds and creds.expired and creds.refresh_token:
45 | creds.refresh(Request())
46 | else:
47 | flow = InstalledAppFlow.from_client_secrets_file(credentials[0], scopes=[
48 | 'https://www.googleapis.com/auth/admin.directory.group',
49 | 'https://www.googleapis.com/auth/admin.directory.group.member'
50 | ])
51 | # creds = flow.run_local_server(port=0)
52 | creds = flow.run_console()
53 | # Save the credentials for the next run
54 | with open('token_sa.pickle', 'wb') as token:
55 | pickle.dump(creds, token)
56 |
57 | drive = googleapiclient.discovery.build("drive", "v3", credentials=creds)
58 | batch = drive.new_batch_http_request()
59 |
60 | aa = glob.glob('%s/*.json' % acc_dir)
61 | pbar = progress.bar.Bar("Readying accounts", max=len(aa))
62 | for i in aa:
63 | ce = json.loads(open(i, 'r').read())['client_email']
64 | batch.add(drive.permissions().create(fileId=did, supportsAllDrives=True, body={
65 | "role": "fileOrganizer",
66 | "type": "user",
67 | "emailAddress": ce
68 | }))
69 | pbar.next()
70 | pbar.finish()
71 | print('Adding...')
72 | batch.execute()
73 |
74 | print('Complete.')
75 | hours, rem = divmod((time.time() - stt), 3600)
76 | minutes, sec = divmod(rem, 60)
77 | print("Elapsed Time:\n{:0>2}:{:0>2}:{:05.2f}".format(int(hours), int(minutes), sec))
--------------------------------------------------------------------------------
/bot/modules/updates.py:
--------------------------------------------------------------------------------
1 | # Implement By https://github.com/jusidama18
2 | # Based on this https://github.com/DevsExpo/FridayUserbot/blob/master/plugins/updater.py
3 |
4 | import sys
5 | import subprocess
6 | import heroku3
7 |
8 | from datetime import datetime
9 | from os import environ, execle, path, remove
10 |
11 | from git import Repo
12 | from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
13 |
14 | from pyrogram import filters
15 |
16 | from bot import app, OWNER_ID, UPSTREAM_REPO, UPSTREAM_BRANCH
17 | from bot.helper import runcmd, get_text, HEROKU_URL
18 | from bot.helper.telegram_helper.bot_commands import BotCommands
19 |
20 | REPO_ = UPSTREAM_REPO
21 | BRANCH_ = UPSTREAM_BRANCH
22 |
23 | # Update Command
24 |
25 | @app.on_message(filters.command(BotCommands.UpdateCommand) & filters.user(OWNER_ID))
26 | async def update_it(client, message):
27 | msg_ = await message.reply_text("`Updating Please Wait!`")
28 | try:
29 | repo = Repo()
30 | except GitCommandError:
31 | return await msg_.edit(
32 | "**Invalid Git Command. Please Report This Bug To [Support Group](https://t.me/SlamMirrorSupport)**"
33 | )
34 | except InvalidGitRepositoryError:
35 | repo = Repo.init()
36 | if "upstream" in repo.remotes:
37 | origin = repo.remote("upstream")
38 | else:
39 | origin = repo.create_remote("upstream", REPO_)
40 | origin.fetch()
41 | repo.create_head(UPSTREAM_BRANCH, origin.refs.master)
42 | repo.heads.master.set_tracking_branch(origin.refs.master)
43 | repo.heads.master.checkout(True)
44 | if repo.active_branch.name != UPSTREAM_BRANCH:
45 | return await msg_.edit(
46 | f"`Seems Like You Are Using Custom Branch - {repo.active_branch.name}! Please Switch To {UPSTREAM_BRANCH} To Make This Updater Function!`"
47 | )
48 | try:
49 | repo.create_remote("upstream", REPO_)
50 | except BaseException:
51 | pass
52 | ups_rem = repo.remote("upstream")
53 | ups_rem.fetch(UPSTREAM_BRANCH)
54 | if not HEROKU_URL:
55 | try:
56 | ups_rem.pull(UPSTREAM_BRANCH)
57 | except GitCommandError:
58 | repo.git.reset("--hard", "FETCH_HEAD")
59 | await runcmd("pip3 install --no-cache-dir -r requirements.txt")
60 | await msg_.edit("`Updated Sucessfully! Give Me Some Time To Restart!`")
61 | with open("./aria.sh", 'rb') as file:
62 | script = file.read()
63 | subprocess.call("./aria.sh", shell=True)
64 | args = [sys.executable, "-m", "bot"]
65 | execle(sys.executable, *args, environ)
66 | exit()
67 | return
68 | else:
69 | await msg_.edit("`Heroku Detected! Pushing, Please wait!`")
70 | ups_rem.fetch(UPSTREAM_BRANCH)
71 | repo.git.reset("--hard", "FETCH_HEAD")
72 | if "heroku" in repo.remotes:
73 | remote = repo.remote("heroku")
74 | remote.set_url(HEROKU_URL)
75 | else:
76 | remote = repo.create_remote("heroku", HEROKU_URL)
77 | try:
78 | remote.push(refspec="HEAD:refs/heads/master", force=True)
79 | except BaseException as error:
80 | await msg_.edit(f"**Updater Error** \nTraceBack : `{error}`")
81 | return repo.__del__()
82 | await msg_.edit(f"`Updated Sucessfully! \n\nCheck your config with` `/{BotCommands.ConfigMenuCommand}`")
83 |
--------------------------------------------------------------------------------
/bot/modules/usage.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | import requests
4 | import heroku3
5 |
6 | from bot import dispatcher, HEROKU_APP_NAME, HEROKU_API_KEY
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 | from bot.helper.telegram_helper.filters import CustomFilters
9 | from bot.helper.telegram_helper.message_utils import sendMessage
10 | from telegram import update
11 | from telegram.ext import CommandHandler
12 |
13 |
14 | def dyno_usage(update, context):
15 | heroku_api = "https://api.heroku.com"
16 | if HEROKU_API_KEY is not None and HEROKU_APP_NAME is not None:
17 | Heroku = heroku3.from_key(HEROKU_API_KEY)
18 | app = Heroku.app(HEROKU_APP_NAME)
19 | else:
20 | sendMessage(
21 | "Please insert your HEROKU_APP_NAME and HEROKU_API_KEY in Vars",
22 | context.bot,
23 | update
24 | )
25 | useragent = (
26 | "Mozilla/5.0 (Linux; Android 10; SM-G975F) "
27 | "AppleWebKit/537.36 (KHTML, like Gecko) "
28 | "Chrome/81.0.4044.117 Mobile Safari/537.36"
29 | )
30 | user_id = Heroku.account().id
31 | headers = {
32 | "User-Agent": useragent,
33 | "Authorization": f"Bearer {HEROKU_API_KEY}",
34 | "Accept": "application/vnd.heroku+json; version=3.account-quotas",
35 | }
36 | path = "/accounts/" + user_id + "/actions/get-quota"
37 | session = requests.Session()
38 | with session as ses:
39 | with ses.get(heroku_api + path, headers=headers) as r:
40 | result = r.json()
41 | """Account Quota."""
42 | quota = result["account_quota"]
43 | quota_used = result["quota_used"]
44 | quota_remain = quota - quota_used
45 | quota_percent = math.floor(quota_remain / quota * 100)
46 | minutes_remain = quota_remain / 60
47 | hours = math.floor(minutes_remain / 60)
48 | minutes = math.floor(minutes_remain % 60)
49 | day = math.floor(hours / 24)
50 |
51 | """App Quota."""
52 | Apps = result["apps"]
53 | for apps in Apps:
54 | if apps.get("app_uuid") == app.id:
55 | AppQuotaUsed = apps.get("quota_used") / 60
56 | AppPercent = math.floor(apps.get("quota_used") * 100 / quota)
57 | break
58 | else:
59 | AppQuotaUsed = 0
60 | AppPercent = 0
61 |
62 | AppHours = math.floor(AppQuotaUsed / 60)
63 | AppMinutes = math.floor(AppQuotaUsed % 60)
64 |
65 | sendMessage(
66 | f"Dyno Usage for {app.name}:\n"
67 | f"• {AppHours} Hours and {AppMinutes} Minutes - {AppPercent}%\n\n"
68 | "Dyno Remaining this month:\n"
69 | f"• {hours} Hours and {minutes} Minutes - {quota_percent}%\n\n"
70 | "Estimated Dyno Expired:\n"
71 | f"• {day} Days",
72 | context.bot,
73 | update
74 | )
75 | return True
76 |
77 |
78 | dyno_usage_handler = CommandHandler(command=BotCommands.UsageCommand, callback=dyno_usage,
79 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
80 |
81 | dispatcher.add_handler(dyno_usage_handler)
82 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/status_utils/aria_download_status.py:
--------------------------------------------------------------------------------
1 | from bot import aria2, DOWNLOAD_DIR, LOGGER
2 | from bot.helper.ext_utils.bot_utils import MirrorStatus
3 | from .status import Status
4 |
5 |
6 | def get_download(gid):
7 | return aria2.get_download(gid)
8 |
9 |
10 | class AriaDownloadStatus(Status):
11 |
12 | def __init__(self, gid, listener):
13 | super().__init__()
14 | self.upload_name = None
15 | self.is_archiving = False
16 | self.__gid = gid
17 | self.__download = get_download(self.__gid)
18 | self.__uid = listener.uid
19 | self.__listener = listener
20 | self.message = listener.message
21 | self.last = None
22 | self.is_waiting = False
23 | self.is_extracting = False
24 |
25 | def __update(self):
26 | self.__download = get_download(self.__gid)
27 |
28 | def progress(self):
29 | """
30 | Calculates the progress of the mirror (upload or download)
31 | :return: returns progress in percentage
32 | """
33 | self.__update()
34 | return self.__download.progress_string()
35 |
36 | def size_raw(self):
37 | """
38 | Gets total size of the mirror file/folder
39 | :return: total size of mirror
40 | """
41 | return self.aria_download().total_length
42 |
43 | def processed_bytes(self):
44 | return self.aria_download().completed_length
45 |
46 | def speed(self):
47 | return self.aria_download().download_speed_string()
48 |
49 | def name(self):
50 | return self.aria_download().name
51 |
52 | def path(self):
53 | return f"{DOWNLOAD_DIR}{self.__uid}"
54 |
55 | def size(self):
56 | return self.aria_download().total_length_string()
57 |
58 | def eta(self):
59 | return self.aria_download().eta_string()
60 |
61 | def status(self):
62 | download = self.aria_download()
63 | if download.is_waiting:
64 | status = MirrorStatus.STATUS_WAITING
65 | elif download.has_failed:
66 | status = MirrorStatus.STATUS_FAILED
67 | else:
68 | status = MirrorStatus.STATUS_DOWNLOADING
69 | return status
70 |
71 | def aria_download(self):
72 | self.__update()
73 | return self.__download
74 |
75 | def download(self):
76 | return self
77 |
78 | def updateName(self,name):
79 | self.__name = name
80 |
81 | def updateGid(self,gid):
82 | self.__gid = gid
83 |
84 | def getListener(self):
85 | return self.__listener
86 |
87 | def uid(self):
88 | return self.__uid
89 |
90 | def gid(self):
91 | self.__update()
92 | return self.__gid
93 |
94 | def cancel_download(self):
95 | LOGGER.info(f"Cancelling Download: {self.name()}")
96 | download = self.aria_download()
97 | if download.is_waiting:
98 | aria2.remove([download])
99 | self.__listener.onDownloadError("Cancelled by user")
100 | return
101 | if len(download.followed_by_ids) != 0:
102 | downloads = aria2.get_downloads(download.followed_by_ids)
103 | aria2.remove(downloads)
104 | self.__listener.onDownloadError("Download stopped by user!")
105 | aria2.remove([download])
106 | self.__listener.onDownloadError("Download stopped by user!")
107 |
--------------------------------------------------------------------------------
/bot/modules/clone.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
3 | from bot.helper.telegram_helper.message_utils import sendMarkup, deleteMessage, sendMessage
4 | from bot.helper.telegram_helper.filters import CustomFilters
5 | from bot.helper.telegram_helper.bot_commands import BotCommands
6 | from bot import dispatcher, LOGGER, CLONE_LIMIT, STOP_DUPLICATE_CLONE
7 | from bot.helper.ext_utils.bot_utils import get_readable_file_size
8 |
9 |
10 | def cloneNode(update, context):
11 | args = update.message.text.split(" ", maxsplit=1)
12 | if len(args) > 1:
13 | link = args[1]
14 | gd = GoogleDriveHelper()
15 | if CLONE_LIMIT is not None or STOP_DUPLICATE_CLONE:
16 | msg1 = sendMessage(f"Checking Your Link...", context.bot, update)
17 | res, clonesize, name = gd.clonehelper(link)
18 | if res != "":
19 | deleteMessage(context.bot, msg1)
20 | sendMessage(res, context.bot, update)
21 | return
22 | if STOP_DUPLICATE_CLONE:
23 | LOGGER.info(f"Checking File/Folder if already in Drive...")
24 | smsg, button = gd.drive_list(name)
25 | if smsg:
26 | deleteMessage(context.bot, msg1)
27 | msg3 = "File/Folder is already available in Drive.\nHere are the search results:"
28 | sendMarkup(msg3, context.bot, update, button)
29 | return
30 | else:
31 | if CLONE_LIMIT is None:
32 | deleteMessage(context.bot, msg1)
33 | if CLONE_LIMIT is not None:
34 | LOGGER.info(f"Checking File/Folder Size...")
35 | limit = CLONE_LIMIT
36 | limit = limit.split(' ', maxsplit=1)
37 | limitint = int(limit[0])
38 | msg2 = f'Failed, Clone limit is {CLONE_LIMIT}.\nYour File/Folder size is {get_readable_file_size(clonesize)}.'
39 | if 'GB' in limit or 'gb' in limit:
40 | if clonesize > limitint * 1024**3:
41 | deleteMessage(context.bot, msg1)
42 | sendMessage(msg2, context.bot, update)
43 | return
44 | else:
45 | deleteMessage(context.bot, msg1)
46 | elif 'TB' in limit or 'tb' in limit:
47 | if clonesize > limitint * 1024**4:
48 | deleteMessage(context.bot, msg1)
49 | sendMessage(msg2, context.bot, update)
50 | return
51 | else:
52 | deleteMessage(context.bot, msg1)
53 | msg = sendMessage(f"Cloning: {link}", context.bot, update)
54 | result, button = gd.clone(link)
55 | deleteMessage(context.bot, msg)
56 | if button == "":
57 | sendMessage(result, context.bot, update)
58 | else:
59 | if update.message.from_user.username:
60 | uname = f'@{update.message.from_user.username}'
61 | else:
62 | uname = f'{update.message.from_user.first_name}'
63 | if uname is not None:
64 | cc = f'\n\ncc: {uname}'
65 | sendMarkup(result + cc, context.bot, update, button)
66 | else:
67 | sendMessage('Provide G-Drive Shareable Link to Clone.', context.bot, update)
68 |
69 | clone_handler = CommandHandler(BotCommands.CloneCommand, cloneNode, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
70 | dispatcher.add_handler(clone_handler)
71 |
--------------------------------------------------------------------------------
/bot/modules/cancel_mirror.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CommandHandler
2 | from bot import download_dict, dispatcher, download_dict_lock, DOWNLOAD_DIR
3 | from bot.helper.ext_utils.fs_utils import clean_download
4 | from bot.helper.telegram_helper.bot_commands import BotCommands
5 | from bot.helper.telegram_helper.filters import CustomFilters
6 | from bot.helper.telegram_helper.message_utils import *
7 |
8 | from time import sleep
9 | from bot.helper.ext_utils.bot_utils import getDownloadByGid, MirrorStatus, getAllDownload
10 |
11 |
12 | def cancel_mirror(update, context):
13 | args = update.message.text.split(" ", maxsplit=1)
14 | mirror_message = None
15 | if len(args) > 1:
16 | gid = args[1]
17 | dl = getDownloadByGid(gid)
18 | if not dl:
19 | sendMessage(f"GID: {gid} Not Found.", context.bot, update)
20 | return
21 | mirror_message = dl.message
22 | elif update.message.reply_to_message:
23 | mirror_message = update.message.reply_to_message
24 | with download_dict_lock:
25 | keys = list(download_dict.keys())
26 | try:
27 | dl = download_dict[mirror_message.message_id]
28 | except:
29 | pass
30 | if len(args) == 1:
31 | msg = f"Please reply to the /{BotCommands.MirrorCommand} message which was used to start the download or send /{BotCommands.CancelMirror} GID to cancel it!"
32 | if mirror_message and mirror_message.message_id not in keys:
33 | if BotCommands.MirrorCommand in mirror_message.text or \
34 | BotCommands.TarMirrorCommand in mirror_message.text or \
35 | BotCommands.UnzipMirrorCommand in mirror_message.text:
36 | msg1 = "Mirror Already Have Been Cancelled"
37 | sendMessage(msg1, context.bot, update)
38 | return
39 | else:
40 | sendMessage(msg, context.bot, update)
41 | return
42 | elif not mirror_message:
43 | sendMessage(msg, context.bot, update)
44 | return
45 | if dl.status() == "Uploading...📤":
46 | sendMessage("Upload in Progress, You Can't Cancel It.", context.bot, update)
47 | return
48 | elif dl.status() == "Archiving...🔐":
49 | sendMessage("Archival in Progress, You Can't Cancel It.", context.bot, update)
50 | return
51 | elif dl.status() == "Extracting...📂":
52 | sendMessage("Extract in Progress, You Can't Cancel It.", context.bot, update)
53 | return
54 | else:
55 | dl.download().cancel_download()
56 | sleep(3) # incase of any error with ondownloaderror listener, clean_download will delete the folder but the download will stuck in status msg.
57 | clean_download(f'{DOWNLOAD_DIR}{mirror_message.message_id}/')
58 |
59 |
60 | def cancel_all(update, context):
61 | count = 0
62 | gid = 1
63 | while True:
64 | dl = getAllDownload()
65 | if dl:
66 | if dl.gid() == gid:
67 | continue
68 | else:
69 | gid = dl.gid()
70 | dl.download().cancel_download()
71 | sleep(0.5)
72 | count += 1
73 | else:
74 | break
75 | delete_all_messages()
76 | sendMessage(f'{count} Download(s) has been Cancelled!', context.bot, update)
77 |
78 |
79 |
80 | cancel_mirror_handler = CommandHandler(BotCommands.CancelMirror, cancel_mirror,
81 | filters=(CustomFilters.authorized_chat | CustomFilters.authorized_user) & CustomFilters.mirror_owner_filter | CustomFilters.sudo_user, run_async=True)
82 | cancel_all_handler = CommandHandler(BotCommands.CancelAllCommand, cancel_all,
83 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
84 | dispatcher.add_handler(cancel_all_handler)
85 | dispatcher.add_handler(cancel_mirror_handler)
86 |
--------------------------------------------------------------------------------
/bot/modules/eval.py:
--------------------------------------------------------------------------------
1 | import io
2 | import os
3 | # Common imports for eval
4 | import textwrap
5 | import traceback
6 | from contextlib import redirect_stdout
7 | from bot.helper.telegram_helper.filters import CustomFilters
8 | from bot import LOGGER, dispatcher
9 | from telegram import ParseMode
10 | from telegram.ext import CommandHandler
11 |
12 | namespaces = {}
13 |
14 |
15 | def namespace_of(chat, update, bot):
16 | if chat not in namespaces:
17 | namespaces[chat] = {
18 | '__builtins__': globals()['__builtins__'],
19 | 'bot': bot,
20 | 'effective_message': update.effective_message,
21 | 'effective_user': update.effective_user,
22 | 'effective_chat': update.effective_chat,
23 | 'update': update
24 | }
25 |
26 | return namespaces[chat]
27 |
28 |
29 | def log_input(update):
30 | user = update.effective_user.id
31 | chat = update.effective_chat.id
32 | LOGGER.info(
33 | f"IN: {update.effective_message.text} (user={user}, chat={chat})")
34 |
35 |
36 | def send(msg, bot, update):
37 | if len(str(msg)) > 2000:
38 | with io.BytesIO(str.encode(msg)) as out_file:
39 | out_file.name = "output.txt"
40 | bot.send_document(
41 | chat_id=update.effective_chat.id, document=out_file)
42 | else:
43 | LOGGER.info(f"OUT: '{msg}'")
44 | bot.send_message(
45 | chat_id=update.effective_chat.id,
46 | text=f"`{msg}`",
47 | parse_mode=ParseMode.MARKDOWN)
48 |
49 |
50 | def evaluate(update, context):
51 | bot = context.bot
52 | send(do(eval, bot, update), bot, update)
53 |
54 |
55 | def execute(update, context):
56 | bot = context.bot
57 | send(do(exec, bot, update), bot, update)
58 |
59 |
60 | def cleanup_code(code):
61 | if code.startswith('```') and code.endswith('```'):
62 | return '\n'.join(code.split('\n')[1:-1])
63 | return code.strip('` \n')
64 |
65 |
66 | def do(func, bot, update):
67 | log_input(update)
68 | content = update.message.text.split(' ', 1)[-1]
69 | body = cleanup_code(content)
70 | env = namespace_of(update.message.chat_id, update, bot)
71 |
72 | os.chdir(os.getcwd())
73 | with open(
74 | os.path.join(os.getcwd(),
75 | 'bot/modules/temp.txt'),
76 | 'w') as temp:
77 | temp.write(body)
78 |
79 | stdout = io.StringIO()
80 |
81 | to_compile = f'def func():\n{textwrap.indent(body, " ")}'
82 |
83 | try:
84 | exec(to_compile, env)
85 | except Exception as e:
86 | return f'{e.__class__.__name__}: {e}'
87 |
88 | func = env['func']
89 |
90 | try:
91 | with redirect_stdout(stdout):
92 | func_return = func()
93 | except Exception as e:
94 | value = stdout.getvalue()
95 | return f'{value}{traceback.format_exc()}'
96 | else:
97 | value = stdout.getvalue()
98 | result = None
99 | if func_return is None:
100 | if value:
101 | result = f'{value}'
102 | else:
103 | try:
104 | result = f'{repr(eval(body, env))}'
105 | except:
106 | pass
107 | else:
108 | result = f'{value}{func_return}'
109 | if result:
110 | return result
111 |
112 |
113 | def clear(update, context):
114 | bot = context.bot
115 | log_input(update)
116 | global namespaces
117 | if update.message.chat_id in namespaces:
118 | del namespaces[update.message.chat_id]
119 | send("Cleared locals.", bot, update)
120 |
121 |
122 | EVAL_HANDLER = CommandHandler(('e', 'ev', 'eva', 'eval'), evaluate, filters=CustomFilters.owner_filter, run_async=True)
123 | EXEC_HANDLER = CommandHandler(('x', 'ex', 'exe', 'exec', 'py'), execute, filters=CustomFilters.owner_filter, run_async=True)
124 | CLEAR_HANDLER = CommandHandler('clearlocals', clear, filters=CustomFilters.owner_filter, run_async=True)
125 |
126 | dispatcher.add_handler(EVAL_HANDLER)
127 | dispatcher.add_handler(EXEC_HANDLER)
128 | dispatcher.add_handler(CLEAR_HANDLER)
129 |
130 |
--------------------------------------------------------------------------------
/bot/helper/__init__.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import os
3 | import shlex
4 | import heroku3
5 |
6 | from functools import wraps
7 | from pyrogram.types import Message
8 | from typing import Tuple
9 | from html_telegraph_poster import TelegraphPoster
10 | from bot import HEROKU_API_KEY, HEROKU_APP_NAME
11 |
12 | # Implement by https://github.com/jusidama18
13 | # Setting Message
14 |
15 | def get_text(message: Message) -> [None, str]:
16 | """Extract Text From Commands"""
17 | text_to_return = message.text
18 | if message.text is None:
19 | return None
20 | if " " in text_to_return:
21 | try:
22 | return message.text.split(None, 1)[1]
23 | except IndexError:
24 | return None
25 | else:
26 | return None
27 |
28 | # Preparing For Setting Config
29 | # Implement by https://github.com/jusidama18 and Based on this https://github.com/DevsExpo/FridayUserbot/blob/master/plugins/heroku_helpers.py
30 |
31 | heroku_client = None
32 | if HEROKU_API_KEY:
33 | heroku_client = heroku3.from_key(HEROKU_API_KEY)
34 |
35 | def check_heroku(func):
36 | @wraps(func)
37 | async def heroku_cli(client, message):
38 | heroku_app = None
39 | if not heroku_client:
40 | await message.reply_text("`Please Add HEROKU_API_KEY Key For This To Function To Work!`", parse_mode="markdown")
41 | elif not HEROKU_APP_NAME:
42 | await message.reply_text("`Please Add HEROKU_APP_NAME For This To Function To Work!`", parse_mode="markdown")
43 | if HEROKU_APP_NAME and heroku_client:
44 | try:
45 | heroku_app = heroku_client.app(HEROKU_APP_NAME)
46 | except:
47 | await message.reply_text(message, "`Heroku Api Key And App Name Doesn't Match!`", parse_mode="markdown")
48 | if heroku_app:
49 | await func(client, message, heroku_app)
50 |
51 | return heroku_cli
52 |
53 | # Preparing For Update Bot
54 | # Implement by https://github.com/jusidama18 and Based on this https://github.com/DevsExpo/FridayUserbot/blob/master/plugins/updater.py
55 |
56 | def fetch_heroku_git_url(api_key, app_name):
57 | if not api_key:
58 | return None
59 | if not app_name:
60 | return None
61 | heroku = heroku3.from_key(api_key)
62 | try:
63 | heroku_applications = heroku.apps()
64 | except:
65 | return None
66 | heroku_app = None
67 | for app in heroku_applications:
68 | if app.name == app_name:
69 | heroku_app = app
70 | break
71 | if not heroku_app:
72 | return None
73 | return heroku_app.git_url.replace("https://", "https://api:" + api_key + "@")
74 |
75 | HEROKU_URL = fetch_heroku_git_url(HEROKU_API_KEY, HEROKU_APP_NAME)
76 |
77 | def post_to_telegraph(a_title: str, content: str) -> str:
78 | """ Create a Telegram Post using HTML Content """
79 | post_client = TelegraphPoster(use_api=True)
80 | auth_name = "slam-mirrorbot"
81 | post_client.create_api_token(auth_name)
82 | post_page = post_client.post(
83 | title=a_title,
84 | author=auth_name,
85 | author_url="https://github.com/breakdowns/slam-mirrorbot",
86 | text=content,
87 | )
88 | return post_page["url"]
89 |
90 |
91 | async def runcmd(cmd: str) -> Tuple[str, str, int, int]:
92 | """ run command in terminal """
93 | args = shlex.split(cmd)
94 | process = await asyncio.create_subprocess_exec(
95 | *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
96 | )
97 | stdout, stderr = await process.communicate()
98 | return (
99 | stdout.decode("utf-8", "replace").strip(),
100 | stderr.decode("utf-8", "replace").strip(),
101 | process.returncode,
102 | process.pid,
103 | )
104 |
105 |
106 | # Solves ValueError: No closing quotation by removing ' or " in file name
107 | def safe_filename(path_):
108 | if path_ is None:
109 | return
110 | safename = path_.replace("'", "").replace('"', "")
111 | if safename != path_:
112 | os.rename(path_, safename)
113 | return safename
114 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/telegram_downloader.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import threading
3 | import time
4 |
5 | from bot import LOGGER, download_dict, download_dict_lock, app
6 | from .download_helper import DownloadHelper
7 | from ..status_utils.telegram_download_status import TelegramDownloadStatus
8 |
9 | global_lock = threading.Lock()
10 | GLOBAL_GID = set()
11 |
12 | logging.getLogger("pyrogram").setLevel(logging.WARNING)
13 |
14 |
15 | class TelegramDownloadHelper(DownloadHelper):
16 | def __init__(self, listener):
17 | super().__init__()
18 | self.__listener = listener
19 | self.__resource_lock = threading.RLock()
20 | self.__name = ""
21 | self.__gid = ''
22 | self.__start_time = time.time()
23 | self._bot = app
24 | self.__is_cancelled = False
25 |
26 | @property
27 | def gid(self):
28 | with self.__resource_lock:
29 | return self.__gid
30 |
31 | @property
32 | def download_speed(self):
33 | with self.__resource_lock:
34 | return self.downloaded_bytes / (time.time() - self.__start_time)
35 |
36 | def __onDownloadStart(self, name, size, file_id):
37 | with download_dict_lock:
38 | download_dict[self.__listener.uid] = TelegramDownloadStatus(self, self.__listener)
39 | with global_lock:
40 | GLOBAL_GID.add(file_id)
41 | with self.__resource_lock:
42 | self.name = name
43 | self.size = size
44 | self.__gid = file_id
45 | self.__listener.onDownloadStarted()
46 |
47 | def __onDownloadProgress(self, current, total):
48 | if self.__is_cancelled:
49 | self.__onDownloadError('Cancelled by user!')
50 | self._bot.stop_transmission()
51 | return
52 | with self.__resource_lock:
53 | self.downloaded_bytes = current
54 | try:
55 | self.progress = current / self.size * 100
56 | except ZeroDivisionError:
57 | self.progress = 0
58 |
59 | def __onDownloadError(self, error):
60 | with global_lock:
61 | try:
62 | GLOBAL_GID.remove(self.gid)
63 | except KeyError:
64 | pass
65 | self.__listener.onDownloadError(error)
66 |
67 | def __onDownloadComplete(self):
68 | with global_lock:
69 | GLOBAL_GID.remove(self.gid)
70 | self.__listener.onDownloadComplete()
71 |
72 | def __download(self, message, path):
73 | download = self._bot.download_media(message,
74 | progress=self.__onDownloadProgress, file_name=path)
75 | if download is not None:
76 | self.__onDownloadComplete()
77 | else:
78 | if not self.__is_cancelled:
79 | self.__onDownloadError('Internal error occurred')
80 |
81 | def add_download(self, message, path, filename):
82 | _message = self._bot.get_messages(message.chat.id, message.message_id)
83 | media = None
84 | media_array = [_message.document, _message.video, _message.audio]
85 | for i in media_array:
86 | if i is not None:
87 | media = i
88 | break
89 | if media is not None:
90 | with global_lock:
91 | # For avoiding locking the thread lock for long time unnecessarily
92 | download = media.file_id not in GLOBAL_GID
93 | if filename == "":
94 | name = media.file_name
95 | else:
96 | name = filename
97 | path = path + name
98 | if download:
99 | self.__onDownloadStart(name, media.file_size, media.file_id)
100 | LOGGER.info(f'Downloading telegram file with id: {media.file_id}')
101 | threading.Thread(target=self.__download, args=(_message, path)).start()
102 | else:
103 | self.__onDownloadError('File already being downloaded!')
104 | else:
105 | self.__onDownloadError('No document in the replied message')
106 |
107 | def cancel_download(self):
108 | LOGGER.info(f'Cancelling download on user request: {self.gid}')
109 | self.__is_cancelled = True
110 |
--------------------------------------------------------------------------------
/extract:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ $# -lt 1 ]; then
4 | echo "Usage: $(basename $0) FILES"
5 | exit 1
6 | fi
7 |
8 | extract() {
9 | arg="$1"
10 | cd "$(dirname "$arg")" || exit
11 | case "$arg" in
12 | *.tar.bz2)
13 | tar xjf "$arg" --one-top-level
14 | local code=$?
15 | ;;
16 | *.tar.gz)
17 | tar xzf "$arg" --one-top-level
18 | local code=$?
19 | ;;
20 | *.bz2)
21 | bunzip2 "$arg"
22 | local code=$?
23 | ;;
24 | *.gz)
25 | gunzip "$arg"
26 | local code=$?
27 | ;;
28 | *.tar)
29 | tar xf "$arg" --one-top-level
30 | local code=$?
31 | ;;
32 | *.tbz2)
33 | (tar xjf "$arg" --one-top-level)
34 | local code=$?
35 | ;;
36 | *.tgz)
37 | tar xzf "$arg" --one-top-level
38 | local code=$?
39 | ;;
40 | *.tar.xz)
41 | a_dir=$(expr "$arg" : '\(.*\).tar.xz')
42 | 7z x "$arg" -o"$a_dir"
43 | local code=$?
44 | ;;
45 | *.zip)
46 | a_dir=$(expr "$arg" : '\(.*\).zip')
47 | 7z x "$arg" -o"$a_dir"
48 | local code=$?
49 | ;;
50 | *.7z)
51 | a_dir=$(expr "$arg" : '\(.*\).7z')
52 | 7z x "$arg" -o"$a_dir"
53 | local code=$?
54 | ;;
55 | *.Z)
56 | uncompress "$arg"
57 | local code=$?
58 | ;;
59 | *.rar)
60 | a_dir=$(expr "$arg" : '\(.*\).rar')
61 | mkdir "$a_dir"
62 | 7z x "$arg" -o"$a_dir"
63 | local code=$?
64 | ;;
65 | *.iso)
66 | a_dir=$(expr "$arg" : '\(.*\).iso')
67 | 7z x "$arg" -o"$a_dir"
68 | local code=$?
69 | ;;
70 | *.wim)
71 | a_dir=$(expr "$arg" : '\(.*\).wim')
72 | 7z x "$arg" -o"$a_dir"
73 | local code=$?
74 | ;;
75 | *.cab)
76 | a_dir=$(expr "$arg" : '\(.*\).cab')
77 | 7z x "$arg" -o"$a_dir"
78 | local code=$?
79 | ;;
80 | *.apm)
81 | a_dir=$(expr "$arg" : '\(.*\).apm')
82 | 7z x "$arg" -o"$a_dir"
83 | local code=$?
84 | ;;
85 | *.arj)
86 | a_dir=$(expr "$arg" : '\(.*\).arj')
87 | 7z x "$arg" -o"$a_dir"
88 | local code=$?
89 | ;;
90 | *.chm)
91 | a_dir=$(expr "$arg" : '\(.*\).chm')
92 | 7z x "$arg" -o"$a_dir"
93 | local code=$?
94 | ;;
95 | *.cpio)
96 | a_dir=$(expr "$arg" : '\(.*\).cpio')
97 | 7z x "$arg" -o"$a_dir"
98 | local code=$?
99 | ;;
100 | *.cramfs)
101 | a_dir=$(expr "$arg" : '\(.*\).cramfs')
102 | 7z x "$arg" -o"$a_dir"
103 | local code=$?
104 | ;;
105 | *.deb)
106 | a_dir=$(expr "$arg" : '\(.*\).deb')
107 | 7z x "$arg" -o"$a_dir"
108 | local code=$?
109 | ;;
110 | *.dmg)
111 | a_dir=$(expr "$arg" : '\(.*\).dmg')
112 | 7z x "$arg" -o"$a_dir"
113 | local code=$?
114 | ;;
115 | *.fat)
116 | a_dir=$(expr "$arg" : '\(.*\).fat')
117 | 7z x "$arg" -o"$a_dir"
118 | local code=$?
119 | ;;
120 | *.hfs)
121 | a_dir=$(expr "$arg" : '\(.*\).hfs')
122 | 7z x "$arg" -o"$a_dir"
123 | local code=$?
124 | ;;
125 | *.lzh)
126 | a_dir=$(expr "$arg" : '\(.*\).lzh')
127 | 7z x "$arg" -o"$a_dir"
128 | local code=$?
129 | ;;
130 | *.lzma)
131 | a_dir=$(expr "$arg" : '\(.*\).lzma')
132 | 7z x "$arg" -o"$a_dir"
133 | local code=$?
134 | ;;
135 | *.lzma2)
136 | a_dir=$(expr "$arg" : '\(.*\).lzma2')
137 | 7z x "$arg" -o"$a_dir"
138 | local code=$?
139 | ;;
140 | *.mbr)
141 | a_dir=$(expr "$arg" : '\(.*\).mbr')
142 | 7z x "$arg" -o"$a_dir"
143 | local code=$?
144 | ;;
145 | *.msi)
146 | a_dir=$(expr "$arg" : '\(.*\).msi')
147 | 7z x "$arg" -o"$a_dir"
148 | local code=$?
149 | ;;
150 | *.mslz)
151 | a_dir=$(expr "$arg" : '\(.*\).mslz')
152 | 7z x "$arg" -o"$a_dir"
153 | local code=$?
154 | ;;
155 | *.nsis)
156 | a_dir=$(expr "$arg" : '\(.*\).nsis')
157 | 7z x "$arg" -o"$a_dir"
158 | local code=$?
159 | ;;
160 | *.ntfs)
161 | a_dir=$(expr "$arg" : '\(.*\).ntfs')
162 | 7z x "$arg" -o"$a_dir"
163 | local code=$?
164 | ;;
165 | *.rpm)
166 | a_dir=$(expr "$arg" : '\(.*\).rpm')
167 | 7z x "$arg" -o"$a_dir"
168 | local code=$?
169 | ;;
170 | *.squashfs)
171 | a_dir=$(expr "$arg" : '\(.*\).squashfs')
172 | 7z x "$arg" -o"$a_dir"
173 | local code=$?
174 | ;;
175 | *.udf)
176 | a_dir=$(expr "$arg" : '\(.*\).udf')
177 | 7z x "$arg" -o"$a_dir"
178 | local code=$?
179 | ;;
180 | *.vhd)
181 | a_dir=$(expr "$arg" : '\(.*\).vhd')
182 | 7z x "$arg" -o"$a_dir"
183 | local code=$?
184 | ;;
185 | *.xar)
186 | a_dir=$(expr "$arg" : '\(.*\).xar')
187 | 7z x "$arg" -o"$a_dir"
188 | local code=$?
189 | ;;
190 | *)
191 | echo "'$arg' cannot be extracted via extract()" 1>&2
192 | exit 1
193 | ;;
194 | esac
195 | cd - || exit $?
196 | exit $code
197 | }
198 |
199 | extract "$1"
200 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/direct_link_generator_license.md:
--------------------------------------------------------------------------------
1 | RAPHIELSCAPE PUBLIC LICENSE
2 | Version 1.c, June 2019
3 |
4 | Copyright (C) 2019 Raphielscape LLC.
5 | Copyright (C) 2019 Devscapes Open Source Holding GmbH.
6 |
7 | Everyone is permitted to copy and distribute verbatim or modified
8 | copies of this license document, and changing it is allowed as long
9 | as the name is changed.
10 |
11 | RAPHIELSCAPE PUBLIC LICENSE
12 | A-1. DEFINITIONS
13 |
14 | 0. “This License” refers to version 1.c of the Raphielscape Public License.
15 |
16 | 1. “Copyright” also means copyright-like laws that apply to other kinds of works.
17 |
18 | 2. “The Work" refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”.
19 | “Licensees” and “recipients” may be individuals or organizations.
20 |
21 | 3. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission,
22 | other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work
23 | or a work “based on” the earlier work.
24 |
25 | 4. Source Form. The “source form” for a work means the preferred form of the work for making modifications to it.
26 | “Object code” means any non-source form of a work.
27 |
28 | The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and
29 | (for an executable work) run the object code and to modify the work, including scripts to control those activities.
30 |
31 | The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.
32 | The Corresponding Source for a work in source code form is that same work.
33 |
34 | 5. "The author" refers to "author" of the code, which is the one that made the particular code which exists inside of
35 | the Corresponding Source.
36 |
37 | 6. "Owner" refers to any parties which is made the early form of the Corresponding Source.
38 |
39 | A-2. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
40 |
41 | 0. You must give any other recipients of the Work or Derivative Works a copy of this License; and
42 |
43 | 1. You must cause any modified files to carry prominent notices stating that You changed the files; and
44 |
45 | 2. You must retain, in the Source form of any Derivative Works that You distribute,
46 | this license, all copyright, patent, trademark, authorships and attribution notices
47 | from the Source form of the Work; and
48 |
49 | 3. Respecting the author and owner of works that are distributed in any way.
50 |
51 | You may add Your own copyright statement to Your modifications and may provide
52 | additional or different license terms and conditions for use, reproduction,
53 | or distribution of Your modifications, or for any such Derivative Works as a whole,
54 | provided Your use, reproduction, and distribution of the Work otherwise complies
55 | with the conditions stated in this License.
56 |
57 | B. DISCLAIMER OF WARRANTY
58 |
59 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
60 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
61 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS
62 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
63 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
64 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
65 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
66 | OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
67 |
68 |
69 | C. REVISED VERSION OF THIS LICENSE
70 |
71 | The Devscapes Open Source Holding GmbH. may publish revised and/or new versions of the
72 | Raphielscape Public License from time to time. Such new versions will be similar in spirit
73 | to the present version, but may differ in detail to address new problems or concerns.
74 |
75 | Each version is given a distinguishing version number. If the Program specifies that a
76 | certain numbered version of the Raphielscape Public License "or any later version" applies to it,
77 | you have the option of following the terms and conditions either of that numbered version or of
78 | any later version published by the Devscapes Open Source Holding GmbH. If the Program does not specify a
79 | version number of the Raphielscape Public License, you may choose any version ever published
80 | by the Devscapes Open Source Holding GmbH.
81 |
82 | END OF LICENSE
--------------------------------------------------------------------------------
/pextract:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [ $# -lt 1 ]; then
4 | echo "Usage: $(basename $0) FILES"
5 | exit 1
6 | fi
7 |
8 | extract() {
9 | arg="$1"
10 | pswd="$2"
11 | cd "$(dirname "$arg")" || exit
12 | case "$arg" in
13 | *.tar.bz2)
14 | tar xjf "$arg" --one-top-level
15 | local code=$?
16 | ;;
17 | *.tar.gz)
18 | tar xzf "$arg" --one-top-level
19 | local code=$?
20 | ;;
21 | *.bz2)
22 | bunzip2 "$arg"
23 | local code=$?
24 | ;;
25 | *.gz)
26 | gunzip "$arg"
27 | local code=$?
28 | ;;
29 | *.tar)
30 | tar xf "$arg" --one-top-level
31 | local code=$?
32 | ;;
33 | *.tbz2)
34 | (tar xjf "$arg" --one-top-level)
35 | local code=$?
36 | ;;
37 | *.tgz)
38 | tar xzf "$arg" --one-top-level
39 | local code=$?
40 | ;;
41 | *.tar.xz)
42 | a_dir=$(expr "$arg" : '\(.*\).tar.xz')
43 | 7z x "$arg" -o"$a_dir" -p"$pswd"
44 | local code=$?
45 | ;;
46 | *.zip)
47 | a_dir=$(expr "$arg" : '\(.*\).zip')
48 | 7z x "$arg" -o"$a_dir" -p"$pswd"
49 | local code=$?
50 | ;;
51 | *.7z)
52 | a_dir=$(expr "$arg" : '\(.*\).7z')
53 | 7z x "$arg" -o"$a_dir" -p"$pswd"
54 | local code=$?
55 | ;;
56 | *.Z)
57 | uncompress "$arg"
58 | local code=$?
59 | ;;
60 | *.rar)
61 | a_dir=$(expr "$arg" : '\(.*\).rar')
62 | mkdir "$a_dir"
63 | 7z x "$arg" -o"$a_dir" -p"$pswd"
64 | local code=$?
65 | ;;
66 | *.iso)
67 | a_dir=$(expr "$arg" : '\(.*\).iso')
68 | 7z x "$arg" -o"$a_dir" -p"$pswd"
69 | local code=$?
70 | ;;
71 | *.wim)
72 | a_dir=$(expr "$arg" : '\(.*\).wim')
73 | 7z x "$arg" -o"$a_dir" -p"$pswd"
74 | local code=$?
75 | ;;
76 | *.cab)
77 | a_dir=$(expr "$arg" : '\(.*\).cab')
78 | 7z x "$arg" -o"$a_dir" -p"$pswd"
79 | local code=$?
80 | ;;
81 | *.apm)
82 | a_dir=$(expr "$arg" : '\(.*\).apm')
83 | 7z x "$arg" -o"$a_dir" -p"$pswd"
84 | local code=$?
85 | ;;
86 | *.arj)
87 | a_dir=$(expr "$arg" : '\(.*\).arj')
88 | 7z x "$arg" -o"$a_dir" -p"$pswd"
89 | local code=$?
90 | ;;
91 | *.chm)
92 | a_dir=$(expr "$arg" : '\(.*\).chm')
93 | 7z x "$arg" -o"$a_dir" -p"$pswd"
94 | local code=$?
95 | ;;
96 | *.cpio)
97 | a_dir=$(expr "$arg" : '\(.*\).cpio')
98 | 7z x "$arg" -o"$a_dir" -p"$pswd"
99 | local code=$?
100 | ;;
101 | *.cramfs)
102 | a_dir=$(expr "$arg" : '\(.*\).cramfs')
103 | 7z x "$arg" -o"$a_dir" -p"$pswd"
104 | local code=$?
105 | ;;
106 | *.deb)
107 | a_dir=$(expr "$arg" : '\(.*\).deb')
108 | 7z x "$arg" -o"$a_dir" -p"$pswd"
109 | local code=$?
110 | ;;
111 | *.dmg)
112 | a_dir=$(expr "$arg" : '\(.*\).dmg')
113 | 7z x "$arg" -o"$a_dir" -p"$pswd"
114 | local code=$?
115 | ;;
116 | *.fat)
117 | a_dir=$(expr "$arg" : '\(.*\).fat')
118 | 7z x "$arg" -o"$a_dir" -p"$pswd"
119 | local code=$?
120 | ;;
121 | *.hfs)
122 | a_dir=$(expr "$arg" : '\(.*\).hfs')
123 | 7z x "$arg" -o"$a_dir" -p"$pswd"
124 | local code=$?
125 | ;;
126 | *.lzh)
127 | a_dir=$(expr "$arg" : '\(.*\).lzh')
128 | 7z x "$arg" -o"$a_dir" -p"$pswd"
129 | local code=$?
130 | ;;
131 | *.lzma)
132 | a_dir=$(expr "$arg" : '\(.*\).lzma')
133 | 7z x "$arg" -o"$a_dir" -p"$pswd"
134 | local code=$?
135 | ;;
136 | *.lzma2)
137 | a_dir=$(expr "$arg" : '\(.*\).lzma2')
138 | 7z x "$arg" -o"$a_dir" -p"$pswd"
139 | local code=$?
140 | ;;
141 | *.mbr)
142 | a_dir=$(expr "$arg" : '\(.*\).mbr')
143 | 7z x "$arg" -o"$a_dir" -p"$pswd"
144 | local code=$?
145 | ;;
146 | *.msi)
147 | a_dir=$(expr "$arg" : '\(.*\).msi')
148 | 7z x "$arg" -o"$a_dir" -p"$pswd"
149 | local code=$?
150 | ;;
151 | *.mslz)
152 | a_dir=$(expr "$arg" : '\(.*\).mslz')
153 | 7z x "$arg" -o"$a_dir" -p"$pswd"
154 | local code=$?
155 | ;;
156 | *.nsis)
157 | a_dir=$(expr "$arg" : '\(.*\).nsis')
158 | 7z x "$arg" -o"$a_dir" -p"$pswd"
159 | local code=$?
160 | ;;
161 | *.ntfs)
162 | a_dir=$(expr "$arg" : '\(.*\).ntfs')
163 | 7z x "$arg" -o"$a_dir" -p"$pswd"
164 | local code=$?
165 | ;;
166 | *.rpm)
167 | a_dir=$(expr "$arg" : '\(.*\).rpm')
168 | 7z x "$arg" -o"$a_dir" -p"$pswd"
169 | local code=$?
170 | ;;
171 | *.squashfs)
172 | a_dir=$(expr "$arg" : '\(.*\).squashfs')
173 | 7z x "$arg" -o"$a_dir" -p"$pswd"
174 | local code=$?
175 | ;;
176 | *.udf)
177 | a_dir=$(expr "$arg" : '\(.*\).udf')
178 | 7z x "$arg" -o"$a_dir" -p"$pswd"
179 | local code=$?
180 | ;;
181 | *.vhd)
182 | a_dir=$(expr "$arg" : '\(.*\).vhd')
183 | 7z x "$arg" -o"$a_dir" -p"$pswd"
184 | local code=$?
185 | ;;
186 | *.xar)
187 | a_dir=$(expr "$arg" : '\(.*\).xar')
188 | 7z x "$arg" -o"$a_dir" -p"$pswd"
189 | local code=$?
190 | ;;
191 | *)
192 | echo "'$arg' cannot be extracted via extract()" 1>&2
193 | exit 1
194 | ;;
195 | esac
196 | cd - || exit $?
197 | exit $code
198 | }
199 |
200 | extract "$1" "$2"
201 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/fs_utils.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from bot import aria2, LOGGER, DOWNLOAD_DIR
3 | import shutil
4 | import os
5 | import pathlib
6 | import magic
7 | import tarfile
8 | from .exceptions import NotSupportedExtractionArchive
9 |
10 |
11 | def clean_download(path: str):
12 | if os.path.exists(path):
13 | LOGGER.info(f"Cleaning download: {path}")
14 | shutil.rmtree(path)
15 |
16 |
17 | def start_cleanup():
18 | try:
19 | shutil.rmtree(DOWNLOAD_DIR)
20 | except FileNotFoundError:
21 | pass
22 |
23 |
24 | def clean_all():
25 | aria2.remove_all(True)
26 | try:
27 | shutil.rmtree(DOWNLOAD_DIR)
28 | except FileNotFoundError:
29 | pass
30 |
31 |
32 | def exit_clean_up(signal, frame):
33 | try:
34 | LOGGER.info("Please wait, while we clean up the downloads and stop running downloads")
35 | clean_all()
36 | sys.exit(0)
37 | except KeyboardInterrupt:
38 | LOGGER.warning("Force Exiting before the cleanup finishes!")
39 | sys.exit(1)
40 |
41 |
42 | def get_path_size(path):
43 | if os.path.isfile(path):
44 | return os.path.getsize(path)
45 | total_size = 0
46 | for root, dirs, files in os.walk(path):
47 | for f in files:
48 | abs_path = os.path.join(root, f)
49 | total_size += os.path.getsize(abs_path)
50 | return total_size
51 |
52 |
53 | def tar(org_path):
54 | tar_path = org_path + ".tar"
55 | path = pathlib.PurePath(org_path)
56 | LOGGER.info(f'Tar: orig_path: {org_path}, tar_path: {tar_path}')
57 | tar = tarfile.open(tar_path, "w")
58 | tar.add(org_path, arcname=os.path.basename(org_path))
59 | tar.close()
60 | return tar_path
61 |
62 |
63 | def get_base_name(orig_path: str):
64 | if orig_path.endswith(".tar.bz2"):
65 | return orig_path.replace(".tar.bz2", "")
66 | elif orig_path.endswith(".tar.gz"):
67 | return orig_path.replace(".tar.gz", "")
68 | elif orig_path.endswith(".bz2"):
69 | return orig_path.replace(".bz2", "")
70 | elif orig_path.endswith(".gz"):
71 | return orig_path.replace(".gz", "")
72 | elif orig_path.endswith(".tar.xz"):
73 | return orig_path.replace(".tar.xz", "")
74 | elif orig_path.endswith(".tar"):
75 | return orig_path.replace(".tar", "")
76 | elif orig_path.endswith(".tbz2"):
77 | return orig_path.replace("tbz2", "")
78 | elif orig_path.endswith(".tgz"):
79 | return orig_path.replace(".tgz", "")
80 | elif orig_path.endswith(".zip"):
81 | return orig_path.replace(".zip", "")
82 | elif orig_path.endswith(".7z"):
83 | return orig_path.replace(".7z", "")
84 | elif orig_path.endswith(".Z"):
85 | return orig_path.replace(".Z", "")
86 | elif orig_path.endswith(".rar"):
87 | return orig_path.replace(".rar", "")
88 | elif orig_path.endswith(".iso"):
89 | return orig_path.replace(".iso", "")
90 | elif orig_path.endswith(".wim"):
91 | return orig_path.replace(".wim", "")
92 | elif orig_path.endswith(".cab"):
93 | return orig_path.replace(".cab", "")
94 | elif orig_path.endswith(".apm"):
95 | return orig_path.replace(".apm", "")
96 | elif orig_path.endswith(".arj"):
97 | return orig_path.replace(".arj", "")
98 | elif orig_path.endswith(".chm"):
99 | return orig_path.replace(".chm", "")
100 | elif orig_path.endswith(".cpio"):
101 | return orig_path.replace(".cpio", "")
102 | elif orig_path.endswith(".cramfs"):
103 | return orig_path.replace(".cramfs", "")
104 | elif orig_path.endswith(".deb"):
105 | return orig_path.replace(".deb", "")
106 | elif orig_path.endswith(".dmg"):
107 | return orig_path.replace(".dmg", "")
108 | elif orig_path.endswith(".fat"):
109 | return orig_path.replace(".fat", "")
110 | elif orig_path.endswith(".hfs"):
111 | return orig_path.replace(".hfs", "")
112 | elif orig_path.endswith(".lzh"):
113 | return orig_path.replace(".lzh", "")
114 | elif orig_path.endswith(".lzma"):
115 | return orig_path.replace(".lzma", "")
116 | elif orig_path.endswith(".lzma2"):
117 | return orig_path.replace(".lzma2", "")
118 | elif orig_path.endswith(".mbr"):
119 | return orig_path.replace(".mbr", "")
120 | elif orig_path.endswith(".msi"):
121 | return orig_path.replace(".msi", "")
122 | elif orig_path.endswith(".mslz"):
123 | return orig_path.replace(".mslz", "")
124 | elif orig_path.endswith(".nsis"):
125 | return orig_path.replace(".nsis", "")
126 | elif orig_path.endswith(".ntfs"):
127 | return orig_path.replace(".ntfs", "")
128 | elif orig_path.endswith(".rpm"):
129 | return orig_path.replace(".rpm", "")
130 | elif orig_path.endswith(".squashfs"):
131 | return orig_path.replace(".squashfs", "")
132 | elif orig_path.endswith(".udf"):
133 | return orig_path.replace(".udf", "")
134 | elif orig_path.endswith(".vhd"):
135 | return orig_path.replace(".vhd", "")
136 | elif orig_path.endswith(".xar"):
137 | return orig_path.replace(".xar", "")
138 | else:
139 | raise NotSupportedExtractionArchive('File format not supported for extraction')
140 |
141 |
142 | def get_mime_type(file_path):
143 | mime = magic.Magic(mime=True)
144 | mime_type = mime.from_file(file_path)
145 | mime_type = mime_type if mime_type else "text/plain"
146 | return mime_type
147 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/aria2_download.py:
--------------------------------------------------------------------------------
1 | from bot import aria2, download_dict_lock, STOP_DUPLICATE_MIRROR, TORRENT_DIRECT_LIMIT
2 | from bot.helper.mirror_utils.upload_utils.gdriveTools import GoogleDriveHelper
3 | from bot.helper.ext_utils.bot_utils import *
4 | from .download_helper import DownloadHelper
5 | from bot.helper.mirror_utils.status_utils.aria_download_status import AriaDownloadStatus
6 | from bot.helper.telegram_helper.message_utils import *
7 | import threading
8 | from aria2p import API
9 | from time import sleep
10 |
11 |
12 | class AriaDownloadHelper(DownloadHelper):
13 |
14 | def __init__(self):
15 | super().__init__()
16 |
17 | @new_thread
18 | def __onDownloadStarted(self, api, gid):
19 | if STOP_DUPLICATE_MIRROR or TORRENT_DIRECT_LIMIT is not None:
20 | sleep(0.5)
21 | dl = getDownloadByGid(gid)
22 | download = api.get_download(gid)
23 |
24 | if STOP_DUPLICATE_MIRROR:
25 | LOGGER.info(f"Checking File/Folder if already in Drive...")
26 | self.name = download.name
27 | sname = download.name
28 | if self.listener.isTar:
29 | sname = sname + ".tar"
30 | if self.listener.extract:
31 | smsg = None
32 | else:
33 | gdrive = GoogleDriveHelper(None)
34 | smsg, button = gdrive.drive_list(sname)
35 | if smsg:
36 | aria2.remove([download])
37 | dl.getListener().onDownloadError(f'File/Folder is already available in Drive.\n\n')
38 | sendMarkup("Here are the search results:", dl.getListener().bot, dl.getListener().update, button)
39 | return
40 |
41 | if TORRENT_DIRECT_LIMIT is not None:
42 | LOGGER.info(f"Checking File/Folder Size...")
43 | sleep(1.5)
44 | size = aria2.get_download(gid).total_length
45 | limit = TORRENT_DIRECT_LIMIT
46 | limit = limit.split(' ', maxsplit=1)
47 | limitint = int(limit[0])
48 | if 'GB' in limit or 'gb' in limit:
49 | if size > limitint * 1024**3:
50 | aria2.remove([download])
51 | dl.getListener().onDownloadError(f'Torrent/Direct limit is {TORRENT_DIRECT_LIMIT}.\nYour File/Folder size is {get_readable_file_size(size)}')
52 | return
53 | elif 'TB' in limit or 'tb' in limit:
54 | if size > limitint * 1024**4:
55 | aria2.remove([download])
56 | dl.getListener().onDownloadError(f'Torrent/Direct limit is {TORRENT_DIRECT_LIMIT}.\nYour File/Folder size is {get_readable_file_size(size)}')
57 | return
58 | update_all_messages()
59 |
60 | def __onDownloadComplete(self, api: API, gid):
61 | LOGGER.info(f"onDownloadComplete: {gid}")
62 | dl = getDownloadByGid(gid)
63 | download = api.get_download(gid)
64 | if download.followed_by_ids:
65 | new_gid = download.followed_by_ids[0]
66 | new_download = api.get_download(new_gid)
67 | with download_dict_lock:
68 | sleep(0.5)
69 | download_dict[dl.uid()] = AriaDownloadStatus(new_gid, dl.getListener())
70 | if new_download.is_torrent:
71 | download_dict[dl.uid()].is_torrent = True
72 | update_all_messages()
73 | LOGGER.info(f'Changed gid from {gid} to {new_gid}')
74 | else:
75 | if dl:
76 | threading.Thread(target=dl.getListener().onDownloadComplete).start()
77 |
78 | @new_thread
79 | def __onDownloadStopped(self, api, gid):
80 | sleep(0.5)
81 | dl = getDownloadByGid(gid)
82 | if dl:
83 | dl.getListener().onDownloadError('Dead torrent!')
84 |
85 | @new_thread
86 | def __onDownloadError(self, api, gid):
87 | sleep(0.5) # sleep for split second to ensure proper dl gid update from onDownloadComplete
88 | dl = getDownloadByGid(gid)
89 | download = api.get_download(gid)
90 | error = download.error_message
91 | LOGGER.info(f"Download Error: {error}")
92 | if dl:
93 | dl.getListener().onDownloadError(error)
94 |
95 | def start_listener(self):
96 | aria2.listen_to_notifications(threaded=True, on_download_start=self.__onDownloadStarted,
97 | on_download_error=self.__onDownloadError,
98 | on_download_stop=self.__onDownloadStopped,
99 | on_download_complete=self.__onDownloadComplete)
100 |
101 | def add_download(self, link: str, path, listener, filename):
102 | if is_magnet(link):
103 | download = aria2.add_magnet(link, {'dir': path, 'out': filename})
104 | else:
105 | download = aria2.add_uris([link], {'dir': path, 'out': filename})
106 | if download.error_message: # no need to proceed further at this point
107 | listener.onDownloadError(download.error_message)
108 | return
109 | with download_dict_lock:
110 | download_dict[listener.uid] = AriaDownloadStatus(download.gid, listener)
111 | LOGGER.info(f"Started: {download.gid} DIR:{download.dir} ")
112 | self.listener = listener
113 |
--------------------------------------------------------------------------------
/bot/modules/authorize.py:
--------------------------------------------------------------------------------
1 | from bot.helper.telegram_helper.message_utils import sendMessage
2 | from bot import AUTHORIZED_CHATS, SUDO_USERS, dispatcher
3 | from telegram.ext import CommandHandler
4 | from bot.helper.telegram_helper.filters import CustomFilters
5 | from telegram.ext import Filters
6 | from telegram import Update
7 | from bot.helper.telegram_helper.bot_commands import BotCommands
8 | from bot.helper.ext_utils.db_handler import DbManger
9 |
10 |
11 | def authorize(update, context):
12 | reply_message = None
13 | message_ = None
14 | reply_message = update.message.reply_to_message
15 | message_ = update.message.text.split(' ')
16 | if len(message_) == 2:
17 | chat_id = int(message_[1])
18 | if chat_id not in AUTHORIZED_CHATS:
19 | msg = DbManger().db_auth(chat_id)
20 | else:
21 | msg = 'User already authorized'
22 | else:
23 | if reply_message is None:
24 | # Trying to authorize a chat
25 | chat_id = update.effective_chat.id
26 | if chat_id not in AUTHORIZED_CHATS:
27 | msg = DbManger().db_auth(chat_id)
28 | else:
29 | msg = 'Already authorized chat'
30 |
31 | else:
32 | # Trying to authorize someone in specific
33 | user_id = reply_message.from_user.id
34 | if user_id not in AUTHORIZED_CHATS:
35 | msg = DbManger().db_auth(user_id)
36 | else:
37 | msg = 'User already authorized'
38 | sendMessage(msg, context.bot, update)
39 |
40 |
41 | def unauthorize(update, context):
42 | reply_message = None
43 | message_ = None
44 | reply_message = update.message.reply_to_message
45 | message_ = update.message.text.split(' ')
46 | if len(message_) == 2:
47 | chat_id = int(message_[1])
48 | if chat_id in AUTHORIZED_CHATS:
49 | msg = DbManger().db_unauth(chat_id)
50 | else:
51 | msg = 'User already unauthorized'
52 | else:
53 | if reply_message is None:
54 | # Trying to unauthorize a chat
55 | chat_id = update.effective_chat.id
56 | if chat_id in AUTHORIZED_CHATS:
57 | msg = DbManger().db_unauth(chat_id)
58 | else:
59 | msg = 'Already unauthorized chat'
60 | else:
61 | # Trying to authorize someone in specific
62 | user_id = reply_message.from_user.id
63 | if user_id in AUTHORIZED_CHATS:
64 | msg = DbManger().db_unauth(user_id)
65 | else:
66 | msg = 'User already unauthorized'
67 | sendMessage(msg, context.bot, update)
68 |
69 |
70 | def addSudo(update, context):
71 | reply_message = None
72 | message_ = None
73 | reply_message = update.message.reply_to_message
74 | message_ = update.message.text.split(' ')
75 | if len(message_) == 2:
76 | chat_id = int(message_[1])
77 | if chat_id not in SUDO_USERS:
78 | msg = DbManger().db_addsudo(chat_id)
79 | else:
80 | msg = 'Already Sudo'
81 | else:
82 | if reply_message is None:
83 | msg = "Give ID or Reply To message of whom you want to Promote"
84 | else:
85 | # Trying to authorize someone in specific
86 | user_id = reply_message.from_user.id
87 | if user_id not in SUDO_USERS:
88 | msg = DbManger().db_addsudo(user_id)
89 | else:
90 | msg = 'Already Sudo'
91 | sendMessage(msg, context.bot, update)
92 |
93 |
94 | def removeSudo(update, context):
95 | reply_message = None
96 | message_ = None
97 | reply_message = update.message.reply_to_message
98 | message_ = update.message.text.split(' ')
99 | if len(message_) == 2:
100 | chat_id = int(message_[1])
101 | if chat_id in SUDO_USERS:
102 | msg = DbManger().db_rmsudo(chat_id)
103 | else:
104 | msg = 'Not a Sudo'
105 | else:
106 | if reply_message is None:
107 | msg = "Give ID or Reply To message of whom you want to remove from Sudo"
108 | else:
109 | user_id = reply_message.from_user.id
110 | if user_id in SUDO_USERS:
111 | msg = DbManger().db_rmsudo(user_id)
112 | else:
113 | msg = 'Not a Sudo'
114 | sendMessage(msg, context.bot, update)
115 |
116 |
117 | def sendAuthChats(update, context):
118 | user = sudo = ''
119 | user += '\n'.join(str(id) for id in AUTHORIZED_CHATS)
120 | sudo += '\n'.join(str(id) for id in SUDO_USERS)
121 | sendMessage(f'Authorized Chats\n{user}\nSudo Users\n{sudo}', context.bot, update)
122 |
123 |
124 | send_auth_handler = CommandHandler(command=BotCommands.AuthorizedUsersCommand, callback=sendAuthChats,
125 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
126 | authorize_handler = CommandHandler(command=BotCommands.AuthorizeCommand, callback=authorize,
127 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
128 | unauthorize_handler = CommandHandler(command=BotCommands.UnAuthorizeCommand, callback=unauthorize,
129 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
130 | addsudo_handler = CommandHandler(command=BotCommands.AddSudoCommand, callback=addSudo,
131 | filters=CustomFilters.owner_filter, run_async=True)
132 | removesudo_handler = CommandHandler(command=BotCommands.RmSudoCommand, callback=removeSudo,
133 | filters=CustomFilters.owner_filter, run_async=True)
134 |
135 | dispatcher.add_handler(send_auth_handler)
136 | dispatcher.add_handler(authorize_handler)
137 | dispatcher.add_handler(unauthorize_handler)
138 | dispatcher.add_handler(addsudo_handler)
139 | dispatcher.add_handler(removesudo_handler)
140 |
--------------------------------------------------------------------------------
/bot/helper/ext_utils/bot_utils.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import re
3 | import threading
4 | import time
5 |
6 | from bot.helper.telegram_helper.bot_commands import BotCommands
7 | from bot import download_dict, download_dict_lock
8 |
9 | LOGGER = logging.getLogger(__name__)
10 |
11 | MAGNET_REGEX = r"magnet:\?xt=urn:btih:[a-zA-Z0-9]*"
12 |
13 | URL_REGEX = r"(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+"
14 |
15 |
16 | class MirrorStatus:
17 | STATUS_UPLOADING = "Uploading...📤"
18 | STATUS_DOWNLOADING = "Downloading...📥"
19 | STATUS_WAITING = "Queued...📝"
20 | STATUS_FAILED = "Failed 🚫. Cleaning Download..."
21 | STATUS_ARCHIVING = "Archiving...🔐"
22 | STATUS_EXTRACTING = "Extracting...📂"
23 |
24 |
25 | PROGRESS_MAX_SIZE = 100 // 8
26 | PROGRESS_INCOMPLETE = ['▏', '▎', '▍', '▌', '▋', '▊', '▉']
27 |
28 | SIZE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
29 |
30 |
31 | class setInterval:
32 | def __init__(self, interval, action):
33 | self.interval = interval
34 | self.action = action
35 | self.stopEvent = threading.Event()
36 | thread = threading.Thread(target=self.__setInterval)
37 | thread.start()
38 |
39 | def __setInterval(self):
40 | nextTime = time.time() + self.interval
41 | while not self.stopEvent.wait(nextTime - time.time()):
42 | nextTime += self.interval
43 | self.action()
44 |
45 | def cancel(self):
46 | self.stopEvent.set()
47 |
48 |
49 | def get_readable_file_size(size_in_bytes) -> str:
50 | if size_in_bytes is None:
51 | return '0B'
52 | index = 0
53 | while size_in_bytes >= 1024:
54 | size_in_bytes /= 1024
55 | index += 1
56 | try:
57 | return f'{round(size_in_bytes, 2)}{SIZE_UNITS[index]}'
58 | except IndexError:
59 | return 'File too large'
60 |
61 |
62 | def getDownloadByGid(gid):
63 | with download_dict_lock:
64 | for dl in download_dict.values():
65 | status = dl.status()
66 | if status != MirrorStatus.STATUS_UPLOADING and status != MirrorStatus.STATUS_ARCHIVING \
67 | and status != MirrorStatus.STATUS_EXTRACTING:
68 | if dl.gid() == gid:
69 | return dl
70 | return None
71 |
72 | def getAllDownload():
73 | with download_dict_lock:
74 | for dlDetails in list(download_dict.values()):
75 | if dlDetails.status() == MirrorStatus.STATUS_DOWNLOADING \
76 | or dlDetails.status() == MirrorStatus.STATUS_WAITING:
77 | if dlDetails:
78 | return dlDetails
79 |
80 | def get_progress_bar_string(status):
81 | completed = status.processed_bytes() / 8
82 | total = status.size_raw() / 8
83 | if total == 0:
84 | p = 0
85 | else:
86 | p = round(completed * 100 / total)
87 | p = min(max(p, 0), 100)
88 | cFull = p // 8
89 | cPart = p % 8 - 1
90 | p_str = '█' * cFull
91 | if cPart >= 0:
92 | p_str += PROGRESS_INCOMPLETE[cPart]
93 | p_str += ' ' * (PROGRESS_MAX_SIZE - cFull)
94 | p_str = f"[{p_str}]"
95 | return p_str
96 |
97 |
98 | def get_readable_message():
99 | with download_dict_lock:
100 | msg = ""
101 | for download in list(download_dict.values()):
102 | msg += f"Filename: {download.name()}"
103 | msg += f"\nStatus: {download.status()}"
104 | if download.status() != MirrorStatus.STATUS_ARCHIVING and download.status() != MirrorStatus.STATUS_EXTRACTING:
105 | msg += f"\n{get_progress_bar_string(download)} {download.progress()}"
106 | if download.status() == MirrorStatus.STATUS_DOWNLOADING:
107 | msg += f"\n😻Downloaded: {get_readable_file_size(download.processed_bytes())} of {download.size()}"
108 | else:
109 | msg += f"\nUploaded: {get_readable_file_size(download.processed_bytes())} of {download.size()}"
110 | msg += f"\n⚡️Speed: {download.speed()}\n⏱ETA: {download.eta()} "
111 | # if hasattr(download, 'is_torrent'):
112 | try:
113 | msg += f"\n🌾Seeders: {download.aria_download().num_seeders}" \
114 | f" | 🥀Peers: {download.aria_download().connections}"
115 | except:
116 | pass
117 | if download.status() == MirrorStatus.STATUS_DOWNLOADING:
118 | msg += f"\nTo Stop🚫: /{BotCommands.CancelMirror} {download.gid()}"
119 | msg += "\n\n"
120 | return msg
121 |
122 |
123 | def get_readable_time(seconds: int) -> str:
124 | result = ''
125 | (days, remainder) = divmod(seconds, 86400)
126 | days = int(days)
127 | if days != 0:
128 | result += f'{days}d'
129 | (hours, remainder) = divmod(remainder, 3600)
130 | hours = int(hours)
131 | if hours != 0:
132 | result += f'{hours}h'
133 | (minutes, seconds) = divmod(remainder, 60)
134 | minutes = int(minutes)
135 | if minutes != 0:
136 | result += f'{minutes}m'
137 | seconds = int(seconds)
138 | result += f'{seconds}s'
139 | return result
140 |
141 |
142 | def is_url(url: str):
143 | url = re.findall(URL_REGEX, url)
144 | if url:
145 | return True
146 | return False
147 |
148 | def is_gdrive_link(url: str):
149 | return "drive.google.com" in url
150 |
151 | def is_mega_link(url: str):
152 | return "mega.nz" in url
153 |
154 | def get_mega_link_type(url: str):
155 | if "folder" in url:
156 | return "folder"
157 | elif "file" in url:
158 | return "file"
159 | elif "/#F!" in url:
160 | return "folder"
161 | return "file"
162 |
163 | def is_magnet(url: str):
164 | magnet = re.findall(MAGNET_REGEX, url)
165 | if magnet:
166 | return True
167 | return False
168 |
169 | def new_thread(fn):
170 | """To use as decorator to make a function call threaded.
171 | Needs import
172 | from threading import Thread"""
173 |
174 | def wrapper(*args, **kwargs):
175 | thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
176 | thread.start()
177 | return thread
178 |
179 | return wrapper
180 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/youtube_dl_download_helper.py:
--------------------------------------------------------------------------------
1 | from .download_helper import DownloadHelper
2 | import time
3 | from youtube_dl import YoutubeDL, DownloadError
4 | from bot import download_dict_lock, download_dict
5 | from ..status_utils.youtube_dl_download_status import YoutubeDLDownloadStatus
6 | import logging
7 | import re
8 | import threading
9 |
10 | LOGGER = logging.getLogger(__name__)
11 |
12 |
13 | class MyLogger:
14 | def __init__(self, obj):
15 | self.obj = obj
16 |
17 | def debug(self, msg):
18 | LOGGER.debug(msg)
19 | # Hack to fix changing changing extension
20 | match = re.search(r'.ffmpeg..Merging formats into..(.*?).$', msg)
21 | if match and not self.obj.is_playlist:
22 | newname = match.group(1)
23 | newname = newname.split("/")
24 | newname = newname[-1]
25 | self.obj.name = newname
26 |
27 | @staticmethod
28 | def warning(msg):
29 | LOGGER.warning(msg)
30 |
31 | @staticmethod
32 | def error(msg):
33 | LOGGER.error(msg)
34 |
35 |
36 | class YoutubeDLHelper(DownloadHelper):
37 | def __init__(self, listener):
38 | super().__init__()
39 | self.name = ""
40 | self.__start_time = time.time()
41 | self.__listener = listener
42 | self.__gid = ""
43 | self.opts = {
44 | 'progress_hooks': [self.__onDownloadProgress],
45 | 'logger': MyLogger(self),
46 | 'usenetrc': True
47 | }
48 | self.__download_speed = 0
49 | self.download_speed_readable = ''
50 | self.downloaded_bytes = 0
51 | self.size = 0
52 | self.is_playlist = False
53 | self.last_downloaded = 0
54 | self.is_cancelled = False
55 | self.vid_id = ''
56 | self.__resource_lock = threading.RLock()
57 |
58 | @property
59 | def download_speed(self):
60 | with self.__resource_lock:
61 | return self.__download_speed
62 |
63 | @property
64 | def gid(self):
65 | with self.__resource_lock:
66 | return self.__gid
67 |
68 | def __onDownloadProgress(self, d):
69 | if self.is_cancelled:
70 | raise ValueError("Cancelling Download..")
71 | if d['status'] == "finished":
72 | if self.is_playlist:
73 | self.last_downloaded = 0
74 | elif d['status'] == "downloading":
75 | with self.__resource_lock:
76 | self.__download_speed = d['speed']
77 | try:
78 | tbyte = d['total_bytes']
79 | except KeyError:
80 | tbyte = d['total_bytes_estimate']
81 | if self.is_playlist:
82 | progress = d['downloaded_bytes'] / tbyte
83 | chunk_size = d['downloaded_bytes'] - self.last_downloaded
84 | self.last_downloaded = tbyte * progress
85 | self.downloaded_bytes += chunk_size
86 | try:
87 | self.progress = (self.downloaded_bytes / self.size) * 100
88 | except ZeroDivisionError:
89 | pass
90 | else:
91 | self.download_speed_readable = d['_speed_str']
92 | self.downloaded_bytes = d['downloaded_bytes']
93 |
94 | def __onDownloadStart(self):
95 | with download_dict_lock:
96 | download_dict[self.__listener.uid] = YoutubeDLDownloadStatus(self, self.__listener)
97 |
98 | def __onDownloadComplete(self):
99 | self.__listener.onDownloadComplete()
100 |
101 | def onDownloadError(self, error):
102 | self.__listener.onDownloadError(error)
103 |
104 | def extractMetaData(self, link, qual, name):
105 | if "hotstar" in link or "sonyliv" in link:
106 | self.opts['geo_bypass_country'] = 'IN'
107 |
108 | with YoutubeDL(self.opts) as ydl:
109 | try:
110 | result = ydl.extract_info(link, download=False)
111 | if name == "":
112 | name = ydl.prepare_filename(result)
113 | else:
114 | name = name
115 | # noobway hack for changing extension after converting to mp3
116 | if qual == "audio":
117 | name = name.replace(".mp4", ".mp3").replace(".webm", ".mp3")
118 | except DownloadError as e:
119 | self.onDownloadError(str(e))
120 | return
121 | if result.get('direct'):
122 | return None
123 | if 'entries' in result:
124 | video = result['entries'][0]
125 | for v in result['entries']:
126 | if v and v.get('filesize'):
127 | self.size += float(v['filesize'])
128 | # For playlists, ydl.prepare-filename returns the following format: {uptime}", context.bot, update)
64 | else :
65 | update.effective_message.reply_photo(IMAGE_URL, start_string, parse_mode=ParseMode.MARKDOWN, reply_markup=reply_markup)
66 | else :
67 | sendMessage(f"Oops! not a Authorized user.", context.bot, update)
68 |
69 |
70 | def restart(update, context):
71 | restart_message = sendMessage("Restarting, Please wait!", context.bot, update)
72 | # Save restart message object in order to reply to it after restarting
73 | with open(".restartmsg", "w") as f:
74 | f.truncate(0)
75 | f.write(f"{restart_message.chat.id}\n{restart_message.message_id}\n")
76 | fs_utils.clean_all()
77 | os.execl(executable, executable, "-m", "bot")
78 |
79 |
80 | def ping(update, context):
81 | start_time = int(round(time.time() * 1000))
82 | reply = sendMessage("Starting Ping", context.bot, update)
83 | end_time = int(round(time.time() * 1000))
84 | editMessage(f'{end_time - start_time} ms', reply)
85 |
86 |
87 | def log(update, context):
88 | sendLogFile(context.bot, update)
89 |
90 |
91 | def bot_help(update, context):
92 | help_string_adm = f'''
93 | /{BotCommands.HelpCommand}: To get this message
94 |
95 | /{BotCommands.MirrorCommand} [download_url][magnet_link]: Start mirroring the link to Google Drive.
96 |
97 | /{BotCommands.UnzipMirrorCommand} [download_url][magnet_link]: Starts mirroring and if downloaded file is any archive, extracts it to Google Drive
98 |
99 | /{BotCommands.TarMirrorCommand} [download_url][magnet_link]: Start mirroring and upload the archived (.tar) version of the download
100 |
101 | /{BotCommands.CloneCommand}: Copy file/folder to Google Drive
102 |
103 | /{BotCommands.CountCommand}: Count file/folder of Google Drive Links
104 |
105 | /{BotCommands.DeleteCommand} [link]: Delete file from Google Drive (Only Owner & Sudo)
106 |
107 | /{BotCommands.WatchCommand} [youtube-dl supported link]: Mirror through youtube-dl. Click /{BotCommands.WatchCommand} for more help.
108 |
109 | /{BotCommands.TarWatchCommand} [youtube-dl supported link]: Mirror through youtube-dl and tar before uploading
110 |
111 | /{BotCommands.CancelMirror}: Reply to the message by which the download was initiated and that download will be cancelled
112 |
113 | /{BotCommands.StatusCommand}: Shows a status of all the downloads
114 |
115 | /{BotCommands.ListCommand} [search term]: Searches the search term in the Google Drive, if found replies with the link
116 |
117 | /{BotCommands.StatsCommand}: Show Stats of the machine the bot is hosted on
118 |
119 | /{BotCommands.AuthorizeCommand}: Authorize a chat or a user to use the bot (Can only be invoked by Owner & Sudo of the bot)
120 |
121 | /{BotCommands.UnAuthorizeCommand}: Unauthorize a chat or a user to use the bot (Can only be invoked by Owner & Sudo of the bot)
122 |
123 | /{BotCommands.AuthorizedUsersCommand}: Show authorized users (Only Owner & Sudo)
124 |
125 | /{BotCommands.AddSudoCommand}: Add sudo user (Only Owner)
126 |
127 | /{BotCommands.RmSudoCommand}: Remove sudo users (Only Owner)
128 |
129 | /{BotCommands.LogCommand}: Get a log file of the bot. Handy for getting crash reports
130 |
131 | /{BotCommands.ConfigMenuCommand}: Get Info Menu about bot config (Owner Only).
132 |
133 | /{BotCommands.UpdateCommand}: Update Bot from Upstream Repo. (Owner Only).
134 |
135 | /{BotCommands.UsageCommand}: To see Heroku Dyno Stats (Owner & Sudo only).
136 |
137 | /{BotCommands.SpeedCommand}: Check Internet Speed of the Host
138 |
139 | /{BotCommands.MediaInfoCommand}: Get detailed info about replied media (Only for Telegram file).
140 |
141 | /{BotCommands.ShellCommand}: Run commands in Shell (Terminal).
142 |
143 | /tshelp: Get help for Torrent search module.
144 | '''
145 |
146 | help_string = f'''
147 | /{BotCommands.HelpCommand}: To get this message
148 |
149 | /{BotCommands.MirrorCommand} [download_url][magnet_link]: Start mirroring the link to Google Drive.
150 |
151 | /{BotCommands.UnzipMirrorCommand} [download_url][magnet_link]: Starts mirroring and if downloaded file is any archive, extracts it to Google Drive
152 |
153 | /{BotCommands.TarMirrorCommand} [download_url][magnet_link]: Start mirroring and upload the archived (.tar) version of the download
154 |
155 | /{BotCommands.CloneCommand}: Copy file/folder to Google Drive
156 |
157 | /{BotCommands.CountCommand}: Count file/folder of Google Drive Links
158 |
159 | /{BotCommands.WatchCommand} [youtube-dl supported link]: Mirror through youtube-dl. Click /{BotCommands.WatchCommand} for more help.
160 |
161 | /{BotCommands.TarWatchCommand} [youtube-dl supported link]: Mirror through youtube-dl and tar before uploading
162 |
163 | /{BotCommands.CancelMirror}: Reply to the message by which the download was initiated and that download will be cancelled
164 |
165 | /{BotCommands.StatusCommand}: Shows a status of all the downloads
166 |
167 | /{BotCommands.ListCommand} [search term]: Searches the search term in the Google Drive, if found replies with the link
168 |
169 | /{BotCommands.StatsCommand}: Show Stats of the machine the bot is hosted on
170 |
171 | /{BotCommands.SpeedCommand}: Check Internet Speed of the Host
172 |
173 | /{BotCommands.MediaInfoCommand}: Get detailed info about replied media (Only for Telegram file).
174 |
175 | /tshelp: Get help for Torrent search module.
176 | '''
177 |
178 | if CustomFilters.sudo_user(update) or CustomFilters.owner_filter(update):
179 | sendMessage(help_string_adm, context.bot, update)
180 | else:
181 | sendMessage(help_string, context.bot, update)
182 |
183 |
184 | botcmds = [
185 | BotCommand(f'{BotCommands.MirrorCommand}', 'Start Mirroring'),
186 | BotCommand(f'{BotCommands.TarMirrorCommand}','Upload tar (zipped) file'),
187 | BotCommand(f'{BotCommands.UnzipMirrorCommand}','Extract files'),
188 | BotCommand(f'{BotCommands.CloneCommand}','Copy file/folder to Drive'),
189 | BotCommand(f'{BotCommands.CountCommand}','Count file/folder of Drive link'),
190 | BotCommand(f'{BotCommands.WatchCommand}','Mirror YT-DL support link'),
191 | BotCommand(f'{BotCommands.TarWatchCommand}','Mirror Youtube playlist link as tar'),
192 | BotCommand(f'{BotCommands.CancelMirror}','Cancel a task'),
193 | BotCommand(f'{BotCommands.CancelAllCommand}','Cancel all tasks'),
194 | BotCommand(f'{BotCommands.DeleteCommand}','Delete file from Drive'),
195 | BotCommand(f'{BotCommands.ListCommand}',' [query] Searches files in Drive'),
196 | BotCommand(f'{BotCommands.StatusCommand}','Get Mirror Status message'),
197 | BotCommand(f'{BotCommands.StatsCommand}','Bot Usage Stats'),
198 | BotCommand(f'{BotCommands.HelpCommand}','Get Detailed Help'),
199 | BotCommand(f'{BotCommands.MediaInfoCommand}','Get detailed info about replied media'),
200 | BotCommand(f'{BotCommands.SpeedCommand}','Check Speed of the host'),
201 | BotCommand(f'{BotCommands.LogCommand}','Bot Log [owner/sudo only]'),
202 | BotCommand(f'{BotCommands.RestartCommand}','Restart bot [owner/sudo only]')]
203 |
204 |
205 | def main():
206 | fs_utils.start_cleanup()
207 | # Check if the bot is restarting
208 | if os.path.isfile(".restartmsg"):
209 | with open(".restartmsg") as f:
210 | chat_id, msg_id = map(int, f)
211 | bot.edit_message_text("Restarted successfully!", chat_id, msg_id)
212 | os.remove(".restartmsg")
213 | bot.set_my_commands(botcmds)
214 |
215 | start_handler = CommandHandler(BotCommands.StartCommand, start, run_async=True)
216 | ping_handler = CommandHandler(BotCommands.PingCommand, ping,
217 | filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
218 | restart_handler = CommandHandler(BotCommands.RestartCommand, restart,
219 | filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
220 | help_handler = CommandHandler(BotCommands.HelpCommand,
221 | bot_help, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
222 | stats_handler = CommandHandler(BotCommands.StatsCommand,
223 | stats, filters=CustomFilters.authorized_chat | CustomFilters.authorized_user, run_async=True)
224 | log_handler = CommandHandler(BotCommands.LogCommand, log, filters=CustomFilters.owner_filter | CustomFilters.sudo_user, run_async=True)
225 | dispatcher.add_handler(start_handler)
226 | dispatcher.add_handler(ping_handler)
227 | dispatcher.add_handler(restart_handler)
228 | dispatcher.add_handler(help_handler)
229 | dispatcher.add_handler(stats_handler)
230 | dispatcher.add_handler(log_handler)
231 | updater.start_polling(drop_pending_updates=IGNORE_PENDING_REQUESTS)
232 | LOGGER.info("Bot Started!")
233 | signal.signal(signal.SIGINT, fs_utils.exit_clean_up)
234 |
235 | app.start()
236 | main()
237 | idle()
238 |
--------------------------------------------------------------------------------
/bot/helper/mirror_utils/download_utils/direct_link_generator.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2019 The Raphielscape Company LLC.
2 | #
3 | # Licensed under the Raphielscape Public License, Version 1.c (the "License");
4 | # you may not use this file except in compliance with the License.
5 | #
6 | """ Helper Module containing various sites direct links generators. This module is copied and modified as per need
7 | from https://github.com/AvinashReddy3108/PaperplaneExtended . I hereby take no credit of the following code other
8 | than the modifications. See https://github.com/AvinashReddy3108/PaperplaneExtended/commits/master/userbot/modules/direct_links.py
9 | for original authorship. """
10 |
11 | from bot import UPTOBOX_TOKEN
12 | import logging
13 | import json
14 | import math
15 | import re
16 | import urllib.parse
17 | from os import popen
18 | from random import choice
19 | from urllib.parse import urlparse
20 |
21 | import lk21
22 | import requests
23 | from bs4 import BeautifulSoup
24 | from js2py import EvalJs
25 | from lk21.extractors.bypasser import Bypass
26 | from base64 import standard_b64encode
27 | from bot.helper.telegram_helper.bot_commands import BotCommands
28 | from bot.helper.ext_utils.exceptions import DirectDownloadLinkException
29 |
30 |
31 | def direct_link_generator(link: str):
32 | """ direct links generator """
33 | if not link:
34 | raise DirectDownloadLinkException("`No links found!`")
35 | elif 'youtube.com' in link or 'youtu.be' in link:
36 | raise DirectDownloadLinkException(f"Youtube Link use /{BotCommands.WatchCommand} or /{BotCommands.TarWatchCommand}")
37 | elif 'zippyshare.com' in link:
38 | return zippy_share(link)
39 | elif 'yadi.sk' in link:
40 | return yandex_disk(link)
41 | elif 'cloud.mail.ru' in link:
42 | return cm_ru(link)
43 | elif 'mediafire.com' in link:
44 | return mediafire(link)
45 | elif 'uptobox.com' in link:
46 | return uptobox(link)
47 | elif 'osdn.net' in link:
48 | return osdn(link)
49 | elif 'github.com' in link:
50 | return github(link)
51 | elif 'hxfile.co' in link:
52 | return hxfile(link)
53 | elif 'anonfiles.com' in link:
54 | return anon(link)
55 | elif 'letsupload.io' in link:
56 | return letsupload(link)
57 | elif 'fembed.com' in link:
58 | return fembed(link)
59 | elif 'femax20.com' in link:
60 | return fembed(link)
61 | elif 'feurl.com' in link:
62 | return fembed(link)
63 | elif 'naniplay.nanime.in' in link:
64 | return fembed(link)
65 | elif 'naniplay.nanime.biz' in link:
66 | return fembed(link)
67 | elif 'naniplay.com' in link:
68 | return fembed(link)
69 | elif 'layarkacaxxi.icu' in link:
70 | return fembed(link)
71 | elif 'sbembed.com' in link:
72 | return sbembed(link)
73 | elif 'streamsb.net' in link:
74 | return sbembed(link)
75 | elif '1drv.ms' in link:
76 | return onedrive(link)
77 | elif 'pixeldrain.com' in link:
78 | return pixeldrain(link)
79 | else:
80 | raise DirectDownloadLinkException(f'No Direct link function found for {link}')
81 |
82 |
83 | def zippy_share(url: str) -> str:
84 | """ ZippyShare direct links generator
85 | Based on https://github.com/KenHV/Mirror-Bot """
86 | link = re.findall("https:/.(.*?).zippyshare", url)[0]
87 | response_content = (requests.get(url)).content
88 | bs_obj = BeautifulSoup(response_content, "lxml")
89 |
90 | try:
91 | js_script = bs_obj.find("div", {"class": "center",}).find_all(
92 | "script"
93 | )[1]
94 | except:
95 | js_script = bs_obj.find("div", {"class": "right",}).find_all(
96 | "script"
97 | )[0]
98 |
99 | js_content = re.findall(r'\.href.=."/(.*?)";', str(js_script))
100 | js_content = 'var x = "/' + js_content[0] + '"'
101 |
102 | evaljs = EvalJs()
103 | setattr(evaljs, "x", None)
104 | evaljs.execute(js_content)
105 | js_content = getattr(evaljs, "x")
106 |
107 | return f"https://{link}.zippyshare.com{js_content}"
108 |
109 |
110 | def yandex_disk(url: str) -> str:
111 | """ Yandex.Disk direct links generator
112 | Based on https://github.com/wldhx/yadisk-direct """
113 | try:
114 | link = re.findall(r'\bhttps?://.*yadi\.sk\S+', url)[0]
115 | except IndexError:
116 | reply = "`No Yandex.Disk links found`\n"
117 | return reply
118 | api = 'https://cloud-api.yandex.net/v1/disk/public/resources/download?public_key={}'
119 | try:
120 | dl_url = requests.get(api.format(link)).json()['href']
121 | return dl_url
122 | except KeyError:
123 | raise DirectDownloadLinkException("`Error: File not found/Download limit reached`\n")
124 |
125 |
126 | def cm_ru(url: str) -> str:
127 | """ cloud.mail.ru direct links generator
128 | Using https://github.com/JrMasterModelBuilder/cmrudl.py """
129 | reply = ''
130 | try:
131 | link = re.findall(r'\bhttps?://.*cloud\.mail\.ru\S+', url)[0]
132 | except IndexError:
133 | raise DirectDownloadLinkException("`No cloud.mail.ru links found`\n")
134 | command = f'vendor/cmrudl.py/cmrudl -s {link}'
135 | result = popen(command).read()
136 | result = result.splitlines()[-1]
137 | try:
138 | data = json.loads(result)
139 | except json.decoder.JSONDecodeError:
140 | raise DirectDownloadLinkException("`Error: Can't extract the link`\n")
141 | dl_url = data['download']
142 | return dl_url
143 |
144 |
145 | def uptobox(url: str) -> str:
146 | """ Uptobox direct links generator
147 | based on https://github.com/jovanzers/WinTenCermin """
148 | try:
149 | link = re.findall(r'\bhttps?://.*uptobox\.com\S+', url)[0]
150 | except IndexError:
151 | raise DirectDownloadLinkException("`No Uptobox links found`\n")
152 | if UPTOBOX_TOKEN is None:
153 | logging.error('UPTOBOX_TOKEN not provided!')
154 | dl_url = url
155 | else:
156 | try:
157 | link = re.findall(r'\bhttp?://.*uptobox\.com/dl\S+', url)[0]
158 | logging.info('Uptobox direct link')
159 | dl_url = url
160 | except:
161 | file_id = re.findall(r'\bhttps?://.*uptobox\.com/(\w+)', url)[0]
162 | file_link = 'https://uptobox.com/api/link?token=%s&file_code=%s' % (UPTOBOX_TOKEN, file_id)
163 | req = requests.get(file_link)
164 | result = req.json()
165 | dl_url = result['data']['dlLink']
166 | return dl_url
167 |
168 |
169 | def mediafire(url: str) -> str:
170 | """ MediaFire direct links generator """
171 | try:
172 | link = re.findall(r'\bhttps?://.*mediafire\.com\S+', url)[0]
173 | except IndexError:
174 | raise DirectDownloadLinkException("`No MediaFire links found`\n")
175 | page = BeautifulSoup(requests.get(link).content, 'lxml')
176 | info = page.find('a', {'aria-label': 'Download file'})
177 | dl_url = info.get('href')
178 | return dl_url
179 |
180 |
181 | def osdn(url: str) -> str:
182 | """ OSDN direct links generator """
183 | osdn_link = 'https://osdn.net'
184 | try:
185 | link = re.findall(r'\bhttps?://.*osdn\.net\S+', url)[0]
186 | except IndexError:
187 | raise DirectDownloadLinkException("`No OSDN links found`\n")
188 | page = BeautifulSoup(
189 | requests.get(link, allow_redirects=True).content, 'lxml')
190 | info = page.find('a', {'class': 'mirror_link'})
191 | link = urllib.parse.unquote(osdn_link + info['href'])
192 | mirrors = page.find('form', {'id': 'mirror-select-form'}).findAll('tr')
193 | urls = []
194 | for data in mirrors[1:]:
195 | mirror = data.find('input')['value']
196 | urls.append(re.sub(r'm=(.*)&f', f'm={mirror}&f', link))
197 | return urls[0]
198 |
199 |
200 | def github(url: str) -> str:
201 | """ GitHub direct links generator """
202 | try:
203 | re.findall(r'\bhttps?://.*github\.com.*releases\S+', url)[0]
204 | except IndexError:
205 | raise DirectDownloadLinkException("`No GitHub Releases links found`\n")
206 | download = requests.get(url, stream=True, allow_redirects=False)
207 | try:
208 | dl_url = download.headers["location"]
209 | return dl_url
210 | except KeyError:
211 | raise DirectDownloadLinkException("`Error: Can't extract the link`\n")
212 |
213 |
214 | def hxfile(url: str) -> str:
215 | """ Hxfile direct links generator
216 | based on https://github.com/breakdowns/slam-mirrorbot """
217 | dl_url = ''
218 | try:
219 | link = re.findall(r'\bhttps?://.*hxfile\.co\S+', url)[0]
220 | except IndexError:
221 | raise DirectDownloadLinkException("`No Hxfile links found`\n")
222 | bypasser = lk21.Bypass()
223 | dl_url=bypasser.bypass_url(link)
224 | return dl_url
225 |
226 |
227 | def anon(url: str) -> str:
228 | """ Anonfiles direct links generator
229 | based on https://github.com/breakdowns/slam-mirrorbot """
230 | dl_url = ''
231 | try:
232 | link = re.findall(r'\bhttps?://.*anonfiles\.com\S+', url)[0]
233 | except IndexError:
234 | raise DirectDownloadLinkException("`No Anonfiles links found`\n")
235 | bypasser = lk21.Bypass()
236 | dl_url=bypasser.bypass_url(link)
237 | return dl_url
238 |
239 |
240 | def letsupload(url: str) -> str:
241 | """ Letsupload direct link generator
242 | Based on https://github.com/breakdowns/slam-mirrorbot """
243 | dl_url = ''
244 | try:
245 | link = re.findall(r'\bhttps?://.*letsupload\.io\S+', url)[0]
246 | except IndexError:
247 | raise DirectDownloadLinkException("`No Letsupload links found`\n")
248 | bypasser = lk21.Bypass()
249 | dl_url=bypasser.bypass_url(link)
250 | return dl_url
251 |
252 |
253 | def fembed(link: str) -> str:
254 | """ Fembed direct link generator
255 | Based on https://github.com/breakdowns/slam-mirrorbot """
256 | bypasser = lk21.Bypass()
257 | dl_url=bypasser.bypass_fembed(link)
258 | lst_link = []
259 | count = len(dl_url)
260 | for i in dl_url:
261 | lst_link.append(dl_url[i])
262 | return lst_link[count-1]
263 |
264 |
265 | def sbembed(link: str) -> str:
266 | """ Sbembed direct link generator
267 | Based on https://github.com/breakdowns/slam-mirrorbot """
268 | bypasser = lk21.Bypass()
269 | dl_url=bypasser.bypass_sbembed(link)
270 | lst_link = []
271 | count = len(dl_url)
272 | for i in dl_url:
273 | lst_link.append(dl_url[i])
274 | return lst_link[count-1]
275 |
276 |
277 | def onedrive(link: str) -> str:
278 | """ Onedrive direct link generator
279 | Based on https://github.com/UsergeTeam/Userge """
280 | link_without_query = urlparse(link)._replace(query=None).geturl()
281 | direct_link_encoded = str(standard_b64encode(bytes(link_without_query, "utf-8")), "utf-8")
282 | direct_link1 = f"https://api.onedrive.com/v1.0/shares/u!{direct_link_encoded}/root/content"
283 | resp = requests.head(direct_link1)
284 | if resp.status_code != 302:
285 | return "`Error: Unauthorized link, the link may be private`"
286 | dl_link = resp.next.url
287 | file_name = dl_link.rsplit("/", 1)[1]
288 | resp2 = requests.head(dl_link)
289 | return dl_link
290 |
291 |
292 | def pixeldrain(url: str) -> str:
293 | """ Based on https://github.com/yash-dk/TorToolkit-Telegram """
294 | url = url.strip("/ ")
295 | file_id = url.split("/")[-1]
296 | info_link = f"https://pixeldrain.com/api/file/{file_id}/info"
297 | dl_link = f"https://pixeldrain.com/api/file/{file_id}"
298 | resp = requests.get(info_link).json()
299 | if resp["success"]:
300 | return dl_link
301 | else:
302 | raise DirectDownloadLinkException("ERROR: Cant't download due {}.".format(resp.text["value"]))
303 |
304 |
305 | def useragent():
306 | """
307 | useragent random setter
308 | """
309 | useragents = BeautifulSoup(
310 | requests.get(
311 | 'https://developers.whatismybrowser.com/'
312 | 'useragents/explore/operating_system_name/android/').content,
313 | 'lxml').findAll('td', {'class': 'useragent'})
314 | user_agent = choice(useragents)
315 | return user_agent.text
316 |
--------------------------------------------------------------------------------
/bot/modules/search.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import html
4 | import asyncio
5 | import aiohttp
6 | import json
7 | import feedparser
8 | import requests
9 | import itertools
10 |
11 | from telegram.ext import CommandHandler
12 | from telegram import ParseMode
13 |
14 | from urllib.parse import quote as urlencode, urlsplit
15 |
16 | from pyrogram import Client, filters, emoji
17 | from pyrogram.parser import html as pyrogram_html
18 | from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
19 | from pyrogram.handlers import MessageHandler, CallbackQueryHandler
20 |
21 | from bot import app, dispatcher, IMAGE_URL
22 | from bot.helper import custom_filters
23 | from bot.helper.telegram_helper.filters import CustomFilters
24 |
25 | search_lock = asyncio.Lock()
26 | search_info = {False: dict(), True: dict()}
27 |
28 | async def return_search(query, page=1, sukebei=False):
29 | page -= 1
30 | query = query.lower().strip()
31 | used_search_info = search_info[sukebei]
32 | async with search_lock:
33 | results, get_time = used_search_info.get(query, (None, 0))
34 | if (time.time() - get_time) > 3600:
35 | results = []
36 | async with aiohttp.ClientSession() as session:
37 | async with session.get(f'https://{"sukebei." if sukebei else ""}nyaa.si/?page=rss&q={urlencode(query)}') as resp:
38 | d = feedparser.parse(await resp.text())
39 | text = ''
40 | a = 0
41 | parser = pyrogram_html.HTML(None)
42 | for i in sorted(d['entries'], key=lambda i: int(i['nyaa_seeders']), reverse=True):
43 | if i['nyaa_size'].startswith('0'):
44 | continue
45 | if not int(i['nyaa_seeders']):
46 | break
47 | link = i['link']
48 | splitted = urlsplit(link)
49 | if splitted.scheme == 'magnet' and splitted.query:
50 | link = f'{link}'
51 | newtext = f'''{a + 1}. {html.escape(i["title"])}
52 | Link: {link}
53 | Size: {i["nyaa_size"]}
54 | Seeders: {i["nyaa_seeders"]}
55 | Leechers: {i["nyaa_leechers"]}
56 | Category: {i["nyaa_category"]}\n\n'''
57 | futtext = text + newtext
58 | if (a and not a % 10) or len((await parser.parse(futtext))['message']) > 4096:
59 | results.append(text)
60 | futtext = newtext
61 | text = futtext
62 | a += 1
63 | results.append(text)
64 | ttl = time.time()
65 | used_search_info[query] = results, ttl
66 | try:
67 | return results[page], len(results), ttl
68 | except IndexError:
69 | return '', len(results), ttl
70 |
71 | message_info = dict()
72 | ignore = set()
73 |
74 | @app.on_message(filters.command(['nyaa']))
75 | async def nyaa_search(client, message):
76 | text = message.text.split(' ')
77 | text.pop(0)
78 | query = ' '.join(text)
79 | await init_search(client, message, query, False)
80 |
81 | @app.on_message(filters.command(['sukebei']))
82 | async def nyaa_search_sukebei(client, message):
83 | text = message.text.split(' ')
84 | text.pop(0)
85 | query = ' '.join(text)
86 | await init_search(client, message, query, True)
87 |
88 | async def init_search(client, message, query, sukebei):
89 | result, pages, ttl = await return_search(query, sukebei=sukebei)
90 | if not result:
91 | await message.reply_text('No results found')
92 | else:
93 | buttons = [InlineKeyboardButton(f'1/{pages}', 'nyaa_nop'), InlineKeyboardButton(f'Next', 'nyaa_next')]
94 | if pages == 1:
95 | buttons.pop()
96 | reply = await message.reply_text(result, reply_markup=InlineKeyboardMarkup([
97 | buttons
98 | ]))
99 | message_info[(reply.chat.id, reply.message_id)] = message.from_user.id, ttl, query, 1, pages, sukebei
100 |
101 | @app.on_callback_query(custom_filters.callback_data('nyaa_nop'))
102 | async def nyaa_nop(client, callback_query):
103 | await callback_query.answer(cache_time=3600)
104 |
105 | callback_lock = asyncio.Lock()
106 | @app.on_callback_query(custom_filters.callback_data(['nyaa_back', 'nyaa_next']))
107 | async def nyaa_callback(client, callback_query):
108 | message = callback_query.message
109 | message_identifier = (message.chat.id, message.message_id)
110 | data = callback_query.data
111 | async with callback_lock:
112 | if message_identifier in ignore:
113 | await callback_query.answer()
114 | return
115 | user_id, ttl, query, current_page, pages, sukebei = message_info.get(message_identifier, (None, 0, None, 0, 0, None))
116 | og_current_page = current_page
117 | if data == 'nyaa_back':
118 | current_page -= 1
119 | elif data == 'nyaa_next':
120 | current_page += 1
121 | if current_page < 1:
122 | current_page = 1
123 | elif current_page > pages:
124 | current_page = pages
125 | ttl_ended = (time.time() - ttl) > 3600
126 | if ttl_ended:
127 | text = getattr(message.text, 'html', 'Search expired')
128 | else:
129 | if callback_query.from_user.id != user_id:
130 | await callback_query.answer('...no', cache_time=3600)
131 | return
132 | text, pages, ttl = await return_search(query, current_page, sukebei)
133 | buttons = [InlineKeyboardButton(f'Prev', 'nyaa_back'), InlineKeyboardButton(f'{current_page}/{pages}', 'nyaa_nop'), InlineKeyboardButton(f'Next', 'nyaa_next')]
134 | if ttl_ended:
135 | buttons = [InlineKeyboardButton('Search Expired', 'nyaa_nop')]
136 | else:
137 | if current_page == 1:
138 | buttons.pop(0)
139 | if current_page == pages:
140 | buttons.pop()
141 | if ttl_ended or current_page != og_current_page:
142 | await callback_query.edit_message_text(text, reply_markup=InlineKeyboardMarkup([
143 | buttons
144 | ]))
145 | message_info[message_identifier] = user_id, ttl, query, current_page, pages, sukebei
146 | if ttl_ended:
147 | ignore.add(message_identifier)
148 | await callback_query.answer()
149 |
150 | # Using upstream API based on: https://github.com/Ryuk-me/Torrents-Api
151 | # Implemented by https://github.com/jusidama18
152 |
153 | class TorrentSearch:
154 | index = 0
155 | query = None
156 | message = None
157 | response = None
158 | response_range = None
159 |
160 | RESULT_LIMIT = 4
161 | RESULT_STR = None
162 |
163 | def __init__(self, command: str, source: str, result_str: str):
164 | self.command = command
165 | self.source = source.rstrip('/')
166 | self.RESULT_STR = result_str
167 |
168 | app.add_handler(MessageHandler(self.find, filters.command([command])))
169 | app.add_handler(CallbackQueryHandler(self.previous, filters.regex(f"{self.command}_previous")))
170 | app.add_handler(CallbackQueryHandler(self.delete, filters.regex(f"{self.command}_delete")))
171 | app.add_handler(CallbackQueryHandler(self.next, filters.regex(f"{self.command}_next")))
172 |
173 | @staticmethod
174 | def format_magnet(string: str):
175 | if not string:
176 | return ""
177 | return string.split('&tr', 1)[0]
178 |
179 | def get_formatted_string(self, values):
180 | string = self.RESULT_STR.format(**values)
181 | extra = ""
182 | if "Files" in values:
183 | tmp_str = "➲[{Quality} - {Type} ({Size})]({Torrent}): `{magnet}`"
184 | extra += "\n".join(
185 | tmp_str.format(**f, magnet=self.format_magnet(f['Magnet']))
186 | for f in values['Files']
187 | )
188 | else:
189 | magnet = values.get('magnet', values.get('Magnet')) # Avoid updating source dict
190 | if magnet:
191 | extra += f"➲Magnet: `{self.format_magnet(magnet)}`"
192 | if (extra):
193 | string += "\n" + extra
194 | return string
195 |
196 | async def update_message(self):
197 | prevBtn = InlineKeyboardButton(f"Prev", callback_data=f"{self.command}_previous")
198 | delBtn = InlineKeyboardButton(f"{emoji.CROSS_MARK}", callback_data=f"{self.command}_delete")
199 | nextBtn = InlineKeyboardButton(f"Next", callback_data=f"{self.command}_next")
200 |
201 | inline = []
202 | if (self.index != 0):
203 | inline.append(prevBtn)
204 | inline.append(delBtn)
205 | if (self.index != len(self.response_range) - 1):
206 | inline.append(nextBtn)
207 |
208 | res_lim = min(self.RESULT_LIMIT, len(self.response) - self.RESULT_LIMIT*self.index)
209 | result = f"**Page - {self.index+1}**\n\n"
210 | result += "\n\n=======================\n\n".join(
211 | self.get_formatted_string(self.response[self.response_range[self.index]+i])
212 | for i in range(res_lim)
213 | )
214 |
215 | await self.message.edit(
216 | result,
217 | reply_markup=InlineKeyboardMarkup([inline]),
218 | parse_mode="markdown",
219 | )
220 |
221 | async def find(self, client, message):
222 | if len(message.command) < 2:
223 | await message.reply_text(f"Usage: /{self.command} query")
224 | return
225 |
226 | query = urlencode(message.text.split(None, 1)[1])
227 | self.message = await message.reply_text("Searching")
228 | try:
229 | async with aiohttp.ClientSession() as session:
230 | async with session.get(f"{self.source}/{query}") as resp:
231 | if (resp.status != 200):
232 | raise Exception('unsuccessful request')
233 | result = await resp.json()
234 | if (result and isinstance(result[0], list)):
235 | result = list(itertools.chain(*result))
236 | self.response = result
237 | self.response_range = range(0, len(self.response), self.RESULT_LIMIT)
238 | except:
239 | await self.message.edit("No Results Found.")
240 | return
241 | await self.update_message()
242 |
243 | async def delete(self, client, message):
244 | index = 0
245 | query = None
246 | message = None
247 | response = None
248 | response_range = None
249 | await self.message.delete()
250 |
251 | async def previous(self, client, message):
252 | self.index -= 1
253 | await self.update_message()
254 |
255 | async def next(self, client, message):
256 | self.index += 1
257 | await self.update_message()
258 |
259 | RESULT_STR_1337 = (
260 | "➲Name: `{Name}`\n"
261 | "➲Size: {Size}\n"
262 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
263 | )
264 | RESULT_STR_PIRATEBAY = (
265 | "➲Name: `{Name}`\n"
266 | "➲Size: {Size}\n"
267 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
268 | )
269 | RESULT_STR_TGX = (
270 | "➲Name: `{Name}`\n"
271 | "➲Size: {Size}\n"
272 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
273 | )
274 | RESULT_STR_YTS = (
275 | "➲Name: `{Name}`"
276 | )
277 | RESULT_STR_EZTV = (
278 | "➲Name: `{Name}`\n"
279 | "➲Size: {Size}\n"
280 | "➲Seeders: {Seeders}"
281 | )
282 | RESULT_STR_TORLOCK = (
283 | "➲Name: `{Name}`\n"
284 | "➲Size: {Size}\n"
285 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
286 | )
287 | RESULT_STR_RARBG = (
288 | "➲Name: `{Name}`\n"
289 | "➲Size: {Size}\n"
290 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
291 | )
292 | RESULT_STR_ALL = (
293 | "➲Name: `{Name}`\n"
294 | "➲Size: {Size}\n"
295 | "➲Seeders: {Seeders} || ➲Leechers: {Leechers}"
296 | )
297 |
298 | torrents_dict = {
299 | '1337x': {'source': "https://slam-api.herokuapp.com/api/1337x/", 'result_str': RESULT_STR_1337},
300 | 'piratebay': {'source': "https://slam-api.herokuapp.com/api/piratebay/", 'result_str': RESULT_STR_PIRATEBAY},
301 | 'tgx': {'source': "https://slam-api.herokuapp.com/api/tgx/", 'result_str': RESULT_STR_TGX},
302 | 'yts': {'source': "https://slam-api.herokuapp.com/api/yts/", 'result_str': RESULT_STR_YTS},
303 | 'eztv': {'source': "https://slam-api.herokuapp.com/api/eztv/", 'result_str': RESULT_STR_EZTV},
304 | 'torlock': {'source': "https://slam-api.herokuapp.com/api/torlock/", 'result_str': RESULT_STR_TORLOCK},
305 | 'rarbg': {'source': "https://slam-api.herokuapp.com/api/rarbg/", 'result_str': RESULT_STR_RARBG},
306 | 'ts': {'source': "https://slam-api.herokuapp.com/api/all/", 'result_str': RESULT_STR_ALL}
307 | }
308 |
309 | torrent_handlers = []
310 | for command, value in torrents_dict.items():
311 | torrent_handlers.append(TorrentSearch(command, value['source'], value['result_str']))
312 |
313 | def searchhelp(update, context):
314 | help_string = '''
315 | • /nyaa [search query]
316 | • /sukebei [search query]
317 | • /1337x [search query]
318 | • /piratebay [search query]
319 | • /tgx [search query]
320 | • /yts [search query]
321 | • /eztv [search query]
322 | • /torlock [search query]
323 | • /rarbg [search query]
324 | • /ts [search query]
325 | '''
326 | update.effective_message.reply_photo(IMAGE_URL, help_string, parse_mode=ParseMode.HTML)
327 |
328 |
329 | SEARCHHELP_HANDLER = CommandHandler("tshelp", searchhelp, filters=(CustomFilters.authorized_chat | CustomFilters.authorized_user) & CustomFilters.mirror_owner_filter, run_async=True)
330 | dispatcher.add_handler(SEARCHHELP_HANDLER)
331 |
--------------------------------------------------------------------------------
/gen_sa_accounts.py:
--------------------------------------------------------------------------------
1 | import errno
2 | import os
3 | import pickle
4 | import sys
5 | from argparse import ArgumentParser
6 | from base64 import b64decode
7 | from glob import glob
8 | from json import loads
9 | from random import choice
10 | from time import sleep
11 |
12 | from google.auth.transport.requests import Request
13 | from google_auth_oauthlib.flow import InstalledAppFlow
14 | from googleapiclient.discovery import build
15 | from googleapiclient.errors import HttpError
16 |
17 | SCOPES = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/cloud-platform',
18 | 'https://www.googleapis.com/auth/iam']
19 | project_create_ops = []
20 | current_key_dump = []
21 | sleep_time = 30
22 |
23 |
24 | # Create count SAs in project
25 | def _create_accounts(service, project, count):
26 | batch = service.new_batch_http_request(callback=_def_batch_resp)
27 | for i in range(count):
28 | aid = _generate_id('mfc-')
29 | batch.add(service.projects().serviceAccounts().create(name='projects/' + project, body={'accountId': aid,
30 | 'serviceAccount': {
31 | 'displayName': aid}}))
32 | batch.execute()
33 |
34 |
35 | # Create accounts needed to fill project
36 | def _create_remaining_accounts(iam, project):
37 | print('Creating accounts in %s' % project)
38 | sa_count = len(_list_sas(iam, project))
39 | while sa_count != 100:
40 | _create_accounts(iam, project, 100 - sa_count)
41 | sa_count = len(_list_sas(iam, project))
42 |
43 |
44 | # Generate a random id
45 | def _generate_id(prefix='saf-'):
46 | chars = '-abcdefghijklmnopqrstuvwxyz1234567890'
47 | return prefix + ''.join(choice(chars) for _ in range(25)) + choice(chars[1:])
48 |
49 |
50 | # List projects using service
51 | def _get_projects(service):
52 | return [i['projectId'] for i in service.projects().list().execute()['projects']]
53 |
54 |
55 | # Default batch callback handler
56 | def _def_batch_resp(id, resp, exception):
57 | if exception is not None:
58 | if str(exception).startswith('