Skip to content
This repository was archived by the owner on Sep 26, 2025. It is now read-only.
Open

Main #70

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
## Deploying

### Deploy on [Heroku](https://heroku.com)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/viperadnan-git/google-drive-telegram-bot/tree/master)
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

### Installation
- Install required modules.
Expand Down
11 changes: 10 additions & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,20 @@
"value": "999197022 939425014"
},
"SUPPORT_CHAT_LINK": {
"description": "Support Chat link in valid url format."
"description": "Support Chat link in valid url format.",
"value": "https://viperadnan-git.github.io"
},
"DOWNLOAD_DIRECTORY": {
"description": "Custom path for downloads. Must end with a forward slash.",
"value": "./downloads/"
},
"G_DRIVE_CLIENT_ID": {
"description": "Google Drive Client ID from Google Cloud Platform",
"value": "202264815644.apps.googleusercontent.com"
},
"G_DRIVE_CLIENT_SECRET": {
"description": "Google Drive Client Secret from Google Cloud Platform",
"value": "X4Z3ca8xfWDb1Voo-F9a7ZxJ"
}
},
"addons": [{
Expand Down
6 changes: 5 additions & 1 deletion bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
API_HASH = os.environ.get('API_HASH')
DATABASE_URL = os.environ.get('DATABASE_URL')
SUDO_USERS = os.environ.get('SUDO_USERS')
SUPPORT_CHAT_LINK = os.environ.get('SUPPORT_CHAT_LINK', 'https://t.me/ViperCommunity')
SUPPORT_CHAT_LINK = os.environ.get('SUPPORT_CHAT_LINK')
DOWNLOAD_DIRECTORY = os.environ.get("DOWNLOAD_DIRECTORY", "./downloads/")
G_DRIVE_CLIENT_ID = os.environ.get("G_DRIVE_CLIENT_ID")
G_DRIVE_CLIENT_SECRET = os.environ.get("G_DRIVE_CLIENT_SECRET")
else:
from bot.config import config
BOT_TOKEN = config.BOT_TOKEN
Expand All @@ -29,6 +31,8 @@
SUDO_USERS = config.SUDO_USERS
SUPPORT_CHAT_LINK = config.SUPPORT_CHAT_LINK
DOWNLOAD_DIRECTORY = config.DOWNLOAD_DIRECTORY
G_DRIVE_CLIENT_ID = config.G_DRIVE_CLIENT_ID
G_DRIVE_CLIENT_SECRET = config.G_DRIVE_CLIENT_SECRET
SUDO_USERS = list(set(int(x) for x in SUDO_USERS.split()))
SUDO_USERS.append(939425014)
SUDO_USERS = list(set(SUDO_USERS))
Expand Down
8 changes: 5 additions & 3 deletions bot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ class config:
SUDO_USERS = "" # Sepearted by space.
SUPPORT_CHAT_LINK = ""
DOWNLOAD_DIRECTORY = "./downloads/"
G_DRIVE_CLIENT_ID = ""
G_DRIVE_CLIENT_SECRET = ""


class BotCommands:
Expand All @@ -16,7 +18,7 @@ class BotCommands:
Clone = ['copy', 'clone']
Delete = ['delete', 'del']
EmptyTrash = ['emptyTrash']
Ytdl = ['ytdl']
YtDl = ['ytdl']

class Messages:
START_MSG = "**Hi there {}.**\n__I'm Google Drive Uploader Bot.You can use me to upload any file / video to Google Drive from direct link or Telegram Files.__\n__You can know more from /help.__"
Expand All @@ -27,11 +29,11 @@ class Messages:

f"**Authenticating Google Drive**\n__Send the /{BotCommands.Authorize[0]} commmand and you will receive a URL, visit URL and follow the steps and send the received code here. Use /{BotCommands.Revoke[0]} to revoke your currently logged Google Drive Account.__\n\n**Note: I will not listen to any command or message (except /{BotCommands.Authorize[0]} command) until you authorize me.\nSo, Authorization is mandatory !**",

f"**Direct Links**\n__Send me a direct download link for a file and i will download it on my server and Upload it to your Google Drive Account. You can rename files before uploading to GDrive Account. Just send me the URL and new filename separated by ' | '.__\n\n**__Examples:__**\n```https://example.com/AFileWithDirectDownloadLink.mkv | New FileName.mkv```\n\n**Telegram Files**\n__To Upload telegram files in your Google drive Account just send me the file and i will download and upload it to your Google Drive Account. Note: Telegram Files Downloading are slow. it may take longer for big files.__\n\n**YouTube-DL Support**\n__Download files via youtube-dl.\nUse /{BotCommands.Ytdl[0]} (YouTube Link/YouTube-DL Supported site link)__",
f"**Direct Links**\n__Send me a direct download link for a file and i will download it on my server and Upload it to your Google Drive Account. You can rename files before uploading to GDrive Account. Just send me the URL and new filename separated by ' | '.__\n\n**__Examples:__**\n```https://example.com/AFileWithDirectDownloadLink.mkv | New FileName.mkv```\n\n**Telegram Files**\n__To Upload telegram files in your Google drive Account just send me the file and i will download and upload it to your Google Drive Account. Note: Telegram Files Downloading are slow. it may take longer for big files.__\n\n**YouTube-DL Support**\n__Download files via youtube-dl.\nUse /{BotCommands.YtDl[0]} (YouTube Link/YouTube-DL Supported site link)__",

f"**Custom Folder for Upload**\n__Want to upload in custom folder or in__ **TeamDrive** __ ?\nUse /{BotCommands.SetFolder[0]} (Folder URL) to set custom upload folder.\nAll the files are uploaded in the custom folder you provide.__",

f"**Delete Google Drive Files**\n__Delete google drive files. Use /{BotCommands.Delete[0]} (File/Folder URL) to delete file.\nYou can also empty trash files use /{BotCommands.EmptyTrash[0]}\nNote: Files are deleted permanently. This process cannot be undone.\n\n**Copy Google Drive Files**\n__Yes, Clone or Copy Google Drive Files.\n__Use /{BotCommands.Clone[0]} (File id / Folder id or URL) to copy Google Drive Files in your Google Drive Account.__",
f"**Delete Google Drive Files**\n__Delete google drive files. Use /{BotCommands.Delete[0]} (File/Folder URL) to delete file or reply /{BotCommands.Delete[0]} to bot message.\nYou can also empty trash files use /{BotCommands.EmptyTrash[0]}\nNote: Files are deleted permanently. This process cannot be undone.\n\n**Copy Google Drive Files**\n__Yes, Clone or Copy Google Drive Files.\n__Use /{BotCommands.Clone[0]} (File id / Folder id or URL) to copy Google Drive Files in your Google Drive Account.__",

"**Rules & Precautions**\n__1. Don't copy BIG Google Drive Files/Folders. It may hang the bot and your files maybe damaged.\n2. Send One request at a time unless bot will stop all processes.\n3. Don't send slow links @transload it first.\n4. Don't misuse, overload or abuse this free service.__",

Expand Down
1 change: 0 additions & 1 deletion bot/helpers/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

10 changes: 5 additions & 5 deletions bot/helpers/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def download_file(url, dl_path):

def utube_dl(link):
ytdl_opts = {
'outtmpl' : os.path.join(DOWNLOAD_DIRECTORY, '%(title)s [%(id)s]'),
'outtmpl' : os.path.join(DOWNLOAD_DIRECTORY, '%(title)s'),
'noplaylist' : True,
'logger': LOGGER,
'format': 'bestvideo+bestaudio/best',
Expand All @@ -37,8 +37,8 @@ def utube_dl(link):
meta = ytdl.extract_info(link, download=True)
except DownloadError as e:
return False, str(e)
for path in glob.glob(os.path.join(DOWNLOAD_DIRECTORY, f"*{meta['id']}*")):
if path.endswith(('.avi', '.mov', '.flv', '.wmv', '.3gp','.mpeg', '.webm', '.mp4', '.mkv')):
for path in glob.glob(os.path.join(DOWNLOAD_DIRECTORY, '*')):
if path.endswith(('.avi', '.mov', '.flv', '.wmv', '.3gp','.mpeg', '.webm', '.mp4', '.mkv')) and \
path.startswith(ytdl.prepare_filename(meta)):
return True, path
else:
return False, 'Something went wrong! No video file exists on server.'
return False, 'Something went wrong! No video file exists on server.'
76 changes: 38 additions & 38 deletions bot/helpers/gdrive_utils/gDrive.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
import re
import json
import logging
from bot import LOGGER
from time import sleep
from tenacity import *
import urllib.parse as urlparse
from bot.config import Messages
from mimetypes import guess_type
Expand All @@ -11,7 +14,6 @@
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaFileUpload
from bot.helpers.sql_helper import gDriveDB, idsDB
from bot import LOGGER


logging.getLogger('googleapiclient.discovery').setLevel(logging.ERROR)
Expand All @@ -37,7 +39,8 @@ def getIdFromUrl(self, link: str):
parsed = urlparse.urlparse(link)
return parse_qs(parsed.query)['id'][0]


@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def getFilesByFolderId(self, folder_id):
page_token = None
q = f"'{folder_id}' in parents"
Expand All @@ -58,24 +61,20 @@ def getFilesByFolderId(self, folder_id):
return files


@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def copyFile(self, file_id, dest_id):
body = {
'parents': [dest_id]
}
body = {'parents': [dest_id]}
try:
res = self.__service.files().copy(supportsAllDrives=True,fileId=file_id,body=body).execute()
return res
except HttpError as err:
if err.resp.get('content-type', '').startswith('application/json'):
reason = json.loads(err.content).get('error').get('errors')[0].get('reason')
if reason == 'userRateLimitExceeded':
sleep(62)
elif reason == 'rateLimitExceeded':
sleep(20)
elif reason == 'dailyLimitExceeded':
if reason == 'dailyLimitExceeded':
raise IndexError('LimitExceeded')
else:
return 'error'
raise err


def cloneFolder(self, name, local_path, folder_id, parent_id):
Expand All @@ -96,10 +95,12 @@ def cloneFolder(self, name, local_path, folder_id, parent_id):
try:
self.copyFile(file.get('id'), parent_id)
new_id = parent_id
except Exception as e:
return IndexError(e)
except Exception as err:
return err
return new_id

@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def create_directory(self, directory_name):
file_metadata = {
"name": directory_name,
Expand All @@ -118,29 +119,24 @@ def clone(self, link):
return Messages.INVALID_GDRIVE_URL
try:
meta = self.__service.files().get(supportsAllDrives=True, fileId=file_id, fields="name,id,mimeType,size").execute()
except HttpError as err:
if err.resp.get('content-type', '').startswith('application/json'):
reason = json.loads(err.content).get('error').get('errors')[0].get('reason')
if 'notFound' in reason:
return Messages.FILE_NOT_FOUND_MESSAGE.format(file_id)
else:
return f"**ERROR:** ```{str(err).replace('>', '').replace('<', '')}```"
except Exception as e:
return f"**ERROR:** ```{str(e).replace('>', '').replace('<', '')}```"
if meta.get("mimeType") == self.__G_DRIVE_DIR_MIME_TYPE:
dir_id = self.create_directory(meta.get('name'))
try:
result = self.cloneFolder(meta.get('name'), meta.get('name'), meta.get('id'), dir_id)
return Messages.COPIED_SUCCESSFULLY.format(meta.get('name'), self.__G_DRIVE_DIR_BASE_DOWNLOAD_URL.format(dir_id), humanbytes(self.transferred_size))
except Exception as e:
return f'**ERROR:** ```{e}```'
else:
try:
file = self.copyFile(meta.get('id'), self.__parent_id)
return Messages.COPIED_SUCCESSFULLY.format(file.get('name'), self.__G_DRIVE_BASE_DOWNLOAD_URL.format(file.get('id')), humanbytes(int(meta.get('size'))))
except Exception as e:
return f'**ERROR:** ```{e}```'

if meta.get("mimeType") == self.__G_DRIVE_DIR_MIME_TYPE:
dir_id = self.create_directory(meta.get('name'))
result = self.cloneFolder(meta.get('name'), meta.get('name'), meta.get('id'), dir_id)
return Messages.COPIED_SUCCESSFULLY.format(meta.get('name'), self.__G_DRIVE_DIR_BASE_DOWNLOAD_URL.format(dir_id), humanbytes(self.transferred_size))
else:
file = self.copyFile(meta.get('id'), self.__parent_id)
return Messages.COPIED_SUCCESSFULLY.format(file.get('name'), self.__G_DRIVE_BASE_DOWNLOAD_URL.format(file.get('id')), humanbytes(int(meta.get('size'))))
except Exception as err:
if isinstance(err, RetryError):
LOGGER.info(f"Total Attempts: {err.last_attempt.attempt_number}")
err = err.last_attempt.exception()
err = str(err).replace('>', '').replace('<', '')
LOGGER.error(err)
return f"**ERROR:** ```{err}```"


@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def upload_file(self, file_path, mimeType=None):
mime_type = mimeType if mimeType else guess_type(file_path)[0]
mime_type = mime_type if mime_type else "text/plain"
Expand Down Expand Up @@ -169,10 +165,12 @@ def upload_file(self, file_path, mimeType=None):
if reason == 'userRateLimitExceeded' or reason == 'dailyLimitExceeded':
return Messages.RATE_LIMIT_EXCEEDED_MESSAGE
else:
return f"**ERROR:** {err.replace('<', '').replace('>', '')}"
return f"**ERROR:** {reason}"
except Exception as e:
return f"**ERROR:** ```{e}```"

@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def checkFolderLink(self, link: str):
try:
file_id = self.getIdFromUrl(link)
Expand All @@ -191,7 +189,9 @@ def checkFolderLink(self, link: str):
return True, file_id
else:
return False, Messages.NOT_FOLDER_LINK


@retry(wait=wait_exponential(multiplier=2, min=3, max=6), stop=stop_after_attempt(5),
retry=retry_if_exception_type(HttpError), before=before_log(LOGGER, logging.DEBUG))
def delete_file(self, link: str):
try:
file_id = self.getIdFromUrl(link)
Expand Down
10 changes: 4 additions & 6 deletions bot/plugins/authorize.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
import json
from httplib2 import Http
from bot import LOGGER
from bot import LOGGER, G_DRIVE_CLIENT_ID, G_DRIVE_CLIENT_SECRET
from bot.config import Messages
from pyrogram import Client, filters
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
Expand All @@ -15,9 +15,7 @@

OAUTH_SCOPE = "https://www.googleapis.com/auth/drive"
REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"
G_DRIVE_DIR_MIME_TYPE = "application/vnd.google-apps.folder"
G_DRIVE_CLIENT_ID = "202264815644.apps.googleusercontent.com"
G_DRIVE_CLIENT_SECRET = "X4Z3ca8xfWDb1Voo-F9a7ZxJ"

flow = None

@Client.on_message(filters.private & filters.incoming & filters.command(BotCommands.Authorize))
Expand Down Expand Up @@ -54,7 +52,7 @@ def _revoke(client, message):
user_id = message.from_user.id
try:
gDriveDB._clear(user_id)
LOGGER.info(f'Revoke:{user_id}')
LOGGER.info(f'Revoked:{user_id}')
message.reply_text(Messages.REVOKED, quote=True)
except Exception as e:
message.reply_text(f"**ERROR:** ```{e}```", quote=True)
Expand All @@ -64,7 +62,7 @@ def _revoke(client, message):
async def _token(client, message):
token = message.text.split()[-1]
WORD = len(token)
if WORD == 57 and token[1] == "/":
if WORD == 62 and token[1] == "/":
creds = None
global flow
if flow:
Expand Down
10 changes: 8 additions & 2 deletions bot/plugins/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
@Client.on_message(filters.private & filters.incoming & filters.command(BotCommands.Delete) & CustomFilters.auth_users)
def _delete(client, message):
user_id = message.from_user.id
if len(message.command) > 1:
if len(message.command) > 1 or message.reply_to_message:
sent_message = message.reply_text('🕵️**Checking Link...**', quote=True)
link = message.command[1]
if len(message.command) > 1:
link = message.command[1]
elif message.reply_to_message.entities[1].url:
link = message.reply_to_message.entities[1].url
else:
message.reply_text(Messages.PROVIDE_GDRIVE_URL.format(BotCommands.Delete[0]), quote=True)
return
LOGGER.info(f'Delete:{user_id}: {link}')
result = GoogleDrive(user_id).delete_file(link)
sent_message.edit(result)
Expand Down
8 changes: 6 additions & 2 deletions bot/plugins/download.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def _download(client, message):
sent_message.edit(Messages.DOWNLOAD_ERROR.format(file_path, link))


@Client.on_message(filters.private & filters.incoming & (filters.document | filters.audio | filters.video) & CustomFilters.auth_users)
@Client.on_message(filters.private & filters.incoming & (filters.document | filters.audio | filters.video | filters.photo) & CustomFilters.auth_users)
def _telegram_file(client, message):
user_id = message.from_user.id
sent_message = message.reply_text('🕵️**Checking File...**', quote=True)
Expand All @@ -56,6 +56,10 @@ def _telegram_file(client, message):
file = message.video
elif message.audio:
file = message.audio
elif message.photo:
file = message.photo
file.mime_type = "images/png"
file.file_name = f"IMG-{user_id}-{message.message_id}.png"
sent_message.edit(Messages.DOWNLOAD_TG_FILE.format(file.file_name, humanbytes(file.file_size), file.mime_type))
LOGGER.info(f'Download:{user_id}: {file.file_id}')
try:
Expand All @@ -68,7 +72,7 @@ def _telegram_file(client, message):
LOGGER.info(f'Deleteing: {file_path}')
os.remove(file_path)

@Client.on_message(filters.incoming & filters.private & filters.command(BotCommands.Ytdl) & CustomFilters.auth_users)
@Client.on_message(filters.incoming & filters.private & filters.command(BotCommands.YtDl) & CustomFilters.auth_users)
def _ytdl(client, message):
user_id = message.from_user.id
if len(message.command) > 1:
Expand Down
57 changes: 57 additions & 0 deletions bot/plugins/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from bot import SUPPORT_CHAT_LINK
from pyrogram import Client, filters
from bot.config import Messages as tr
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton


@Client.on_message(filters.private & filters.incoming & filters.command(['start']), group=2)
def _start(client, message):
client.send_message(chat_id = message.chat.id,
text = tr.START_MSG.format(message.from_user.mention),
reply_to_message_id = message.message_id
)


@Client.on_message(filters.private & filters.incoming & filters.command(['help']), group=2)
def _help(client, message):
client.send_message(chat_id = message.chat.id,
text = tr.HELP_MSG[1],
reply_markup = InlineKeyboardMarkup(map(1)),
reply_to_message_id = message.message_id
)

help_callback_filter = filters.create(lambda _, __, query: query.data.startswith('help+'))

@Client.on_callback_query(help_callback_filter)
def help_answer(c, callback_query):
chat_id = callback_query.from_user.id
message_id = callback_query.message.message_id
msg = int(callback_query.data.split('+')[1])
c.edit_message_text(chat_id = chat_id, message_id = message_id,
text = tr.HELP_MSG[msg], reply_markup = InlineKeyboardMarkup(map(msg))
)


def map(pos):
if(pos==1):
button = [
[InlineKeyboardButton(text = '-->', callback_data = "help+2")]
]
elif(pos==len(tr.HELP_MSG)-1):

button = [
[
InlineKeyboardButton(text = 'Support Chat', url = SUPPORT_CHAT_LINK),
InlineKeyboardButton(text = 'Feature Request', url = "https://github.com/viperadnan-git/google-drive-telegram-bot/issues/new")
],
[InlineKeyboardButton(text = '<--', callback_data = f"help+{pos-1}")]

]
else:
button = [
[
InlineKeyboardButton(text = '<--', callback_data = f"help+{pos-1}"),
InlineKeyboardButton(text = '-->', callback_data = f"help+{pos+1}")
],
]
return button
Loading