├── Aptfile ├── Procfile ├── README.md ├── Set.sh ├── app.json ├── iCopy ├── Bot.py ├── config │ └── text.toml ├── docs │ ├── CHANGELOG.md │ ├── TODO.md │ └── develop_update.md ├── drive │ ├── __init__.py │ └── gdrive.py ├── utils │ ├── __init__.py │ ├── __version__.py │ ├── callback_stage.py │ ├── dedupe_payload.py │ ├── get_functions.py │ ├── get_set.py │ ├── keyboard.py │ ├── load.py │ ├── messages.py │ ├── process_bar.py │ ├── purge_payload.py │ ├── restricted.py │ ├── size_payload.py │ ├── task_box.py │ └── task_payload.py └── workflow │ ├── __init__.py │ ├── copy_workflow.py │ ├── dedupe_workflow.py │ ├── purge_workflow.py │ ├── quick_workflow.py │ ├── regex_workflow.py │ ├── size_workflow.py │ └── start_workflow.py ├── requirements.txt └── runtime.txt /Aptfile: -------------------------------------------------------------------------------- 1 | wget 2 | wget2 3 | p7zip-full 4 | unzip 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: source Set.sh 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iCopy Version 0.2.0-Beta.6.7 2 | 3 | [iCopy](https://bbs.jsu.net/c/official-project/icopy/6) 4 | 5 | [![iCopy Telegram-Bot](https://img.shields.io/badge/iCopy-Telegram%20BOT-red?style=flat-square&logo=appveyor)](https://bbs.jsu.net/c/official-project/icopy/6) 6 | [![Programming Language](https://img.shields.io/badge/LANGUAGE-Python%203.6%2B-success?style=flat-square&logo=appveyor)](https://bbs.jsu.net/c/official-project/icopy/6) 7 | [![Version](https://img.shields.io/badge/Version-0.2.0--beta.6.7-ff69b4?style=flat-square&logo=appveyor)](https://bbs.jsu.net/c/official-project/icopy/6) 8 | [![License](https://img.shields.io/github/license/fxxkrlab/iCopy?style=flat-square&logo=appveyor)](https://bbs.jsu.net/c/official-project/icopy/6) 9 | [![DATABASE](https://img.shields.io/badge/DATABASE-MongoDB-brightgreen?style=flat-square&logo=appveyor)](https://github.com/mongodb/mongo) 10 | [![Stars](https://img.shields.io/github/stars/Nenokkadine/FClone-Bot?style=flat-square&logo=appveyor)](https://github.com/Nenokkadine/FClone-Bot) 11 | 12 | ## Deployment to Heroku 13 | 14 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https://github.com/Nenokkadine/Fclone-Bot/tree/master) 15 | 16 | ## Credits 17 | 18 | iCopy - [fxxkrlab](https://github.com/fxxkrlab/iCopy) 19 | Fclone - [Mawaya](https://github.com/mawaya/rclone) 20 | -------------------------------------------------------------------------------- /Set.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | cd ./iCopy/config 3 | echo "[tg] 4 | 5 | "token = "\"$BOT_TOKEN\" 6 | 7 | "usr_id = "\"$USER_ID\" 8 | 9 | [database] 10 | 11 | "db_connect_method = "\"$DB_CONNECT_METHOD\" 12 | 13 | "db_addr = "\"$DB_ADDRESS\" 14 | 15 | "db_port = "$DB_PORT"" 16 | 17 | "db_name = "\"$DB_NAME\" 18 | 19 | "db_user = "\"$DB_USERNAME\" 20 | 21 | "db_passwd = "\"$DB_PASS\" 22 | 23 | [general] 24 | 25 | "language = "\"$LANGUAGE\" 26 | 27 | "cloner = "\"$CLONER\" 28 | 29 | "option = "\"$OPTION\" 30 | 31 | "remote = "\"$RCLONE_RMT\" 32 | 33 | "parallel_c = "\"$PARALLEL_CHECKERS\" 34 | 35 | "parallel_t = "\"$PARALLEL_TRANSFERS\" 36 | 37 | "min_sleep = "\"$MIN_SLEEP\" 38 | 39 | "sa_path = "\"$SA_PATH\" 40 | 41 | run_args = $RUN_ARGS 42 | " >> conf.toml 43 | echo "[$RCLONE_RMT] 44 | type = drive 45 | scope = drive 46 | "service_account_file = /app/iCopy/accounts/$SA_INIT_FILE" 47 | "service_account_file_path = $SA_PATH" 48 | "team_drive = $FOLDER_ID" 49 | " >> rclone.conf 50 | cd .. 51 | wget --no-check-certificate -q $SA_ZIP_URL -O accounts.zip 52 | unzip -qq accounts.zip -d /app/iCopy/ 53 | chmod 777 Bot.py 54 | python3 Bot.py 55 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iCopy", 3 | "description": "Fclone Telegram Bot", 4 | "repository": "https://github.com/Nenokkadine/Fclone-Bot", 5 | "keywords": ["Fclone","Icopy"], 6 | "logo" : "https://f002.backblazeb2.com/file/jsuforum-upload/optimized/1X/cff2835c1652bb57a18aac42a3eee34b51cd9b89_2_1380x386.gif", 7 | "env": { 8 | "BOT_TOKEN": { 9 | "description": "Get from Telegram Botfather" 10 | }, 11 | "USER_ID": { 12 | "description": "get from telegram get_id_bot" 13 | }, 14 | "DB_CONNECT_METHOD": { 15 | "description": "DB Connect Method", 16 | "value": "mongodb+srv" 17 | }, 18 | "DB_ADDRESS": { 19 | "description": "MongoDB Host Address" 20 | }, 21 | "DB_PORT": { 22 | "description": "MongoDB Port, Default is 27017", 23 | "value": "27017" 24 | }, 25 | "DB_NAME": { 26 | "description": "U Can give any name, Default is iCopy", 27 | "value": "iCopy" 28 | }, 29 | "DB_USERNAME": { 30 | "description": "MongoDB Username" 31 | }, 32 | "DB_PASS": { 33 | "description": "MongoDB Password" 34 | }, 35 | "LANGUAGE": { 36 | "description": "English by Default Support cn and jp", 37 | "value": "eng" 38 | }, 39 | "CLONER": { 40 | "description": "Dont Change this", 41 | "value": "fclone" 42 | }, 43 | "OPTION": { 44 | "description": "Copy or Sync Default is Copy", 45 | "value": "copy" 46 | }, 47 | "RCLONE_RMT": { 48 | "description": "Give a Rclone Remote Name Default is also fine", 49 | "value": "icopy" 50 | }, 51 | "PARALLEL_CHECKERS": { 52 | "description": "Dont Give More than 300 Heroku Free Dyno may Crash", 53 | "value": "250" 54 | }, 55 | "PARALLEL_TRANSFERS": { 56 | "description": "Dont Give More than 300 Heroku Free Dyno may Crash", 57 | "value": "250" 58 | }, 59 | "MIN_SLEEP": { 60 | "description": "customize drive-pacer-min-sleep", 61 | "value": "1ms" 62 | }, 63 | "SA_INIT_FILE": { 64 | "description": "Give a service account File Name. ex 1.json " 65 | }, 66 | "RCLONE_CONFIG": { 67 | "description": "Dont Change", 68 | "value": "/app/iCopy/config/rclone.conf" 69 | }, 70 | "SA_ZIP_URL": { 71 | "description": "Service Accounts ZIP URL.It Should be Zipped Such that it should have a folder named accounts with SA in it " 72 | }, 73 | "TEAM_DRIVE_ID": { 74 | "description": "Destination Team Drive ID" 75 | }, 76 | "RUN_ARGS": { 77 | "description": "Only Change this if U Know", 78 | "value": "['-P', '--ignore-checksum' , '--stats=1s', '--log-level=DEBUG', '--log-file=/app/icopy_cloner_debug.log']" 79 | }, 80 | "SA_PATH": { 81 | "description": "Dont Change", 82 | "value": "/app/iCopy/accounts" 83 | } 84 | }, 85 | "buildpacks": [ 86 | { 87 | "url": "heroku/python" 88 | }, 89 | { 90 | "url": "https://github.com/heroku/heroku-buildpack-apt.git" 91 | }, 92 | { 93 | "url": "https://github.com/opendoor-labs/heroku-buildpack-p7zip.git" 94 | }, 95 | { 96 | "url" : "https://github.com/Nenokkadine/Fpack.git" 97 | } 98 | ], 99 | "formation": { 100 | "worker": { 101 | "quantity": 1, 102 | "size": "free" 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /iCopy/Bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os, sys, logging 5 | from telegram import Bot 6 | from telegram.utils.request import Request as TGRequest 7 | from utils import load 8 | from telegram.ext import ( 9 | Updater, 10 | CommandHandler, 11 | MessageHandler, 12 | Filters, 13 | CallbackQueryHandler, 14 | ConversationHandler, 15 | ) 16 | from utils import ( 17 | get_set as _set, 18 | get_functions as _func, 19 | task_box as _box, 20 | task_payload as _payload, 21 | callback_stage as _stage, 22 | __version__, 23 | ) 24 | 25 | from workflow import ( 26 | start_workflow as _start, 27 | quick_workflow as _quick, 28 | copy_workflow as _copy, 29 | size_workflow as _size, 30 | regex_workflow as _regex, 31 | purge_workflow as _purge, 32 | dedupe_workflow as _dedupe, 33 | ) 34 | from multiprocessing import Process as _mp, Manager 35 | from threading import Thread 36 | from utils.load import ns 37 | #from web import dash 38 | 39 | logging.basicConfig( 40 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO 41 | ) 42 | logger = logging.getLogger(__name__) 43 | 44 | # ############################### Main #################################### 45 | 46 | def main(): 47 | ### bot define 48 | request = TGRequest(con_pool_size=8) 49 | bot = Bot(token=f"{load.cfg['tg']['token']}", request=request) 50 | updater = Updater(bot=bot, use_context=True) 51 | 52 | ### judge is restart 53 | is_restart = load.db_counters.find_one({"_id": "is_restart"}) 54 | if is_restart is not None: 55 | if is_restart["status"] == 0: 56 | pass 57 | else: 58 | _func.check_restart(bot) 59 | 60 | else: 61 | load.db_counters.update( 62 | {"_id": "is_restart"}, {"status": 0}, upsert=True, 63 | ) 64 | 65 | dp = updater.dispatcher 66 | 67 | # Entry Conversation 68 | conv_handler = ConversationHandler( 69 | entry_points=[ 70 | # Entry Points 71 | CommandHandler("set", _set._setting), 72 | CommandHandler("menu", _start.menu), 73 | CommandHandler("quick", _quick.quick), 74 | CommandHandler("copy", _copy.copy), 75 | CommandHandler("task", _box.taskinfo), 76 | CommandHandler("size", _size.size), 77 | CommandHandler("purge", _purge.purge), 78 | CommandHandler("dedupe", _dedupe.dedupe), 79 | MessageHandler( 80 | Filters.regex(pattern=load.regex_entry_pattern), _regex.regex_entry 81 | ), 82 | ], 83 | 84 | states={ 85 | _stage.SET_FAV_MULTI: [ 86 | # fav settings function 87 | MessageHandler(Filters.text, _set._multi_settings_recieved), 88 | ], 89 | _stage.CHOOSE_MODE: [ 90 | # call function judged via callback pattern 91 | CallbackQueryHandler(_quick.quick, pattern="quick"), 92 | CallbackQueryHandler(_copy.copy, pattern="copy"), 93 | ], 94 | _stage.GET_LINK: [ 95 | # get Shared_Link states 96 | MessageHandler(Filters.text, _func.get_share_link), 97 | ], 98 | _stage.IS_COVER_QUICK: [ 99 | # cover quick setting 100 | CallbackQueryHandler(_func.modify_quick_in_db, pattern="cover_quick"), 101 | CallbackQueryHandler(_func.cancel, pattern="not_cover_quick"), 102 | MessageHandler(Filters.text, _func.cancel), 103 | ], 104 | _stage.GET_DST: [ 105 | # request DST 106 | CallbackQueryHandler(_copy.request_srcinfo), 107 | ], 108 | _stage.COOK_ID: [ 109 | # request to COOK ID 110 | MessageHandler(Filters.text, _size.size_handle), 111 | ], 112 | _stage.REGEX_IN: [ 113 | # regex in choose mode 114 | CallbackQueryHandler(_regex.regex_callback, pattern=r"quick|copy|size"), 115 | ], 116 | _stage.REGEX_GET_DST: [ 117 | # regex copy end 118 | CallbackQueryHandler(_regex.regex_copy_end), 119 | ], 120 | _stage.COOK_FAV_TO_SIZE: [CallbackQueryHandler(_size.pre_cook_fav_to_size),], 121 | _stage.COOK_FAV_PURGE: [CallbackQueryHandler(_purge.pre_to_purge),], 122 | _stage.COOK_ID_DEDU: [CallbackQueryHandler(_dedupe.dedupe_mode),], 123 | _stage.COOK_FAV_DEDU: [CallbackQueryHandler(_dedupe.dedupe_fav_mode),], 124 | _stage.FAV_PRE_DEDU_INFO: [CallbackQueryHandler(_dedupe.pre_favdedu_info)], 125 | }, 126 | fallbacks=[CommandHandler("cancel", _func.cancel)], 127 | ) 128 | 129 | def stop_and_restart(): 130 | progress.terminate() 131 | load.myclient.close() 132 | updater.stop() 133 | os.execl(sys.executable, sys.executable, *sys.argv) 134 | 135 | def restart(update, context): 136 | restart_msg = update.message.reply_text(load._text[load._lang]["is_restarting"]) 137 | restart_chat_id = restart_msg.chat_id 138 | restart_msg_id = restart_msg.message_id 139 | load.db_counters.update_one( 140 | {"_id": "is_restart"}, 141 | { 142 | "$set": { 143 | "status": 1, 144 | "chat_id": restart_chat_id, 145 | "message_id": restart_msg_id, 146 | } 147 | }, 148 | True, 149 | ) 150 | Thread(target=stop_and_restart).start() 151 | 152 | dp.add_handler(conv_handler) 153 | dp.add_handler(CommandHandler("start", _start.start)) 154 | dp.add_handler(CommandHandler("reset", _box.task_reset)) 155 | dp.add_handler(CommandHandler("kill", _func.taskill)) 156 | dp.add_handler(CommandHandler("ver", _func._version)) 157 | 158 | dp.add_handler( 159 | CommandHandler( 160 | "restart", 161 | restart, 162 | filters=Filters.user(user_id=int(load.cfg["tg"]["usr_id"])), 163 | ) 164 | ) 165 | 166 | dp.add_error_handler(_func.error) 167 | 168 | updater.start_polling() 169 | logger.info("Fxxkr LAB iCopy " + __version__.__version__ + " Start") 170 | updater.idle() 171 | 172 | 173 | if __name__ == "__main__": 174 | ns.x = 0 175 | progress = _mp(target=_payload.task_buffer, args=(ns,)) 176 | progress.start() 177 | #web = _mp(target=dash.dashboard) 178 | #web.start() 179 | main() 180 | -------------------------------------------------------------------------------- /iCopy/config/text.toml: -------------------------------------------------------------------------------- 1 | 2 | [cn] 3 | start = "Hi! replace ~ 欢迎使用 iCopy" #replace==待替换占位符 4 | guide_to_menu = "请输入 '/menu' 选择模式" 5 | menu_msg = "请选择模式" 6 | quick_mode = "极速模式" 7 | copy_mode = "自定义模式" 8 | size_mode = "文件统计" 9 | purge_mode = "回收站清理" 10 | dedupe_mode = "文件去重" 11 | mode_select_msg = "本次任务您选择了 ┋replace┋" #replace==待替换占位符 12 | request_share_link = "请输入 Google Drive 分享链接 或 分享ID" 13 | request_dst_target = "请选择文件保存位置" 14 | is_cover = "覆盖" 15 | not_cover = "返回" 16 | is_cover_quick_msg = "QUICK\\_MODE 已存在指定快捷路径\n是否需要覆盖" 17 | insert_quick_success = "QUICK\\_MODE 指定快捷路径已设置成功" 18 | modify_quick_success = "QUICK\\_MODE 指定快捷路径已更新成功" 19 | null_fav_quick = "未设置QUICK\\_MODE 转存目录\n请设置后再次尝试" 20 | null_fav = "未设置COPY\\_MODE 转存目录\n请设置后再次尝试" 21 | ready_to_task = "转存任务准备中..." 22 | doing = "正在执行任务" 23 | done = "任务成功" 24 | killed = "任务终止" 25 | current_task_id = "任务编号 : " 26 | task_start_time = "任务开始时间 : " 27 | task_finished_time = "任务结束时间 : " 28 | task_files_size = "文件总大小 : " 29 | task_files_num = "任务文件数 : " 30 | task_status = "任务状态 : " 31 | elapsed_time = "总耗时 : " 32 | cancel_msg = "欢迎再次使用 iCopy" 33 | get_quick_count_invaild = "quick 模式下的快捷目录只允许存在一个" 34 | get_multi_fav_error = "请根据'/set'规则提交目录设置" 35 | get_single_fav_error = "请根据'/set rule'规则发送命令" 36 | get_multi_in_single = "多行设置请使用 '/set'" 37 | task_src_info = "[任务目标]:" 38 | task_dst_info = "[转存地址]:" 39 | is_set_dedupe = "设置收藏夹遇到错误" 40 | set_fav_success = "设置收藏夹成功" 41 | delete_fav_success = "删除收藏夹成功" 42 | delete_quick_success = "QUICK_MODE 指定快捷路径已删除" 43 | show_fav_list = "下列为已设置的收藏夹" 44 | show_fav_list_null = "未设置任何收藏夹" 45 | is_restarting = "iCopy机器人正在重启...\n请在30秒后开始使用" 46 | is_killed_by_user = "任务已被用户终止" 47 | is_current_task = "当前任务 : \n\n" 48 | current_task_src_name = "源文件 : " 49 | current_task_dst_name = "目标地 : " 50 | is_not_current_task = "当前没有任务" 51 | show_wait_list = "个任务正在队列中\n\n" 52 | show_wait_list_null = "等待队列为空" 53 | interrupted = "任务中断" 54 | is_interrupted_error = "任务意外中断" 55 | restart_success = "iCopy 重启完成" 56 | add_task_successful = "添加任务成功" 57 | purge_fav = "清空收藏夹 (不包含 quick目录)" 58 | reset_successful = "任务 ID : replace 已被成功重置" #replace==待替换占位符 59 | over_limit_error = "超出最大可重置任务序号" 60 | over_limit_to_check = "超出最大可查询任务序号" 61 | over_limit_to_dedupe = "超出最大可去重任务序号" 62 | global_command_error = "输入的命令或者命令格式不存在" 63 | ready_to_size = "正在准备统计" 64 | sizing = "正在统计中,请耐心等待\n如果同时存在复制任务,则会影响复制与统计双方的准确性" 65 | total_file_num = "总文件数 : " 66 | total_file_size = "总大小 : " 67 | sizing_done = "统计完成" 68 | task_is_in_queue = "任务正在队列中" 69 | finished_could_be_check = "只能查询已完成的任务" 70 | finished_could_be_dedupe = "只能对已完成的任务进行去重" 71 | support_error = "指定的任务不支持查询" 72 | request_target_folder = "请选择目标收藏夹" 73 | ready_to_purge = "正在准备清空回收站" 74 | purging = "正在清空回收站,请耐心等待\n如果同时存在复制任务,则会影响复制与清空回收站双方的准确性" 75 | purging_done = "已清空回收站" 76 | request_dedupe_mode = "请选择去重模式 规则" 77 | ready_to_dedupe = "正在准备文件去重" 78 | deduping = "正在执行去重文件,请耐心等待\n如果同时存在复制任务,则会影响复制与文件去重双方的准确性" 79 | deduping_done = "文件去重已完成" 80 | is_folder_not_drive = "被选择的收藏夹是一个'文件夹'不是'团队盘'\n无法清空回收站" 81 | 82 | [eng] 83 | start = "Hi! replace . Welcome to use iCopy" #replace==symbolic placeholder 84 | guide_to_menu = "Please Input '/menu' to Choose run mode" 85 | menu_msg = "Pls Choose the Mode" 86 | quick_mode = "QUICK MODE" 87 | copy_mode = "COPY MODE" 88 | size_mode = "Size" 89 | purge_mode = "Empty Recycle bin" 90 | dedupe_mode = "dedupe mode" 91 | mode_select_msg = "┋replace┋ has be choosen" #replace==symbolic placeholder 92 | request_share_link = "Pls input Shared_Link or Shared_ID" 93 | request_dst_target = "Please select the destination path for the task" 94 | is_cover = "Cover it" 95 | not_cover = "Cancel" 96 | is_cover_quick_msg = "QUICK\\_MODE The specified directory already exists\nDo you need to cover it?" 97 | insert_quick_success = "QUICK\\_MODE specified path is setted successfully" 98 | modify_quick_success = "QUICK\\_MODE specified path is renewed successfully" 99 | null_fav_quick = "QUICK\\_MODE directory is not set\nPlease try again after setting" 100 | null_fav = "COPY\\_MODE directory is not set\nPlease try again after setting" 101 | ready_to_task = "Now is Ready to Task..." 102 | doing = "Transferring" 103 | done = "Done" 104 | killed = "Killed" 105 | current_task_id = "task id : " 106 | task_start_time = "Task start time : " 107 | task_finished_time = "Task end time : " 108 | task_files_size = "Total file size : " 109 | task_files_num = "Task file num : " 110 | task_status = "task_status : " 111 | elapsed_time = "Elapsed Time : " 112 | cancel_msg = "Welcome to enjoy with iCopy again" 113 | get_quick_count_invaild = "Only ONE Dst ID can be specified under quick mode" 114 | get_multi_fav_error = "pls submit DST ID according '/set'" 115 | get_single_fav_error = "pls submit single DST ID according '/set rule'" 116 | get_multi_in_single = "pls use '/set' while modify multi DST IDs" 117 | task_src_info = "[Resource From]:" 118 | task_dst_info = "[Transfer To]:" 119 | is_set_err = "SET FAV ERROR" 120 | set_fav_success = "set Favorites success" 121 | delete_fav_success = "Favorites deleted successfully" 122 | delete_quick_success = "QUICK\\_MODE specified path is deleted successfully" 123 | show_fav_list = "The following are favorites that have been set" 124 | show_fav_list_null = "Favorites are not set" 125 | is_restarting = "iCopy is restarting...\nPlease start using in 30 seconds" 126 | is_killed_by_user = "Task is killed by user" 127 | is_current_task = "Current Task : \n\n" 128 | current_task_src_name = "Source : " 129 | current_task_dst_name = "Destination : " 130 | is_not_current_task = "No Task is in Processing" 131 | show_wait_list = "task in the queue\n\n" 132 | show_wait_list_null = "There is no task in waiting" 133 | interrupted = "Interrupted" 134 | is_interrupted_error = "Unexpected interruption" 135 | restart_success = "iCopy restart is complete" 136 | add_task_successful = "Add Task Successful" 137 | purge_fav = "purge Favorites (except Quick path)" 138 | reset_successful = "Task ID: replace has been reset successfully" #replace==symbolic placeholder 139 | over_limit_error = "Exceeded maximum resettable task number" 140 | over_limit_to_check = "Exceeded maximum task number which could be check" 141 | over_limit_to_dedupe = "Exceeded maximum task number which could be dedupe" 142 | global_command_error = "The input command or command format does not exist" 143 | ready_to_size = "Preparing statistics" 144 | sizing = "In statistics, pls wait \nIf there is a copy task, it will affect the accuracy of both copy and statistics" 145 | total_file_num = "Total Files : " 146 | total_file_size = "Total Size : " 147 | sizing_done = "Size Finished" 148 | task_is_in_queue = "Task is in the queue" 149 | finished_could_be_check = "You could only check the task which is finished" 150 | finished_could_be_dedupe = "You could only dedupe the task which is finished" 151 | support_error = "The specified task does not support queries" 152 | request_target_folder = "Pls choose the TARGET drive or folder" 153 | ready_to_purge = "Ready to Purge" 154 | purging = "In empty recycle bin, pls wait \nIf there is a copy task, it will affect the accuracy of both copy and recyle bin empting" 155 | purging_done = "The recycle bin has been emptied" 156 | request_dedupe_mode = "Please select to dedupe mode rule" 157 | ready_to_dedupe = "Ready to dedupe" 158 | deduping = "In deduping task, pls wait \nIf there is a copy task, it will affect the accuracy of both copy and dedupe" 159 | deduping_done = "The task is be deduped" 160 | is_folder_not_drive = "The selected favorites is a 'folder' not a 'shared drive'\n Can not be purged" 161 | 162 | [jp] 163 | start = "Hi! replace . iCopyへようこそ" #replace==置き換え指示記号 164 | guide_to_menu = "実行モードを選択するには、'/menu' を入力してください" 165 | menu_msg = "ご覧のモードを選んでください" 166 | quick_mode = "「クイック」" 167 | copy_mode = "「カスタマイズ」" 168 | size_mode = "「サイズ」" 169 | purge_mode = "「ごみ箱を空にする」" 170 | dedupe_mode = "「重複を取り除く」" 171 | mode_select_msg = "┋replace┋が選択されました" #replace==置き換え指示記号 172 | request_share_link = "共有リンクまたは共有IDを入力してください" 173 | request_dst_target = "ファイルの保存場所を選択してください" 174 | is_cover = "カバー" 175 | not_cover = "キャンセル" 176 | is_cover_quick_msg = "QUICK_MODE指定されたフォルダーはすでに存在します\n新しいフォルダで上書きしますか?" 177 | insert_quick_success = "QUICK_MODEで指定されたフォルダーが正常に設定されました" 178 | modify_quick_success = "QUICK_MODEで指定されたフォルダーが更新されました" 179 | null_fav_quick = "QUICK\\_MODEフォルダーが設定されていません\n設定後にもう一度お試しください" 180 | null_fav = "COPY\\_MODEフォルダーが設定されていません\n設定後にもう一度お試しください" 181 | ready_to_task = "任務準備中..." 182 | doing = "タスク実行中" 183 | done = "任務成功" 184 | killed = "任務中止" 185 | current_task_id = "タスク番号 : " 186 | task_start_time = "タスク開始時刻 : " 187 | task_finished_time = "タスク終了時間 : " 188 | task_files_size = "ファイルサイズ : " 189 | task_files_num = "タスクファイル数 : " 190 | task_status = "タスク状態 : " 191 | elapsed_time = "合計時間 : " 192 | cancel_msg = "再びiCopyへようこそ" 193 | get_quick_count_invaild = "「クイックモード」で一つフォルダーしか指定されることができない" 194 | get_multi_fav_error = "'/set'にルールをよって、フォルダーのIDを入力してください" 195 | get_single_fav_error = "'/set rule'にルールをよって、フォルダーのIDを入力してください" 196 | get_multi_in_single = "複数のフォルダーを設定する場合は、「/set」コマンドを使用してください" 197 | task_src_info = "[任務目標]:" 198 | task_dst_info = "[保存場所]:" 199 | is_set_dedupe = "お気に入りを設定するとエラーが発生します" 200 | set_fav_success = "お気に入りを設定しました" 201 | delete_fav_success = "お気に入り削除に成功" 202 | delete_quick_success = "QUICK_MODEで指定されたフォルダーが削除されました" 203 | show_fav_list = "以下は既設のお気に入りである" 204 | show_fav_list_null = "お気に入りは設定いない" 205 | is_restarting = "iCopyが再起動しています...\n30秒後に使用を開始してください" 206 | is_killed_by_user = "タスクはユーザによって終了される" 207 | is_current_task = "進行中のタスク : \n\n" 208 | current_task_src_name = "源フォルダー : " 209 | current_task_dst_name = "目標フォルダー : " 210 | is_not_current_task = "進行中のタスクがない" 211 | show_wait_list = "タスクが待機しています\n\n" 212 | show_wait_list_null = "待機中のタスクはありません" 213 | interrupted = "タスク中断" 214 | is_interrupted_error = "予期しない中断" 215 | restart_success = "iCopyの再開が完了する" 216 | add_task_successful = "タスク追加成功" 217 | purge_fav = "お気に入りをクリア(クイックフォルダーを除く)" 218 | reset_successful = "タスク ID: replace がリセットされた"#replace==置き換え指示記号 219 | over_limit_error = "最大リセット可能タスク番号を超える" 220 | over_limit_to_check = "最大チェック可能タスク番号を超える" 221 | over_limit_to_dedupe = "最大重複除外可能タスク番号を超える" 222 | global_command_error = "入力するコマンドやコマンドフォーマットは存在しない" 223 | ready_to_size = "統計を準備している" 224 | sizing = "統計中ですので、お待ちください\nコピータスクが同時に存在する場合,コピーと統計の双方の正確さに影響する" 225 | total_file_num = "総 ファイル 数 : " 226 | total_file_size = "総 サイズ : " 227 | sizing_done = "統計終了" 228 | task_is_in_queue = "タスクが列に並んでいる" 229 | finished_could_be_check = "既に完了した任務しか調べられない" 230 | finished_could_be_dedupe = "すでに完了したタスクのみが重複ファイルの除去を行うことができる" 231 | support_error = "指定の任務の照会を支持しない" 232 | request_target_folder = "目標フォルダーを選んでください" 233 | ready_to_purge = "ごみ箱を空にする準備をしています" 234 | purging = "ごみ箱をクリアしています。\n同時にコピーの任務がある場合、コピーとごみ箱を空にすることの両方の正確さに影響します" 235 | purging_done = "既にごみ箱を空にした" 236 | request_dedupe_mode = "重複除外モードのルールを選択してください" 237 | ready_to_dedupe = "重複ファイルを除去する準備をしています" 238 | deduping = "重複ファイルを除去しています。\n同時にコピーの任務がある場合、コピーと重複除外の両方の正確さに影響します" 239 | deduping_done = "重複ファイルの除去が完了しました" 240 | is_folder_not_drive = "選択されたお気に入りは「フォルダ」ではなく「シェアドライバ」で\nゴミ箱を空にできない" -------------------------------------------------------------------------------- /iCopy/docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # iCopy v0.2 CHANGELOG 2 | 3 | ## version 0.2.0-beta.6.7 4 | 5 | + Update : 6 | + ADD : renew requirements.txt. 7 | + ADD : "\_\_version\_\_" 8 | 9 | + Fixbugs : 10 | + Compatible with the old version of the database format 11 | + Fix floatify format error when the size is 0 12 | 13 | ## version 0.2.0-beta.6.6 14 | 15 | + Update : 16 | + Block "googleapiclient.discovery" warning prompt. 17 | + Deprecated "cache_discovery" 18 | 19 | ## version 0.2.0-beta.6.5 20 | 21 | + Fixbugs: 22 | + FIX : local variable assignment error 23 | + FIX : try to change restart function args 24 | 25 | ## version 0.2.0-beta.6.4 26 | 27 | + Fixbugs: 28 | + FIX : "/set" function "import as name" error 29 | 30 | ## version 0.2.0-beta.6.3 31 | 32 | + Update : 33 | + ADD : "/kill size","/kill purge","/kill dedupe" was added to terminate the execution of these tasks 34 | 35 | + Root Command: 36 | 37 | + start - nothing just say hello 38 | + menu - main entry point 39 | quick - quick mode 40 | copy - full mode 41 | set - customize settings 42 | task - task query 43 | reset - restore task 44 | size - just size task 45 | dedupe - dedupe drives and folders 46 | purge - delete files and folder in specified fav trash bin 47 | cancel - cancel TG conversation 48 | kill - kill task 49 | ver - check iCopy version 50 | restart - restart iCopy 51 | 52 | + Child Command: 53 | 54 | + set - customize settings 55 | ┖ set - batch way 56 | ┖ set rule - rules 57 | ┖ fav|quick +/- id - single way 58 | ┖ set purge - purge favorites 59 | + size - size query 60 | ┖ size - size the shared resource 61 | ┖ size id - size specified task 62 | ┖ size fav - size specified favorites 63 | + dedupe - dedupe drives and folders 64 | ┖ dedupe - dedupe specified favorites 65 | ┖ dedupe id - dedupe specified task 66 | + task - task query 67 | ┖ task - task in processing 68 | ┖ task list - future 10 tasks 69 | ┖ task id - show the specified task 70 | + reset - restore task 71 | ┖ reset - restore current task 72 | ┖ reset id - restore the specified task 73 | + kill - kill task 74 | ┖ kill - kill current transferring task 75 | ┖ kill task - kill current transferring task 76 | ┖ kill size - kill sizing task 77 | ┖ kill purge - kill purge task 78 | ┖ kill dedupe - kill dedupe task 79 | 80 | ## version 0.2.0-beta.6.2 81 | 82 | + Update : 83 | + ADD : Add "rmdir" operation to the "/Purge" function to clear the empty folder in the root directory. 84 | 85 | + Fixbugs : 86 | + FIX : "/dedupe" sendMsg error. 87 | + FIX : Insert DATABASE error while dedupe payload finished. 88 | 89 | ## version 0.2.0-beta.6.1 90 | 91 | + Update : 92 | + ADD : "/dedupe" Now you can choose favorites to dedupe 93 | + CHANGE : Move the stage tag uniformly to new file “utils/callback_stage.py" 94 | 95 | + Fixbugs : 96 | + Judge select favorites if is shared drive when you use "/purge" mode. 97 | + Separately define bot variables in asynchronous-process to prevent errors in connecting Telegram. 98 | 99 | + Root Command: 100 | 101 | + start - nothing just say hello 102 | + menu - main entry point 103 | quick - quick mode 104 | copy - full mode 105 | set - customize settings 106 | task - task query 107 | reset - restore task 108 | size - just size task 109 | dedupe - dedupe drives and folders 110 | purge - delete files and folder in specified fav trash bin 111 | cancel - cancel TG conversation 112 | kill - kill task which is in processing 113 | ver - check iCopy version 114 | restart - restart iCopy 115 | 116 | + Child Command: 117 | 118 | + set - customize settings 119 | ┖ set - batch way 120 | ┖ set rule - rules 121 | ┖ fav|quick +/- id - single way 122 | ┖ set purge - purge favorites 123 | + size - size query 124 | ┖ size - size the shared resource 125 | ┖ size id - size specified task 126 | ┖ size fav - size specified favorites 127 | + dedupe - dedupe drives and folders 128 | ┖ dedupe - dedupe specified favorites 129 | ┖ dedupe id - dedupe specified task via task id 130 | + task - task query 131 | ┖ task - task in processing 132 | ┖ task list - future 10 tasks 133 | ┖ task id - show the specified task 134 | + reset - restore task 135 | ┖ reset - restore current task 136 | ┖ reset id - restore the specified task 137 | 138 | ## version 0.2.0-beta.6 139 | 140 | + Update : 141 | + ADD : insert more details into Database and more initializated data 142 | + ADD : feedback dst endpoint link when task end normally 143 | + ADD : "/task id" only support the task which is start after v0.2.0b6 144 | + ADD : mark tasks that have been reset in the database 145 | + ADD : "/size id" & "/size fav" 146 | + ADD : "/purge" to empty shared drive trash bin 147 | + ADD : "/dedupe id" to dedupe task 148 | + ADD : Record the last time of size and dedupe 149 | 150 | + Fix : 151 | + FIX : Update RegEX Rules 152 | 153 | + Root Command: 154 | 155 | + start - nothing just say hello 156 | + menu - main entry point 157 | quick - quick mode 158 | copy - full mode 159 | set - customize settings 160 | task - task query 161 | reset - restore task 162 | size - just size task 163 | dedupe - dedupe specified task 164 | purge - delete files and folder in specified fav trash bin 165 | cancel - cancel TG conversation 166 | kill - kill task which is in processing 167 | ver - check iCopy version 168 | restart - restart iCopy 169 | 170 | + Child Command: 171 | 172 | + set - customize settings 173 | ┖ set - batch way 174 | ┖ set rule - rules 175 | ┖ fav|quick +/- id - single way 176 | ┖ set purge - purge favorites 177 | + size - size query 178 | ┖ size - size the shared resource 179 | ┖ size id - size specified task 180 | ┖ size fav - size specified favorites 181 | + task - task query 182 | ┖ task - task in processing 183 | ┖ task list - future 10 tasks 184 | ┖ task id - show the specified task 185 | + reset - restore task 186 | ┖ reset - restore current task 187 | ┖ reset id - restore the specified task 188 | 189 | ## version 0.2.0-beta.5.1 190 | 191 | + Update : 192 | + ADD : More task info into Database 193 | 194 | + Fixbugs : 195 | + FIX : delete "directly in" mode keyboard after selection is choosen 196 | + FIX : Purge local var after Conversation END 197 | The task will not be committed twice now 198 | + FIX : "/task list" error 199 | Now "/task list" will display up to 10 tasks pending 200 | 201 | + Root Command: 202 | 203 | + start - nothing just say hello 204 | + menu - main entry point 205 | quick - quick mode 206 | copy - full mode 207 | set - customize settings 208 | task - task query 209 | reset - restore task 210 | size - just size task 211 | cancel - cancel TG conversation 212 | kill - kill task which is in processing 213 | ver - check iCopy version 214 | restart - restart iCopy 215 | 216 | + Child Command: 217 | 218 | + set - customize settings 219 | ┖ set - batch way 220 | ┖ set rule - rules 221 | ┖ fav|quick +/- id - single way 222 | ┖ set purge - purge favorites 223 | + task - task query 224 | ┖ task - task in processing 225 | ┖ task list - future 10 tasks 226 | + reset - restore task 227 | ┖ reset - restore current task 228 | ┖ reset id - restore the specified task 229 | 230 | ## version 0.2.0-beta.5 231 | 232 | + Update : 233 | + ADD : directly input sharelink then choose mode. 234 | 235 | + Fixbugs : 236 | + FIX : get shared drive name failed when the shared drive is temporarily granted permission for an outside party. 237 | + FIX : Fix the error of repeated tasks when entering multiple tasks at the same time. 238 | + FIX : "reset" notice msg error 239 | 240 | ## version 0.2.0-beta.4.1 241 | 242 | + Fixbugs : 243 | + FIX : "/reset task_id" Database operation error 244 | 245 | ## version 0.2.0-beta.4 246 | 247 | + Update : 248 | + ADD "/size" a function to get simple size info 249 | 250 | + Fixbugs : 251 | + FIX : "/reset" send notice msg error 252 | + FIX : get shared drive name failed when the shared drive is temporarily granted permission for an outside party. 253 | 254 | *** 255 | 256 | ## version 0.2.0-beta.3 257 | 258 | Notice : The new "conf.toml" should be replaced or you could modify the "conf.toml" by referring to the "example" one. 259 | 260 | + Update : 261 | + ADD "/set purge" 262 | Allow to Purge Favorties Setting Now. 263 | this will not delete quick mode setting. 264 | + Now '--drive-server-side-across-configs' is Built in the iCopy. Remove from conf.toml 265 | + '--ignore-checksum' is write in conf.toml default 266 | + ADD "/reset" and "/reset id" command. 267 | You could restore task with the command 268 | 269 | *** 270 | 271 | ## version 0.2.0-beta.2 272 | 273 | Update : send confirm msg after task added 274 | Update : '/start' is not in Conversation Handle any more 275 | Update : Use '/menu' to select run mode instead of '/start' 276 | 277 | *** 278 | 279 | ## version 0.2.0-beta.1 280 | 281 | The first beta version of v0.2 282 | β1 is a relatively stable without bugs version 283 | The following Command is Supported 284 | 285 | + Root Command: 286 | 287 | + start - main entry point 288 | quick - quick mode 289 | copy - full mode 290 | set - customize settings 291 | task - task query 292 | cancel - cancel TG conversation 293 | kill - kill task which is in processing 294 | ver - check iCopy version 295 | restart - restart iCopy 296 | 297 | + Child Command: 298 | 299 | + set - ustomize settings 300 | ┖ set - batch way 301 | ┖ set rule - rules 302 | ┖ set fav|quick +/- id - single way 303 | task - task query 304 | ┖ task - task in processing 305 | ┖ task list - future 10 tasks 306 | 307 | *** 308 | 309 | ## version 0.2.0-alpha.1 ~ alpha.15 310 | 311 | iCopy rebuild basework finished 312 | 313 | ## version 0.1.7-beta.3 314 | 315 | Archived version 316 | ... 317 | -------------------------------------------------------------------------------- /iCopy/docs/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | + before beta6 4 | null & fixbugs 5 | 6 | + before beta7 7 | del fav keyboard 8 | choose fav keyboard 9 | del Task info in MongoDB 10 | 11 | ------- 12 | 13 | + future 14 | modify task info 15 | Support vaild folder len[28,33,72] 16 | Web DASHBOARD 17 | -------------------------------------------------------------------------------- /iCopy/docs/develop_update.md: -------------------------------------------------------------------------------- 1 | # DEVELOP UPDATE CHANGELOG 2 | 3 | 4 | -------------------------------------------------------------------------------- /iCopy/drive/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f0rx4/FClone-Bot/f73d648a4a3a5c727bbb79b98af5cf61fb7d2498/iCopy/drive/__init__.py -------------------------------------------------------------------------------- /iCopy/drive/gdrive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os, sys, logging, random 5 | 6 | from glob import glob 7 | from googleapiclient import discovery 8 | from google.oauth2 import service_account 9 | from googleapiclient import errors 10 | from google.auth.transport.requests import Request 11 | 12 | from utils import load 13 | logger = logging.getLogger(__name__) 14 | logging.getLogger('googleapiclient.discovery').setLevel(logging.CRITICAL) 15 | 16 | class GoogleDrive: 17 | def __init__(self): 18 | service_account_file = random.choice(glob(load.cfg['general']['sa_path'] + '/*.json')) 19 | 20 | credentials = None 21 | scopes = ['https://www.googleapis.com/auth/drive'] 22 | 23 | credentials = service_account.Credentials.from_service_account_file( 24 | service_account_file, scopes=scopes) 25 | 26 | self.service = discovery.build('drive', 'v3', credentials=credentials, cache_discovery=False) 27 | 28 | def drive_list(self): 29 | result = [] 30 | raw_drives = {} 31 | page_token = None 32 | 33 | while True: 34 | try: 35 | param = { 36 | 'pageSize': 100, 37 | } 38 | if page_token: 39 | param['pageToken'] = page_token 40 | drives = self.service.drives().list(**param).execute() 41 | 42 | result.extend(drives['drives']) 43 | logger.debug('Received {} drives'.format(len(drives['drives']))) 44 | page_token = drives.get('nextPageToken') 45 | if not page_token: 46 | break 47 | except: 48 | break 49 | 50 | for item in result: 51 | raw_drives[item['id']] = item['name'] 52 | return raw_drives 53 | 54 | def file_get_name(self, file_id): 55 | param = { 56 | 'fileId': file_id, 57 | 'supportsAllDrives': True, 58 | 'fields': 'name, driveId', 59 | } 60 | raw_file_info = self.service.files().get(**param).execute() 61 | file_name = raw_file_info['name'] 62 | 63 | return file_name 64 | 65 | def drive_get(self, drive_id): 66 | param = { 67 | 'driveId': drive_id, 68 | } 69 | drive_info = self.service.drives().get(**param).execute() 70 | 71 | return drive_info 72 | 73 | def get_dst_endpoint_id(self, dst_id, src_name): 74 | page_token = None 75 | result = [] 76 | while True: 77 | try: 78 | param = { 79 | 'q': r"name = '{}' and " 80 | r"mimeType = 'application/vnd.google-apps.folder' and " 81 | r"'{}' in parents and trashed = false".format(src_name, dst_id), 82 | 'includeItemsFromAllDrives': True, 83 | 'supportsAllDrives': True, 84 | 'fields': 'nextPageToken, files(id, name)', 85 | 'pageSize': 1000, 86 | } 87 | if page_token: 88 | param['pageToken'] = page_token 89 | 90 | all_files = self.service.files().list(**param).execute() 91 | result = all_files['files'][0] 92 | page_token = all_files.get('nextPageToken') 93 | 94 | if not page_token: 95 | break 96 | except: 97 | break 98 | return result -------------------------------------------------------------------------------- /iCopy/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f0rx4/FClone-Bot/f73d648a4a3a5c727bbb79b98af5cf61fb7d2498/iCopy/utils/__init__.py -------------------------------------------------------------------------------- /iCopy/utils/__version__.py: -------------------------------------------------------------------------------- 1 | ### local version 2 | __version__ = "v0.2.0-beta.6.7" -------------------------------------------------------------------------------- /iCopy/utils/callback_stage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from telegram.ext import ConversationHandler 5 | 6 | ( 7 | SET_FAV_MULTI, 8 | CHOOSE_MODE, 9 | GET_LINK, 10 | IS_COVER_QUICK, 11 | GET_DST, 12 | COOK_ID, 13 | REGEX_IN, 14 | REGEX_GET_DST, 15 | COOK_FAV_TO_SIZE, 16 | COOK_FAV_PURGE, 17 | COOK_ID_DEDU, 18 | COOK_FAV_DEDU, 19 | FAV_PRE_DEDU_INFO, 20 | 21 | ) = range(13) -------------------------------------------------------------------------------- /iCopy/utils/dedupe_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re, time, pymongo 5 | from utils import load 6 | from utils.load import _lang, _text 7 | import subprocess 8 | from telegram import ParseMode 9 | from telegram.utils.request import Request as TGRequest 10 | from telegram import Bot 11 | from multiprocessing import Manager 12 | 13 | myclient = pymongo.MongoClient( 14 | f"{load.cfg['database']['db_connect_method']}://{load.user}:{load.passwd}@{load.cfg['database']['db_addr']}", 15 | port=load.cfg["database"]["db_port"], 16 | connect=False, 17 | ) 18 | mydb = myclient[load.cfg["database"]["db_name"]] 19 | task_list = mydb["task_list"] 20 | fav_col = mydb["fav_col"] 21 | 22 | _cfg = load.cfg 23 | deduping_process = subprocess.Popen 24 | 25 | request = TGRequest(con_pool_size=8) 26 | bot = Bot(token=f"{_cfg['tg']['token']}", request=request) 27 | 28 | def dedupe_run(command): 29 | global deduping_process 30 | deduping_process = subprocess.Popen( 31 | command, 32 | stdout=subprocess.PIPE, 33 | stderr=subprocess.STDOUT, 34 | shell=False, 35 | encoding="utf-8", 36 | errors="ignore", 37 | universal_newlines=True, 38 | ) 39 | while True: 40 | line = deduping_process.stdout.readline().rstrip() 41 | if not line: 42 | break 43 | yield line 44 | deduping_process.communicate() 45 | 46 | def dedupe_task( 47 | ns, 48 | dedu_mode, 49 | dedu_chat_id, 50 | dedu_message_id, 51 | dedu_task_id, 52 | dedu_link, 53 | dedu_id, 54 | dedu_name, 55 | ): 56 | cloner = _cfg["general"]["cloner"] 57 | option = "dedupe" 58 | mode_suffix = "--dedupe-mode" 59 | mode = dedu_mode 60 | remote = _cfg["general"]["remote"] 61 | src_id = dedu_id 62 | src_block = remote + ":" + "{" + src_id + "}" 63 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}" 64 | transfers = "--transfers=" + f"{_cfg['general']['parallel_t']}" 65 | flags = ["-P"] 66 | sa_sleep_suffix = "--drive-pacer-min-sleep" 67 | sa_sleep = _cfg["general"]["min_sleep"] 68 | 69 | command = [ 70 | cloner, 71 | option, 72 | mode_suffix, 73 | mode, 74 | src_block, 75 | checkers, 76 | transfers, 77 | sa_sleep_suffix, 78 | sa_sleep, 79 | ] 80 | command += flags 81 | 82 | dedupe_process(ns, command, dedu_mode, dedu_chat_id, dedu_message_id, dedu_task_id, dedu_link, dedu_id, dedu_name) 83 | 84 | ns.dedupe = 0 85 | 86 | def dedupe_process(ns, command, dedu_mode, dedu_chat_id, dedu_message_id, dedu_task_id, dedu_link, dedu_id, dedu_name): 87 | for output in dedupe_run(command): 88 | if ns.dedupe == 1: 89 | deduping_process.kill() 90 | 91 | if ns.dedupe == 0: 92 | last_dedupe_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 93 | 94 | if dedu_task_id == 0: 95 | fav_col.update_one( 96 | {"G_id": dedu_id}, {"$set": {"last_dedupe_time": last_dedupe_time,}}, 97 | ) 98 | 99 | deduped_msg = ( 100 | " ༺ ✪iCopy✪ ༻ | " 101 | + "🏳️" 102 | + " FAVORITES" 103 | + "\n\n" 104 | + '{}'.format(dedu_link, dedu_name) 105 | + "\n[" 106 | + dedu_id 107 | + "]\n\n" 108 | + _text[_lang]["deduping_done"] 109 | ) 110 | 111 | else: 112 | task_list.update_one( 113 | {"_id": int(dedu_task_id)}, {"$set": {"last_dedupe_time": last_dedupe_time,}}, 114 | ) 115 | 116 | deduped_msg = ( 117 | " ༺ ✪iCopy✪ ༻ | " 118 | + "🏳️" + _text[_lang]["current_task_id"] 119 | + str(dedu_task_id) 120 | + "\n\n" 121 | + '{}'.format(dedu_link, dedu_name) 122 | + "\n[" 123 | + dedu_id 124 | + "]\n\n" 125 | + _text[_lang]["deduping_done"] 126 | ) 127 | 128 | bot.edit_message_text( 129 | chat_id=dedu_chat_id, 130 | message_id=dedu_message_id, 131 | text=deduped_msg, 132 | parse_mode=ParseMode.HTML, 133 | disable_web_page_preview=True, 134 | ) 135 | elif ns.dedupe == 1: 136 | bot.edit_message_text( 137 | chat_id=dedu_chat_id, 138 | message_id=dedu_message_id, 139 | text=_text[_lang]["is_killed_by_user"], 140 | ) 141 | 142 | -------------------------------------------------------------------------------- /iCopy/utils/get_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import logging, re, json, requests 5 | from utils import ( 6 | load, 7 | messages as _msg, 8 | restricted as _r, 9 | get_set as _set, 10 | task_box as _box, 11 | task_payload as _payload, 12 | callback_stage as _stage, 13 | __version__, 14 | ) 15 | from workflow import copy_workflow as _copy 16 | from utils.load import _lang, _text 17 | from telegram.ext import ConversationHandler 18 | from drive.gdrive import GoogleDrive as _gd 19 | from telegram import ParseMode 20 | from threading import Thread 21 | from utils.load import ns 22 | 23 | 24 | logging.basicConfig( 25 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO 26 | ) 27 | logger = logging.getLogger(__name__) 28 | 29 | regex1 = r"[-\w]{11,}" 30 | regex2 = r"[-\w]" 31 | judge_folder_len = [28, 33] 32 | pick_quick = [] 33 | mode = "" 34 | 35 | def cook_to_id(get_share_link): 36 | share_id_list = [] 37 | unsupported_type = [] 38 | share_id = "" 39 | 40 | share_link = get_share_link.strip().replace(" ", "").splitlines() 41 | for item in share_link: 42 | if "drive.google.com" in item: 43 | share_id = re.findall(regex1, item) 44 | if len(share_id) <= 33: 45 | share_id = "".join(share_id) 46 | 47 | share_id_list.append(share_id) 48 | else: 49 | unsupported_type.append({"type": "link", "value": item}) 50 | 51 | else: 52 | if len(item) >= 11 and len(item) <= 33 and re.match(regex2, item): 53 | share_id_list.append(item) 54 | else: 55 | unsupported_type.append({"type": "id", "value": item}) 56 | 57 | return share_id_list 58 | 59 | 60 | def get_name_from_id(update, taget_id, list_name): 61 | cook_list = list(list_name) 62 | if len(taget_id) >= 11 and len(taget_id) < 28: 63 | cook_list.append( 64 | {"G_type": "G_drive", "G_id": taget_id, "G_name": load.all_drive[taget_id],} 65 | ) 66 | elif len(taget_id) in judge_folder_len: 67 | cook_list.append( 68 | { 69 | "G_type": "G_Folder", 70 | "G_id": taget_id, 71 | "G_name": _gd().file_get_name(file_id=taget_id), 72 | } 73 | ) 74 | else: 75 | update.effective_message.reply_text(_msg.get_fav_len_invaild(_lang, taget_id)) 76 | 77 | return ConversationHandler.END 78 | 79 | return cook_list 80 | 81 | def get_src_name_from_id(update, taget_id, list_name): 82 | cook_list = [] 83 | cook_list = list(list_name) 84 | if len(taget_id) >= 11 and len(taget_id) < 28: 85 | target_info = _gd.drive_get(_gd(),drive_id=taget_id) 86 | cook_list.append( 87 | {"G_type": "G_drive", "G_id": taget_id, "G_name": target_info['name'],} 88 | ) 89 | elif len(taget_id) in judge_folder_len: 90 | cook_list.append( 91 | { 92 | "G_type": "G_Folder", 93 | "G_id": taget_id, 94 | "G_name": _gd().file_get_name(file_id=taget_id), 95 | } 96 | ) 97 | else: 98 | update.effective_message.reply_text(_msg.get_fav_len_invaild(_lang, taget_id)) 99 | 100 | return ConversationHandler.END 101 | 102 | return cook_list 103 | 104 | 105 | def insert_to_db_quick(pick_quick, update): 106 | is_quick = {"_id": "fav_quick"} 107 | is_quick_cur = load.fav_col.find(is_quick) 108 | if list(is_quick_cur) == []: 109 | for item in pick_quick: 110 | item["_id"] = "fav_quick" 111 | load.fav_col.insert_one(item) 112 | 113 | update.effective_message.reply_text( 114 | _text[_lang]["insert_quick_success"], parse_mode=ParseMode.MARKDOWN_V2 115 | ) 116 | 117 | return ConversationHandler.END 118 | 119 | else: 120 | status = "is_cover" 121 | 122 | return status 123 | 124 | 125 | def modify_quick_in_db(update, context): 126 | pick_quick = _set.pick_quick 127 | for item in pick_quick: 128 | load.fav_col.update({"_id": "fav_quick"}, item, upsert=True) 129 | 130 | update.effective_message.reply_text( 131 | _text[_lang]["modify_quick_success"], parse_mode=ParseMode.MARKDOWN_V2 132 | ) 133 | 134 | return ConversationHandler.END 135 | 136 | 137 | def delete_in_db_quick(): 138 | load.fav_col.delete_one({"_id": "fav_quick"}) 139 | 140 | return 141 | 142 | 143 | def delete_in_db(delete_request): 144 | load.fav_col.delete_one(delete_request) 145 | 146 | return 147 | 148 | 149 | def get_share_link(update, context): 150 | get_share_link = update.effective_message.text 151 | tmp_src_name_list = "" 152 | tmp_task_list = [] 153 | src_name_list = [] 154 | src_id_list = cook_to_id(get_share_link) 155 | is_quick = {"_id": "fav_quick"} 156 | is_quick_cur = load.fav_col.find(is_quick) 157 | is_dstinfo = _copy.current_dst_info 158 | 159 | if is_dstinfo != "": 160 | dstinfo = is_dstinfo.split("id+name") 161 | dst_id = dstinfo[0] 162 | dst_name = dstinfo[1] 163 | else: 164 | for doc in is_quick_cur: 165 | dst_id = doc["G_id"] 166 | dst_name = doc["G_name"] 167 | 168 | for item in src_id_list: 169 | src_name_list += get_src_name_from_id(update, item, list_name=tmp_src_name_list) 170 | tmp_src_name_list = "" 171 | 172 | for item in src_name_list: 173 | src_id = item["G_id"] 174 | src_name = item["G_name"] 175 | 176 | tmp_task_list.append( 177 | { 178 | "mode_type": mode, 179 | "src_id": src_id, 180 | "src_name": src_name, 181 | "dst_id": dst_id, 182 | "dst_name": dst_name, 183 | "chat_id": update.message.chat_id, 184 | "raw_message_id": update.message.message_id, 185 | } 186 | ) 187 | 188 | Thread(target=_box.cook_task_to_db, args=(update, context, tmp_task_list)).start() 189 | _copy.current_dst_info = "" 190 | return ConversationHandler.END 191 | 192 | def taskill(update, context): 193 | entry_cmd = update.effective_message.text 194 | if "/kill" == entry_cmd : 195 | ns.x = 1 196 | 197 | elif context.args[0] == "task": 198 | ns.x = 1 199 | 200 | elif context.args[0] == "size": 201 | ns.size = 1 202 | 203 | elif context.args[0] == "purge": 204 | ns.purge = 1 205 | 206 | elif context.arg[0] == "dedupe": 207 | ns.dedupe = 1 208 | 209 | else: 210 | update.effective_message.reply_text(_text[_lang]["global_command_error"]) 211 | 212 | def check_restart(bot): 213 | check_restart = load.db_counters.find_one({"_id": "is_restart"}) 214 | chat_id = check_restart["chat_id"] 215 | message_id = check_restart["message_id"] 216 | load.db_counters.update_one({"_id": "is_restart"}, {"$set": {"status": 0,}}, True) 217 | bot.edit_message_text( 218 | chat_id=chat_id, message_id=message_id, text=_text[_lang]["restart_success"] 219 | ) 220 | 221 | def _version(update, context): 222 | update.message.reply_text( 223 | "Welcome to use iCopy Telegram BOT\n\n" 224 | "Current Version : " + __version__.__version__ + "\n\n" 225 | f"Latest Version : {_get_ver()}" 226 | ) 227 | 228 | def _get_ver(): 229 | _url = "https://api.github.com/repos/fxxkrlab/iCopy/releases" 230 | _r_ver = requests.get(_url).json() 231 | _latest_ver = _r_ver[0]["tag_name"] 232 | return _latest_ver 233 | 234 | @_r.restricted 235 | def cancel(update, context): 236 | user = update.effective_user.first_name 237 | logger.info("User %s canceled the conversation.", user) 238 | update.effective_message.reply_text( 239 | f"Bye! {update.effective_user.first_name} ," + _text[_lang]["cancel_msg"] 240 | ) 241 | return ConversationHandler.END 242 | 243 | def error(update, context): 244 | """Log Errors caused by Updates.""" 245 | logger.warning('Update "%s" caused error "%s"', update, context.error) 246 | -------------------------------------------------------------------------------- /iCopy/utils/get_set.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils import ( 5 | load, 6 | get_functions as _func, 7 | messages as _msg, 8 | restricted as _r, 9 | keyboard as _KB, 10 | callback_stage as _stage, 11 | ) 12 | from telegram.ext import ConversationHandler 13 | from telegram import ParseMode 14 | from utils.load import _lang, _text 15 | from drive.gdrive import GoogleDrive as _gd 16 | ''' 17 | ( 18 | SET_FAV_MULTI, 19 | CHOOSE_MODE, 20 | GET_LINK, 21 | IS_COVER_QUICK, 22 | GET_DST, 23 | COOK_ID, 24 | REGEX_IN, 25 | REGEX_GET_DST, 26 | COOK_FAV_TO_SIZE, 27 | COOK_FAV_PURGE, 28 | COOK_ID_DEDU, 29 | COOK_FAV_DEDU, 30 | ) = range(12) 31 | ''' 32 | pick_quick = [] 33 | pick_fav = [] 34 | unpick_fav = [] 35 | judge_folder_len = [28, 33] 36 | showlist = [] 37 | showitem = "" 38 | 39 | 40 | @_r.restricted 41 | def _setting(update, context): 42 | entry_cmd = update.effective_message.text 43 | if " " in entry_cmd: 44 | entry_cmd = entry_cmd.replace(" ", "") 45 | 46 | if "/set" == entry_cmd.strip(): 47 | update.effective_message.reply_text( 48 | _msg.set_multi_fav_guide(_lang), parse_mode=ParseMode.MARKDOWN_V2 49 | ) 50 | 51 | return _stage.SET_FAV_MULTI 52 | 53 | if "purge" == entry_cmd[4:]: 54 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"}) 55 | if fav_count is not None and fav_count['fav_sum'] != 0: 56 | fav_sum = fav_count['fav_sum'] 57 | query = { "fav_type": {"$regex": "^fav"} } 58 | del_query = load.fav_col.delete_many(query) 59 | fav_sum -= int(del_query.deleted_count) 60 | load.db_counters.update( 61 | {"_id": "fav_count_list"}, 62 | {"fav_sum": fav_sum}, 63 | upsert=True, 64 | ) 65 | 66 | update.effective_message.reply_text( 67 | _text[_lang]["purge_fav"] 68 | ) 69 | 70 | return ConversationHandler.END 71 | 72 | else: 73 | update.effective_message.reply_text( 74 | _text[_lang]["show_fav_list_null"] 75 | ) 76 | 77 | return ConversationHandler.END 78 | 79 | 80 | if "/setlist" == entry_cmd: 81 | global showitem 82 | global showlist 83 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"}) 84 | fav_list = load.fav_col.find({"fav_type": "fav"}) 85 | if fav_count is not None: 86 | if fav_count["fav_sum"] == 0: 87 | update.effective_message.reply_text(_text[_lang]["show_fav_list_null"]) 88 | 89 | return ConversationHandler.END 90 | 91 | elif fav_count["fav_sum"] != 0: 92 | for item in fav_list: 93 | showitem = ( 94 | "type : " 95 | + item["G_type"] 96 | + " | name : " 97 | + item["G_name"] 98 | + "\nid : " 99 | + item["G_id"] 100 | + "\n" 101 | + "--------------------\n" 102 | ) 103 | showlist.append(showitem) 104 | 105 | showlist = "".join(showlist) 106 | 107 | update.effective_message.reply_text( 108 | _text[_lang]["show_fav_list"] + "\n\n" + showlist 109 | ) 110 | 111 | showlist = [] 112 | 113 | return ConversationHandler.END 114 | 115 | else: 116 | update.effective_message.reply_text(_text[_lang]["show_fav_list_null"]) 117 | 118 | return ConversationHandler.END 119 | 120 | ### set single DST ID ### 121 | elif "quick" or "fav" in entry_cmd: 122 | ### single quick (drive or folder) 123 | if len(entry_cmd.splitlines()) == 1: 124 | each = entry_cmd[4:] 125 | if "quick" == each[:5]: 126 | if "quick+" == each[:6]: 127 | global pick_quick 128 | pick_quick = _func.get_name_from_id( 129 | update, each[6:], list_name=pick_quick 130 | ) 131 | insert_fav_quick = _func.insert_to_db_quick(pick_quick, update) 132 | if insert_fav_quick == "is_cover": 133 | update.effective_message.reply_text( 134 | _text[_lang]["is_cover_quick_msg"], 135 | parse_mode=ParseMode.MARKDOWN_V2, 136 | reply_markup=_KB.is_cover_keyboard(), 137 | ) 138 | 139 | return _stage.IS_COVER_QUICK 140 | 141 | elif "quick-" == each[:6]: 142 | _func.delete_in_db_quick 143 | update.effective_message.reply_text( 144 | _text[_lang]["delete_quick_success"] 145 | ) 146 | 147 | ### set fav folder(fav folder could be a drive or folder of GDrive) 148 | elif "fav" == each[:3]: 149 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"}) 150 | fav_sum = 0 151 | 152 | if fav_count != None: 153 | fav_sum = fav_count["fav_sum"] 154 | 155 | if "+" == each[3]: 156 | global pick_fav 157 | pick_fav = _func.get_name_from_id( 158 | update, each[4:], list_name=pick_fav 159 | ) 160 | for item in pick_fav: 161 | item["fav_type"] = "fav" 162 | try: 163 | load.fav_col.insert_one(item) 164 | except: 165 | update.effective_message.reply_text( 166 | _text[_lang]["is_set_err"], 167 | ) 168 | else: 169 | fav_sum += 1 170 | load.db_counters.update( 171 | {"_id": "fav_count_list"}, 172 | {"fav_sum": fav_sum}, 173 | upsert=True, 174 | ) 175 | 176 | update.effective_message.reply_text(_text[_lang]["set_fav_success"]) 177 | 178 | pick_fav = [] 179 | 180 | if "-" == each[3]: 181 | global unpick_fav 182 | unpick_fav.append(each[4:]) 183 | for item in unpick_fav: 184 | delete_request = {"G_id": item} 185 | _func.delete_in_db(delete_request) 186 | fav_count = load.fav_col.find({"fav_type": "fav"}) 187 | fav_sum = len(list(fav_count)) 188 | load.db_counters.update( 189 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True 190 | ) 191 | 192 | update.effective_message.reply_text( 193 | _text[_lang]["delete_fav_success"] 194 | ) 195 | 196 | unpick_fav = [] 197 | 198 | ### single rule 199 | elif "rule" == entry_cmd[4:8]: 200 | update.effective_message.reply_text( 201 | _msg.set_single_fav_guide(_lang), parse_mode=ParseMode.MARKDOWN_V2 202 | ) 203 | 204 | return ConversationHandler.END 205 | 206 | else: 207 | update.effective_message.reply_text( 208 | _text[_lang]["get_single_fav_error"], 209 | parse_mode=ParseMode.MARKDOWN_V2, 210 | ) 211 | 212 | return ConversationHandler.END 213 | 214 | return ConversationHandler.END 215 | 216 | else: 217 | update.effective_message.reply_text( 218 | _text[_lang]["get_multi_in_single"], parse_mode=ParseMode.MARKDOWN_V2 219 | ) 220 | 221 | return ConversationHandler.END 222 | 223 | else: 224 | update.effective_message.reply_text( 225 | _msg.set_help(_lang), parse_mode=ParseMode.MARKDOWN_V2 226 | ) 227 | return ConversationHandler.END 228 | 229 | 230 | ### set multi DST ID ### 231 | def _multi_settings_recieved(update, context): 232 | _tmp_quick_counter = 0 233 | fav_msg = update.effective_message.text 234 | fav_msg = fav_msg.replace(" ", "").splitlines() 235 | global pick_quick 236 | for each in fav_msg: 237 | print(each) 238 | ### modify quick DST 239 | if "quick+" == each[:6]: 240 | _tmp_quick_counter += 1 241 | if _tmp_quick_counter == 1: 242 | global pick_quick 243 | pick_quick += _func.get_name_from_id( 244 | update, each[6:], list_name=pick_quick 245 | ) 246 | insert_fav_quick = _func.insert_to_db_quick(pick_quick, update) 247 | if insert_fav_quick == "error": 248 | update.effective_message.reply_text( 249 | _text[_lang]["is_cover_quick_msg"], 250 | parse_mode=ParseMode.MARKDOWN_V2, 251 | reply_markup=_KB.is_cover_keyboard(), 252 | ) 253 | 254 | return _stage.IS_COVER_QUICK 255 | 256 | elif _tmp_quick_counter < 1: 257 | pass 258 | elif _tmp_quick_counter > 1: 259 | print("error!") 260 | update.effective_message.reply_text( 261 | _text[_lang]["get_quick_count_invaild"] 262 | ) 263 | elif "quick-" == each[:6]: 264 | _func.delete_in_db_quick 265 | update.effective_message.reply_text(_text[_lang]["delete_quick_success"]) 266 | 267 | ### set fav folder(fav folder could be a drive or folder of GDrive) 268 | 269 | elif "fav" == each[:3]: 270 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"}) 271 | fav_sum = 0 272 | 273 | if fav_count != None: 274 | fav_sum = fav_count["fav_sum"] 275 | 276 | if "+" == each[3]: 277 | global pick_fav 278 | pick_fav += _func.get_name_from_id(update, each[4:], list_name=pick_fav) 279 | for item in pick_fav: 280 | item["fav_type"] = "fav" 281 | try: 282 | load.fav_col.insert_one(item) 283 | except: 284 | update.effective_message.reply_text(_text[_lang]["is_set_err"],) 285 | else: 286 | fav_sum += 1 287 | load.db_counters.update( 288 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True 289 | ) 290 | 291 | update.effective_message.reply_text(_text[_lang]["set_fav_success"]) 292 | pick_fav = [] 293 | 294 | if "-" == each[3]: 295 | global unpick_fav 296 | unpick_fav.append(each[4:]) 297 | for item in unpick_fav: 298 | delete_request = {"G_id": item} 299 | _func.delete_in_db(delete_request) 300 | fav_count = load.fav_col.find({"fav_type": "fav"}) 301 | fav_sum = len(list(fav_count)) 302 | load.db_counters.update( 303 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True 304 | ) 305 | 306 | update.effective_message.reply_text(_text[_lang]["delete_fav_success"]) 307 | 308 | unpick_fav = [] 309 | 310 | else: 311 | if "/cancel" == update.effective_message.text: 312 | 313 | return _func.cancel(update, context) 314 | else: 315 | update.effective_message.reply_text(_text[_lang]["get_multi_fav_error"]) 316 | 317 | return ConversationHandler.END 318 | -------------------------------------------------------------------------------- /iCopy/utils/keyboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils import load 5 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 6 | 7 | _langtext = load._text[load._lang] 8 | 9 | # start InlineKeyBoard 10 | def start_keyboard(): 11 | keyboard = [ 12 | [ 13 | InlineKeyboardButton(_langtext['quick_mode'], callback_data="quick"), 14 | InlineKeyboardButton(_langtext['copy_mode'], callback_data="copy"), 15 | ], 16 | ] 17 | 18 | return InlineKeyboardMarkup(keyboard) 19 | 20 | def regex_in_keyboard(): 21 | keyboard = [ 22 | [ 23 | InlineKeyboardButton(_langtext['quick_mode'], callback_data="quick"), 24 | InlineKeyboardButton(_langtext['copy_mode'], callback_data="copy"), 25 | InlineKeyboardButton(_langtext['size_mode'], callback_data="size"), 26 | ], 27 | ] 28 | 29 | return InlineKeyboardMarkup(keyboard) 30 | 31 | def is_cover_keyboard(): 32 | keyboard = [ 33 | [ 34 | InlineKeyboardButton(_langtext['is_cover'], callback_data="cover_quick"), 35 | InlineKeyboardButton(_langtext['not_cover'], callback_data="not_cover_quick"), 36 | ], 37 | ] 38 | 39 | return InlineKeyboardMarkup(keyboard) 40 | 41 | def dedupe_mode_keyboard(): 42 | keyboard = [ 43 | [ 44 | InlineKeyboardButton("first", callback_data="first"), 45 | ], 46 | [ 47 | InlineKeyboardButton("newest", callback_data="newest"), 48 | InlineKeyboardButton("oldest", callback_data="oldest"), 49 | ], 50 | [ 51 | InlineKeyboardButton("largest", callback_data="largest"), 52 | InlineKeyboardButton("smallest", callback_data="smallest"), 53 | ], 54 | ] 55 | 56 | return InlineKeyboardMarkup(keyboard) 57 | 58 | def dst_keyboard(update, context): 59 | favs = load.fav_col.find({"fav_type":"fav"}) 60 | button_list = [] 61 | 62 | for item in favs: 63 | button_list.append(InlineKeyboardButton(item['G_name'], callback_data=item['G_id']+"id+name"+item['G_name'])) 64 | return InlineKeyboardMarkup(build_dst_keyboard(button_list,n_cols=2)) 65 | 66 | def build_dst_keyboard(buttons,n_cols,header_buttons=None,footer_buttons=None): 67 | menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)] 68 | if header_buttons: 69 | menu.insert(0, header_buttons) 70 | if footer_buttons: 71 | menu.append(footer_buttons) 72 | return menu -------------------------------------------------------------------------------- /iCopy/utils/load.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os, sys, toml 5 | import pymongo 6 | from urllib import parse 7 | from drive import gdrive 8 | from multiprocessing import Manager 9 | from telegram.utils.request import Request as TGRequest 10 | from telegram import Bot 11 | 12 | 13 | _cfgFile_RAW = os.path.abspath(os.path.join("config", "conf.toml")) 14 | cfg = toml.load(_cfgFile_RAW) 15 | _textFile_RAW = os.path.abspath(os.path.join("config", "text.toml")) 16 | _text = toml.load(_textFile_RAW) 17 | 18 | 19 | ### load language selector 20 | _lang = cfg["general"]["language"] 21 | 22 | ### ENABLED_USERS 23 | ENABLED_USERS = os.environ.get("ENABLED_USERS", f"{cfg['tg']['usr_id']}") 24 | 25 | ### Mongodb 26 | user = parse.quote_plus(f"{cfg['database']['db_user']}") 27 | passwd = parse.quote_plus(f"{cfg['database']['db_passwd']}") 28 | myclient = pymongo.MongoClient( 29 | f"{cfg['database']['db_connect_method']}://{user}:{passwd}@{cfg['database']['db_addr']}", 30 | port=cfg["database"]["db_port"], 31 | connect=False, 32 | ) 33 | mydb = myclient[cfg["database"]["db_name"]] 34 | 35 | # main_col = mydb['main_col'] 36 | fav_col = mydb["fav_col"] 37 | task_list = mydb["task_list"] 38 | db_counters = mydb["counters"] 39 | 40 | ### drive().list 41 | all_drive = gdrive.GoogleDrive().drive_list() 42 | 43 | ### ns 44 | manager = Manager() 45 | ns = manager.Namespace() 46 | 47 | ### Restore Unexpected Interrupted Task status 2 --> 0 48 | task_list.update_one( 49 | {"status": 2}, {"$set": {"status": 0,}}, 50 | ) 51 | 52 | ### regex entry pattern 53 | regex_entry_pattern = r"https://drive\.google\.com/(?:drive/(?:u/[\d]+/)?(?:mobile/)?folders/([\w.\-_]+)(?:\?[\=\w]+)?|folderview\?id=([\w.\-_]+)(?:\&[=\w]+)?|open\?id=([\w.\-_]+)(?:\&[=\w]+)?|(?:a/[\w.\-_]+/)?file/d/([\w.\-_]+)|(?:a/[\w.\-_]+/)?uc\?id\=([\w.\-_]+)&?)" 54 | 55 | ### define bot 56 | request = TGRequest(con_pool_size=8) 57 | bot = Bot(token=f"{cfg['tg']['token']}", request=request) -------------------------------------------------------------------------------- /iCopy/utils/messages.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | 5 | def restricted_msg(_lang,_first_name,_user_id): 6 | if "cn" == _lang: 7 | return(f"HI ! {_first_name} 您好\n" 8 | f"您的用户ID:{_user_id} 未经授权\n" 9 | "请用正确的方式添加") 10 | if "eng" == _lang: 11 | return(f"HI ! {_first_name}\n" 12 | f"Your user ID:{_user_id} is not allowed\n" 13 | "Pls set it in the correct way") 14 | if "jp" == _lang: 15 | return(f"HI ! {_first_name} こんにちは" 16 | f"ユーザーID:{_user_id}は許可されていない" 17 | "正しい方法で追加してください") 18 | 19 | # ##### /set Messages ##### 20 | 21 | def set_help(_lang): 22 | if "cn" == _lang: 23 | return("命令必须符合'/set' 或 '/set rule'规则") 24 | if "eng" == _lang: 25 | return("Only rules in '/set' and '/set rule' is vaild") 26 | if "jp" == _lang: 27 | return("'/set'または'/set rule'のルールに準拠する必要がある") 28 | 29 | def set_multi_fav_rule(): 30 | return ("\n " 31 | "`quick +\- folder_ID/drive_ID` \n " 32 | "`fav +\- folder_ID/drive_ID` \n " 33 | "\n") 34 | 35 | def set_multi_fav_guide(_lang): 36 | 37 | if "cn" == _lang: 38 | return ("*请输入需要修改的目标地址* \n " 39 | "\n" 40 | "例 : 如下 *\+/\-* \n " 41 | "_quick \| fav_ 为对应前缀 \n " 42 | + set_multi_fav_rule() + 43 | "说明:" 44 | "随意组合排序: '*\+*' *_增加_*, '*\-*' *_取消_* \n " 45 | "_quick_ 只可存在_一个_,为快速目录") 46 | if "eng" == _lang: 47 | return ("*pls modify the Dst\\_ID List* \n " 48 | "\n" 49 | "e\.g : *\+/\-* \n " 50 | "_quick \| drive \| folder_ is the prefix \n " 51 | + set_multi_fav_rule() + 52 | "explain:" 53 | "The order does not matter: '*\+*' *_select_*, '*\-*' *_unselect_* \n " 54 | "_Only one quick\\_id can exist for quick\\_mode_") 55 | if "jp" == _lang: 56 | return ("*フォルダーIDやシェアドライバIDを入力してください* \n " 57 | "\n" 58 | "例 : 接頭辞 *\+/\-* ID\n " 59 | "_quick と drive と folder_ は接頭辞です \n " 60 | + set_multi_fav_rule() + 61 | "説明:" 62 | "順番は関係ない: '*\+*' *_增加する_*, '*\-*' *_キャンセル_* \n " 63 | "「クイックモード」は1つしか存在できません") 64 | 65 | def set_single_fav_rule(): 66 | return ("\n " 67 | "`/set quick\|fav \+/\- folder/drive ID` \n " 68 | "\n") 69 | 70 | def set_single_fav_guide(_lang): 71 | 72 | if "cn" == _lang: 73 | return ("*请输入需要修改的目标地址* \n " 74 | "\n" 75 | "例 : 如下 *\+/\-* \n " 76 | + set_single_fav_rule()) 77 | if "eng" == _lang: 78 | return ("*pls modify the Dst\\_ID List* \n " 79 | "\n" 80 | "e\.g : *\+/\-* \n " 81 | + set_single_fav_rule()) 82 | if "jp" == _lang: 83 | return ("*フォルダーIDやシェアドライバIDを入力してください* \n " 84 | "\n" 85 | "例 : 接頭辞 *\+/\-* ID\n " 86 | + set_single_fav_rule()) 87 | 88 | def get_fav_len_invaild(_lang, each): 89 | if "cn" == _lang: 90 | return(f"您提交的 ID:{each[6:]} 不是有效的") 91 | if "eng" == _lang: 92 | return(f"ID:{each[6:]} is not vaild") 93 | if "jp" == _lang: 94 | return(f"入力したID:{each[6:]}は無効です") 95 | -------------------------------------------------------------------------------- /iCopy/utils/process_bar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | 6 | def status(val): 7 | if val == 0: 8 | ss = "│░░░░░░░░░░░░░│" 9 | 10 | if 0 < val <= 48: 11 | if val < 8: 12 | ss = "│█░░░░░░░░░░░░│" 13 | 14 | if 8 <= val <= 16: 15 | ss = "│██░░░░░░░░░░░│" 16 | 17 | if 16 < val <= 24: 18 | ss = "│███░░░░░░░░░░│" 19 | 20 | if 24 < val <= 32: 21 | ss = "│████░░░░░░░░░│" 22 | 23 | if 32 < val <= 40: 24 | ss = "│█████░░░░░░░░│" 25 | 26 | if 40 < val <= 48: 27 | ss = "│██████░░░░░░░│" 28 | 29 | if 48 < val <= 96: 30 | if 48 < val <= 56: 31 | ss = "│███████░░░░░░│" 32 | 33 | if 56 < val <= 64: 34 | ss = "│████████░░░░░│" 35 | 36 | if 64 < val <= 72: 37 | ss = "│█████████░░░░│" 38 | 39 | if 72 < val <= 80: 40 | ss = "│██████████░░░│" 41 | 42 | if 80 < val <= 88: 43 | ss = "│███████████░░│" 44 | 45 | if 88 < val <= 96: 46 | ss = "│████████████░│" 47 | 48 | if 96 < val <= 100: 49 | if 96 < val < 100: 50 | ss = "│█████████████│" 51 | 52 | if val == 100: 53 | ss = "✭│█████████████│✭" 54 | 55 | return ss -------------------------------------------------------------------------------- /iCopy/utils/purge_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re, time, pymongo 5 | from utils import load 6 | from utils.load import _lang, _text 7 | import subprocess 8 | from telegram.utils.request import Request as TGRequest 9 | from telegram import Bot 10 | from threading import Thread 11 | from multiprocessing import Manager 12 | 13 | myclient = pymongo.MongoClient( 14 | f"{load.cfg['database']['db_connect_method']}://{load.user}:{load.passwd}@{load.cfg['database']['db_addr']}", 15 | port=load.cfg["database"]["db_port"], 16 | connect=False, 17 | ) 18 | mydb = myclient[load.cfg["database"]["db_name"]] 19 | fav_col = mydb["fav_col"] 20 | 21 | _cfg = load.cfg 22 | purging_process = subprocess.Popen 23 | 24 | request = TGRequest(con_pool_size=8) 25 | bot = Bot(token=f"{_cfg['tg']['token']}", request=request) 26 | 27 | def purge_run(command): 28 | global purging_process 29 | purging_process = subprocess.Popen( 30 | command, 31 | stdout=subprocess.PIPE, 32 | stderr=subprocess.STDOUT, 33 | shell=False, 34 | encoding="utf-8", 35 | errors="ignore", 36 | universal_newlines=True, 37 | ) 38 | while True: 39 | line = purging_process.stdout.readline().rstrip() 40 | if not line: 41 | break 42 | yield line 43 | purging_process.communicate() 44 | 45 | def purge_fav(ns, purge_chat_id, purge_message_id, fav_id, fav_name): 46 | cloner = _cfg["general"]["cloner"] 47 | option1 = "delete" 48 | option2 = "rmdir" 49 | remote = _cfg["general"]["remote"] 50 | src_id = fav_id 51 | src_block = remote + ":" + "{" + src_id + "}" 52 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}" 53 | transfers = "--transfers=" + f"{_cfg['general']['parallel_t']}" 54 | rmdirs = "--rmdirs" 55 | flags = ["--drive-trashed-only", "--drive-use-trash=false", "-P"] 56 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}" 57 | 58 | command1 = [cloner, option1, src_block, rmdirs, checkers, transfers, sa_sleep] 59 | command1 += flags 60 | 61 | command2 = [cloner, option2, src_block, checkers, transfers, sa_sleep] 62 | command2 += flags 63 | 64 | purge_process(ns, command1, command2, purge_chat_id, purge_message_id, fav_id, fav_name) 65 | 66 | ns.purge = 0 67 | 68 | def purge_process(ns, command1, command2, purge_chat_id, purge_message_id, fav_id, fav_name): 69 | for output in purge_run(command=command1): 70 | if ns.purge == 1: 71 | purging_process.kill() 72 | 73 | for output in purge_run(command=command2): 74 | if ns.purge == 1: 75 | purging_process.kill() 76 | 77 | if ns.purge == 0: 78 | last_purge_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 79 | 80 | fav_col.update_one( 81 | {"G_id": fav_id}, {"$set": {"last_purge_time": last_purge_time,}}, 82 | ) 83 | 84 | purged_msg = ( 85 | "༺ ✪iCopy✪ ༻\n\n" 86 | + fav_name 87 | + "\n[" 88 | + fav_id 89 | + "]\n\n" 90 | + _text[_lang]["purging_done"] 91 | ) 92 | 93 | bot.edit_message_text( 94 | chat_id=purge_chat_id, message_id=purge_message_id, text=purged_msg, 95 | ) 96 | 97 | if ns.purge == 1: 98 | bot.edit_message_text( 99 | chat_id=purge_chat_id, 100 | message_id=purge_message_id, 101 | text=_text[_lang]["is_killed_by_user"], 102 | ) 103 | -------------------------------------------------------------------------------- /iCopy/utils/restricted.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from functools import wraps 5 | from utils.load import _lang, _text 6 | from utils import messages as _msg, load 7 | from telegram import ParseMode 8 | 9 | def restricted(func): 10 | @wraps(func) 11 | def wrapped(update, context, *args, **kwargs): 12 | _user_id = str(update.effective_user.id) 13 | _first_name = update.effective_user.first_name 14 | if _user_id not in load.ENABLED_USERS: 15 | print(f"Unauthorized access denied for {_user_id}.") 16 | update.effective_message.reply_text(_msg.restricted_msg(_lang,_first_name,_user_id)) 17 | return 18 | return func(update, context, *args, **kwargs) 19 | 20 | return wrapped 21 | 22 | def restricted_quick(func): 23 | @wraps(func) 24 | def wrapped(update, context, *args, **kwargs): 25 | is_quick = {"_id": "fav_quick"} 26 | is_quick_cur = load.fav_col.find(is_quick) 27 | if list(is_quick_cur) == []: 28 | print("fav quick directory is not set.") 29 | update.effective_message.reply_text( 30 | _text[_lang]["null_fav_quick"], 31 | ) 32 | return 33 | return func(update, context, *args, **kwargs) 34 | return wrapped 35 | 36 | def restricted_copy(func): 37 | @wraps(func) 38 | def wrapped(update, context, *args, **kwargs): 39 | is_fav = {"fav_type": "fav"} 40 | is_fav_cur = load.fav_col.find(is_fav) 41 | if list(is_fav_cur) == []: 42 | print("fav directory is not set.") 43 | update.effective_message.reply_text( 44 | _text[_lang]["null_fav"], 45 | ) 46 | return 47 | return func(update, context, *args, **kwargs) 48 | return wrapped -------------------------------------------------------------------------------- /iCopy/utils/size_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re, pymongo 5 | from utils import load 6 | from utils.load import _lang, _text 7 | from threading import Thread 8 | import subprocess 9 | from telegram import ParseMode 10 | from telegram.utils.request import Request as TGRequest 11 | from telegram import Bot 12 | from multiprocessing import Manager 13 | 14 | myclient = pymongo.MongoClient( 15 | f"{load.cfg['database']['db_connect_method']}://{load.user}:{load.passwd}@{load.cfg['database']['db_addr']}", 16 | port=load.cfg["database"]["db_port"], 17 | connect=False, 18 | ) 19 | mydb = myclient[load.cfg["database"]["db_name"]] 20 | task_list = mydb["task_list"] 21 | fav_col = mydb["fav_col"] 22 | 23 | _cfg = load.cfg 24 | 25 | request = TGRequest(con_pool_size=8) 26 | bot = Bot(token=f"{_cfg['tg']['token']}", request=request) 27 | 28 | simple_sizing = subprocess.Popen 29 | size_object = "" 30 | size_size = "" 31 | size_info = "" 32 | 33 | def simpe_size_run(command): 34 | global simple_sizing 35 | simple_sizing = subprocess.Popen( 36 | command, 37 | stdout=subprocess.PIPE, 38 | stderr=subprocess.STDOUT, 39 | shell=False, 40 | encoding="utf-8", 41 | errors="ignore", 42 | universal_newlines=True, 43 | ) 44 | while True: 45 | line = simple_sizing.stdout.readline().rstrip() 46 | if not line: 47 | break 48 | yield line 49 | simple_sizing.communicate() 50 | 51 | def simple_size(ns, update, context, item, size_chat_id, size_message_id, share_name_list): 52 | cloner = _cfg["general"]["cloner"] 53 | option = "size" 54 | remote = _cfg["general"]["remote"] 55 | src_id = item 56 | src_block = remote + ":" + "{" + src_id + "}" 57 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}" 58 | flags = ["--size-only"] 59 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}" 60 | 61 | command = [cloner, option, src_block, checkers, sa_sleep] 62 | command += flags 63 | 64 | simple_size_process(ns,command, size_chat_id, size_message_id, share_name_list) 65 | 66 | ns.size = 0 67 | 68 | 69 | def simple_size_process(ns,command, size_chat_id, size_message_id, share_name_list): 70 | for output in simpe_size_run(command): 71 | if ns.size == 1: 72 | simple_sizing.kill() 73 | 74 | regex_total_object = r"^Total objects:" 75 | regex_total_size = r"Total size:" 76 | if output: 77 | size_total_object = re.findall(regex_total_object, output) 78 | size_total_size = re.findall(regex_total_size, output) 79 | 80 | global size_object 81 | global size_size 82 | 83 | if size_total_object: 84 | size_object = output[15:] 85 | if size_total_size: 86 | size_size_all = output[12:] 87 | size_size = size_size_all.split("(") 88 | 89 | if ns.size == 0: 90 | for item in share_name_list: 91 | item_type = item["G_type"] 92 | item_id = item["G_id"] 93 | item_name = item["G_name"] 94 | 95 | global size_info 96 | size_info = ( 97 | " ༺ ✪iCopy✪ ༻ [" 98 | + _text[_lang]["sizing_done"] 99 | + "]\n\n" 100 | + item_type 101 | + " : " 102 | + item_name 103 | + "\n" 104 | + item_id 105 | + "\n" 106 | + "--------------------\n" 107 | + _text[_lang]["total_file_num"] 108 | + str(size_object) 109 | + "\n" 110 | + _text[_lang]["total_file_size"] 111 | + str(size_size[0]) 112 | + "\n(" 113 | + str(size_size[1]) 114 | ) 115 | 116 | bot.edit_message_text( 117 | chat_id=size_chat_id, message_id=size_message_id, text=size_info 118 | ) 119 | 120 | if ns.size == 1: 121 | bot.edit_message_text( 122 | chat_id=size_chat_id, 123 | message_id=size_message_id, 124 | text=_text[_lang]["is_killed_by_user"], 125 | ) 126 | 127 | def owner_size( 128 | ns, size_chat_id, size_message_id, task_id, task_link, endpoint_id, endpoint_name 129 | ): 130 | cloner = _cfg["general"]["cloner"] 131 | option = "size" 132 | remote = _cfg["general"]["remote"] 133 | src_id = endpoint_id 134 | src_block = remote + ":" + "{" + src_id + "}" 135 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}" 136 | flags = ["--size-only"] 137 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}" 138 | 139 | command = [cloner, option, src_block, checkers, sa_sleep] 140 | command += flags 141 | 142 | owner_size_process( 143 | ns, 144 | command, 145 | size_chat_id, 146 | size_message_id, 147 | task_id, 148 | task_link, 149 | endpoint_id, 150 | endpoint_name, 151 | ) 152 | 153 | ns.size = 0 154 | 155 | 156 | def owner_size_process( 157 | ns, 158 | command, 159 | size_chat_id, 160 | size_message_id, 161 | task_id, 162 | task_link, 163 | endpoint_id, 164 | endpoint_name, 165 | ): 166 | for output in simpe_size_run(command): 167 | if ns.size == 1: 168 | simple_sizing.kill() 169 | 170 | regex_total_object = r"^Total objects:" 171 | regex_total_size = r"Total size:" 172 | if output: 173 | size_total_object = re.findall(regex_total_object, output) 174 | size_total_size = re.findall(regex_total_size, output) 175 | 176 | global size_object 177 | global size_size 178 | 179 | if size_total_object: 180 | size_object = output[15:] 181 | if size_total_size: 182 | size_size_all = output[12:] 183 | size_size = size_size_all.split("(") 184 | 185 | if ns.size == 0: 186 | size_size_re = r"([\d.]+[\s]?)([kMGTP]?Bytes)" 187 | get_size_size = re.search(size_size_re, str(size_size[0])) 188 | if get_size_size: 189 | size_size_num = get_size_size.group(1).strip() 190 | size_size_tail = get_size_size.group(2).strip() 191 | 192 | if task_id == 0: 193 | size_info = ( 194 | " ༺ ✪iCopy✪ ༻ | favorites | [" 195 | + _text[_lang]["sizing_done"] 196 | + "]\n\n" 197 | + '{}'.format(task_link, endpoint_name) 198 | + "\n" 199 | + "--------------------\n" 200 | + _text[_lang]["total_file_num"] 201 | + str(size_object) 202 | + "\n" 203 | + _text[_lang]["total_file_size"] 204 | + str(size_size[0]) 205 | + "\n(" 206 | + str(size_size[1]) 207 | ) 208 | 209 | fav_col.update_one( 210 | {"G_id": endpoint_id}, 211 | { 212 | "$set": { 213 | "fav_object": int(size_object), 214 | "fav_size": float(size_size_num), 215 | "fav_size_tail": size_size_tail, 216 | }, 217 | }, 218 | upsert=True, 219 | ) 220 | 221 | else: 222 | size_info = ( 223 | " ༺ ✪iCopy✪ ༻ | " 224 | + "🏳️" 225 | + _text[_lang]["current_task_id"] 226 | + str(task_id) 227 | + " | [" 228 | + _text[_lang]["sizing_done"] 229 | + "]\n\n" 230 | + '{}'.format(task_link, endpoint_name) 231 | + "\n" 232 | + "--------------------\n" 233 | + _text[_lang]["total_file_num"] 234 | + str(size_object) 235 | + "\n" 236 | + _text[_lang]["total_file_size"] 237 | + str(size_size[0]) 238 | + "\n(" 239 | + str(size_size[1]) 240 | ) 241 | 242 | task_list.update_one( 243 | {"_id": int(task_id)}, 244 | { 245 | "$set": { 246 | "task_current_prog_num": int(size_object), 247 | "task_total_prog_num": int(size_object), 248 | "task_current_prog_size": float(size_size_num), 249 | "task_total_prog_size": float(size_size_num), 250 | "task_current_prog_size_tail": size_size_tail[:1], 251 | "task_total_prog_size_tail": size_size_tail, 252 | } 253 | }, 254 | ) 255 | 256 | bot.edit_message_text( 257 | chat_id=size_chat_id, 258 | message_id=size_message_id, 259 | text=size_info, 260 | parse_mode=ParseMode.HTML, 261 | disable_web_page_preview=True, 262 | ) 263 | 264 | elif ns.size == 1: 265 | bot.edit_message_text( 266 | chat_id=size_chat_id, 267 | message_id=size_message_id, 268 | text=_text[_lang]["is_killed_by_user"], 269 | ) 270 | -------------------------------------------------------------------------------- /iCopy/utils/task_box.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import time, pymongo, re 5 | from utils import load,task_payload as _payload 6 | from telegram.ext import ConversationHandler 7 | from utils.load import _lang, _text 8 | from telegram import ParseMode 9 | 10 | future = load.db_counters.find_one({"_id": "task_list_id"}) 11 | future_id = 0 12 | waititem = "" 13 | waitlist = [] 14 | 15 | if future != None: 16 | future_id = future['future_id'] 17 | 18 | def cook_task_to_db(update, context, tmp_task_list): 19 | 20 | for item in tmp_task_list: 21 | global future_id 22 | future_id += 1 23 | item["_id"] = future_id 24 | item["status"] = 0 25 | item["error"] = 0 26 | item["create_time"] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 27 | item["finished_time"] = "" 28 | item["start_time"] = "" 29 | item["task_current_prog_num"] = 0 30 | item["task_total_prog_num"] = 0 31 | item["task_current_prog_size"] = 0 32 | item["task_total_prog_size"] = 0 33 | item["task_current_prog_size_tail"] = "" 34 | item["task_total_prog_size_tail"] = "" 35 | item["dst_endpoint_link"] = "" 36 | item["dst_endpoint_id"] = "" 37 | item["is_reset"] = 0 38 | 39 | insert_callback = load.task_list.insert_many(tmp_task_list) 40 | if insert_callback.inserted_ids: 41 | load.db_counters.update({"_id": "task_list_id"},{"future_id":future_id},upsert=True) 42 | update.effective_message.reply_text( 43 | _text[_lang]["add_task_successful"] 44 | ) 45 | 46 | 47 | def taskinfo(update, context): 48 | entry_cmd = update.effective_message.text 49 | match_cmd = re.search(r'^\/task ([1-9]\d*)$', entry_cmd, flags=re.I) 50 | if " " in entry_cmd: 51 | entry_cmd = entry_cmd.replace(" ","") 52 | 53 | if entry_cmd == "/task": 54 | current_task = load.task_list.find_one({"status":2}) 55 | if current_task is not None: 56 | current_task_id = current_task['_id'] 57 | current_task_src_name = current_task['src_name'] 58 | current_task_dst_name = current_task['dst_name'] 59 | update.effective_message.reply_text( 60 | _text[_lang]["is_current_task"] 61 | + _text[_lang]["current_task_id"] 62 | + str(current_task_id) 63 | + "\n" 64 | + _text[_lang]["current_task_src_name"] 65 | + current_task_src_name 66 | + "\n" 67 | + _text[_lang]["current_task_dst_name"] 68 | + current_task_dst_name 69 | ) 70 | 71 | return ConversationHandler.END 72 | 73 | else: 74 | update.effective_message.reply_text( 75 | _text[_lang]['is_not_current_task'] 76 | ) 77 | 78 | return ConversationHandler.END 79 | 80 | elif entry_cmd[5:] == "list": 81 | global waititem 82 | global waitlist 83 | task_list = load.task_list.find({"status":0}).limit(10) 84 | 85 | if task_list != []: 86 | for doc in task_list: 87 | waititem = _text[_lang]["current_task_id"] + str(doc['_id']) + _text[_lang]["current_task_src_name"] + doc['src_name'] + "\n--------------------\n" 88 | waitlist.append(waititem) 89 | 90 | task_wait_num = len(waitlist) 91 | waitlist = "".join(waitlist) 92 | 93 | update.effective_message.reply_text(str(task_wait_num) +_text[_lang]["show_wait_list"] + waitlist) 94 | 95 | waitlist = [] 96 | 97 | return ConversationHandler.END 98 | 99 | else: 100 | update.effective_message.reply_text( 101 | _text[_lang]["show_wait_list_null"] 102 | ) 103 | 104 | return ConversationHandler.END 105 | 106 | elif match_cmd: 107 | limit_query = load.db_counters.find_one({"_id":"task_list_id"}) 108 | check_query = match_cmd.group(1) 109 | 110 | if int(check_query) <= limit_query['future_id']: 111 | 112 | check_task = load.task_list.find_one({"_id": int(check_query)}) 113 | 114 | if check_task["status"] == 1: 115 | if "task_current_prog_num" and "task_current_prog_size_tail" in check_task: 116 | if check_task["error"] == 0: 117 | task_status_now = _text[_lang]["done"] 118 | elif check_task["error"] == 1: 119 | task_status_now == _text[_lang]["is_interrupted_error"] 120 | elif check_task["error"] == 9: 121 | task_status_now == _text[_lang]["is_killed_by_user"] 122 | 123 | check_msg = ( 124 | _text[_lang]["current_task_id"] 125 | + str(check_query) 126 | + "\n" 127 | + '{}'.format(check_task["dst_endpoint_link"], check_task["src_name"]) 128 | + "\n" 129 | + _text[_lang]["task_status"] 130 | + task_status_now 131 | + "\n" 132 | + _text[_lang]["total_file_num"] 133 | + str(check_task["task_current_prog_num"]) 134 | + "/" 135 | + str(check_task["task_total_prog_num"]) 136 | + "\n" 137 | + _text[_lang]["total_file_size"] 138 | + str(check_task["task_current_prog_size"]) 139 | + check_task["task_current_prog_size_tail"] 140 | + "/" 141 | + str(check_task["task_total_prog_size"]) 142 | + check_task["task_total_prog_size_tail"] 143 | ) 144 | 145 | else: 146 | check_msg = _text[_lang]["support_error"] 147 | 148 | elif check_task["status"] == 0: 149 | check_msg = _text[_lang]["task_is_in_queue"] + "\n" + _text[_lang]["finished_could_be_check"] 150 | 151 | elif check_task["status"] == 2: 152 | check_msg = _text[_lang]["doing"] + "\n" + _text[_lang]["finished_could_be_check"] 153 | 154 | update.effective_message.reply_text(check_msg,parse_mode=ParseMode.HTML,disable_web_page_preview=True) 155 | 156 | else: 157 | update.effective_message.reply_text(_text[_lang]["over_limit_to_check"]) 158 | 159 | 160 | else: 161 | return ConversationHandler.END 162 | 163 | def task_reset(update, context): 164 | entry_cmd = update.effective_message.text 165 | match_cmd = re.search(r'^\/RESET ([1-9]\d*)$', entry_cmd, flags=re.I) 166 | 167 | if "/reset" == entry_cmd: 168 | check_query = load.db_counters.find_one({"_id": "last_task"}) 169 | load.task_list.update_one({"_id": check_query['task_id']}, {"$set": {"status": 0,"is_reset": 1}}) 170 | update.effective_message.reply_text( 171 | _text[_lang]["reset_successful"].replace("replace",str(check_query['task_id'])) 172 | ) 173 | 174 | elif match_cmd: 175 | limit_query = load.db_counters.find_one({"_id":"task_list_id"}) 176 | check_query = match_cmd.group(1) 177 | 178 | if int(check_query) <= limit_query['future_id']: 179 | 180 | load.task_list.update_one({"_id": int(check_query)}, {"$set": {"status": 0,"is_reset": 1}}) 181 | update.effective_message.reply_text( 182 | _text[_lang]["reset_successful"].replace("replace",check_query) 183 | ) 184 | 185 | else: 186 | update.effective_message.reply_text( 187 | _text[_lang]["over_limit_error"] 188 | ) 189 | 190 | else: 191 | update.effective_message.reply_text( 192 | _text[_lang]["global_command_error"] 193 | ) -------------------------------------------------------------------------------- /iCopy/utils/task_payload.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re, time, pymongo 5 | from utils import load, process_bar as _bar, get_functions as _func 6 | from multiprocessing import Process as _mp, Manager 7 | from utils.load import _lang, _text 8 | import subprocess 9 | from threading import Timer 10 | from drive.gdrive import GoogleDrive as _gd 11 | from telegram import ParseMode 12 | from telegram.utils.request import Request as TGRequest 13 | from telegram import Bot 14 | 15 | myclient = pymongo.MongoClient( 16 | f"{load.cfg['database']['db_connect_method']}://{load.user}:{load.passwd}@{load.cfg['database']['db_addr']}", 17 | port=load.cfg["database"]["db_port"], 18 | connect=False, 19 | ) 20 | mydb = myclient[load.cfg["database"]["db_name"]] 21 | task_list = mydb["task_list"] 22 | db_counters = mydb["counters"] 23 | 24 | _cfg = load.cfg 25 | 26 | request = TGRequest(con_pool_size=8) 27 | bot = Bot(token=f"{_cfg['tg']['token']}", request=request) 28 | 29 | message_info = "" 30 | prog_bar = "" 31 | current_working_file = "" 32 | old_working_file = "" 33 | now_elapsed_time = "" 34 | context_old = "" 35 | icopyprocess = subprocess.Popen 36 | interruption = 0 37 | dst_id = "" 38 | src_name = "" 39 | 40 | 41 | def task_buffer(ns): 42 | global dst_id 43 | global src_name 44 | while True: 45 | wait_list = task_list.find({"status": 0}) 46 | for task in wait_list: 47 | if _cfg["general"]["cloner"] == "fclone": 48 | flags = ["--drive-server-side-across-configs", "--check-first"] 49 | else: 50 | flags = ["--drive-server-side-across-configs"] 51 | command = [] 52 | 53 | cloner = _cfg["general"]["cloner"] 54 | option = _cfg["general"]["option"] 55 | remote = _cfg["general"]["remote"] 56 | src_id = task["src_id"] 57 | src_name = task["src_name"] 58 | if "/" in src_name: 59 | src_name = src_name.replace("/", "|") 60 | if "'" in src_name: 61 | src_name = src_name.replace("'", "") 62 | if '"' in src_name: 63 | src_name = src_name.replace('"', "") 64 | 65 | dst_id = task["dst_id"] 66 | src_block = remote + ":" + "{" + src_id + "}" 67 | dst_block = remote + ":" + "{" + dst_id + "}" + "/" + src_name 68 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}" 69 | transfers = "--transfers=" + f"{_cfg['general']['parallel_t']}" 70 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}" 71 | 72 | flags += _cfg["general"]["run_args"] 73 | flags += [checkers, transfers, sa_sleep] 74 | 75 | command = [cloner, option, src_block, dst_block] 76 | 77 | command += flags 78 | 79 | chat_id = task["chat_id"] 80 | 81 | task_process(chat_id, command, task, ns) 82 | 83 | ns.x = 0 84 | 85 | flags = [] 86 | 87 | global old_working_line 88 | global current_working_line 89 | old_working_line = 0 90 | current_working_line = 0 91 | time.sleep(3) 92 | 93 | time.sleep(5) 94 | 95 | 96 | def task_process(chat_id, command, task, ns): 97 | # mark is in processing in db 98 | task_list.update_one({"_id": task["_id"]}, {"$set": {"status": 2,}}) 99 | db_counters.update({"_id": "last_task"}, {"task_id": task["_id"]}, upsert=True) 100 | chat_id = chat_id 101 | message = bot.send_message(chat_id=chat_id, text=_text[_lang]["ready_to_task"]) 102 | message_id = message.message_id 103 | 104 | interval = 0.1 105 | timeout = 60 106 | xtime = 0 107 | old_working_line = 0 108 | current_working_line = 0 109 | task_current_prog_num = 0 110 | task_total_prog_num = 0 111 | task_percent = 0 112 | task_current_prog_size = "0" 113 | task_total_prog_size = "0 Bytes" 114 | task_in_size_speed = "-" 115 | task_in_file_speed = "-" 116 | task_eta_in_file = "-" 117 | task_current_prog_size_tail = "" 118 | task_total_prog_size_tail = "" 119 | start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 120 | 121 | for toutput in run(command): 122 | if ns.x == 1: 123 | global icopyprocess 124 | icopyprocess.kill() 125 | 126 | regex_working_file = r"^ * " 127 | regex_elapsed_time = r"^Elapsed time:" 128 | regex_total_files = ( 129 | r"Transferred:\s+(\d+) / (\d+), (\d+)%(?:,\s*([\d.]+\sFiles/s))?" 130 | ) 131 | regex_total_size = ( 132 | r"Transferred:[\s]+([\d.]+\s*)([kMGTP]?) / ([\d.]+[\s]?)([kMGTP]?Bytes)," 133 | r"\s*(?:\-|(\d+)\%),\s*([\d.]+\s*[kMGTP]?Bytes/s),\s*ETA\s*([\-0-9hmsdwy]+)" 134 | ) 135 | 136 | output = toutput 137 | 138 | if output: 139 | task_total_files = re.search(regex_total_files, output) 140 | task_total_size = re.search(regex_total_size, output) 141 | task_elapsed_time = re.findall(regex_elapsed_time, output) 142 | task_working_file = re.findall(regex_working_file, output) 143 | 144 | if task_total_files: 145 | task_current_prog_num = task_total_files.group(1) 146 | task_total_prog_num = task_total_files.group(2) 147 | task_percent = int(task_total_files.group(3)) 148 | task_in_file_speed = task_total_files.group(4) 149 | 150 | if task_total_size: 151 | task_current_prog_size = task_total_size.group(1).strip() 152 | task_current_prog_size_tail = task_total_size.group(2) 153 | task_total_prog_size = task_total_size.group(3).strip() 154 | task_total_prog_size_tail = task_total_size.group(4) 155 | task_in_size_speed = task_total_size.group(6) 156 | task_eta_in_file = task_total_size.group(7) 157 | 158 | if task_elapsed_time: 159 | global now_elapsed_time 160 | now_elapsed_time = output.replace(" ", "").split(":")[1] 161 | 162 | if task_working_file: 163 | global current_working_file 164 | current_working_line += 1 165 | current_working_file = ( 166 | output.lstrip("* ").rsplit(":")[0].rstrip("Transferred") 167 | ) 168 | 169 | global prog_bar 170 | prog_bar = _bar.status(0) 171 | if task_percent != 0: 172 | prog_bar = _bar.status(task_percent) 173 | 174 | global message_info 175 | message_info = ( 176 | _text[_lang]["task_src_info"] 177 | + "\n" 178 | + "📃" 179 | + task["src_name"] 180 | + "\n" 181 | + "----------------------------------------" 182 | + "\n" 183 | + _text[_lang]["task_dst_info"] 184 | + "\n" 185 | + "📁" 186 | + task["dst_name"] 187 | + ":" 188 | + "\n" 189 | + " ┕─📃" 190 | + task["src_name"] 191 | + "\n" 192 | + "----------------------------------------" 193 | + "\n\n" 194 | + _text[_lang]["task_start_time"] 195 | + start_time 196 | + "\n\n" 197 | + _text[_lang]["task_files_size"] 198 | + str(task_current_prog_size) 199 | + task_current_prog_size_tail 200 | + "/" 201 | + str(task_total_prog_size) 202 | + task_total_prog_size_tail 203 | + "\n" 204 | + _text[_lang]["task_files_num"] 205 | + str(task_current_prog_num) 206 | + "/" 207 | + str(task_total_prog_num) 208 | + "\n" 209 | + _text[_lang]["task_status"] 210 | + "\n\n" 211 | + str(task_in_size_speed) 212 | + " | " 213 | + str(task_in_file_speed) 214 | + "\n\n" 215 | + str(task_percent) 216 | + "%" 217 | + str(prog_bar) 218 | ) 219 | 220 | if ( 221 | int(time.time()) - xtime > interval 222 | and old_working_line != current_working_line 223 | ): 224 | Timer( 225 | 0, 226 | task_message_box, 227 | args=( 228 | bot, 229 | chat_id, 230 | message_id, 231 | " ༺ ✪iCopy✪ ༻ \n" 232 | + _text[_lang]["doing"] 233 | + " | " 234 | + "🏳️" 235 | + _text[_lang]["current_task_id"] 236 | + str(task["_id"]) 237 | + "\n\n" 238 | + message_info 239 | + "\n\n" 240 | + current_working_file[:30] 241 | + "\n" 242 | + "ETA : " 243 | + str(task_eta_in_file), 244 | ), 245 | ).start() 246 | old_working_line = current_working_line 247 | global old_working_file 248 | old_working_file = current_working_file 249 | time.sleep(3.5) 250 | xtime = time.time() 251 | 252 | if ( 253 | int(time.time()) - xtime > timeout 254 | and current_working_file == old_working_file 255 | and task_percent > 5 256 | ): 257 | global interruption 258 | interruption = 1 259 | break 260 | 261 | old_working_file = "" 262 | finished_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) 263 | 264 | dst_endpoint_id = _gd.get_dst_endpoint_id(_gd(), dst_id, src_name) 265 | if dst_endpoint_id: 266 | dst_endpoint_link = r"https://drive.google.com/open?id={}".format( 267 | dst_endpoint_id["id"] 268 | ) 269 | 270 | if ns.x == 0: 271 | time.sleep(5) 272 | prog_bar = _bar.status(100) 273 | bot.edit_message_text( 274 | chat_id=chat_id, 275 | message_id=message_id, 276 | text=" ༺ ✪iCopy✪ ༻ \n" 277 | + _text[_lang]["done"] 278 | + " | " 279 | + "🏳️" 280 | + _text[_lang]["current_task_id"] 281 | + str(task["_id"]) 282 | + "\n\n" 283 | + message_info.replace( 284 | " ┕─📃" + task["src_name"], 285 | " ┕─📃" 286 | + '{}'.format(dst_endpoint_link, task["src_name"]), 287 | ) 288 | + "\n" 289 | + _text[_lang]["task_finished_time"] 290 | + finished_time 291 | + "\n" 292 | + _text[_lang]["elapsed_time"] 293 | + str(now_elapsed_time), 294 | parse_mode=ParseMode.HTML, 295 | disable_web_page_preview=True, 296 | ) 297 | check_is_reset = task_list.find_one({"_id": task["_id"]}) 298 | if check_is_reset['is_reset'] == 0: 299 | if task_total_prog_size != 0 and task_current_prog_size != 0: 300 | task_list.update_one( 301 | {"_id": task["_id"]}, 302 | { 303 | "$set": { 304 | "status": 1, 305 | "start_time": start_time, 306 | "finished_time": finished_time, 307 | "task_current_prog_num": int(task_current_prog_num), 308 | "task_total_prog_num": int(task_total_prog_num), 309 | "task_current_prog_size": float(task_current_prog_size), 310 | "task_total_prog_size": float(task_total_prog_size), 311 | "task_current_prog_size_tail": task_current_prog_size_tail, 312 | "task_total_prog_size_tail": task_total_prog_size_tail, 313 | "dst_endpoint_link": dst_endpoint_link, 314 | "dst_endpoint_id": dst_endpoint_id["id"], 315 | } 316 | }, 317 | ) 318 | else: 319 | task_list.update_one( 320 | {"_id": task["_id"]}, 321 | { 322 | "$set": { 323 | "status": 1, 324 | "start_time": start_time, 325 | "finished_time": finished_time, 326 | "task_current_prog_num": int(task_current_prog_num), 327 | "task_total_prog_num": int(task_total_prog_num), 328 | "task_current_prog_size": int(task_current_prog_size), 329 | "task_total_prog_size": int(task_total_prog_size), 330 | "task_current_prog_size_tail": task_current_prog_size_tail, 331 | "task_total_prog_size_tail": task_total_prog_size_tail, 332 | "dst_endpoint_link": dst_endpoint_link, 333 | "dst_endpoint_id": dst_endpoint_id["id"], 334 | } 335 | }, 336 | ) 337 | 338 | elif check_is_reset['is_reset'] == 1: 339 | if "task_current_prog_num" and "task_total_prog_num" in check_is_reset: 340 | task_list.update_one( 341 | {"_id": task["_id"]}, 342 | { 343 | "$set": { 344 | "status": 1, 345 | "start_time": start_time, 346 | "finished_time": finished_time, 347 | "task_current_prog_num": int(task_current_prog_num) + check_is_reset['task_current_prog_num'], 348 | "task_total_prog_num": int(task_total_prog_num) + check_is_reset['task_total_prog_num'], 349 | "task_current_prog_size": 0, 350 | "task_total_prog_size": 0, 351 | "task_current_prog_size_tail": "", 352 | "task_total_prog_size_tail": "", 353 | "dst_endpoint_link": dst_endpoint_link, 354 | "dst_endpoint_id": dst_endpoint_id["id"], 355 | } 356 | }, 357 | ) 358 | else: 359 | task_list.update_one( 360 | {"_id": task["_id"]}, 361 | { 362 | "$set": { 363 | "status": 1, 364 | "start_time": start_time, 365 | "finished_time": finished_time, 366 | "task_current_prog_num": int(task_current_prog_num), 367 | "task_total_prog_num": int(task_total_prog_num), 368 | "task_current_prog_size": 0, 369 | "task_total_prog_size": 0, 370 | "task_current_prog_size_tail": "", 371 | "task_total_prog_size_tail": "", 372 | "dst_endpoint_link": dst_endpoint_link, 373 | "dst_endpoint_id": dst_endpoint_id["id"], 374 | } 375 | }, 376 | ) 377 | 378 | if ns.x == 1: 379 | bot.edit_message_text( 380 | chat_id=chat_id, 381 | message_id=message_id, 382 | text=" ༺ ✪iCopy✪ ༻ \n" 383 | + _text[_lang]["killed"] 384 | + " | " 385 | + "🏳️" 386 | + _text[_lang]["current_task_id"] 387 | + str(task["_id"]) 388 | + "\n\n" 389 | + message_info.replace( 390 | "\n\n" + _text[_lang]["task_start_time"] + start_time, "" 391 | ).replace( 392 | "\n" 393 | + _text[_lang]["task_status"] 394 | + "\n\n" 395 | + str(task_in_size_speed) 396 | + " | " 397 | + str(task_in_file_speed), 398 | "", 399 | ) 400 | + "\n" 401 | + _text[_lang]["is_killed_by_user"], 402 | ) 403 | 404 | task_list.update_one( 405 | {"_id": task["_id"]}, 406 | { 407 | "$set": { 408 | "status": 1, 409 | "error": 9, 410 | "start_time": start_time, 411 | "finished_time": finished_time, 412 | } 413 | }, 414 | ) 415 | 416 | if interruption == 1: 417 | bot.edit_message_text( 418 | chat_id=chat_id, 419 | message_id=message_id, 420 | text=" ༺ ✪iCopy✪ ༻ \n" 421 | + _text[_lang]["interrupted"] 422 | + " | " 423 | + "🏳️" 424 | + _text[_lang]["current_task_id"] 425 | + str(task["_id"]) 426 | + "\n\n" 427 | + message_info.replace( 428 | "\n\n" + _text[_lang]["task_start_time"] + start_time, "" 429 | ).replace( 430 | "\n" 431 | + _text[_lang]["task_status"] 432 | + "\n\n" 433 | + str(task_in_size_speed) 434 | + " | " 435 | + str(task_in_file_speed), 436 | "", 437 | ) 438 | + "\n" 439 | + _text[_lang]["is_interrupted_error"], 440 | ) 441 | 442 | task_list.update_one( 443 | {"_id": task["_id"]}, 444 | { 445 | "$set": { 446 | "status": 1, 447 | "error": 1, 448 | "start_time": start_time, 449 | "finished_time": finished_time, 450 | } 451 | }, 452 | ) 453 | 454 | prog_bar = _bar.status(0) 455 | 456 | 457 | def task_message_box(bot, chat_id, message_id, context): 458 | global context_old 459 | context_old = "iCopy" 460 | if context_old != context: 461 | bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=context) 462 | context_old = context 463 | 464 | 465 | def run(command): 466 | global icopyprocess 467 | icopyprocess = subprocess.Popen( 468 | command, 469 | stdout=subprocess.PIPE, 470 | stderr=subprocess.STDOUT, 471 | shell=False, 472 | encoding="utf-8", 473 | errors="ignore", 474 | universal_newlines=True, 475 | ) 476 | while True: 477 | line = icopyprocess.stdout.readline().rstrip() 478 | if not line: 479 | break 480 | yield line 481 | icopyprocess.communicate() 482 | 483 | -------------------------------------------------------------------------------- /iCopy/workflow/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/f0rx4/FClone-Bot/f73d648a4a3a5c727bbb79b98af5cf61fb7d2498/iCopy/workflow/__init__.py -------------------------------------------------------------------------------- /iCopy/workflow/copy_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils.load import _lang, _text 5 | from telegram.ext import ConversationHandler 6 | from utils import ( 7 | messages as _msg, 8 | restricted as _r, 9 | get_functions as _func, 10 | task_box as _box, 11 | keyboard as _KB, 12 | callback_stage as _stage, 13 | ) 14 | 15 | current_dst_info = "" 16 | 17 | 18 | @_r.restricted 19 | @_r.restricted_copy 20 | def copy(update, context): 21 | _func.mode = "copy" 22 | call_mode = update.effective_message.text 23 | 24 | if "/copy" == call_mode.strip()[:5]: 25 | update.effective_message.reply_text( 26 | _text[_lang]["mode_select_msg"].replace( 27 | "replace", _text[_lang]["copy_mode"] 28 | ) 29 | + "\n" 30 | + _text[_lang]["request_dst_target"], 31 | reply_markup=_KB.dst_keyboard(update, context), 32 | ) 33 | 34 | return _stage.GET_DST 35 | 36 | if update.callback_query.data == "copy": 37 | update.callback_query.edit_message_text( 38 | _text[_lang]["mode_select_msg"].replace( 39 | "replace", _text[_lang]["copy_mode"] 40 | ) 41 | + "\n" 42 | + _text[_lang]["request_dst_target"], 43 | reply_markup=_KB.dst_keyboard(update, context), 44 | ) 45 | 46 | return _stage.GET_DST 47 | 48 | 49 | def request_srcinfo(update, context): 50 | global current_dst_info 51 | current_dst_info = update.callback_query.data 52 | update.callback_query.edit_message_text(_text[_lang]["request_share_link"]) 53 | 54 | return _stage.GET_LINK 55 | 56 | -------------------------------------------------------------------------------- /iCopy/workflow/dedupe_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | from utils.load import _lang, _text, ns 6 | from utils import ( 7 | load, 8 | restricted as _r, 9 | keyboard as _KB, 10 | dedupe_payload as _d_payload, 11 | callback_stage as _stage, 12 | ) 13 | from multiprocessing import Process as _mp 14 | from telegram.ext import ConversationHandler 15 | from drive.gdrive import GoogleDrive as _gd 16 | 17 | bot = load.bot 18 | check_task = {} 19 | dedufav_callback = "" 20 | ns.dedupe = 0 21 | 22 | @_r.restricted 23 | def dedupe(update, context): 24 | entry_cmd = update.effective_message.text 25 | match_cmd = re.search(r"^\/dedupe ([1-9]\d*)$", entry_cmd, flags=re.I) 26 | 27 | if entry_cmd == "/dedupe": 28 | update.effective_message.reply_text( 29 | _text[_lang]["mode_select_msg"].replace( 30 | "replace", _text[_lang]["dedupe_mode"] 31 | ) 32 | + "\n" 33 | + _text[_lang]["request_target_folder"], 34 | reply_markup=_KB.dst_keyboard(update, context), 35 | ) 36 | 37 | return _stage.COOK_FAV_DEDU 38 | 39 | elif match_cmd: 40 | limit_query = load.db_counters.find_one({"_id": "task_list_id"}) 41 | check_query = match_cmd.group(1) 42 | 43 | if int(check_query) <= limit_query["future_id"]: 44 | 45 | global check_task 46 | check_task = load.task_list.find_one({"_id": int(check_query)}) 47 | 48 | if check_task["status"] == 1: 49 | update.effective_message.reply_text( 50 | _text[_lang]["mode_select_msg"].replace( 51 | "replace", _text[_lang]["dedupe_mode"] 52 | ) 53 | + "\n" 54 | + _text[_lang]["request_dedupe_mode"], 55 | reply_markup=_KB.dedupe_mode_keyboard(), 56 | ) 57 | 58 | return _stage.COOK_ID_DEDU 59 | 60 | elif check_task["status"] == 0: 61 | update.effective_message.reply_text( 62 | _text[_lang]["task_is_in_queue"] 63 | + "\n" 64 | + _text[_lang]["finished_could_be_dedupe"], 65 | ) 66 | 67 | return ConversationHandler.END 68 | 69 | elif check_task["status"] == 2: 70 | update.effective_message.reply_text( 71 | _text[_lang]["doing"] 72 | + "\n" 73 | + _text[_lang]["finished_could_be_dedupe"], 74 | ) 75 | 76 | return ConversationHandler.END 77 | 78 | else: 79 | update.effective_message.reply_text(_text[_lang]["over_limit_to_dedupe"],) 80 | 81 | return ConversationHandler.END 82 | 83 | else: 84 | update.effective_message.reply_text(_text[_lang]["global_command_error"]) 85 | 86 | 87 | def dedupe_mode(update, context): 88 | 89 | get_callback = update.callback_query.data 90 | dedu_msg = bot.edit_message_text( 91 | chat_id=update.callback_query.message.chat_id, 92 | message_id=update.callback_query.message.message_id, 93 | text=_text[_lang]["ready_to_dedupe"], 94 | reply_markup=None, 95 | ) 96 | 97 | dedu_chat_id = dedu_msg.chat_id 98 | dedu_message_id = dedu_msg.message_id 99 | dedu_mode = get_callback 100 | 101 | if "dst_endpoint_id" and "dst_endpoint_link" in check_task: 102 | dedu_task_id = check_task["_id"] 103 | dedu_name = check_task["src_name"] 104 | dedu_id = check_task["dst_endpoint_id"] 105 | dedu_link = check_task["dst_endpoint_link"] 106 | 107 | else: 108 | dst_endpoint_id = _gd.get_dst_endpoint_id( 109 | _gd(), check_task["dst_id"], check_task["src_name"] 110 | ) 111 | if dst_endpoint_id: 112 | dst_endpoint_link = r"https://drive.google.com/open?id={}".format( 113 | dst_endpoint_id["id"] 114 | ) 115 | 116 | load.task_list.update_one( 117 | {"_id": int(check_task["_id"])}, 118 | { 119 | "$set": { 120 | "dst_endpoint_id": dst_endpoint_id["id"], 121 | "dst_endpoint_link": dst_endpoint_link, 122 | }, 123 | }, 124 | ) 125 | 126 | dedu_task_id = check_task["_id"] 127 | dedu_name = check_task["src_name"] 128 | dedu_id = dst_endpoint_id 129 | dedu_link = dst_endpoint_link 130 | 131 | progress = _mp( 132 | target=_d_payload.dedupe_task, 133 | args=( 134 | ns, 135 | dedu_mode, 136 | dedu_chat_id, 137 | dedu_message_id, 138 | dedu_task_id, 139 | dedu_link, 140 | dedu_id, 141 | dedu_name, 142 | ), 143 | ) 144 | progress.start() 145 | 146 | bot.edit_message_text( 147 | chat_id=dedu_chat_id, message_id=dedu_message_id, text=_text[_lang]["deduping"], 148 | ) 149 | 150 | return ConversationHandler.END 151 | 152 | 153 | def dedupe_fav_mode(update, context): 154 | global dedufav_callback 155 | dedufav_callback = update.callback_query.data 156 | update.callback_query.edit_message_text( 157 | _text[_lang]["mode_select_msg"].replace("replace", _text[_lang]["dedupe_mode"]) 158 | + "\n" 159 | + _text[_lang]["request_dedupe_mode"], 160 | reply_markup=_KB.dedupe_mode_keyboard(), 161 | ) 162 | 163 | return _stage.FAV_PRE_DEDU_INFO 164 | 165 | 166 | def pre_favdedu_info(update, context): 167 | dedu_mode = update.callback_query.data 168 | 169 | dedu_msg = update.callback_query.edit_message_text( 170 | text=_text[_lang]["ready_to_dedupe"], 171 | reply_markup=None, 172 | ) 173 | 174 | dedu_chat_id = dedu_msg.chat_id 175 | dedu_message_id = dedu_msg.message_id 176 | 177 | dedu_task_id = 0 178 | 179 | dedufav_info = dedufav_callback.split("id+name") 180 | dedu_id = dedufav_info[0] 181 | dedu_name = dedufav_info[1] 182 | dedu_link = r"https://drive.google.com/open?id={}".format( 183 | dedufav_info[0] 184 | ) 185 | 186 | progress = _mp( 187 | target=_d_payload.dedupe_task, 188 | args=( 189 | ns, 190 | dedu_mode, 191 | dedu_chat_id, 192 | dedu_message_id, 193 | dedu_task_id, 194 | dedu_link, 195 | dedu_id, 196 | dedu_name, 197 | ), 198 | ) 199 | progress.start() 200 | 201 | bot.edit_message_text( 202 | chat_id=dedu_chat_id, message_id=dedu_message_id, text=_text[_lang]["deduping"], 203 | ) 204 | 205 | return ConversationHandler.END 206 | -------------------------------------------------------------------------------- /iCopy/workflow/purge_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils.load import _lang, _text, ns 5 | from utils import ( 6 | load, 7 | restricted as _r, 8 | keyboard as _KB, 9 | purge_payload as _p_payload, 10 | callback_stage as _stage, 11 | ) 12 | from multiprocessing import Process as _mp 13 | from telegram.ext import ConversationHandler 14 | 15 | bot = load.bot 16 | ns.purge = 0 17 | 18 | @_r.restricted 19 | def purge(update, context): 20 | update.effective_message.reply_text( 21 | _text[_lang]["mode_select_msg"].replace( 22 | "replace", _text[_lang]["purge_mode"] 23 | ) 24 | + "\n" 25 | + _text[_lang]["request_target_folder"], 26 | reply_markup=_KB.dst_keyboard(update, context), 27 | ) 28 | 29 | return _stage.COOK_FAV_PURGE 30 | 31 | def pre_to_purge(update, context): 32 | get_callback = update.callback_query.data 33 | purge_msg = bot.edit_message_text( 34 | chat_id=update.callback_query.message.chat_id, 35 | message_id=update.callback_query.message.message_id, 36 | text=_text[_lang]["ready_to_purge"], 37 | reply_markup=None, 38 | ) 39 | 40 | purge_chat_id = purge_msg.chat_id 41 | purge_message_id = purge_msg.message_id 42 | 43 | fav_info_list = get_callback.split("id+name") 44 | fav_id = fav_info_list[0] 45 | fav_name = fav_info_list[1] 46 | 47 | if len(fav_id) < 28: 48 | progress = _mp( 49 | target=_p_payload.purge_fav, 50 | args=( 51 | ns, 52 | purge_chat_id, 53 | purge_message_id, 54 | fav_id, 55 | fav_name, 56 | ), 57 | ) 58 | 59 | progress.start() 60 | 61 | bot.edit_message_text( 62 | chat_id=purge_chat_id, 63 | message_id=purge_message_id, 64 | text=_text[_lang]["purging"], 65 | ) 66 | 67 | return ConversationHandler.END 68 | 69 | else: 70 | bot.edit_message_text( 71 | chat_id=purge_chat_id, 72 | message_id=purge_message_id, 73 | text=_text[_lang]["is_folder_not_drive"], 74 | ) 75 | 76 | return ConversationHandler.END -------------------------------------------------------------------------------- /iCopy/workflow/quick_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils.load import _lang, _text 5 | from telegram.ext import ConversationHandler 6 | from utils import ( 7 | restricted as _r, 8 | get_functions as _func, 9 | task_box as _box, 10 | callback_stage as _stage, 11 | ) 12 | 13 | @_r.restricted 14 | @_r.restricted_quick 15 | def quick(update, context): 16 | _func.mode = "quick" 17 | call_mode = update.effective_message.text 18 | 19 | if "/quick" == call_mode.strip()[:6]: 20 | update.effective_message.reply_text( 21 | _text[_lang]["mode_select_msg"].replace( 22 | "replace", _text[_lang]["quick_mode"] 23 | ) 24 | + "\n" 25 | + _text[_lang]["request_share_link"] 26 | ) 27 | 28 | return _stage.GET_LINK 29 | 30 | if update.callback_query.data == "quick": 31 | update.callback_query.edit_message_text( 32 | _text[_lang]["mode_select_msg"].replace( 33 | "replace", _text[_lang]["quick_mode"] 34 | ) 35 | + "\n" 36 | + _text[_lang]["request_share_link"] 37 | ) 38 | 39 | return _stage.GET_LINK 40 | -------------------------------------------------------------------------------- /iCopy/workflow/regex_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | from utils.load import _lang, _text, ns 5 | from utils import ( 6 | load, 7 | get_functions as _func, 8 | restricted as _r, 9 | keyboard as _KB, 10 | task_box as _box, 11 | size_payload as _s_payload, 12 | callback_stage as _stage, 13 | ) 14 | from telegram.ext import CallbackQueryHandler, ConversationHandler 15 | from threading import Thread 16 | from multiprocessing import Process as _mp 17 | 18 | ns.size = 0 19 | ns.dedupe = 0 20 | ns.purge = 0 21 | 22 | src_name_list = [] 23 | src_id_list = [] 24 | regex_in_update = None 25 | regex_in_context = None 26 | 27 | 28 | @_r.restricted 29 | def regex_entry(update, context): 30 | global src_id_list 31 | global src_name_list 32 | src_id_list = [] 33 | src_name_list = [] 34 | tmp_src_name_list = "" 35 | get_share_link = update.effective_message.text 36 | src_id_list = _func.cook_to_id(get_share_link) 37 | for item in src_id_list: 38 | src_name_list += _func.get_src_name_from_id( 39 | update, taget_id=item, list_name=tmp_src_name_list 40 | ) 41 | tmp_src_name_list = "" 42 | 43 | update.effective_message.reply_text( 44 | _text[_lang]["menu_msg"], reply_markup=_KB.regex_in_keyboard(), 45 | ) 46 | 47 | global regex_in_update 48 | global regex_in_context 49 | regex_in_update = update 50 | regex_in_context = context 51 | 52 | return _stage.REGEX_IN 53 | 54 | 55 | def regex_callback(update, context): 56 | if "quick" == update.callback_query.data: 57 | load.bot.edit_message_text( 58 | chat_id=update.callback_query.message.chat_id, 59 | message_id=update.callback_query.message.message_id, 60 | text=_text[_lang]["mode_select_msg"].replace( 61 | "replace", _text[_lang]["quick_mode"] 62 | ), 63 | reply_markup=None, 64 | ) 65 | 66 | regex_in_chat_id = regex_in_update.effective_message.chat_id 67 | regex_in_message_id = regex_in_update.effective_message.message_id 68 | tmp_task_list = [] 69 | mode = "quick" 70 | is_quick = {"_id": "fav_quick"} 71 | is_quick_cur = load.fav_col.find(is_quick) 72 | 73 | if is_quick_cur is not None: 74 | for doc in is_quick_cur: 75 | dst_id = doc["G_id"] 76 | dst_name = doc["G_name"] 77 | 78 | for item in src_name_list: 79 | src_id = item["G_id"] 80 | src_name = item["G_name"] 81 | 82 | tmp_task_list.append( 83 | { 84 | "mode_type": mode, 85 | "src_id": src_id, 86 | "src_name": src_name, 87 | "dst_id": dst_id, 88 | "dst_name": dst_name, 89 | "chat_id": regex_in_chat_id, 90 | "raw_message_id": regex_in_message_id, 91 | } 92 | ) 93 | 94 | Thread( 95 | target=_box.cook_task_to_db, 96 | args=(regex_in_update, regex_in_context, tmp_task_list), 97 | ).start() 98 | tmp_task_list = [] 99 | return ConversationHandler.END 100 | 101 | if "copy" == update.callback_query.data: 102 | update.callback_query.edit_message_text( 103 | _text[_lang]["mode_select_msg"].replace( 104 | "replace", _text[_lang]["copy_mode"] 105 | ) 106 | + "\n" 107 | + _text[_lang]["request_dst_target"], 108 | reply_markup=_KB.dst_keyboard(update, context), 109 | ) 110 | 111 | return _stage.REGEX_GET_DST 112 | 113 | if "size" == update.callback_query.data: 114 | 115 | for item in src_id_list: 116 | size_msg = load.bot.edit_message_text( 117 | chat_id=update.callback_query.message.chat_id, 118 | message_id=update.callback_query.message.message_id, 119 | text=_text[_lang]["ready_to_size"], 120 | reply_markup=None, 121 | ) 122 | size_chat_id = size_msg.chat_id 123 | size_message_id = size_msg.message_id 124 | 125 | # to payload 126 | progress = _mp( 127 | target=_s_payload.simple_size, 128 | args=( 129 | ns, 130 | update, 131 | context, 132 | item, 133 | size_chat_id, 134 | size_message_id, 135 | src_name_list, 136 | ), 137 | ) 138 | progress.start() 139 | 140 | context.bot.edit_message_text( 141 | chat_id=size_chat_id, 142 | message_id=size_message_id, 143 | text=_text[_lang]["sizing"], 144 | ) 145 | 146 | return ConversationHandler.END 147 | 148 | 149 | def regex_copy_end(update, context): 150 | 151 | load.bot.edit_message_text( 152 | chat_id=update.callback_query.message.chat_id, 153 | message_id=update.callback_query.message.message_id, 154 | text=_text[_lang]["mode_select_msg"].replace( 155 | "replace", _text[_lang]["copy_mode"] 156 | ), 157 | reply_markup=None, 158 | ) 159 | 160 | mode = "copy" 161 | regex_in_chat_id = regex_in_update.effective_message.chat_id 162 | regex_in_message_id = regex_in_update.effective_message.message_id 163 | tmp_task_list = [] 164 | 165 | is_dstinfo = update.callback_query.data 166 | dstinfo = is_dstinfo.split("id+name") 167 | dst_id = dstinfo[0] 168 | dst_name = dstinfo[1] 169 | 170 | for item in src_name_list: 171 | src_id = item["G_id"] 172 | src_name = item["G_name"] 173 | 174 | tmp_task_list.append( 175 | { 176 | "mode_type": mode, 177 | "src_id": src_id, 178 | "src_name": src_name, 179 | "dst_id": dst_id, 180 | "dst_name": dst_name, 181 | "chat_id": regex_in_chat_id, 182 | "raw_message_id": regex_in_message_id, 183 | } 184 | ) 185 | 186 | Thread( 187 | target=_box.cook_task_to_db, 188 | args=(regex_in_update, regex_in_context, tmp_task_list), 189 | ).start() 190 | dstinfo = "" 191 | return ConversationHandler.END 192 | 193 | -------------------------------------------------------------------------------- /iCopy/workflow/size_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | from telegram.ext import ConversationHandler 6 | from utils.load import _lang, _text, ns 7 | from utils import ( 8 | load, 9 | restricted as _r, 10 | get_functions as _func, 11 | task_box as _box, 12 | size_payload as _s_payload, 13 | keyboard as _KB, 14 | callback_stage as _stage, 15 | ) 16 | from drive.gdrive import GoogleDrive as _gd 17 | from multiprocessing import Process as _mp 18 | 19 | bot = load.bot 20 | ns.size = 0 21 | 22 | @_r.restricted 23 | def size(update, context): 24 | entry_cmd = update.effective_message.text 25 | match_cmd = re.search(r"^\/size ([1-9]\d*)$", entry_cmd, flags=re.I) 26 | if "/size" == entry_cmd: 27 | update.effective_message.reply_text(_text[_lang]["request_share_link"]) 28 | 29 | return _stage.COOK_ID 30 | 31 | elif match_cmd: 32 | limit_query = load.db_counters.find_one({"_id": "task_list_id"}) 33 | check_query = match_cmd.group(1) 34 | size_msg = update.effective_message.reply_text(_text[_lang]["ready_to_size"]) 35 | size_chat_id = size_msg.chat_id 36 | size_message_id = size_msg.message_id 37 | 38 | if int(check_query) <= limit_query["future_id"]: 39 | 40 | check_task = load.task_list.find_one({"_id": int(check_query)}) 41 | 42 | if check_task["status"] == 1: 43 | if "dst_endpoint_link" and "dst_endpoint_id" in check_task: 44 | task_id = str(check_query) 45 | task_link = check_task["dst_endpoint_link"] 46 | endpoint_id = check_task["dst_endpoint_id"] 47 | endpoint_name = check_task["src_name"] 48 | 49 | progress = _mp( 50 | target=_s_payload.owner_size, 51 | args=( 52 | ns, 53 | size_chat_id, 54 | size_message_id, 55 | task_id, 56 | task_link, 57 | endpoint_id, 58 | endpoint_name, 59 | ), 60 | ) 61 | progress.start() 62 | 63 | context.bot.edit_message_text( 64 | chat_id=size_chat_id, 65 | message_id=size_message_id, 66 | text=_text[_lang]["sizing"], 67 | ) 68 | 69 | return ConversationHandler.END 70 | 71 | else: 72 | dst_endpoint_id = _gd.get_dst_endpoint_id( 73 | _gd(), check_task["dst_id"], check_task["src_name"] 74 | ) 75 | if dst_endpoint_id: 76 | dst_endpoint_link = r"https://drive.google.com/open?id={}".format( 77 | dst_endpoint_id["id"] 78 | ) 79 | 80 | load.task_list.update_one( 81 | {"_id": int(check_query)}, 82 | { 83 | "$set": { 84 | "dst_endpoint_id": dst_endpoint_id["id"], 85 | "dst_endpoint_link": dst_endpoint_link, 86 | }, 87 | }, 88 | ) 89 | 90 | task_id = str(check_query) 91 | task_link = dst_endpoint_link 92 | endpoint_id = dst_endpoint_id["id"] 93 | endpoint_name = check_task["src_name"] 94 | 95 | progress = _mp( 96 | target=_s_payload.owner_size, 97 | args=( 98 | ns, 99 | size_chat_id, 100 | size_message_id, 101 | task_id, 102 | task_link, 103 | endpoint_id, 104 | endpoint_name, 105 | ), 106 | ) 107 | progress.start() 108 | 109 | context.bot.edit_message_text( 110 | chat_id=size_chat_id, 111 | message_id=size_message_id, 112 | text=_text[_lang]["sizing"], 113 | ) 114 | 115 | return ConversationHandler.END 116 | 117 | else: 118 | bot.edit_message_text( 119 | chat_id=size_chat_id, 120 | message_id=size_message_id, 121 | text=_text[_lang]["support_error"], 122 | ) 123 | 124 | return ConversationHandler.END 125 | 126 | elif check_task["status"] == 0: 127 | bot.edit_message_text( 128 | chat_id=size_chat_id, 129 | message_id=size_message_id, 130 | text=_text[_lang]["task_is_in_queue"] 131 | + "\n" 132 | + _text[_lang]["finished_could_be_check"], 133 | ) 134 | 135 | return ConversationHandler.END 136 | 137 | elif check_task["status"] == 2: 138 | bot.edit_message_text( 139 | chat_id=size_chat_id, 140 | message_id=size_message_id, 141 | text=_text[_lang]["doing"] 142 | + "\n" 143 | + _text[_lang]["finished_could_be_check"], 144 | ) 145 | 146 | return ConversationHandler.END 147 | 148 | else: 149 | bot.edit_message_text( 150 | chat_id=size_chat_id, 151 | message_id=size_message_id, 152 | text=_text[_lang]["over_limit_to_check"], 153 | ) 154 | 155 | return ConversationHandler.END 156 | 157 | elif entry_cmd.split(" ")[1].strip() == "fav": 158 | update.effective_message.reply_text( 159 | _text[_lang]["request_target_folder"], 160 | reply_markup=_KB.dst_keyboard(update, context), 161 | ) 162 | 163 | return _stage.COOK_FAV_TO_SIZE 164 | 165 | 166 | def pre_cook_fav_to_size(update, context): 167 | get_callback = update.callback_query.data 168 | size_msg = bot.edit_message_text( 169 | chat_id=update.callback_query.message.chat_id, 170 | message_id=update.callback_query.message.message_id, 171 | text=_text[_lang]["ready_to_size"], 172 | reply_markup=None, 173 | ) 174 | size_chat_id = size_msg.chat_id 175 | size_message_id = size_msg.message_id 176 | 177 | fav_info_list = get_callback.split("id+name") 178 | fav_id = fav_info_list[0] 179 | fav_name = fav_info_list[1] 180 | 181 | task_id = 0 182 | task_link = r"https://drive.google.com/open?id={}".format(fav_id) 183 | endpoint_id = fav_id 184 | endpoint_name = fav_name 185 | 186 | progress = _mp( 187 | target=_s_payload.owner_size, 188 | args=( 189 | ns, 190 | size_chat_id, 191 | size_message_id, 192 | task_id, 193 | task_link, 194 | endpoint_id, 195 | endpoint_name, 196 | ), 197 | ) 198 | progress.start() 199 | 200 | context.bot.edit_message_text( 201 | chat_id=size_chat_id, 202 | message_id=size_message_id, 203 | text=_text[_lang]["sizing"], 204 | ) 205 | 206 | return ConversationHandler.END 207 | 208 | 209 | def size_handle(update, context): 210 | tmp_share_name_list = "" 211 | share_id_list = [] 212 | share_name_list = [] 213 | share_id = update.effective_message.text 214 | share_id_list = _func.cook_to_id(share_id) 215 | for item in share_id_list: 216 | size_msg = update.effective_message.reply_text(_text[_lang]["ready_to_size"]) 217 | 218 | size_chat_id = size_msg.chat_id 219 | size_message_id = size_msg.message_id 220 | share_name_list += _func.get_src_name_from_id( 221 | update, item, list_name=tmp_share_name_list 222 | ) 223 | tmp_share_name_list = "" 224 | progress = _mp( 225 | target=_s_payload.simple_size, 226 | args=( 227 | ns, 228 | update, 229 | context, 230 | item, 231 | size_chat_id, 232 | size_message_id, 233 | share_name_list, 234 | ), 235 | ) 236 | progress.start() 237 | 238 | context.bot.edit_message_text( 239 | chat_id=size_chat_id, 240 | message_id=size_message_id, 241 | text=_text[_lang]["sizing"], 242 | ) 243 | 244 | return ConversationHandler.END 245 | 246 | -------------------------------------------------------------------------------- /iCopy/workflow/start_workflow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import os, logging, re 5 | from telegram import ParseMode 6 | from telegram.ext import ( 7 | Updater, 8 | CommandHandler, 9 | MessageHandler, 10 | Filters, 11 | CallbackQueryHandler, 12 | ConversationHandler, 13 | ) 14 | from utils.load import _lang, _text 15 | from utils import ( 16 | messages as _msg, 17 | restricted as _r, 18 | keyboard as _KB, 19 | callback_stage as _stage, 20 | ) 21 | 22 | @_r.restricted 23 | def start(update, context): 24 | _first_name = update.effective_user.first_name 25 | update.effective_message.reply_text( 26 | _text[_lang]["start"].replace("replace",_first_name) 27 | + "\n" 28 | + _text[_lang]["guide_to_menu"] 29 | ) 30 | 31 | @_r.restricted 32 | def menu(update, context): 33 | update.effective_message.reply_text( 34 | _text[_lang]["menu_msg"], 35 | reply_markup=_KB.start_keyboard(), 36 | ) 37 | 38 | return _stage.CHOOSE_MODE 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | python-telegram-bot==12.8 2 | urllib3==1.25.9 3 | requests==2.24.0 4 | google_api_python_client==1.10.0 5 | protobuf==3.12.2 6 | pymongo[srv]==3.10.1 7 | toml==0.10.1 8 | covid -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.8.5 --------------------------------------------------------------------------------