├── iCopy
├── drive
│ ├── __init__.py
│ └── gdrive.py
├── utils
│ ├── __init__.py
│ ├── __version__.py
│ ├── callback_stage.py
│ ├── process_bar.py
│ ├── restricted.py
│ ├── load.py
│ ├── keyboard.py
│ ├── messages.py
│ ├── purge_payload.py
│ ├── dedupe_payload.py
│ ├── task_box.py
│ ├── get_functions.py
│ ├── size_payload.py
│ ├── get_set.py
│ └── task_payload.py
├── web
│ ├── __init__.py
│ ├── vue-admin-simple
│ │ └── dist
│ │ │ ├── favicon.ico
│ │ │ ├── static
│ │ │ ├── img
│ │ │ │ ├── 404.a57b6f31.png
│ │ │ │ └── 404_cloud.0f4bc32b.png
│ │ │ ├── css
│ │ │ │ ├── chunk-718881df.f9066ea7.css
│ │ │ │ ├── chunk-130a7162.011d879c.css
│ │ │ │ ├── chunk-2fd9fc79.011d879c.css
│ │ │ │ ├── chunk-285369fe.6d571437.css
│ │ │ │ ├── chunk-libs.3dfb7769.css
│ │ │ │ └── chunk-238c903c.3c7f5ad9.css
│ │ │ ├── fonts
│ │ │ │ ├── fa-solid-900.781e85bb.ttf
│ │ │ │ ├── fa-solid-900.89bd2e38.eot
│ │ │ │ ├── element-icons.535877f5.woff
│ │ │ │ ├── element-icons.732389de.ttf
│ │ │ │ ├── fa-brands-400.085b1dd8.ttf
│ │ │ │ ├── fa-brands-400.0fabb660.eot
│ │ │ │ ├── fa-brands-400.cac68c83.woff2
│ │ │ │ ├── fa-brands-400.dc0bd022.woff
│ │ │ │ ├── fa-regular-400.05b53beb.woff
│ │ │ │ ├── fa-regular-400.1a78af41.ttf
│ │ │ │ ├── fa-regular-400.ad3a7c0d.eot
│ │ │ │ ├── fa-solid-900.c500da19.woff2
│ │ │ │ ├── fa-solid-900.ee09ad75.woff
│ │ │ │ └── fa-regular-400.3a3398a6.woff2
│ │ │ └── js
│ │ │ │ ├── chunk-2d21843e.48a6c318.js
│ │ │ │ ├── chunk-718881df.f7f1bdd7.js
│ │ │ │ ├── chunk-238c903c.f33d3eaf.js
│ │ │ │ ├── chunk-2fd9fc79.3cba3c7b.js
│ │ │ │ ├── chunk-285369fe.c6caa2dc.js
│ │ │ │ └── chunk-130a7162.684ec14a.js
│ │ │ └── index.html
│ ├── cook_resp.py
│ └── dash.py
├── workflow
│ ├── __init__.py
│ ├── start_workflow.py
│ ├── quick_workflow.py
│ ├── copy_workflow.py
│ ├── purge_workflow.py
│ ├── regex_workflow.py
│ ├── dedupe_workflow.py
│ └── size_workflow.py
├── docs
│ ├── TODO.md
│ ├── develop_update.md
│ ├── COMMAND.md
│ └── CHANGELOG.md
├── config
│ ├── conf.toml.example
│ └── text.toml
└── iCopy.py
├── runtime.txt
├── Procfile
├── Aptfile
├── requirements.txt
├── Set.sh
├── README.md
├── app.json
└── iCopy.ipynb
/iCopy/drive/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/iCopy/utils/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/iCopy/web/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.8.5
--------------------------------------------------------------------------------
/iCopy/workflow/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | worker: source Set.sh
2 |
--------------------------------------------------------------------------------
/Aptfile:
--------------------------------------------------------------------------------
1 | wget
2 | wget2
3 | p7zip-full
4 | unzip
5 |
--------------------------------------------------------------------------------
/iCopy/utils/__version__.py:
--------------------------------------------------------------------------------
1 | ### local version
2 | __version__ = "v0.2.2-Post.2"
--------------------------------------------------------------------------------
/iCopy/docs/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | + new keyboard function
4 | + more web functions
5 |
--------------------------------------------------------------------------------
/iCopy/docs/develop_update.md:
--------------------------------------------------------------------------------
1 | # DEVELOP UPDATE CHANGELOG
2 |
3 | More web functions is under construction.
4 |
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/favicon.ico
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/img/404.a57b6f31.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/img/404.a57b6f31.png
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/css/chunk-718881df.f9066ea7.css:
--------------------------------------------------------------------------------
1 | .dashboard-container[data-v-f7ec0d96]{margin:30px}.dashboard-text[data-v-f7ec0d96]{font-size:30px;line-height:46px}
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/img/404_cloud.0f4bc32b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/img/404_cloud.0f4bc32b.png
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.781e85bb.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.781e85bb.ttf
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.89bd2e38.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.89bd2e38.eot
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/element-icons.535877f5.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/element-icons.535877f5.woff
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/element-icons.732389de.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/element-icons.732389de.ttf
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.085b1dd8.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.085b1dd8.ttf
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.0fabb660.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.0fabb660.eot
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.cac68c83.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.cac68c83.woff2
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.dc0bd022.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-brands-400.dc0bd022.woff
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.05b53beb.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.05b53beb.woff
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.1a78af41.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.1a78af41.ttf
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.ad3a7c0d.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.ad3a7c0d.eot
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.c500da19.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.c500da19.woff2
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.ee09ad75.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-solid-900.ee09ad75.woff
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.3a3398a6.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/coko8023/iCopy-Heroku/HEAD/iCopy/web/vue-admin-simple/dist/static/fonts/fa-regular-400.3a3398a6.woff2
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/css/chunk-130a7162.011d879c.css:
--------------------------------------------------------------------------------
1 | .demo-table-expand{font-size:0}.demo-table-expand label{width:150px;color:#99a9bf}.demo-table-expand .el-form-item{margin-right:0;margin-bottom:0;width:50%}
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/css/chunk-2fd9fc79.011d879c.css:
--------------------------------------------------------------------------------
1 | .demo-table-expand{font-size:0}.demo-table-expand label{width:150px;color:#99a9bf}.demo-table-expand .el-form-item{margin-right:0;margin-bottom:0;width:50%}
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.24.0
2 | urllib3==1.25.9
3 | google_api_python_client==1.10.0
4 | protobuf==3.12.2
5 | pymongo[srv]==3.10.1
6 | toml==0.10.1
7 | python-telegram-bot==12.8
8 | sanic==20.6.3
9 | Sanic_Cors==0.10.0.post3
--------------------------------------------------------------------------------
/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 | SET_WEB
21 |
22 | ) = range(14)
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/js/chunk-2d21843e.48a6c318.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d21843e"],{c9f8:function(t,e,a){"use strict";a.r(e);var l=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("el-table",{staticStyle:{width:"100%"},attrs:{data:t.tableData,height:"100%"}},[a("el-table-column",{attrs:{prop:"name",label:"GDrive NAME",width:"300"}}),a("el-table-column",{attrs:{prop:"id",label:"GDrive ID",width:"300"}})],1)},n=[],o={data:function(){return{tableData:[]}},mounted:function(){var t=this;this.$request({url:"/drivelist",method:"get"}).then((function(e){console.log(e);var a=[];for(var l in e.data)a.push({id:l,name:e.data[l]});console.log(a),t.tableData=a})).catch((function(t){console.log(t)}))}},i=o,r=a("2877"),c=Object(r["a"])(i,l,n,!1,null,null,null);e["default"]=c.exports}}]);
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/js/chunk-718881df.f7f1bdd7.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-718881df"],{3737:function(t,e,a){},9406:function(t,e,a){"use strict";a.r(e);var c=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},n=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"dashboard-container"},[a("img",{attrs:{alt:"iCopy logo",src:"https://f002.backblazeb2.com/file/jsuforum-upload/optimized/1X/cff2835c1652bb57a18aac42a3eee34b51cd9b89_2_1380x386.gif",width:"33.3%"}}),a("div",{staticClass:"dashboard-text"},[t._v("Welcome iCopy WEB DASHBOARD")])])}],s=a("5530"),i=a("2f62"),o={name:"Dashboard",computed:Object(s["a"])({},Object(i["b"])(["name"]))},r=o,f=(a("f667"),a("2877")),u=Object(f["a"])(r,c,n,!1,null,"f7ec0d96",null);e["default"]=u.exports},f667:function(t,e,a){"use strict";var c=a("3737"),n=a.n(c);n.a}}]);
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Set.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | cd ./iCopy/config
3 | echo "
4 | [tg]
5 |
6 | token = \"$BOT_TOKEN\"
7 |
8 | usr_id = \"$USER_ID\"
9 |
10 | [database]
11 |
12 | db_connect_method = \"$DB_CONNECT_METHOD\"
13 |
14 | db_addr = \"$DB_ADDRESS\"
15 |
16 | db_port = $DB_PORT
17 |
18 | db_name = \"$DB_NAME\"
19 |
20 | db_user = \"$DB_USERNAME\"
21 |
22 | db_passwd = \"$DB_PASS\"
23 |
24 | [general]
25 |
26 | language = \"$LANGUAGE\"
27 |
28 | cloner = \"$CLONER\"
29 |
30 | option = \"$OPTION\"
31 |
32 | remote = \"$RCLONE_RMT\"
33 |
34 | parallel_c = \"$PARALLEL_CHECKERS\"
35 |
36 | parallel_t = \"$PARALLEL_TRANSFERS\"
37 |
38 | min_sleep = \"$MIN_SLEEP\"
39 |
40 | sa_path = \"$SA_PATH\"
41 |
42 | run_args = $RUN_ARGS
43 |
44 | [web]
45 |
46 | dashboard = 0
47 |
48 | port = 8000
49 |
50 | " >> conf.toml
51 |
52 | echo "[$RCLONE_RMT]
53 | type = drive
54 | scope = drive
55 | service_account_file = /app/iCopy/accounts/$SA_INIT_FILE
56 | service_account_file_path = $SA_PATH
57 | " >> rclone.conf
58 | cd ..
59 | ACCOUNTS_FILE="accounts.zip"
60 | wget --no-check-certificate -q $SA_ZIP_URL -O $ACCOUNTS_FILE
61 | unzip -qq $ACCOUNTS_FILE -d /app/iCopy/
62 | rm -rf $ACCOUNTS_FILE
63 | chmod 777 iCopy.py
64 | python3 iCopy.py
65 |
--------------------------------------------------------------------------------
/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/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/config/conf.toml.example:
--------------------------------------------------------------------------------
1 |
2 | [tg]
3 |
4 | token = "Bot API Token HERE" # get from tg::botfather
5 |
6 | usr_id = "user id HERE" # get from tg::@get_id_bot
7 |
8 | [database]
9 |
10 | db_connect_method = "mongodb+srv" # like "mongodb", "mongodb+srv",etc...
11 |
12 | db_addr = "Your database address here" # mongodb host address
13 |
14 | db_port = 27017 # mongodb connect port,default is 27017
15 |
16 | db_name = "iCopy" # set Your mongodb collection_Name,default is "iCopy"
17 |
18 | db_user = "Your mongodb user_Name HERE" # mongodb username
19 |
20 | db_passwd = "Your mongodb user_Passwd HERE" # mongodb passwd
21 |
22 | [general]
23 |
24 | language = "cn" # cn / eng / jp,default is "cn"
25 |
26 | cloner = "fclone" # Only fclone is suitable now,Future supports: rclone / gclone / autorclone
27 |
28 | option = "copy" # cloner option,e.g. "copy" , "sync"
29 |
30 | remote = "" # e.g. "fc" , "gc" , "rc", "sa" ,etc...
31 |
32 | parallel_c = "4" # customize checkers_NUM,default = "16"
33 |
34 | parallel_t = "4" # customize transfers_NUM,default = "32"
35 |
36 | min_sleep = "1ms" # customize drive-pacer-min-sleep,default = "1ms"
37 |
38 | sa_path = "/root/accounts" #Service_Accounts path,No slash at the end,default is "/root/accounts"
39 |
40 | run_args = ['--log-level=DEBUG', '--log-file=/root/icopy_cloner_debug.log'] # status flags
41 |
42 | [web]
43 |
44 | dashboard = 0 # web service switch
45 |
46 | port = 8000 # web service port, default is 8000.
47 |
--------------------------------------------------------------------------------
/iCopy/docs/COMMAND.md:
--------------------------------------------------------------------------------
1 | # BOT COMMAND
2 |
3 | + Root Command:
4 |
5 | + start - nothing just say hello
6 | + menu - main entry point
7 | quick - quick mode
8 | copy - full mode
9 | set - customize settings
10 | task - task query
11 | reset - restore task
12 | size - just size task
13 | dedupe - dedupe drives and folders
14 | purge - delete files and folder in specified fav trash bin
15 | cancel - cancel TG conversation
16 | kill - kill task
17 | ver - check iCopy version
18 | restart - restart iCopy
19 |
20 | + Child Command:
21 |
22 | + set - customize settings
23 | ┖ set - batch way
24 | ┖ set rule - rules
25 | ┖ fav|quick +/- id - single way
26 | ┖ set purge - purge favorites
27 | ┖ set web - set web account&password
28 | + size - size query
29 | ┖ size - size the shared resource
30 | ┖ size id - size specified task
31 | ┖ size fav - size specified favorites
32 | + dedupe - dedupe drives and folders
33 | ┖ dedupe - dedupe specified favorites
34 | ┖ dedupe id - dedupe specified task
35 | + task - task query
36 | ┖ task - task in processing
37 | ┖ task list - future 10 tasks
38 | ┖ task id - show the specified task
39 | + reset - restore task
40 | ┖ reset - restore current task
41 | ┖ reset id - restore the specified task
42 | + kill - kill task
43 | ┖ kill - kill current transferring task
44 | ┖ kill task - kill current transferring task
45 | ┖ kill size - kill sizing task
46 | ┖ kill purge - kill purge task
47 | ┖ kill dedupe - kill dedupe task
48 |
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/css/chunk-285369fe.6d571437.css:
--------------------------------------------------------------------------------
1 | @supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-8bbd11dc]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-8bbd11dc]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-8bbd11dc]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-8bbd11dc]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-8bbd11dc]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-8bbd11dc]{position:relative}.login-container .title-container .title[data-v-8bbd11dc]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-8bbd11dc]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
--------------------------------------------------------------------------------
/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/web/vue-admin-simple/dist/static/js/chunk-238c903c.f33d3eaf.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-238c903c"],{"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"8cdb":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._m(1),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("97ef"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"c095f994",null);s["default"]=r.exports},"97ef":function(t,s,a){"use strict";var e=a("ed94"),c=a.n(e);c.a},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},ed94:function(t,s,a){}}]);
--------------------------------------------------------------------------------
/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/web/vue-admin-simple/dist/static/js/chunk-2fd9fc79.3cba3c7b.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2fd9fc79"],{"587b":function(e,t,a){},"7a8a":function(e,t,a){"use strict";a.r(t);var l=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-table",{staticStyle:{width:"100%"},attrs:{data:e.tableData,height:"100%"}},[a("el-table-column",{attrs:{type:"expand"},scopedSlots:e._u([{key:"default",fn:function(t){return[a("el-form",{staticClass:"demo-table-expand",attrs:{"label-position":"left",inline:""}},[a("el-form-item",{attrs:{label:"G Type"}},[a("span",[e._v(e._s(t.row.G_type))])]),a("el-form-item",{attrs:{label:"G NAME"}},[a("span",[e._v(e._s(t.row.G_name))])]),a("el-form-item",{attrs:{label:"G ID"}},[a("span",[e._v(e._s(t.row.G_id))])]),a("el-form-item",{attrs:{label:"Objects"}},[a("span",[e._v(e._s(t.row.fav_object))])]),a("el-form-item",{attrs:{label:"Size"}},[a("span",[e._v(e._s(t.row.show_size))])]),a("el-progress",{attrs:{type:"circle",percentage:t.row.percent,color:e.colors}})],1)]}}])}),a("el-table-column",{attrs:{fixed:"",label:"G Name",prop:"G_name",width:"300"}}),a("el-table-column",{attrs:{label:"G ID",prop:"G_id"}}),a("el-table-column",{attrs:{label:"Objects",prop:"fav_object"}}),a("el-table-column",{attrs:{label:"Size",prop:"show_size"}}),a("el-table-column",{attrs:{label:"Percentage",prop:"show_percent"}})],1)},o=[],r={data:function(){return{tableData:[],colors:[{color:"#58D68D",percentage:20},{color:"#48C9B0",percentage:40},{color:"#5DADE2",percentage:60},{color:"#F4D03F",percentage:80},{color:"#EB984E",percentage:90},{color:"#E74C3C",percentage:100}]}},mounted:function(){var e=this;this.$request({url:"/favlist",method:"get"}).then((function(t){e.tableData=t.data,console.log(t.data)})).catch((function(e){console.log(e)}))}},n=r,s=(a("fd5b"),a("2877")),c=Object(s["a"])(n,l,o,!1,null,null,null);t["default"]=c.exports},fd5b:function(e,t,a){"use strict";var l=a("587b"),o=a.n(l);o.a}}]);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iCopy Version 0.2.2
2 |
3 | [
](https://bbs.jsu.net/c/official-project/icopy/6)
4 |
5 | [](https://bbs.jsu.net/c/official-project/icopy/6)
6 | [](https://bbs.jsu.net/c/official-project/icopy/6)
7 | [](https://bbs.jsu.net/c/official-project/icopy/6)
8 | [](https://bbs.jsu.net/c/official-project/icopy/6)
9 | [](https://github.com/mongodb/mongo)
10 | [](https://github.com/Nenokkadine/FClone-Bot)
11 |
12 | ## Running it Google Colab
13 | [](https://colab.research.google.com/github/Godcic/iCopy-Heroku/blob/master/iCopy.ipynb)
14 |
15 | ## Deployment to Heroku
16 | iCopy Version - v0.2.2 (No Web DashBoard Only Bot)
17 |
18 | [](https://dashboard.heroku.com/new?template=https://github.com/Godcic/iCopy-Heroku/tree/master)
19 |
20 | iCopy Version - v0.2.2 (Only Web DashBoard)(NOT SUPPORT)
21 |
22 | [](https://dashboard.heroku.com/new?template=https://github.com/Godcic/iCopy-Heroku/tree/web)
23 |
24 | Deploy Both If U wanna Use Web Dashboard and use the same config vars for Web one as the Bot one.
25 |
26 | ## Credits
27 |
28 | iCopy - [fxxkrlab](https://github.com/fxxkrlab/iCopy)
29 | Fclone - [Mawaya](https://github.com/mawaya/rclone)
30 | FClone-Bot - [Nenokkadine](https://github.com/Nenokkadine/FClone-Bot)
31 |
--------------------------------------------------------------------------------
/iCopy/web/cook_resp.py:
--------------------------------------------------------------------------------
1 | from utils import load
2 | import pymongo
3 |
4 | cfg = load.cfg
5 |
6 | # ### Mongodb
7 | myclient = pymongo.MongoClient(
8 | f"{cfg['database']['db_connect_method']}://{load.user}:{load.passwd}@{cfg['database']['db_addr']}",
9 | port=cfg["database"]["db_port"],
10 | connect=False,
11 | )
12 | mydb = myclient[cfg["database"]["db_name"]]
13 |
14 | fav_col = mydb["fav_col"]
15 | task_list = mydb["task_list"]
16 | db_counters = mydb["counters"]
17 |
18 | def get_drive_list():
19 | drivelist = {}
20 | all_drive = load.all_drive
21 |
22 | drivelist['data'] = all_drive
23 | drivelist['code'] = 20000
24 | drivelist['message'] = ""
25 |
26 | return drivelist
27 |
28 | def cook_fav_info():
29 | favlist = {}
30 | fav_info_array = []
31 | fav_info = fav_col.find({"fav_type":"fav"},{"_id": 0})
32 | for each in fav_info:
33 | if 'fav_size' and 'fav_object' in each:
34 | each['show_size'] = str(each['fav_size']) + " " +each['fav_size_tail']
35 | each['percent'] = float(each['fav_object'] / 4000 )
36 | each['show_percent'] = str(each['percent']) + "%"
37 | else:
38 | each['fav_size'] = "UNKNOW"
39 | each['fav_object'] = "UNKNOW"
40 | each['show_size'] = "UNKNOW"
41 | each['show_percent'] = "UNKNOW"
42 | fav_info_array.append(each)
43 |
44 | favlist['data'] = fav_info_array
45 | favlist['code'] = 20000
46 | favlist['message'] = ""
47 |
48 | return favlist
49 |
50 | def cook_task_info():
51 | tasklist = {}
52 | task_info_array = []
53 | task_info = task_list.find({"status":1,"error":0})
54 | for each in task_info:
55 | each['show_status'] = "Completed"
56 | if "task_total_prog_size_tail" in each:
57 | each['show_size'] = str(each['task_total_prog_size']) + " " + each['task_total_prog_size_tail']
58 | else:
59 | each['show_size'] = "UNKNOW"
60 | task_info_array.append(each)
61 |
62 | tasklist['code'] = 20000
63 | tasklist['data'] = task_info_array
64 | tasklist['message'] = ""
65 |
66 | return tasklist
67 |
--------------------------------------------------------------------------------
/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/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 | login_col = mydb["login_col"]
40 | task_details = mydb["task_details"]
41 |
42 | # ### check is dashboard admin
43 | if cfg['web']['dashboard']:
44 | vaild_usr_doc = login_col.find_one({"_id": "login_info"})
45 | if vaild_usr_doc is None:
46 | login_col.insert_one({"_id": "login_info","user_id":1,"username":"admin","password":"admin","user_role":"admin"})
47 |
48 | ### drive().list
49 | all_drive = gdrive.GoogleDrive().drive_list()
50 |
51 | ### ns
52 | manager = Manager()
53 | ns = manager.Namespace()
54 |
55 | ### Restore Unexpected Interrupted Task status 2 --> 0
56 | task_list.update_one(
57 | {"status": 2}, {"$set": {"status": 0,}},
58 | )
59 |
60 | ### regex entry pattern
61 | 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.\-_]+)&?)"
62 |
63 | ### define bot
64 | request = TGRequest(con_pool_size=8)
65 | bot = Bot(token=f"{cfg['tg']['token']}", request=request)
--------------------------------------------------------------------------------
/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/web/vue-admin-simple/dist/static/js/chunk-285369fe.c6caa2dc.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-285369fe"],{2017:function(e,t,s){"use strict";var n=s("cafe"),o=s.n(n);o.a},7538:function(e,t,s){},"9ed6":function(e,t,s){"use strict";s.r(t);var n=function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"login-container"},[s("el-form",{ref:"loginForm",staticClass:"login-form",attrs:{model:e.loginForm,rules:e.loginRules,"auto-complete":"on","label-position":"left"}},[s("div",{staticClass:"title-container"},[s("h3",{staticClass:"title"},[e._v("iCopy DASHBOARD")])]),s("el-form-item",{attrs:{prop:"username"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"user"}})],1),s("el-input",{ref:"username",attrs:{placeholder:"Username",name:"username",type:"text",tabindex:"1","auto-complete":"on"},model:{value:e.loginForm.username,callback:function(t){e.$set(e.loginForm,"username",t)},expression:"loginForm.username"}})],1),s("el-form-item",{attrs:{prop:"password"}},[s("span",{staticClass:"svg-container"},[s("svg-icon",{attrs:{"icon-class":"password"}})],1),s("el-input",{key:e.passwordType,ref:"password",attrs:{type:e.passwordType,placeholder:"Password",name:"password",tabindex:"2","auto-complete":"on"},nativeOn:{keyup:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.handleLogin(t)}},model:{value:e.loginForm.password,callback:function(t){e.$set(e.loginForm,"password",t)},expression:"loginForm.password"}}),s("span",{staticClass:"show-pwd",on:{click:e.showPwd}},[s("svg-icon",{attrs:{"icon-class":"password"===e.passwordType?"eye":"eye-open"}})],1)],1),s("el-button",{staticStyle:{width:"100%","margin-bottom":"30px"},attrs:{loading:e.loading,type:"primary"},nativeOn:{click:function(t){return t.preventDefault(),e.handleLogin(t)}}},[e._v("Login")])],1)],1)},o=[],r={name:"Login",data:function(){var e=function(e,t,s){s()},t=function(e,t,s){s()};return{loginForm:{username:"",password:""},loginRules:{username:[{required:!0,trigger:"blur",validator:e}],password:[{required:!0,trigger:"blur",validator:t}]},loading:!1,passwordType:"password",redirect:void 0}},watch:{$route:{handler:function(e){this.redirect=e.query&&e.query.redirect},immediate:!0}},methods:{showPwd:function(){var e=this;"password"===this.passwordType?this.passwordType="":this.passwordType="password",this.$nextTick((function(){e.$refs.password.focus()}))},handleLogin:function(){var e=this;this.$refs.loginForm.validate((function(t){if(!t)return console.log("error submit!!"),!1;e.loading=!0,e.$store.dispatch("user/login",e.loginForm).then((function(){e.$router.push({path:e.redirect||"/"}),e.loading=!1})).catch((function(){e.loading=!1}))}))}}},a=r,i=(s("2017"),s("c785"),s("2877")),l=Object(i["a"])(a,n,o,!1,null,"8bbd11dc",null);t["default"]=l.exports},c785:function(e,t,s){"use strict";var n=s("7538"),o=s.n(n);o.a},cafe:function(e,t,s){}}]);
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TG-iCopy",
3 | "description": "Fclone Telegram Bot",
4 | "repository": "https://github.com/Nenokkadine/Fclone-Bot",
5 | "keywords": ["Fclone","Icopy"],
6 | "env": {
7 | "BOT_TOKEN": {
8 | "description": "Get from Telegram Botfather"
9 | },
10 | "USER_ID": {
11 | "description": "get from telegram get_id_bot"
12 | },
13 | "DB_ADDRESS": {
14 | "description": "MongoDB Host Address"
15 | },
16 | "DB_USERNAME": {
17 | "description": "MongoDB Username"
18 | },
19 | "DB_PASS": {
20 | "description": "MongoDB Password"
21 | },
22 | "SA_ZIP_URL": {
23 | "description": "Service Accounts ZIP URL.It Should be Zipped Such that it should have a folder named accounts with SA in it "
24 | },
25 | "SA_INIT_FILE": {
26 | "description": "Give a service account File Name. ex 1.json "
27 | },
28 | "LANGUAGE": {
29 | "description": "cn by Default Support eng and jp",
30 | "value": "cn"
31 | },
32 | "PARALLEL_CHECKERS": {
33 | "description": "Dont Give More than 300 Heroku Free Dyno may Crash",
34 | "value": "250"
35 | },
36 | "PARALLEL_TRANSFERS": {
37 | "description": "Dont Give More than 300 Heroku Free Dyno may Crash",
38 | "value": "250"
39 | },
40 | "DB_CONNECT_METHOD": {
41 | "description": "DB Connect Method",
42 | "value": "mongodb+srv"
43 | },
44 | "DB_PORT": {
45 | "description": "MongoDB Port, Default is 27017",
46 | "value": "27017"
47 | },
48 | "DB_NAME": {
49 | "description": "U Can give any name, Default is iCopy",
50 | "value": "iCopy"
51 | },
52 | "CLONER": {
53 | "description": "Dont Change this",
54 | "value": "fclone"
55 | },
56 | "OPTION": {
57 | "description": "Copy or Sync Default is Copy",
58 | "value": "copy"
59 | },
60 | "RCLONE_RMT": {
61 | "description": "Give a Rclone Remote Name Default is also fine",
62 | "value": "icopy"
63 | },
64 | "MIN_SLEEP": {
65 | "description": "customize drive-pacer-min-sleep",
66 | "value": "1ms"
67 | },
68 | "RCLONE_CONFIG": {
69 | "description": "Dont Change",
70 | "value": "/app/iCopy/config/rclone.conf"
71 | },
72 | "RUN_ARGS": {
73 | "description": "Only Change this if U Know",
74 | "value": "['--log-level=DEBUG', '--log-file=/app/icopy_cloner_debug.log']"
75 | },
76 | "SA_PATH": {
77 | "description": "Dont Change",
78 | "value": "/app/iCopy/accounts"
79 | }
80 | },
81 | "buildpacks": [
82 | {
83 | "url": "heroku/python"
84 | },
85 | {
86 | "url": "https://github.com/heroku/heroku-buildpack-apt.git"
87 | },
88 | {
89 | "url": "https://github.com/opendoor-labs/heroku-buildpack-p7zip.git"
90 | },
91 | {
92 | "url" : "https://github.com/Godcic/Fpack.git"
93 | }
94 | ],
95 | "formation": {
96 | "worker": {
97 | "quantity": 1,
98 | "size": "free"
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/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/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/web/vue-admin-simple/dist/static/css/chunk-libs.3dfb7769.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figcaption,figure,main{display:block}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:inherit;font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details,menu{display:block}summary{display:list-item}canvas{display:inline-block}[hidden],template{display:none}#nprogress{pointer-events:none}#nprogress .bar{background:#29d;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;-webkit-box-shadow:0 0 10px #29d,0 0 5px #29d;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translateY(-4px);transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;-webkit-box-sizing:border-box;box-sizing:border-box;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/js/chunk-130a7162.684ec14a.js:
--------------------------------------------------------------------------------
1 | (window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-130a7162"],{"29af":function(a,t,s){"use strict";s.r(t);var e=function(){var a=this,t=a.$createElement,s=a._self._c||t;return s("el-table",{staticStyle:{width:"100%"},attrs:{data:a.tableData,height:"100%","default-sort":{prop:"_id",order:"descending"}}},[s("el-table-column",{attrs:{type:"expand"},scopedSlots:a._u([{key:"default",fn:function(t){return[s("el-form",{staticClass:"demo-table-expand",attrs:{"label-position":"left",inline:""}},[s("el-form-item",{attrs:{label:"TASK ID"}},[s("i",{staticClass:"fas fa-flag"}),s("span",[a._v(" "+a._s(t.row._id))])]),s("el-form-item",{attrs:{label:"Status"}},[s("i",{staticClass:"fas fa-check-double"}),s("span",[a._v(" "+a._s(t.row.show_status))])]),s("el-form-item",{attrs:{label:"Souce"}},[s("i",{staticClass:"fas fa-id-card"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.src_name))]),s("i",{staticClass:"fas fa-chevron-right"}),s("br"),s("i",{staticClass:"fas fa-barcode"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.src_id))]),s("i",{staticClass:"fas fa-chevron-right"})]),s("el-form-item",{attrs:{label:"Destination"}},[s("i",{staticClass:"fas fa-id-card"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.dst_name))]),s("i",{staticClass:"fas fa-chevron-right"}),s("br"),s("i",{staticClass:"fas fa-barcode"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.dst_id))]),s("i",{staticClass:"fas fa-chevron-right"})]),s("el-form-item",{attrs:{label:"Task Time"}},[s("span",[s("i",{staticClass:"far fa-calendar-alt"}),a._v(" : "+a._s(t.row.create_time)+" (created)")]),s("br"),s("span",[s("i",{staticClass:"far fa-calendar-plus"}),a._v(" : "+a._s(t.row.start_time)+" (started)")]),s("br"),s("span",[s("i",{staticClass:"far fa-calendar-check"}),a._v(" : "+a._s(t.row.finished_time)+" (finished)")])]),s("el-form-item",{attrs:{label:"Endpoint INFO"}},[s("i",{staticClass:"fas fa-id-card"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.src_name))]),s("i",{staticClass:"fas fa-chevron-right"}),s("br"),s("i",{staticClass:"fas fa-barcode"}),a._v(" : "),s("i",{staticClass:"fas fa-chevron-left"}),s("span",[a._v(a._s(t.row.dst_endpoint_id))]),s("i",{staticClass:"fas fa-chevron-right"}),s("br"),s("i",{staticClass:"fas fa-external-link-alt"}),s("span",[a._v(" : "),s("el-link",{attrs:{href:t.row.dst_endpoint_link,target:"_blank"}},[a._v("GDrive Link")])],1),s("br"),s("i",{staticClass:"fas fa-file-import"}),s("span",[a._v(" : "+a._s(t.row.task_total_prog_num)+" (total objects)")]),s("br"),s("i",{staticClass:"fas fa-archive"}),s("span",[a._v(" : "+a._s(t.row.show_size)+" (total size)")])])],1)]}}])}),s("el-table-column",{attrs:{fixed:"",label:"ID",prop:"_id",sortable:"",width:"100"}}),s("el-table-column",{attrs:{label:"Name",prop:"src_name",formatter:a.formatter,width:"900"}}),s("el-table-column",{attrs:{label:"Objects",prop:"task_total_prog_num",width:"200"}}),s("el-table-column",{attrs:{label:"Size",prop:"show_size",width:"200"}})],1)},i=[],r={data:function(){return{tableData:[]}},mounted:function(){var a=this;this.$request({url:"/tasklist",method:"get"}).then((function(t){a.tableData=t.data})).catch((function(a){console.log(a)}))},methods:{formatter:function(a){return a.src_name}}},l=r,n=(s("f1ec"),s("2877")),o=Object(n["a"])(l,e,i,!1,null,null,null);t["default"]=o.exports},b044:function(a,t,s){},f1ec:function(a,t,s){"use strict";var e=s("b044"),i=s.n(e);i.a}}]);
--------------------------------------------------------------------------------
/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 = "rmdirs"
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 | flags += _cfg["general"]["run_args"]
57 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}"
58 |
59 | command1 = [cloner, option1, src_block, rmdirs, checkers, transfers, sa_sleep]
60 | command1 += flags
61 |
62 | command2 = [cloner, option2, src_block, checkers, transfers, sa_sleep]
63 | command2 += flags
64 |
65 | purge_process(ns, command1, command2, purge_chat_id, purge_message_id, fav_id, fav_name)
66 |
67 | ns.purge = 0
68 |
69 | def purge_process(ns, command1, command2, purge_chat_id, purge_message_id, fav_id, fav_name):
70 | for output in purge_run(command=command1):
71 | if ns.purge == 1:
72 | purging_process.kill()
73 |
74 | for output in purge_run(command=command2):
75 | if ns.purge == 1:
76 | purging_process.kill()
77 |
78 | if ns.purge == 0:
79 | last_purge_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
80 |
81 | fav_col.update_one(
82 | {"G_id": fav_id}, {"$set": {"last_purge_time": last_purge_time,}},
83 | )
84 |
85 | purged_msg = (
86 | "༺ ✪iCopy✪ ༻\n\n"
87 | + fav_name
88 | + "\n["
89 | + fav_id
90 | + "]\n\n"
91 | + _text[_lang]["purging_done"]
92 | )
93 |
94 | bot.edit_message_text(
95 | chat_id=purge_chat_id, message_id=purge_message_id, text=purged_msg,
96 | )
97 |
98 | if ns.purge == 1:
99 | bot.edit_message_text(
100 | chat_id=purge_chat_id,
101 | message_id=purge_message_id,
102 | text=_text[_lang]["is_killed_by_user"],
103 | )
104 |
--------------------------------------------------------------------------------
/iCopy/web/dash.py:
--------------------------------------------------------------------------------
1 | import json, logging, hashlib
2 | from sanic import Sanic, response
3 | from sanic_cors import CORS, cross_origin
4 | from utils import load
5 | from web import cook_resp as _resp
6 |
7 | logging.basicConfig(
8 | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.DEBUG
9 | )
10 |
11 | app = Sanic(__name__)
12 | CORS(app)
13 | _cfg = load.cfg
14 |
15 | async def get_vaild_info():
16 | mydb = _resp.mydb
17 | login_col = mydb["login_col"]
18 | vaild_usr_doc = login_col.find_one({"_id": "login_info"})
19 | vaild_username = vaild_usr_doc["username"]
20 | vaild_password = vaild_usr_doc["password"]
21 | vaild_info = {"vaild_username":vaild_username,"vaild_password":vaild_password}
22 | return vaild_info
23 |
24 | async def auth(token):
25 | result = {}
26 | vaild_info = await get_vaild_info()
27 | vaild_username = vaild_info['vaild_username']
28 | vaild_password = vaild_info['vaild_password']
29 | vaild_token = hashlib.md5((str(vaild_username) + str(vaild_password)).encode('utf-8')).hexdigest()
30 | if token != vaild_token:
31 | result['code'] = 50008
32 | result['message'] = "login faild"
33 |
34 | return response.json(result)
35 |
36 | else:
37 | return True
38 |
39 | # ### login&auth route.v1
40 | @app.route("/v1/login", methods=["POST",])
41 | async def login(request):
42 | result = {}
43 | vaild_info = await get_vaild_info()
44 | current_username = request.json['username']
45 | current_password = request.json['password']
46 | vaild_username = vaild_info['vaild_username']
47 | vaild_password = vaild_info['vaild_password']
48 | if vaild_username == current_username and vaild_password == current_password:
49 | token = hashlib.md5((str(vaild_username) + str(vaild_password)).encode('utf-8')).hexdigest()
50 | result['code'] = 20000
51 | result['data'] = {"token":token}
52 | result['message'] = "login success"
53 |
54 | else:
55 | result['code'] = 20001
56 | result['message'] = "login faild"
57 |
58 | return response.json(result)
59 |
60 | @app.route("/v1/userinfo")
61 | async def userinfo(request):
62 | result = {}
63 | vaild_info = await get_vaild_info()
64 | vaild_username = vaild_info['vaild_username']
65 | await auth(request.args['token'])
66 | result['code'] = 20000
67 | result['data'] = {"name":vaild_username,"avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"}
68 | result['message'] = ""
69 |
70 | return response.json(result)
71 |
72 | # ### api route.v1
73 |
74 | @app.route("/v1/drivelist")
75 | async def drivelist(request):
76 | x_auth = await auth(request.headers['X-Token'])
77 | if x_auth != True:
78 | return x_auth
79 |
80 | drivelist = _resp.get_drive_list()
81 |
82 | return response.json(drivelist, ensure_ascii=False)
83 |
84 |
85 | @app.route("/v1/favlist")
86 | async def sanic_fav_info(request):
87 | x_auth = await auth(request.headers['X-Token'])
88 | if x_auth != True:
89 | return x_auth
90 |
91 | fav_info = _resp.cook_fav_info()
92 |
93 | return response.json(fav_info, ensure_ascii=False)
94 |
95 | @app.route("/v1/tasklist")
96 | async def sanic_task_info(request):
97 | x_auth = await auth(request.headers['X-Token'])
98 | if x_auth != True:
99 | return x_auth
100 |
101 | task_info = _resp.cook_task_info()
102 |
103 | return response.json(task_info, ensure_ascii=False)
104 |
105 | @app.route("/v1/currenttask")
106 | async def taskdetail(request):
107 | x_auth = await auth(request.headers['X-Token'])
108 | if x_auth != True:
109 | return x_auth
110 |
111 | task_sum = load.task_list.find()
112 | y = len(list(task_sum))
113 | x = "Current Task SUM"
114 |
115 | return response.json({x: y})
116 |
117 | app.static("/", "./web/vue-admin-simple/dist/")
118 | app.static("/", "./web/vue-admin-simple/dist/index.html", content_type="text/html; charset=utf-8")
119 | web_port = _cfg['web']['port']
120 | def dashboard():
121 | app.run(host="0.0.0.0", port=web_port)
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/static/css/chunk-238c903c.3c7f5ad9.css:
--------------------------------------------------------------------------------
1 | .wscn-http404-container[data-v-c095f994]{-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);position:absolute;top:40%;left:50%}.wscn-http404[data-v-c095f994]{position:relative;width:1200px;padding:0 50px;overflow:hidden}.wscn-http404 .pic-404[data-v-c095f994]{position:relative;float:left;width:600px;overflow:hidden}.wscn-http404 .pic-404__parent[data-v-c095f994]{width:100%}.wscn-http404 .pic-404__child[data-v-c095f994]{position:absolute}.wscn-http404 .pic-404__child.left[data-v-c095f994]{width:80px;top:17px;left:220px;opacity:0;-webkit-animation-name:cloudLeft-data-v-c095f994;animation-name:cloudLeft-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}.wscn-http404 .pic-404__child.mid[data-v-c095f994]{width:46px;top:10px;left:420px;opacity:0;-webkit-animation-name:cloudMid-data-v-c095f994;animation-name:cloudMid-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1.2s;animation-delay:1.2s}.wscn-http404 .pic-404__child.right[data-v-c095f994]{width:62px;top:100px;left:500px;opacity:0;-webkit-animation-name:cloudRight-data-v-c095f994;animation-name:cloudRight-data-v-c095f994;-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-delay:1s;animation-delay:1s}@-webkit-keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@keyframes cloudLeft-data-v-c095f994{0%{top:17px;left:220px;opacity:0}20%{top:33px;left:188px;opacity:1}80%{top:81px;left:92px;opacity:1}to{top:97px;left:60px;opacity:0}}@-webkit-keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@keyframes cloudMid-data-v-c095f994{0%{top:10px;left:420px;opacity:0}20%{top:40px;left:360px;opacity:1}70%{top:130px;left:180px;opacity:1}to{top:160px;left:120px;opacity:0}}@-webkit-keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}@keyframes cloudRight-data-v-c095f994{0%{top:100px;left:500px;opacity:0}20%{top:120px;left:460px;opacity:1}80%{top:180px;left:340px;opacity:1}to{top:200px;left:300px;opacity:0}}.wscn-http404 .bullshit[data-v-c095f994]{position:relative;float:left;width:300px;padding:30px 0;overflow:hidden}.wscn-http404 .bullshit__oops[data-v-c095f994]{font-size:32px;line-height:40px;color:#1482f0;margin-bottom:20px;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__headline[data-v-c095f994],.wscn-http404 .bullshit__oops[data-v-c095f994]{font-weight:700;opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__headline[data-v-c095f994]{font-size:20px;line-height:24px;color:#222;margin-bottom:10px;-webkit-animation-delay:.1s;animation-delay:.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994]{font-size:13px;line-height:21px;color:grey;margin-bottom:30px;-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.wscn-http404 .bullshit__info[data-v-c095f994],.wscn-http404 .bullshit__return-home[data-v-c095f994]{opacity:0;-webkit-animation-name:slideUp-data-v-c095f994;animation-name:slideUp-data-v-c095f994;-webkit-animation-duration:.5s;animation-duration:.5s}.wscn-http404 .bullshit__return-home[data-v-c095f994]{display:block;float:left;width:110px;height:36px;background:#1482f0;border-radius:100px;text-align:center;color:#fff;font-size:14px;line-height:36px;cursor:pointer;-webkit-animation-delay:.3s;animation-delay:.3s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}@-webkit-keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}@keyframes slideUp-data-v-c095f994{0%{-webkit-transform:translateY(60px);transform:translateY(60px);opacity:0}to{-webkit-transform:translateY(0);transform:translateY(0);opacity:1}}
--------------------------------------------------------------------------------
/iCopy/web/vue-admin-simple/dist/index.html:
--------------------------------------------------------------------------------
1 |
iCopy WEB DASHBOARD
--------------------------------------------------------------------------------
/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 | flags += _cfg["general"]["run_args"]
67 | sa_sleep_suffix = "--drive-pacer-min-sleep"
68 | sa_sleep = _cfg["general"]["min_sleep"]
69 |
70 | command = [
71 | cloner,
72 | option,
73 | mode_suffix,
74 | mode,
75 | src_block,
76 | checkers,
77 | transfers,
78 | sa_sleep_suffix,
79 | sa_sleep,
80 | ]
81 | command += flags
82 |
83 | dedupe_process(ns, command, dedu_mode, dedu_chat_id, dedu_message_id, dedu_task_id, dedu_link, dedu_id, dedu_name)
84 |
85 | ns.dedupe = 0
86 |
87 | def dedupe_process(ns, command, dedu_mode, dedu_chat_id, dedu_message_id, dedu_task_id, dedu_link, dedu_id, dedu_name):
88 | for output in dedupe_run(command):
89 | if ns.dedupe == 1:
90 | deduping_process.kill()
91 |
92 | if ns.dedupe == 0:
93 | last_dedupe_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
94 |
95 | if dedu_task_id == 0:
96 | fav_col.update_one(
97 | {"G_id": dedu_id}, {"$set": {"last_dedupe_time": last_dedupe_time,}},
98 | )
99 |
100 | deduped_msg = (
101 | " ༺ ✪iCopy✪ ༻ | "
102 | + "🏳️"
103 | + " FAVORITES"
104 | + "\n\n"
105 | + '{}'.format(dedu_link, dedu_name)
106 | + "\n["
107 | + dedu_id
108 | + "]\n\n"
109 | + _text[_lang]["deduping_done"]
110 | )
111 |
112 | else:
113 | task_list.update_one(
114 | {"_id": int(dedu_task_id)}, {"$set": {"last_dedupe_time": last_dedupe_time,}},
115 | )
116 |
117 | deduped_msg = (
118 | " ༺ ✪iCopy✪ ༻ | "
119 | + "🏳️" + _text[_lang]["current_task_id"]
120 | + str(dedu_task_id)
121 | + "\n\n"
122 | + '{}'.format(dedu_link, dedu_name)
123 | + "\n["
124 | + dedu_id
125 | + "]\n\n"
126 | + _text[_lang]["deduping_done"]
127 | )
128 |
129 | bot.edit_message_text(
130 | chat_id=dedu_chat_id,
131 | message_id=dedu_message_id,
132 | text=deduped_msg,
133 | parse_mode=ParseMode.HTML,
134 | disable_web_page_preview=True,
135 | )
136 | elif ns.dedupe == 1:
137 | bot.edit_message_text(
138 | chat_id=dedu_chat_id,
139 | message_id=dedu_message_id,
140 | text=_text[_lang]["is_killed_by_user"],
141 | )
142 |
143 |
--------------------------------------------------------------------------------
/iCopy.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "name": "iCopy.ipynb",
7 | "provenance": [],
8 | "collapsed_sections": [],
9 | "authorship_tag": "ABX9TyMIOr05NhpujkN2XObP9vC3",
10 | "include_colab_link": true
11 | },
12 | "kernelspec": {
13 | "name": "python3",
14 | "display_name": "Python 3"
15 | },
16 | "accelerator": "GPU"
17 | },
18 | "cells": [
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {
22 | "id": "view-in-github",
23 | "colab_type": "text"
24 | },
25 | "source": [
26 | "
"
27 | ]
28 | },
29 | {
30 | "cell_type": "code",
31 | "metadata": {
32 | "id": "NZUNVC5FMrc1",
33 | "colab_type": "code",
34 | "cellView": "form",
35 | "colab": {}
36 | },
37 | "source": [
38 | "#@markdown ← Run this cell to install all required modules
\n",
39 | "!sudo add-apt-repository ppa:deadsnakes/ppa -y\n",
40 | "!sudo apt update\n",
41 | "!sudo apt install unzip\n",
42 | "!sudo apt install python3.8\n",
43 | "!sudo apt-get install python3.8-venv\n",
44 | "!sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.8 3\n",
45 | "!curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py\n",
46 | "!python3 get-pip.py --force-reinstall\n",
47 | "!pip3 install -U pip\n",
48 | "!git clone https://github.com/Godcic/iCopy-Heroku.git\n",
49 | "%cd /content/iCopy-Heroku\n",
50 | "!pip3 install -r requirements.txt\n",
51 | "!bash <(wget -qO- https://git.io/JJYE0)\n",
52 | "from IPython.display import HTML, clear_output\n",
53 | "clear_output()\n",
54 | "print(\"Successfully Installed\")\n"
55 | ],
56 | "execution_count": null,
57 | "outputs": []
58 | },
59 | {
60 | "cell_type": "code",
61 | "metadata": {
62 | "id": "QsbKzy_7my1L",
63 | "colab_type": "code",
64 | "cellView": "form",
65 | "colab": {}
66 | },
67 | "source": [
68 | "#@markdown ←Fill all the Vars and Run the cell
\n",
69 | "BOT_TOKEN = \"\" #@param {type:\"string\"}\n",
70 | "USER_ID = \"\" #@param {type:\"string\"}\n",
71 | "DB_CONNECT_METHOD =\"mongodb+srv\"#@param {type:\"string\"}\n",
72 | "DB_ADDRESS = \"\"#@param {type:\"string\"}\n",
73 | "DB_PORT=\"27017\"#@param {type:\"string\"}\n",
74 | "DB_NAME=\"iCopy\"#@param {type:\"string\"}\n",
75 | "DB_USERNAME=\"\"#@param {type:\"string\"}\n",
76 | "DB_PASS=\"\"#@param {type:\"string\"}\n",
77 | "LANGUAGE=\"cn\"#@param {type:\"string\"}\n",
78 | "CLONER=\"fclone\"#@param {type:\"string\"}\n",
79 | "OPTION=\"copy\"#@param {type:\"string\"}\n",
80 | "RCLONE_RMT=\"iCopy\"#@param {type:\"string\"}\n",
81 | "PARALLEL_CHECKERS=\"2500\"#@param {type:\"string\"}\n",
82 | "PARALLEL_TRANSFERS=\"2500\"#@param {type:\"string\"}\n",
83 | "MIN_SLEEP=\"1ms\"#@param {type:\"string\"}\n",
84 | "SA_INIT_FILE=\"\"#@param {type:\"string\"}\n",
85 | "SA_ZIP_URL=\"\"#@param {type:\"string\"}\n",
86 | "RUN_ARGS=\"['--log-level=DEBUG', '--log-file=/content/icopy_cloner_debug.log']\"\n",
87 | "SA_PATH = \"/content/iCopy-Heroku/iCopy/accounts\"\n",
88 | "import os\n",
89 | "os.environ['RCLONE_CONFIG']=\"/content/iCopy-Heroku/iCopy/config/rclone.conf\"\n",
90 | "%cd /content/iCopy-Heroku/iCopy/config/\n",
91 | "!echo -e \"[tg]\\n\"token = \"\\\"$BOT_TOKEN\\\"\\n\"usr_id = \"\\\"$USER_ID\\\"\\n[database]\\n\"db_connect_method = \"\\\"$DB_CONNECT_METHOD\\\"\\n\"db_addr = \"\\\"$DB_ADDRESS\\\"\\n\"db_port = \"$DB_PORT\"\"\\n\"db_name = \"\\\"$DB_NAME\\\"\\n\"db_user = \"\\\"$DB_USERNAME\\\"\\n\"db_passwd = \"\\\"$DB_PASS\\\"\\n[general]\\n\"language = \"\\\"$LANGUAGE\\\" \\n\"cloner = \"\\\"$CLONER\\\"\\n\"option = \"\\\"$OPTION\\\" \\n\"remote = \"\\\"$RCLONE_RMT\\\"\\n\"parallel_c = \"\\\"$PARALLEL_CHECKERS\\\"\\n\"parallel_t = \"\\\"$PARALLEL_TRANSFERS\\\"\\n\"min_sleep = \"\\\"$MIN_SLEEP\\\"\\n\"sa_path = \"\\\"$SA_PATH\\\"\\nrun_args = $RUN_ARGS\\n[web]\\ndashboard = 0\\nport = 8000\" >> conf.toml\n",
92 | "!echo \"[$RCLONE_RMT]\\ntype = drive\\nscope = drive\\n\"service_account_file = /app/iCopy/accounts/$SA_INIT_FILE\"\\n\"service_account_file_path = $SA_PATH\"\" >> rclone.conf\n",
93 | "%cd ..\n",
94 | "!wget --no-check-certificate -q $SA_ZIP_URL -O accounts.zip\n",
95 | "!unzip -qq accounts.zip -d /content/iCopy-Heroku/iCopy/"
96 | ],
97 | "execution_count": null,
98 | "outputs": []
99 | },
100 | {
101 | "cell_type": "code",
102 | "metadata": {
103 | "id": "tJvoyAzBguoO",
104 | "colab_type": "code",
105 | "cellView": "form",
106 | "colab": {}
107 | },
108 | "source": [
109 | "#@markdown ←Run this cell to start the Bot
\n",
110 | "%cd /content/iCopy-Heroku/iCopy/\n",
111 | "!chmod 777 iCopy.py\n",
112 | "!python3 iCopy.py"
113 | ],
114 | "execution_count": null,
115 | "outputs": []
116 | }
117 | ]
118 | }
--------------------------------------------------------------------------------
/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/iCopy.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 | _stage.SET_WEB: [MessageHandler(Filters.text, _set.setWeb),],
126 | },
127 | fallbacks=[CommandHandler("cancel", _func.cancel)],
128 | )
129 |
130 | def stop_and_restart():
131 | progress.terminate()
132 | load.myclient.close()
133 | updater.stop()
134 | os.execl(sys.executable, sys.executable, *sys.argv)
135 |
136 | def restart(update, context):
137 | restart_msg = update.message.reply_text(load._text[load._lang]["is_restarting"])
138 | restart_chat_id = restart_msg.chat_id
139 | restart_msg_id = restart_msg.message_id
140 | load.db_counters.update_one(
141 | {"_id": "is_restart"},
142 | {
143 | "$set": {
144 | "status": 1,
145 | "chat_id": restart_chat_id,
146 | "message_id": restart_msg_id,
147 | }
148 | },
149 | True,
150 | )
151 | Thread(target=stop_and_restart).start()
152 |
153 | dp.add_handler(conv_handler)
154 | dp.add_handler(CommandHandler("start", _start.start))
155 | dp.add_handler(CommandHandler("reset", _box.task_reset))
156 | dp.add_handler(CommandHandler("kill", _func.taskill))
157 | dp.add_handler(CommandHandler("ver", _func._version))
158 |
159 | dp.add_handler(
160 | CommandHandler(
161 | "restart",
162 | restart,
163 | filters=Filters.user(user_id=int(load.cfg["tg"]["usr_id"])),
164 | )
165 | )
166 |
167 | dp.add_error_handler(_func.error)
168 |
169 | updater.start_polling()
170 | logger.info("Fxxkr LAB iCopy " + __version__.__version__ + " Start")
171 | updater.idle()
172 |
173 |
174 | if __name__ == "__main__":
175 | ns.x = 0
176 | progress = _mp(target=_payload.task_buffer, args=(ns,))
177 | progress.start()
178 | if load.cfg['web']['dashboard']:
179 | web = _mp(target=dash.dashboard)
180 | web.start()
181 |
182 | main()
183 |
--------------------------------------------------------------------------------
/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/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/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 | count = 0
35 |
36 | def cook_to_id(get_share_link):
37 | share_id_list = []
38 | unsupported_type = []
39 | share_id = ""
40 |
41 | share_link = get_share_link.strip().replace(" ", "").splitlines()
42 | for item in share_link:
43 | if "drive.google.com" in item:
44 | share_id = re.findall(regex1, item)
45 | if len(share_id) <= 33:
46 | share_id = "".join(share_id)
47 |
48 | share_id_list.append(share_id)
49 | else:
50 | unsupported_type.append({"type": "link", "value": item})
51 |
52 | else:
53 | if len(item) >= 11 and len(item) <= 33 and re.match(regex2, item):
54 | share_id_list.append(item)
55 | else:
56 | unsupported_type.append({"type": "id", "value": item})
57 |
58 | return share_id_list
59 |
60 |
61 | def get_name_from_id(update, taget_id, list_name):
62 | cook_list = list(list_name)
63 | if len(taget_id) >= 11 and len(taget_id) < 28:
64 | cook_list.append(
65 | {"G_type": "G_drive", "G_id": taget_id, "G_name": load.all_drive[taget_id],}
66 | )
67 | elif len(taget_id) in judge_folder_len:
68 | cook_list.append(
69 | {
70 | "G_type": "G_Folder",
71 | "G_id": taget_id,
72 | "G_name": _gd().file_get_name(file_id=taget_id),
73 | }
74 | )
75 | else:
76 | update.effective_message.reply_text(_msg.get_fav_len_invaild(_lang, taget_id))
77 |
78 | return ConversationHandler.END
79 |
80 | return cook_list
81 |
82 | def get_src_name_from_id(update, taget_id, list_name):
83 | cook_list = []
84 | cook_list = list(list_name)
85 | if len(taget_id) >= 11 and len(taget_id) < 28:
86 | target_info = _gd.drive_get(_gd(),drive_id=taget_id)
87 | cook_list.append(
88 | {"G_type": "G_drive", "G_id": taget_id, "G_name": target_info['name'],}
89 | )
90 | elif len(taget_id) in judge_folder_len:
91 | cook_list.append(
92 | {
93 | "G_type": "G_Folder",
94 | "G_id": taget_id,
95 | "G_name": _gd().file_get_name(file_id=taget_id),
96 | }
97 | )
98 | else:
99 | update.effective_message.reply_text(_msg.get_fav_len_invaild(_lang, taget_id))
100 |
101 | return ConversationHandler.END
102 |
103 | return cook_list
104 |
105 |
106 | def insert_to_db_quick(pick_quick, update):
107 | is_quick = {"_id": "fav_quick"}
108 | is_quick_cur = load.fav_col.find(is_quick)
109 | if list(is_quick_cur) == []:
110 | for item in pick_quick:
111 | item["_id"] = "fav_quick"
112 | load.fav_col.insert_one(item)
113 |
114 | update.effective_message.reply_text(
115 | _text[_lang]["insert_quick_success"], parse_mode=ParseMode.MARKDOWN_V2
116 | )
117 |
118 | return ConversationHandler.END
119 |
120 | else:
121 | status = "is_cover"
122 |
123 | return status
124 |
125 |
126 | def modify_quick_in_db(update, context):
127 | pick_quick = _set.pick_quick
128 | for item in pick_quick:
129 | load.fav_col.update({"_id": "fav_quick"}, item, upsert=True)
130 |
131 | update.effective_message.reply_text(
132 | _text[_lang]["modify_quick_success"], parse_mode=ParseMode.MARKDOWN_V2
133 | )
134 |
135 | return ConversationHandler.END
136 |
137 |
138 | def delete_in_db_quick():
139 | load.fav_col.delete_one({"_id": "fav_quick"})
140 |
141 | return
142 |
143 |
144 | def delete_in_db(delete_request):
145 | load.fav_col.delete_one(delete_request)
146 |
147 | return
148 |
149 |
150 | def get_share_link(update, context):
151 | get_share_link = update.effective_message.text
152 | tmp_src_name_list = ""
153 | tmp_task_list = []
154 | src_name_list = []
155 | src_id_list = cook_to_id(get_share_link)
156 | is_quick = {"_id": "fav_quick"}
157 | is_quick_cur = load.fav_col.find(is_quick)
158 | is_dstinfo = _copy.current_dst_info
159 |
160 | if is_dstinfo != "":
161 | dstinfo = is_dstinfo.split("id+name")
162 | dst_id = dstinfo[0]
163 | dst_name = dstinfo[1]
164 | else:
165 | for doc in is_quick_cur:
166 | dst_id = doc["G_id"]
167 | dst_name = doc["G_name"]
168 |
169 | for item in src_id_list:
170 | src_name_list += get_src_name_from_id(update, item, list_name=tmp_src_name_list)
171 | tmp_src_name_list = ""
172 |
173 | for item in src_name_list:
174 | src_id = item["G_id"]
175 | src_name = item["G_name"]
176 |
177 | tmp_task_list.append(
178 | {
179 | "mode_type": mode,
180 | "src_id": src_id,
181 | "src_name": src_name,
182 | "dst_id": dst_id,
183 | "dst_name": dst_name,
184 | "chat_id": update.message.chat_id,
185 | "raw_message_id": update.message.message_id,
186 | }
187 | )
188 |
189 | Thread(target=_box.cook_task_to_db, args=(update, context, tmp_task_list)).start()
190 | _copy.current_dst_info = ""
191 | return ConversationHandler.END
192 |
193 | def taskill(update, context):
194 | entry_cmd = update.effective_message.text
195 | if "/kill" == entry_cmd :
196 | ns.x = 1
197 |
198 | elif context.args[0] == "task":
199 | ns.x = 1
200 |
201 | elif context.args[0] == "size":
202 | ns.size = 1
203 |
204 | elif context.args[0] == "purge":
205 | ns.purge = 1
206 |
207 | elif context.arg[0] == "dedupe":
208 | ns.dedupe = 1
209 |
210 | else:
211 | update.effective_message.reply_text(_text[_lang]["global_command_error"])
212 |
213 | def getIDbypath(dst_id, src_name):
214 | global count
215 | if count < 10:
216 | try:
217 | dst_endpoint_id = _gd.get_dst_endpoint_id(_gd(), dst_id, src_name)
218 | if dst_endpoint_id:
219 | dst_endpoint_link = r"https://drive.google.com/open?id={}".format(
220 | dst_endpoint_id['id']
221 | )
222 | dst_info = {"dst_endpoint_id":dst_endpoint_id['id'],"dst_endpoint_link":dst_endpoint_link,"linkstatus":True}
223 | count = 0
224 | return dst_info
225 |
226 | except:
227 | count += 1
228 | return getIDbypath(dst_id, src_name)
229 |
230 | else:
231 | dst_info = {"dst_endpoint_id":" ","dst_endpoint_link":" ","linkstatus":False}
232 | count = 0
233 | return dst_info
234 |
235 | def check_restart(bot):
236 | check_restart = load.db_counters.find_one({"_id": "is_restart"})
237 | chat_id = check_restart["chat_id"]
238 | message_id = check_restart["message_id"]
239 | load.db_counters.update_one({"_id": "is_restart"}, {"$set": {"status": 0,}}, True)
240 | bot.edit_message_text(
241 | chat_id=chat_id, message_id=message_id, text=_text[_lang]["restart_success"]
242 | )
243 |
244 | def _version(update, context):
245 | update.message.reply_text(
246 | "Welcome to use iCopy Telegram BOT\n\n"
247 | "Current Version : " + __version__.__version__ + "\n\n"
248 | f"Latest Version : {_get_ver()}"
249 | )
250 |
251 | def _get_ver():
252 | _url = "https://api.github.com/repos/fxxkrlab/iCopy/releases"
253 | _r_ver = requests.get(_url).json()
254 | _latest_ver = _r_ver[0]["tag_name"]
255 | return _latest_ver
256 |
257 | @_r.restricted
258 | def cancel(update, context):
259 | user = update.effective_user.first_name
260 | logger.info("User %s canceled the conversation.", user)
261 | update.effective_message.reply_text(
262 | f"Bye! {update.effective_user.first_name} ," + _text[_lang]["cancel_msg"]
263 | )
264 | return ConversationHandler.END
265 |
266 | def error(update, context):
267 | """Log Errors caused by Updates."""
268 | logger.warning('Update "%s" caused error "%s"', update, context.error)
269 |
--------------------------------------------------------------------------------
/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/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 | flags += _cfg["general"]["run_args"]
60 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}"
61 |
62 | command = [cloner, option, src_block, checkers, sa_sleep]
63 | command += flags
64 |
65 | simple_size_process(ns,command, size_chat_id, size_message_id, share_name_list)
66 |
67 | ns.size = 0
68 |
69 |
70 | def simple_size_process(ns,command, size_chat_id, size_message_id, share_name_list):
71 | for output in simpe_size_run(command):
72 | if ns.size == 1:
73 | simple_sizing.kill()
74 |
75 | regex_total_object = r"^Total objects:"
76 | regex_total_size = r"Total size:"
77 | if output:
78 | size_total_object = re.findall(regex_total_object, output)
79 | size_total_size = re.findall(regex_total_size, output)
80 |
81 | global size_object
82 | global size_size
83 |
84 | if size_total_object:
85 | size_object = output[15:]
86 | if size_total_size:
87 | size_size_all = output[12:]
88 | size_size = size_size_all.split("(")
89 |
90 | if ns.size == 0:
91 | for item in share_name_list:
92 | item_type = item["G_type"]
93 | item_id = item["G_id"]
94 | item_name = item["G_name"]
95 |
96 | global size_info
97 | size_info = (
98 | " ༺ ✪iCopy✪ ༻ ["
99 | + _text[_lang]["sizing_done"]
100 | + "]\n\n"
101 | + item_type
102 | + " : "
103 | + item_name
104 | + "\n"
105 | + item_id
106 | + "\n"
107 | + "--------------------\n"
108 | + _text[_lang]["total_file_num"]
109 | + str(size_object)
110 | + "\n"
111 | + _text[_lang]["total_file_size"]
112 | + str(size_size[0])
113 | + "\n("
114 | + str(size_size[1])
115 | )
116 |
117 | bot.edit_message_text(
118 | chat_id=size_chat_id, message_id=size_message_id, text=size_info
119 | )
120 |
121 | if ns.size == 1:
122 | bot.edit_message_text(
123 | chat_id=size_chat_id,
124 | message_id=size_message_id,
125 | text=_text[_lang]["is_killed_by_user"],
126 | )
127 |
128 | def owner_size(
129 | ns, size_chat_id, size_message_id, task_id, task_link, endpoint_id, endpoint_name
130 | ):
131 | cloner = _cfg["general"]["cloner"]
132 | option = "size"
133 | remote = _cfg["general"]["remote"]
134 | src_id = endpoint_id
135 | src_block = remote + ":" + "{" + src_id + "}"
136 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}"
137 | flags = ["--size-only"]
138 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}"
139 |
140 | command = [cloner, option, src_block, checkers, sa_sleep]
141 | command += flags
142 |
143 | owner_size_process(
144 | ns,
145 | command,
146 | size_chat_id,
147 | size_message_id,
148 | task_id,
149 | task_link,
150 | endpoint_id,
151 | endpoint_name,
152 | )
153 |
154 | ns.size = 0
155 |
156 |
157 | def owner_size_process(
158 | ns,
159 | command,
160 | size_chat_id,
161 | size_message_id,
162 | task_id,
163 | task_link,
164 | endpoint_id,
165 | endpoint_name,
166 | ):
167 | for output in simpe_size_run(command):
168 | if ns.size == 1:
169 | simple_sizing.kill()
170 |
171 | regex_total_object = r"^Total objects:"
172 | regex_total_size = r"Total size:"
173 | if output:
174 | size_total_object = re.findall(regex_total_object, output)
175 | size_total_size = re.findall(regex_total_size, output)
176 |
177 | global size_object
178 | global size_size
179 |
180 | if size_total_object:
181 | size_object = output[15:]
182 | if size_total_size:
183 | size_size_all = output[12:]
184 | size_size = size_size_all.split("(")
185 |
186 | if ns.size == 0:
187 | size_size_re = r"([\d.]+[\s]?)([kMGTP]?Bytes)"
188 | get_size_size = re.search(size_size_re, str(size_size[0]))
189 | if get_size_size:
190 | size_size_num = get_size_size.group(1).strip()
191 | size_size_tail = get_size_size.group(2).strip()
192 |
193 | if task_id == 0:
194 | size_info = (
195 | " ༺ ✪iCopy✪ ༻ | favorites | ["
196 | + _text[_lang]["sizing_done"]
197 | + "]\n\n"
198 | + '{}'.format(task_link, endpoint_name)
199 | + "\n"
200 | + "--------------------\n"
201 | + _text[_lang]["total_file_num"]
202 | + str(size_object)
203 | + "\n"
204 | + _text[_lang]["total_file_size"]
205 | + str(size_size[0])
206 | + "\n("
207 | + str(size_size[1])
208 | )
209 |
210 | fav_col.update_one(
211 | {"G_id": endpoint_id},
212 | {
213 | "$set": {
214 | "fav_object": int(size_object),
215 | "fav_size": float(size_size_num),
216 | "fav_size_tail": size_size_tail,
217 | },
218 | },
219 | upsert=True,
220 | )
221 |
222 | else:
223 | size_info = (
224 | " ༺ ✪iCopy✪ ༻ | "
225 | + "🏳️"
226 | + _text[_lang]["current_task_id"]
227 | + str(task_id)
228 | + " | ["
229 | + _text[_lang]["sizing_done"]
230 | + "]\n\n"
231 | + '{}'.format(task_link, endpoint_name)
232 | + "\n"
233 | + "--------------------\n"
234 | + _text[_lang]["total_file_num"]
235 | + str(size_object)
236 | + "\n"
237 | + _text[_lang]["total_file_size"]
238 | + str(size_size[0])
239 | + "\n("
240 | + str(size_size[1])
241 | )
242 |
243 | task_list.update_one(
244 | {"_id": int(task_id)},
245 | {
246 | "$set": {
247 | "task_current_prog_num": int(size_object),
248 | "task_total_prog_num": int(size_object),
249 | "task_current_prog_size": float(size_size_num),
250 | "task_total_prog_size": float(size_size_num),
251 | "task_current_prog_size_tail": size_size_tail[:1],
252 | "task_total_prog_size_tail": size_size_tail,
253 | }
254 | },
255 | )
256 |
257 | bot.edit_message_text(
258 | chat_id=size_chat_id,
259 | message_id=size_message_id,
260 | text=size_info,
261 | parse_mode=ParseMode.HTML,
262 | disable_web_page_preview=True,
263 | )
264 |
265 | elif ns.size == 1:
266 | bot.edit_message_text(
267 | chat_id=size_chat_id,
268 | message_id=size_message_id,
269 | text=_text[_lang]["is_killed_by_user"],
270 | )
271 |
--------------------------------------------------------------------------------
/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 | set_web_account = "请设置iCopy WEB DASHBOARD 账号秘密\n格式 : 账号,密码"
82 | set_web_success = "设置成功"
83 |
84 | [eng]
85 | start = "Hi! replace . Welcome to use iCopy" #replace==symbolic placeholder
86 | guide_to_menu = "Please Input '/menu' to Choose run mode"
87 | menu_msg = "Pls Choose the Mode"
88 | quick_mode = "QUICK MODE"
89 | copy_mode = "COPY MODE"
90 | size_mode = "Size"
91 | purge_mode = "Empty Recycle bin"
92 | dedupe_mode = "dedupe mode"
93 | mode_select_msg = "┋replace┋ has be choosen" #replace==symbolic placeholder
94 | request_share_link = "Pls input Shared_Link or Shared_ID"
95 | request_dst_target = "Please select the destination path for the task"
96 | is_cover = "Cover it"
97 | not_cover = "Cancel"
98 | is_cover_quick_msg = "QUICK\\_MODE The specified directory already exists\nDo you need to cover it?"
99 | insert_quick_success = "QUICK\\_MODE specified path is setted successfully"
100 | modify_quick_success = "QUICK\\_MODE specified path is renewed successfully"
101 | null_fav_quick = "QUICK\\_MODE directory is not set\nPlease try again after setting"
102 | null_fav = "COPY\\_MODE directory is not set\nPlease try again after setting"
103 | ready_to_task = "Now is Ready to Task..."
104 | doing = "Transferring"
105 | done = "Done"
106 | killed = "Killed"
107 | current_task_id = "task id : "
108 | task_start_time = "Task start time : "
109 | task_finished_time = "Task end time : "
110 | task_files_size = "Total file size : "
111 | task_files_num = "Task file num : "
112 | task_status = "task_status : "
113 | elapsed_time = "Elapsed Time : "
114 | cancel_msg = "Welcome to enjoy with iCopy again"
115 | get_quick_count_invaild = "Only ONE Dst ID can be specified under quick mode"
116 | get_multi_fav_error = "pls submit DST ID according '/set'"
117 | get_single_fav_error = "pls submit single DST ID according '/set rule'"
118 | get_multi_in_single = "pls use '/set' while modify multi DST IDs"
119 | task_src_info = "[Resource From]:"
120 | task_dst_info = "[Transfer To]:"
121 | is_set_err = "SET FAV ERROR"
122 | set_fav_success = "set Favorites success"
123 | delete_fav_success = "Favorites deleted successfully"
124 | delete_quick_success = "QUICK\\_MODE specified path is deleted successfully"
125 | show_fav_list = "The following are favorites that have been set"
126 | show_fav_list_null = "Favorites are not set"
127 | is_restarting = "iCopy is restarting...\nPlease start using in 30 seconds"
128 | is_killed_by_user = "Task is killed by user"
129 | is_current_task = "Current Task : \n\n"
130 | current_task_src_name = "Source : "
131 | current_task_dst_name = "Destination : "
132 | is_not_current_task = "No Task is in Processing"
133 | show_wait_list = "task in the queue\n\n"
134 | show_wait_list_null = "There is no task in waiting"
135 | interrupted = "Interrupted"
136 | is_interrupted_error = "Unexpected interruption"
137 | restart_success = "iCopy restart is complete"
138 | add_task_successful = "Add Task Successful"
139 | purge_fav = "purge Favorites (except Quick path)"
140 | reset_successful = "Task ID: replace has been reset successfully" #replace==symbolic placeholder
141 | over_limit_error = "Exceeded maximum resettable task number"
142 | over_limit_to_check = "Exceeded maximum task number which could be check"
143 | over_limit_to_dedupe = "Exceeded maximum task number which could be dedupe"
144 | global_command_error = "The input command or command format does not exist"
145 | ready_to_size = "Preparing statistics"
146 | sizing = "In statistics, pls wait \nIf there is a copy task, it will affect the accuracy of both copy and statistics"
147 | total_file_num = "Total Files : "
148 | total_file_size = "Total Size : "
149 | sizing_done = "Size Finished"
150 | task_is_in_queue = "Task is in the queue"
151 | finished_could_be_check = "You could only check the task which is finished"
152 | finished_could_be_dedupe = "You could only dedupe the task which is finished"
153 | support_error = "The specified task does not support queries"
154 | request_target_folder = "Pls choose the TARGET drive or folder"
155 | ready_to_purge = "Ready to Purge"
156 | 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"
157 | purging_done = "The recycle bin has been emptied"
158 | request_dedupe_mode = "Please select to dedupe mode rule"
159 | ready_to_dedupe = "Ready to dedupe"
160 | deduping = "In deduping task, pls wait \nIf there is a copy task, it will affect the accuracy of both copy and dedupe"
161 | deduping_done = "The task is be deduped"
162 | is_folder_not_drive = "The selected favorites is a 'folder' not a 'shared drive'\n Can not be purged"
163 | set_web_account = "Please set your iCopy WEB DASHBOARD Account&Password follow the example\nexample : account,password"
164 | set_web_success = "Set Successfully"
165 |
166 | [jp]
167 | start = "Hi! replace . iCopyへようこそ" #replace==置き換え指示記号
168 | guide_to_menu = "実行モードを選択するには、'/menu' を入力してください"
169 | menu_msg = "ご覧のモードを選んでください"
170 | quick_mode = "「クイック」"
171 | copy_mode = "「カスタマイズ」"
172 | size_mode = "「サイズ」"
173 | purge_mode = "「ごみ箱を空にする」"
174 | dedupe_mode = "「重複を取り除く」"
175 | mode_select_msg = "┋replace┋が選択されました" #replace==置き換え指示記号
176 | request_share_link = "共有リンクまたは共有IDを入力してください"
177 | request_dst_target = "ファイルの保存場所を選択してください"
178 | is_cover = "カバー"
179 | not_cover = "キャンセル"
180 | is_cover_quick_msg = "QUICK_MODE指定されたフォルダーはすでに存在します\n新しいフォルダで上書きしますか?"
181 | insert_quick_success = "QUICK_MODEで指定されたフォルダーが正常に設定されました"
182 | modify_quick_success = "QUICK_MODEで指定されたフォルダーが更新されました"
183 | null_fav_quick = "QUICK\\_MODEフォルダーが設定されていません\n設定後にもう一度お試しください"
184 | null_fav = "COPY\\_MODEフォルダーが設定されていません\n設定後にもう一度お試しください"
185 | ready_to_task = "任務準備中..."
186 | doing = "タスク実行中"
187 | done = "任務成功"
188 | killed = "任務中止"
189 | current_task_id = "タスク番号 : "
190 | task_start_time = "タスク開始時刻 : "
191 | task_finished_time = "タスク終了時間 : "
192 | task_files_size = "ファイルサイズ : "
193 | task_files_num = "タスクファイル数 : "
194 | task_status = "タスク状態 : "
195 | elapsed_time = "合計時間 : "
196 | cancel_msg = "再びiCopyへようこそ"
197 | get_quick_count_invaild = "「クイックモード」で一つフォルダーしか指定されることができない"
198 | get_multi_fav_error = "'/set'にルールをよって、フォルダーのIDを入力してください"
199 | get_single_fav_error = "'/set rule'にルールをよって、フォルダーのIDを入力してください"
200 | get_multi_in_single = "複数のフォルダーを設定する場合は、「/set」コマンドを使用してください"
201 | task_src_info = "[任務目標]:"
202 | task_dst_info = "[保存場所]:"
203 | is_set_dedupe = "お気に入りを設定するとエラーが発生します"
204 | set_fav_success = "お気に入りを設定しました"
205 | delete_fav_success = "お気に入り削除に成功"
206 | delete_quick_success = "QUICK_MODEで指定されたフォルダーが削除されました"
207 | show_fav_list = "以下は既設のお気に入りである"
208 | show_fav_list_null = "お気に入りは設定いない"
209 | is_restarting = "iCopyが再起動しています...\n30秒後に使用を開始してください"
210 | is_killed_by_user = "タスクはユーザによって終了される"
211 | is_current_task = "進行中のタスク : \n\n"
212 | current_task_src_name = "源フォルダー : "
213 | current_task_dst_name = "目標フォルダー : "
214 | is_not_current_task = "進行中のタスクがない"
215 | show_wait_list = "タスクが待機しています\n\n"
216 | show_wait_list_null = "待機中のタスクはありません"
217 | interrupted = "タスク中断"
218 | is_interrupted_error = "予期しない中断"
219 | restart_success = "iCopyの再開が完了する"
220 | add_task_successful = "タスク追加成功"
221 | purge_fav = "お気に入りをクリア(クイックフォルダーを除く)"
222 | reset_successful = "タスク ID: replace がリセットされた"#replace==置き換え指示記号
223 | over_limit_error = "最大リセット可能タスク番号を超える"
224 | over_limit_to_check = "最大チェック可能タスク番号を超える"
225 | over_limit_to_dedupe = "最大重複除外可能タスク番号を超える"
226 | global_command_error = "入力するコマンドやコマンドフォーマットは存在しない"
227 | ready_to_size = "統計を準備している"
228 | sizing = "統計中ですので、お待ちください\nコピータスクが同時に存在する場合,コピーと統計の双方の正確さに影響する"
229 | total_file_num = "総 ファイル 数 : "
230 | total_file_size = "総 サイズ : "
231 | sizing_done = "統計終了"
232 | task_is_in_queue = "タスクが列に並んでいる"
233 | finished_could_be_check = "既に完了した任務しか調べられない"
234 | finished_could_be_dedupe = "すでに完了したタスクのみが重複ファイルの除去を行うことができる"
235 | support_error = "指定の任務の照会を支持しない"
236 | request_target_folder = "目標フォルダーを選んでください"
237 | ready_to_purge = "ごみ箱を空にする準備をしています"
238 | purging = "ごみ箱をクリアしています。\n同時にコピーの任務がある場合、コピーとごみ箱を空にすることの両方の正確さに影響します"
239 | purging_done = "既にごみ箱を空にした"
240 | request_dedupe_mode = "重複除外モードのルールを選択してください"
241 | ready_to_dedupe = "重複ファイルを除去する準備をしています"
242 | deduping = "重複ファイルを除去しています。\n同時にコピーの任務がある場合、コピーと重複除外の両方の正確さに影響します"
243 | deduping_done = "重複ファイルの除去が完了しました"
244 | is_folder_not_drive = "選択されたお気に入りは「フォルダ」ではなく「シェアドライバ」で\nゴミ箱を空にできない"
245 | set_web_account = "例に従って、iCopy WEB DASHBOARDアカウントとパスワードを設定してください\n例 : アカウント,パスワード"
246 | set_web_success = "正しく設定されました"
--------------------------------------------------------------------------------
/iCopy/docs/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # iCopy v0.2 CHANGELOG
2 |
3 | ## version 0.2.2-Post.2
4 |
5 | + Fixbugs:
6 | + FIXBUG : Msg ParseMode Error
7 |
8 | ## version 0.2.2
9 |
10 | + Update :
11 | + ADD : source link in task message
12 |
13 | + Fixbugs:
14 | + FIXBUG : syntax error
15 | + FIXBUG : local variable error
16 |
17 | ## version 0.2.1-Post.5
18 |
19 | + Fixbugs:
20 | + FIXBUG : local variable assignment error
21 | + FIXBUG : dict error
22 | + FIXBUG : SQL Error
23 |
24 | ## version 0.2.1-beta.1
25 |
26 | NOTICE : Please replace the old "conf.toml" with the new one."conf.[web]dashboard" is the switch of WEB Service.conf.[web]port is the WEB service port.
27 | ( 0 = Off, 1 = On )
28 | Default Account&Password : admin,admin
29 | Upgrade method :
30 |
31 | ```bash
32 | 1 git pull
33 | 2 pip3 install -r requirements.txt
34 | 3 edit and replace the new conf.toml
35 | 4 python3 iCopy.py
36 | ```
37 |
38 | + Update :
39 | + ADD : iCopy WEB DASHBOARD
40 | + ADD : "/set web" command for set web account&password
41 | + ADD : WEB Section in "conf.toml"
42 | + ADD : API v1
43 | + ADD : logs for dedupe,purge,size
44 | + ADD : COMMAND doc in docs
45 | + FIX : run_args
46 | + FIX : Command & Flags
47 |
48 | + Fixbugs :
49 | + FIXBUG : local variable assignment error
50 |
51 | ## version 0.2.0-beta.6.7
52 |
53 | + Update :
54 | + ADD : renew requirements.txt.
55 | + ADD : "\_\_version\_\_"
56 |
57 | + Fixbugs :
58 | + Compatible with the old version of the database format
59 | + Fix floatify format error when the size is 0
60 |
61 | ## version 0.2.0-beta.6.6
62 |
63 | + Update :
64 | + Block "googleapiclient.discovery" warning prompt.
65 | + Deprecated "cache_discovery"
66 |
67 | ## version 0.2.0-beta.6.5
68 |
69 | + Fixbugs:
70 | + FIX : local variable assignment error
71 | + FIX : try to change restart function args
72 |
73 | ## version 0.2.0-beta.6.4
74 |
75 | + Fixbugs:
76 | + FIX : "/set" function "import as name" error
77 |
78 | ## version 0.2.0-beta.6.3
79 |
80 | + Update :
81 | + ADD : "/kill size","/kill purge","/kill dedupe" was added to terminate the execution of these tasks
82 |
83 | + Root Command:
84 |
85 | + start - nothing just say hello
86 | + menu - main entry point
87 | quick - quick mode
88 | copy - full mode
89 | set - customize settings
90 | task - task query
91 | reset - restore task
92 | size - just size task
93 | dedupe - dedupe drives and folders
94 | purge - delete files and folder in specified fav trash bin
95 | cancel - cancel TG conversation
96 | kill - kill task
97 | ver - check iCopy version
98 | restart - restart iCopy
99 |
100 | + Child Command:
101 |
102 | + set - customize settings
103 | ┖ set - batch way
104 | ┖ set rule - rules
105 | ┖ fav|quick +/- id - single way
106 | ┖ set purge - purge favorites
107 | + size - size query
108 | ┖ size - size the shared resource
109 | ┖ size id - size specified task
110 | ┖ size fav - size specified favorites
111 | + dedupe - dedupe drives and folders
112 | ┖ dedupe - dedupe specified favorites
113 | ┖ dedupe id - dedupe specified task
114 | + task - task query
115 | ┖ task - task in processing
116 | ┖ task list - future 10 tasks
117 | ┖ task id - show the specified task
118 | + reset - restore task
119 | ┖ reset - restore current task
120 | ┖ reset id - restore the specified task
121 | + kill - kill task
122 | ┖ kill - kill current transferring task
123 | ┖ kill task - kill current transferring task
124 | ┖ kill size - kill sizing task
125 | ┖ kill purge - kill purge task
126 | ┖ kill dedupe - kill dedupe task
127 |
128 | ## version 0.2.0-beta.6.2
129 |
130 | + Update :
131 | + ADD : Add "rmdir" operation to the "/Purge" function to clear the empty folder in the root directory.
132 |
133 | + Fixbugs :
134 | + FIX : "/dedupe" sendMsg error.
135 | + FIX : Insert DATABASE error while dedupe payload finished.
136 |
137 | ## version 0.2.0-beta.6.1
138 |
139 | + Update :
140 | + ADD : "/dedupe" Now you can choose favorites to dedupe
141 | + CHANGE : Move the stage tag uniformly to new file “utils/callback_stage.py"
142 |
143 | + Fixbugs :
144 | + Judge select favorites if is shared drive when you use "/purge" mode.
145 | + Separately define bot variables in asynchronous-process to prevent errors in connecting Telegram.
146 |
147 | + Root Command:
148 |
149 | + start - nothing just say hello
150 | + menu - main entry point
151 | quick - quick mode
152 | copy - full mode
153 | set - customize settings
154 | task - task query
155 | reset - restore task
156 | size - just size task
157 | dedupe - dedupe drives and folders
158 | purge - delete files and folder in specified fav trash bin
159 | cancel - cancel TG conversation
160 | kill - kill task which is in processing
161 | ver - check iCopy version
162 | restart - restart iCopy
163 |
164 | + Child Command:
165 |
166 | + set - customize settings
167 | ┖ set - batch way
168 | ┖ set rule - rules
169 | ┖ fav|quick +/- id - single way
170 | ┖ set purge - purge favorites
171 | + size - size query
172 | ┖ size - size the shared resource
173 | ┖ size id - size specified task
174 | ┖ size fav - size specified favorites
175 | + dedupe - dedupe drives and folders
176 | ┖ dedupe - dedupe specified favorites
177 | ┖ dedupe id - dedupe specified task via task id
178 | + task - task query
179 | ┖ task - task in processing
180 | ┖ task list - future 10 tasks
181 | ┖ task id - show the specified task
182 | + reset - restore task
183 | ┖ reset - restore current task
184 | ┖ reset id - restore the specified task
185 |
186 | ## version 0.2.0-beta.6
187 |
188 | + Update :
189 | + ADD : insert more details into Database and more initializated data
190 | + ADD : feedback dst endpoint link when task end normally
191 | + ADD : "/task id" only support the task which is start after v0.2.0b6
192 | + ADD : mark tasks that have been reset in the database
193 | + ADD : "/size id" & "/size fav"
194 | + ADD : "/purge" to empty shared drive trash bin
195 | + ADD : "/dedupe id" to dedupe task
196 | + ADD : Record the last time of size and dedupe
197 |
198 | + Fix :
199 | + FIX : Update RegEX Rules
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 | dedupe - dedupe specified task
212 | purge - delete files and folder in specified fav trash bin
213 | cancel - cancel TG conversation
214 | kill - kill task which is in processing
215 | ver - check iCopy version
216 | restart - restart iCopy
217 |
218 | + Child Command:
219 |
220 | + set - customize settings
221 | ┖ set - batch way
222 | ┖ set rule - rules
223 | ┖ fav|quick +/- id - single way
224 | ┖ set purge - purge favorites
225 | + size - size query
226 | ┖ size - size the shared resource
227 | ┖ size id - size specified task
228 | ┖ size fav - size specified favorites
229 | + task - task query
230 | ┖ task - task in processing
231 | ┖ task list - future 10 tasks
232 | ┖ task id - show the specified task
233 | + reset - restore task
234 | ┖ reset - restore current task
235 | ┖ reset id - restore the specified task
236 |
237 | ## version 0.2.0-beta.5.1
238 |
239 | + Update :
240 | + ADD : More task info into Database
241 |
242 | + Fixbugs :
243 | + FIX : delete "directly in" mode keyboard after selection is choosen
244 | + FIX : Purge local var after Conversation END
245 | The task will not be committed twice now
246 | + FIX : "/task list" error
247 | Now "/task list" will display up to 10 tasks pending
248 |
249 | + Root Command:
250 |
251 | + start - nothing just say hello
252 | + menu - main entry point
253 | quick - quick mode
254 | copy - full mode
255 | set - customize settings
256 | task - task query
257 | reset - restore task
258 | size - just size task
259 | cancel - cancel TG conversation
260 | kill - kill task which is in processing
261 | ver - check iCopy version
262 | restart - restart iCopy
263 |
264 | + Child Command:
265 |
266 | + set - customize settings
267 | ┖ set - batch way
268 | ┖ set rule - rules
269 | ┖ fav|quick +/- id - single way
270 | ┖ set purge - purge favorites
271 | + task - task query
272 | ┖ task - task in processing
273 | ┖ task list - future 10 tasks
274 | + reset - restore task
275 | ┖ reset - restore current task
276 | ┖ reset id - restore the specified task
277 |
278 | ## version 0.2.0-beta.5
279 |
280 | + Update :
281 | + ADD : directly input sharelink then choose mode.
282 |
283 | + Fixbugs :
284 | + FIX : get shared drive name failed when the shared drive is temporarily granted permission for an outside party.
285 | + FIX : Fix the error of repeated tasks when entering multiple tasks at the same time.
286 | + FIX : "reset" notice msg error
287 |
288 | ## version 0.2.0-beta.4.1
289 |
290 | + Fixbugs :
291 | + FIX : "/reset task_id" Database operation error
292 |
293 | ## version 0.2.0-beta.4
294 |
295 | + Update :
296 | + ADD "/size" a function to get simple size info
297 |
298 | + Fixbugs :
299 | + FIX : "/reset" send notice msg error
300 | + FIX : get shared drive name failed when the shared drive is temporarily granted permission for an outside party.
301 |
302 | ***
303 |
304 | ## version 0.2.0-beta.3
305 |
306 | Notice : The new "conf.toml" should be replaced or you could modify the "conf.toml" by referring to the "example" one.
307 |
308 | + Update :
309 | + ADD "/set purge"
310 | Allow to Purge Favorties Setting Now.
311 | this will not delete quick mode setting.
312 | + Now '--drive-server-side-across-configs' is Built in the iCopy. Remove from conf.toml
313 | + '--ignore-checksum' is write in conf.toml default
314 | + ADD "/reset" and "/reset id" command.
315 | You could restore task with the command
316 |
317 | ***
318 |
319 | ## version 0.2.0-beta.2
320 |
321 | Update : send confirm msg after task added
322 | Update : '/start' is not in Conversation Handle any more
323 | Update : Use '/menu' to select run mode instead of '/start'
324 |
325 | ***
326 |
327 | ## version 0.2.0-beta.1
328 |
329 | The first beta version of v0.2
330 | β1 is a relatively stable without bugs version
331 | The following Command is Supported
332 |
333 | + Root Command:
334 |
335 | + start - main entry point
336 | quick - quick mode
337 | copy - full mode
338 | set - customize settings
339 | task - task query
340 | cancel - cancel TG conversation
341 | kill - kill task which is in processing
342 | ver - check iCopy version
343 | restart - restart iCopy
344 |
345 | + Child Command:
346 |
347 | + set - ustomize settings
348 | ┖ set - batch way
349 | ┖ set rule - rules
350 | ┖ set fav|quick +/- id - single way
351 | task - task query
352 | ┖ task - task in processing
353 | ┖ task list - future 10 tasks
354 |
355 | ***
356 |
357 | ## version 0.2.0-alpha.1 ~ alpha.15
358 |
359 | iCopy rebuild basework finished
360 |
361 | ## version 0.1.7-beta.3
362 |
363 | Archived version
364 | ...
365 |
--------------------------------------------------------------------------------
/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 | elif "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 | elif "web" == entry_cmd[4:]:
80 | update.effective_message.reply_text(
81 | _text[_lang]["set_web_account"]
82 | )
83 |
84 | return _stage.SET_WEB
85 |
86 | elif "/setlist" == entry_cmd:
87 | global showitem
88 | global showlist
89 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"})
90 | fav_list = load.fav_col.find({"fav_type": "fav"})
91 | if fav_count is not None:
92 | if fav_count["fav_sum"] == 0:
93 | update.effective_message.reply_text(_text[_lang]["show_fav_list_null"])
94 |
95 | return ConversationHandler.END
96 |
97 | elif fav_count["fav_sum"] != 0:
98 | for item in fav_list:
99 | showitem = (
100 | "type : "
101 | + item["G_type"]
102 | + " | name : "
103 | + item["G_name"]
104 | + "\nid : "
105 | + item["G_id"]
106 | + "\n"
107 | + "--------------------\n"
108 | )
109 | showlist.append(showitem)
110 |
111 | showlist = "".join(showlist)
112 |
113 | update.effective_message.reply_text(
114 | _text[_lang]["show_fav_list"] + "\n\n" + showlist
115 | )
116 |
117 | showlist = []
118 |
119 | return ConversationHandler.END
120 |
121 | else:
122 | update.effective_message.reply_text(_text[_lang]["show_fav_list_null"])
123 |
124 | return ConversationHandler.END
125 |
126 | ### set single DST ID ###
127 | elif "quick" or "fav" in entry_cmd:
128 | ### single quick (drive or folder)
129 | if len(entry_cmd.splitlines()) == 1:
130 | each = entry_cmd[4:]
131 | if "quick" == each[:5]:
132 | if "quick+" == each[:6]:
133 | global pick_quick
134 | pick_quick = _func.get_name_from_id(
135 | update, each[6:], list_name=pick_quick
136 | )
137 | insert_fav_quick = _func.insert_to_db_quick(pick_quick, update)
138 | if insert_fav_quick == "is_cover":
139 | update.effective_message.reply_text(
140 | _text[_lang]["is_cover_quick_msg"],
141 | parse_mode=ParseMode.MARKDOWN_V2,
142 | reply_markup=_KB.is_cover_keyboard(),
143 | )
144 |
145 | return _stage.IS_COVER_QUICK
146 |
147 | elif "quick-" == each[:6]:
148 | _func.delete_in_db_quick
149 | update.effective_message.reply_text(
150 | _text[_lang]["delete_quick_success"]
151 | )
152 |
153 | ### set fav folder(fav folder could be a drive or folder of GDrive)
154 | elif "fav" == each[:3]:
155 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"})
156 | fav_sum = 0
157 |
158 | if fav_count != None:
159 | fav_sum = fav_count["fav_sum"]
160 |
161 | if "+" == each[3]:
162 | global pick_fav
163 | pick_fav = _func.get_name_from_id(
164 | update, each[4:], list_name=pick_fav
165 | )
166 | for item in pick_fav:
167 | item["fav_type"] = "fav"
168 | try:
169 | load.fav_col.insert_one(item)
170 | except:
171 | update.effective_message.reply_text(
172 | _text[_lang]["is_set_err"],
173 | )
174 | else:
175 | fav_sum += 1
176 | load.db_counters.update(
177 | {"_id": "fav_count_list"},
178 | {"fav_sum": fav_sum},
179 | upsert=True,
180 | )
181 |
182 | update.effective_message.reply_text(_text[_lang]["set_fav_success"])
183 |
184 | pick_fav = []
185 |
186 | if "-" == each[3]:
187 | global unpick_fav
188 | unpick_fav.append(each[4:])
189 | for item in unpick_fav:
190 | delete_request = {"G_id": item}
191 | _func.delete_in_db(delete_request)
192 | fav_count = load.fav_col.find({"fav_type": "fav"})
193 | fav_sum = len(list(fav_count))
194 | load.db_counters.update(
195 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True
196 | )
197 |
198 | update.effective_message.reply_text(
199 | _text[_lang]["delete_fav_success"]
200 | )
201 |
202 | unpick_fav = []
203 |
204 | ### single rule
205 | elif "rule" == entry_cmd[4:8]:
206 | update.effective_message.reply_text(
207 | _msg.set_single_fav_guide(_lang), parse_mode=ParseMode.MARKDOWN_V2
208 | )
209 |
210 | return ConversationHandler.END
211 |
212 | else:
213 | update.effective_message.reply_text(
214 | _text[_lang]["get_single_fav_error"],
215 | parse_mode=ParseMode.MARKDOWN_V2,
216 | )
217 |
218 | return ConversationHandler.END
219 |
220 | return ConversationHandler.END
221 |
222 | else:
223 | update.effective_message.reply_text(
224 | _text[_lang]["get_multi_in_single"], parse_mode=ParseMode.MARKDOWN_V2
225 | )
226 |
227 | return ConversationHandler.END
228 |
229 | else:
230 | update.effective_message.reply_text(
231 | _msg.set_help(_lang), parse_mode=ParseMode.MARKDOWN_V2
232 | )
233 | return ConversationHandler.END
234 |
235 |
236 | ### set multi DST ID ###
237 | def _multi_settings_recieved(update, context):
238 | _tmp_quick_counter = 0
239 | fav_msg = update.effective_message.text
240 | fav_msg = fav_msg.replace(" ", "").splitlines()
241 | global pick_quick
242 | for each in fav_msg:
243 | print(each)
244 | ### modify quick DST
245 | if "quick+" == each[:6]:
246 | _tmp_quick_counter += 1
247 | if _tmp_quick_counter == 1:
248 | global pick_quick
249 | pick_quick += _func.get_name_from_id(
250 | update, each[6:], list_name=pick_quick
251 | )
252 | insert_fav_quick = _func.insert_to_db_quick(pick_quick, update)
253 | if insert_fav_quick == "error":
254 | update.effective_message.reply_text(
255 | _text[_lang]["is_cover_quick_msg"],
256 | parse_mode=ParseMode.MARKDOWN_V2,
257 | reply_markup=_KB.is_cover_keyboard(),
258 | )
259 |
260 | return _stage.IS_COVER_QUICK
261 |
262 | elif _tmp_quick_counter < 1:
263 | pass
264 | elif _tmp_quick_counter > 1:
265 | print("error!")
266 | update.effective_message.reply_text(
267 | _text[_lang]["get_quick_count_invaild"]
268 | )
269 | elif "quick-" == each[:6]:
270 | _func.delete_in_db_quick
271 | update.effective_message.reply_text(_text[_lang]["delete_quick_success"])
272 |
273 | ### set fav folder(fav folder could be a drive or folder of GDrive)
274 |
275 | elif "fav" == each[:3]:
276 | fav_count = load.db_counters.find_one({"_id": "fav_count_list"})
277 | fav_sum = 0
278 |
279 | if fav_count != None:
280 | fav_sum = fav_count["fav_sum"]
281 |
282 | if "+" == each[3]:
283 | global pick_fav
284 | pick_fav += _func.get_name_from_id(update, each[4:], list_name=pick_fav)
285 | for item in pick_fav:
286 | item["fav_type"] = "fav"
287 | try:
288 | load.fav_col.insert_one(item)
289 | except:
290 | update.effective_message.reply_text(_text[_lang]["is_set_err"],)
291 | else:
292 | fav_sum += 1
293 | load.db_counters.update(
294 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True
295 | )
296 |
297 | update.effective_message.reply_text(_text[_lang]["set_fav_success"])
298 | pick_fav = []
299 |
300 | if "-" == each[3]:
301 | global unpick_fav
302 | unpick_fav.append(each[4:])
303 | for item in unpick_fav:
304 | delete_request = {"G_id": item}
305 | _func.delete_in_db(delete_request)
306 | fav_count = load.fav_col.find({"fav_type": "fav"})
307 | fav_sum = len(list(fav_count))
308 | load.db_counters.update(
309 | {"_id": "fav_count_list"}, {"fav_sum": fav_sum}, upsert=True
310 | )
311 |
312 | update.effective_message.reply_text(_text[_lang]["delete_fav_success"])
313 |
314 | unpick_fav = []
315 |
316 | else:
317 | if "/cancel" == update.effective_message.text:
318 |
319 | return _func.cancel(update, context)
320 | else:
321 | update.effective_message.reply_text(_text[_lang]["get_multi_fav_error"])
322 |
323 | return ConversationHandler.END
324 |
325 | # ### set web acc/passwd
326 | def setWeb(update, context):
327 | accpw_list = []
328 | raw_accpw = update.effective_message.text
329 | accpw_list = raw_accpw.split(",")
330 | web_acc = accpw_list[0]
331 | web_pw = accpw_list[1]
332 | load.login_col.update_one({"_id": "login_info"},{"$set": {"username": web_acc,"password": web_pw,},},)
333 | update.effective_message.reply_text(
334 | _text[_lang]["set_web_success"]
335 | )
336 |
337 | return ConversationHandler.END
338 |
--------------------------------------------------------------------------------
/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", '-P', '--ignore-checksum' , '--stats=1s']
49 |
50 | command = []
51 |
52 | cloner = _cfg["general"]["cloner"]
53 | option = _cfg["general"]["option"]
54 | remote = _cfg["general"]["remote"]
55 | src_id = task["src_id"]
56 | src_name = task["src_name"]
57 | if "/" in src_name:
58 | src_name = src_name.replace("/", "|")
59 | if "'" in src_name:
60 | src_name = src_name.replace("'", "")
61 | if '"' in src_name:
62 | src_name = src_name.replace('"', "")
63 |
64 | dst_id = task["dst_id"]
65 | src_block = remote + ":" + "{" + src_id + "}"
66 | dst_block = remote + ":" + "{" + dst_id + "}" + "/" + src_name
67 | checkers = "--checkers=" + f"{_cfg['general']['parallel_c']}"
68 | transfers = "--transfers=" + f"{_cfg['general']['parallel_t']}"
69 | sa_sleep = "--drive-pacer-min-sleep=" + f"{_cfg['general']['min_sleep']}"
70 |
71 | flags += _cfg["general"]["run_args"]
72 | flags += [checkers, transfers, sa_sleep]
73 |
74 | command = [cloner, option, src_block, dst_block]
75 |
76 | command += flags
77 |
78 | chat_id = task["chat_id"]
79 |
80 | task_process(chat_id, command, task, ns, src_name)
81 |
82 | ns.x = 0
83 |
84 | flags = []
85 |
86 | global old_working_line
87 | global current_working_line
88 | old_working_line = 0
89 | current_working_line = 0
90 | time.sleep(3)
91 |
92 | time.sleep(5)
93 |
94 |
95 | def task_process(chat_id, command, task, ns, src_name):
96 | # mark is in processing in db
97 | task_list.update_one({"_id": task["_id"]}, {"$set": {"status": 2,}})
98 | db_counters.update({"_id": "last_task"}, {"task_id": task["_id"]}, upsert=True)
99 | chat_id = chat_id
100 | message = bot.send_message(chat_id=chat_id, text=_text[_lang]["ready_to_task"])
101 | message_id = message.message_id
102 | dst_info = {}
103 | interval = 0.1
104 | timeout = 180
105 | xtime = 0
106 | old_working_line = 0
107 | current_working_line = 0
108 | task_current_prog_num = 0
109 | task_total_prog_num = 0
110 | task_percent = 0
111 | task_current_prog_size = "0"
112 | task_total_prog_size = "0 Bytes"
113 | task_in_size_speed = "-"
114 | task_in_file_speed = "-"
115 | task_eta_in_file = "-"
116 | task_current_prog_size_tail = ""
117 | task_total_prog_size_tail = ""
118 | dst_id = task['dst_id']
119 | src_link = r"https://drive.google.com/open?id={}".format(task["src_id"])
120 | start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
121 |
122 | for toutput in run(command):
123 | if ns.x == 1:
124 | global icopyprocess
125 | icopyprocess.kill()
126 |
127 | regex_working_file = r"^ * "
128 | regex_elapsed_time = r"^Elapsed time:"
129 | regex_total_files = (
130 | r"Transferred:\s+(\d+) / (\d+), (\d+)%(?:,\s*([\d.]+\sFiles/s))?"
131 | )
132 | regex_total_size = (
133 | r"Transferred:[\s]+([\d.]+\s*)([kMGTP]?) / ([\d.]+[\s]?)([kMGTP]?Bytes),"
134 | r"\s*(?:\-|(\d+)\%),\s*([\d.]+\s*[kMGTP]?Bytes/s),\s*ETA\s*([\-0-9hmsdwy]+)"
135 | )
136 |
137 | output = toutput
138 |
139 | if output:
140 | task_total_files = re.search(regex_total_files, output)
141 | task_total_size = re.search(regex_total_size, output)
142 | task_elapsed_time = re.findall(regex_elapsed_time, output)
143 | task_working_file = re.findall(regex_working_file, output)
144 |
145 | if task_total_files:
146 | task_current_prog_num = task_total_files.group(1)
147 | task_total_prog_num = task_total_files.group(2)
148 | task_percent = int(task_total_files.group(3))
149 | task_in_file_speed = task_total_files.group(4)
150 |
151 | if task_total_size:
152 | task_current_prog_size = task_total_size.group(1).strip()
153 | task_current_prog_size_tail = task_total_size.group(2)
154 | task_total_prog_size = task_total_size.group(3).strip()
155 | task_total_prog_size_tail = task_total_size.group(4)
156 | task_in_size_speed = task_total_size.group(6)
157 | task_eta_in_file = task_total_size.group(7)
158 |
159 | if task_elapsed_time:
160 | global now_elapsed_time
161 | now_elapsed_time = output.replace(" ", "").split(":")[1]
162 |
163 | if task_working_file:
164 | global current_working_file
165 | current_working_line += 1
166 | current_working_file = (
167 | output.lstrip("* ").rsplit(":")[0].rstrip("Transferred")
168 | )
169 |
170 | global prog_bar
171 | prog_bar = _bar.status(0)
172 | if task_percent != 0:
173 | prog_bar = _bar.status(task_percent)
174 |
175 | global message_info
176 | message_info = (
177 | _text[_lang]["task_src_info"]
178 | + "\n"
179 | + "📃"
180 | + '{}'.format(src_link, src_name)
181 | + "\n"
182 | + "----------------------------------------"
183 | + "\n"
184 | + _text[_lang]["task_dst_info"]
185 | + "\n"
186 | + "📁"
187 | + task["dst_name"]
188 | + ":"
189 | + "\n"
190 | + " ┕─📃"
191 | + src_name
192 | + "\n"
193 | + "----------------------------------------"
194 | + "\n\n"
195 | + _text[_lang]["task_start_time"]
196 | + start_time
197 | + "\n\n"
198 | + _text[_lang]["task_files_size"]
199 | + str(task_current_prog_size)
200 | + task_current_prog_size_tail
201 | + "/"
202 | + str(task_total_prog_size)
203 | + task_total_prog_size_tail
204 | + "\n"
205 | + _text[_lang]["task_files_num"]
206 | + str(task_current_prog_num)
207 | + "/"
208 | + str(task_total_prog_num)
209 | + "\n"
210 | + _text[_lang]["task_status"]
211 | + "\n\n"
212 | + str(task_in_size_speed)
213 | + " | "
214 | + str(task_in_file_speed)
215 | + "\n\n"
216 | + str(task_percent)
217 | + "%"
218 | + str(prog_bar)
219 | )
220 |
221 | if (
222 | int(time.time()) - xtime > interval
223 | and old_working_line != current_working_line
224 | ):
225 | Timer(
226 | 0,
227 | task_message_box,
228 | args=(
229 | bot,
230 | chat_id,
231 | message_id,
232 | " ༺ ✪iCopy✪ ༻ \n"
233 | + _text[_lang]["doing"]
234 | + " | "
235 | + "🏳️"
236 | + _text[_lang]["current_task_id"]
237 | + str(task["_id"])
238 | + "\n\n"
239 | + message_info
240 | + "\n\n"
241 | + current_working_file[:30]
242 | + "\n"
243 | + "ETA : "
244 | + str(task_eta_in_file),
245 | ),
246 | ).start()
247 | old_working_line = current_working_line
248 | global old_working_file
249 | old_working_file = current_working_file
250 | time.sleep(3.5)
251 | xtime = time.time()
252 |
253 | if (
254 | int(time.time()) - xtime > timeout
255 | and current_working_file == old_working_file
256 | and task_percent > 5
257 | ):
258 | global interruption
259 | interruption = 1
260 | break
261 |
262 | old_working_file = ""
263 | finished_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
264 |
265 | if ns.x == 0:
266 | time.sleep(4)
267 | prog_bar = _bar.status(100)
268 | dst_info = _func.getIDbypath(dst_id, src_name)
269 | dst_endpoint_id = dst_info['dst_endpoint_id']
270 | dst_endpoint_link = dst_info['dst_endpoint_link']
271 | bot.edit_message_text(
272 | chat_id=chat_id,
273 | message_id=message_id,
274 | text=" ༺ ✪iCopy✪ ༻ \n"
275 | + _text[_lang]["done"]
276 | + " | "
277 | + "🏳️"
278 | + _text[_lang]["current_task_id"]
279 | + str(task["_id"])
280 | + "\n\n"
281 | + message_info.replace(
282 | " ┕─📃" + src_name,
283 | " ┕─📃"
284 | + '{}'.format(dst_endpoint_link, src_name),
285 | )
286 | + "\n"
287 | + _text[_lang]["task_finished_time"]
288 | + finished_time
289 | + "\n"
290 | + _text[_lang]["elapsed_time"]
291 | + str(now_elapsed_time),
292 | parse_mode=ParseMode.HTML,
293 | disable_web_page_preview=True,
294 | )
295 | check_is_reset = task_list.find_one({"_id": task["_id"]})
296 | if check_is_reset['is_reset'] == 0:
297 | if task_total_prog_size != 0 and task_current_prog_size != 0:
298 | task_list.update_one(
299 | {"_id": task["_id"]},
300 | {
301 | "$set": {
302 | "status": 1,
303 | "start_time": start_time,
304 | "finished_time": finished_time,
305 | "task_current_prog_num": int(task_current_prog_num),
306 | "task_total_prog_num": int(task_total_prog_num),
307 | "task_current_prog_size": float(task_current_prog_size),
308 | "task_total_prog_size": float(task_total_prog_size),
309 | "task_current_prog_size_tail": task_current_prog_size_tail,
310 | "task_total_prog_size_tail": task_total_prog_size_tail,
311 | "dst_endpoint_link": dst_endpoint_link,
312 | "dst_endpoint_id": dst_endpoint_id,
313 | }
314 | },
315 | )
316 | else:
317 | task_list.update_one(
318 | {"_id": task["_id"]},
319 | {
320 | "$set": {
321 | "status": 1,
322 | "start_time": start_time,
323 | "finished_time": finished_time,
324 | "task_current_prog_num": int(task_current_prog_num),
325 | "task_total_prog_num": int(task_total_prog_num),
326 | "task_current_prog_size": int(task_current_prog_size),
327 | "task_total_prog_size": int(task_total_prog_size),
328 | "task_current_prog_size_tail": task_current_prog_size_tail,
329 | "task_total_prog_size_tail": task_total_prog_size_tail,
330 | "dst_endpoint_link": dst_endpoint_link,
331 | "dst_endpoint_id": dst_endpoint_id,
332 | }
333 | },
334 | )
335 |
336 | elif check_is_reset['is_reset'] == 1:
337 | if "task_current_prog_num" and "task_total_prog_num" in check_is_reset:
338 | task_list.update_one(
339 | {"_id": task["_id"]},
340 | {
341 | "$set": {
342 | "status": 1,
343 | "start_time": start_time,
344 | "finished_time": finished_time,
345 | "task_current_prog_num": int(task_current_prog_num) + check_is_reset['task_current_prog_num'],
346 | "task_total_prog_num": int(task_total_prog_num) + check_is_reset['task_total_prog_num'],
347 | "task_current_prog_size": 0,
348 | "task_total_prog_size": 0,
349 | "task_current_prog_size_tail": "",
350 | "task_total_prog_size_tail": "",
351 | "dst_endpoint_link": dst_endpoint_link,
352 | "dst_endpoint_id": dst_endpoint_id,
353 | }
354 | },
355 | )
356 | else:
357 | task_list.update_one(
358 | {"_id": task["_id"]},
359 | {
360 | "$set": {
361 | "status": 1,
362 | "start_time": start_time,
363 | "finished_time": finished_time,
364 | "task_current_prog_num": int(task_current_prog_num),
365 | "task_total_prog_num": int(task_total_prog_num),
366 | "task_current_prog_size": 0,
367 | "task_total_prog_size": 0,
368 | "task_current_prog_size_tail": "",
369 | "task_total_prog_size_tail": "",
370 | "dst_endpoint_link": dst_endpoint_link,
371 | "dst_endpoint_id": dst_endpoint_id,
372 | }
373 | },
374 | )
375 |
376 | if ns.x == 1:
377 | bot.edit_message_text(
378 | chat_id=chat_id,
379 | message_id=message_id,
380 | text=" ༺ ✪iCopy✪ ༻ \n"
381 | + _text[_lang]["killed"]
382 | + " | "
383 | + "🏳️"
384 | + _text[_lang]["current_task_id"]
385 | + str(task["_id"])
386 | + "\n\n"
387 | + message_info.replace(
388 | "\n\n" + _text[_lang]["task_start_time"] + start_time, ""
389 | ).replace(
390 | "\n"
391 | + _text[_lang]["task_status"]
392 | + "\n\n"
393 | + str(task_in_size_speed)
394 | + " | "
395 | + str(task_in_file_speed),
396 | "",
397 | )
398 | + "\n"
399 | + _text[_lang]["is_killed_by_user"],
400 | )
401 |
402 | task_list.update_one(
403 | {"_id": task["_id"]},
404 | {
405 | "$set": {
406 | "status": 1,
407 | "error": 9,
408 | "start_time": start_time,
409 | "finished_time": finished_time,
410 | }
411 | },
412 | )
413 |
414 | if interruption == 1:
415 | bot.edit_message_text(
416 | chat_id=chat_id,
417 | message_id=message_id,
418 | text=" ༺ ✪iCopy✪ ༻ \n"
419 | + _text[_lang]["interrupted"]
420 | + " | "
421 | + "🏳️"
422 | + _text[_lang]["current_task_id"]
423 | + str(task["_id"])
424 | + "\n\n"
425 | + message_info.replace(
426 | "\n\n" + _text[_lang]["task_start_time"] + start_time, ""
427 | ).replace(
428 | "\n"
429 | + _text[_lang]["task_status"]
430 | + "\n\n"
431 | + str(task_in_size_speed)
432 | + " | "
433 | + str(task_in_file_speed),
434 | "",
435 | )
436 | + "\n"
437 | + _text[_lang]["is_interrupted_error"],
438 | )
439 |
440 | task_list.update_one(
441 | {"_id": task["_id"]},
442 | {
443 | "$set": {
444 | "status": 1,
445 | "error": 1,
446 | "start_time": start_time,
447 | "finished_time": finished_time,
448 | }
449 | },
450 | )
451 |
452 | prog_bar = _bar.status(0)
453 |
454 |
455 | def task_message_box(bot, chat_id, message_id, context):
456 | global context_old
457 | context_old = "iCopy"
458 | if context_old != context:
459 | bot.edit_message_text(chat_id=chat_id, message_id=message_id, text=context, parse_mode=ParseMode.HTML, disable_web_page_preview=True,)
460 | context_old = context
461 |
462 |
463 | def run(command):
464 | global icopyprocess
465 | icopyprocess = subprocess.Popen(
466 | command,
467 | stdout=subprocess.PIPE,
468 | stderr=subprocess.STDOUT,
469 | shell=False,
470 | encoding="utf-8",
471 | errors="ignore",
472 | universal_newlines=True,
473 | )
474 | while True:
475 | line = icopyprocess.stdout.readline().rstrip()
476 | if not line:
477 | break
478 | yield line
479 | icopyprocess.communicate()
480 |
481 |
--------------------------------------------------------------------------------