From 6645be300e55341eda6640892120a68042ef2aad Mon Sep 17 00:00:00 2001 From: hlf20010508 Date: Wed, 20 Sep 2023 21:49:34 +0800 Subject: [PATCH] refactor: refactor all - Refactor all files. - Remove command /status, use reply for status. --- .dockerignore | 9 +- .gitignore | 3 +- README.md | 7 +- bot.py | 603 ------------------- docker-compose.yml | 6 +- main.py | 32 + modules/client.py | 24 + modules/env.py | 24 + modules/global_var.py | 70 +++ modules/handlers/auth.py | 69 +++ modules/handlers/auto_delete.py | 31 + modules/handlers/clear.py | 19 + modules/handlers/help.py | 18 + modules/handlers/links.py | 31 + modules/handlers/start.py | 18 + modules/handlers/transfer.py | 107 ++++ modules/handlers/url.py | 99 +++ log.py => modules/log.py | 7 + onedrive.py => modules/onedrive.py | 7 +- modules/transfer.py | 113 ++++ modules/utils.py | 113 ++++ auth_server.py => server/auth_server.py | 2 +- {ssl => server/ssl}/server.crt | 0 {ssl => server/ssl}/server.key | 0 {templates => server/templates}/tg_code.html | 0 25 files changed, 793 insertions(+), 619 deletions(-) delete mode 100644 bot.py create mode 100644 main.py create mode 100644 modules/client.py create mode 100644 modules/env.py create mode 100644 modules/global_var.py create mode 100644 modules/handlers/auth.py create mode 100644 modules/handlers/auto_delete.py create mode 100644 modules/handlers/clear.py create mode 100644 modules/handlers/help.py create mode 100644 modules/handlers/links.py create mode 100644 modules/handlers/start.py create mode 100644 modules/handlers/transfer.py create mode 100644 modules/handlers/url.py rename log.py => modules/log.py (77%) rename onedrive.py => modules/onedrive.py (99%) create mode 100644 modules/transfer.py create mode 100644 modules/utils.py rename auth_server.py => server/auth_server.py (92%) rename {ssl => server/ssl}/server.crt (100%) rename {ssl => server/ssl}/server.key (100%) rename {templates => server/templates}/tg_code.html (100%) diff --git a/.dockerignore b/.dockerignore index e529f83..a752d7b 100755 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,4 @@ * -!bot.py -!onedrive.py -!auth_server.py -!ssl -!templates -!log.py \ No newline at end of file +!main.py +!modules +!server \ No newline at end of file diff --git a/.gitignore b/.gitignore index c9b8198..6be9ca5 100755 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ log temp session dev.sh -test.py \ No newline at end of file +test.py +.devrc \ No newline at end of file diff --git a/README.md b/README.md index 9bc6b75..f4d12ee 100755 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ That's why you need to prepare a lot of things to use this bot. ## Bot Command - `/start` to start with bot. - `/auth` to authorize telegram and onedrive. -- `/status` to show pinned status message. - `/clear` to clear all history except status message. - `/links message_link range` to transfer sequential restricted content. - `/url file_url` to upload file through url. @@ -43,7 +42,7 @@ Example: - Add this bot to a group or channel. - In the group or channel, forward or upload files(or videos, photos, gifs, stickers, voices). - If you want to transfer restricted content from a group or channel, right click the content, copy the message link, and send the link. -- Wait until the transfer completes. You can check status on pinned status message. +- Wait until the transfer completes. You can check status on replied message. - Use `/help` for more information about other command. ## Preparation @@ -57,8 +56,8 @@ Example: telegram-onedrive: ... volumes: - - /path/to/*.crt:/telegram-onedrive/ssl/server.crt - - /path/to/*.key:/telegram-onedrive/ssl/server.key + - /path/to/*.crt:/telegram-onedrive/server/ssl/server.crt + - /path/to/*.key:/telegram-onedrive/server/ssl/server.key ... ``` 3. Create a Telegram bot through [BotFather](https://t.me/BotFather). Record `token` as `tg_bot_token`. diff --git a/bot.py b/bot.py deleted file mode 100644 index 39eba85..0000000 --- a/bot.py +++ /dev/null @@ -1,603 +0,0 @@ -""" -:project: telegram-onedrive -:author: L-ING -:copyright: (C) 2023 L-ING -:license: MIT, see LICENSE for more details. -""" - -import os -import urllib3 -import asyncio -import math -import subprocess -import re -import inspect -from io import BytesIO -from telethon import TelegramClient, events -from telethon.tl import types -import requests -from onedrive import Onedrive -from log import logger -from urllib.parse import unquote -import time -import mimetypes - -if not os.path.exists('session'): - os.mkdir('session') - -urllib3.disable_warnings() - -status_bar = None - -cmd_helper = '''- /auth: Authorize for Telegram and OneDrive. -- /status: Show pinned status message. -- /clear: Clear all history except status message. - -- `/links` message_link range: Transfer sequential restricted content. -- `/url` file_url: Upload file through url. -- `/autoDelete true` to auto delete message. -- `/autoDelete false` to not auto delete message. -''' - -part_size = 2 * 1024 * 1024 - -# auth server -server_uri = os.environ["server_uri"] -# telegram api -tg_api_id = int(os.environ["tg_api_id"]) -tg_api_hash = os.environ["tg_api_hash"] -tg_user_phone = os.environ["tg_user_phone"] -tg_user_name = os.environ.get("tg_user_name", None) -# telegram bot -tg_bot_token = os.environ["tg_bot_token"] -# onedrive -od_client_id = os.environ["od_client_id"] -od_client_secret = os.environ["od_client_secret"] -remote_root_path = os.environ.get("remote_root_path", "/") - -delete_flag = True if os.environ.get("delete_flag", "false") == 'true' else False - -# clients -tg_bot = TelegramClient("session/bot", tg_api_id, tg_api_hash, sequential_updates=True).start( - bot_token=tg_bot_token -) - -tg_client = TelegramClient("session/user", tg_api_id, tg_api_hash, sequential_updates=True) - -onedrive = Onedrive( - client_id=od_client_id, - client_secret=od_client_secret, - redirect_uri=os.path.join(server_uri, "auth"), - remote_root_path=remote_root_path, -) - - -def cmd_parser(event): - return event.text.split()[1:] - - -async def clear_history(event): - ids = [] - async for message in tg_client.iter_messages(event.chat_id): - ids.append(message.id) - await tg_client.delete_messages(event.chat_id, ids) - - -async def delete_message(message): - global delete_flag - if delete_flag: - await message.delete() - - -# if message is not edited, it will raise MessageNotModifiedError -async def edit_message(bot, event, message): - try: - await bot.edit_message(event, message) - except: - pass - - -async def check_in_group(event): - if isinstance(event.message.peer_id, types.PeerUser): - await event.respond(''' -This bot must be used in a Group or Channel! - -Add this bot to a Group or Channel as Admin, and give it ability to Delete Messages. - ''') - raise events.StopPropagation - - -async def check_login(event): - try: - if await tg_client.get_me(): - return True - else: - await res_not_login(event) - return False - except: - await res_not_login(event) - return False - - -async def res_not_login(event): - await event.respond(''' -You haven't logined to Telegram. - ''') - await auth(event, propagate=True) - - -async def download_part(client, input_location, offset, part_size): - stream = client.iter_download( - input_location, offset=offset, request_size=part_size, limit=part_size - ) - part = await stream.__anext__() - await stream.close() - return part - - -async def multi_parts_uploader( - client, document, name, conn_num=5, progress_callback=None -): - input_location = types.InputDocumentFileLocation( - id=document.id, - access_hash=document.access_hash, - file_reference=document.file_reference, - thumb_size="", - ) - - upload_session = onedrive.multipart_upload_session_builder(name) - uploader = onedrive.multipart_uploader(upload_session, document.size) - - task_list = [] - total_part_num = ( - 1 if part_size >= document.size else math.ceil(document.size / part_size) - ) - current_part_num = 0 - current_size = 0 - offset = 0 - pre_offset = 0 - if progress_callback: - cor = progress_callback(current_size, document.size) - if inspect.isawaitable(cor): - await cor - - buffer = BytesIO() - while current_part_num < total_part_num: - task_list.append( - asyncio.ensure_future(download_part(client, input_location, offset, part_size)) - ) - current_part_num += 1 - if current_part_num < total_part_num: - offset += part_size - if current_part_num % conn_num == 0 or current_part_num == total_part_num: - for part in await asyncio.gather(*task_list): - buffer.write(part) - current_size += len(part) - task_list.clear() - buffer.seek(0) - await onedrive.multipart_upload(uploader, buffer, pre_offset, buffer.getbuffer().nbytes) - pre_offset = offset - buffer = BytesIO() - if progress_callback: - cor = progress_callback(current_size, document.size) - if inspect.isawaitable(cor): - await cor - buffer.close() - - -def get_filename_from_cd(cd): - """ - Get filename from Content-Disposition - """ - if not cd: - return None - fname = re.findall('filename=(.+)', cd) - if len(fname) == 0: - return None - return unquote(fname[0].strip().strip("'").strip('"')) - - -def get_filename_from_url(url): - name = unquote(url.split('/')[-1].split('?')[0].strip().strip("'").strip('"')) - if len(name) > 0: - return name - else: - return None - - -def get_ext(content_type): - return mimetypes.guess_extension(content_type) - - -async def multi_parts_uploader_from_url(url, progress_callback=None): - response = requests.get(url, stream=True) - if response.status_code == 200: - total_length = int(response.headers['Content-Length']) - name = get_filename_from_cd(response.headers.get('Content-Disposition')) - if not name: - name = get_filename_from_url(url) - if name: - ext = get_ext(response.headers['Content-Type']) - if ext != name.split('.')[-1]: - name = name.split('.')[0] + ext - else: - name = str(int(time.time())) + ext - - upload_session = onedrive.multipart_upload_session_builder(name) - uploader = onedrive.multipart_uploader(upload_session, total_length) - - offset = 0 - if progress_callback: - cor = progress_callback(offset, total_length) - if inspect.isawaitable(cor): - await cor - for chunk in response.iter_content(chunk_size=part_size): - buffer = BytesIO() - buffer.write(chunk) - buffer.seek(0) - await onedrive.multipart_upload(uploader, buffer, offset, buffer.getbuffer().nbytes) - offset += buffer.getbuffer().nbytes - if progress_callback: - cor = progress_callback(offset, total_length) - if inspect.isawaitable(cor): - await cor - return name - else: - raise Exception("File from url not found") - - -def get_link(string): - regex = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))" - url = re.findall(regex,string) - try: - link = [x[0] for x in url][0] - if link: - return link - else: - return False - except: - return False - - -@tg_bot.on(events.NewMessage(pattern="/start", incoming=True, from_users=tg_user_name)) -async def start(event): - """Send a message when the command /start is issued.""" - await event.respond(''' -Transfer files to Onedrive. - -Forward or upload files to me, or pass message link to transfer restricted content from group or channel. - -%s -- /help: Ask for help. - '''%cmd_helper) - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/help", incoming=True, from_users=tg_user_name)) -async def help(event): - """Send a message when the command /help is issued.""" - await event.respond(''' -%s - -- To transfer files, forward or upload to me. -- To transfer restricted content, right click the content, copy the message link, and send to me. -- Uploading through url will call Onedrive's API, which means Onedrive's server will visit the url and download the file for you. If the url is invalid to OneDrive, the bot will try using bot's uploader to transfer. -'''%cmd_helper) - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/auth", incoming=True, from_users=tg_user_name)) -async def auth(event, propagate=False): - await check_in_group(event) - auth_server = subprocess.Popen(('python', 'auth_server.py')) - async with tg_bot.conversation(event.chat_id) as conv: - - async def tg_code_callback(): - await conv.send_message( - "Please visit %s to input your code to login to Telegram." % server_uri - ) - res = requests.get(url=os.path.join(server_uri, "tg"), verify=False).json() - while not res["success"]: - await asyncio.sleep(1) - res = requests.get( - url=os.path.join(server_uri, "tg"), verify=False - ).json() - return res["code"] - - async def od_code_callback(): - res = requests.get( - url=os.path.join(server_uri, "auth"), - params={"get": True}, - verify=False, - ).json() - while not res["success"]: - await asyncio.sleep(1) - res = requests.get( - url=os.path.join(server_uri, "auth"), - params={"get": True}, - verify=False, - ).json() - return res["code"] - - await conv.send_message("Logining into Telegram...") - global tg_client - tg_client = await tg_client.start(tg_user_phone, code_callback=tg_code_callback) - await conv.send_message("Login to Telegram successful!") - - try: - onedrive.load_session() - except: - auth_url = onedrive.get_auth_url() - await conv.send_message( - "Here are the authorization url of OneDrive:\n\n%s" % auth_url - ) - code = await od_code_callback() - onedrive.auth(code) - await conv.send_message("Onedrive authorization successful!") - - async for message in tg_client.iter_messages(event.chat_id, filter=types.InputMessagesFilterPinned()): - await tg_client.unpin_message(event.chat_id, message) - global status_bar - status_bar = await conv.send_message("Status:\n\nNo job yet.") - await tg_bot.pin_message(event.chat_id, status_bar) - auth_server.kill() - if not propagate: - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/autoDelete", incoming=True, from_users=tg_user_name)) -async def auto_delete(event): - global delete_flag - error_message = ''' -Command `/autoDelete` Usage: - -`/autoDelete true` to auto delete message. -`/autoDelete false` to not auto delete message. -''' - cmd = cmd_parser(event) - if len(cmd) == 0: - await event.respond(error_message) - elif cmd[0] == 'true': - delete_flag = True - await event.respond('Bot will auto delete message.') - elif cmd[0] == 'false': - delete_flag = False - await event.respond("Bot won't auto delete message.") - else: - await event.respond(error_message) - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/status", incoming=True, from_users=tg_user_name)) -async def status(event): - await check_in_group(event) - if await check_login(event): - global status_bar - async for message in tg_client.iter_messages(event.chat_id, filter=types.InputMessagesFilterPinned()): - await tg_client.unpin_message(event.chat_id, message) - status_bar = await event.respond("Status:\n\nNo job yet.") - await tg_bot.pin_message(event.chat_id, status_bar) - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/clear", incoming=True, from_users=tg_user_name)) -async def clear(event): - await check_in_group(event) - await check_login(event) - await clear_history(event) - await status(event) - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/url", incoming=True, from_users=tg_user_name)) -async def url(event): - await check_in_group(event) - await check_login(event) - - async def callback(current, total): - current = current / (1024 * 1024) - total = total / (1024 * 1024) - status = "Uploaded %.2fMB out of %.2fMB: %.2f%%"% (current, total, current / total * 100) - logger(status) - msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) - - try: - cmd = cmd_parser(event) - _url = cmd[0] - # lest the url is bold - _url = _url.strip().strip('*') - name = unquote(_url.split('/')[-1]) - except: - await event.reply(''' -Command `/url` format wrong. - -Usage: `/url` file_url - ''') - raise events.StopPropagation - - if not get_link(_url): - await event.reply(logger("Please offer an HTTP url.")) - raise events.StopPropagation - - try: - logger('upload url: %s' % _url) - progress_url = onedrive.upload_from_url(_url) - logger('progress url: %s' % progress_url) - except Exception as e: - await event.reply(logger(e)) - raise events.StopPropagation - - try: - response = onedrive.upload_from_url_progress(progress_url) - progress = response.content - while progress['status'] in ['notStarted', 'inProgress']: - status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) - logger(status) - msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) - await asyncio.sleep(5) - response = onedrive.upload_from_url_progress(progress_url) - progress = response.content - status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) - logger(status) - if progress['status'] == 'completed': - logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) - msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) - if not delete_flag: - await event.reply('Done.') - await delete_message(event) - await edit_message(tg_bot, status_bar, 'Status:\n\nNo job yet.') - else: - logger('use local uploader to upload from url') - name = await multi_parts_uploader_from_url(_url, callback) - logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) - if not delete_flag: - await event.reply('Done.') - await delete_message(event) - await edit_message(tg_bot, status_bar, 'Status:\n\nNo job yet.') - - except Exception as e: - if 'errorCode' in progress.keys(): - if progress['errorCode'] == 'ParameterIsTooLong' or progress['errorCode'] == 'NameContainsInvalidCharacters': - # await event.reply(logger("Analysis: url too long.OneDrive API doesn't support long url.")) - try: - logger('use local uploader to upload from url') - name = await multi_parts_uploader_from_url(_url, callback) - logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) - if not delete_flag: - await event.reply('Done.') - await delete_message(event) - await edit_message(tg_bot, status_bar, 'Status:\n\nNo job yet.') - except Exception as e1: - await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e1), _url, progress_url, logger(progress))) - else: - await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e), _url, progress_url, logger(progress))) - if progress['errorCode'] == 'Forbidden': - await event.reply(logger("Analysis: url protocol is not HTTP, or the url has been forbidden because of too many failed requests.")) - elif progress['errorCode'] == 'NotFound' or progress['errorCode'] == 'operationNotFound': - await event.reply(logger("Analysis: content not found.")) - else: - await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e), _url, progress_url, logger(progress))) - - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(pattern="/links", incoming=True, from_users=tg_user_name)) -async def links(event): - await check_in_group(event) - await check_login(event) - try: - cmd = cmd_parser(event) - link = cmd[0] - head_message_id = int(link.split('/')[-1]) - link_body = '/'.join(link.split('/')[:-1]) - offsets = int(cmd[1]) - await delete_message(event) - for offset in range(offsets): - await tg_client.send_message(event.chat_id, message='%s/%d'%(link_body, head_message_id + offset)) - except: - await event.reply(''' -Command `/links` format wrong. - -Usage: `/links` message_link range - ''') - raise events.StopPropagation - raise events.StopPropagation - - -@tg_bot.on(events.NewMessage(incoming=True, from_users=tg_user_name)) -async def transfer(event): - await check_in_group(event) - await check_login(event) - - async def callback(current, total): - current = current / (1024 * 1024) - total = total / (1024 * 1024) - status = "Uploaded %.2fMB out of %.2fMB: %.2f%%"% (current, total, current / total * 100) - logger(status) - msg_link = 'https://t.me/c/%d/%d'%(event.message.peer_id.channel_id, event.message.id) - await edit_message(tg_bot, status_bar, 'Status:\n\n%s\n\n%s'%(msg_link, status)) - - if event.media and not isinstance(event.media, types.MessageMediaWebPage): - message = await tg_client.get_messages(event.message.peer_id, ids=event.message.id) - - try: - if "document" in event.media.to_dict().keys(): - if message.media: - if "document" in message.media.to_dict().keys(): - if event.media.document.id == message.media.document.id: - name = "%d%s" % (event.media.document.id, event.file.ext) - await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback) - logger("File uploaded to %s" % os.path.join(remote_root_path, name)) - await delete_message(message) - await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") - - if "photo" in event.media.to_dict().keys(): - if message.media: - if "photo" in message.media.to_dict().keys(): - if event.media.photo.id == message.media.photo.id: - name = "%d%s" % (event.media.photo.id, event.file.ext) - buffer = await message.download_media(file=bytes, progress_callback=callback) - onedrive.stream_upload(buffer, name) - logger("File uploaded to %s" % os.path.join(remote_root_path, name)) - await delete_message(message) - await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") - except Exception as e: - await event.reply('Error: %s' % logger(e)) - - else: - msg_link = get_link(event.text) - if msg_link: - try: - chat = "" - if "?single" in msg_link: - msg_link = msg_link.split("?single")[0] - msg_id = int(msg_link.split("/")[-1]) - if 't.me/c/' in msg_link: - if 't.me/b/' in msg_link: - chat = str(msg_link.split("/")[-2]) - else: - chat = int('-100' + str(msg_link.split("/")[-2])) - - message = await tg_client.get_messages(chat, ids=msg_id) - except: - logger('Not message link.') - await event.reply("Please offer a message link.\n\nUse /help for available command.") - raise events.StopPropagation - - if message: - try: - if "document" in message.media.to_dict().keys(): - name = "%d%s" % (message.media.document.id, message.file.ext) - await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback) - logger("File uploaded to %s" % os.path.join(remote_root_path, name)) - await delete_message(event) - await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") - - if "photo" in message.media.to_dict().keys(): - name = "%d%s" % (message.media.photo.id, message.file.ext) - buffer = await message.download_media(file=bytes, progress_callback=callback) - onedrive.stream_upload(buffer, name) - logger("File uploaded to %s" % os.path.join(remote_root_path, name)) - await delete_message(event) - await edit_message(tg_bot, status_bar, "Status:\n\nNo job yet.") - - except Exception as e: - await event.reply('Error: %s' % logger(e)) - else: - await event.reply(logger("Message not found.")) - else: - if event.text != '/auth': - logger('Unknown command.') - await event.reply("Use /help for available command.") - raise events.StopPropagation - - -def main(): - tg_bot.run_until_disconnected() - - -if __name__ == "__main__": - main() diff --git a/docker-compose.yml b/docker-compose.yml index 04988ed..deb4f98 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,8 @@ services: restart: always network_mode: host # volumes: - # - /path/to/*.crt:/telegram-onedrive/ssl/server.crt - # - /path/to/*.key:/telegram-onedrive/ssl/server.key + # - /path/to/*.crt:/telegram-onedrive/server/ssl/server.crt + # - /path/to/*.key:/telegram-onedrive/server/ssl/server.key # - telegram-onedrive-session:/telegram-onedrive/session environment: - server_uri=$server_uri @@ -20,6 +20,6 @@ services: - od_client_secret=$od_client_secret - remote_root_path=$remote_root_path - delete_flag=$delete_flag(optional) - command: python bot.py + command: python main.py # volumes: # telegram-onedrive-session: \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..b92c8dd --- /dev/null +++ b/main.py @@ -0,0 +1,32 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +import os +import urllib3 + + +def main(): + urllib3.disable_warnings() + if not os.path.exists('session'): + os.mkdir('session') + + from modules.client import tg_bot + + from modules.handlers.start import start_handler + from modules.handlers.help import help_handler + from modules.handlers.auth import auth_handler + from modules.handlers.auto_delete import auto_delete_handler + from modules.handlers.clear import clear_handler + from modules.handlers.links import links_handler + from modules.handlers.url import url_handler + from modules.handlers.transfer import transfer_handler + + tg_bot.run_until_disconnected() + + +if __name__ == "__main__": + main() diff --git a/modules/client.py b/modules/client.py new file mode 100644 index 0000000..78018fe --- /dev/null +++ b/modules/client.py @@ -0,0 +1,24 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +import os +from telethon import TelegramClient +from modules.onedrive import Onedrive +from modules.env import tg_api_id, tg_api_hash, tg_bot_token, od_client_id, od_client_secret, server_uri, remote_root_path + +tg_bot = TelegramClient("session/bot", tg_api_id, tg_api_hash, sequential_updates=True).start( + bot_token=tg_bot_token +) + +tg_client = TelegramClient("session/user", tg_api_id, tg_api_hash, sequential_updates=True) + +onedrive = Onedrive( + client_id=od_client_id, + client_secret=od_client_secret, + redirect_uri=os.path.join(server_uri, "auth"), + remote_root_path=remote_root_path, +) \ No newline at end of file diff --git a/modules/env.py b/modules/env.py new file mode 100644 index 0000000..3d9e0a2 --- /dev/null +++ b/modules/env.py @@ -0,0 +1,24 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +import os + +# auth server +server_uri = os.environ["server_uri"] +# telegram api +tg_api_id = int(os.environ["tg_api_id"]) +tg_api_hash = os.environ["tg_api_hash"] +tg_user_phone = os.environ["tg_user_phone"] +tg_user_name = os.environ.get("tg_user_name", None) +# telegram bot +tg_bot_token = os.environ["tg_bot_token"] +# onedrive +od_client_id = os.environ["od_client_id"] +od_client_secret = os.environ["od_client_secret"] +remote_root_path = os.environ.get("remote_root_path", "/") + +delete_flag = True if os.environ.get("delete_flag", "false") == 'true' else False \ No newline at end of file diff --git a/modules/global_var.py b/modules/global_var.py new file mode 100644 index 0000000..300f8b2 --- /dev/null +++ b/modules/global_var.py @@ -0,0 +1,70 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +PART_SIZE = 2 * 1024 * 1024 + + +cmd_helper = ''' +- /auth: Authorize for Telegram and OneDrive. +- /clear: Clear all history except status message. + +- `/links` message_link range: Transfer sequential restricted content. +- `/url` file_url: Upload file through url. +- `/autoDelete true` to auto delete message. +- `/autoDelete false` to not auto delete message. +''' + + +start_res = ''' +Transfer files to Onedrive. + +Forward or upload files to me, or pass message link to transfer restricted content from group or channel. +%s +- /help: Ask for help. +'''%cmd_helper + + +help_res = ''' +%s +- To transfer files, forward or upload to me. +- To transfer restricted content, right click the content, copy the message link, and send to me. +- Uploading through url will call Onedrive's API, which means Onedrive's server will visit the url and download the file for you. If the url is invalid to OneDrive, the bot will try using bot's uploader to transfer. +'''%cmd_helper + + +check_in_group_res = ''' +This bot must be used in a Group or Channel! + +Add this bot to a Group or Channel as Admin, and give it ability to Delete Messages. +''' + + +not_login_res = ''' +You haven't logined to Telegram. +''' + + +auto_delete_res = ''' +Command `/autoDelete` Usage: + +`/autoDelete true` to auto delete message. +`/autoDelete false` to not auto delete message. +''' + + +links_res = ''' +Command `/links` format wrong. + +Usage: `/links` message_link range +''' + + +url_res = ''' +Command `/url` format wrong. + +Usage: `/url` file_url +''' diff --git a/modules/handlers/auth.py b/modules/handlers/auth.py new file mode 100644 index 0000000..1c8ab53 --- /dev/null +++ b/modules/handlers/auth.py @@ -0,0 +1,69 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +import subprocess +import requests +import asyncio +import os +from telethon import events +from modules.env import tg_user_name, tg_user_phone, server_uri +from modules.utils import check_in_group +from modules.client import tg_bot, tg_client, onedrive + + +@tg_bot.on(events.NewMessage(pattern="/auth", incoming=True, from_users=tg_user_name)) +async def auth_handler(event, propagate=False): + await check_in_group(event) + auth_server = subprocess.Popen(('python', 'server/auth_server.py')) + async with tg_bot.conversation(event.chat_id) as conv: + + async def tg_code_callback(): + await conv.send_message( + "Please visit %s to input your code to login to Telegram." % server_uri + ) + res = requests.get(url=os.path.join(server_uri, "tg"), verify=False).json() + while not res["success"]: + await asyncio.sleep(1) + res = requests.get( + url=os.path.join(server_uri, "tg"), verify=False + ).json() + return res["code"] + + async def od_code_callback(): + res = requests.get( + url=os.path.join(server_uri, "auth"), + params={"get": True}, + verify=False, + ).json() + while not res["success"]: + await asyncio.sleep(1) + res = requests.get( + url=os.path.join(server_uri, "auth"), + params={"get": True}, + verify=False, + ).json() + return res["code"] + + await conv.send_message("Logining into Telegram...") + global tg_client + tg_client = await tg_client.start(tg_user_phone, code_callback=tg_code_callback) + await conv.send_message("Login to Telegram successful!") + + try: + onedrive.load_session() + except: + auth_url = onedrive.get_auth_url() + await conv.send_message( + "Here are the authorization url of OneDrive:\n\n%s" % auth_url + ) + code = await od_code_callback() + onedrive.auth(code) + await conv.send_message("Onedrive authorization successful!") + + auth_server.kill() + if not propagate: + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/auto_delete.py b/modules/handlers/auto_delete.py new file mode 100644 index 0000000..73346d0 --- /dev/null +++ b/modules/handlers/auto_delete.py @@ -0,0 +1,31 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from modules.client import tg_bot +from modules.utils import cmd_parser +from modules.env import tg_user_name +from modules.global_var import auto_delete_res + + +@tg_bot.on(events.NewMessage(pattern="/autoDelete", incoming=True, from_users=tg_user_name)) +async def auto_delete_handler(event): + + cmd = cmd_parser(event) + if len(cmd) == 0: + await event.respond(auto_delete_res) + elif cmd[0] == 'true': + from modules import env + env.delete_flag = True + await event.respond('Bot will auto delete message.') + elif cmd[0] == 'false': + from modules import env + env.delete_flag = False + await event.respond("Bot won't auto delete message.") + else: + await event.respond(auto_delete_res) + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/clear.py b/modules/handlers/clear.py new file mode 100644 index 0000000..38799f5 --- /dev/null +++ b/modules/handlers/clear.py @@ -0,0 +1,19 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from modules.client import tg_bot +from modules.env import tg_user_name +from modules.utils import check_in_group, check_login, clear_history + + +@tg_bot.on(events.NewMessage(pattern="/clear", incoming=True, from_users=tg_user_name)) +async def clear_handler(event): + await check_in_group(event) + await check_login(event) + await clear_history(event) + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/help.py b/modules/handlers/help.py new file mode 100644 index 0000000..7bdf957 --- /dev/null +++ b/modules/handlers/help.py @@ -0,0 +1,18 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from modules.client import tg_bot +from modules.env import tg_user_name +from modules.global_var import help_res + + +@tg_bot.on(events.NewMessage(pattern="/help", incoming=True, from_users=tg_user_name)) +async def help_handler(event): + """Send a message when the command /help is issued.""" + await event.respond(help_res) + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/links.py b/modules/handlers/links.py new file mode 100644 index 0000000..431d265 --- /dev/null +++ b/modules/handlers/links.py @@ -0,0 +1,31 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from modules.client import tg_bot, tg_client +from modules.env import tg_user_name +from modules.utils import check_in_group, check_login, cmd_parser, delete_message +from modules.global_var import links_res + + +@tg_bot.on(events.NewMessage(pattern="/links", incoming=True, from_users=tg_user_name)) +async def links_handler(event): + await check_in_group(event) + await check_login(event) + try: + cmd = cmd_parser(event) + link = cmd[0] + head_message_id = int(link.split('/')[-1]) + link_body = '/'.join(link.split('/')[:-1]) + offsets = int(cmd[1]) + await delete_message(event) + for offset in range(offsets): + await tg_client.send_message(event.chat_id, message='%s/%d'%(link_body, head_message_id + offset)) + except: + await event.reply(links_res) + raise events.StopPropagation + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/start.py b/modules/handlers/start.py new file mode 100644 index 0000000..de587da --- /dev/null +++ b/modules/handlers/start.py @@ -0,0 +1,18 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from modules.client import tg_bot +from modules.env import tg_user_name +from modules.global_var import start_res + + +@tg_bot.on(events.NewMessage(pattern="/start", incoming=True, from_users=tg_user_name)) +async def start_handler(event): + """Send a message when the command /start is issued.""" + await event.respond(start_res) + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/transfer.py b/modules/handlers/transfer.py new file mode 100644 index 0000000..6d91752 --- /dev/null +++ b/modules/handlers/transfer.py @@ -0,0 +1,107 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from telethon.tl import types +import os +from modules.client import tg_bot, tg_client, onedrive +from modules.env import tg_user_name, remote_root_path +from modules.utils import Callback, check_in_group, check_login, edit_message, delete_message, get_link +from modules.log import logger +from modules.transfer import multi_parts_uploader + + +@tg_bot.on(events.NewMessage(incoming=True, from_users=tg_user_name)) +async def transfer_handler(event): + await check_in_group(event) + await check_login(event) + + if event.media and not isinstance(event.media, types.MessageMediaWebPage): + message = await tg_client.get_messages(event.message.peer_id, ids=event.message.id) + + try: + if "document" in event.media.to_dict().keys(): + if message.media: + if "document" in message.media.to_dict().keys(): + if event.media.document.id == message.media.document.id: + name = "%d%s" % (event.media.document.id, event.file.ext) + status_message = await event.reply('In progress...', silent=True) + callback = Callback(event, status_message) + await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback.run) + logger("File uploaded to %s" % os.path.join(remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(message) + await delete_message(status_message) + + if "photo" in event.media.to_dict().keys(): + if message.media: + if "photo" in message.media.to_dict().keys(): + if event.media.photo.id == message.media.photo.id: + name = "%d%s" % (event.media.photo.id, event.file.ext) + status_message = await event.reply('In progress...', silent=True) + callback = Callback(event, status_message) + buffer = await message.download_media(file=bytes, progress_callback=callback.run) + onedrive.stream_upload(buffer, name) + logger("File uploaded to %s" % os.path.join(remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(message) + await delete_message(status_message) + except Exception as e: + await event.reply('Error: %s' % logger(e)) + + else: + msg_link = get_link(event.text) + if msg_link: + try: + chat = "" + if "?single" in msg_link: + msg_link = msg_link.split("?single")[0] + msg_id = int(msg_link.split("/")[-1]) + if 't.me/c/' in msg_link: + if 't.me/b/' in msg_link: + chat = str(msg_link.split("/")[-2]) + else: + chat = int('-100' + str(msg_link.split("/")[-2])) + + message = await tg_client.get_messages(chat, ids=msg_id) + except: + logger('Not message link.') + await event.reply("Please offer a message link.\n\nUse /help for available command.") + raise events.StopPropagation + + if message: + try: + if "document" in message.media.to_dict().keys(): + name = "%d%s" % (message.media.document.id, message.file.ext) + status_message = await event.reply('In progress...', silent=True) + callback = Callback(event, status_message) + await multi_parts_uploader(tg_client, message.media.document, name, progress_callback=callback.run) + logger("File uploaded to %s" % os.path.join(remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(event) + await delete_message(status_message) + + if "photo" in message.media.to_dict().keys(): + name = "%d%s" % (message.media.photo.id, message.file.ext) + status_message = await event.reply('In progress...', silent=True) + callback = Callback(event, status_message) + buffer = await message.download_media(file=bytes, progress_callback=callback.run) + onedrive.stream_upload(buffer, name) + logger("File uploaded to %s" % os.path.join(remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(event) + await delete_message(status_message) + + except Exception as e: + await event.reply('Error: %s' % logger(e)) + else: + await event.reply(logger("Message not found.")) + else: + if event.text != '/auth': + logger('Unknown command.') + await event.reply("Use /help for available command.") + raise events.StopPropagation \ No newline at end of file diff --git a/modules/handlers/url.py b/modules/handlers/url.py new file mode 100644 index 0000000..81161e4 --- /dev/null +++ b/modules/handlers/url.py @@ -0,0 +1,99 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from urllib.parse import unquote +import asyncio +import os +from modules.client import tg_bot, onedrive +from modules.env import tg_user_name +from modules.utils import Callback, check_in_group, check_login, edit_message, cmd_parser, get_link, delete_message +from modules.log import logger +from modules.transfer import multi_parts_uploader_from_url +from modules.global_var import url_res + + +@tg_bot.on(events.NewMessage(pattern="/url", incoming=True, from_users=tg_user_name)) +async def url_handler(event): + await check_in_group(event) + await check_login(event) + + try: + cmd = cmd_parser(event) + _url = cmd[0] + # lest the url is bold + _url = _url.strip().strip('*') + name = unquote(_url.split('/')[-1]) + except: + await event.reply(url_res) + raise events.StopPropagation + + if not get_link(_url): + await event.reply(logger("Please offer an HTTP url.")) + raise events.StopPropagation + + status_message = await event.reply('In progress...', silent=True) + + try: + logger('upload url: %s' % _url) + progress_url = onedrive.upload_from_url(_url) + logger('progress url: %s' % progress_url) + except Exception as e: + await event.reply(logger(e)) + raise events.StopPropagation + + try: + response = onedrive.upload_from_url_progress(progress_url) + progress = response.content + while progress['status'] in ['notStarted', 'inProgress']: + status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) + logger(status) + await edit_message(tg_bot, status_message, 'Status:\n%s' % status) + await asyncio.sleep(5) + response = onedrive.upload_from_url_progress(progress_url) + progress = response.content + status = "Uploaded: %.2f%%" % float(progress['percentageComplete']) + logger(status) + await edit_message(tg_bot, status_message, 'Status:\n%s' % status) + if progress['status'] == 'completed': + logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(event) + await delete_message(status_message) + else: + logger('use local uploader to upload from url') + callback = Callback(event, status_message) + name = await multi_parts_uploader_from_url(_url, callback.run) + logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(event) + await delete_message(status_message) + + except Exception as e: + if 'errorCode' in progress.keys(): + if progress['errorCode'] == 'ParameterIsTooLong' or progress['errorCode'] == 'NameContainsInvalidCharacters': + # await event.reply(logger("Analysis: url too long.OneDrive API doesn't support long url.")) + try: + logger('use local uploader to upload from url') + callback = Callback(event, status_message) + name = await multi_parts_uploader_from_url(_url, callback.run) + logger("File uploaded to %s"%os.path.join(onedrive.remote_root_path, name)) + await edit_message(tg_bot, status_message, 'Done.') + await delete_message(event) + await delete_message(status_message) + except Exception as e1: + await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e1), _url, progress_url, logger(progress))) + else: + await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e), _url, progress_url, logger(progress))) + if progress['errorCode'] == 'Forbidden': + await event.reply(logger("Analysis: url protocol is not HTTP, or the url has been forbidden because of too many failed requests.")) + elif progress['errorCode'] == 'NotFound' or progress['errorCode'] == 'operationNotFound': + await event.reply(logger("Analysis: content not found.")) + else: + await event.reply('Error: %s\nUpload url: %s\nProgress url: %s\n\nResponse: %s' % (logger(e), _url, progress_url, logger(progress))) + + raise events.StopPropagation \ No newline at end of file diff --git a/log.py b/modules/log.py similarity index 77% rename from log.py rename to modules/log.py index f9ef508..ba153dd 100644 --- a/log.py +++ b/modules/log.py @@ -1,3 +1,10 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + from datetime import datetime from traceback import print_exc from io import StringIO diff --git a/onedrive.py b/modules/onedrive.py similarity index 99% rename from onedrive.py rename to modules/onedrive.py index f629c73..d23341b 100644 --- a/onedrive.py +++ b/modules/onedrive.py @@ -20,6 +20,7 @@ import time from urllib.parse import unquote + def authenticate_request(self, request): if self._session is None: raise RuntimeError("""Session must be authenticated @@ -27,20 +28,23 @@ def authenticate_request(self, request): if self._session.is_expired() and 'offline_access' in self.scopes: self.refresh_token() - self.save_session(path='onedrive.session') + self.save_session(path='session/onedrive.session') request.append_option( HeaderOption("Authorization", "bearer {}".format(self._session.access_token))) + def create_session(self, item=None): return ItemCreateSessionRequestBuilder(self.append_to_request_url("createUploadSession"), self._client, item=item) + def http_response_init(self, status, headers, content): self._status = status self._headers = headers self._content = content + class Onedrive: def __init__(self, client_id, client_secret, redirect_uri, remote_root_path): api_base_url = "https://graph.microsoft.com/v1.0/" @@ -248,6 +252,7 @@ def post(self, begin, length, buffer, options=None): """ return self.request(begin, length, buffer, options).post() + # Overwrite the standard upload operation to use this one AuthProvider.authenticate_request = authenticate_request ItemRequestBuilder.create_session = create_session diff --git a/modules/transfer.py b/modules/transfer.py new file mode 100644 index 0000000..6f6e676 --- /dev/null +++ b/modules/transfer.py @@ -0,0 +1,113 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon.tl import types +import math +import inspect +from io import BytesIO +import asyncio +import requests +import time +from modules.client import onedrive +from modules.global_var import PART_SIZE +from modules.utils import get_filename_from_cd, get_filename_from_url, get_ext + + +async def download_part(client, input_location, offset): + stream = client.iter_download( + input_location, offset=offset, request_size=PART_SIZE, limit=PART_SIZE + ) + part = await stream.__anext__() + await stream.close() + return part + + +async def multi_parts_uploader( + client, document, name, conn_num=5, progress_callback=None +): + input_location = types.InputDocumentFileLocation( + id=document.id, + access_hash=document.access_hash, + file_reference=document.file_reference, + thumb_size="", + ) + + upload_session = onedrive.multipart_upload_session_builder(name) + uploader = onedrive.multipart_uploader(upload_session, document.size) + + task_list = [] + total_part_num = ( + 1 if PART_SIZE >= document.size else math.ceil(document.size / PART_SIZE) + ) + current_part_num = 0 + current_size = 0 + offset = 0 + pre_offset = 0 + if progress_callback: + cor = progress_callback(current_size, document.size) + if inspect.isawaitable(cor): + await cor + + buffer = BytesIO() + while current_part_num < total_part_num: + task_list.append( + asyncio.ensure_future(download_part(client, input_location, offset)) + ) + current_part_num += 1 + if current_part_num < total_part_num: + offset += PART_SIZE + if current_part_num % conn_num == 0 or current_part_num == total_part_num: + for part in await asyncio.gather(*task_list): + buffer.write(part) + current_size += len(part) + task_list.clear() + buffer.seek(0) + await onedrive.multipart_upload(uploader, buffer, pre_offset, buffer.getbuffer().nbytes) + pre_offset = offset + buffer = BytesIO() + if progress_callback: + cor = progress_callback(current_size, document.size) + if inspect.isawaitable(cor): + await cor + buffer.close() + + +async def multi_parts_uploader_from_url(url, progress_callback=None): + response = requests.get(url, stream=True) + if response.status_code == 200: + total_length = int(response.headers['Content-Length']) + name = get_filename_from_cd(response.headers.get('Content-Disposition')) + if not name: + name = get_filename_from_url(url) + if name: + ext = get_ext(response.headers['Content-Type']) + if ext != name.split('.')[-1]: + name = name.split('.')[0] + ext + else: + name = str(int(time.time())) + ext + + upload_session = onedrive.multipart_upload_session_builder(name) + uploader = onedrive.multipart_uploader(upload_session, total_length) + + offset = 0 + if progress_callback: + cor = progress_callback(offset, total_length) + if inspect.isawaitable(cor): + await cor + for chunk in response.iter_content(chunk_size=PART_SIZE): + buffer = BytesIO() + buffer.write(chunk) + buffer.seek(0) + await onedrive.multipart_upload(uploader, buffer, offset, buffer.getbuffer().nbytes) + offset += buffer.getbuffer().nbytes + if progress_callback: + cor = progress_callback(offset, total_length) + if inspect.isawaitable(cor): + await cor + return name + else: + raise Exception("File from url not found") \ No newline at end of file diff --git a/modules/utils.py b/modules/utils.py new file mode 100644 index 0000000..a232373 --- /dev/null +++ b/modules/utils.py @@ -0,0 +1,113 @@ +""" +:project: telegram-onedrive +:author: L-ING +:copyright: (C) 2023 L-ING +:license: MIT, see LICENSE for more details. +""" + +from telethon import events +from telethon.tl import types +import re +from urllib.parse import unquote +import mimetypes +from modules.client import tg_bot, tg_client +from modules.log import logger +from modules.global_var import check_in_group_res, not_login_res + +class Callback: + def __init__(self, event, status_message): + self.event = event + self.status_message = status_message + + async def run(self, current, total): + current = current / (1024 * 1024) + total = total / (1024 * 1024) + status = "Uploaded %.2fMB out of %.2fMB: %.2f%%"% (current, total, current / total * 100) + logger(status) + await edit_message(tg_bot, self.status_message, 'Status:\n%s' % status) + + +def cmd_parser(event): + return event.text.split()[1:] + + +async def clear_history(event): + ids = [] + async for message in tg_client.iter_messages(event.chat_id): + ids.append(message.id) + await tg_client.delete_messages(event.chat_id, ids) + + +async def delete_message(message): + from modules import env + if env.delete_flag: + await message.delete() + + +# if message is not edited, it will raise MessageNotModifiedError +async def edit_message(bot, event, message): + try: + await bot.edit_message(event, message) + except: + pass + + +async def check_in_group(event): + if isinstance(event.message.peer_id, types.PeerUser): + await event.respond(check_in_group_res) + raise events.StopPropagation + + +async def check_login(event): + try: + if await tg_client.get_me(): + return True + else: + await res_not_login(event) + return False + except: + await res_not_login(event) + return False + + +async def res_not_login(event): + from modules.handlers.auth import auth_handler + await event.respond(not_login_res) + await auth_handler(event, propagate=True) + + +def get_filename_from_cd(cd): + """ + Get filename from Content-Disposition + """ + if not cd: + return None + fname = re.findall('filename=(.+)', cd) + if len(fname) == 0: + return None + return unquote(fname[0].strip().strip("'").strip('"')) + + +def get_filename_from_url(url): + name = unquote(url.split('/')[-1].split('?')[0].strip().strip("'").strip('"')) + if len(name) > 0: + return name + else: + return None + + +def get_ext(content_type): + return mimetypes.guess_extension(content_type) + + +def get_link(string): + regex = r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))" + url = re.findall(regex,string) + try: + link = [x[0] for x in url][0] + if link: + return link + else: + return False + except: + return False diff --git a/auth_server.py b/server/auth_server.py similarity index 92% rename from auth_server.py rename to server/auth_server.py index ac9ffcf..0cf801f 100644 --- a/auth_server.py +++ b/server/auth_server.py @@ -48,4 +48,4 @@ def onedrive_code(): if __name__ == "__main__": server_uri = os.environ["server_uri"] port = int(server_uri.split(':')[-1].split('/')[0]) - app.run(host="0.0.0.0", port=port, ssl_context=("ssl/server.crt", "ssl/server.key")) + app.run(host="0.0.0.0", port=port, ssl_context=("server/ssl/server.crt", "server/ssl/server.key")) diff --git a/ssl/server.crt b/server/ssl/server.crt similarity index 100% rename from ssl/server.crt rename to server/ssl/server.crt diff --git a/ssl/server.key b/server/ssl/server.key similarity index 100% rename from ssl/server.key rename to server/ssl/server.key diff --git a/templates/tg_code.html b/server/templates/tg_code.html similarity index 100% rename from templates/tg_code.html rename to server/templates/tg_code.html